rdpf.hpp 8.1 KB

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