duoram.tcc 18 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532
  1. // Templated method implementations for duoram.hpp
  2. #include <stdio.h>
  3. // Pass the player number and desired size
  4. template <typename T>
  5. Duoram<T>::Duoram(int player, size_t size) : player(player),
  6. oram_size(size), p0_blind(blind), p1_blind(peer_blinded_db) {
  7. if (player < 2) {
  8. database.resize(size);
  9. blind.resize(size);
  10. peer_blinded_db.resize(size);
  11. } else {
  12. p0_blind.resize(size);
  13. p1_blind.resize(size);
  14. }
  15. }
  16. // For debugging; print the contents of the Duoram to stdout
  17. template <typename T>
  18. void Duoram<T>::dump() const
  19. {
  20. for (size_t i=0; i<oram_size; ++i) {
  21. if (player < 2) {
  22. printf("%04lx %016lx %016lx %016lx\n",
  23. i, database[i].share(), blind[i].share(),
  24. peer_blinded_db[i].share());
  25. } else {
  26. printf("%04lx %016lx %016lx\n",
  27. i, p0_blind[i].share(), p1_blind[i].share());
  28. }
  29. }
  30. printf("\n");
  31. }
  32. // For debugging or checking your answers (using this in general is
  33. // of course insecure)
  34. // This one reconstructs the whole database
  35. template <typename T>
  36. std::vector<T> Duoram<T>::Shape::reconstruct() const
  37. {
  38. int player = tio.player();
  39. std::vector<T> res;
  40. res.resize(duoram.size());
  41. // Player 1 sends their share of the database to player 0
  42. if (player == 1) {
  43. tio.queue_peer(duoram.database.data(), duoram.size()*sizeof(T));
  44. } else if (player == 0) {
  45. tio.recv_peer(res.data(), duoram.size()*sizeof(T));
  46. for(size_t i=0;i<duoram.size();++i) {
  47. res[i] += duoram.database[i];
  48. }
  49. }
  50. // The server (player 2) does nothing
  51. // Players 1 and 2 will get an empty vector here
  52. return res;
  53. }
  54. // This one reconstructs a single database value
  55. template <typename T>
  56. T Duoram<T>::Shape::reconstruct(const T& share) const
  57. {
  58. int player = tio.player();
  59. T res;
  60. // Player 1 sends their share of the value to player 0
  61. if (player == 1) {
  62. tio.queue_peer(&share, sizeof(T));
  63. } else if (player == 0) {
  64. tio.recv_peer(&res, sizeof(T));
  65. res += share;
  66. }
  67. // The server (player 2) does nothing
  68. // Players 1 and 2 will get 0 here
  69. return res;
  70. }
  71. // Function to set the shape_size of a shape and compute the number of
  72. // bits you need to address a shape of that size (which is the number of
  73. // bits in sz-1). This is typically called by subclass constructors.
  74. template <typename T>
  75. void Duoram<T>::Shape::set_shape_size(size_t sz)
  76. {
  77. shape_size = sz;
  78. // Compute the number of bits in (sz-1)
  79. // But use 0 if sz=0 for some reason (though that should never
  80. // happen)
  81. if (sz > 1) {
  82. addr_size = 64-__builtin_clzll(sz-1);
  83. addr_mask = address_t((size_t(1)<<addr_size)-1);
  84. } else {
  85. addr_size = 0;
  86. addr_mask = 0;
  87. }
  88. }
  89. // Constructor for the Flat shape. len=0 means the maximum size (the
  90. // parent's size minus start).
  91. template <typename T>
  92. Duoram<T>::Flat::Flat(Duoram &duoram, MPCTIO &tio, yield_t &yield,
  93. size_t start, size_t len) : Shape(*this, duoram, tio, yield)
  94. {
  95. size_t parentsize = duoram.size();
  96. if (start > parentsize) {
  97. start = parentsize;
  98. }
  99. this->start = start;
  100. size_t maxshapesize = parentsize - start;
  101. if (len > maxshapesize || len == 0) {
  102. len = maxshapesize;
  103. }
  104. this->set_shape_size(len);
  105. }
  106. // Oblivious read from an additively shared index of Duoram memory
  107. template <typename T>
  108. Duoram<T>::Shape::MemRefAS::operator T()
  109. {
  110. T res;
  111. int player = shape.tio.player();
  112. if (player < 2) {
  113. // Computational players do this
  114. RDPFTriple dt = shape.tio.rdpftriple(shape.addr_size);
  115. // Compute the index offset
  116. RegAS indoffset = dt.as_target;
  117. indoffset -= idx;
  118. // We only need two of the DPFs for reading
  119. RDPFPair dp(std::move(dt), 0, player == 0 ? 2 : 1);
  120. // The RDPFTriple dt is now broken, since we've moved things out
  121. // of it.
  122. // Send it to the peer and the server
  123. shape.tio.queue_peer(&indoffset, BITBYTES(shape.addr_size));
  124. shape.tio.queue_server(&indoffset, BITBYTES(shape.addr_size));
  125. shape.yield();
  126. // Receive the above from the peer
  127. RegAS peerindoffset;
  128. shape.tio.recv_peer(&peerindoffset, BITBYTES(shape.addr_size));
  129. // Reconstruct the total offset
  130. auto indshift = combine(indoffset, peerindoffset, shape.addr_size);
  131. // Evaluate the DPFs and compute the dotproducts
  132. StreamEval ev(dp, indshift, 0, shape.tio.aes_ops());
  133. for (size_t i=0; i<shape.shape_size; ++i) {
  134. auto L = ev.next();
  135. // The values from the two DPFs
  136. auto [V0, V1] = dp.unit<T>(L);
  137. // References to the appropriate cells in our database, our
  138. // blind, and our copy of the peer's blinded database
  139. auto [DB, BL, PBD] = shape.get_comp(i);
  140. res += (DB + PBD) * V0.share() - BL * (V1-V0).share();
  141. }
  142. // Receive the cancellation term from the server
  143. T gamma;
  144. shape.tio.iostream_server() >> gamma;
  145. res += gamma;
  146. } else {
  147. // The server does this
  148. RDPFPair dp = shape.tio.rdpfpair(shape.addr_size);
  149. RegAS p0indoffset, p1indoffset;
  150. // Receive the index offset from the computational players and
  151. // combine them
  152. shape.tio.recv_p0(&p0indoffset, BITBYTES(shape.addr_size));
  153. shape.tio.recv_p1(&p1indoffset, BITBYTES(shape.addr_size));
  154. auto indshift = combine(p0indoffset, p1indoffset, shape.addr_size);
  155. // Evaluate the DPFs to compute the cancellation terms
  156. T gamma0, gamma1;
  157. StreamEval ev(dp, indshift, 0, shape.tio.aes_ops());
  158. for (size_t i=0; i<shape.shape_size; ++i) {
  159. auto L = ev.next();
  160. // The values from the two DPFs
  161. auto [V0, V1] = dp.unit<T>(L);
  162. // shape.get_server(i) returns a pair of references to the
  163. // appropriate cells in the two blinded databases
  164. auto [BL0, BL1] = shape.get_server(i);
  165. gamma0 -= BL0 * V1.share();
  166. gamma1 -= BL1 * V0.share();
  167. }
  168. // Choose a random blinding factor
  169. T rho;
  170. rho.randomize();
  171. gamma0 += rho;
  172. gamma1 -= rho;
  173. // Send the cancellation terms to the computational players
  174. shape.tio.iostream_p0() << gamma0;
  175. shape.tio.iostream_p1() << gamma1;
  176. shape.yield();
  177. }
  178. return res; // The server will always get 0
  179. }
  180. // Oblivious update to an additively shared index of Duoram memory
  181. template <typename T>
  182. typename Duoram<T>::Shape::MemRefAS
  183. &Duoram<T>::Shape::MemRefAS::operator+=(const T& M)
  184. {
  185. int player = shape.tio.player();
  186. if (player < 2) {
  187. // Computational players do this
  188. RDPFTriple dt = shape.tio.rdpftriple(shape.addr_size);
  189. // Compute the index and message offsets
  190. RegAS indoffset = dt.as_target;
  191. indoffset -= idx;
  192. auto Moffset = std::make_tuple(M, M, M);
  193. Moffset -= dt.scaled_value<T>();
  194. // Send them to the peer, and everything except the first offset
  195. // to the server
  196. shape.tio.queue_peer(&indoffset, BITBYTES(shape.addr_size));
  197. shape.tio.iostream_peer() << Moffset;
  198. shape.tio.queue_server(&indoffset, BITBYTES(shape.addr_size));
  199. shape.tio.iostream_server() << std::get<1>(Moffset) <<
  200. std::get<2>(Moffset);
  201. shape.yield();
  202. // Receive the above from the peer
  203. RegAS peerindoffset;
  204. std::tuple<T,T,T> peerMoffset;
  205. shape.tio.recv_peer(&peerindoffset, BITBYTES(shape.addr_size));
  206. shape.tio.iostream_peer() >> peerMoffset;
  207. // Reconstruct the total offsets
  208. auto indshift = combine(indoffset, peerindoffset, shape.addr_size);
  209. auto Mshift = combine(Moffset, peerMoffset);
  210. // Evaluate the DPFs and add them to the database
  211. StreamEval ev(dt, indshift, 0, shape.tio.aes_ops());
  212. for (size_t i=0; i<shape.shape_size; ++i) {
  213. auto L = ev.next();
  214. // The values from the three DPFs
  215. auto [V0, V1, V2] = dt.scaled<T>(L) + dt.unit<T>(L) * Mshift;
  216. // References to the appropriate cells in our database, our
  217. // blind, and our copy of the peer's blinded database
  218. auto [DB, BL, PBD] = shape.get_comp(i);
  219. DB += V0;
  220. if (player == 0) {
  221. BL -= V1;
  222. PBD += V2-V0;
  223. } else {
  224. BL -= V2;
  225. PBD += V1-V0;
  226. }
  227. }
  228. } else {
  229. // The server does this
  230. RDPFPair dp = shape.tio.rdpfpair(shape.addr_size);
  231. RegAS p0indoffset, p1indoffset;
  232. std::tuple<T,T> p0Moffset, p1Moffset;
  233. // Receive the index and message offsets from the computational
  234. // players and combine them
  235. shape.tio.recv_p0(&p0indoffset, BITBYTES(shape.addr_size));
  236. shape.tio.iostream_p0() >> p0Moffset;
  237. shape.tio.recv_p1(&p1indoffset, BITBYTES(shape.addr_size));
  238. shape.tio.iostream_p1() >> p1Moffset;
  239. auto indshift = combine(p0indoffset, p1indoffset, shape.addr_size);
  240. auto Mshift = combine(p0Moffset, p1Moffset);
  241. // Evaluate the DPFs and subtract them from the blinds
  242. StreamEval ev(dp, indshift, 0, shape.tio.aes_ops());
  243. for (size_t i=0; i<shape.shape_size; ++i) {
  244. auto L = ev.next();
  245. // The values from the two DPFs
  246. auto V = dp.scaled<T>(L) + dp.unit<T>(L) * Mshift;
  247. // shape.get_server(i) returns a pair of references to the
  248. // appropriate cells in the two blinded databases, so we can
  249. // subtract the pair directly.
  250. shape.get_server(i) -= V;
  251. }
  252. }
  253. return *this;
  254. }
  255. // The MemRefXS routines are almost identical to the MemRefAS routines,
  256. // but I couldn't figure out how to get them to be two instances of a
  257. // template. Sorry for the code duplication.
  258. // Oblivious read from an XOR shared index of Duoram memory
  259. template <typename T>
  260. Duoram<T>::Shape::MemRefXS::operator T()
  261. {
  262. T res;
  263. int player = shape.tio.player();
  264. if (player < 2) {
  265. // Computational players do this
  266. RDPFTriple dt = shape.tio.rdpftriple(shape.addr_size);
  267. // Compute the index offset
  268. RegXS indoffset = dt.xs_target;
  269. indoffset -= idx;
  270. // We only need two of the DPFs for reading
  271. RDPFPair dp(std::move(dt), 0, player == 0 ? 2 : 1);
  272. // Send it to the peer and the server
  273. shape.tio.queue_peer(&indoffset, BITBYTES(shape.addr_size));
  274. shape.tio.queue_server(&indoffset, BITBYTES(shape.addr_size));
  275. shape.yield();
  276. // Receive the above from the peer
  277. RegXS peerindoffset;
  278. shape.tio.recv_peer(&peerindoffset, BITBYTES(shape.addr_size));
  279. // Reconstruct the total offset
  280. auto indshift = combine(indoffset, peerindoffset, shape.addr_size);
  281. // Evaluate the DPFs and compute the dotproducts
  282. StreamEval ev(dp, 0, indshift, shape.tio.aes_ops());
  283. for (size_t i=0; i<shape.shape_size; ++i) {
  284. auto L = ev.next();
  285. // The values from the two DPFs
  286. auto [V0, V1] = dp.unit<T>(L);
  287. // References to the appropriate cells in our database, our
  288. // blind, and our copy of the peer's blinded database
  289. auto [DB, BL, PBD] = shape.get_comp(i);
  290. res += (DB + PBD) * V0.share() - BL * (V1-V0).share();
  291. }
  292. // Receive the cancellation term from the server
  293. T gamma;
  294. shape.tio.iostream_server() >> gamma;
  295. res += gamma;
  296. } else {
  297. // The server does this
  298. RDPFPair dp = shape.tio.rdpfpair(shape.addr_size);
  299. RegXS p0indoffset, p1indoffset;
  300. // Receive the index offset from the computational players and
  301. // combine them
  302. shape.tio.recv_p0(&p0indoffset, BITBYTES(shape.addr_size));
  303. shape.tio.recv_p1(&p1indoffset, BITBYTES(shape.addr_size));
  304. auto indshift = combine(p0indoffset, p1indoffset, shape.addr_size);
  305. // Evaluate the DPFs to compute the cancellation terms
  306. T gamma0, gamma1;
  307. StreamEval ev(dp, 0, indshift, shape.tio.aes_ops());
  308. for (size_t i=0; i<shape.shape_size; ++i) {
  309. auto L = ev.next();
  310. // The values from the two DPFs
  311. auto [V0, V1] = dp.unit<T>(L);
  312. // shape.get_server(i) returns a pair of references to the
  313. // appropriate cells in the two blinded databases
  314. auto [BL0, BL1] = shape.get_server(i);
  315. gamma0 -= BL0 * V1.share();
  316. gamma1 -= BL1 * V0.share();
  317. }
  318. // Choose a random blinding factor
  319. T rho;
  320. rho.randomize();
  321. gamma0 += rho;
  322. gamma1 -= rho;
  323. // Send the cancellation terms to the computational players
  324. shape.tio.iostream_p0() << gamma0;
  325. shape.tio.iostream_p1() << gamma1;
  326. shape.yield();
  327. }
  328. return res; // The server will always get 0
  329. }
  330. // Oblivious update to an XOR shared index of Duoram memory
  331. template <typename T>
  332. typename Duoram<T>::Shape::MemRefXS
  333. &Duoram<T>::Shape::MemRefXS::operator+=(const T& M)
  334. {
  335. int player = shape.tio.player();
  336. if (player < 2) {
  337. // Computational players do this
  338. RDPFTriple dt = shape.tio.rdpftriple(shape.addr_size);
  339. // Compute the index and message offsets
  340. RegXS indoffset = dt.xs_target;
  341. indoffset -= idx;
  342. auto Moffset = std::make_tuple(M, M, M);
  343. Moffset -= dt.scaled_value<T>();
  344. // Send them to the peer, and everything except the first offset
  345. // to the server
  346. shape.tio.queue_peer(&indoffset, BITBYTES(shape.addr_size));
  347. shape.tio.iostream_peer() << Moffset;
  348. shape.tio.queue_server(&indoffset, BITBYTES(shape.addr_size));
  349. shape.tio.iostream_server() << std::get<1>(Moffset) <<
  350. std::get<2>(Moffset);
  351. shape.yield();
  352. // Receive the above from the peer
  353. RegXS peerindoffset;
  354. std::tuple<T,T,T> peerMoffset;
  355. shape.tio.recv_peer(&peerindoffset, BITBYTES(shape.addr_size));
  356. shape.tio.iostream_peer() >> peerMoffset;
  357. // Reconstruct the total offsets
  358. auto indshift = combine(indoffset, peerindoffset, shape.addr_size);
  359. auto Mshift = combine(Moffset, peerMoffset);
  360. // Evaluate the DPFs and add them to the database
  361. StreamEval ev(dt, 0, indshift, shape.tio.aes_ops());
  362. for (size_t i=0; i<shape.shape_size; ++i) {
  363. auto L = ev.next();
  364. // The values from the three DPFs
  365. auto [V0, V1, V2] = dt.scaled<T>(L) + dt.unit<T>(L) * Mshift;
  366. // References to the appropriate cells in our database, our
  367. // blind, and our copy of the peer's blinded database
  368. auto [DB, BL, PBD] = shape.get_comp(i);
  369. DB += V0;
  370. if (player == 0) {
  371. BL -= V1;
  372. PBD += V2-V0;
  373. } else {
  374. BL -= V2;
  375. PBD += V1-V0;
  376. }
  377. }
  378. } else {
  379. // The server does this
  380. RDPFPair dp = shape.tio.rdpfpair(shape.addr_size);
  381. RegXS p0indoffset, p1indoffset;
  382. std::tuple<T,T> p0Moffset, p1Moffset;
  383. // Receive the index and message offsets from the computational
  384. // players and combine them
  385. shape.tio.recv_p0(&p0indoffset, BITBYTES(shape.addr_size));
  386. shape.tio.iostream_p0() >> p0Moffset;
  387. shape.tio.recv_p1(&p1indoffset, BITBYTES(shape.addr_size));
  388. shape.tio.iostream_p1() >> p1Moffset;
  389. auto indshift = combine(p0indoffset, p1indoffset, shape.addr_size);
  390. auto Mshift = combine(p0Moffset, p1Moffset);
  391. // Evaluate the DPFs and subtract them from the blinds
  392. StreamEval ev(dp, 0, indshift, shape.tio.aes_ops());
  393. for (size_t i=0; i<shape.shape_size; ++i) {
  394. auto L = ev.next();
  395. // The values from the two DPFs
  396. auto V = dp.scaled<T>(L) + dp.unit<T>(L) * Mshift;
  397. // shape.get_server(i) returns a pair of references to the
  398. // appropriate cells in the two blinded databases, so we can
  399. // subtract the pair directly.
  400. shape.get_server(i) -= V;
  401. }
  402. }
  403. return *this;
  404. }
  405. // Explicit read from a given index of Duoram memory
  406. template <typename T>
  407. Duoram<T>::Shape::MemRefExpl::operator T()
  408. {
  409. T res;
  410. int player = shape.tio.player();
  411. if (player < 2) {
  412. res = std::get<0>(shape.get_comp(idx));
  413. }
  414. return res; // The server will always get 0
  415. }
  416. // Explicit update to a given index of Duoram memory
  417. template <typename T>
  418. typename Duoram<T>::Shape::MemRefExpl
  419. &Duoram<T>::Shape::MemRefExpl::operator+=(const T& M)
  420. {
  421. int player = shape.tio.player();
  422. if (player < 2) {
  423. // Computational players do this
  424. // Pick a blinding factor
  425. T blind;
  426. blind.randomize();
  427. // Send the blind to the server, and the blinded value to the
  428. // peer
  429. shape.tio.iostream_server() << blind;
  430. shape.tio.iostream_peer() << (M + blind);
  431. shape.yield();
  432. // Receive the peer's blinded value
  433. T peerblinded;
  434. shape.tio.iostream_peer() >> peerblinded;
  435. // Our database, our blind, the peer's blinded database
  436. auto [ DB, BL, PBD ] = shape.get_comp(idx);
  437. DB += M;
  438. BL += blind;
  439. PBD += peerblinded;
  440. } else if (player == 2) {
  441. // The server does this
  442. // Receive the updates to the blinds
  443. T p0blind, p1blind;
  444. shape.tio.iostream_p0() >> p0blind;
  445. shape.tio.iostream_p1() >> p1blind;
  446. // The two computational parties' blinds
  447. auto [ BL0, BL1 ] = shape.get_server(idx);
  448. BL0 += p0blind;
  449. BL1 += p1blind;
  450. }
  451. return *this;
  452. }