Skip to content

Commit a6fad69

Browse files
Add code to support precomputations in the hashed elgamal encryption code. (#271)
Co-authored-by: Jeff <spelmaa@wwu.edu>
1 parent 53a22dd commit a6fad69

3 files changed

Lines changed: 113 additions & 2 deletions

File tree

src/electionguard/elgamal.cpp

Lines changed: 13 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
#include "electionguard/elgamal.hpp"
22
#include "electionguard/hash.hpp"
33
#include "electionguard/hmac.hpp"
4+
#include "electionguard/precompute_buffers.hpp"
45

56
#include "../kremlin/Hacl_HMAC.h"
67
#include "../kremlin/Lib_Memzero0.h"
@@ -484,9 +485,19 @@ namespace electionguard
484485
uint32_t plaintext_len = plaintext_on_boundary.size();
485486
uint32_t num_xor_keys = plaintext_len / HASHED_CIPHERTEXT_BLOCK_LENGTH;
486487

487-
auto g_to_r = g_pow_p(nonce);
488+
unique_ptr<ElementModP> g_to_r = nullptr;
489+
unique_ptr<ElementModP> publicKey_to_r = nullptr;
490+
// check if the are precompute values rather than doing the exponentiations here
491+
unique_ptr<Triple> triple = PrecomputeBufferContext::getTriple();
492+
if (triple != nullptr) {
493+
g_to_r = triple->get_g_to_exp();
494+
publicKey_to_r = triple->get_pubkey_to_exp();
495+
} else {
496+
g_to_r = g_pow_p(nonce);
497+
498+
publicKey_to_r = pow_mod_p(publicKey, nonce);
499+
}
488500

489-
auto publicKey_to_r = pow_mod_p(publicKey, nonce);
490501

491502
// hash g_to_r and publicKey_to_r to get the master key
492503
auto master_key = hash_elems({g_to_r.get(), publicKey_to_r.get()});

test/electionguard/benchmark/bench_hashed_elgamal.cpp

Lines changed: 54 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@
44
#include <electionguard/constants.h>
55
#include <electionguard/elgamal.hpp>
66
#include <electionguard/group.hpp>
7+
#include <electionguard/precompute_buffers.hpp>
78

89
using namespace electionguard;
910
using namespace std;
@@ -51,4 +52,57 @@ BENCHMARK_DEFINE_F(HashedElgamalEncryptFixture, HashedElGamalEncrypt)(benchmark:
5152

5253
BENCHMARK_REGISTER_F(HashedElgamalEncryptFixture, HashedElGamalEncrypt)->Unit(benchmark::kMillisecond);
5354

55+
class HashedElgamalEncryptPrecomputeFixture : public benchmark::Fixture
56+
{
57+
public:
58+
void SetUp(const ::benchmark::State &state)
59+
{
60+
61+
nonce = rand_q();
62+
secret = ElementModQ::fromHex(a_fixed_secret);
63+
keypair = ElGamalKeyPair::fromSecret(TWO_MOD_Q(), false);
64+
uint64_t qwords_to_use[4] = {0x0102030405060708, 0x090a0b0c0d0e0f10, 0x1112131415161718,
65+
0x191a1b1c1d1e1f20};
66+
descriptionHash = make_unique<ElementModQ>(qwords_to_use);
67+
uint8_t bytes_to_use[32] = {0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08,
68+
0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f, 0x10,
69+
0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17, 0x18,
70+
0x19, 0x1a, 0x1b, 0x1c, 0x1d, 0x1e, 0x1f, 0x20};
71+
vector<uint8_t> plain(bytes_to_use, bytes_to_use + sizeof(bytes_to_use));
72+
plaintext = plain;
73+
74+
std::unique_ptr<HashedElGamalCiphertext> HEGResult = hashedElgamalEncrypt(
75+
plaintext, *nonce, *keypair->getPublicKey(), *descriptionHash, NO_PADDING);
76+
77+
// cause precomputed entries that will be used by the selection
78+
// encryptions, that should be more than enough and on teardown
79+
// the rest will be removed.
80+
PrecomputeBufferContext::init(300);
81+
PrecomputeBufferContext::populate(*keypair->getPublicKey());
82+
}
83+
84+
void TearDown(const ::benchmark::State &state)
85+
{
86+
PrecomputeBufferContext::empty_queues();
87+
}
88+
89+
unique_ptr<ElementModQ> nonce;
90+
unique_ptr<ElementModQ> secret;
91+
unique_ptr<ElGamalKeyPair> keypair;
92+
unique_ptr<ElementModQ> descriptionHash;
93+
vector<uint8_t> plaintext;
94+
};
95+
96+
BENCHMARK_DEFINE_F(HashedElgamalEncryptPrecomputeFixture, HashedElGamalEncryptPrecompute)
97+
(benchmark::State &state)
98+
{
99+
for (auto _ : state) {
100+
hashedElgamalEncrypt(plaintext, *nonce, *keypair->getPublicKey(), *descriptionHash,
101+
NO_PADDING);
102+
}
103+
}
104+
105+
BENCHMARK_REGISTER_F(HashedElgamalEncryptPrecomputeFixture, HashedElGamalEncryptPrecompute)
106+
->Unit(benchmark::kMillisecond);
107+
54108

test/electionguard/test_elgamal.cpp

Lines changed: 46 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -429,6 +429,52 @@ TEST_CASE("HashedElGamalCiphertext encrypt failure length cases")
429429
CHECK(encrypt_no_pad_not_block_length_failed);
430430
}
431431

432+
TEST_CASE("HashedElGamalCiphertext encrypt and decrypt string data with padding and precompute")
433+
{
434+
uint64_t qwords_to_use[4] = {0x0102030405060708, 0x090a0b0c0d0e0f10, 0x1112131415161718,
435+
0x191a1b1c1d1e1f20};
436+
437+
uint8_t bytes_to_use[32] = {0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0a, 0x0b,
438+
0x0c, 0x0d, 0x0e, 0x0f, 0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16,
439+
0x17, 0x18, 0x19, 0x1a, 0x1b, 0x1c, 0x1d, 0x1e, 0x1f, 0x20};
440+
441+
const auto nonce = make_unique<ElementModQ>(qwords_to_use);
442+
const auto descriptionHash = make_unique<ElementModQ>(qwords_to_use);
443+
const auto &secret = TWO_MOD_Q();
444+
auto keypair = ElGamalKeyPair::fromSecret(secret, false);
445+
auto *publicKey = keypair->getPublicKey();
446+
447+
std::wstring plaintext_string(L"ElectionGuard Rocks!");
448+
vector<uint8_t> plaintext((uint8_t *)&plaintext_string.front(),
449+
+(uint8_t *)&plaintext_string.front() +
450+
(plaintext_string.size() * 2));
451+
452+
// cause precomputed entries that will be used by the selection
453+
// encryptions, that should be more than enough and on teardown
454+
// the rest will be removed.
455+
PrecomputeBufferContext::init(3);
456+
PrecomputeBufferContext::populate(*keypair->getPublicKey());
457+
PrecomputeBufferContext::stop_populate();
458+
459+
auto HEGResult =
460+
hashedElgamalEncrypt(plaintext, *nonce, *publicKey, *descriptionHash, BYTES_128);
461+
462+
auto pad = HEGResult->getPad();
463+
unique_ptr<ElementModP> p_pad = make_unique<ElementModP>(*pad);
464+
auto ciphertext = HEGResult->getData();
465+
auto mac = HEGResult->getMac();
466+
467+
CHECK(ciphertext.size() == 128);
468+
469+
// now lets decrypt
470+
unique_ptr<HashedElGamalCiphertext> newHEG =
471+
make_unique<HashedElGamalCiphertext>(move(p_pad), HEGResult->getData(), HEGResult->getMac());
472+
473+
vector<uint8_t> new_plaintext = newHEG->decrypt(secret, *descriptionHash, true);
474+
475+
CHECK(plaintext == new_plaintext);
476+
PrecomputeBufferContext::empty_queues();
477+
}
432478

433479
TEST_CASE("elgamalEncrypt_with_precomputed encrypt 1, decrypts with secret")
434480
{

0 commit comments

Comments
 (0)