rdpf.hpp 8.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283
  1. #ifndef __RDPF_HPP__
  2. #define __RDPF_HPP__
  3. #include <vector>
  4. #include <iostream>
  5. #include "mpcio.hpp"
  6. #include "coroutine.hpp"
  7. #include "types.hpp"
  8. #include "bitutils.hpp"
  9. // Streaming evaluation, to avoid taking up enough memory to store
  10. // an entire evaluation. T can be RDPF, RDPFPair, or RDPFTriple.
  11. template <typename T>
  12. class StreamEval {
  13. const T &rdpf;
  14. size_t &op_counter;
  15. bool use_expansion;
  16. nbits_t depth;
  17. address_t counter_xor_offset;
  18. address_t indexmask;
  19. address_t pathindex;
  20. address_t nextindex;
  21. std::vector<typename T::node> path;
  22. public:
  23. // Create an Eval object that will start its output at index start.
  24. // It will wrap around to 0 when it hits 2^depth. If use_expansion
  25. // is true, then if the DPF has been expanded, just output values
  26. // from that. If use_expansion=false or if the DPF has not been
  27. // expanded, compute the values on the fly. If xor_offset is
  28. // non-zero, then the outputs are actually
  29. // DPF(start XOR xor_offset)
  30. // DPF((start+1) XOR xor_offset)
  31. // DPF((start+2) XOR xor_offset)
  32. // etc.
  33. StreamEval(const T &rdpf, address_t start,
  34. address_t xor_offset, size_t &op_counter,
  35. bool use_expansion = true);
  36. // Get the next value (or tuple of values) from the evaluator
  37. typename T::node next();
  38. };
  39. struct RDPF {
  40. // The type of nodes
  41. using node = DPFnode;
  42. // The 128-bit seed
  43. DPFnode seed;
  44. // Which half of the DPF are we?
  45. bit_t whichhalf;
  46. // correction words; the depth of the DPF is the length of this
  47. // vector
  48. std::vector<DPFnode> cw;
  49. // correction flag bits: the one for level i is bit i of this word
  50. value_t cfbits;
  51. // The amount we have to scale the low words of the leaf values by
  52. // to get additive shares of a unit vector
  53. value_t unit_sum_inverse;
  54. // Additive share of the scaling value M_as such that the high words
  55. // of the leaf values for P0 and P1 add to M_as * e_{target}
  56. RegAS scaled_sum;
  57. // XOR share of the scaling value M_xs such that the high words
  58. // of the leaf values for P0 and P1 XOR to M_xs * e_{target}
  59. RegXS scaled_xor;
  60. // If we're saving the expansion, put it here
  61. std::vector<DPFnode> expansion;
  62. RDPF() {}
  63. // Construct a DPF with the given (XOR-shared) target location, and
  64. // of the given depth, to be used for random-access memory reads and
  65. // writes. The DPF is constructed collaboratively by P0 and P1,
  66. // with the server P2 helping by providing correlated randomness,
  67. // such as SelectTriples.
  68. //
  69. // Cost:
  70. // (2 DPFnode + 2 bytes)*depth + 1 word communication in
  71. // 2*depth + 1 messages
  72. // (2 DPFnode + 1 byte)*depth communication from P2 to each party
  73. // 2^{depth+1}-2 local AES operations for P0,P1
  74. // 0 local AES operations for P2
  75. RDPF(MPCTIO &tio, yield_t &yield,
  76. RegXS target, nbits_t depth, bool save_expansion = false);
  77. // The number of bytes it will take to store this RDPF
  78. size_t size() const;
  79. // The number of bytes it will take to store a RDPF of the given
  80. // depth
  81. static size_t size(nbits_t depth);
  82. // The depth
  83. inline nbits_t depth() const { return cw.size(); }
  84. // The seed
  85. inline node get_seed() const { return seed; }
  86. // Do we have a precomputed expansion?
  87. inline bool has_expansion() const { return expansion.size() > 0; }
  88. // Get an element of the expansion
  89. inline node get_expansion(address_t index) const {
  90. return expansion[index];
  91. }
  92. // Descend from a node at depth parentdepth to one of its children
  93. // whichchild = 0: left child
  94. // whichchild = 1: right child
  95. //
  96. // Cost: 1 AES operation
  97. DPFnode descend(const DPFnode &parent, nbits_t parentdepth,
  98. bit_t whichchild, size_t &op_counter) const;
  99. // Get the leaf node for the given input
  100. //
  101. // Cost: depth AES operations
  102. DPFnode leaf(address_t input, size_t &op_counter) const;
  103. // Expand the DPF if it's not already expanded
  104. void expand(size_t &op_counter);
  105. // Get the bit-shared unit vector entry from the leaf node
  106. inline RegBS unit_bs(DPFnode leaf) const {
  107. RegBS b;
  108. b.bshare = get_lsb(leaf);
  109. return b;
  110. }
  111. // Get the additive-shared unit vector entry from the leaf node
  112. inline RegAS unit_as(DPFnode leaf) const {
  113. RegAS a;
  114. value_t lowword = value_t(_mm_cvtsi128_si64x(leaf));
  115. if (whichhalf == 1) {
  116. lowword = -lowword;
  117. }
  118. a.ashare = lowword * unit_sum_inverse;
  119. return a;
  120. }
  121. // Get the XOR-shared scaled vector entry from the leaf ndoe
  122. inline RegXS scaled_xs(DPFnode leaf) const {
  123. RegXS x;
  124. value_t highword =
  125. value_t(_mm_cvtsi128_si64x(_mm_srli_si128(leaf,8)));
  126. x.xshare = highword;
  127. return x;
  128. }
  129. // Get the additive-shared scaled vector entry from the leaf ndoe
  130. inline RegAS scaled_as(DPFnode leaf) const {
  131. RegAS a;
  132. value_t highword =
  133. value_t(_mm_cvtsi128_si64x(_mm_srli_si128(leaf,8)));
  134. if (whichhalf == 1) {
  135. highword = -highword;
  136. }
  137. a.ashare = highword;
  138. return a;
  139. }
  140. };
  141. // Computational peers will generate triples of RDPFs with the _same_
  142. // random target for use in Duoram. They will each hold a share of the
  143. // target (neither knowing the complete target index). They will each
  144. // give one of the DPFs (not a matching pair) to the server, but not the
  145. // shares of the target index. So computational peers will hold a
  146. // RDPFTriple (which includes both an additive and an XOR share of the
  147. // target index), while the server will hold a RDPFPair (which does
  148. // not).
  149. struct RDPFTriple {
  150. // The type of node triples
  151. using node = std::tuple<DPFnode, DPFnode, DPFnode>;
  152. RegAS as_target;
  153. RegXS xs_target;
  154. RDPF dpf[3];
  155. // The depth
  156. inline nbits_t depth() const { return dpf[0].depth(); }
  157. // The seed
  158. inline node get_seed() const {
  159. return std::make_tuple(dpf[0].get_seed(), dpf[1].get_seed(),
  160. dpf[2].get_seed());
  161. }
  162. // Do we have a precomputed expansion?
  163. inline bool has_expansion() const {
  164. return dpf[0].expansion.size() > 0;
  165. }
  166. // Get an element of the expansion
  167. inline node get_expansion(address_t index) const {
  168. return std::make_tuple(dpf[0].get_expansion(index),
  169. dpf[1].get_expansion(index), dpf[2].get_expansion(index));
  170. }
  171. RDPFTriple() {}
  172. // Construct three RDPFs of the given depth all with the same
  173. // randomly generated target index.
  174. RDPFTriple(MPCTIO &tio, yield_t &yield,
  175. nbits_t depth, bool save_expansion = false);
  176. // Descend the three RDPFs in lock step
  177. node descend(const node &parent, nbits_t parentdepth,
  178. bit_t whichchild, size_t &op_counter) const;
  179. // Templated versions of functions to get DPF components and outputs
  180. // so that the appropriate one can be selected with a template
  181. // parameter
  182. template <typename T>
  183. inline std::tuple<T,T,T> scaled_value() const;
  184. template <typename T>
  185. inline std::tuple<T,T,T> unit(node leaf) const;
  186. template <typename T>
  187. inline std::tuple<T,T,T> scaled(node leaf) const;
  188. };
  189. struct RDPFPair {
  190. // The type of node pairs
  191. using node = std::tuple<DPFnode, DPFnode>;
  192. RDPF dpf[2];
  193. RDPFPair() {}
  194. // Create an RDPFPair from an RDPFTriple, keeping two of the RDPFs
  195. // and dropping one. This _moves_ the dpfs from the triple to the
  196. // pair, so the triple will no longer be valid after using this.
  197. // which0 and which1 indicate which of the dpfs to keep.
  198. RDPFPair(RDPFTriple &&trip, int which0, int which1) {
  199. dpf[0] = std::move(trip.dpf[which0]);
  200. dpf[1] = std::move(trip.dpf[which1]);
  201. }
  202. // The depth
  203. inline nbits_t depth() const { return dpf[0].depth(); }
  204. // The seed
  205. inline node get_seed() const {
  206. return std::make_tuple(dpf[0].get_seed(), dpf[1].get_seed());
  207. }
  208. // Do we have a precomputed expansion?
  209. inline bool has_expansion() const {
  210. return dpf[0].expansion.size() > 0;
  211. }
  212. // Get an element of the expansion
  213. inline node get_expansion(address_t index) const {
  214. return std::make_tuple(dpf[0].get_expansion(index),
  215. dpf[1].get_expansion(index));
  216. }
  217. // Descend the two RDPFs in lock step
  218. node descend(const node &parent, nbits_t parentdepth,
  219. bit_t whichchild, size_t &op_counter) const;
  220. // Templated versions of functions to get DPF components and outputs
  221. // so that the appropriate one can be selected with a template
  222. // parameter
  223. template <typename T>
  224. inline std::tuple<T,T> scaled_value() const;
  225. template <typename T>
  226. inline std::tuple<T,T> unit(node leaf) const;
  227. template <typename T>
  228. inline std::tuple<T,T> scaled(node leaf) const;
  229. };
  230. #include "rdpf.tcc"
  231. #endif