Skip to content

Commit 53a22dd

Browse files
Add support to use precomputations when computing the constant chaum … (#269)
* Add support to use precomputations when computing the constant chaum pedersen proof associated with a contest. * Fix the initialization of the count to determine how often we produce computations for the triple queue. * Update the way the caller determines if there precomputed values for TwoTriplesAndAQuadruple Co-authored-by: Jeff <spelmaa@wwu.edu>
1 parent 95482bb commit 53a22dd

8 files changed

Lines changed: 97 additions & 56 deletions

File tree

include/electionguard/precompute_buffers.hpp

Lines changed: 10 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -115,10 +115,9 @@ namespace electionguard
115115
unique_ptr<Triple> triple1;
116116
unique_ptr<Triple> triple2;
117117
unique_ptr<Quadruple> quad;
118-
bool populated = false;
119118

120119
public:
121-
explicit TwoTriplesAndAQuadruple() { populated = false; }
120+
explicit TwoTriplesAndAQuadruple() {}
122121
TwoTriplesAndAQuadruple(unique_ptr<Triple> in_triple1, unique_ptr<Triple> in_triple2,
123122
unique_ptr<Quadruple> in_quad);
124123
TwoTriplesAndAQuadruple(const TwoTriplesAndAQuadruple &other);
@@ -134,8 +133,6 @@ namespace electionguard
134133

135134
unique_ptr<Quadruple> get_quad() { return quad->clone(); }
136135

137-
bool isPopulated() { return populated; }
138-
139136
unique_ptr<TwoTriplesAndAQuadruple> clone();
140137
};
141138

@@ -225,6 +222,14 @@ namespace electionguard
225222
/// </summary>
226223
static std::unique_ptr<TwoTriplesAndAQuadruple> getTwoTriplesAndAQuadruple();
227224

225+
/// <summary>
226+
/// Get the next triple from the triple queue.
227+
/// This method is called by hashedElgamalEncrypt in order to get
228+
/// the precomputed value to perform the hashed elgamal encryption.
229+
/// <returns>std::unique_ptr<Triple></returns>
230+
/// </summary>
231+
static std::unique_ptr<Triple> getTriple();
232+
228233
/// <summary>
229234
/// Empty the precomputed values queues.
230235
/// <returns>void</returns>
@@ -236,7 +241,7 @@ namespace electionguard
236241
static std::mutex queue_lock;
237242
bool populate_OK = false;
238243
std::queue<std::unique_ptr<Triple>> triple_queue;
239-
std::queue<std::unique_ptr<Quadruple>> quadruple_queue;
244+
std::queue<std::unique_ptr<TwoTriplesAndAQuadruple>> twoTriplesAndAQuadruple_queue;
240245
};
241246
} // namespace electionguard
242247

src/electionguard/chaum_pedersen.cpp

Lines changed: 15 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -581,13 +581,24 @@ namespace electionguard
581581
auto *alpha = message.getPad();
582582
auto *beta = message.getData();
583583

584-
// Pick a random number in Q.
584+
// Derive nonce from seed and the constant string below
585585
auto nonces = make_unique<Nonces>(seed, "constant-chaum-pedersen-proof");
586-
auto u = nonces->get(0);
586+
unique_ptr<ElementModQ> u;
587587

588588
// Compute the NIZKP
589-
auto a = g_pow_p(*u); //𝑔^𝑢 mod 𝑝
590-
auto b = pow_mod_p(k, *u); // 𝐾^𝑢 mod 𝑝
589+
unique_ptr<ElementModP> a; //𝑔^𝑢 mod 𝑝
590+
unique_ptr<ElementModP> b; // 𝐾^𝑢 mod 𝑝
591+
// check if the are precompute values rather than doing the exponentiations here
592+
unique_ptr<Triple> triple = PrecomputeBufferContext::getTriple();
593+
if (triple != nullptr) {
594+
u = triple->get_exp();
595+
a = triple->get_g_to_exp();
596+
b = triple->get_pubkey_to_exp();
597+
} else {
598+
u = nonces->get(0);
599+
a = g_pow_p(*u); //𝑔^𝑢 mod 𝑝
600+
b = pow_mod_p(k, *u); // 𝐾^𝑢 mod 𝑝
601+
}
591602

592603
// sha256(𝑄', A, B, a, b)
593604
auto c =

src/electionguard/encrypt.cpp

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -234,7 +234,7 @@ namespace electionguard
234234
{
235235
unique_ptr<CiphertextBallotSelection> encrypted = NULL;
236236
unique_ptr<ElGamalCiphertext> ciphertext;
237-
unique_ptr<TwoTriplesAndAQuadruple> precomputedTwoTriplesAndAQuad;
237+
unique_ptr<TwoTriplesAndAQuadruple> precomputedTwoTriplesAndAQuad = nullptr;
238238

239239
// Validate Input
240240
if (!selection.isValid(description.getObjectId())) {
@@ -256,7 +256,7 @@ namespace electionguard
256256
precomputedTwoTriplesAndAQuad = PrecomputeBufferContext::getTwoTriplesAndAQuadruple();
257257

258258
// check if we found the precomputed values needed
259-
if (precomputedTwoTriplesAndAQuad->isPopulated()) {
259+
if (precomputedTwoTriplesAndAQuad != nullptr) {
260260
auto triple1 = precomputedTwoTriplesAndAQuad->get_triple1();
261261
auto g_to_exp = triple1->get_g_to_exp();
262262
auto pubkey_to_exp = triple1->get_pubkey_to_exp();

src/electionguard/precompute_buffers.cpp

Lines changed: 48 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -144,7 +144,6 @@ namespace electionguard
144144
this->triple1 = move(triple1);
145145
this->triple2 = move(triple2);
146146
this->quad = move(quad);
147-
populated = true;
148147
}
149148

150149
TwoTriplesAndAQuadruple::TwoTriplesAndAQuadruple(const TwoTriplesAndAQuadruple &other)
@@ -204,9 +203,10 @@ namespace electionguard
204203
// This loop goes through until the queues are full but can be stopped
205204
// between generations of two triples and a quad. By full it means
206205
// we check how many quads are in the queue, to start with we will
207-
// try 500 and see how that works. If the vendor wanted to pass the
206+
// try 5000 and see how that works. If the vendor wanted to pass the
208207
// queue size in we could use that.
209208
// for now we just go through the loop once
209+
int iteration_count = 0;
210210
do {
211211
// TODO - Can we tolerate not returning until we have finished
212212
// generating two triples and a quadruple?
@@ -220,16 +220,31 @@ namespace electionguard
220220
unique_ptr<Triple> triple2 = make_unique<Triple>(elgamalPublicKey);
221221
unique_ptr<Quadruple> quad = make_unique<Quadruple>(elgamalPublicKey);
222222

223-
getInstance().triple_queue.push(move(triple1));
224-
225-
getInstance().triple_queue.push(move(triple2));
226-
227-
getInstance().quadruple_queue.push(move(quad));
228-
223+
unique_ptr<TwoTriplesAndAQuadruple> twoTriplesAndAQuadruple =
224+
make_unique<TwoTriplesAndAQuadruple>(move(triple1), move(triple2), move(quad));
225+
226+
getInstance().twoTriplesAndAQuadruple_queue.push(move(twoTriplesAndAQuadruple));
227+
228+
// This is very rudimentary. We can add a more complex algorithm in
229+
// the future, that would look at the queues and increase production if one
230+
// is getting lower than expected.
231+
// Every third iteration we generate two extra triples, one for use with
232+
// the contest constant chaum pedersen proof and one for hashed elgamal encryption
233+
// we need less of these because this exponentiation is done only every contest
234+
// encryption whereas the two triples and a quadruple is used every selection
235+
// encryption. The generating two triples every third iteration is a guess
236+
// on how many precomputes we will need.
237+
if ((iteration_count % 3) == 0) {
238+
unique_ptr<Triple> contest_triple1 = make_unique<Triple>(elgamalPublicKey);
239+
getInstance().triple_queue.push(move(contest_triple1));
240+
unique_ptr<Triple> contest_triple2 = make_unique<Triple>(elgamalPublicKey);
241+
getInstance().triple_queue.push(move(contest_triple2));
242+
}
243+
iteration_count++;
229244
} else {
230245
return;
231246
}
232-
} while (getInstance().quadruple_queue.size() < getInstance().max);
247+
} while (getInstance().twoTriplesAndAQuadruple_queue.size() < getInstance().max);
233248
}
234249

235250
void PrecomputeBufferContext::stop_populate() { getInstance().populate_OK = false; }
@@ -238,29 +253,36 @@ namespace electionguard
238253

239254
uint32_t PrecomputeBufferContext::get_current_queue_size()
240255
{
241-
return getInstance().quadruple_queue.size();
256+
return getInstance().twoTriplesAndAQuadruple_queue.size();
242257
}
243258

244259
std::unique_ptr<TwoTriplesAndAQuadruple> PrecomputeBufferContext::getTwoTriplesAndAQuadruple()
245260
{
246-
unique_ptr<TwoTriplesAndAQuadruple> result = make_unique<TwoTriplesAndAQuadruple>();
261+
unique_ptr<TwoTriplesAndAQuadruple> result = nullptr;
247262

248263
// take a lock while we get the triples and a quadruple
249264
std::lock_guard<std::mutex> lock(queue_lock);
250265

251266
// make sure there are enough in the queues
252-
if ((getInstance().triple_queue.size() >= 2) &&
253-
(getInstance().quadruple_queue.size() >= 1)) {
267+
if (!getInstance().twoTriplesAndAQuadruple_queue.empty()) {
268+
result = std::move(getInstance().twoTriplesAndAQuadruple_queue.front());
269+
getInstance().twoTriplesAndAQuadruple_queue.pop();
270+
}
254271

255-
unique_ptr<Triple> triple1 = std::move(getInstance().triple_queue.front());
256-
getInstance().triple_queue.pop();
257-
unique_ptr<Triple> triple2 = std::move(getInstance().triple_queue.front());
258-
getInstance().triple_queue.pop();
272+
return result;
273+
}
259274

260-
unique_ptr<Quadruple> quad = std::move(getInstance().quadruple_queue.front());
261-
getInstance().quadruple_queue.pop();
275+
std::unique_ptr<Triple> PrecomputeBufferContext::getTriple()
276+
{
277+
unique_ptr<Triple> result = nullptr;
278+
279+
// take a lock while we get the triples and a quadruple
280+
std::lock_guard<std::mutex> lock(queue_lock);
262281

263-
result = make_unique<TwoTriplesAndAQuadruple>(move(triple1), move(triple2), move(quad));
282+
// make sure there are enough in the queues
283+
if (!getInstance().triple_queue.empty()) {
284+
result = std::move(getInstance().triple_queue.front());
285+
getInstance().triple_queue.pop();
264286
}
265287

266288
return result;
@@ -269,11 +291,13 @@ namespace electionguard
269291
void PrecomputeBufferContext::empty_queues()
270292
{
271293
std::lock_guard<std::mutex> lock(queue_lock);
272-
uint32_t size = getInstance().quadruple_queue.size();
273-
for (int i = 0; i < (int)size; i++) {
294+
uint32_t triple_size = getInstance().triple_queue.size();
295+
for (int i = 0; i < (int)triple_size; i++) {
274296
getInstance().triple_queue.pop();
275-
getInstance().triple_queue.pop();
276-
getInstance().quadruple_queue.pop();
297+
}
298+
uint32_t twoTriplesAndAQuadruple_size = getInstance().twoTriplesAndAQuadruple_queue.size();
299+
for (int i = 0; i < (int)twoTriplesAndAQuadruple_size; i++) {
300+
getInstance().twoTriplesAndAQuadruple_queue.pop();
277301
}
278302
}
279303

test/electionguard/benchmark/bench_precompute.cpp

Lines changed: 9 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -46,7 +46,7 @@ BENCHMARK_DEFINE_F(ElGamalEncryptPrecomputedFixture, ElGamalEncryptPrecomputed)(
4646
auto precomputedTwoTriplesAndAQuad = PrecomputeBufferContext::getTwoTriplesAndAQuadruple();
4747

4848
// check if we found the precomputed values needed
49-
if (precomputedTwoTriplesAndAQuad->isPopulated()) {
49+
if (precomputedTwoTriplesAndAQuad != nullptr) {
5050
auto triple1 = precomputedTwoTriplesAndAQuad->get_triple1();
5151
auto g_to_exp = triple1->get_g_to_exp();
5252
auto pubkey_to_exp = triple1->get_pubkey_to_exp();
@@ -102,7 +102,7 @@ class ChaumPedersenPrecomputedFixture : public benchmark::Fixture
102102
auto precomputedTwoTriplesAndAQuad = PrecomputeBufferContext::getTwoTriplesAndAQuadruple();
103103

104104
// check if we found the precomputed values needed
105-
if (precomputedTwoTriplesAndAQuad->isPopulated()) {
105+
if (precomputedTwoTriplesAndAQuad != nullptr) {
106106
disjunctive = DisjunctiveChaumPedersenProof::make_with_precomputed(
107107
*message, move(precomputedTwoTriplesAndAQuad), ONE_MOD_Q(), 1);
108108
}
@@ -130,7 +130,7 @@ BENCHMARK_DEFINE_F(ChaumPedersenPrecomputedFixture, disjunctiveChaumPedersenPrec
130130
auto precomputedTwoTriplesAndAQuad = PrecomputeBufferContext::getTwoTriplesAndAQuadruple();
131131

132132
// check if we found the precomputed values needed
133-
if (precomputedTwoTriplesAndAQuad->isPopulated()) {
133+
if (precomputedTwoTriplesAndAQuad != nullptr) {
134134
DisjunctiveChaumPedersenProof::make_with_precomputed(
135135
*message, move(precomputedTwoTriplesAndAQuad), ONE_MOD_Q(), 1);
136136
}
@@ -147,7 +147,7 @@ BENCHMARK_DEFINE_F(ChaumPedersenPrecomputedFixture, disjunctiveChaumPedersenPrec
147147
auto precomputedTwoTriplesAndAQuad = PrecomputeBufferContext::getTwoTriplesAndAQuadruple();
148148

149149
// check if we found the precomputed values needed
150-
if (precomputedTwoTriplesAndAQuad->isPopulated()) {
150+
if (precomputedTwoTriplesAndAQuad != nullptr) {
151151
auto item = DisjunctiveChaumPedersenProofPrecomputedHarness::make_zero_with_precomputed(
152152
*message, move(precomputedTwoTriplesAndAQuad), ONE_MOD_Q());
153153
}
@@ -164,7 +164,7 @@ BENCHMARK_DEFINE_F(ChaumPedersenPrecomputedFixture, disjunctiveChaumPedersenPrec
164164
auto precomputedTwoTriplesAndAQuad = PrecomputeBufferContext::getTwoTriplesAndAQuadruple();
165165

166166
// check if we found the precomputed values needed
167-
if (precomputedTwoTriplesAndAQuad->isPopulated()) {
167+
if (precomputedTwoTriplesAndAQuad != nullptr) {
168168
auto item = DisjunctiveChaumPedersenProofPrecomputedHarness::make_one_with_precomputed(
169169
*message, move(precomputedTwoTriplesAndAQuad), ONE_MOD_Q());
170170
}
@@ -202,7 +202,7 @@ class CiphertextBallotSelectionPrecomputedFixture : public benchmark::Fixture
202202
auto precomputedTwoTriplesAndAQuad = PrecomputeBufferContext::getTwoTriplesAndAQuadruple();
203203

204204
// check if we found the precomputed values needed
205-
if (precomputedTwoTriplesAndAQuad->isPopulated()) {
205+
if (precomputedTwoTriplesAndAQuad != nullptr) {
206206
auto triple1 = precomputedTwoTriplesAndAQuad->get_triple1();
207207
auto g_to_exp = triple1->get_g_to_exp();
208208
auto pubkey_to_exp = triple1->get_pubkey_to_exp();
@@ -236,7 +236,7 @@ BENCHMARK_DEFINE_F(CiphertextBallotSelectionPrecomputedFixture,
236236
auto precomputedTwoTriplesAndAQuad = PrecomputeBufferContext::getTwoTriplesAndAQuadruple();
237237

238238
// check if we found the precomputed values needed
239-
if (precomputedTwoTriplesAndAQuad->isPopulated()) {
239+
if (precomputedTwoTriplesAndAQuad != nullptr) {
240240
auto triple1 = precomputedTwoTriplesAndAQuad->get_triple1();
241241
auto g_to_exp = triple1->get_g_to_exp();
242242
auto pubkey_to_exp = triple1->get_pubkey_to_exp();
@@ -340,6 +340,7 @@ BENCHMARK_DEFINE_F(PrecomputeFixture, precomputed)
340340

341341
auto precomputedTwoTriplesAndAQuad = PrecomputeBufferContext::getTwoTriplesAndAQuadruple();
342342
}
343+
PrecomputeBufferContext::empty_queues();
343344
}
344345

345346
BENCHMARK_REGISTER_F(PrecomputeFixture, precomputed)
@@ -395,6 +396,7 @@ class EncryptBallotPrecomputeFixture : public benchmark::Fixture
395396

396397
BENCHMARK_DEFINE_F(EncryptBallotPrecomputeFixture, encryptBallotPrecompute_Full_NoProofCheck)(benchmark::State &state)
397398
{
399+
PrecomputeBufferContext::stop_populate();
398400
while (state.KeepRunning()) {
399401
auto result = encryptBallot(*ballot, *internal, *context, *device->getHash(),
400402
make_unique<ElementModQ>(*nonce), 0UL, false);

test/electionguard/test_chaum_pedersen.cpp

Lines changed: 3 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -146,13 +146,9 @@ TEST_CASE("Disjunctive CP Proof simple valid inputs generate valid proofs")
146146
auto precomputedTwoTriplesAndAQuad4 = PrecomputeBufferContext::getTwoTriplesAndAQuadruple();
147147

148148
CHECK(precomputedTwoTriplesAndAQuad1 != nullptr);
149-
CHECK(precomputedTwoTriplesAndAQuad1->isPopulated() == true);
150149
CHECK(precomputedTwoTriplesAndAQuad2 != nullptr);
151-
CHECK(precomputedTwoTriplesAndAQuad2->isPopulated() == true);
152150
CHECK(precomputedTwoTriplesAndAQuad3 != nullptr);
153-
CHECK(precomputedTwoTriplesAndAQuad3->isPopulated() == true);
154151
CHECK(precomputedTwoTriplesAndAQuad4 != nullptr);
155-
CHECK(precomputedTwoTriplesAndAQuad4->isPopulated() == true);
156152

157153
auto triple1_1 = precomputedTwoTriplesAndAQuad1->get_triple1();
158154
auto triple1_2 = precomputedTwoTriplesAndAQuad2->get_triple1();
@@ -194,6 +190,7 @@ TEST_CASE("Disjunctive CP Proof simple valid inputs generate valid proofs")
194190
false);
195191
CHECK(fourthMessageOneProof->isValid(*fourthMessage, *keypair->getPublicKey(), ONE_MOD_Q()) ==
196192
true);
193+
PrecomputeBufferContext::empty_queues();
197194
}
198195

199196
TEST_CASE("Disjunctive CP Proof encryption of zero with precomputed values")
@@ -213,9 +210,7 @@ TEST_CASE("Disjunctive CP Proof encryption of zero with precomputed values")
213210
auto precomputedTwoTriplesAndAQuad2 = PrecomputeBufferContext::getTwoTriplesAndAQuadruple();
214211

215212
CHECK(precomputedTwoTriplesAndAQuad1 != nullptr);
216-
CHECK(precomputedTwoTriplesAndAQuad1->isPopulated() == true);
217213
CHECK(precomputedTwoTriplesAndAQuad2 != nullptr);
218-
CHECK(precomputedTwoTriplesAndAQuad2->isPopulated() == true);
219214

220215
auto triple1_1 = precomputedTwoTriplesAndAQuad1->get_triple1();
221216
auto triple1_2 = precomputedTwoTriplesAndAQuad2->get_triple1();
@@ -233,6 +228,7 @@ TEST_CASE("Disjunctive CP Proof encryption of zero with precomputed values")
233228

234229
CHECK(proof->isValid(*message1, *keypair->getPublicKey(), ONE_MOD_Q()) == true);
235230
CHECK(badProof->isValid(*message2, *keypair->getPublicKey(), ONE_MOD_Q()) == false);
231+
PrecomputeBufferContext::empty_queues();
236232
}
237233

238234
TEST_CASE("Disjunctive CP Proof encryption of one with precomputed values")
@@ -252,9 +248,7 @@ TEST_CASE("Disjunctive CP Proof encryption of one with precomputed values")
252248
auto precomputedTwoTriplesAndAQuad2 = PrecomputeBufferContext::getTwoTriplesAndAQuadruple();
253249

254250
CHECK(precomputedTwoTriplesAndAQuad1 != nullptr);
255-
CHECK(precomputedTwoTriplesAndAQuad1->isPopulated() == true);
256251
CHECK(precomputedTwoTriplesAndAQuad2 != nullptr);
257-
CHECK(precomputedTwoTriplesAndAQuad2->isPopulated() == true);
258252

259253
auto triple1_1 = precomputedTwoTriplesAndAQuad1->get_triple1();
260254
auto triple1_2 = precomputedTwoTriplesAndAQuad2->get_triple1();
@@ -272,4 +266,5 @@ TEST_CASE("Disjunctive CP Proof encryption of one with precomputed values")
272266

273267
CHECK(proof->isValid(*message1, *keypair->getPublicKey(), ONE_MOD_Q()) == true);
274268
CHECK(badProof->isValid(*message2, *keypair->getPublicKey(), ONE_MOD_Q()) == false);
269+
PrecomputeBufferContext::empty_queues();
275270
}

test/electionguard/test_elgamal.cpp

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -83,7 +83,6 @@ TEST_CASE("elgamalEncrypt simple encrypt 0 compared with elgamalEncrypt_with_pre
8383
auto precomputedTwoTriplesAndAQuad = PrecomputeBufferContext::getTwoTriplesAndAQuadruple();
8484

8585
CHECK(precomputedTwoTriplesAndAQuad != nullptr);
86-
CHECK(precomputedTwoTriplesAndAQuad->isPopulated() == true);
8786

8887
auto triple1 = precomputedTwoTriplesAndAQuad->get_triple1();
8988

@@ -98,6 +97,7 @@ TEST_CASE("elgamalEncrypt simple encrypt 0 compared with elgamalEncrypt_with_pre
9897
CHECK((0UL == decrypted1));
9998
auto decrypted2 = cipherText2->decrypt(secret);
10099
CHECK((0UL == decrypted2));
100+
PrecomputeBufferContext::empty_queues();
101101
}
102102

103103
TEST_CASE("elgamalEncrypt_with_precomputed simple encrypt 0")
@@ -118,7 +118,6 @@ TEST_CASE("elgamalEncrypt_with_precomputed simple encrypt 0")
118118
auto precomputedTwoTriplesAndAQuad = PrecomputeBufferContext::getTwoTriplesAndAQuadruple();
119119

120120
CHECK(precomputedTwoTriplesAndAQuad != nullptr);
121-
CHECK(precomputedTwoTriplesAndAQuad->isPopulated() == true);
122121

123122
auto triple1 = precomputedTwoTriplesAndAQuad->get_triple1();
124123
CHECK(triple1 != nullptr);
@@ -128,6 +127,7 @@ TEST_CASE("elgamalEncrypt_with_precomputed simple encrypt 0")
128127

129128
auto decrypted = cipherText->decrypt(secret);
130129
CHECK((0UL == decrypted));
130+
PrecomputeBufferContext::empty_queues();
131131
}
132132

133133
TEST_CASE("elgamalEncrypt simple encrypt 1 decrypts with secret")
@@ -454,4 +454,5 @@ TEST_CASE("elgamalEncrypt_with_precomputed encrypt 1, decrypts with secret")
454454

455455
auto decrypted = cipherText->decrypt(*secret);
456456
CHECK(1UL == decrypted);
457+
PrecomputeBufferContext::empty_queues();
457458
}

0 commit comments

Comments
 (0)