rdpf.hpp 8.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273
  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. struct RDPF {
  10. // The 128-bit seed
  11. DPFnode seed;
  12. // Which half of the DPF are we?
  13. bit_t whichhalf;
  14. // correction words; the depth of the DPF is the length of this
  15. // vector
  16. std::vector<DPFnode> cw;
  17. // correction flag bits: the one for level i is bit i of this word
  18. value_t cfbits;
  19. // The amount we have to scale the low words of the leaf values by
  20. // to get additive shares of a unit vector
  21. value_t unit_sum_inverse;
  22. // Additive share of the scaling value M_as such that the high words
  23. // of the leaf values for P0 and P1 add to M_as * e_{target}
  24. RegAS scaled_sum;
  25. // XOR share of the scaling value M_xs such that the high words
  26. // of the leaf values for P0 and P1 XOR to M_xs * e_{target}
  27. RegXS scaled_xor;
  28. // If we're saving the expansion, put it here
  29. std::vector<DPFnode> expansion;
  30. RDPF() {}
  31. // Construct a DPF with the given (XOR-shared) target location, and
  32. // of the given depth, to be used for random-access memory reads and
  33. // writes. The DPF is constructed collaboratively by P0 and P1,
  34. // with the server P2 helping by providing correlated randomness,
  35. // such as SelectTriples.
  36. //
  37. // Cost:
  38. // (2 DPFnode + 2 bytes)*depth + 1 word communication in
  39. // 2*depth + 1 messages
  40. // (2 DPFnode + 1 byte)*depth communication from P2 to each party
  41. // 2^{depth+1}-2 local AES operations for P0,P1
  42. // 0 local AES operations for P2
  43. RDPF(MPCTIO &tio, yield_t &yield,
  44. RegXS target, nbits_t depth, bool save_expansion = false);
  45. // The number of bytes it will take to store this RDPF
  46. size_t size() const;
  47. // The number of bytes it will take to store a RDPF of the given
  48. // depth
  49. static size_t size(nbits_t depth);
  50. // The depth
  51. inline nbits_t depth() const { return cw.size(); }
  52. // Descend from a node at depth parentdepth to one of its children
  53. // whichchild = 0: left child
  54. // whichchild = 1: right child
  55. //
  56. // Cost: 1 AES operation
  57. DPFnode descend(const DPFnode parent, nbits_t parentdepth,
  58. bit_t whichchild, size_t &op_counter) const;
  59. // Get the leaf node for the given input
  60. //
  61. // Cost: depth AES operations
  62. DPFnode leaf(address_t input, size_t &op_counter) const;
  63. // Expand the DPF if it's not already expanded
  64. void expand(size_t &op_counter);
  65. // Get the bit-shared unit vector entry from the leaf node
  66. inline RegBS unit_bs(DPFnode leaf) const {
  67. RegBS b;
  68. b.bshare = get_lsb(leaf);
  69. return b;
  70. }
  71. // Get the additive-shared unit vector entry from the leaf node
  72. inline RegAS unit_as(DPFnode leaf) const {
  73. RegAS a;
  74. value_t lowword = value_t(_mm_cvtsi128_si64x(leaf));
  75. if (whichhalf == 1) {
  76. lowword = -lowword;
  77. }
  78. a.ashare = lowword * unit_sum_inverse;
  79. return a;
  80. }
  81. // Get the XOR-shared scaled vector entry from the leaf ndoe
  82. inline RegXS scaled_xs(DPFnode leaf) const {
  83. RegXS x;
  84. value_t highword =
  85. value_t(_mm_cvtsi128_si64x(_mm_srli_si128(leaf,8)));
  86. x.xshare = highword;
  87. return x;
  88. }
  89. // Get the additive-shared scaled vector entry from the leaf ndoe
  90. inline RegAS scaled_as(DPFnode leaf) const {
  91. RegAS a;
  92. value_t highword =
  93. value_t(_mm_cvtsi128_si64x(_mm_srli_si128(leaf,8)));
  94. if (whichhalf == 1) {
  95. highword = -highword;
  96. }
  97. a.ashare = highword;
  98. return a;
  99. }
  100. };
  101. // I/O for RDPFs
  102. template <typename T>
  103. T& operator>>(T &is, RDPF &rdpf)
  104. {
  105. is.read((char *)&rdpf.seed, sizeof(rdpf.seed));
  106. uint8_t depth;
  107. // The whichhalf bit is the high bit of depth
  108. is.read((char *)&depth, sizeof(depth));
  109. rdpf.whichhalf = !!(depth & 0x80);
  110. depth &= 0x7f;
  111. bool read_expanded = false;
  112. if (depth > 64) {
  113. read_expanded = true;
  114. depth -= 64;
  115. }
  116. assert(depth <= ADDRESS_MAX_BITS);
  117. rdpf.cw.clear();
  118. for (uint8_t i=0; i<depth; ++i) {
  119. DPFnode cw;
  120. is.read((char *)&cw, sizeof(cw));
  121. rdpf.cw.push_back(cw);
  122. }
  123. if (read_expanded) {
  124. rdpf.expansion.resize(1<<depth);
  125. is.read((char *)rdpf.expansion.data(),
  126. sizeof(rdpf.expansion[0])<<depth);
  127. }
  128. value_t cfbits = 0;
  129. is.read((char *)&cfbits, BITBYTES(depth));
  130. rdpf.cfbits = cfbits;
  131. is.read((char *)&rdpf.unit_sum_inverse, sizeof(rdpf.unit_sum_inverse));
  132. is.read((char *)&rdpf.scaled_sum, sizeof(rdpf.scaled_sum));
  133. is.read((char *)&rdpf.scaled_xor, sizeof(rdpf.scaled_xor));
  134. return is;
  135. }
  136. // Write the DPF to the output stream. If expanded=true, then include
  137. // the expansion _if_ the DPF is itself already expanded. You can use
  138. // this to write DPFs to files.
  139. template <typename T>
  140. T& write_maybe_expanded(T &os, const RDPF &rdpf,
  141. bool expanded = true)
  142. {
  143. os.write((const char *)&rdpf.seed, sizeof(rdpf.seed));
  144. uint8_t depth = rdpf.cw.size();
  145. assert(depth <= ADDRESS_MAX_BITS);
  146. // The whichhalf bit is the high bit of depth
  147. // If we're writing an expansion, add 64 to depth as well
  148. uint8_t whichhalf_and_depth = depth |
  149. (uint8_t(rdpf.whichhalf)<<7);
  150. bool write_expansion = false;
  151. if (expanded && rdpf.expansion.size() == (size_t(1)<<depth)) {
  152. write_expansion = true;
  153. whichhalf_and_depth += 64;
  154. }
  155. os.write((const char *)&whichhalf_and_depth,
  156. sizeof(whichhalf_and_depth));
  157. for (uint8_t i=0; i<depth; ++i) {
  158. os.write((const char *)&rdpf.cw[i], sizeof(rdpf.cw[i]));
  159. }
  160. if (write_expansion) {
  161. os.write((const char *)rdpf.expansion.data(),
  162. sizeof(rdpf.expansion[0])<<depth);
  163. }
  164. os.write((const char *)&rdpf.cfbits, BITBYTES(depth));
  165. os.write((const char *)&rdpf.unit_sum_inverse, sizeof(rdpf.unit_sum_inverse));
  166. os.write((const char *)&rdpf.scaled_sum, sizeof(rdpf.scaled_sum));
  167. os.write((const char *)&rdpf.scaled_xor, sizeof(rdpf.scaled_xor));
  168. return os;
  169. }
  170. // The ordinary << version never writes the expansion, since this is
  171. // what we use to send DPFs over the network.
  172. template <typename T>
  173. T& operator<<(T &os, const RDPF &rdpf)
  174. {
  175. return write_maybe_expanded(os, rdpf, false);
  176. }
  177. // Computational peers will generate triples of RDPFs with the _same_
  178. // random target for use in Duoram. They will each hold a share of the
  179. // target (neither knowing the complete target index). They will each
  180. // give one of the DPFs (not a matching pair) to the server, but not the
  181. // shares of the target index. So computational peers will hold a
  182. // RDPFTriple (which includes both an additive and an XOR share of the
  183. // target index), while the server will hold a RDPFPair (which does
  184. // not).
  185. struct RDPFTriple {
  186. RegAS as_target;
  187. RegXS xs_target;
  188. RDPF dpf[3];
  189. RDPFTriple() {}
  190. // Construct three RDPFs of the given depth all with the same
  191. // randomly generated target index.
  192. RDPFTriple(MPCTIO &tio, yield_t &yield,
  193. nbits_t depth, bool save_expansion = false);
  194. };
  195. // I/O for RDPF Triples
  196. // We never write RDPFTriples over the network, so always write
  197. // the DPF expansions if they're available.
  198. template <typename T>
  199. T& operator<<(T &os, const RDPFTriple &rdpftrip)
  200. {
  201. write_maybe_expanded(os, rdpftrip.dpf[0], true);
  202. write_maybe_expanded(os, rdpftrip.dpf[1], true);
  203. write_maybe_expanded(os, rdpftrip.dpf[2], true);
  204. nbits_t depth = rdpftrip.dpf[0].depth();
  205. os.write((const char *)&rdpftrip.as_target.ashare, BITBYTES(depth));
  206. os.write((const char *)&rdpftrip.xs_target.xshare, BITBYTES(depth));
  207. return os;
  208. }
  209. template <typename T>
  210. T& operator>>(T &is, RDPFTriple &rdpftrip)
  211. {
  212. is >> rdpftrip.dpf[0] >> rdpftrip.dpf[1] >> rdpftrip.dpf[2];
  213. nbits_t depth = rdpftrip.dpf[0].depth();
  214. rdpftrip.as_target.ashare = 0;
  215. is.read((char *)&rdpftrip.as_target.ashare, BITBYTES(depth));
  216. rdpftrip.xs_target.xshare = 0;
  217. is.read((char *)&rdpftrip.xs_target.xshare, BITBYTES(depth));
  218. return is;
  219. }
  220. struct RDPFPair {
  221. RDPF dpf[2];
  222. };
  223. // I/O for RDPF Pairs
  224. // We never write RDPFPairs over the network, so always write
  225. // the DPF expansions if they're available.
  226. template <typename T>
  227. T& operator<<(T &os, const RDPFPair &rdpfpair)
  228. {
  229. write_maybe_expanded(os, rdpfpair.dpf[0], true);
  230. write_maybe_expanded(os, rdpfpair.dpf[1], true);
  231. return os;
  232. }
  233. template <typename T>
  234. T& operator>>(T &is, RDPFPair &rdpfpair)
  235. {
  236. is >> rdpfpair.dpf[0] >> rdpfpair.dpf[1];
  237. return is;
  238. }
  239. #endif