- Add checks for the context and manifest matching manifesthash values (#306)
- Add C++ and C# unit tests
This commit is contained in:
Родитель
6ba0bcab88
Коммит
62d1d030d3
|
@ -154,5 +154,48 @@ namespace ElectionGuard.Encrypt.Tests
|
|||
Assert.That(constants.Contains(Constants.G.ToHex()));
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void Test_EncryptMediator_Hashes_Match()
|
||||
{
|
||||
var keypair = ElGamalKeyPair.FromSecret(Constants.TWO_MOD_Q);
|
||||
var manifest = ManifestGenerator.GetManifestFromFile();
|
||||
var internalManifest = new InternalManifest(manifest);
|
||||
var context = new CiphertextElectionContext(
|
||||
1UL, 1UL, keypair.PublicKey, Constants.TWO_MOD_Q, internalManifest.ManifestHash); // make a context with the correct manifesthash
|
||||
var device = new EncryptionDevice(12345UL, 23456UL, 34567UL, "Location");
|
||||
try
|
||||
{
|
||||
var mediator = new EncryptionMediator(internalManifest, context, device);
|
||||
Assert.IsNotNull(mediator); // should not be null if it gets created
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
// if there is an exception then the manifest hash would not be equal
|
||||
Assert.AreNotEqual(context.ManifestHash.ToHex(), internalManifest.ManifestHash.ToHex());
|
||||
}
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void Test_EncryptMediator_Hashes_Dont_Match()
|
||||
{
|
||||
var keypair = ElGamalKeyPair.FromSecret(Constants.TWO_MOD_Q);
|
||||
var manifest = ManifestGenerator.GetManifestFromFile();
|
||||
var internalManifest = new InternalManifest(manifest);
|
||||
var context = new CiphertextElectionContext(
|
||||
1UL, 1UL, keypair.PublicKey, Constants.TWO_MOD_Q, Constants.ONE_MOD_Q); // make a context with a different manifesthash
|
||||
var device = new EncryptionDevice(12345UL, 23456UL, 34567UL, "Location");
|
||||
try
|
||||
{
|
||||
var mediator = new EncryptionMediator(internalManifest, context, device);
|
||||
Assert.IsNull(mediator); // should not be created, so null at best
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
// if there is an exception then the manifest hash would not be equal
|
||||
Assert.AreNotEqual(context.ManifestHash.ToHex(), internalManifest.ManifestHash.ToHex());
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
}
|
||||
|
|
|
@ -110,6 +110,11 @@ namespace ElectionGuard
|
|||
CiphertextElectionContext context,
|
||||
EncryptionDevice device)
|
||||
{
|
||||
if (manifest.ManifestHash.ToHex() != context.ManifestHash.ToHex())
|
||||
{
|
||||
Status.ELECTIONGUARD_STATUS_ERROR_INVALID_ARGUMENT.ThrowIfError();
|
||||
}
|
||||
|
||||
var status = NativeInterface.EncryptionMediator.New(
|
||||
manifest.Handle, context.Handle, device.Handle, out Handle);
|
||||
status.ThrowIfError();
|
||||
|
|
|
@ -5,16 +5,16 @@
|
|||
#include "electionguard/ballot_code.hpp"
|
||||
#include "electionguard/elgamal.hpp"
|
||||
#include "electionguard/hash.hpp"
|
||||
#include "electionguard/precompute_buffers.hpp"
|
||||
#include "log.hpp"
|
||||
#include "nonces.hpp"
|
||||
#include "serialize.hpp"
|
||||
#include "utils.hpp"
|
||||
#include "electionguard/precompute_buffers.hpp"
|
||||
#include <nlohmann/json.hpp>
|
||||
|
||||
#include <algorithm>
|
||||
#include <future>
|
||||
#include <iostream>
|
||||
#include <nlohmann/json.hpp>
|
||||
|
||||
extern "C" {
|
||||
#include "../karamel/Hacl_Bignum4096.h"
|
||||
|
@ -90,18 +90,10 @@ namespace electionguard
|
|||
return DeviceSerializer::fromBson(move(data));
|
||||
}
|
||||
|
||||
uint64_t EncryptionDevice::getDeviceUuid() const {
|
||||
return pimpl->deviceUuid;
|
||||
}
|
||||
uint64_t EncryptionDevice::getSessionUuid() const {
|
||||
return pimpl->sessionUuid;
|
||||
}
|
||||
uint64_t EncryptionDevice::getLaunchCode() const {
|
||||
return pimpl->launchCode;
|
||||
}
|
||||
std::string EncryptionDevice::getLocation() const {
|
||||
return pimpl->location;
|
||||
}
|
||||
uint64_t EncryptionDevice::getDeviceUuid() const { return pimpl->deviceUuid; }
|
||||
uint64_t EncryptionDevice::getSessionUuid() const { return pimpl->sessionUuid; }
|
||||
uint64_t EncryptionDevice::getLaunchCode() const { return pimpl->launchCode; }
|
||||
std::string EncryptionDevice::getLocation() const { return pimpl->location; }
|
||||
|
||||
#pragma endregion
|
||||
|
||||
|
@ -127,6 +119,11 @@ namespace electionguard
|
|||
const EncryptionDevice &encryptionDevice)
|
||||
: pimpl(new Impl(internalManifest, context, encryptionDevice))
|
||||
{
|
||||
if (internalManifest.getManifestHash()->toHex() != context.getManifestHash()->toHex()) {
|
||||
throw invalid_argument("manifest and context do not match hashes manifest:" +
|
||||
internalManifest.getManifestHash()->toHex() +
|
||||
" context:" + context.getManifestHash()->toHex());
|
||||
}
|
||||
}
|
||||
|
||||
EncryptionMediator::~EncryptionMediator() = default;
|
||||
|
@ -296,8 +293,8 @@ namespace electionguard
|
|||
auto pubkey_to_exp = triple1->get_pubkey_to_exp();
|
||||
|
||||
// Generate the encryption using precomputed values
|
||||
ciphertext = elgamalEncrypt_with_precomputed(selection.getVote(),
|
||||
*g_to_exp, *pubkey_to_exp);
|
||||
ciphertext =
|
||||
elgamalEncrypt_with_precomputed(selection.getVote(), *g_to_exp, *pubkey_to_exp);
|
||||
if (ciphertext == nullptr) {
|
||||
throw runtime_error("encryptSelection:: Error generating ciphertext");
|
||||
}
|
||||
|
@ -309,12 +306,10 @@ namespace electionguard
|
|||
encrypted = CiphertextBallotSelection::make_with_precomputed(
|
||||
selection.getObjectId(), description.getSequenceOrder(), *descriptionHash,
|
||||
move(ciphertext), cryptoExtendedBaseHash, selection.getVote(),
|
||||
move(precomputedTwoTriplesAndAQuad),
|
||||
isPlaceholder, true);
|
||||
move(precomputedTwoTriplesAndAQuad), isPlaceholder, true);
|
||||
} else {
|
||||
// Generate the encryption
|
||||
ciphertext =
|
||||
elgamalEncrypt(selection.getVote(), *selectionNonce, elgamalPublicKey);
|
||||
ciphertext = elgamalEncrypt(selection.getVote(), *selectionNonce, elgamalPublicKey);
|
||||
if (ciphertext == nullptr) {
|
||||
throw runtime_error("encryptSelection:: Error generating ciphertext");
|
||||
}
|
||||
|
@ -336,17 +331,17 @@ namespace electionguard
|
|||
|
||||
// verify the selection.
|
||||
if (encrypted->isValidEncryption(*descriptionHash, elgamalPublicKey,
|
||||
cryptoExtendedBaseHash)) {
|
||||
cryptoExtendedBaseHash)) {
|
||||
return encrypted;
|
||||
}
|
||||
throw runtime_error("encryptSelection failed validity check");
|
||||
throw runtime_error("encryptSelection failed validity check");
|
||||
}
|
||||
|
||||
string getOvervoteAndWriteIns(const PlaintextBallotContest &contest,
|
||||
const InternalManifest &internalManifest,
|
||||
eg_valid_contest_return_type_t is_overvote)
|
||||
{
|
||||
json overvoteAndWriteIns;
|
||||
json overvoteAndWriteIns;
|
||||
auto selections = contest.getSelections();
|
||||
|
||||
// if an overvote is detected then put the selections into json
|
||||
|
@ -403,15 +398,14 @@ namespace electionguard
|
|||
|
||||
string overvoteAndWriteIns_string("");
|
||||
if (overvoteAndWriteIns.dump() != string("null")) {
|
||||
overvoteAndWriteIns_string = overvoteAndWriteIns.dump();
|
||||
overvoteAndWriteIns_string = overvoteAndWriteIns.dump();
|
||||
}
|
||||
|
||||
return overvoteAndWriteIns_string;
|
||||
}
|
||||
|
||||
unique_ptr<CiphertextBallotContest>
|
||||
encryptContest(const PlaintextBallotContest &contest,
|
||||
const InternalManifest &internalManifest,
|
||||
encryptContest(const PlaintextBallotContest &contest, const InternalManifest &internalManifest,
|
||||
const ContestDescriptionWithPlaceholders &description,
|
||||
const ElementModP &elgamalPublicKey, const ElementModQ &cryptoExtendedBaseHash,
|
||||
const ElementModQ &nonceSeed, bool shouldVerifyProofs /* = true */)
|
||||
|
@ -419,8 +413,8 @@ namespace electionguard
|
|||
{
|
||||
// Validate Input
|
||||
bool supportOvervotes = true;
|
||||
eg_valid_contest_return_type_t is_valid_contest =
|
||||
contest.isValid(description.getObjectId(), description.getSelections().size(),
|
||||
eg_valid_contest_return_type_t is_valid_contest = contest.isValid(
|
||||
description.getObjectId(), description.getSelections().size(),
|
||||
description.getNumberElected(), description.getVotesAllowed(), supportOvervotes);
|
||||
if ((is_valid_contest != SUCCESS) && (is_valid_contest != OVERVOTE)) {
|
||||
throw invalid_argument("the plaintext contest was invalid");
|
||||
|
@ -471,8 +465,8 @@ namespace electionguard
|
|||
|
||||
// if the is an overvote then we need to make all the selection votes 0
|
||||
if (is_valid_contest == OVERVOTE) {
|
||||
duplicate_selection = make_unique<PlaintextBallotSelection>(
|
||||
selection_ptr->getObjectId(), 0, false);
|
||||
duplicate_selection =
|
||||
make_unique<PlaintextBallotSelection>(selection_ptr->getObjectId(), 0, false);
|
||||
selection_ptr = duplicate_selection.get();
|
||||
}
|
||||
|
||||
|
@ -480,7 +474,7 @@ namespace electionguard
|
|||
|
||||
encryptedSelections.push_back(encryptSelection(
|
||||
*selection_ptr, selectionDescription.get(), *elgamalPublicKey_ptr,
|
||||
*cryptoExtendedBaseHash_ptr, *sharedNonce.get(), false, shouldVerifyProofs));
|
||||
*cryptoExtendedBaseHash_ptr, *sharedNonce.get(), false, shouldVerifyProofs));
|
||||
} else {
|
||||
// Should never happen since the contest is normalized by emplaceMissingValues
|
||||
throw runtime_error("encryptedContest:: Error constructing encrypted selection");
|
||||
|
@ -507,18 +501,17 @@ namespace electionguard
|
|||
|
||||
auto placeholderSelection = selectionFrom(placeholder, true, selectPlaceholder);
|
||||
encryptedSelections.push_back(encryptSelection(
|
||||
*placeholderSelection, placeholder, *elgamalPublicKey_ptr,
|
||||
*cryptoExtendedBaseHash_ptr, *sharedNonce.get(), false, shouldVerifyProofs));
|
||||
*placeholderSelection, placeholder, *elgamalPublicKey_ptr,
|
||||
*cryptoExtendedBaseHash_ptr, *sharedNonce.get(), false, shouldVerifyProofs));
|
||||
}
|
||||
|
||||
// Derive the extendedDataNonce from the selection nonce and a constant
|
||||
auto noncesForExtendedData =
|
||||
make_unique<Nonces>(*sharedNonce->clone(), "constant-extended-data");
|
||||
make_unique<Nonces>(*sharedNonce->clone(), "constant-extended-data");
|
||||
auto extendedDataNonce = noncesForExtendedData->get(0);
|
||||
|
||||
vector<uint8_t> extendedData_plaintext((uint8_t *)&extendedData.front(),
|
||||
(uint8_t *)&extendedData.front() +
|
||||
extendedData.size());
|
||||
vector<uint8_t> extendedData_plaintext(
|
||||
(uint8_t *)&extendedData.front(), (uint8_t *)&extendedData.front() + extendedData.size());
|
||||
|
||||
// Perform HashedElGamalCiphertext calculation
|
||||
unique_ptr<HashedElGamalCiphertext> hashedElGamal =
|
||||
|
@ -536,8 +529,8 @@ namespace electionguard
|
|||
auto encryptedContest = CiphertextBallotContest::make(
|
||||
contest.getObjectId(), description.getSequenceOrder(), *descriptionHash,
|
||||
move(encryptedSelections), elgamalPublicKey, cryptoExtendedBaseHash, *chaumPedersenNonce,
|
||||
description.getNumberElected(), sharedNonce->clone(), nullptr,
|
||||
nullptr, move(hashedElGamal));
|
||||
description.getNumberElected(), sharedNonce->clone(), nullptr, nullptr,
|
||||
move(hashedElGamal));
|
||||
|
||||
if (encryptedContest == nullptr || encryptedContest->getProof() == nullptr) {
|
||||
throw runtime_error("encryptedContest:: Error constructing encrypted constest");
|
||||
|
@ -574,8 +567,8 @@ namespace electionguard
|
|||
hasContest = true;
|
||||
auto encrypted = encryptContest(
|
||||
contest.get(), internalManifest, description.get(),
|
||||
*context.getElGamalPublicKey(),
|
||||
*context.getCryptoExtendedBaseHash(), nonceSeed, shouldVerifyProofs);
|
||||
*context.getElGamalPublicKey(), *context.getCryptoExtendedBaseHash(),
|
||||
nonceSeed, shouldVerifyProofs);
|
||||
|
||||
encryptedContests.push_back(move(encrypted));
|
||||
break;
|
||||
|
|
|
@ -60,7 +60,7 @@ TEST_CASE("Encrypt simple selection using precomputed values succeeds")
|
|||
auto hashContext = metadata->crypto_hash();
|
||||
auto plaintext = BallotGenerator::selectionFrom(*metadata);
|
||||
|
||||
// cause a two triples and a quad to be populated
|
||||
// cause a two triples and a quad to be populated
|
||||
PrecomputeBufferContext::init(1);
|
||||
PrecomputeBufferContext::populate(*keypair->getPublicKey());
|
||||
PrecomputeBufferContext::stop_populate();
|
||||
|
@ -132,7 +132,7 @@ TEST_CASE("Encrypt PlaintextBallot with EncryptionMediator against constructed "
|
|||
auto device = make_unique<EncryptionDevice>(12345UL, 23456UL, 34567UL, "Location");
|
||||
|
||||
auto mediator = make_unique<EncryptionMediator>(*internal, *context, *device);
|
||||
|
||||
|
||||
// Act
|
||||
auto plaintext = BallotGenerator::getFakeBallot(*internal);
|
||||
// Log::debug(plaintext->toJson());
|
||||
|
@ -201,7 +201,7 @@ TEST_CASE("Encrypt PlaintextBallot overvote")
|
|||
CHECK(heg != nullptr);
|
||||
CHECK(heg->getData().size() == (size_t)(BYTES_512 + sizeof(uint16_t)));
|
||||
|
||||
unique_ptr<ElementModP> new_pad = make_unique<ElementModP>(*heg->getPad());
|
||||
unique_ptr<ElementModP> new_pad = make_unique<ElementModP>(*heg->getPad());
|
||||
unique_ptr<HashedElGamalCiphertext> newHEG =
|
||||
make_unique<HashedElGamalCiphertext>(move(new_pad), heg->getData(), heg->getMac());
|
||||
|
||||
|
@ -210,8 +210,8 @@ TEST_CASE("Encrypt PlaintextBallot overvote")
|
|||
string new_plaintext_string((char *)&new_plaintext.front(), new_plaintext.size());
|
||||
|
||||
CHECK(new_plaintext_string ==
|
||||
string("{\"error\":\"overvote\",\"error_data\":[\"benjamin-franklin-selection\""
|
||||
",\"john-adams-selection\"]}"));
|
||||
string("{\"error\":\"overvote\",\"error_data\":[\"benjamin-franklin-selection\""
|
||||
",\"john-adams-selection\"]}"));
|
||||
}
|
||||
|
||||
TEST_CASE("Encrypt simple PlaintextBallot with EncryptionMediator succeeds")
|
||||
|
@ -260,28 +260,29 @@ TEST_CASE("Encrypt full PlaintextBallot with WriteIn and Overvote with Encryptio
|
|||
auto device = make_unique<EncryptionDevice>(12345UL, 23456UL, 34567UL, "Location");
|
||||
|
||||
auto mediator = make_unique<EncryptionMediator>(*internal, *context, *device);
|
||||
|
||||
|
||||
// Act
|
||||
string plaintextBallot_json = string("{\"object_id\": \"03a29d15-667c-4ac8-afd7-549f19b8e4eb\","
|
||||
"\"style_id\": \"jefferson-county-ballot-style\", \"contests\": [ {\"object_id\":"
|
||||
"\"justice-supreme-court\", \"sequence_order\": 0, \"ballot_selections\": [{"
|
||||
"\"object_id\": \"john-adams-selection\", \"sequence_order\": 0, \"vote\": 1,"
|
||||
"\"is_placeholder_selection\": false, \"extended_data\": null}, {\"object_id\""
|
||||
": \"benjamin-franklin-selection\", \"sequence_order\": 1, \"vote\": 1,"
|
||||
"\"is_placeholder_selection\": false, \"extended_data\": null}, {\"object_id\":"
|
||||
" \"write-in-selection\", \"sequence_order\": 3, \"vote\": 1, \"is_placeholder_selection\""
|
||||
": false, \"write_in\": \"Susan B. Anthony\"}], \"extended_data\": null}]}");
|
||||
// TODO - Once the relevant plaintext ballot file is in the environment then
|
||||
string plaintextBallot_json = string(
|
||||
"{\"object_id\": \"03a29d15-667c-4ac8-afd7-549f19b8e4eb\","
|
||||
"\"style_id\": \"jefferson-county-ballot-style\", \"contests\": [ {\"object_id\":"
|
||||
"\"justice-supreme-court\", \"sequence_order\": 0, \"ballot_selections\": [{"
|
||||
"\"object_id\": \"john-adams-selection\", \"sequence_order\": 0, \"vote\": 1,"
|
||||
"\"is_placeholder_selection\": false, \"extended_data\": null}, {\"object_id\""
|
||||
": \"benjamin-franklin-selection\", \"sequence_order\": 1, \"vote\": 1,"
|
||||
"\"is_placeholder_selection\": false, \"extended_data\": null}, {\"object_id\":"
|
||||
" \"write-in-selection\", \"sequence_order\": 3, \"vote\": 1, \"is_placeholder_selection\""
|
||||
": false, \"write_in\": \"Susan B. Anthony\"}], \"extended_data\": null}]}");
|
||||
// TODO - Once the relevant plaintext ballot file is in the environment then
|
||||
// uncomment the below and stop using the hard coded string.
|
||||
//auto plaintextBallot =
|
||||
// BallotGenerator::getSimpleBallotFromFile(TEST_BALLOT_WITH_WRITEIN_SELECTED);
|
||||
auto plaintextBallot = PlaintextBallot::fromJson(plaintextBallot_json);
|
||||
auto ciphertext = mediator->encrypt(*plaintextBallot);
|
||||
|
||||
|
||||
// Assert
|
||||
CHECK(ciphertext->isValidEncryption(*context->getManifestHash(), *keypair->getPublicKey(),
|
||||
*context->getCryptoExtendedBaseHash()) == true);
|
||||
|
||||
|
||||
// check to make sure we have a hashed elgamal ciphertext
|
||||
unique_ptr<HashedElGamalCiphertext> heg = nullptr;
|
||||
auto contests = ciphertext->getContests();
|
||||
|
@ -307,9 +308,9 @@ TEST_CASE("Encrypt full PlaintextBallot with WriteIn and Overvote with Encryptio
|
|||
Log::debug(new_plaintext_string);
|
||||
|
||||
CHECK(new_plaintext_string ==
|
||||
string("{\"error\":\"overvote\",\"error_data\":[\"john-adams-selection\","
|
||||
"\"benjamin-franklin-selection\",\"write-in-selection\"],\"write_ins\""
|
||||
":{\"write-in-selection\":\"Susan B. Anthony\"}}"));
|
||||
string("{\"error\":\"overvote\",\"error_data\":[\"john-adams-selection\","
|
||||
"\"benjamin-franklin-selection\",\"write-in-selection\"],\"write_ins\""
|
||||
":{\"write-in-selection\":\"Susan B. Anthony\"}}"));
|
||||
}
|
||||
|
||||
TEST_CASE("Encrypt simple CompactPlaintextBallot with EncryptionMediator succeeds")
|
||||
|
@ -438,3 +439,37 @@ TEST_CASE("Encrypt simple ballot from file succeeds with precomputed values")
|
|||
*context->getCryptoExtendedBaseHash()) == true);
|
||||
PrecomputeBufferContext::empty_queues();
|
||||
}
|
||||
|
||||
TEST_CASE("Create EncryptionMediator with same manifest hash")
|
||||
{
|
||||
auto secret = ElementModQ::fromHex(a_fixed_secret);
|
||||
auto keypair = ElGamalKeyPair::fromSecret(*secret);
|
||||
auto manifest = ManifestGenerator::getJeffersonCountyManifest_Minimal();
|
||||
auto internal = make_unique<InternalManifest>(*manifest);
|
||||
auto context = make_unique<CiphertextElectionContext>(
|
||||
1UL, 1UL, keypair->getPublicKey()->clone(), Q().clone(),
|
||||
internal.get()->getManifestHash()->clone(), Q().clone(), Q().clone());
|
||||
auto device = make_unique<EncryptionDevice>(12345UL, 23456UL, 34567UL, "Location");
|
||||
|
||||
auto mediator = make_unique<EncryptionMediator>(*internal, *context, *device);
|
||||
CHECK(mediator != nullptr);
|
||||
}
|
||||
|
||||
TEST_CASE("Create EncryptionMediator with different manifest hash")
|
||||
{
|
||||
auto secret = ElementModQ::fromHex(a_fixed_secret);
|
||||
auto keypair = ElGamalKeyPair::fromSecret(*secret);
|
||||
auto manifest = ManifestGenerator::getJeffersonCountyManifest_Minimal();
|
||||
auto internal = make_unique<InternalManifest>(*manifest);
|
||||
auto context =
|
||||
make_unique<CiphertextElectionContext>(1UL, 1UL, keypair->getPublicKey()->clone(),
|
||||
Q().clone(), Q().clone(), Q().clone(), Q().clone());
|
||||
auto device = make_unique<EncryptionDevice>(12345UL, 23456UL, 34567UL, "Location");
|
||||
|
||||
try {
|
||||
auto mediator = make_unique<EncryptionMediator>(*internal, *context, *device);
|
||||
CHECK(mediator == nullptr);
|
||||
} catch (const std::exception &e) {
|
||||
CHECK(internal->getManifestHash()->toHex() != context->getManifestHash()->toHex());
|
||||
}
|
||||
}
|
||||
|
|
Загрузка…
Ссылка в новой задаче