Browse Source

The Path Shape

A Path is a Shape that represents a path from the root of a complete
binary tree down to a given node.
Ian Goldberg 1 year ago
parent
commit
9bd6655f2e
4 changed files with 188 additions and 0 deletions
  1. 2 0
      duoram.hpp
  2. 72 0
      online.cpp
  3. 93 0
      shapes.hpp
  4. 21 0
      shapes.tcc

+ 2 - 0
duoram.hpp

@@ -61,6 +61,7 @@ public:
     class Flat;
     class Pad;
     class Stride;
+    class Path;
 
     // Oblivious indices for use in related-index ORAM accesses
     template <typename U, nbits_t WIDTH>
@@ -92,6 +93,7 @@ class Duoram<T>::Shape {
     friend class Flat;
     friend class Pad;
     friend class Stride;
+    friend class Path;
 
     template <typename U, nbits_t WIDTH>
     friend class OblivIndex;

+ 72 - 0
online.cpp

@@ -1442,6 +1442,71 @@ static void related(MPCIO &mpcio,
     });
 }
 
+template <typename T>
+static void path(MPCIO &mpcio,
+    const PRACOptions &opts, char **args)
+{
+    nbits_t depth = 5;
+
+    // The depth of the (complete) binary tree
+    if (*args) {
+        depth = atoi(*args);
+        ++args;
+    }
+    // The target node
+    size_t target_node = 3 << (depth-1);
+    if (*args) {
+        target_node = atoi(*args);
+        ++args;
+    }
+
+    MPCTIO tio(mpcio, 0, opts.num_threads);
+    run_coroutines(tio, [&mpcio, &tio, depth, target_node] (yield_t &yield) {
+        size_t size = size_t(1)<<(depth+1);
+        Duoram<T> oram(tio.player(), size);
+        auto A = oram.flat(tio, yield);
+
+        // Initialize A with words with sequential top and bottom halves
+        // (just so we can more easily eyeball the right answers)
+        A.init([] (size_t i) { return i * 0x100000001; } );
+
+        // We use this layout for the tree:
+        // A[0] is unused
+        // A[1] is the root (layer 0)
+        // A[2..3] is layer 1
+        // A[4..7] is layer 2
+        // ...
+        // A[(1<<j)..((2<<j)-1)] is layer j
+        //
+        // So the parent of x is at location (x/2) and the children of x
+        // are at locations 2*x and 2*x+1
+
+        // Create a Path from the root to the target node
+        typename Duoram<T>::Path P(A, tio, yield, target_node);
+
+        // Re-initialize that path to something recognizable
+        P.init([] (size_t i) { return 0xff + i * 0x1000000010000; } );
+
+        // ORAM update along that path
+        RegXS idx;
+        idx.set(tio.player() * arc4random_uniform(P.size()));
+        T val;
+        val.set(tio.player() * 0xaaaa00000000);
+        P[idx] += val;
+
+        // Check the answer
+        auto check = A.reconstruct();
+        if (depth <= 10) {
+            oram.dump();
+            if (tio.player() == 0) {
+                for (address_t i=0;i<size;++i) {
+                    printf("%04x %016lx\n", i, check[i].share());
+                }
+            }
+        }
+    });
+}
+
 void online_main(MPCIO &mpcio, const PRACOptions &opts, char **args)
 {
     MPCTIO tio(mpcio, 0);
@@ -1538,6 +1603,13 @@ void online_main(MPCIO &mpcio, const PRACOptions &opts, char **args)
         } else {
             related<RegAS>(mpcio, opts, args);
         }
+    } else if (!strcmp(*args, "path")) {
+        ++args;
+        if (opts.use_xor_db) {
+            path<RegXS>(mpcio, opts, args);
+        } else {
+            path<RegAS>(mpcio, opts, args);
+        }
     } else if (!strcmp(*args, "cell")) {
         ++args;
         cell(mpcio, opts, args);

+ 93 - 0
shapes.hpp

@@ -204,6 +204,99 @@ public:
     }
 };
 
+
+// A Path is a Shape that represents a path from the root of a complete
+// binary tree down to a given node.
+
+// We assume this layout for the tree (the _parent_ shape of the Path):
+// A[0] is unused
+// A[1] is the root (layer 0)
+// A[2..3] is layer 1
+// A[4..7] is layer 2
+// ...
+// A[(1<<j)..((2<<j)-1)] is layer j
+//
+// So the parent of x is at location (x/2) and the children of x
+// are at locations 2*x and 2*x+1
+
+template <typename T>
+class Duoram<T>::Path : public Duoram<T>::Shape {
+    size_t target_node;
+
+    inline size_t indexmap(size_t idx) const override {
+        size_t paridx = target_node >> (this->shape_size - idx - 1);
+        return paridx;
+    }
+
+public:
+    // Constructor
+    Path(Shape &parent, MPCTIO &tio, yield_t &yield,
+        size_t target_node);
+
+    // Copy the given Path except for the tio and yield
+    Path(const Path &copy_from, MPCTIO &tio, yield_t &yield) :
+        Shape(copy_from, tio, yield),
+        target_node(copy_from.target_node) {}
+
+    // Update the context (MPCTIO and yield if you've started a new
+    // thread, or just yield if you've started a new coroutine in the
+    // same thread).  Returns a new Shape with an updated context.
+    Path context(MPCTIO &new_tio, yield_t &new_yield) const {
+        return Path(*this, new_tio, new_yield);
+    }
+    Path context(yield_t &new_yield) const {
+        return Path(*this, this->tio, new_yield);
+    }
+
+    // Index into this Path in various ways
+    typename Duoram::Shape::template MemRefS<RegAS,T,std::nullopt_t,Path,1>
+            operator[](const RegAS &idx) {
+        typename Duoram<T>::Shape::
+            template MemRefS<RegAS,T,std::nullopt_t,Path,1>
+            res(*this, idx, std::nullopt);
+        return res;
+    }
+    typename Duoram::Shape::template MemRefS<RegXS,T,std::nullopt_t,Path,1>
+            operator[](const RegXS &idx) {
+        typename Duoram<T>::Shape::
+            template MemRefS<RegXS,T,std::nullopt_t,Path,1>
+            res(*this, idx, std::nullopt);
+        return res;
+    }
+    template <typename U, nbits_t WIDTH>
+    typename Duoram::Shape::template MemRefS<U,T,std::nullopt_t,Path,WIDTH>
+            operator[](OblivIndex<U,WIDTH> &obidx) {
+        typename Duoram<T>::Shape::
+            template MemRefS<RegXS,T,std::nullopt_t,Path,WIDTH>
+            res(*this, obidx, 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, Path>
+            operator[](const std::vector<U> &indcs) {
+        typename Duoram<T>::Shape::
+            template MemRefInd<U,Path>
+            res(*this, indcs);
+        return res;
+    }
+    template <typename U, size_t N>
+    Duoram::Shape::MemRefInd<U, Path>
+            operator[](const std::array<U,N> &indcs) {
+        typename Duoram<T>::Shape::
+            template MemRefInd<U,Path>
+            res(*this, indcs);
+        return res;
+    }
+};
+
+
 #include "shapes.tcc"
 
 #endif

+ 21 - 0
shapes.tcc

@@ -61,4 +61,25 @@ Duoram<T>::Stride::Stride(Shape &parent, MPCTIO &tio, yield_t &yield,
     this->set_shape_size(numelements);
 }
 
+// Constructor for the Path shape.
+template <typename T>
+Duoram<T>::Path::Path(Shape &parent, MPCTIO &tio, yield_t &yield,
+    size_t target_node) :
+    Shape(parent, parent.duoram, tio, yield)
+{
+    size_t parentsize = parent.size();
+    assert(target_node > 0 && target_node < parentsize);
+    this->target_node = target_node;
+
+    // How many nodes are there on the path from the root (index 1) to
+    // the target node?  Recall that the parent of the node at index x
+    // is just the node at index (x>>1).
+    size_t path_num_nodes = 1, cur_node = target_node;
+    while (cur_node > 1) {
+        cur_node >>= 1;
+        ++path_num_nodes;
+    }
+    this->set_shape_size(path_num_nodes);
+}
+
 #endif