소스 검색

The Duoram update protocol for the computational peers

Ian Goldberg 1 년 전
부모
커밋
dcc320c967
5개의 변경된 파일375개의 추가작업 그리고 22개의 파일을 삭제
  1. 24 0
      duoram.hpp
  2. 44 20
      duoram.tcc
  3. 1 2
      online.cpp
  4. 86 0
      rdpf.hpp
  5. 220 0
      types.hpp

+ 24 - 0
duoram.hpp

@@ -68,6 +68,9 @@ public:
             size_t len = 0) {
         return Flat(*this, tio, yield, start, len);
     }
+
+    // For debugging; print the contents of the Duoram to stdout
+    void dump() const;
 };
 
 // The parent class of all Shapes.  This is an abstract class that
@@ -137,6 +140,27 @@ protected:
     // instantiated.
     virtual size_t indexmap(size_t idx) const = 0;
 
+    // Get a pair (for the server) of references to the underlying
+    // Duoram entries at share virtual index idx.  (That is, it gets
+    // duoram.p0_blind[indexmap(idx)], etc.)
+    inline std::tuple<T&,T&> get_server(size_t idx) const {
+        size_t physaddr = indexmap(idx);
+        return std::make_tuple(
+            duoram.p0_blind[physaddr],
+            duoram.p1_blind[physaddr]);
+    }
+
+    // Get a triple (for the computational players) of references to the
+    // underlying Duoram entries at share virtual index idx.  (That is,
+    // it gets duoram.database[indexmap(idx)], etc.)
+    inline std::tuple<T&,T&,T&> get_comp(size_t idx) const {
+        size_t physaddr = indexmap(idx);
+        return std::tie(
+            duoram.database[physaddr],
+            duoram.blind[physaddr],
+            duoram.peer_blinded_db[physaddr]);
+    }
+
 public:
     // Get the size
     inline size_t size() { return shape_size; }

+ 44 - 20
duoram.tcc

@@ -1,5 +1,7 @@
 // Templated method implementations for duoram.hpp
 
+#include <stdio.h>
+
 // Pass the player number and desired size
 template <typename T>
 Duoram<T>::Duoram(int player, size_t size) : player(player),
@@ -14,6 +16,23 @@ Duoram<T>::Duoram(int player, size_t size) : player(player),
     }
 }
 
+// For debugging; print the contents of the Duoram to stdout
+template <typename T>
+void Duoram<T>::dump() const
+{
+    for (size_t i=0; i<oram_size; ++i) {
+        if (player < 2) {
+            printf("%04lx %016lx %016lx %016lx\n",
+                i, database[i].share(), blind[i].share(),
+                peer_blinded_db[i].share());
+        } else {
+            printf("%04lx %016lx %016lx\n",
+                i, p0_blind[i].share(), p1_blind[i].share());
+        }
+    }
+    printf("\n");
+}
+
 // For debugging or checking your answers (using this in general is
 // of course insecure)
 template <typename T>
@@ -90,35 +109,40 @@ typename Duoram<T>::Shape::MemRefAS
     int player = shape.tio.player();
     if (player < 2) {
         // Computational players do this
+
         RDPFTriple dt = shape.tio.rdpftriple(shape.addr_size);
+
+        // Compute the index and message offsets
         RegAS indoffset = idx;
         indoffset -= dt.as_target;
-        RegAS Moffset[3];
-        Moffset[0] = Moffset[1] = Moffset[2] = M;
-        Moffset[0] -= dt.dpf[0].scaled_sum;
-        Moffset[1] -= dt.dpf[1].scaled_sum;
-        Moffset[2] -= dt.dpf[2].scaled_sum;
+        auto Moffset = std::make_tuple(M, M, M);
+        Moffset -= dt.scaled_sum();
+
+        // Send them to the peer, and everything except the first offset
+        // to the server
         shape.tio.queue_peer(&indoffset, BITBYTES(shape.addr_size));
-        shape.tio.iostream_peer() << Moffset[0] << Moffset[1] <<
-            Moffset[2];
+        shape.tio.iostream_peer() << Moffset;
         shape.tio.queue_server(&indoffset, BITBYTES(shape.addr_size));
-        shape.tio.iostream_server() << Moffset[1] << Moffset[2];
+        shape.tio.iostream_server() << std::get<1>(Moffset) <<
+            std::get<2>(Moffset);
+
         shape.yield();
+
+        // Receive the above from the peer
         RegAS peerindoffset;
-        RegAS peerMoffset[3];
+        std::tuple<RegAS,RegAS,RegAS> peerMoffset;
         shape.tio.recv_peer(&peerindoffset, BITBYTES(shape.addr_size));
-        shape.tio.iostream_peer() >> peerMoffset[0] >> peerMoffset[1] >>
-            peerMoffset[2];
-        indoffset += peerindoffset;
-        indoffset &= shape.addr_mask;
-        Moffset[0] += peerMoffset[0];
-        Moffset[1] += peerMoffset[1];
-        Moffset[2] += peerMoffset[2];
-        StreamEval<RDPFTriple> ev(dt, indoffset.ashare,
-            shape.tio.aes_ops());
-        for (size_t i=0; i<shape.shape_size; ++i) {
-            auto [L0, L1, L2] = ev.next();
+        shape.tio.iostream_peer() >> peerMoffset;
+
+        // Reconstruct the total offsets
+        auto indshift = combine(indoffset, peerindoffset, shape.addr_size);
+        auto Mshift = combine(Moffset, peerMoffset);
 
+        // Evaluate the DPFs and add them to the database
+        StreamEval ev(dt, indshift, shape.tio.aes_ops());
+        for (size_t i=0; i<shape.shape_size; ++i) {
+            auto L = ev.next();
+            shape.get_comp(i) += dt.scaled_as(L) + dt.unit_as(L) * Mshift;
         }
     } else {
         // The server does this

+ 1 - 2
online.cpp

@@ -450,8 +450,7 @@ static void duoram_test(MPCIO &mpcio, yield_t &yield,
             RegAS Aa = A[aidx];
             auto Ax = A[xidx];
             auto Ae = A[eidx];
-            printf("%ld %ld\n", A.size(), Aa.ashare);
-
+            oram.dump();
             auto check = A.reconstruct();
             if (tio.player() == 0) {
                 for (address_t i=0;i<size;++i) {

+ 86 - 0
rdpf.hpp

@@ -200,6 +200,52 @@ struct RDPFTriple {
     // Descend the three RDPFs in lock step
     node descend(const node &parent, nbits_t parentdepth,
         bit_t whichchild, size_t &op_counter) const;
+
+    // Additive share of the scaling value M_as such that the high words
+    // of the leaf values for P0 and P1 add to M_as * e_{target}
+    inline std::tuple<RegAS,RegAS,RegAS> scaled_sum() const {
+        return std::make_tuple(dpf[0].scaled_sum, dpf[1].scaled_sum,
+            dpf[2].scaled_sum);
+    }
+
+    // XOR share of the scaling value M_xs such that the high words
+    // of the leaf values for P0 and P1 XOR to M_xs * e_{target}
+    inline std::tuple<RegXS,RegXS,RegXS> scaled_xor() const {
+        return std::make_tuple(dpf[0].scaled_xor, dpf[1].scaled_xor,
+            dpf[2].scaled_xor);
+    }
+
+    // Get the bit-shared unit vector entry from the leaf node
+    inline std::tuple<RegBS,RegBS,RegBS> unit_bs(node leaf) const {
+        return std::make_tuple(
+            dpf[0].unit_bs(std::get<0>(leaf)),
+            dpf[1].unit_bs(std::get<1>(leaf)),
+            dpf[2].unit_bs(std::get<2>(leaf)));
+    }
+
+    // Get the additive-shared unit vector entry from the leaf node
+    inline std::tuple<RegAS,RegAS,RegAS> unit_as(node leaf) const {
+        return std::make_tuple(
+            dpf[0].unit_as(std::get<0>(leaf)),
+            dpf[1].unit_as(std::get<1>(leaf)),
+            dpf[2].unit_as(std::get<2>(leaf)));
+    }
+
+    // Get the XOR-shared scaled vector entry from the leaf ndoe
+    inline std::tuple<RegXS,RegXS,RegXS> scaled_xs(node leaf) const {
+        return std::make_tuple(
+            dpf[0].scaled_xs(std::get<0>(leaf)),
+            dpf[1].scaled_xs(std::get<1>(leaf)),
+            dpf[2].scaled_xs(std::get<2>(leaf)));
+    }
+
+    // Get the additive-shared scaled vector entry from the leaf node
+    inline std::tuple<RegAS,RegAS,RegAS> scaled_as(node leaf) const {
+        return std::make_tuple(
+            dpf[0].scaled_as(std::get<0>(leaf)),
+            dpf[1].scaled_as(std::get<1>(leaf)),
+            dpf[2].scaled_as(std::get<2>(leaf)));
+    }
 };
 
 struct RDPFPair {
@@ -230,6 +276,46 @@ struct RDPFPair {
     // Descend the two RDPFs in lock step
     node descend(const node &parent, nbits_t parentdepth,
         bit_t whichchild, size_t &op_counter) const;
+
+    // Additive share of the scaling value M_as such that the high words
+    // of the leaf values for P0 and P1 add to M_as * e_{target}
+    inline std::tuple<RegAS,RegAS> scaled_sum() const {
+        return std::make_tuple(dpf[0].scaled_sum, dpf[1].scaled_sum);
+    }
+
+    // XOR share of the scaling value M_xs such that the high words
+    // of the leaf values for P0 and P1 XOR to M_xs * e_{target}
+    inline std::tuple<RegXS,RegXS> scaled_xor() const {
+        return std::make_tuple(dpf[0].scaled_xor, dpf[1].scaled_xor);
+    }
+
+    // Get the bit-shared unit vector entry from the leaf node
+    inline std::tuple<RegBS,RegBS> unit_bs(node leaf) const {
+        return std::make_tuple(
+            dpf[0].unit_bs(std::get<0>(leaf)),
+            dpf[1].unit_bs(std::get<1>(leaf)));
+    }
+
+    // Get the additive-shared unit vector entry from the leaf node
+    inline std::tuple<RegAS,RegAS> unit_as(node leaf) const {
+        return std::make_tuple(
+            dpf[0].unit_as(std::get<0>(leaf)),
+            dpf[1].unit_as(std::get<1>(leaf)));
+    }
+
+    // Get the XOR-shared scaled vector entry from the leaf ndoe
+    inline std::tuple<RegXS,RegXS> scaled_xs(node leaf) const {
+        return std::make_tuple(
+            dpf[0].scaled_xs(std::get<0>(leaf)),
+            dpf[1].scaled_xs(std::get<1>(leaf)));
+    }
+
+    // Get the additive-shared scaled vector entry from the leaf node
+    inline std::tuple<RegAS,RegAS> scaled_as(node leaf) const {
+        return std::make_tuple(
+            dpf[0].scaled_as(std::get<0>(leaf)),
+            dpf[1].scaled_as(std::get<1>(leaf)));
+    }
 };
 
 #include "rdpf.tcc"

+ 220 - 0
types.hpp

@@ -48,6 +48,8 @@ struct RegAS {
 
     RegAS() : ashare(0) {}
 
+    inline value_t share() const { return ashare; }
+
     // Set each side's share to a random value nbits bits long
     inline void randomize(size_t nbits = VALUE_BITS) {
         value_t mask = MASKBITS(nbits);
@@ -100,12 +102,23 @@ struct RegAS {
     }
 };
 
+inline value_t combine(const RegAS &A, const RegAS &B,
+        nbits_t nbits = VALUE_BITS) {
+    value_t mask = ~0;
+    if (nbits < VALUE_BITS) {
+        mask = (value_t(1)<<nbits)-1;
+    }
+    return (A.ashare + B.ashare) & mask;
+}
+
 // The type of a register holding a bit share
 struct RegBS {
     bit_t bshare;
 
     RegBS() : bshare(0) {}
 
+    inline bit_t share() const { return bshare; }
+
     // Set each side's share to a random bit
     inline void randomize() {
         unsigned char randb;
@@ -131,6 +144,8 @@ struct RegXS {
 
     RegXS() : xshare(0) {}
 
+    inline value_t share() const { return xshare; }
+
     // Set each side's share to a random value nbits bits long
     inline void randomize(size_t nbits = VALUE_BITS) {
         value_t mask = MASKBITS(nbits);
@@ -168,6 +183,205 @@ struct RegXS {
     }
 };
 
+inline value_t combine(const RegXS &A, const RegXS &B,
+        nbits_t nbits = VALUE_BITS) {
+    value_t mask = ~0;
+    if (nbits < VALUE_BITS) {
+        mask = (value_t(1)<<nbits)-1;
+    }
+    return (A.xshare ^ B.xshare) & mask;
+}
+
+// Some useful operations on tuples of the above types
+
+template <typename T>
+std::tuple<T,T> operator+=(std::tuple<T,T> &A,
+    const std::tuple<T,T> &B)
+{
+    std::get<0>(A) += std::get<0>(B);
+    std::get<1>(A) += std::get<1>(B);
+    return A;
+}
+
+template <typename T>
+std::tuple<T,T> operator+=(const std::tuple<T&,T&> &A,
+    const std::tuple<T,T> &B)
+{
+    std::get<0>(A) += std::get<0>(B);
+    std::get<1>(A) += std::get<1>(B);
+    return A;
+}
+
+template <typename T>
+std::tuple<T,T> operator+(const std::tuple<T,T> &A,
+    const std::tuple<T,T> &B)
+{
+    auto res = A;
+    res += B;
+    return res;
+}
+
+template <typename T>
+std::tuple<T,T> operator-=(const std::tuple<T&,T&> &A,
+    const std::tuple<T,T> &B)
+{
+    std::get<0>(A) -= std::get<0>(B);
+    std::get<1>(A) -= std::get<1>(B);
+    return A;
+}
+
+template <typename T>
+std::tuple<T,T> operator-=(std::tuple<T,T> &A,
+    const std::tuple<T,T> &B)
+{
+    std::get<0>(A) -= std::get<0>(B);
+    std::get<1>(A) -= std::get<1>(B);
+    return A;
+}
+
+template <typename T>
+std::tuple<T,T> operator-(const std::tuple<T,T> &A,
+    const std::tuple<T,T> &B)
+{
+    auto res = A;
+    res -= B;
+    return res;
+}
+
+template <typename T>
+std::tuple<T,T> operator*=(const std::tuple<T&,T&> &A,
+    const std::tuple<value_t,value_t> &B)
+{
+    std::get<0>(A) *= std::get<0>(B);
+    std::get<1>(A) *= std::get<1>(B);
+    return A;
+}
+
+template <typename T>
+std::tuple<T,T> operator*=(std::tuple<T,T> &A,
+    const std::tuple<value_t,value_t> &B)
+{
+    std::get<0>(A) *= std::get<0>(B);
+    std::get<1>(A) *= std::get<1>(B);
+    return A;
+}
+
+template <typename T>
+std::tuple<T,T> operator*(const std::tuple<T,T> &A,
+    const std::tuple<value_t,value_t> &B)
+{
+    auto res = A;
+    res *= B;
+    return res;
+}
+
+template <typename T>
+inline std::tuple<value_t,value_t> combine(
+        const std::tuple<T,T> &A, const std::tuple<T,T> &B,
+        nbits_t nbits = VALUE_BITS) {
+    return std::make_tuple(
+        combine(std::get<0>(A), std::get<0>(B), nbits),
+        combine(std::get<1>(A), std::get<1>(B), nbits));
+}
+
+template <typename T>
+std::tuple<T,T,T> operator+=(const std::tuple<T&,T&,T&> &A,
+    const std::tuple<T,T,T> &B)
+{
+    std::get<0>(A) += std::get<0>(B);
+    std::get<1>(A) += std::get<1>(B);
+    std::get<2>(A) += std::get<2>(B);
+    return A;
+}
+
+template <typename T>
+std::tuple<T,T,T> operator+=(std::tuple<T,T,T> &A,
+    const std::tuple<T,T,T> &B)
+{
+    std::get<0>(A) += std::get<0>(B);
+    std::get<1>(A) += std::get<1>(B);
+    std::get<2>(A) += std::get<2>(B);
+    return A;
+}
+
+template <typename T>
+std::tuple<T,T,T> operator+(const std::tuple<T,T,T> &A,
+    const std::tuple<T,T,T> &B)
+{
+    auto res = A;
+    res += B;
+    return res;
+}
+
+template <typename T>
+std::tuple<T,T,T> operator-=(const std::tuple<T&,T&,T&> &A,
+    const std::tuple<T,T,T> &B)
+{
+    std::get<0>(A) -= std::get<0>(B);
+    std::get<1>(A) -= std::get<1>(B);
+    std::get<2>(A) -= std::get<2>(B);
+    return A;
+}
+
+template <typename T>
+std::tuple<T,T,T> operator-=(std::tuple<T,T,T> &A,
+    const std::tuple<T,T,T> &B)
+{
+    std::get<0>(A) -= std::get<0>(B);
+    std::get<1>(A) -= std::get<1>(B);
+    std::get<2>(A) -= std::get<2>(B);
+    return A;
+}
+
+template <typename T>
+std::tuple<T,T,T> operator-(const std::tuple<T,T,T> &A,
+    const std::tuple<T,T,T> &B)
+{
+    auto res = A;
+    res -= B;
+    return res;
+}
+
+template <typename T>
+std::tuple<T,T,T> operator*=(const std::tuple<T&,T&,T&> &A,
+    const std::tuple<value_t,value_t,value_t> &B)
+{
+    std::get<0>(A) *= std::get<0>(B);
+    std::get<1>(A) *= std::get<1>(B);
+    std::get<2>(A) *= std::get<2>(B);
+    return A;
+}
+
+template <typename T>
+std::tuple<T,T,T> operator*=(std::tuple<T,T,T> &A,
+    const std::tuple<value_t,value_t,value_t> &B)
+{
+    std::get<0>(A) *= std::get<0>(B);
+    std::get<1>(A) *= std::get<1>(B);
+    std::get<2>(A) *= std::get<2>(B);
+    return A;
+}
+
+template <typename T>
+std::tuple<T,T,T> operator*(const std::tuple<T,T,T> &A,
+    const std::tuple<value_t,value_t,value_t> &B)
+{
+    auto res = A;
+    res *= B;
+    return res;
+}
+
+template <typename T>
+inline std::tuple<value_t,value_t,value_t> combine(
+        const std::tuple<T,T,T> &A, const std::tuple<T,T,T> &B,
+        nbits_t nbits = VALUE_BITS) {
+    return std::make_tuple(
+        combine(std::get<0>(A), std::get<0>(B), nbits),
+        combine(std::get<1>(A), std::get<1>(B), nbits),
+        combine(std::get<2>(A), std::get<2>(B), nbits));
+}
+
+
 // The _maximum_ number of bits in an MPC address; the actual size of
 // the memory will typically be set at runtime, but it cannot exceed
 // this value.  It is more efficient (in terms of communication) in some
@@ -253,9 +467,15 @@ struct RDPFTripleName { static constexpr const char *name = "r"; };
 
 // Default I/O for various types
 
+// Otherwise the comma is treated as an argument separator
+#define COMMA ,
 DEFAULT_IO(RegBS)
 DEFAULT_IO(RegAS)
+DEFAULT_IO(std::tuple<RegAS COMMA RegAS>)
+DEFAULT_IO(std::tuple<RegAS COMMA RegAS COMMA RegAS>)
 DEFAULT_IO(RegXS)
+DEFAULT_IO(std::tuple<RegXS COMMA RegXS>)
+DEFAULT_IO(std::tuple<RegXS COMMA RegXS COMMA RegXS>)
 DEFAULT_IO(MultTriple)
 DEFAULT_IO(HalfTriple)