Repository navigation
Expand file tree
/
Copy pathsemi_evaluator_dotp.cpp
More file actions
377 lines (336 loc) · 17.6 KB
/
Copy pathsemi_evaluator_dotp.cpp
File metadata and controls
377 lines (336 loc) · 17.6 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
#include "semi_evaluator_dotp.h"
#include <array>
#include <numeric>
namespace semi
{
/*
override Evaluator as we need to add custom setup for dot products
*/
void DotPEvaluator::runSetup(const std::unordered_map<common::utils::wire_t, int> &input_mapping)
{
/*
Parallelization approach here:
==> We do not actually parallelize, as we empirically noticed that parallelization will not
make the setup faster, but even slower in some cases.
If one would still want to parallelize:
We do not want to parallelize over gates because the setup for interactive gates
uses PRG calls that we have to keep in order.
Instead, we parallelize PRG calls to different PRGs done by the dealer as this
party is the bottleneck.
For non-interactive layers, we still parallelize over them.
*/
std::vector<Ring> gamma_to_send; /// Gamma shares to send to the last party.
size_t message_size = 0;
for (size_t depth = 0; depth < circ_.gates_by_level.size(); ++depth)
{
if (circ_.level_mults[depth] == 0 && depth > 0) { // Only noninteractive gates
// Safe to parallelize because non-interactive gates do not utilize the PRGs
// require depth > 0 because input gates also use PRGs (they are somehwat
// semi-interactively in a sense that while they are interactive, their
// interaction is handled outside the normal circuit evaluation later).
// #pragma omp parallel for // If parallelization is desired here, completely disabled as documented above
for (size_t i = 0; i < circ_.gates_by_level[depth].size(); i++) {
auto &gate = circ_.gates_by_level[depth][i];
switch (gate->type) {
case common::utils::GateType::kAdd: {
// For each addition gate, we just locally add the masks of both input wires:
auto *g = static_cast<common::utils::FIn2Gate *>(gate.get());
wires_[g->out].getLambda() = wires_[g->in1].getLambda() + wires_[g->in2].getLambda();
break;
}
case common::utils::GateType::kConstMul: {
// For each constant multiplication gate, we just locally multiply the input wire's mask with the constant
auto *g = static_cast<common::utils::ConstOpGate<Ring> *>(gate.get());
wires_[g->out].getLambda() = wires_[g->in].getLambda() * g->cval;
break;
}
case common::utils::GateType::kInp: // interactive
case common::utils::GateType::kMul: // interactive
case common::utils::GateType::kDotprod: // interactive
{
std::cout << gate->type << std::endl;
throw std::runtime_error("Interactive gate not allowed here in parallelized setup context.");
}
default: {
std::cout << gate->type << std::endl;
throw std::runtime_error("Unsupported GateType.");
}
}
}
} else { // only interactive gates (we do the layering so that each layer is fully noninteractive or fully interactive)
// Interactive gates, not to be processed in parallel
for (size_t i = 0; i < circ_.gates_by_level[depth].size(); i++) {
auto &gate = circ_.gates_by_level[depth][i];
switch (gate->type) {
case common::utils::GateType::kInp: {
/*
Say, party Pi provides the input, i.e., input_mapping[input_wire] == i.
Then, lambda^i is sampled as usual with a key known to D and Pi.
Meanwhile, all other lambda^j are set to 0.
This only works as we rerandomize masks on opening values.
*/
size_t i = input_mapping.at(gate->out);
if (id_ == 0) {
Ring fullLambda = 0;
for (size_t j = 1; j <= nP_; j++) {
Ring x;
if (i == j) {
x = getRandomElem(rgen_.as_D_with_i(i));
} else {
x = 0;
}
fullLambda += x;
}
wires_[gate->out].getLambda() = fullLambda;
} else if (id_ == i) {
Ring lambda = getRandomElem(rgen_.me_and_D());
wires_[gate->out].getLambda() = lambda;
setup_gates_[gate->out] = std::make_unique<rzkf::SetupInput<Ring>>(lambda);
} else {
wires_[gate->out].getLambda() = 0;
}
break;
}
case common::utils::GateType::kMul: {
// Compute gamma.
// This corresponds to Pi_mult steps 1 and 2.
// Dealer and each party except nP_ non-interactively sample their share,
// Dealer computes the remaining share and adds it to gamma_to_send which
// will later be sent to party nP_ as one single, big message.
// Also compute the output mask lambda_z
std::vector<Ring> lambdaShares(nP_ + 1);
if (id_ == 0) {
auto *g = static_cast<common::utils::FIn2Gate *>(gate.get());
Ring gamma = wires_[g->in1].getLambda() * wires_[g->in2].getLambda();
Ring fullLambda = 0; // for output mask
for (size_t i = 1; i < nP_; i++) {
Ring x = getRandomElem(rgen_.as_D_with_i(i));
Ring y = getRandomElem(rgen_.as_D_with_i(i));
lambdaShares[i] = y;
fullLambda += y;
gamma -= x;
}
Ring last_x = gamma;
Ring last_y = getRandomElem(rgen_.as_D_with_i(nP_));
lambdaShares[nP_] = last_y;
fullLambda += last_y;
gamma_to_send.push_back(last_x); // remaining share
wires_[gate->out].getLambda() = fullLambda;
} else if (id_ == nP_) {
// Receive later
message_size++;
wires_[gate->out].getLambda() = getRandomElem(rgen_.me_and_D());
} else {
Ring share = getRandomElem(rgen_.me_and_D());
// Party stores its share of gamma for the online phase
setup_gates_[gate->out] = std::make_unique<rzkf::SetupMult<Ring>>(share);
wires_[gate->out].getLambda() = getRandomElem(rgen_.me_and_D());
}
break;
}
case common::utils::GateType::kDotprod: {
// Compute gamma.
// This corresponds to Pi_mult steps 1 and 2.
// Dealer and each party except nP_ non-interactively sample their share,
// Dealer computes the remaining share and adds it to gamma_to_send which
// will later be sent to party nP_ as one single, big message.
// Also compute the output mask lambda_z
std::vector<Ring> lambdaShares(nP_ + 1);
if (id_ == 0) {
auto *g = static_cast<common::utils::SIMDGate *>(gate.get());
Ring gamma = 0;
for (size_t i = 0; i < g->in1.size(); i++) {
gamma += wires_[g->in1[i]].getLambda() * wires_[g->in2[i]].getLambda();
}
Ring fullLambda = 0; // for output mask
for (size_t i = 1; i < nP_; i++) {
Ring x = getRandomElem(rgen_.as_D_with_i(i));
Ring y = getRandomElem(rgen_.as_D_with_i(i));
lambdaShares[i] = y;
fullLambda += y;
gamma -= x;
}
Ring last_x = gamma;
Ring last_y = getRandomElem(rgen_.as_D_with_i(nP_));
lambdaShares[nP_] = last_y;
fullLambda += last_y;
gamma_to_send.push_back(last_x); // remaining share
wires_[gate->out].getLambda() = fullLambda;
} else if (id_ == nP_) {
// Receive later
message_size++;
wires_[gate->out].getLambda() = getRandomElem(rgen_.me_and_D());
} else {
Ring share = getRandomElem(rgen_.me_and_D());
// Party stores its share of gamma for the online phase
setup_gates_[gate->out] = std::make_unique<rzkf::SetupMult<Ring>>(share);
wires_[gate->out].getLambda() = getRandomElem(rgen_.me_and_D());
}
break;
}
case common::utils::GateType::kAdd: // not interactive
case common::utils::GateType::kConstMul: // not interactive
{
std::cout << gate->type << std::endl;
throw std::runtime_error("Non-interactive gate should not be here in un-parallelized context.");
}
default: {
std::cout << gate->type << std::endl;
throw std::runtime_error("Unsupported GateType.");
}
}
}
}
}
// Dealer sends all gamma shares for party nP_ to that party.
if (id_ == 0) {
network_->send(nP_, gamma_to_send.data(), sizeof(Ring) * gamma_to_send.size());
} else if (id_ == nP_) {
gamma_to_send.resize(message_size);
network_->recv(0, gamma_to_send.data(), sizeof(Ring) * message_size);
}
// Party nP_ stores all its gamma shares as preprocessing material for the specific corresponding gates.
message_size = 0;
for (size_t depth = 0; depth < circ_.gates_by_level.size(); ++depth)
{
for (auto &gate : circ_.gates_by_level[depth])
{
switch (gate->type)
{
case common::utils::GateType::kInp:
case common::utils::GateType::kAdd:
case common::utils::GateType::kConstMul:
break;
case common::utils::GateType::kMul:
case common::utils::GateType::kDotprod:
{
if (id_ == nP_) {
Ring share = gamma_to_send[message_size];
setup_gates_[gate->out] = std::make_unique<rzkf::SetupMult<Ring>>(share);
message_size++;
}
break;
}
default: {
std::cout << gate->type << std::endl;
throw std::runtime_error("Unsupported GateType.");
}
}
}
}
}
/*
override Evaluator as we need to add custom setup for dot products
*/
void DotPEvaluator::evaluateGatesAtDepthPartySend(size_t depth, std::vector<Ring> &mult_vals)
{
#pragma omp parallel for
for (size_t i = 0; i < circ_.gates_by_level[depth].size(); i++) {
auto &gate = circ_.gates_by_level[depth][i];
switch (gate->type)
{
case common::utils::GateType::kMul:
{
auto *g = static_cast<common::utils::FIn2Gate *>(gate.get());
if (id_ != 0)
{
auto *setup_mat = static_cast<rzkf::SetupMult<Ring> *>(setup_gates_[gate->out].get());
// This is Pi_mult step 3:
Ring val = wires_[g->in1].getM() * wires_[g->in2].getLambda()
+ wires_[g->in1].getLambda() * wires_[g->in2].getM()
+ setup_mat->gamma_share
- wires_[gate->out].getLambda();
if (id_ == 1) // Only one party must add the constant m_x * m_y
val += wires_[g->in1].getM() * wires_[g->in2].getM();
mult_vals[i] = val; // Own share to later be communicated
}
break;
}
case common::utils::GateType::kDotprod:
{
auto *g = static_cast<common::utils::SIMDGate *>(gate.get());
if (id_ != 0)
{
auto *setup_mat = static_cast<rzkf::SetupMult<Ring> *>(setup_gates_[gate->out].get());
// This is Pi_mult step 3, generalized to vectors:
Ring val = 0;
for (size_t i = 0; i < g->in1.size(); i++) {
val += wires_[g->in1[i]].getM() * wires_[g->in2[i]].getLambda()
+ wires_[g->in1[i]].getLambda() * wires_[g->in2[i]].getM();
if (id_ == 1) // Only one party must add the constant m_x * m_y
val += wires_[g->in1[i]].getM() * wires_[g->in2[i]].getM();
}
val += setup_mat->gamma_share - wires_[gate->out].getLambda();
mult_vals[i] = val; // Own share to later be communicated
}
break;
}
case ::common::utils::GateType::kAdd:
case ::common::utils::GateType::kConstMul:
case ::common::utils::GateType::kInp:
{
break;
}
default:
{
std::cout << gate->type << std::endl;
throw std::runtime_error("Unsupported GateType.");
}
}
}
}
/*
override Evaluator as we need to add custom setup for dot products
*/
void DotPEvaluator::evaluateGatesAtDepthPartyRecv(size_t depth, std::vector<Ring> mult_vals)
{
#pragma omp parallel for
for (size_t i = 0; i < circ_.gates_by_level[depth].size(); i++) {
auto &gate = circ_.gates_by_level[depth][i];
switch (gate->type)
{
case common::utils::GateType::kAdd:
{
auto *g = static_cast<common::utils::FIn2Gate *>(gate.get());
if (id_ != 0)
wires_[g->out].getM() = wires_[g->in1].getM() + wires_[g->in2].getM(); // Online simply adds up m values
break;
}
case common::utils::GateType::kConstMul:
{
auto *g = static_cast<common::utils::ConstOpGate<Ring> *>(gate.get());
if (id_ != 0)
wires_[g->out].getM() = wires_[g->in].getM() * g->cval; // Online simply multiplies m value by const
break;
}
case common::utils::GateType::kMul:
{
auto *g = static_cast<common::utils::FIn2Gate *>(gate.get());
if (id_ != 0)
{
wires_[g->out].getM() = mult_vals[i]; // use as m what has been reconstructed in the interaction phase
}
break;
}
case common::utils::GateType::kDotprod:
{
auto *g = static_cast<common::utils::SIMDGate *>(gate.get());
if (id_ != 0)
{
wires_[g->out].getM() = mult_vals[i]; // use as m what has been reconstructed in the interaction phase
}
break;
}
case ::common::utils::GateType::kInp:
{
break;
}
default:
{
std::cout << gate->type << std::endl;
throw std::runtime_error("Unsupported GateType.");
}
}
}
}
}; // namespace semi