Browse Source

New RDPF2of3 struct

When computational peers do an ORAM read, they hold an RDPFTriple, but
they only need two of them.  Previously, we were moving the two RDPFs
they needed out of the RDPFTriple into an RDPFPair, breaking the triple.
That _was_ fine, but now that we're going to be reusing RDPFTriples when
performing related-index ORAM accesses, we can't break that triple.

RDPF2of3 acts just like an RDPFPair, except it holds const references to
two RDPFs (contained in an RDPFTriple), rather than holding the RDPFs
themselves.
Ian Goldberg 1 year ago
parent
commit
c384b452a8
3 changed files with 144 additions and 12 deletions
  1. 1 3
      duoram.tcc
  2. 117 9
      rdpf.hpp
  3. 26 0
      rdpf.tcc

+ 1 - 3
duoram.tcc

@@ -294,9 +294,7 @@ Duoram<T>::Shape::MemRefS<U,FT,FST,Sh,WIDTH>::operator FT()
         indoffset -= oblividx->idx;
 
         // We only need two of the DPFs for reading
-        RDPFPair<1> dp(std::move(dt), 0, player == 0 ? 2 : 1);
-        // The RDPFTriple dt is now broken, since we've moved things out
-        // of it.
+        RDPF2of3<1> dp(dt, 0, player == 0 ? 2 : 1);
 
         // Send it to the peer and the server
         shape.tio.queue_peer(&indoffset, BITBYTES(shape.addr_size));

+ 117 - 9
rdpf.hpp

@@ -368,15 +368,6 @@ struct RDPFPair {
 
     RDPFPair() {}
 
-    // Create an RDPFPair from an RDPFTriple, keeping two of the RDPFs
-    // and dropping one.  This _moves_ the dpfs from the triple to the
-    // pair, so the triple will no longer be valid after using this.
-    // which0 and which1 indicate which of the dpfs to keep.
-    RDPFPair(RDPFTriple<WIDTH> &&trip, int which0, int which1) {
-        dpf[0] = std::move(trip.dpf[which0]);
-        dpf[1] = std::move(trip.dpf[which1]);
-    }
-
     // The depth
     inline nbits_t depth() const { return dpf[0].depth(); }
 
@@ -464,6 +455,123 @@ struct RDPFPair {
 
 };
 
+// These are used by computational peers, who hold RPDFTriples, but when
+// reading, only need to use 2 of the 3 RDPFs.  The API follows that of
+// RDPFPair, but internally, it holds two references to external RDPFs,
+// instead of holding the RDPFs themselves.
+
+template <nbits_t WIDTH>
+struct RDPF2of3 {
+    template <typename T>
+    using Pair = std::tuple<T, T>;
+    template <typename T>
+    using WPair = std::tuple<
+        typename std::array<T,WIDTH>,
+        typename std::array<T,WIDTH> >;
+
+    // The type of pairs of nodes, LeafNodes, and the wide shared
+    // register types
+    using node = Pair<DPFnode>;
+    using LeafNode = Pair<typename RDPF<WIDTH>::LeafNode>;
+    using RegASWP = WPair<RegAS>;
+    using RegXSWP = WPair<RegXS>;
+
+    const RDPF<WIDTH> &dpf0, &dpf1;
+
+    // Create an RDPFPair from an RDPFTriple, keeping two of the RDPFs
+    // and dropping one.  This _moves_ the dpfs from the triple to the
+    // pair, so the triple will no longer be valid after using this.
+    // which0 and which1 indicate which of the dpfs to keep.
+    RDPF2of3(const RDPFTriple<WIDTH> &trip, int which0, int which1) :
+        dpf0(trip.dpf[which0]), dpf1(trip.dpf[which1]) {}
+
+    // The depth
+    inline nbits_t depth() const { return dpf0.depth(); }
+
+    // Set the current depth for an incremental RDPFPair; 0 means to use
+    // maxdepth
+    inline void depth(nbits_t newdepth) {
+        dpf0.depth(newdepth);
+        dpf1.depth(newdepth);
+    }
+
+    // The seed
+    inline node get_seed() const {
+        return std::make_tuple(dpf0.get_seed(), dpf1.get_seed());
+    }
+
+    // Do we have a precomputed expansion?
+    inline bool has_expansion() const {
+        int li_index = dpf0.maxdepth - dpf0.curdepth;
+        return dpf0.li[li_index].expansion.size() > 0;
+    }
+
+    // Get an element of the expansion
+    inline LeafNode get_expansion(address_t index) const {
+        return std::make_tuple(dpf0.get_expansion(index),
+            dpf1.get_expansion(index));
+    }
+
+    // Descend the two RDPFs in lock step
+    node descend(const node &parent, nbits_t parentdepth,
+        bit_t whichchild, size_t &aes_ops) const;
+
+    // Descend the two RDPFs in lock step to a leaf node
+    LeafNode descend_to_leaf(const node &parent, nbits_t parentdepth,
+        bit_t whichchild, size_t &aes_ops) const;
+
+    // Overloaded versions of functions to get DPF components and
+    // outputs so that the appropriate one can be selected with a
+    // parameter
+
+    // 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 void scaled_value(RegASWP &v) const {
+        std::get<0>(v) = dpf0.scaled_sum;
+        std::get<1>(v) = dpf1.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 void scaled_value(RegXSWP &v) const {
+        std::get<0>(v) = dpf0.scaled_xor;
+        std::get<1>(v) = dpf1.scaled_xor;
+    }
+
+    // Get the additive-shared unit vector entry from the leaf node
+    inline void unit(std::tuple<RegAS,RegAS> &u, const LeafNode &leaf) const {
+        std::get<0>(u) = dpf0.unit_as(std::get<0>(leaf));
+        std::get<1>(u) = dpf1.unit_as(std::get<1>(leaf));
+    }
+
+    // Get the bit-shared unit vector entry from the leaf node
+    inline void unit(std::tuple<RegXS,RegXS> &u, const LeafNode &leaf) const {
+        std::get<0>(u) = dpf0.unit_bs(std::get<0>(leaf));
+        std::get<1>(u) = dpf1.unit_bs(std::get<1>(leaf));
+    }
+
+    // For any more complex entry type, that type will handle the conversion
+    // for each DPF
+    template <typename T>
+    inline void unit(std::tuple<T,T> &u, const LeafNode &leaf) const {
+        std::get<0>(u).unit(dpf0, std::get<0>(leaf));
+        std::get<1>(u).unit(dpf1, std::get<1>(leaf));
+    }
+
+    // Get the additive-shared scaled vector entry from the leaf node
+    inline void scaled(RegASWP &s, const LeafNode &leaf) const {
+        std::get<0>(s) = dpf0.scaled_as(std::get<0>(leaf));
+        std::get<1>(s) = dpf1.scaled_as(std::get<1>(leaf));
+    }
+
+    // Get the XOR-shared scaled vector entry from the leaf node
+    inline void scaled(RegXSWP &s, const LeafNode &leaf) const {
+        std::get<0>(s) = dpf0.scaled_xs(std::get<0>(leaf));
+        std::get<1>(s) = dpf1.scaled_xs(std::get<1>(leaf));
+    }
+
+};
+
 // Streaming evaluation, to avoid taking up enough memory to store
 // an entire evaluation.  T can be RDPF, RDPFPair, or RDPFTriple.
 template <typename T>

+ 26 - 0
rdpf.tcc

@@ -1099,3 +1099,29 @@ typename RDPFPair<WIDTH>::LeafNode RDPFPair<WIDTH>::descend_to_leaf(
     C1 = dpf[1].descend_to_leaf(P1, parentdepth, whichchild, aes_ops);
     return std::make_tuple(C0,C1);
 }
+
+template <nbits_t WIDTH>
+typename RDPF2of3<WIDTH>::node RDPF2of3<WIDTH>::descend(
+    const RDPF2of3<WIDTH>::node &parent,
+    nbits_t parentdepth, bit_t whichchild,
+    size_t &aes_ops) const
+{
+    auto [P0, P1] = parent;
+    DPFnode C0, C1;
+    C0 = dpf0.descend(P0, parentdepth, whichchild, aes_ops);
+    C1 = dpf1.descend(P1, parentdepth, whichchild, aes_ops);
+    return std::make_tuple(C0,C1);
+}
+
+template <nbits_t WIDTH>
+typename RDPF2of3<WIDTH>::LeafNode RDPF2of3<WIDTH>::descend_to_leaf(
+    const RDPF2of3<WIDTH>::node &parent,
+    nbits_t parentdepth, bit_t whichchild,
+    size_t &aes_ops) const
+{
+    auto [P0, P1] = parent;
+    typename RDPF<WIDTH>::LeafNode C0, C1;
+    C0 = dpf0.descend_to_leaf(P0, parentdepth, whichchild, aes_ops);
+    C1 = dpf1.descend_to_leaf(P1, parentdepth, whichchild, aes_ops);
+    return std::make_tuple(C0,C1);
+}