rdpf.hpp 24 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664
  1. #ifndef __RDPF_HPP__
  2. #define __RDPF_HPP__
  3. #include <array>
  4. #include <vector>
  5. #include <iostream>
  6. #include "mpcio.hpp"
  7. #include "coroutine.hpp"
  8. #include "types.hpp"
  9. #include "bitutils.hpp"
  10. #include "dpf.hpp"
  11. // DPFs for oblivious random accesses to memory. See dpf.hpp for the
  12. // differences between the different kinds of DPFs.
  13. // A single RDPF can use its unit vector for any as reads of the same
  14. // memory location as you like, as long as it's OK that everyone _knows_
  15. // it's the same memory location. The same RDPF can also be configured
  16. // to allow for WIDTH independent updates; if you otherwise would try to
  17. // reuse the same RDPF for multiple updates of the same memory location,
  18. // you would leak the difference between the update _values_. Typically
  19. // WIDTH=1, since most RDPFs are not reused at all.
  20. //
  21. // We implement this by have a "wide" LeafNode type that can store one
  22. // 64-bit value for the read, and WIDTH 64-bit values for the writes.
  23. // Since each DPFnode is 128 bits, you need 1 + (WIDTH/2) DPFnodes in a
  24. // LeafNode. We will also need to pass around arrays of WIDTH RegAS and
  25. // RegXS values, so we make dedicated wide types for those (RegASW and
  26. // RegXSW).
  27. template <nbits_t WIDTH>
  28. struct RDPF : public DPF {
  29. template <typename T>
  30. using W = std::array<T, WIDTH>;
  31. // The wide shared register types
  32. using RegASW = W<RegAS>;
  33. using RegXSW = W<RegXS>;
  34. // The number of 128-bit leaf node entries you need to get 1 unit
  35. // value and WIDTH scaled values (each is 64 bits)
  36. static const nbits_t LWIDTH = 1 + (WIDTH/2);
  37. using LeafNode = std::array<DPFnode,LWIDTH>;
  38. // Information for leaf levels of the RDPF. Normal RDPFs only have
  39. // one leaf level (at the bottom), but incremental RDPFs have a leaf
  40. // level for each level of the DPF.
  41. struct LeafInfo {
  42. static const nbits_t W = WIDTH;
  43. // The correction word for this leaf level
  44. LeafNode leaf_cw;
  45. // The amount we have to scale the low words of the leaf values by
  46. // to get additive shares of a unit vector
  47. value_t unit_sum_inverse;
  48. // Additive share of the scaling values M_as such that the high words
  49. // of the WIDTH leaf values for P0 and P1 add to M_as * e_{target}
  50. std::array<RegAS,WIDTH> scaled_sum;
  51. // XOR share of the scaling values M_xs such that the high words
  52. // of the WIDTH leaf values for P0 and P1 XOR to M_xs * e_{target}
  53. std::array<RegXS,WIDTH> scaled_xor;
  54. // If we're saving the expansion, put it here
  55. std::vector<LeafNode> expansion;
  56. LeafInfo() : unit_sum_inverse(0) {}
  57. };
  58. // The depth of this RDPF. If this is not an incremental DPF, then
  59. // both the maximum depth and current depth are just the normal
  60. // depth (specified at DPF creation time). If this is an
  61. // incremental DPF, then the maximum depth is the one specified at
  62. // creation time, but the current depth will be between 1 and that
  63. // value (inclusive).
  64. nbits_t maxdepth, curdepth;
  65. // The LeafInfo for each leaf level. Normal RDPFs only have one
  66. // leaf level, so this will be a vector of length 1. Incremental
  67. // RDPFs will have one entry for each level in the DPF. The entry
  68. // corresponding to level i of the DPF (of total depth d) is
  69. // leaf_info[d-i].
  70. std::vector<LeafInfo> li;
  71. // The leaf correction flag bits for each leaf level. The bit for
  72. // level i (for an incremental DPF of max depth m) is leaf_cfbits &
  73. // (1<<(m-i)). For a normal (not incremental) RDPF, it's the same,
  74. // but therefore only the low bit gets used.
  75. value_t leaf_cfbits;
  76. RDPF() {}
  77. // Construct a DPF with the given (XOR-shared) target location, and
  78. // of the given depth, to be used for random-access memory reads and
  79. // writes. The DPF is constructed collaboratively by P0 and P1,
  80. // with the server P2 helping by providing correlated randomness,
  81. // such as SelectTriples.
  82. //
  83. // Cost:
  84. // (2 DPFnode + 2 bytes)*depth + 1 word communication in
  85. // 2*depth + 1 messages
  86. // (2 DPFnode + 1 byte)*depth communication from P2 to each party
  87. // 2^{depth+1}-2 local AES operations for P0,P1
  88. // 0 local AES operations for P2
  89. RDPF(MPCTIO &tio, yield_t &yield,
  90. RegXS target, nbits_t depth, bool incremental = false,
  91. bool save_expansion = false);
  92. // Do we have a precomputed expansion?
  93. inline bool has_expansion() const {
  94. return li[maxdepth-curdepth].expansion.size() > 0;
  95. }
  96. // Get an element of the expansion
  97. inline LeafNode get_expansion(address_t index) const {
  98. return li[maxdepth-curdepth].expansion[index];
  99. }
  100. // The depth
  101. inline nbits_t depth() const { return curdepth; }
  102. // Set the current depth for an incremental RDPF; 0 means to use
  103. // maxdepth
  104. inline void depth(nbits_t newdepth) {
  105. if (newdepth > 0 && newdepth < maxdepth) {
  106. curdepth = newdepth;
  107. } else {
  108. curdepth = maxdepth;
  109. }
  110. }
  111. // Get the leaf node for the given input
  112. //
  113. // Cost: depth AES operations
  114. LeafNode leaf(address_t input, size_t &aes_ops) const;
  115. // Expand the DPF if it's not already expanded
  116. void expand(size_t &aes_ops);
  117. // Descend from a node at depth parentdepth to one of its leaf children
  118. // whichchild = 0: left child
  119. // whichchild = 1: right child
  120. //
  121. // Cost: 1 AES operation
  122. inline LeafNode descend_to_leaf(const DPFnode &parent,
  123. nbits_t parentdepth, bit_t whichchild, size_t &aes_ops) const;
  124. // Get the bit-shared unit vector entry from the leaf node
  125. inline RegBS unit_bs(const LeafNode &leaf) const {
  126. RegBS b;
  127. b.bshare = get_lsb(leaf[0]);
  128. return b;
  129. }
  130. // Get the additive-shared unit vector entry from the leaf node
  131. inline RegAS unit_as(const LeafNode &leaf) const {
  132. RegAS a;
  133. value_t lowword = value_t(_mm_cvtsi128_si64x(leaf[0]));
  134. if (whichhalf == 1) {
  135. lowword = -lowword;
  136. }
  137. a.ashare = lowword * li[maxdepth-curdepth].unit_sum_inverse;
  138. return a;
  139. }
  140. // Get the XOR-shared scaled vector entry from the leaf node
  141. inline RegXSW scaled_xs(const LeafNode &leaf) const {
  142. RegXSW x;
  143. nbits_t j = 0;
  144. value_t highword =
  145. value_t(_mm_cvtsi128_si64x(_mm_srli_si128(leaf[0],8)));
  146. x[j++].xshare = highword;
  147. for (nbits_t i=1;i<LWIDTH;++i) {
  148. value_t lowword =
  149. value_t(_mm_cvtsi128_si64x(leaf[i]));
  150. value_t highword =
  151. value_t(_mm_cvtsi128_si64x(_mm_srli_si128(leaf[i],8)));
  152. x[j++].xshare = lowword;
  153. if (j < WIDTH) {
  154. x[j++].xshare = highword;
  155. }
  156. }
  157. return x;
  158. }
  159. // Get the additive-shared scaled vector entry from the leaf node
  160. inline RegASW scaled_as(const LeafNode &leaf) const {
  161. RegASW a;
  162. nbits_t j = 0;
  163. value_t highword =
  164. value_t(_mm_cvtsi128_si64x(_mm_srli_si128(leaf[0],8)));
  165. if (whichhalf == 1) {
  166. highword = -highword;
  167. }
  168. a[j++].ashare = highword;
  169. for (nbits_t i=1;i<WIDTH;++i) {
  170. value_t lowword =
  171. value_t(_mm_cvtsi128_si64x(leaf[i]));
  172. value_t highword =
  173. value_t(_mm_cvtsi128_si64x(_mm_srli_si128(leaf[i],8)));
  174. if (whichhalf == 1) {
  175. lowword = -lowword;
  176. highword = -highword;
  177. }
  178. a[j++].ashare = lowword;
  179. if (j < WIDTH) {
  180. a[j++].ashare = highword;
  181. }
  182. }
  183. return a;
  184. }
  185. private:
  186. // Expand one leaf layer of the DPF if it's not already expanded
  187. void expand_leaf_layer(nbits_t li_index, size_t &aes_ops);
  188. };
  189. // Computational peers will generate triples of RDPFs with the _same_
  190. // random target for use in Duoram. They will each hold a share of the
  191. // target (neither knowing the complete target index). They will each
  192. // give one of the DPFs (not a matching pair) to the server, but not the
  193. // shares of the target index. So computational peers will hold a
  194. // RDPFTriple (which includes both an additive and an XOR share of the
  195. // target index), while the server will hold a RDPFPair (which does
  196. // not).
  197. template <nbits_t WIDTH>
  198. struct RDPFTriple {
  199. template <typename T>
  200. using Triple = std::tuple<T, T, T>;
  201. template <typename T>
  202. using WTriple = std::tuple<
  203. typename std::array<T,WIDTH>,
  204. typename std::array<T,WIDTH>,
  205. typename std::array<T,WIDTH> >;
  206. // The type of triples of nodes, LeafNodes, and the wide shared
  207. // register types
  208. using node = Triple<DPFnode>;
  209. using LeafNode = Triple<typename RDPF<WIDTH>::LeafNode>;
  210. using RegASWT = WTriple<RegAS>;
  211. using RegXSWT = WTriple<RegXS>;
  212. RegAS as_target;
  213. RegXS xs_target;
  214. RDPF<WIDTH> dpf[3];
  215. // The depth
  216. inline nbits_t depth() const { return dpf[0].depth(); }
  217. // Set the current depth for an incremental RDPFTriple; 0 means to
  218. // use maxdepth
  219. inline void depth(nbits_t newdepth) {
  220. dpf[0].depth(newdepth);
  221. dpf[1].depth(newdepth);
  222. dpf[2].depth(newdepth);
  223. }
  224. // The seed
  225. inline node get_seed() const {
  226. return std::make_tuple(dpf[0].get_seed(), dpf[1].get_seed(),
  227. dpf[2].get_seed());
  228. }
  229. // Do we have a precomputed expansion?
  230. inline bool has_expansion() const {
  231. int li_index = dpf[0].maxdepth - dpf[0].curdepth;
  232. return dpf[0].li[li_index].expansion.size() > 0;
  233. }
  234. // Get an element of the expansion
  235. inline LeafNode get_expansion(address_t index) const {
  236. return std::make_tuple(dpf[0].get_expansion(index),
  237. dpf[1].get_expansion(index), dpf[2].get_expansion(index));
  238. }
  239. RDPFTriple() {}
  240. // Construct three RDPFs of the given depth all with the same
  241. // randomly generated target index.
  242. RDPFTriple(MPCTIO &tio, yield_t &yield,
  243. nbits_t depth, bool incremental = false, bool save_expansion = false);
  244. // Descend the three RDPFs in lock step
  245. node descend(const node &parent, nbits_t parentdepth,
  246. bit_t whichchild, size_t &aes_ops) const;
  247. // Descend the three RDPFs in lock step to a leaf node
  248. LeafNode descend_to_leaf(const node &parent, nbits_t parentdepth,
  249. bit_t whichchild, size_t &aes_ops) const;
  250. // Overloaded versions of functions to get DPF components and
  251. // outputs so that the appropriate one can be selected with a
  252. // parameter
  253. // Only RegXS, not RegAS, indices are used with incremental RDPFs
  254. inline void get_target(RegAS &target) const { target = as_target; }
  255. inline void get_target(RegXS &target) const {
  256. target = xs_target >> (dpf[0].maxdepth - dpf[0].curdepth);
  257. }
  258. // Additive share of the scaling value M_as such that the high words
  259. // of the leaf values for P0 and P1 add to M_as * e_{target}
  260. inline void scaled_value(RegASWT &v) const {
  261. int li_index = dpf[0].maxdepth - dpf[0].curdepth;
  262. std::get<0>(v) = dpf[0].li[li_index].scaled_sum;
  263. std::get<1>(v) = dpf[1].li[li_index].scaled_sum;
  264. std::get<2>(v) = dpf[2].li[li_index].scaled_sum;
  265. }
  266. // XOR share of the scaling value M_xs such that the high words
  267. // of the leaf values for P0 and P1 XOR to M_xs * e_{target}
  268. inline void scaled_value(RegXSWT &v) const {
  269. int li_index = dpf[0].maxdepth - dpf[0].curdepth;
  270. std::get<0>(v) = dpf[0].li[li_index].scaled_xor;
  271. std::get<1>(v) = dpf[1].li[li_index].scaled_xor;
  272. std::get<2>(v) = dpf[2].li[li_index].scaled_xor;
  273. }
  274. // Get the additive-shared unit vector entry from the leaf node
  275. inline void unit(std::tuple<RegAS,RegAS,RegAS> &u, const LeafNode &leaf) const {
  276. std::get<0>(u) = dpf[0].unit_as(std::get<0>(leaf));
  277. std::get<1>(u) = dpf[1].unit_as(std::get<1>(leaf));
  278. std::get<2>(u) = dpf[2].unit_as(std::get<2>(leaf));
  279. }
  280. // Get the bit-shared unit vector entry from the leaf node
  281. inline void unit(std::tuple<RegXS,RegXS,RegXS> &u, const LeafNode &leaf) const {
  282. std::get<0>(u) = dpf[0].unit_bs(std::get<0>(leaf));
  283. std::get<1>(u) = dpf[1].unit_bs(std::get<1>(leaf));
  284. std::get<2>(u) = dpf[2].unit_bs(std::get<2>(leaf));
  285. }
  286. // For any more complex entry type, that type will handle the conversion
  287. // for each DPF
  288. template <typename T>
  289. inline void unit(std::tuple<T,T,T> &u, const LeafNode &leaf) const {
  290. std::get<0>(u).unit(dpf[0], std::get<0>(leaf));
  291. std::get<1>(u).unit(dpf[1], std::get<1>(leaf));
  292. std::get<2>(u).unit(dpf[2], std::get<2>(leaf));
  293. }
  294. // Get the additive-shared scaled vector entry from the leaf node
  295. inline void scaled(RegASWT &s, const LeafNode &leaf) const {
  296. std::get<0>(s) = dpf[0].scaled_as(std::get<0>(leaf));
  297. std::get<1>(s) = dpf[1].scaled_as(std::get<1>(leaf));
  298. std::get<2>(s) = dpf[2].scaled_as(std::get<2>(leaf));
  299. }
  300. // Get the XOR-shared scaled vector entry from the leaf node
  301. inline void scaled(RegXSWT &s, const LeafNode &leaf) const {
  302. std::get<0>(s) = dpf[0].scaled_xs(std::get<0>(leaf));
  303. std::get<1>(s) = dpf[1].scaled_xs(std::get<1>(leaf));
  304. std::get<2>(s) = dpf[2].scaled_xs(std::get<2>(leaf));
  305. }
  306. };
  307. template <nbits_t WIDTH>
  308. struct RDPFPair {
  309. template <typename T>
  310. using Pair = std::tuple<T, T>;
  311. template <typename T>
  312. using WPair = std::tuple<
  313. typename std::array<T,WIDTH>,
  314. typename std::array<T,WIDTH> >;
  315. // The type of pairs of nodes, LeafNodes, and the wide shared
  316. // register types
  317. using node = Pair<DPFnode>;
  318. using LeafNode = Pair<typename RDPF<WIDTH>::LeafNode>;
  319. using RegASWP = WPair<RegAS>;
  320. using RegXSWP = WPair<RegXS>;
  321. RDPF<WIDTH> dpf[2];
  322. RDPFPair() {}
  323. // The depth
  324. inline nbits_t depth() const { return dpf[0].depth(); }
  325. // Set the current depth for an incremental RDPFPair; 0 means to use
  326. // maxdepth
  327. inline void depth(nbits_t newdepth) {
  328. dpf[0].depth(newdepth);
  329. dpf[1].depth(newdepth);
  330. }
  331. // The seed
  332. inline node get_seed() const {
  333. return std::make_tuple(dpf[0].get_seed(), dpf[1].get_seed());
  334. }
  335. // Do we have a precomputed expansion?
  336. inline bool has_expansion() const {
  337. int li_index = dpf[0].maxdepth - dpf[0].curdepth;
  338. return dpf[0].li[li_index].expansion.size() > 0;
  339. }
  340. // Get an element of the expansion
  341. inline LeafNode get_expansion(address_t index) const {
  342. return std::make_tuple(dpf[0].get_expansion(index),
  343. dpf[1].get_expansion(index));
  344. }
  345. // Descend the two RDPFs in lock step
  346. node descend(const node &parent, nbits_t parentdepth,
  347. bit_t whichchild, size_t &aes_ops) const;
  348. // Descend the two RDPFs in lock step to a leaf node
  349. LeafNode descend_to_leaf(const node &parent, nbits_t parentdepth,
  350. bit_t whichchild, size_t &aes_ops) const;
  351. // Overloaded versions of functions to get DPF components and
  352. // outputs so that the appropriate one can be selected with a
  353. // parameter
  354. // Additive share of the scaling value M_as such that the high words
  355. // of the leaf values for P0 and P1 add to M_as * e_{target}
  356. inline void scaled_value(RegASWP &v) const {
  357. std::get<0>(v) = dpf[0].scaled_sum;
  358. std::get<1>(v) = dpf[1].scaled_sum;
  359. }
  360. // XOR share of the scaling value M_xs such that the high words
  361. // of the leaf values for P0 and P1 XOR to M_xs * e_{target}
  362. inline void scaled_value(RegXSWP &v) const {
  363. std::get<0>(v) = dpf[0].scaled_xor;
  364. std::get<1>(v) = dpf[1].scaled_xor;
  365. }
  366. // Get the additive-shared unit vector entry from the leaf node
  367. inline void unit(std::tuple<RegAS,RegAS> &u, const LeafNode &leaf) const {
  368. std::get<0>(u) = dpf[0].unit_as(std::get<0>(leaf));
  369. std::get<1>(u) = dpf[1].unit_as(std::get<1>(leaf));
  370. }
  371. // Get the bit-shared unit vector entry from the leaf node
  372. inline void unit(std::tuple<RegXS,RegXS> &u, const LeafNode &leaf) const {
  373. std::get<0>(u) = dpf[0].unit_bs(std::get<0>(leaf));
  374. std::get<1>(u) = dpf[1].unit_bs(std::get<1>(leaf));
  375. }
  376. // For any more complex entry type, that type will handle the conversion
  377. // for each DPF
  378. template <typename T>
  379. inline void unit(std::tuple<T,T> &u, const LeafNode &leaf) const {
  380. std::get<0>(u).unit(dpf[0], std::get<0>(leaf));
  381. std::get<1>(u).unit(dpf[1], std::get<1>(leaf));
  382. }
  383. // Get the additive-shared scaled vector entry from the leaf node
  384. inline void scaled(RegASWP &s, const LeafNode &leaf) const {
  385. std::get<0>(s) = dpf[0].scaled_as(std::get<0>(leaf));
  386. std::get<1>(s) = dpf[1].scaled_as(std::get<1>(leaf));
  387. }
  388. // Get the XOR-shared scaled vector entry from the leaf node
  389. inline void scaled(RegXSWP &s, const LeafNode &leaf) const {
  390. std::get<0>(s) = dpf[0].scaled_xs(std::get<0>(leaf));
  391. std::get<1>(s) = dpf[1].scaled_xs(std::get<1>(leaf));
  392. }
  393. };
  394. // These are used by computational peers, who hold RPDFTriples, but when
  395. // reading, only need to use 2 of the 3 RDPFs. The API follows that of
  396. // RDPFPair, but internally, it holds two references to external RDPFs,
  397. // instead of holding the RDPFs themselves.
  398. template <nbits_t WIDTH>
  399. struct RDPF2of3 {
  400. template <typename T>
  401. using Pair = std::tuple<T, T>;
  402. template <typename T>
  403. using WPair = std::tuple<
  404. typename std::array<T,WIDTH>,
  405. typename std::array<T,WIDTH> >;
  406. // The type of pairs of nodes, LeafNodes, and the wide shared
  407. // register types
  408. using node = Pair<DPFnode>;
  409. using LeafNode = Pair<typename RDPF<WIDTH>::LeafNode>;
  410. using RegASWP = WPair<RegAS>;
  411. using RegXSWP = WPair<RegXS>;
  412. const RDPF<WIDTH> &dpf0, &dpf1;
  413. // Create an RDPFPair from an RDPFTriple, keeping two of the RDPFs
  414. // and dropping one. This _moves_ the dpfs from the triple to the
  415. // pair, so the triple will no longer be valid after using this.
  416. // which0 and which1 indicate which of the dpfs to keep.
  417. RDPF2of3(const RDPFTriple<WIDTH> &trip, int which0, int which1) :
  418. dpf0(trip.dpf[which0]), dpf1(trip.dpf[which1]) {}
  419. // The depth
  420. inline nbits_t depth() const { return dpf0.depth(); }
  421. // Set the current depth for an incremental RDPFPair; 0 means to use
  422. // maxdepth
  423. inline void depth(nbits_t newdepth) {
  424. dpf0.depth(newdepth);
  425. dpf1.depth(newdepth);
  426. }
  427. // The seed
  428. inline node get_seed() const {
  429. return std::make_tuple(dpf0.get_seed(), dpf1.get_seed());
  430. }
  431. // Do we have a precomputed expansion?
  432. inline bool has_expansion() const {
  433. int li_index = dpf0.maxdepth - dpf0.curdepth;
  434. return dpf0.li[li_index].expansion.size() > 0;
  435. }
  436. // Get an element of the expansion
  437. inline LeafNode get_expansion(address_t index) const {
  438. return std::make_tuple(dpf0.get_expansion(index),
  439. dpf1.get_expansion(index));
  440. }
  441. // Descend the two RDPFs in lock step
  442. node descend(const node &parent, nbits_t parentdepth,
  443. bit_t whichchild, size_t &aes_ops) const;
  444. // Descend the two RDPFs in lock step to a leaf node
  445. LeafNode descend_to_leaf(const node &parent, nbits_t parentdepth,
  446. bit_t whichchild, size_t &aes_ops) const;
  447. // Overloaded versions of functions to get DPF components and
  448. // outputs so that the appropriate one can be selected with a
  449. // parameter
  450. // Additive share of the scaling value M_as such that the high words
  451. // of the leaf values for P0 and P1 add to M_as * e_{target}
  452. inline void scaled_value(RegASWP &v) const {
  453. std::get<0>(v) = dpf0.scaled_sum;
  454. std::get<1>(v) = dpf1.scaled_sum;
  455. }
  456. // XOR share of the scaling value M_xs such that the high words
  457. // of the leaf values for P0 and P1 XOR to M_xs * e_{target}
  458. inline void scaled_value(RegXSWP &v) const {
  459. std::get<0>(v) = dpf0.scaled_xor;
  460. std::get<1>(v) = dpf1.scaled_xor;
  461. }
  462. // Get the additive-shared unit vector entry from the leaf node
  463. inline void unit(std::tuple<RegAS,RegAS> &u, const LeafNode &leaf) const {
  464. std::get<0>(u) = dpf0.unit_as(std::get<0>(leaf));
  465. std::get<1>(u) = dpf1.unit_as(std::get<1>(leaf));
  466. }
  467. // Get the bit-shared unit vector entry from the leaf node
  468. inline void unit(std::tuple<RegXS,RegXS> &u, const LeafNode &leaf) const {
  469. std::get<0>(u) = dpf0.unit_bs(std::get<0>(leaf));
  470. std::get<1>(u) = dpf1.unit_bs(std::get<1>(leaf));
  471. }
  472. // For any more complex entry type, that type will handle the conversion
  473. // for each DPF
  474. template <typename T>
  475. inline void unit(std::tuple<T,T> &u, const LeafNode &leaf) const {
  476. std::get<0>(u).unit(dpf0, std::get<0>(leaf));
  477. std::get<1>(u).unit(dpf1, std::get<1>(leaf));
  478. }
  479. // Get the additive-shared scaled vector entry from the leaf node
  480. inline void scaled(RegASWP &s, const LeafNode &leaf) const {
  481. std::get<0>(s) = dpf0.scaled_as(std::get<0>(leaf));
  482. std::get<1>(s) = dpf1.scaled_as(std::get<1>(leaf));
  483. }
  484. // Get the XOR-shared scaled vector entry from the leaf node
  485. inline void scaled(RegXSWP &s, const LeafNode &leaf) const {
  486. std::get<0>(s) = dpf0.scaled_xs(std::get<0>(leaf));
  487. std::get<1>(s) = dpf1.scaled_xs(std::get<1>(leaf));
  488. }
  489. };
  490. // Streaming evaluation, to avoid taking up enough memory to store
  491. // an entire evaluation. T can be RDPF, RDPFPair, or RDPFTriple.
  492. template <typename T>
  493. class StreamEval {
  494. const T &rdpf;
  495. size_t &aes_ops;
  496. bool use_expansion;
  497. nbits_t depth;
  498. address_t counter_xor_offset;
  499. address_t indexmask;
  500. address_t pathindex;
  501. address_t nextindex;
  502. std::vector<typename T::node> path;
  503. public:
  504. // Create a StreamEval object that will start its output at index
  505. // start. It will wrap around to 0 when it hits 2^depth. If
  506. // use_expansion is true, then if the DPF has been expanded, just
  507. // output values from that. If use_expansion=false or if the DPF
  508. // has not been expanded, compute the values on the fly. If
  509. // xor_offset is non-zero, then the outputs are actually
  510. // DPF(start XOR xor_offset)
  511. // DPF((start+1) XOR xor_offset)
  512. // DPF((start+2) XOR xor_offset)
  513. // etc.
  514. StreamEval(const T &rdpf, address_t start,
  515. address_t xor_offset, size_t &aes_ops,
  516. bool use_expansion = true);
  517. // Get the next value (or tuple of values) from the evaluator
  518. typename T::LeafNode next();
  519. };
  520. // Parallel evaluation. This class launches a number of threads each
  521. // running a StreamEval to evaluate a chunk of the RDPF (or RDPFPair or
  522. // RDPFTriple), and accumulates the results within each chunk, and then
  523. // accumulates all the chunks together. T can be RDPF, RDPFPair, or
  524. // RDPFTriple.
  525. template <typename T>
  526. struct ParallelEval {
  527. const T &rdpf;
  528. address_t start;
  529. address_t xor_offset;
  530. address_t num_evals;
  531. int num_threads;
  532. size_t &aes_ops;
  533. // Create a Parallel evaluator that will evaluate the given rdpf at
  534. // DPF(start XOR xor_offset)
  535. // DPF((start+1) XOR xor_offset)
  536. // DPF((start+2) XOR xor_offset)
  537. // ...
  538. // DPF((start+num_evals-1) XOR xor_offset)
  539. // where all indices are taken mod 2^depth, and accumulate the
  540. // results into a single answer.
  541. ParallelEval(const T &rdpf, address_t start,
  542. address_t xor_offset, address_t num_evals,
  543. int num_threads, size_t &aes_ops) :
  544. rdpf(rdpf), start(start), xor_offset(xor_offset),
  545. num_evals(num_evals), num_threads(num_threads),
  546. aes_ops(aes_ops) {}
  547. // Run the parallel evaluator. The type V is the type of the
  548. // accumulator; init should be the "zero" value of the accumulator.
  549. // The type W (process) is a lambda type with the signature
  550. // (int, address_t, const T::node &) -> V
  551. // which will be called like this for each i from 0 to num_evals-1,
  552. // across num_thread threads:
  553. // value_i = process(t, i, DPF((start+i) XOR xor_offset))
  554. // t is the thread number (0 <= t < num_threads).
  555. // The resulting num_evals values will be combined using V's +=
  556. // operator, first accumulating the values within each thread
  557. // (starting with the init value), and then accumulating the totals
  558. // from each thread together (again starting with the init value):
  559. //
  560. // total = init
  561. // for each thread t:
  562. // accum_t = init
  563. // for each accum_i generated by thread t:
  564. // accum_t += value_i
  565. // total += accum_t
  566. template <typename V, typename W>
  567. inline V reduce(V init, W process);
  568. };
  569. #include "rdpf.tcc"
  570. #endif