duoram.hpp 26 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709
  1. #ifndef __DUORAM_HPP__
  2. #define __DUORAM_HPP__
  3. #include <optional>
  4. #include <functional>
  5. #include "types.hpp"
  6. #include "mpcio.hpp"
  7. #include "coroutine.hpp"
  8. // Implementation of the 3-party protocols described in:
  9. // Adithya Vadapalli, Ryan Henry, Ian Goldberg, "Duoram: A
  10. // Bandwidth-Efficient Distributed ORAM for 2- and 3-Party Computation".
  11. // A Duoram object is like physical memory: it's just a flat address
  12. // space, and you can't access it directly. Instead, you need to access
  13. // it through a "Shape", such as Flat, Tree, Path, etc. Shapes can be
  14. // nested, so you can have a Path of a Subtree of a Tree sitting on the
  15. // base Duoram. Each Shape's parent must remain in scope (references to
  16. // it must remain valid) for the lifetime of the child Shapre. Each
  17. // shape is bound to a context, which is a thread-specific MPCTIO and a
  18. // coroutine-specific yield_t. If you launch new threads and/or
  19. // coroutines, you'll need to make a copy of the current Shape for your
  20. // new context, and call context() on it. Be sure not to call context()
  21. // on a Shape shared with other threads or coroutines.
  22. // This is templated, because you can have a Duoram of additively shared
  23. // (RegAS) or XOR shared (RegXS) elements, or more complex cell types
  24. // (see cell.hpp for example).
  25. template <typename T>
  26. class Duoram {
  27. // The computational parties have three vectors: the share of the
  28. // database itself, the party's own blinding factors for its
  29. // database share, and the _other_ computational party's blinded
  30. // database share (its database share plus its blind).
  31. // The player number (0 and 1 for the computational parties and 2
  32. // for the server) and the size of the Duoram
  33. int player;
  34. size_t oram_size;
  35. // The server has two vectors: a copy of each computational party's
  36. // blind. The database vector will remain empty.
  37. std::vector<T> database; // computational parties only
  38. std::vector<T> blind; // computational parties use this name
  39. std::vector<T> &p0_blind; // server uses this name
  40. std::vector<T> peer_blinded_db; // computational parties
  41. std::vector<T> &p1_blind; // server
  42. public:
  43. // The type of this Duoram
  44. using type = T;
  45. // The different Shapes are subclasses of this inner class
  46. class Shape;
  47. // These are the different Shapes that exist
  48. class Flat;
  49. class Pad;
  50. class Stride;
  51. class Path;
  52. // Oblivious indices for use in related-index ORAM accesses
  53. template <typename U, nbits_t WIDTH>
  54. class OblivIndex;
  55. // Pass the player number and desired size
  56. Duoram(int player, size_t size);
  57. // Get the size
  58. inline size_t size() { return oram_size; }
  59. // Get the basic Flat shape for this Duoram
  60. Flat flat(MPCTIO &tio, yield_t &yield, size_t start = 0,
  61. size_t len = 0) {
  62. return Flat(*this, tio, yield, start, len);
  63. }
  64. // For debugging; print the contents of the Duoram to stdout
  65. void dump() const;
  66. };
  67. // The parent class of all Shapes. This is an abstract class that
  68. // cannot itself be instantiated.
  69. template <typename T>
  70. class Duoram<T>::Shape {
  71. // Subclasses should be able to access _other_ Shapes'
  72. // get_{comp,server} functions
  73. friend class Flat;
  74. friend class Pad;
  75. friend class Stride;
  76. friend class Path;
  77. template <typename U, nbits_t WIDTH>
  78. friend class OblivIndex;
  79. // When you index into a shape (A[x]), you get one of these types,
  80. // depending on the type of x (the index), _not_ on the type T (the
  81. // underlying type of the Duoram). That is, you can have an
  82. // additive-shared index (x) into an XOR-shared database (T), for
  83. // example.
  84. // When x is additively or XOR shared
  85. // U is the sharing type of the indices, while T is the sharing type
  86. // of the data in the database. If we are referencing an entire
  87. // entry of type T, then the field type FT will equal T, and the
  88. // field selector type FST will be nullopt_t. If we are referencing
  89. // a particular field of T, then FT will be the type of the field
  90. // (RegAS or RegXS) and FST will be a pointer-to-member T::* type
  91. // pointing to that field. Sh is the specific Shape subtype used to
  92. // create the MemRefS. WIDTH is the RDPF width to use.
  93. template <typename U, typename FT, typename FST, typename Sh, nbits_t WIDTH>
  94. class MemRefS;
  95. // When x is unshared explicit value. FT and FST are as above.
  96. template <typename FT, typename FST>
  97. class MemRefExpl;
  98. // When x is a vector or array of values of type U, used to denote a
  99. // collection of independent memory operations that can be performed
  100. // simultaneously. Sh is the specific Shape subtype used to create
  101. // the MemRefInd.
  102. template <typename U, typename Sh>
  103. class MemRefInd;
  104. protected:
  105. // A reference to the parent shape. As with ".." in the root
  106. // directory of a filesystem, the topmost shape is indicated by
  107. // having parent = *this.
  108. const Shape &parent;
  109. // A reference to the backing physical storage
  110. Duoram &duoram;
  111. // The size of this shape
  112. size_t shape_size;
  113. // The number of bits needed to address this shape (the number of
  114. // bits in shape_size-1)
  115. nbits_t addr_size;
  116. // And a mask with the low addr_size bits set
  117. address_t addr_mask;
  118. // The Shape's context (MPCTIO and yield_t)
  119. MPCTIO &tio;
  120. yield_t &yield;
  121. // If you enable explicit-only mode, sending updates of your blind
  122. // to the server and of your blinded database to your peer will be
  123. // temporarily disabled. When you disable it (which will happen
  124. // automatically at the next ORAM read or write, or you can do it
  125. // explicitly), new random blinds will be chosen for the whole
  126. // Shape, and the blinds sent to the server, and the blinded
  127. // database sent to the peer.
  128. bool explicitmode;
  129. // A function to set the shape_size and compute addr_size and
  130. // addr_mask
  131. void set_shape_size(size_t sz);
  132. // We need a constructor because we hold non-static references; this
  133. // constructor is called by the subclass constructors
  134. Shape(const Shape &parent, Duoram &duoram, MPCTIO &tio,
  135. yield_t &yield) : parent(parent), duoram(duoram), shape_size(0),
  136. tio(tio), yield(yield), explicitmode(false) {}
  137. // Copy the given Shape except for the tio and yield
  138. Shape(const Shape &copy_from, MPCTIO &tio, yield_t &yield) :
  139. parent(copy_from.parent), duoram(copy_from.duoram),
  140. shape_size(copy_from.shape_size),
  141. addr_size(copy_from.addr_size), addr_mask(copy_from.addr_mask),
  142. tio(tio), yield(yield),
  143. explicitmode(copy_from.explicitmode) {}
  144. // The index-mapping function. Input the index relative to this
  145. // shape, and output the corresponding index relative to the parent
  146. // shape.
  147. //
  148. // This is a pure virtual function; all subclasses of Shape must
  149. // implement it, and of course Shape itself therefore cannot be
  150. // instantiated.
  151. virtual size_t indexmap(size_t idx) const = 0;
  152. // Get a pair (for the server) of references to the underlying
  153. // Duoram entries at share virtual index idx.
  154. virtual inline std::tuple<T&,T&> get_server(size_t idx,
  155. std::nullopt_t null = std::nullopt) const {
  156. size_t parindex = indexmap(idx);
  157. if (&(this->parent) == this) {
  158. return std::tie(
  159. duoram.p0_blind[parindex],
  160. duoram.p1_blind[parindex]);
  161. } else {
  162. return this->parent.get_server(parindex, null);
  163. }
  164. }
  165. // Get a triple (for the computational players) of references to the
  166. // underlying Duoram entries at share virtual index idx.
  167. virtual inline std::tuple<T&,T&,T&> get_comp(size_t idx,
  168. std::nullopt_t null = std::nullopt) const {
  169. size_t parindex = indexmap(idx);
  170. if (&(this->parent) == this) {
  171. return std::tie(
  172. duoram.database[parindex],
  173. duoram.blind[parindex],
  174. duoram.peer_blinded_db[parindex]);
  175. } else {
  176. return this->parent.get_comp(parindex, null);
  177. }
  178. }
  179. // Get a pair (for the server) of references to a particular field
  180. // of the underlying Duoram entries at share virtual index idx.
  181. template <typename FT>
  182. inline std::tuple<FT&,FT&> get_server(size_t idx, FT T::*field) const {
  183. size_t parindex = indexmap(idx);
  184. if (&(this->parent) == this) {
  185. return std::tie(
  186. duoram.p0_blind[parindex].*field,
  187. duoram.p1_blind[parindex].*field);
  188. } else {
  189. return this->parent.get_server(parindex, field);
  190. }
  191. }
  192. // Get a triple (for the computational players) of references to a
  193. // particular field to the underlying Duoram entries at share
  194. // virtual index idx.
  195. template <typename FT>
  196. inline std::tuple<FT&,FT&,FT&> get_comp(size_t idx, FT T::*field) const {
  197. size_t parindex = indexmap(idx);
  198. if (&(this->parent) == this) {
  199. return std::tie(
  200. duoram.database[parindex].*field,
  201. duoram.blind[parindex].*field,
  202. duoram.peer_blinded_db[parindex].*field);
  203. } else {
  204. return this->parent.get_comp(parindex, field);
  205. }
  206. }
  207. public:
  208. // Get the size
  209. inline size_t size() const { return shape_size; }
  210. // Initialize the contents of the Shape to a constant. This method
  211. // does no communication; all the operations are local. This only
  212. // works for T=RegXS or RegAS.
  213. void init(size_t value) {
  214. T v;
  215. v.set(value);
  216. init([v] (size_t i) { return v; });
  217. }
  218. // As above, but for general T
  219. void init(const T &value) {
  220. init([value] (size_t i) { return value; });
  221. }
  222. // As above, but use the default initializer for T (probably sets
  223. // everything to 0).
  224. void init() {
  225. T deflt;
  226. init(deflt);
  227. }
  228. // Pass a function f: size_t -> size_t, and initialize element i of the
  229. // Shape to f(i) for each i. This method does no communication; all
  230. // the operations are local. This function must be deterministic
  231. // and public. Only works for T=RegAS or RegXS.
  232. void init(std::function<size_t(size_t)> f) {
  233. int player = tio.player();
  234. if (player < 2) {
  235. for (size_t i=0; i<shape_size; ++i) {
  236. auto [DB, BL, PBD] = get_comp(i);
  237. BL.set(0);
  238. if (player) {
  239. DB.set(f(i));
  240. PBD.set(0);
  241. } else {
  242. DB.set(0);
  243. PBD.set(f(i));
  244. }
  245. }
  246. } else {
  247. for (size_t i=0; i<shape_size; ++i) {
  248. auto [BL0, BL1] = get_server(i);
  249. BL0.set(0);
  250. BL1.set(0);
  251. }
  252. }
  253. }
  254. // Pass a function f: size_t -> T, and initialize element i of the
  255. // Shape to f(i) for each i. This method does no communication; all
  256. // the operations are local. This function must be deterministic
  257. // and public.
  258. void init(std::function<T(size_t)> f) {
  259. int player = tio.player();
  260. if (player < 2) {
  261. for (size_t i=0; i<shape_size; ++i) {
  262. auto [DB, BL, PBD] = get_comp(i);
  263. BL = T();
  264. if (player) {
  265. DB = f(i);
  266. PBD = T();
  267. } else {
  268. DB = T();
  269. PBD = f(i);
  270. }
  271. }
  272. } else {
  273. for (size_t i=0; i<shape_size; ++i) {
  274. auto [BL0, BL1] = get_server(i);
  275. BL0 = T();
  276. BL1 = T();
  277. }
  278. }
  279. }
  280. // Enable or disable explicit-only mode. Only using [] with
  281. // explicit (address_t) indices are allowed in this mode. Using []
  282. // with RegAS or RegXS indices will automatically turn off this
  283. // mode, or you can turn it off explicitly. In explicit-only mode,
  284. // updates to the memory in the Shape will not induce communication
  285. // to the server or peer, but when it turns off, a message of the
  286. // size of the entire Shape will be sent to each of the server and
  287. // the peer. This is useful if you're going to be doing multiple
  288. // explicit writes to every element of the Shape before you do your
  289. // next oblivious read or write. Bitonic sort is a prime example.
  290. void explicitonly(bool enable);
  291. // Create an OblivIndex, non-incrementally (supply the shares of the
  292. // index directly) or incrementally (the bits of the index will be
  293. // supplied later, one at a time)
  294. // Non-incremental, RegXS index
  295. OblivIndex<RegXS,1> oblivindex(const RegXS &idx, nbits_t depth=0) {
  296. if (depth == 0) {
  297. depth = this->addr_size;
  298. }
  299. typename Duoram<T>::OblivIndex<RegXS,1>
  300. res(this->tio, this->yield, idx, depth);
  301. return res;
  302. }
  303. // Non-incremental, RegAS index
  304. OblivIndex<RegAS,1> oblivindex(const RegAS &idx, nbits_t depth=0) {
  305. if (depth == 0) {
  306. depth = this->addr_size;
  307. }
  308. typename Duoram<T>::OblivIndex<RegAS,1>
  309. res(this->tio, this->yield, idx, depth);
  310. return res;
  311. }
  312. // Incremental (requires RegXS index, supplied bit-by-bit later)
  313. OblivIndex<RegXS,1> oblivindex(nbits_t depth=0) {
  314. if (depth == 0) {
  315. depth = this->addr_size;
  316. }
  317. typename Duoram<T>::OblivIndex<RegXS,1>
  318. res(this->tio, this->yield, depth);
  319. return res;
  320. }
  321. // For debugging or checking your answers (using this in general is
  322. // of course insecure)
  323. // This one reconstructs the whole database
  324. std::vector<T> reconstruct() const;
  325. // This one reconstructs a single database value
  326. T reconstruct(const T& share) const;
  327. };
  328. // The most basic shape is Flat. It is almost always the topmost shape,
  329. // and serves to provide MPCTIO and yield_t context to a Duoram without
  330. // changing the indices or size (but can specify a subrange if desired).
  331. template <typename T>
  332. class Duoram<T>::Flat : public Duoram<T>::Shape {
  333. // If this is a subrange, start may be non-0, but it's usually 0
  334. size_t start;
  335. size_t len;
  336. inline size_t indexmap(size_t idx) const {
  337. size_t paridx = idx + start;
  338. return paridx;
  339. }
  340. // Internal function to aid bitonic_sort
  341. void butterfly(address_t start, address_t len, bool dir);
  342. public:
  343. // Constructor. len=0 means the maximum size (the parent's size
  344. // minus start).
  345. Flat(Duoram &duoram, MPCTIO &tio, yield_t &yield, size_t start = 0,
  346. size_t len = 0);
  347. // Constructor. len=0 means the maximum size (the parent's size
  348. // minus start).
  349. Flat(const Shape &parent, MPCTIO &tio, yield_t &yield, size_t start = 0,
  350. size_t len = 0);
  351. // Copy the given Flat except for the tio and yield
  352. Flat(const Flat &copy_from, MPCTIO &tio, yield_t &yield) :
  353. Shape(copy_from, tio, yield), start(copy_from.start),
  354. len(copy_from.len) {}
  355. // Update the context (MPCTIO and yield if you've started a new
  356. // thread, or just yield if you've started a new coroutine in the
  357. // same thread). Returns a new Shape with an updated context.
  358. Flat context(MPCTIO &new_tio, yield_t &new_yield) const {
  359. return Flat(*this, new_tio, new_yield);
  360. }
  361. Flat context(yield_t &new_yield) const {
  362. return Flat(*this, this->tio, new_yield);
  363. }
  364. // Index into this Flat in various ways
  365. typename Duoram::Shape::template MemRefS<RegAS,T,std::nullopt_t,Flat,1>
  366. operator[](const RegAS &idx) {
  367. typename Duoram<T>::Shape::
  368. template MemRefS<RegAS,T,std::nullopt_t,Flat,1>
  369. res(*this, idx, std::nullopt);
  370. return res;
  371. }
  372. typename Duoram::Shape::template MemRefS<RegXS,T,std::nullopt_t,Flat,1>
  373. operator[](const RegXS &idx) {
  374. typename Duoram<T>::Shape::
  375. template MemRefS<RegXS,T,std::nullopt_t,Flat,1>
  376. res(*this, idx, std::nullopt);
  377. return res;
  378. }
  379. template <typename U, nbits_t WIDTH>
  380. typename Duoram::Shape::template MemRefS<U,T,std::nullopt_t,Flat,WIDTH>
  381. operator[](OblivIndex<U,WIDTH> &obidx) {
  382. typename Duoram<T>::Shape::
  383. template MemRefS<RegXS,T,std::nullopt_t,Flat,WIDTH>
  384. res(*this, obidx, std::nullopt);
  385. return res;
  386. }
  387. typename Duoram::Shape::template MemRefExpl<T,std::nullopt_t>
  388. operator[](address_t idx) {
  389. typename Duoram<T>::Shape::
  390. template MemRefExpl<T,std::nullopt_t>
  391. res(*this, idx, std::nullopt);
  392. return res;
  393. }
  394. template <typename U>
  395. Duoram::Shape::MemRefInd<U, Flat>
  396. operator[](const std::vector<U> &indcs) {
  397. typename Duoram<T>::Shape::
  398. template MemRefInd<U,Flat>
  399. res(*this, indcs);
  400. return res;
  401. }
  402. template <typename U, size_t N>
  403. Duoram::Shape::MemRefInd<U, Flat>
  404. operator[](const std::array<U,N> &indcs) {
  405. typename Duoram<T>::Shape::
  406. template MemRefInd<U,Flat>
  407. res(*this, indcs);
  408. return res;
  409. }
  410. // Oblivious sort the elements indexed by the two given indices.
  411. // Without reconstructing the values, if dir=0, this[idx1] will
  412. // become a share of the smaller of the reconstructed values, and
  413. // this[idx2] will become a share of the larger. If dir=1, it's the
  414. // other way around.
  415. //
  416. // Note: this only works for additively shared databases
  417. template<typename U,typename V>
  418. void osort(const U &idx1, const V &idx2, bool dir=0);
  419. // Bitonic sort the elements from start to start+len-1, in
  420. // increasing order if dir=0 or decreasing order if dir=1. Note that
  421. // the elements must be at most 63 bits long each for the notion of
  422. // ">" to make consistent sense.
  423. void bitonic_sort(address_t start, address_t len, bool dir=0);
  424. // Assuming the memory is already sorted, do an oblivious binary
  425. // search for the smallest index containing the value at least the
  426. // given one. (The answer will be the length of the Flat if all
  427. // elements are smaller than the target.) Only available for additive
  428. // shared databases for now.
  429. // The basic version uses log(N) ORAM reads of size N, where N is
  430. // the smallest power of 2 strictly larger than the Flat size
  431. RegAS basic_binary_search(RegAS &target);
  432. // This version does 1 ORAM read of size 2, 1 of size 4, 1 of size
  433. // 8, ..., 1 of size N/2, where N is the smallest power of 2
  434. // strictly larger than the Flat size
  435. RegXS binary_search(RegAS &target);
  436. };
  437. // Oblivious indices for use in related-index ORAM accesses.
  438. template <typename T>
  439. template <typename U, nbits_t WIDTH>
  440. class Duoram<T>::OblivIndex {
  441. template <typename Ux,typename FT,typename FST,typename Sh,nbits_t WIDTHx>
  442. friend class Shape::MemRefS;
  443. int player;
  444. std::optional<RDPFTriple<WIDTH>> dt;
  445. std::optional<RDPFPair<WIDTH>> dp;
  446. nbits_t curdepth, maxdepth;
  447. nbits_t next_windex;
  448. bool incremental;
  449. U idx;
  450. public:
  451. // Non-incremental constructor
  452. OblivIndex(MPCTIO &tio, yield_t &yield, const U &idx, nbits_t depth) :
  453. player(tio.player()), curdepth(depth), maxdepth(depth),
  454. next_windex(0), incremental(false), idx(idx)
  455. {
  456. if (player < 2) {
  457. dt = tio.rdpftriple<WIDTH>(yield, depth);
  458. } else {
  459. dp = tio.rdpfpair<WIDTH>(yield, depth);
  460. }
  461. }
  462. // Incremental constructor: only for U=RegXS
  463. OblivIndex(MPCTIO &tio, yield_t &yield, nbits_t depth) :
  464. player(tio.player()), curdepth(0), maxdepth(depth),
  465. next_windex(0), incremental(true), idx(RegXS())
  466. {
  467. if (player < 2) {
  468. dt = tio.rdpftriple(yield, depth, true);
  469. } else {
  470. dp = tio.rdpfpair(yield, depth, true);
  471. }
  472. }
  473. // Incrementally append a (shared) bit to the oblivious index
  474. void incr(RegBS bit)
  475. {
  476. assert(incremental);
  477. idx.xshare = (idx.xshare << 1) | value_t(bit.bshare);
  478. ++curdepth;
  479. if (player < 2) {
  480. dt->depth(curdepth);
  481. } else {
  482. dp->depth(curdepth);
  483. }
  484. }
  485. // Get a copy of the index
  486. U index() { return idx; }
  487. // Get the next wide-RDPF index
  488. nbits_t windex() { assert(next_windex < WIDTH); return next_windex++; }
  489. };
  490. // An additive or XOR shared memory reference. You get one of these
  491. // from a Shape A and an additively shared RegAS index x, or an XOR
  492. // shared RegXS index x, with A[x]. Then you perform operations on this
  493. // object, which do the Duoram operations. As above, T is the sharing
  494. // type of the data in the database, while U is the sharing type of the
  495. // index used to create this memory reference. If we are referencing an
  496. // entire entry of type T, then the field type FT will equal T, and the
  497. // field selector type FST will be nullopt_t. If we are referencing a
  498. // particular field of T, then FT will be the type of the field (RegAS
  499. // or RegXS) and FST will be a pointer-to-member T::* type pointing to
  500. // that field. Sh is the specific Shape subtype used to create the
  501. // MemRefS. WIDTH is the RDPF width to use.
  502. template <typename T>
  503. template <typename U, typename FT, typename FST, typename Sh, nbits_t WIDTH>
  504. class Duoram<T>::Shape::MemRefS {
  505. Sh &shape;
  506. // oblividx is a reference to the OblivIndex we're using. In the
  507. // common case, we own the actual OblivIndex, and it's stored in
  508. // our_oblividx, and oblividx is a pointer to that. Sometimes
  509. // (for example incremental ORAM accesses), the caller will own (and
  510. // modify between uses) the OblivIndex. In that case, oblividx will
  511. // be a pointer to the caller's OblivIndex object, and
  512. // our_oblividx will be nullopt.
  513. std::optional<Duoram<T>::OblivIndex<U,WIDTH>> our_oblividx;
  514. Duoram<T>::OblivIndex<U,WIDTH> *oblividx;
  515. FST fieldsel;
  516. private:
  517. // Oblivious update to a shared index of Duoram memory, only for
  518. // FT = RegAS or RegXS
  519. MemRefS<U,FT,FST,Sh,WIDTH> &oram_update(const FT& M, const prac_template_true&);
  520. // Oblivious update to a shared index of Duoram memory, for
  521. // FT not RegAS or RegXS
  522. MemRefS<U,FT,FST,Sh,WIDTH> &oram_update(const FT& M, const prac_template_false&);
  523. public:
  524. MemRefS<U,FT,FST,Sh,WIDTH>(Sh &shape, const U &idx, FST fieldsel) :
  525. shape(shape), fieldsel(fieldsel) {
  526. our_oblividx.emplace(shape.tio, shape.yield, idx,
  527. shape.addr_size);
  528. oblividx = &(*our_oblividx);
  529. }
  530. MemRefS<U,FT,FST,Sh,WIDTH>(Sh &shape, OblivIndex<U,WIDTH> &obidx, FST fieldsel) :
  531. shape(shape), fieldsel(fieldsel) {
  532. oblividx = &obidx;
  533. }
  534. // Create a MemRefExpl for accessing a partcular field of T
  535. template <typename SFT>
  536. MemRefS<U,SFT,SFT T::*,Sh,WIDTH> field(SFT T::*subfieldsel) {
  537. auto res = MemRefS<U,SFT,SFT T::*,Sh,WIDTH>(this->shape,
  538. *oblividx, subfieldsel);
  539. return res;
  540. }
  541. // Oblivious read from a shared index of Duoram memory
  542. operator FT();
  543. // Oblivious update to a shared index of Duoram memory
  544. MemRefS<U,FT,FST,Sh,WIDTH> &operator+=(const FT& M);
  545. // Oblivious write to a shared index of Duoram memory
  546. MemRefS<U,FT,FST,Sh,WIDTH> &operator=(const FT& M);
  547. };
  548. // An explicit memory reference. You get one of these from a Shape A
  549. // and an address_t index x with A[x]. Then you perform operations on
  550. // this object, which update the Duoram state without performing Duoram
  551. // operations. If we are referencing an entire entry of type T, then
  552. // the field type FT will equal T, and the field selector type FST will
  553. // be nullopt_t. If we are referencing a particular field of T, then FT
  554. // will be the type of the field (RegAS or RegXS) and FST will be a
  555. // pointer-to-member T::* type pointing to that field.
  556. template <typename T> template <typename FT, typename FST>
  557. class Duoram<T>::Shape::MemRefExpl {
  558. Shape &shape;
  559. address_t idx;
  560. FST fieldsel;
  561. public:
  562. MemRefExpl(Shape &shape, address_t idx, FST fieldsel) :
  563. shape(shape), idx(idx), fieldsel(fieldsel) {}
  564. // Create a MemRefExpl for accessing a partcular field of T
  565. template <typename SFT>
  566. MemRefExpl<SFT,SFT T::*> field(SFT T::*subfieldsel) {
  567. auto res = MemRefExpl<SFT,SFT T::*>(this->shape, idx, subfieldsel);
  568. return res;
  569. }
  570. // Explicit read from a given index of Duoram memory
  571. operator FT();
  572. // Explicit update to a given index of Duoram memory
  573. MemRefExpl &operator+=(const FT& M);
  574. // Explicit write to a given index of Duoram memory
  575. MemRefExpl &operator=(const FT& M);
  576. // Convenience function
  577. MemRefExpl &operator-=(const FT& M) { *this += (-M); return *this; }
  578. };
  579. // A collection of independent memory references that can be processed
  580. // simultaneously. You get one of these from a Shape A (of specific
  581. // subclass Sh) and a vector or array of indices v with each element of
  582. // type U.
  583. template <typename T> template <typename U, typename Sh>
  584. class Duoram<T>::Shape::MemRefInd {
  585. Sh &shape;
  586. std::vector<U> indcs;
  587. public:
  588. MemRefInd(Sh &shape, std::vector<U> indcs) :
  589. shape(shape), indcs(indcs) {}
  590. template <size_t N>
  591. MemRefInd(Sh &shape, std::array<U,N> aindcs) :
  592. shape(shape) { for ( auto &i : aindcs ) { indcs.push_back(i); } }
  593. // Independent reads from shared or explicit indices of Duoram memory
  594. operator std::vector<T>();
  595. // Independent updates to shared or explicit indices of Duoram memory
  596. MemRefInd &operator+=(const std::vector<T>& M);
  597. template <size_t N>
  598. MemRefInd &operator+=(const std::array<T,N>& M);
  599. // Independent writes to shared or explicit indices of Duoram memory
  600. MemRefInd &operator=(const std::vector<T>& M);
  601. template <size_t N>
  602. MemRefInd &operator=(const std::array<T,N>& M);
  603. // Convenience function
  604. MemRefInd &operator-=(const std::vector<T>& M) { *this += (-M); return *this; }
  605. template <size_t N>
  606. MemRefInd &operator-=(const std::array<T,N>& M) { *this += (-M); return *this; }
  607. };
  608. #include "duoram.tcc"
  609. #endif