Просмотр исходного кода

Explicit and ORAM reads, updates, and writes of wide storage cells and individual fields thereof

Ian Goldberg 1 год назад
Родитель
Сommit
5f87ed2ccf
4 измененных файлов с 266 добавлено и 149 удалено
  1. 59 12
      baltree.cpp
  2. 121 67
      duoram.hpp
  3. 81 65
      duoram.tcc
  4. 5 5
      online.cpp

+ 59 - 12
baltree.cpp

@@ -83,16 +83,24 @@ struct Cell {
         value = dpf.unit_bs(leaf);
     }
 
-    // You need a method to turn WIDTH DPFs into an element of your
-    // type, using each DPF's scaled_sum or scaled_xor value as
-    // appropriate for each field.  We need WIDTH of them because
-    // reusing a scaled value from a DPF leaks information.  The dpfs
-    // argument is a function that given 0 <= f < WIDTH, returns a
-    // reference to DPF number f.
-    inline void scaled_value(std::function<const RDPF &(size_t)> dpfs) {
-        key = dpfs(0).scaled_sum;
-        pointers = dpfs(1).scaled_xor;
-        value = dpfs(2).scaled_xor;
+    // Perform an update on each of the fields, using field-specific
+    // MemRefs constructed from the Shape shape and the index idx
+    template <typename Sh, typename U>
+    inline static void update(Sh &shape, yield_t &shyield, U idx,
+            const Cell &M) {
+        run_coroutines(shyield,
+            [&shape, &idx, &M] (yield_t &yield) {
+                Sh Sh_coro = shape.context(yield);
+                Sh_coro[idx].field(&Cell::key) += M.key;
+            },
+            [&shape, &idx, &M] (yield_t &yield) {
+                Sh Sh_coro = shape.context(yield);
+                Sh_coro[idx].field(&Cell::pointers) += M.pointers;
+            },
+            [&shape, &idx, &M] (yield_t &yield) {
+                Sh Sh_coro = shape.context(yield);
+                Sh_coro[idx].field(&Cell::value) += M.value;
+            });
     }
 };
 
@@ -138,11 +146,50 @@ void baltree (MPCIO &mpcio,
         expl_read_c.dump();
         printf("\n");
         Cell oram_read_c = A[idx];
-        printf ("oram_read_c = ");
+        printf("oram_read_c = ");
         oram_read_c.dump();
         printf("\n");
 
+        RegXS valueupdate;
+        valueupdate.set(0x4040404040404040 * tio.player());
+        RegXS pointersset;
+        pointersset.set(0x123456789abcdef0 * tio.player());
+        A[1].field(&Cell::value) += valueupdate;
+        A[3].field(&Cell::pointers) = pointersset;
+        RegXS pointval = A[0].field(&Cell::pointers);
+        printf("pointval = ");
+        pointval.dump();
         printf("\n");
-        oram.dump();
+
+        idx.set(1 * tio.player());
+        RegXS oram_value_read = A[idx].field(&Cell::value);
+        printf("oram_value_read = ");
+        oram_value_read.dump();
+        printf("\n");
+        valueupdate.set(0x8080808080808080 * tio.player());
+        A[idx].field(&Cell::value) += valueupdate;
+        idx.set(2 * tio.player());
+        A[idx].field(&Cell::value) = valueupdate;
+
+        c.key.set(0x0102030405060708 * tio.player());
+        c.pointers.set(0x1112131415161718 * tio.player());
+        c.value.set(0x2122232425262728 * tio.player());
+        A[idx] += c;
+        idx.set(3 * tio.player());
+        A[idx] = c;
+
+        printf("\n");
+
+        if (depth < 10) {
+            oram.dump();
+            auto R = A.reconstruct();
+            if (tio.player() == 0) {
+                for(size_t i=0;i<R.size();++i) {
+                    printf("\n%04lx ", i);
+                    R[i].dump();
+                }
+                printf("\n");
+            }
+        }
     });
 }

+ 121 - 67
duoram.hpp

@@ -89,16 +89,19 @@ class Duoram<T>::Shape {
     // additive-shared index (x) into an XOR-shared database (T), for
     // example.
 
-    // The parent class of the MemRef* classes, used when deferencing a
-    // Shape with A[x]
-    class MemRef;
-
     // When x is additively or XOR shared
     // U is the sharing type of the indices, while T is the sharing type
-    // of the data in the database.
-    template <typename U>
+    // of the data in the database.  If we are referencing an entire
+    // entry of type T, then the field type FT will equal T, and the
+    // field selector type FST will be nullopt_t.  If we are referencing
+    // a particular field of T, then FT will be the type of the field
+    // (RegAS or RegXS) and FST will be a pointer-to-member T::* type
+    // pointing to that field.  Sh is the specific Shape subtype used to
+    // create the MemRefS.
+    template <typename U, typename FT, typename FST, typename Sh>
     class MemRefS;
-    // When x is unshared explicit value
+    // When x is unshared explicit value.  FT and FST are as above.
+    template <typename FT, typename FST>
     class MemRefExpl;
     // When x is a vector or array of values of type U, used to denote a
     // collection of independent memory operations that can be performed
@@ -173,7 +176,8 @@ protected:
     // 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 {
+    inline std::tuple<T&,T&> get_server(size_t idx,
+        std::nullopt_t null = std::nullopt) const {
         size_t physaddr = indexmap(idx);
         return std::tie(
             duoram.p0_blind[physaddr],
@@ -183,7 +187,8 @@ protected:
     // 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 {
+    inline std::tuple<T&,T&,T&> get_comp(size_t idx,
+        std::nullopt_t null = std::nullopt) const {
         size_t physaddr = indexmap(idx);
         return std::tie(
             duoram.database[physaddr],
@@ -191,15 +196,34 @@ protected:
             duoram.peer_blinded_db[physaddr]);
     }
 
+    // Get a pair (for the server) of references to a particular field
+    // of the underlying Duoram entries at share virtual index idx.
+    // (That is, it gets duoram.p0_blind[indexmap(idx)].field, etc.)
+    template <typename FT>
+    inline std::tuple<FT&,FT&> get_server(size_t idx, FT T::*field) const {
+        size_t physaddr = indexmap(idx);
+        return std::tie(
+            duoram.p0_blind[physaddr].*field,
+            duoram.p1_blind[physaddr].*field);
+    }
+
+    // Get a triple (for the computational players) of references to a
+    // particular field to the underlying Duoram entries at share
+    // virtual index idx.  (That is, it gets
+    // duoram.database[indexmap(idx)].field, etc.)
+    template <typename FT>
+    inline std::tuple<FT&,FT&,FT&> get_comp(size_t idx, FT T::*field) const {
+        size_t physaddr = indexmap(idx);
+        return std::tie(
+            duoram.database[physaddr].*field,
+            duoram.blind[physaddr].*field,
+            duoram.peer_blinded_db[physaddr].*field);
+    }
+
 public:
     // Get the size
     inline size_t size() { return shape_size; }
 
-    // Index into this Shape in various ways
-    MemRefS<RegAS> operator[](const RegAS &idx) { return MemRefS<RegAS>(*this, idx); }
-    MemRefS<RegXS> operator[](const RegXS &idx) { return MemRefS<RegXS>(*this, idx); }
-    MemRefExpl operator[](address_t idx) { return MemRefExpl(*this, idx); }
-
     // Enable or disable explicit-only mode.  Only using [] with
     // explicit (address_t) indices are allowed in this mode.  Using []
     // with RegAS or RegXS indices will automatically turn off this
@@ -265,15 +289,42 @@ public:
         return Flat(*this, this->tio, new_yield);
     }
 
-    // Generate independent memory references for this Flat
+    // Index into this Flat in various ways
+    typename Duoram::Shape::template MemRefS<RegAS,T,std::nullopt_t,Flat>
+            operator[](const RegAS &idx) {
+        typename Duoram<T>::Shape::
+            template MemRefS<RegAS,T,std::nullopt_t,Flat>
+            res(*this, idx, std::nullopt);
+        return res;
+    }
+    typename Duoram::Shape::template MemRefS<RegXS,T,std::nullopt_t,Flat>
+            operator[](const RegXS &idx) {
+        typename Duoram<T>::Shape::
+            template MemRefS<RegXS,T,std::nullopt_t,Flat>
+            res(*this, idx, std::nullopt);
+        return res;
+    }
+    typename Duoram::Shape::template MemRefExpl<T,std::nullopt_t>
+            operator[](address_t idx) {
+        typename Duoram<T>::Shape::
+            template MemRefExpl<T,std::nullopt_t>
+            res(*this, idx, std::nullopt);
+        return res;
+    }
     template <typename U>
-    Duoram::Shape::MemRefInd<U, Flat> indep(const std::vector<U> &indcs) {
-        typename Duoram<T>::Shape::template MemRefInd<U,Flat> res(*this, indcs);
+    Duoram::Shape::MemRefInd<U, Flat>
+            operator[](const std::vector<U> &indcs) {
+        typename Duoram<T>::Shape::
+            template MemRefInd<U,Flat>
+            res(*this, indcs);
         return res;
     }
     template <typename U, size_t N>
-    Duoram::Shape::MemRefInd<U, Flat> indep(const std::array<U,N> &indcs) {
-        typename Duoram<T>::Shape::template MemRefInd<U,Flat> res(*this, indcs);
+    Duoram::Shape::MemRefInd<U, Flat>
+            operator[](const std::array<U,N> &indcs) {
+        typename Duoram<T>::Shape::
+            template MemRefInd<U,Flat>
+            res(*this, indcs);
         return res;
     }
 
@@ -301,89 +352,92 @@ public:
     RegAS obliv_binary_search(RegAS &target);
 };
 
-// The parent class of shared memory references
-template <typename T>
-class Duoram<T>::Shape::MemRef {
-protected:
-    Shape &shape;
-
-    MemRef(Shape &shape): shape(shape) {}
-
-public:
-
-    // Oblivious read from a shared index of Duoram memory
-    virtual operator T() = 0;
-
-    // Oblivious update to a shared index of Duoram memory
-    virtual MemRef &operator+=(const T& M) = 0;
-
-    // Oblivious write to a shared index of Duoram memory
-    virtual MemRef &operator=(const T& M) = 0;
-
-    // Convenience function
-    MemRef &operator-=(const T& M) { *this += (-M); return *this; }
-
-};
-
 // An additive or XOR shared memory reference.  You get one of these
 // from a Shape A and an additively shared RegAS index x, or an XOR
 // shared RegXS index x, with A[x].  Then you perform operations on this
 // object, which do the Duoram operations.  As above, T is the sharing
 // type of the data in the database, while U is the sharing type of the
-// index used to create this memory reference.
+// index used to create this memory reference.  If we are referencing an
+// entire entry of type T, then the field type FT will equal T, and the
+// field selector type FST will be nullopt_t.  If we are referencing a
+// particular field of T, then FT will be the type of the field (RegAS
+// or RegXS) and FST will be a pointer-to-member T::* type pointing to
+// that field.  Sh is the specific Shape subtype used to create the
+// MemRefS.
 
-template <typename T> template <typename U>
-class Duoram<T>::Shape::MemRefS : public Duoram<T>::Shape::MemRef {
+template <typename T>
+template <typename U, typename FT, typename FST, typename Sh>
+class Duoram<T>::Shape::MemRefS {
+    Sh &shape;
     U idx;
+    FST fieldsel;
 
 private:
     // Oblivious update to a shared index of Duoram memory, only for
-    // ST = RegAS or RegXS
-    template <typename ST>
-    MemRefS<U> &oram_update(const ST& M, const prac_template_true&);
+    // FT = RegAS or RegXS
+    MemRefS<U,FT,FST,Sh> &oram_update(const FT& M, const prac_template_true&);
     // Oblivious update to a shared index of Duoram memory, for
-    // ST not RegAS or RegXS
-    template <typename ST>
-    MemRefS<U> &oram_update(const ST& M, const prac_template_false&);
+    // FT not RegAS or RegXS
+    MemRefS<U,FT,FST,Sh> &oram_update(const FT& M, const prac_template_false&);
 
 public:
-    MemRefS<U>(Shape &shape, const U &idx) :
-        MemRef(shape), idx(idx) {}
+    MemRefS<U,FT,FST,Sh>(Sh &shape, const U &idx, FST fieldsel) :
+        shape(shape), idx(idx), fieldsel(fieldsel) {}
+
+    // Create a MemRefExpl for accessing a partcular field of T
+    template <typename SFT>
+    MemRefS<U,SFT,SFT T::*,Sh> field(SFT T::*subfieldsel) {
+        auto res = MemRefS<U,SFT,SFT T::*,Sh>(this->shape, idx, subfieldsel);
+        return res;
+    }
 
     // Oblivious read from a shared index of Duoram memory
-    operator T() override;
+    operator FT();
 
     // Oblivious update to a shared index of Duoram memory
-    MemRefS<U> &operator+=(const T& M) override;
+    MemRefS<U,FT,FST,Sh> &operator+=(const FT& M);
 
     // Oblivious write to a shared index of Duoram memory
-    MemRefS<U> &operator=(const T& M) override;
+    MemRefS<U,FT,FST,Sh> &operator=(const FT& M);
 };
 
 // An explicit memory reference.  You get one of these from a Shape A
 // and an address_t index x with A[x].  Then you perform operations on
 // this object, which update the Duoram state without performing Duoram
-// operations.
-
-template <typename T>
-class Duoram<T>::Shape::MemRefExpl : public Duoram<T>::Shape::MemRef {
+// operations.  If we are referencing an entire entry of type T, then
+// the field type FT will equal T, and the field selector type FST will
+// be nullopt_t.  If we are referencing a particular field of T, then FT
+// will be the type of the field (RegAS or RegXS) and FST will be a
+// pointer-to-member T::* type pointing to that field.
+
+template <typename T> template <typename FT, typename FST>
+class Duoram<T>::Shape::MemRefExpl {
+    Shape &shape;
     address_t idx;
+    FST fieldsel;
 
 public:
-    MemRefExpl(Shape &shape, address_t idx) :
-        MemRef(shape), idx(idx) {}
+    MemRefExpl(Shape &shape, address_t idx, FST fieldsel) :
+        shape(shape), idx(idx), fieldsel(fieldsel) {}
+
+    // Create a MemRefExpl for accessing a partcular field of T
+    template <typename SFT>
+    MemRefExpl<SFT,SFT T::*> field(SFT T::*subfieldsel) {
+        auto res = MemRefExpl<SFT,SFT T::*>(this->shape, idx, subfieldsel);
+        return res;
+    }
 
     // Explicit read from a given index of Duoram memory
-    operator T() override;
+    operator FT();
 
     // Explicit update to a given index of Duoram memory
-    MemRefExpl &operator+=(const T& M) override;
+    MemRefExpl &operator+=(const FT& M);
 
     // Explicit write to a given index of Duoram memory
-    MemRefExpl &operator=(const T& M) override;
+    MemRefExpl &operator=(const FT& M);
 
     // Convenience function
-    MemRefExpl &operator-=(const T& M) { *this += (-M); return *this; }
+    MemRefExpl &operator-=(const FT& M) { *this += (-M); return *this; }
 };
 
 // A collection of independent memory references that can be processed

+ 81 - 65
duoram.tcc

@@ -254,12 +254,20 @@ inline address_t IfRegXS<RegXS>(address_t val) { return val; }
 
 // Oblivious read from an additively or XOR shared index of Duoram memory
 // T is the sharing type of the _values_ in the database; U is the
-// sharing type of the _indices_ in the database.
-template <typename T> template <typename U>
-Duoram<T>::Shape::MemRefS<U>::operator T()
+// sharing type of the _indices_ in the database.  If we are referencing
+// an entire entry of type T, then the field type FT will equal T, and
+// the field selector type FST will be nullopt_t.  If we are referencing
+// a particular field of T, then FT will be the type of the field (RegAS
+// or RegXS) and FST will be a pointer-to-member T::* type pointing to
+// that field.  Sh is the specific Shape subtype used to create the
+// MemRefS.
+
+template <typename T>
+template <typename U,typename FT,typename FST,typename Sh>
+Duoram<T>::Shape::MemRefS<U,FT,FST,Sh>::operator FT()
 {
-    T res;
-    Shape &shape = this->shape;
+    FT res;
+    Sh &shape = this->shape;
     shape.explicitonly(false);
     int player = shape.tio.player();
     if (player < 2) {
@@ -293,21 +301,21 @@ Duoram<T>::Shape::MemRefS<U>::operator T()
         ParallelEval pe(dp, IfRegAS<U>(indshift), IfRegXS<U>(indshift),
             shape.shape_size, shape.tio.cpu_nthreads(),
             shape.tio.aes_ops());
-        T init;
-        res = pe.reduce(init, [&dp, &shape] (int thread_num, address_t i,
-                const RDPFPair::node &leaf) {
+        FT init;
+        res = pe.reduce(init, [this, &dp, &shape] (int thread_num,
+                address_t i, const RDPFPair::node &leaf) {
             // The values from the two DPFs, which will each be of type T
-            auto [V0, V1] = dp.unit<T>(leaf);
+            auto [V0, V1] = dp.unit<FT>(leaf);
             // References to the appropriate cells in our database, our
             // blind, and our copy of the peer's blinded database
-            auto [DB, BL, PBD] = shape.get_comp(i);
+            auto [DB, BL, PBD] = shape.get_comp(i, fieldsel);
             return (DB + PBD).mulshare(V0) - BL.mulshare(V1-V0);
         });
 
         shape.yield();
 
         // Receive the cancellation term from the server
-        T gamma;
+        FT gamma;
         shape.tio.iostream_server() >> gamma;
         res += gamma;
     } else {
@@ -325,23 +333,23 @@ Duoram<T>::Shape::MemRefS<U>::operator T()
         auto indshift = combine(p0indoffset, p1indoffset, shape.addr_size);
 
         // Evaluate the DPFs to compute the cancellation terms
-        std::tuple<T,T> init, gamma;
+        std::tuple<FT,FT> init, gamma;
         ParallelEval pe(dp, IfRegAS<U>(indshift), IfRegXS<U>(indshift),
             shape.shape_size, shape.tio.cpu_nthreads(),
             shape.tio.aes_ops());
-        gamma = pe.reduce(init, [&dp, &shape] (int thread_num, address_t i,
-                const RDPFPair::node &leaf) {
-            // The values from the two DPFs, each of type T
-            auto [V0, V1] = dp.unit<T>(leaf);
+        gamma = pe.reduce(init, [this, &dp, &shape] (int thread_num,
+                address_t i, const RDPFPair::node &leaf) {
+            // The values from the two DPFs, each of type FT
+            auto [V0, V1] = dp.unit<FT>(leaf);
 
             // shape.get_server(i) returns a pair of references to the
             // appropriate cells in the two blinded databases
-            auto [BL0, BL1] = shape.get_server(i);
+            auto [BL0, BL1] = shape.get_server(i, fieldsel);
             return std::make_tuple(-BL0.mulshare(V1), -BL1.mulshare(V0));
         });
 
         // Choose a random blinding factor
-        T rho;
+        FT rho;
         rho.randomize();
 
         std::get<0>(gamma) += rho;
@@ -357,13 +365,14 @@ Duoram<T>::Shape::MemRefS<U>::operator T()
 }
 
 // Oblivious update to a shared index of Duoram memory, only for
-// ST = RegAS or RegXS
-template <typename T> template <typename U> template <typename ST>
-typename Duoram<T>::Shape::template MemRefS<U>
-    &Duoram<T>::Shape::MemRefS<U>::oram_update(const ST& M,
+// FT = RegAS or RegXS.  The template parameters are as above.
+template <typename T>
+template <typename U, typename FT, typename FST, typename Sh>
+typename Duoram<T>::Shape::template MemRefS<U,FT,FST,Sh>
+    &Duoram<T>::Shape::MemRefS<U,FT,FST,Sh>::oram_update(const FT& M,
         const prac_template_true &)
 {
-    Shape &shape = this->shape;
+    Sh &shape = this->shape;
     shape.explicitonly(false);
     int player = shape.tio.player();
     if (player < 2) {
@@ -375,7 +384,7 @@ typename Duoram<T>::Shape::template MemRefS<U>
         U indoffset = dt.target<U>();
         indoffset -= idx;
         auto Moffset = std::make_tuple(M, M, M);
-        Moffset -= dt.scaled_value<ST>();
+        Moffset -= dt.scaled_value<FT>();
 
         // Send them to the peer, and everything except the first offset
         // to the server
@@ -389,7 +398,7 @@ typename Duoram<T>::Shape::template MemRefS<U>
 
         // Receive the above from the peer
         U peerindoffset;
-        std::tuple<ST,ST,ST> peerMoffset;
+        std::tuple<FT,FT,FT> peerMoffset;
         shape.tio.recv_peer(&peerindoffset, BITBYTES(shape.addr_size));
         shape.tio.iostream_peer() >> peerMoffset;
 
@@ -402,13 +411,14 @@ typename Duoram<T>::Shape::template MemRefS<U>
             shape.shape_size, shape.tio.cpu_nthreads(),
             shape.tio.aes_ops());
         int init = 0;
-        pe.reduce(init, [&dt, &shape, &Mshift, player] (int thread_num,
+        pe.reduce(init, [this, &dt, &shape, &Mshift, player] (int thread_num,
                 address_t i, const RDPFTriple::node &leaf) {
             // The values from the three DPFs
-            auto [V0, V1, V2] = dt.scaled<ST>(leaf) + dt.unit<ST>(leaf) * Mshift;
+            auto [V0, V1, V2] =
+                dt.scaled<FT>(leaf) + dt.unit<FT>(leaf) * Mshift;
             // References to the appropriate cells in our database, our
             // blind, and our copy of the peer's blinded database
-            auto [DB, BL, PBD] = shape.get_comp(i);
+            auto [DB, BL, PBD] = shape.get_comp(i,fieldsel);
             DB += V0;
             if (player == 0) {
                 BL -= V1;
@@ -424,7 +434,7 @@ typename Duoram<T>::Shape::template MemRefS<U>
 
         RDPFPair dp = shape.tio.rdpfpair(shape.yield, shape.addr_size);
         U p0indoffset, p1indoffset;
-        std::tuple<ST,ST> p0Moffset, p1Moffset;
+        std::tuple<FT,FT> p0Moffset, p1Moffset;
 
         shape.yield();
 
@@ -442,14 +452,14 @@ typename Duoram<T>::Shape::template MemRefS<U>
             shape.shape_size, shape.tio.cpu_nthreads(),
             shape.tio.aes_ops());
         int init = 0;
-        pe.reduce(init, [&dp, &shape, &Mshift] (int thread_num,
+        pe.reduce(init, [this, &dp, &shape, &Mshift] (int thread_num,
                 address_t i, const RDPFPair::node &leaf) {
             // The values from the two DPFs
-            auto V = dp.scaled<ST>(leaf) + dp.unit<ST>(leaf) * Mshift;
+            auto V = dp.scaled<FT>(leaf) + dp.unit<FT>(leaf) * Mshift;
             // shape.get_server(i) returns a pair of references to the
             // appropriate cells in the two blinded databases, so we can
             // subtract the pair directly.
-            shape.get_server(i) -= V;
+            shape.get_server(i,fieldsel) -= V;
             return 0;
         });
     }
@@ -457,30 +467,36 @@ typename Duoram<T>::Shape::template MemRefS<U>
 }
 
 // Oblivious update to a shared index of Duoram memory, only for
-// ST not RegAS or RegXS
-template <typename T> template <typename U> template <typename ST>
-typename Duoram<T>::Shape::template MemRefS<U>
-    &Duoram<T>::Shape::MemRefS<U>::oram_update(const ST& M,
+// FT not RegAS or RegXS.  The template parameters are as above.
+template <typename T>
+template <typename U, typename FT, typename FST, typename Sh>
+typename Duoram<T>::Shape::template MemRefS<U,FT,FST,Sh>
+    &Duoram<T>::Shape::MemRefS<U,FT,FST,Sh>::oram_update(const FT& M,
         const prac_template_false &)
 {
+    T::update(shape, shape.yield, idx, M);
     return *this;
 }
 
-// Oblivious update to an additively or XOR shared index of Duoram memory
-template <typename T> template <typename U>
-typename Duoram<T>::Shape::template MemRefS<U>
-    &Duoram<T>::Shape::MemRefS<U>::operator+=(const T& M)
+// Oblivious update to an additively or XOR shared index of Duoram
+// memory. The template parameters are as above.
+template <typename T>
+template <typename U, typename FT, typename FST, typename Sh>
+typename Duoram<T>::Shape::template MemRefS<U,FT,FST,Sh>
+    &Duoram<T>::Shape::MemRefS<U,FT,FST,Sh>::operator+=(const FT& M)
 {
-    return oram_update(M, prac_basic_Reg_S<T>());
+    return oram_update(M, prac_basic_Reg_S<FT>());
 }
 
-// Oblivious write to an additively or XOR shared index of Duoram memory
-template <typename T> template <typename U>
-typename Duoram<T>::Shape::template MemRefS<U>
-    &Duoram<T>::Shape::MemRefS<U>::operator=(const T& M)
+// Oblivious write to an additively or XOR shared index of Duoram
+// memory. The template parameters are as above.
+template <typename T>
+template <typename U, typename FT, typename FST, typename Sh>
+typename Duoram<T>::Shape::template MemRefS<U,FT,FST,Sh>
+    &Duoram<T>::Shape::MemRefS<U,FT,FST,Sh>::operator=(const FT& M)
 {
-    T oldval = *this;
-    T update = M - oldval;
+    FT oldval = *this;
+    FT update = M - oldval;
     *this += update;
     return *this;
 }
@@ -527,22 +543,22 @@ void Duoram<RegAS>::Flat::osort(const U &idx1, const V &idx2, bool dir)
 }
 
 // Explicit read from a given index of Duoram memory
-template <typename T>
-Duoram<T>::Shape::MemRefExpl::operator T()
+template <typename T> template <typename FT, typename FST>
+Duoram<T>::Shape::MemRefExpl<FT,FST>::operator FT()
 {
     Shape &shape = this->shape;
-    T res;
+    FT res;
     int player = shape.tio.player();
     if (player < 2) {
-        res = std::get<0>(shape.get_comp(idx));
+        res = std::get<0>(shape.get_comp(idx, fieldsel));
     }
     return res;  // The server will always get 0
 }
 
 // Explicit update to a given index of Duoram memory
-template <typename T>
-typename Duoram<T>::Shape::MemRefExpl
-    &Duoram<T>::Shape::MemRefExpl::operator+=(const T& M)
+template <typename T> template <typename FT, typename FST>
+typename Duoram<T>::Shape::MemRefExpl<FT,FST>
+    &Duoram<T>::Shape::MemRefExpl<FT,FST>::operator+=(const FT& M)
 {
     Shape &shape = this->shape;
     int player = shape.tio.player();
@@ -550,7 +566,7 @@ typename Duoram<T>::Shape::MemRefExpl
     // blinds and the blinded DB when we leave explicit-only mode.
     if (shape.explicitmode) {
         if (player < 2) {
-            auto [ DB, BL, PBD ] = shape.get_comp(idx);
+            auto [ DB, BL, PBD ] = shape.get_comp(idx, fieldsel);
             DB += M;
         }
         return *this;
@@ -559,7 +575,7 @@ typename Duoram<T>::Shape::MemRefExpl
         // Computational players do this
 
         // Pick a blinding factor
-        T blind;
+        FT blind;
         blind.randomize();
 
         // Send the blind to the server, and the blinded value to the
@@ -570,11 +586,11 @@ typename Duoram<T>::Shape::MemRefExpl
         shape.yield();
 
         // Receive the peer's blinded value
-        T peerblinded;
+        FT peerblinded;
         shape.tio.iostream_peer() >> peerblinded;
 
         // Our database, our blind, the peer's blinded database
-        auto [ DB, BL, PBD ] = shape.get_comp(idx);
+        auto [ DB, BL, PBD ] = shape.get_comp(idx, fieldsel);
         DB += M;
         BL += blind;
         PBD += peerblinded;
@@ -584,12 +600,12 @@ typename Duoram<T>::Shape::MemRefExpl
         shape.yield();
 
         // Receive the updates to the blinds
-        T p0blind, p1blind;
+        FT p0blind, p1blind;
         shape.tio.iostream_p0() >> p0blind;
         shape.tio.iostream_p1() >> p1blind;
 
         // The two computational parties' blinds
-        auto [ BL0, BL1 ] = shape.get_server(idx);
+        auto [ BL0, BL1 ] = shape.get_server(idx, fieldsel);
         BL0 += p0blind;
         BL1 += p1blind;
     }
@@ -597,12 +613,12 @@ typename Duoram<T>::Shape::MemRefExpl
 }
 
 // Explicit write to a given index of Duoram memory
-template <typename T>
-typename Duoram<T>::Shape::MemRefExpl
-    &Duoram<T>::Shape::MemRefExpl::operator=(const T& M)
+template <typename T> template <typename FT, typename FST>
+typename Duoram<T>::Shape::MemRefExpl<FT,FST>
+    &Duoram<T>::Shape::MemRefExpl<FT,FST>::operator=(const FT& M)
 {
-    T oldval = *this;
-    T update = M - oldval;
+    FT oldval = *this;
+    FT update = M - oldval;
     *this += update;
     return *this;
 }

+ 5 - 5
online.cpp

@@ -616,9 +616,9 @@ static void duoram_test(MPCIO &mpcio,
 
         // Simultaneous independent reads
         printf("3 independent reading\n");
-        std::vector<T> Av = A.indep(std::array {
+        std::vector<T> Av = A[std::array {
             aidx, aidx2, aidx3
-        });
+        }];
 
         // Simultaneous independent updates
         T Aw1, Aw2, Aw3;
@@ -626,7 +626,7 @@ static void duoram_test(MPCIO &mpcio,
         Aw2.set(0x202020202020202 * tio.player());
         Aw3.set(0x303030303030303 * tio.player());
         printf("3 independent updating\n");
-        A.indep(std::array { aidx, aidx2, aidx3 }) -=
+        A[std::array { aidx, aidx2, aidx3 }] -=
             std::array { Aw1, Aw2, Aw3 };
 
         if (depth <= 10) {
@@ -713,7 +713,7 @@ static void duoram(MPCIO &mpcio,
         mpcio.reset_stats();
         tio.reset_lamport();
         // Read all the entries in the list at once
-        std::vector<T> read_outputs = A.indep(list_indices);
+        std::vector<T> read_outputs = A[list_indices];
         tio.sync_lamport();
         mpcio.dump_stats(std::cout);
 
@@ -730,7 +730,7 @@ static void duoram(MPCIO &mpcio,
             indep_values.push_back(read_outputs[i]+one);
         }
         // Update all the indices at once
-        A.indep(indep_indices) += indep_values;
+        A[indep_indices] += indep_values;
         tio.sync_lamport();
         mpcio.dump_stats(std::cout);