Browse Source

CDPF creation

Ian Goldberg 1 year ago
parent
commit
3811dce023
9 changed files with 232 additions and 12 deletions
  1. 3 3
      Makefile
  2. 112 3
      cdpf.cpp
  3. 24 3
      cdpf.hpp
  4. 45 0
      cdpf.tcc
  5. 19 0
      mpcio.cpp
  6. 3 0
      mpcio.hpp
  7. 21 0
      preproc.cpp
  8. 3 3
      rdpf.cpp
  9. 2 0
      types.hpp

+ 3 - 3
Makefile

@@ -30,13 +30,13 @@ depend:
 
 prac.o: mpcio.hpp types.hpp preproc.hpp options.hpp online.hpp
 mpcio.o: mpcio.hpp types.hpp rdpf.hpp coroutine.hpp bitutils.hpp dpf.hpp
-mpcio.o: prg.hpp aes.hpp rdpf.tcc
+mpcio.o: prg.hpp aes.hpp rdpf.tcc cdpf.hpp cdpf.tcc
 preproc.o: types.hpp coroutine.hpp mpcio.hpp preproc.hpp options.hpp rdpf.hpp
-preproc.o: bitutils.hpp dpf.hpp prg.hpp aes.hpp rdpf.tcc
+preproc.o: bitutils.hpp dpf.hpp prg.hpp aes.hpp rdpf.tcc cdpf.hpp cdpf.tcc
 online.o: online.hpp mpcio.hpp types.hpp options.hpp mpcops.hpp coroutine.hpp
 online.o: rdpf.hpp bitutils.hpp dpf.hpp prg.hpp aes.hpp rdpf.tcc duoram.hpp
 online.o: duoram.tcc
 mpcops.o: mpcops.hpp types.hpp mpcio.hpp coroutine.hpp bitutils.hpp
 rdpf.o: rdpf.hpp mpcio.hpp types.hpp coroutine.hpp bitutils.hpp dpf.hpp
 rdpf.o: prg.hpp aes.hpp rdpf.tcc mpcops.hpp
-cdpf.o: cdpf.hpp types.hpp dpf.hpp prg.hpp bitutils.hpp aes.hpp
+cdpf.o: bitutils.hpp cdpf.hpp types.hpp dpf.hpp prg.hpp aes.hpp cdpf.tcc

+ 112 - 3
cdpf.cpp

@@ -3,9 +3,10 @@
 #include "cdpf.hpp"
 
 // Generate a pair of CDPFs with the given target value
-std::tuple<CDPF,CDPF> CDPF::generate(value_t target)
+std::tuple<CDPF,CDPF> CDPF::generate(value_t target, size_t &aes_ops)
 {
     CDPF dpf0, dpf1;
+    nbits_t depth = VALUE_BITS - 7;
 
     // Pick two random seeds
     arc4random_buf(&dpf0.seed, sizeof(dpf0.seed));
@@ -15,14 +16,122 @@ std::tuple<CDPF,CDPF> CDPF::generate(value_t target)
     dpf1.seed = set_lsb(dpf1.seed, 1);
     dpf0.whichhalf = 0;
     dpf1.whichhalf = 1;
+    dpf0.cfbits = 0;
+    dpf1.cfbits = 0;
+    dpf0.as_target.randomize();
+    dpf1.as_target.ashare = target - dpf0.as_target.ashare;
+    dpf0.xs_target.randomize();
+    dpf1.xs_target.xshare = target ^ dpf0.xs_target.xshare;
+
+    // The current node in each CDPF as we descend the tree.  The
+    // invariant is that cur0 and cur1 are the nodes on the path to the
+    // target at level curlevel.  They will necessarily be different,
+    // and indeed must have different flag (low) bits.
+    DPFnode cur0 = dpf0.seed;
+    DPFnode cur1 = dpf1.seed;
+    nbits_t curlevel = 0;
+
+    while(curlevel < depth) {
+        // Construct the two (uncorrected) children of each node
+        DPFnode left0, right0, left1, right1;
+        prgboth(left0, right0, cur0, aes_ops);
+        prgboth(left1, right1, cur1, aes_ops);
+
+        // Which way lies the target?
+        bool targetdir = !!(target & (value_t(1)<<(depth-curlevel-1)));
+        DPFnode CW;
+        bool cfbit = !get_lsb(left0 ^ left1 ^ right0 ^ right1);
+        bool flag0 = get_lsb(cur0);
+        bool flag1 = get_lsb(cur1);
+        // The last level is special
+        if (curlevel < depth-1) {
+            if (targetdir == 0) {
+                // The target is to the left, so make the correction word
+                // and bit make the right children the same and the left
+                // children have different flag bits.
+
+                // Recall that descend will apply (only for the party whose
+                // current node (cur0 or cur1) has the flag bit set, for
+                // which exactly one of the two will) CW to both children,
+                // and cfbit to the flag bit of the right child.
+                CW = right0 ^ right1 ^ lsb128_mask[cfbit];
+
+                // Compute the current nodes for the next level
+                // Exactly one of these two XORs will fire, so afterwards,
+                // cur0 ^ cur1 = left0 ^ left1 ^ CW, which will have low bit
+                // 1 by the definition of cfbit.
+                cur0 = xor_if(left0, CW, flag0);
+                cur1 = xor_if(left1, CW, flag1);
+            } else {
+                // The target is to the right, so make the correction word
+                // and bit make the left children the same and the right
+                // children have different flag bits.
+                CW = left0 ^ left1;
+
+                // Compute the current nodes for the next level
+                // Exactly one of these two XORs will fire, so similar to
+                // the above, afterwards, cur0 ^ cur1 = right0 ^ right1 ^ CWR,
+                // which will have low bit 1.
+                DPFnode CWR = CW ^ lsb128_mask[cfbit];
+                cur0 = xor_if(right0, CWR, flag0);
+                cur1 = xor_if(right1, CWR, flag1);
+            }
+        } else {
+            // We're at the last level before the leaves.  We still want
+            // the children not in the direction of targetdir to end up
+            // the same, but now we want the child in the direction of
+            // targetdir to also end up the same, except for the single
+            // target bit.  Importantly, the low bit (the flag bit in
+            // all other nodes) is not special, and will in fact usually
+            // end up the same for the two DPFs (unless the target bit
+            // happens to be the low bit of the word; i.e., the low 7
+            // bits of target are all 0).
+
+            // This will be a 128-bit word with a single bit set, in
+            // position (target & 0x7f).
+            uint8_t loc = (target & 0x7f);
+            DPFnode target_set_bit = _mm_set_epi64x(
+                loc >= 64 ? (uint64_t(1)<<(loc-64)) : 0,
+                loc >= 64 ? 0 : (uint64_t(1)<<loc));
+
+            if (targetdir == 0) {
+                // We want the right children to be the same, and the
+                // left children to be the same except for the target
+                // bit.
+                // Remember for exactly one of the two parties, CW will
+                // be applied to the left and CWR will be applied to the
+                // right.
+                CW = left0 ^ left1 ^ target_set_bit;
+                DPFnode CWR = right0 ^ right1;
+                dpf0.leaf_cwr = CWR;
+                dpf1.leaf_cwr = CWR;
+            } else {
+                // We want the left children to be the same, and the
+                // right children to be the same except for the target
+                // bit.
+                // Remember for exactly one of the two parties, CW will
+                // be applied to the left and CWR will be applied to the
+                // right.
+                CW = left0 ^ left1;
+                DPFnode CWR = right0 ^ right1 ^ target_set_bit;
+                dpf0.leaf_cwr = CWR;
+                dpf1.leaf_cwr = CWR;
+            }
+        }
+        dpf0.cw.push_back(CW);
+        dpf1.cw.push_back(CW);
+        dpf0.cfbits |= (value_t(cfbit)<<curlevel);
+        dpf1.cfbits |= (value_t(cfbit)<<curlevel);
+        ++curlevel;
+    }
 
     return std::make_tuple(dpf0, dpf1);
 }
 
 // Generate a pair of CDPFs with a random target value
-std::tuple<CDPF,CDPF> CDPF::generate()
+std::tuple<CDPF,CDPF> CDPF::generate(size_t &aes_ops)
 {
     value_t target;
     arc4random_buf(&target, sizeof(target));
-    return generate(target);
+    return generate(target, aes_ops);
 }

+ 24 - 3
cdpf.hpp

@@ -89,13 +89,34 @@ struct CDPF : public DPF {
     // 64), and having the 128-bit labels on the leaf nodes directly
     // represent the 128 bits that would have come out of the subtree of
     // a (notional) depth-64 tree rooted at that depth-57 node.
-    DPFnode leaf_cw;
+    DPFnode leaf_cwr;
 
     // Generate a pair of CDPFs with the given target value
-    static std::tuple<CDPF,CDPF> generate(value_t target);
+    static std::tuple<CDPF,CDPF> generate(value_t target, size_t &aes_ops);
 
     // Generate a pair of CDPFs with a random target value
-    static std::tuple<CDPF,CDPF> generate();
+    static std::tuple<CDPF,CDPF> generate(size_t &aes_ops);
+
+    // Descend from the parent of a leaf node to the leaf node
+    inline DPFnode descend_to_leaf(const DPFnode &parent,
+        bit_t whichchild, size_t &aes_ops) const;
 };
 
+// Descend from the parent of a leaf node to the leaf node
+inline DPFnode CDPF::descend_to_leaf(const DPFnode &parent,
+    bit_t whichchild, size_t &aes_ops) const
+{
+    DPFnode prgout;
+    bool flag = get_lsb(parent);
+    prg(prgout, parent, whichchild, aes_ops);
+    if (flag) {
+        DPFnode CW = cw.back();
+        DPFnode CWR = leaf_cwr;
+        prgout ^= (whichchild ? CWR : CW);
+    }
+    return prgout;
+}
+
+#include "cdpf.tcc"
+
 #endif

+ 45 - 0
cdpf.tcc

@@ -0,0 +1,45 @@
+// Templated method implementations for cdpf.hpp
+
+// I/O for CDPFs
+
+// Read the DPF from the output stream.  You can use this to read DPFs
+// from files or from the network.
+template <typename T>
+T& operator>>(T &is, CDPF &cdpf)
+{
+    is.read((char *)&cdpf.seed, sizeof(cdpf.seed));
+    cdpf.whichhalf = get_lsb(cdpf.seed);
+    uint8_t depth = VALUE_BITS - 7;
+    cdpf.cw.clear();
+    for (uint8_t i=0; i<depth; ++i) {
+        DPFnode cw;
+        is.read((char *)&cw, sizeof(cw));
+        cdpf.cw.push_back(cw);
+    }
+    value_t cfbits = 0;
+    is.read((char *)&cfbits, BITBYTES(depth));
+    cdpf.cfbits = cfbits;
+    is.read((char *)&cdpf.leaf_cwr, sizeof(cdpf.leaf_cwr));
+    is.read((char *)&cdpf.as_target, sizeof(cdpf.as_target));
+    is.read((char *)&cdpf.xs_target, sizeof(cdpf.xs_target));
+
+    return is;
+}
+
+// Write the DPF to the output stream.  You can use this to write DPFs
+// to files or over the network.
+template <typename T>
+T& operator<<(T &os, const CDPF &cdpf)
+{
+    os.write((const char *)&cdpf.seed, sizeof(cdpf.seed));
+    uint8_t depth = VALUE_BITS - 7;
+    for (uint8_t i=0; i<depth; ++i) {
+        os.write((const char *)&cdpf.cw[i], sizeof(cdpf.cw[i]));
+    }
+    os.write((const char *)&cdpf.cfbits, BITBYTES(depth));
+    os.write((const char *)&cdpf.leaf_cwr, sizeof(cdpf.leaf_cwr));
+    os.write((const char *)&cdpf.as_target, sizeof(cdpf.as_target));
+    os.write((const char *)&cdpf.xs_target, sizeof(cdpf.xs_target));
+
+    return os;
+}

+ 19 - 0
mpcio.cpp

@@ -2,6 +2,7 @@
 #include <sys/resource.h>  // getrusage
 #include "mpcio.hpp"
 #include "rdpf.hpp"
+#include "cdpf.hpp"
 #include "bitutils.hpp"
 
 // T is the type being stored
@@ -641,6 +642,24 @@ RDPFPair MPCTIO::rdpfpair(nbits_t depth)
     return val;
 }
 
+CDPF MPCTIO::cdpf()
+{
+    CDPF val;
+    if (mpcio.player < 2) {
+        MPCPeerIO &mpcpio = static_cast<MPCPeerIO&>(mpcio);
+        if (mpcpio.preprocessing) {
+            iostream_server() >> val;
+        } else {
+            mpcpio.cdpfs[thread_num].get(val);
+        }
+    } else if (mpcio.preprocessing) {
+        auto [ cdpf0, cdpf1 ] = CDPF::generate(aes_ops());
+        iostream_p0() << cdpf0;
+        iostream_p1() << cdpf1;
+    }
+    return val;
+}
+
 // The port number for the P1 -> P0 connection
 static const unsigned short port_p1_p0 = 2115;
 

+ 3 - 0
mpcio.hpp

@@ -200,6 +200,7 @@ struct MPCPeerIO : public MPCIO {
     std::deque<MPCSingleIO> serverios;
     std::vector<PreCompStorage<MultTriple, MultTripleName>> triples;
     std::vector<PreCompStorage<HalfTriple, HalfTripleName>> halftriples;
+    std::vector<PreCompStorage<CDPF, CDPFName>> cdpfs;
     // The outer vector is (like above) one item per thread
     // The inner array is indexed by DPF depth (depth d is at entry d-1)
     std::vector<std::array<PreCompStorage<RDPFTriple, RDPFTripleName>,ADDRESS_MAX_BITS>> rdpftriples;
@@ -341,6 +342,8 @@ public:
     RDPFTriple rdpftriple(nbits_t depth);
     // The server calls:
     RDPFPair rdpfpair(nbits_t depth);
+    // Anyone can call:
+    CDPF cdpf();
 
     // Accessors
 

+ 21 - 0
preproc.cpp

@@ -4,6 +4,7 @@
 #include "coroutine.hpp"
 #include "preproc.hpp"
 #include "rdpf.hpp"
+#include "cdpf.hpp"
 
 // Keep track of open files that coroutines might be writing into
 class Openfiles {
@@ -129,6 +130,16 @@ void preprocessing_comp(MPCIO &mpcio, const PRACOptions &opts, char **args)
                                 tripfile.os() << rdpftrip;
                             });
                     }
+                } else if (type == 0x40) {
+                    // Comparison DPFs
+                    auto cdpffile = ofiles.open("cdpf",
+                        mpcio.player, thread_num);
+
+                    CDPF C;
+                    for (unsigned int i=0; i<num; ++i) {
+                        C = tio.cdpf();
+                        cdpffile.os() << C;
+                    }
                 }
             }
             run_coroutines(tio, coroutines);
@@ -231,6 +242,16 @@ void preprocessing_server(MPCServerIO &mpcsrvio, const PRACOptions &opts, char *
                                 });
                         }
                     }
+                } else if (type[0] == 'c') {
+                    unsigned char typetag = 0x40;
+                    stio.queue_p0(&typetag, 1);
+                    stio.queue_p0(&num, 4);
+                    stio.queue_p1(&typetag, 1);
+                    stio.queue_p1(&num, 4);
+
+                    for (unsigned int i=0; i<num; ++i) {
+                        stio.cdpf();
+                    }
 		}
                 free(arg);
                 ++threadargs;

+ 3 - 3
rdpf.cpp

@@ -32,7 +32,7 @@ RDPF::RDPF(MPCTIO &tio, yield_t &yield,
     RegXS target, nbits_t depth, bool save_expansion)
 {
     int player = tio.player();
-    size_t &aesops = tio.aes_ops();
+    size_t &aes_ops = tio.aes_ops();
 
     // Choose a random seed
     arc4random_buf(&seed, sizeof(seed));
@@ -72,7 +72,7 @@ RDPF::RDPF(MPCTIO &tio, yield_t &yield,
         if (player < 2) {
             for(size_t i=0;i<curlevel_size;++i) {
                 DPFnode lchild, rchild;
-                prgboth(lchild, rchild, curlevel[i], aesops);
+                prgboth(lchild, rchild, curlevel[i], aes_ops);
                 L = (L ^ lchild);
                 R = (R ^ rchild);
                 if (nextlevel) {
@@ -148,7 +148,7 @@ RDPF::RDPF(MPCTIO &tio, yield_t &yield,
             });
         run_coroutines(yield, coroutines);
         bool parity_bit = our_parity_bit ^ peer_parity_bit;
-        cfbits |= (size_t(parity_bit)<<level);
+        cfbits |= (value_t(parity_bit)<<level);
         DPFnode CWR = CW ^ lsb128_mask[parity_bit];
         if (player < 2) {
             if (level < depth-1) {

+ 2 - 0
types.hpp

@@ -487,6 +487,8 @@ struct RDPFPair;
 struct RDPFPairName { static constexpr const char *name = "r"; };
 struct RDPFTriple;
 struct RDPFTripleName { static constexpr const char *name = "r"; };
+struct CDPF;
+struct CDPFName { static constexpr const char *name = "c"; };
 
 // We want the I/O (using << and >>) for many classes
 // to just be a common thing: write out the bytes