|
@@ -4,6 +4,141 @@
|
|
|
#include "mpcio.hpp"
|
|
|
#include "options.hpp"
|
|
|
|
|
|
+// This file demonstrates how to implement custom ORAM wide cell types.
|
|
|
+// Such types can be structures of arbitrary numbers of RegAS and RegXS
|
|
|
+// fields. The example here imagines a cell of a binary search tree,
|
|
|
+// where you would want the key to be additively shared (so that you can
|
|
|
+// easily do comparisons), the pointers field to be XOR shared (so that
|
|
|
+// you can easily do bit operations to pack two pointers and maybe some
|
|
|
+// tree balancing information into one field) and the value doesn't
|
|
|
+// really matter, but XOR shared is usually slightly more efficient.
|
|
|
+
|
|
|
+struct Cell {
|
|
|
+ RegAS key;
|
|
|
+ RegXS pointers;
|
|
|
+ RegXS value;
|
|
|
+
|
|
|
+// Field-access macros so we can write A[i].CELL_KEY instead of
|
|
|
+// A[i].field(&Cell::key)
|
|
|
+
|
|
|
+#define CELL_KEY field(&Cell::key)
|
|
|
+#define CELL_POINTERS field(&Cell::pointers)
|
|
|
+#define CELL_VALUE field(&Cell::value)
|
|
|
+
|
|
|
+ // For debugging and checking answers
|
|
|
+ void dump() const {
|
|
|
+ printf("[%016lx %016lx %016lx]", key.share(), pointers.share(),
|
|
|
+ value.share());
|
|
|
+ }
|
|
|
+
|
|
|
+ // You'll need to be able to create a random element, and do the
|
|
|
+ // operations +=, +, -=, - (binary and unary). Note that for
|
|
|
+ // XOR-shared fields, + and - are both really XOR.
|
|
|
+
|
|
|
+ inline void randomize() {
|
|
|
+ key.randomize();
|
|
|
+ pointers.randomize();
|
|
|
+ value.randomize();
|
|
|
+ }
|
|
|
+
|
|
|
+ inline Cell &operator+=(const Cell &rhs) {
|
|
|
+ this->key += rhs.key;
|
|
|
+ this->pointers += rhs.pointers;
|
|
|
+ this->value += rhs.value;
|
|
|
+ return *this;
|
|
|
+ }
|
|
|
+
|
|
|
+ inline Cell operator+(const Cell &rhs) const {
|
|
|
+ Cell res = *this;
|
|
|
+ res += rhs;
|
|
|
+ return res;
|
|
|
+ }
|
|
|
+
|
|
|
+ inline Cell &operator-=(const Cell &rhs) {
|
|
|
+ this->key -= rhs.key;
|
|
|
+ this->pointers -= rhs.pointers;
|
|
|
+ this->value -= rhs.value;
|
|
|
+ return *this;
|
|
|
+ }
|
|
|
+
|
|
|
+ inline Cell operator-(const Cell &rhs) const {
|
|
|
+ Cell res = *this;
|
|
|
+ res -= rhs;
|
|
|
+ return res;
|
|
|
+ }
|
|
|
+
|
|
|
+ inline Cell operator-() const {
|
|
|
+ Cell res;
|
|
|
+ res.key = -this->key;
|
|
|
+ res.pointers = -this->pointers;
|
|
|
+ res.value = -this->value;
|
|
|
+ return res;
|
|
|
+ }
|
|
|
+
|
|
|
+ // Multiply each field by the local share of the corresponding field
|
|
|
+ // in the argument
|
|
|
+ inline Cell mulshare(const Cell &rhs) const {
|
|
|
+ Cell res = *this;
|
|
|
+ res.key.mulshareeq(rhs.key);
|
|
|
+ res.pointers.mulshareeq(rhs.pointers);
|
|
|
+ res.value.mulshareeq(rhs.value);
|
|
|
+ return res;
|
|
|
+ }
|
|
|
+
|
|
|
+ // You need a method to turn a leaf node of a DPF into a share of a
|
|
|
+ // unit element of your type. Typically set each RegAS to
|
|
|
+ // dpf.unit_as(leaf) and each RegXS or RegBS to dpf.unit_bs(leaf).
|
|
|
+ // Note that RegXS will extend a RegBS of 1 to the all-1s word, not
|
|
|
+ // the word with value 1. This is used for ORAM reads, where the
|
|
|
+ // same DPF is used for all the fields.
|
|
|
+ template <nbits_t WIDTH>
|
|
|
+ inline void unit(const RDPF<WIDTH> &dpf,
|
|
|
+ typename RDPF<WIDTH>::LeafNode leaf) {
|
|
|
+ key = dpf.unit_as(leaf);
|
|
|
+ pointers = dpf.unit_bs(leaf);
|
|
|
+ value = dpf.unit_bs(leaf);
|
|
|
+ }
|
|
|
+
|
|
|
+ // 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 ­ield, U idx,
|
|
|
+ const Cell &M) {
|
|
|
+ run_coroutines(shyield,
|
|
|
+ [&shape, &idx, &M] (yield_t &yield) {
|
|
|
+ Sh Sh_coro = shape.context(yield);
|
|
|
+ Sh_coro[idx].CELL_KEY += M.key;
|
|
|
+ },
|
|
|
+ [&shape, &idx, &M] (yield_t &yield) {
|
|
|
+ Sh Sh_coro = shape.context(yield);
|
|
|
+ Sh_coro[idx].CELL_POINTERS += M.pointers;
|
|
|
+ },
|
|
|
+ [&shape, &idx, &M] (yield_t &yield) {
|
|
|
+ Sh Sh_coro = shape.context(yield);
|
|
|
+ Sh_coro[idx].CELL_VALUE += M.value;
|
|
|
+ });
|
|
|
+ }
|
|
|
+};
|
|
|
+
|
|
|
+// I/O operations (for sending over the network)
|
|
|
+
|
|
|
+template <typename T>
|
|
|
+T& operator>>(T& is, Cell &x)
|
|
|
+{
|
|
|
+ is >> x.key >> x.pointers >> x.value;
|
|
|
+ return is;
|
|
|
+}
|
|
|
+
|
|
|
+template <typename T>
|
|
|
+T& operator<<(T& os, const Cell &x)
|
|
|
+{
|
|
|
+ os << x.key << x.pointers << x.value;
|
|
|
+ return os;
|
|
|
+}
|
|
|
+
|
|
|
+// This macro will define I/O on tuples of two or three of the cell type
|
|
|
+DEFAULT_TUPLE_IO(Cell)
|
|
|
+
|
|
|
void cell(MPCIO &mpcio,
|
|
|
const PRACOptions &opts, char **args);
|
|
|
|