Support external_ballot_identifer as a string (#71)

* Support external_ballot_identifer as a string.  Export/import ballots from encrypter

Add support for encrypter to export ballots to the file system.  Import encrypter ballots into coordinator.  Add a hashtable implementation that tracks ballot cast/spoil state against the external identifier (ballot_collection.c).  Support loading ballots into a buffer.  

Fix memory leaks in bignum.c, crypto.c, decryption/coordinator.c, decryption/trustee.c, voting/coordinator.c, voting/encrypter.c, etc.

Rework the API example to demonstrate the new external id and file system changes.

Improve debug logging.  Add more api documentation.  Modify MAX_BALLOT_PAYLOAD to 2000.  Explicitly add // TODO: to track remaining work.

Note this commit is a work in progress and currently breaks the simple example.

* Remove Simple Example, add api doc stubs

Add stubs for the api documentation.  Remove the simple example and use the api sample in the PR pipeline workflow

* Fix Merge Issue

* Verify malloc success

* Address PR Feedback

* Fix LGTM alerts

* Add soft delete of encrypter files

* Free CP Proof

* Fix windows Makefile

* test fix pr workflow on windows

* use secure api's

* explicitly compile release

* flush file write before read

* clean up makefile

* Document consumers resonsible for free

when calling an api that generates a filename as an out parameter, the caller is responsible for managing the memory of the out parameter when the call is successful

* Rename static coordinators in API to differentiate local vars

add an underscore prefix to static coordinator instance handles to differentiate vars until #62 is complete

* add new enum cases to the end on voting coordinator

* Add convenience logging

* Handle error cases on file access

use convenience logging

* use consistent file formatting when rebuilding tracker string

* optimize null check

* explicitly set tracker array positions to NULL on failure

* Reformat refactor Decryption_Coordinator_receive_share

* guard tallies_initialized when freeing resources

explicitly initialize num_tallies

* rename _encrypted_ballot_count

* don't return early

* do not fail soft delete if rename fails

* fix assignment

* adjust number of ballots to force a rebuild
This commit is contained in:
Matt Wilhelm 2020-01-28 09:44:07 -05:00 коммит произвёл Ethan Chumley
Родитель 6aa4464f2d
Коммит d9dd0a1037
72 изменённых файлов: 3605 добавлений и 1721 удалений

36
.github/workflows/pr-workflow.yml поставляемый
Просмотреть файл

@ -22,14 +22,14 @@ jobs:
cmake --version
cmake -S . -B build
cmake --build build
- name: 'Build Simple Election'
- name: 'Build API Sample Election'
run: |
ElectionGuard_DIR="$PWD/build/ElectionGuard" cmake -S examples/simple -B simple_build
cmake --build simple_build --target simple
- name: 'Test Simple Election'
ElectionGuard_DIR="$PWD/build/ElectionGuard" cmake -S examples/api -B api_build
cmake --build api_build --target api
- name: 'Test API Sample Election'
run: |
cd simple_build
./simple
cd api_build
./api
windows_build:
name: 'Windows Build'
runs-on: 'windows-latest'
@ -55,15 +55,15 @@ jobs:
cmake --version
cmake -S . -B build -G "MSYS Makefiles"
cmake --build build
- name: 'Build Simple Election'
- name: 'Build API Sample Election'
run: |
$env:CMAKE_PREFIX_PATH="./build/ElectionGuard"
cmake -S examples/simple -B simple_build -G "MSYS Makefiles"
cmake --build simple_build --target simple
- name: 'Test Simple Election'
cmake -S examples/api -B api_build -G "MSYS Makefiles"
cmake --build api_build --target api
- name: 'Test API Sample Election'
run: |
cd simple_build
$outcome = Invoke-Expression ".\simple.exe"
$env:Path+=";.\build\"
$outcome = Invoke-Expression ".\api_build\api.exe"
echo $outcome
macos_build:
name: 'MacOS Build'
@ -82,11 +82,11 @@ jobs:
cmake --version
cmake -S . -B build
cmake --build build
- name: 'Build Simple Election'
- name: 'Build API Sample Election'
run: |
ElectionGuard_DIR="$PWD/build/ElectionGuard" cmake -S examples/simple -B simple_build
cmake --build simple_build --target simple
- name: 'Test Simple Election'
ElectionGuard_DIR="$PWD/build/ElectionGuard" cmake -S examples/api -B api_build
cmake --build api_build --target api
- name: 'Test API Sample Election'
run: |
cd simple_build
./simple
cd api_build
./api

4
.gitignore поставляемый
Просмотреть файл

@ -34,3 +34,7 @@ _deps
ElectionGuardConfig.cmake
*_build/
ballots/
ballots_encrypter/
tallies/

Просмотреть файл

@ -12,8 +12,8 @@ set(CMAKE_C_EXTENSIONS OFF)
message("Generated with config types: ${CMAKE_CONFIGURATION_TYPES}")
IF(CMAKE_BUILD_TYPE MATCHES Debug)
message("setting DEBUG_PRINT")
add_compile_definitions(DEBUG_PRINT)
message("setting DEBUG during compile")
add_compile_definitions(DEBUG)
ENDIF()
@ -22,11 +22,13 @@ add_library(electionguard
${PROJECT_SOURCE_DIR}/src/electionguard/api/filename.c
${PROJECT_SOURCE_DIR}/src/electionguard/api/create_election.c
${PROJECT_SOURCE_DIR}/src/electionguard/api/encrypt_ballot.c
${PROJECT_SOURCE_DIR}/src/electionguard/api/load_ballots.c
${PROJECT_SOURCE_DIR}/src/electionguard/api/record_ballots.c
${PROJECT_SOURCE_DIR}/src/electionguard/api/tally_votes.c
${PROJECT_SOURCE_DIR}/src/electionguard/crypto_reps.h
${PROJECT_SOURCE_DIR}/src/electionguard/voting/ballot_collection.c
${PROJECT_SOURCE_DIR}/src/electionguard/voting/coordinator.c
${PROJECT_SOURCE_DIR}/src/electionguard/voting/num_ballots.h
${PROJECT_SOURCE_DIR}/src/electionguard/voting/messages.c
${PROJECT_SOURCE_DIR}/src/electionguard/voting/message_reps.h
${PROJECT_SOURCE_DIR}/src/electionguard/voting/nouns.c
${PROJECT_SOURCE_DIR}/src/electionguard/voting/tracker.c
@ -54,6 +56,7 @@ add_library(electionguard
${PROJECT_SOURCE_DIR}/src/electionguard/keyceremony/trustee.c
${PROJECT_SOURCE_DIR}/src/electionguard/uint4096.c
${PROJECT_SOURCE_DIR}/src/electionguard/bignum.c
${PROJECT_SOURCE_DIR}/src/electionguard/log.h
${PROJECT_SOURCE_DIR}/src/electionguard/sha2-openbsd.c
${PROJECT_SOURCE_DIR}/src/electionguard/sha2-openbsd.h
${PROJECT_SOURCE_DIR}/src/electionguard/crypto.c
@ -62,15 +65,15 @@ add_library(electionguard
${PROJECT_SOURCE_DIR}/src/electionguard/random_source.h
${PROJECT_SOURCE_DIR}/src/electionguard/random_source.c
${PROJECT_SOURCE_DIR}/src/electionguard/trustee_state_rep.h
${PROJECT_SOURCE_DIR}/src/electionguard/file.c
${PROJECT_SOURCE_DIR}/src/electionguard/directory.c
${PROJECT_SOURCE_DIR}/src/electionguard/directory.h
${PROJECT_SOURCE_DIR}/src/electionguard/uthash.h
${PROJECT_SOURCE_DIR}/include/electionguard/api/config.h
${PROJECT_SOURCE_DIR}/include/electionguard/api/create_election.h
${PROJECT_SOURCE_DIR}/include/electionguard/api/encrypt_ballot.h
${PROJECT_SOURCE_DIR}/include/electionguard/api/load_ballots.h
${PROJECT_SOURCE_DIR}/include/electionguard/api/record_ballots.h
${PROJECT_SOURCE_DIR}/include/electionguard/api/tally_votes.h
${PROJECT_SOURCE_DIR}/include/electionguard/file.h
${PROJECT_SOURCE_DIR}/include/electionguard/max_values.h
${PROJECT_SOURCE_DIR}/include/electionguard/trustee_state.h
${PROJECT_SOURCE_DIR}/include/electionguard/voting/messages.h
@ -109,8 +112,8 @@ if (MINGW)
set_target_properties(electionguard PROPERTIES PREFIX "")
endif()
# Set the public include directory depending on if the target is being exported
# or installed
# Set the public include directory depending on
# if the target is being exported or installed
target_include_directories(electionguard
SYSTEM PUBLIC
$<INSTALL_INTERFACE:include>
@ -148,18 +151,9 @@ install(
add_subdirectory(docs)
file(MAKE_DIRECTORY "${CMAKE_CURRENT_BINARY_DIR}/simple_build")
file(MAKE_DIRECTORY "${CMAKE_CURRENT_BINARY_DIR}/api_build")
set(CMAKE_CONFIGURATION_TYPES "Debug" CACHE STRING "" FORCE)
add_test(NAME build_simple
COMMAND "${CMAKE_COMMAND}"
-E env ElectionGuard_DIR=${CMAKE_CURRENT_BINARY_DIR}/ElectionGuard
"${CMAKE_CTEST_COMMAND}"
--build-and-test "${PROJECT_SOURCE_DIR}/examples/simple" "${CMAKE_CURRENT_BINARY_DIR}/simple_build"
--build-generator ${CMAKE_GENERATOR}
--test-command simple
)
add_test(NAME build_api
COMMAND "${CMAKE_COMMAND}"
-E env ElectionGuard_DIR=${CMAKE_CURRENT_BINARY_DIR}/ElectionGuard

Просмотреть файл

@ -14,7 +14,7 @@ If new to the project,
2. Install according to [ReadMe](./README.rst)
OR
refer to [Documentation](https://electionguard.github.io/ElectionGuard-SDK-C-Documentation/)
3. Once you're up and running, be sure to run the simple election test
3. Once you're up and running, be sure to run the API sample election test
⚠ Note: If you get stuck, take a look at our [GitHub Action](./.github/workflows/pr-workflow.yml)

67
Makefile.mk Normal file
Просмотреть файл

@ -0,0 +1,67 @@
.PHONY: add-dependencies build build-debug clean run-api test
BUILD_DEBUG?=true
.EXPORT_ALL_VARIABLES:
ELECTIONGUARD_DIR="$(realpath .)/build/ElectionGuard"
# Detect operating system
ifeq ($(OS),Windows_NT)
OPERATING_SYSTEM := Windows
else
OPERATING_SYSTEM := $(shell uname 2>/dev/null || echo Unknown)
endif
add-dependencies:
ifeq ($(OPERATING_SYSTEM),Darwin)
endif
ifeq ($(OPERATING_SYSTEM),Linux)
endif
ifeq ($(OPERATING_SYSTEM),Windows)
endif
ifeq ($(BUILD_DEBUG),true)
build: build-debug
else
build: build-release
endif
build-debug: clean
if [ ! -d "build" ]; then mkdir build; fi
ifeq ($(OPERATING_SYSTEM),Windows)
cmake -S . -B build -G "MSYS Makefiles" -DCMAKE_BUILD_TYPE=Debug
else
cmake -S . -B build -DCMAKE_BUILD_TYPE=Debug
endif
cmake --build build
build-release: clean
if [ ! -d "build" ]; then mkdir build; fi
ifeq ($(OPERATING_SYSTEM),Windows)
cmake -S . -B build -G "MSYS Makefiles" -DCMAKE_BUILD_TYPE=Release -DBUILD_SHARED_LIBS=ON
else
cmake -S . -B build -DBUILD_SHARED_LIBS=ON
endif
cmake --build build
clean:
rm -rf ./build/* ./api_build/*
run-api: build
if [ ! -d "api_build" ]; then mkdir api_build; fi
ifeq ($(OPERATING_SYSTEM),Windows)
CMAKE_PREFIX_PATH="./build/ElectionGuard" cmake -S examples/api -B api_build -G "MSYS Makefiles"
cmake --build api_build --target api
PATH=$(PWD)/build:$$PATH; ./api_build/api
else
ElectionGuard_DIR=$(ELECTIONGUARD_DIR) cmake -S examples/api -B api_build
cmake --build api_build --target api
./api_build/api
endif
test: build
cmake --build build --target test

Просмотреть файл

@ -23,13 +23,13 @@
cmake --build build
3. You should now have a `electionguard.a` or `electionguard.dll` (depending on the how cmake was configured).
3. (Optional) Build the simple example election driver.
3. (Optional) Build the api example election driver.
1. Open a command prompt and navigate to the directory with the ElectionGuard-SDK-C-Implementation repo.
2. Run the following commands:
set PATH=%PATH%;C:\path\to\ElectionGuard-SDK-C-Implementation\build\
set CMAKE_PREFIX_PATH=C:\path\to\ElectionGuard-SDK-C-Implementation\build\ElectionGuard
cmake -S examples/simple -B simple_build -G "MSYS Makefiles"
cmake --build simple_build --target simple
cmake -S examples/api -B api_build -G "MSYS Makefiles"
cmake --build api_build --target api
3. You should now have a `simple.exe` that simulates some random voters and generates election record artifacts.
3. You should now have a `api.exe` that simulates some random voters and generates election record artifacts.

Просмотреть файл

@ -53,11 +53,17 @@ To build the SDK static library ``libelectionguard.a``, run
cmake --build build
Alternatively there is a `Makefile` to wrap the cmake commands:
.. code:: sh
make build
Testing
--------
Currently you can exercise the SDK by running the :doc:`example client
<examples/simple>`. We include a cmake test to do so automatically. You can
Currently you can exercise the SDK by running the :doc:`api client
<examples/api>`. We include a cmake test to do so automatically. You can
also execute the client directly to better examine the output it produces.
.. warning::
@ -95,11 +101,17 @@ to refer to the built library, and build the client.
.. code:: sh
mkdir simple_build
ElectionGuard_DIR="$PWD/build/ElectionGuard" cmake -S examples/simple -B simple_build
cmake --build simple_build --target simple
mkdir api_build
ElectionGuard_DIR="$PWD/build/ElectionGuard" cmake -S examples/api -B api_build
cmake --build api_build --target api
The built binary should be located at :file:`simple_build/simple`.
The built binary should be located at :file:`api_build/api`.
alternatively, you can use the convenience Makefile
.. code:: sh
make run-api
Debugging

Просмотреть файл

@ -6,5 +6,5 @@ let
};
in with import nixpkgs {}; rec {
electionguard = callPackages ./derivation.nix { };
simple = callPackages ./examples/simple { inherit electionguard; };
api = callPackages ./examples/api { inherit electionguard; };
}

Просмотреть файл

@ -41,10 +41,16 @@ IF(BUILD_DOCUMENTATION)
${CMAKE_CURRENT_SOURCE_DIR}/examples.rst
${CMAKE_CURRENT_SOURCE_DIR}/voting.rst
${CMAKE_CURRENT_SOURCE_DIR}/keyceremony.rst
${CMAKE_CURRENT_SOURCE_DIR}/examples/simple.rst
${CMAKE_CURRENT_SOURCE_DIR}/examples/api.rst
${CMAKE_CURRENT_SOURCE_DIR}/api/index.rst
${CMAKE_CURRENT_SOURCE_DIR}/api/crypto.rst
${CMAKE_CURRENT_SOURCE_DIR}/api/trustee_state.rst
${CMAKE_CURRENT_SOURCE_DIR}/api/api/config.rst
${CMAKE_CURRENT_SOURCE_DIR}/api/api/create_election.rst
${CMAKE_CURRENT_SOURCE_DIR}/api/api/encrypt_ballot.rst
${CMAKE_CURRENT_SOURCE_DIR}/api/api/load_ballots.rst
${CMAKE_CURRENT_SOURCE_DIR}/api/api/record_ballots.rst
${CMAKE_CURRENT_SOURCE_DIR}/api/api/tally_votes.rst
${CMAKE_CURRENT_SOURCE_DIR}/api/voting/coordinator.rst
${CMAKE_CURRENT_SOURCE_DIR}/api/voting/encrypter.rst
${CMAKE_CURRENT_SOURCE_DIR}/api/voting/messages.rst

2
docs/api/api/config.rst Normal file
Просмотреть файл

@ -0,0 +1,2 @@
Config
======

Просмотреть файл

@ -0,0 +1,2 @@
Create Election
===============

Просмотреть файл

@ -0,0 +1,2 @@
Encrypt Ballot
==============

Просмотреть файл

@ -0,0 +1,2 @@
Load Ballots
============

Просмотреть файл

@ -0,0 +1,2 @@
Record Ballots
==============

Просмотреть файл

@ -0,0 +1,2 @@
Tally Votes
===========

19
docs/examples/api.rst Normal file
Просмотреть файл

@ -0,0 +1,19 @@
API Example Client
===============
This simple client is designed to showcase the API by demonstrating
the correct sequence of function calls used in an elecition.
.. warning::
This example client **does not** do any sort of error handling in
order to more clearly highlight the "happy path" of an election.
Please do not use this code in a real system.
:file:`main.c`
--------------
.. literalinclude:: /../examples/api/main.c
:language: c
:linenos:
:caption: main.c

Просмотреть файл

@ -1,43 +0,0 @@
Simple Client
===============
This simple client is designed to showcase the API by demonstrating
the correct sequence of function calls used in an elecition.
.. warning::
This example client **does not** do any sort of error handling in
order to more clearly highlight the "happy path" of an election.
Please do not use this code in a real system.
:file:`main.c`
--------------
.. literalinclude:: /../examples/simple/main.c
:language: c
:linenos:
:caption: main.c
:file:`main_keyceremony.c`
--------------------------
.. literalinclude:: /../examples/simple/main_keyceremony.c
:language: c
:linenos:
:caption: main_keyceremony.c
:file:`main_voting.c`
---------------------
.. literalinclude:: /../examples/simple/main_voting.c
:language: c
:linenos:
:caption: main_voting.c
:file:`main_decryption.c`
-------------------------
.. literalinclude:: /../examples/simple/main_decryption.c
:language: c
:linenos:
:caption: main_decryption.c

Просмотреть файл

@ -1,20 +1,24 @@
Example Clients
===============
These examples demonstrate correct usage of the ElectionGuard SDK.
These examples demonstrate usage of the ElectionGuard SDK.
.. toctree::
:maxdepth: 1
:hidden:
Simple Client <examples/simple>
API Client <examples/api>
- The :doc:`simple client <examples/simple>` carries out an election where all
entities communicate via argument-passing.
- The :doc:`api client <examples/api>` carries out an election where all
entities communicate via argument-passing. It demonstrates using the
high level API's
It should successfully exit with return status ``0``, and produce two files
in the current directory with names of the form :file:`voting_results-XXXXXX`
and :file:`tally-XXXXXX` containing ballots and the decrypted tallies
respectively. It should also print all of the ballot trackers on STDOUT using
- It should successfully exit with return status ``0``, and produce three files:
- ballots/registered-ballots_YYY_MM_DD
- ballots_encrypter/encrypted-ballots_YYYY_MM_DD
- tallies/tallies_YYYY_MM_DD
- It should also print all of the ballot trackers on STDOUT using
a representation where each byte is converted to a specific word.
- It should execute verification steps as part of the example

Просмотреть файл

@ -17,7 +17,6 @@ add_executable(api
${PROJECT_SOURCE_DIR}/main.c
)
find_package(ElectionGuard REQUIRED)
find_package(GMP REQUIRED)
@ -31,7 +30,6 @@ else()
target_link_libraries(api electionguard gmp)
endif()
if (WIN32)
target_link_libraries(api Bcrypt)
endif (WIN32)

Просмотреть файл

@ -10,30 +10,47 @@
#include <electionguard/api/create_election.h>
#include <electionguard/api/encrypt_ballot.h>
#include <electionguard/api/load_ballots.h>
#include <electionguard/api/record_ballots.h>
#include <electionguard/api/tally_votes.h>
#include <electionguard/max_values.h>
// test struct to capture ballot state
struct test_ballot
{
uint64_t ballotId;
char external_identifier[MAX_EXTERNAL_ID_LENGTH];
bool isCast;
bool isSpoiled;
char *tracker;
uint8_t selections[MAX_SELECTIONS];
};
// misc functions
static bool random_bit();
static bool compare_string(char *first, char *second);
static int32_t fill_random_ballot(uint8_t *selections);
static bool result_equals_expected_selections(struct test_ballot *testBallots, uint32_t actual_tally, uint32_t selection_index);
// assertion functions
static bool loaded_ballots_match_encrypted_ballots(
struct register_ballot_message *loaded_ballots,
struct register_ballot_message *encrypted_ballots,
uint32_t ballot_count);
static bool loaded_ballot_identifiers_match_encrypted_ballots(
char **loaded_ballot_identifiers,
struct test_ballot *test_ballots,
uint32_t ballot_count);
static bool result_equals_expected_selections(
struct test_ballot *testBallots,
uint32_t actual_tally,
uint32_t selection_index);
// Election Parameters
uint32_t const NUM_TRUSTEES = 3;
uint32_t const THRESHOLD = 2;
uint32_t const NUM_ENCRYPTERS = 3;
uint32_t const NUM_SELECTIONS = 6;
uint32_t const NUM_SELECTIONS = 12; // the number of total contest selections for an election
uint32_t const DECRYPTING_TRUSTEES = 2; // must be >= THRESHOLD && <= NUM_TRUSTEES
uint32_t const NUM_RANDOM_BALLOT_SELECTIONS = 6;
uint32_t const NUM_RANDOM_BALLOTS = 5; // the number of ballots to use when executing the test
int main()
{
@ -41,6 +58,8 @@ int main()
srand(time(NULL));
// TODO: name all output artifacts for the test run instance (e.g. by start time)
struct api_config config = {
.num_selections = NUM_SELECTIONS,
.num_trustees = NUM_TRUSTEES,
@ -66,104 +85,214 @@ int main()
printf("\n--- Encrypt Ballots ---\n\n");
struct register_ballot_message encrypted_ballots[NUM_RANDOM_BALLOT_SELECTIONS];
struct test_ballot testBallots[NUM_RANDOM_BALLOT_SELECTIONS];
struct register_ballot_message memory_encrypted_ballots[NUM_RANDOM_BALLOTS];
struct test_ballot testBallots[NUM_RANDOM_BALLOTS];
char *encrypted_ballots_filename = NULL;
char *encrypted_output_path = "./ballots_encrypter/"; // This outputs to the directy above the cwd.
char encrypted_output_prefix[50];
//generate a unique file name
time_t now = time(NULL);
struct tm *local_time = 0;
local_time = localtime(&now);
if (local_time == 0)
{
ok = false;
}
sprintf(encrypted_output_prefix, "%s_%d_%d_%d", "encrypted-ballots",
local_time->tm_year + 1900, local_time->tm_mon + 1, local_time->tm_mday);
if (ok)
{
uint64_t current_num_ballots = 0;
for (uint64_t i = 0; i < NUM_RANDOM_BALLOT_SELECTIONS && ok; i++)
ok = API_EncryptBallot_soft_delete_file(encrypted_output_path, encrypted_output_prefix);
for (uint32_t i = 0; i < NUM_RANDOM_BALLOTS && ok; i++)
{
struct test_ballot testBallot;
struct register_ballot_message encrypted_ballot_message;
//char *tracker;
sprintf(testBallot.external_identifier, "some_string_value_%d", i);
// we're assuming that the returned number of true selections for this ballot
struct register_ballot_message encrypted_ballot_message;
// TODO: demonstrate the empty null field
// for now, we're assuming that the returned number of true selections for this ballot
// is the the correct expected number for this ballot style in order to encrypt it
uint32_t selected_count = fill_random_ballot(testBallot.selections);
ok = API_EncryptBallot(testBallot.selections, selected_count, config, &current_num_ballots,
&testBallot.ballotId, &encrypted_ballot_message,
&testBallot.tracker);
ok = API_EncryptBallot(
testBallot.selections,
selected_count,
config,
testBallot.external_identifier,
&encrypted_ballot_message,
encrypted_output_path,
encrypted_output_prefix,
&encrypted_ballots_filename,
&testBallot.tracker
);
if (ok)
{
encrypted_ballots[i] = encrypted_ballot_message;
// ballot_identifiers[i] = testBallot.ballotId;
// ballot_trackers[i] = testBallot.tracker;
memory_encrypted_ballots[i] = encrypted_ballot_message;
testBallots[i] = testBallot;
// Print id and tracker
printf("Ballot id: %lu\n%s\n\n", testBallot.ballotId, testBallot.tracker);
printf("Encrypted Ballot id: %s\n%s\n\n",
testBallots[i].external_identifier, testBallots[i].tracker);
} else {
printf("encrypt ballot failed");
}
}
}
// TODO: test simulating multiple encrypters or an encrypter being reset
// [START] OPTIONAL:
// When running an encrypter on another device, it is possible to import a file
// and pass it to the tally functions, but this is not strictly necessary
// from an API Perspective.
printf("\n--- Load Ballots ---\n\n");
// load the ballot ID's so we can cast/spoil them
char *loaded_external_identifiers[NUM_RANDOM_BALLOTS];
// Load the data from the file that is created
struct register_ballot_message loaded_encrypted_ballots[NUM_RANDOM_BALLOTS];
if (ok)
{
ok = API_LoadBallots(
0,
NUM_RANDOM_BALLOTS,
NUM_SELECTIONS,
encrypted_ballots_filename,
loaded_external_identifiers,
loaded_encrypted_ballots
) == API_LOADBALLOTS_SUCCESS;
printf("\n--- Validate Ballots ---\n\n");
// verify the loaded ballots match the ones in memory
assert(
loaded_ballot_identifiers_match_encrypted_ballots(
loaded_external_identifiers,
testBallots,
NUM_RANDOM_BALLOTS)
);
assert(
loaded_ballots_match_encrypted_ballots(
loaded_encrypted_ballots,
memory_encrypted_ballots,
NUM_RANDOM_BALLOTS
)
);
}
// TODO: test loading ballots in batches
// free the ballots we loaded from disk
// since they are not actually used in this test
for (uint32_t i = 0; i < NUM_RANDOM_BALLOTS && ok; i++)
API_EncryptBallot_free(loaded_encrypted_ballots[i], NULL);
// free the external identifiers we loaded for assertions
// since they are not actually used in this test
for (uint32_t i = 0; i < NUM_RANDOM_BALLOTS && ok; i++)
free(loaded_external_identifiers[i]);
// [END] OPTIONAL:
// Register & Record Cast/Spoil Multiple Ballots
printf("\n--- Randomly Assigning Ballots to be Cast or Spoil Arrays ---\n\n");
printf("\n\n--- Randomly Assigning Ballots to be Cast or Spoil Arrays ---\n\n");
uint32_t current_cast_index = 0;
uint32_t current_spoiled_index = 0;
uint64_t casted_ballot_ids[NUM_RANDOM_BALLOT_SELECTIONS];
uint64_t spoiled_ballot_ids[NUM_RANDOM_BALLOT_SELECTIONS];
char *casted_ballot_ids[NUM_RANDOM_BALLOTS];
char *spoiled_ballot_ids[NUM_RANDOM_BALLOTS];
char *memory_external_identifiers[NUM_RANDOM_BALLOTS];
for (uint64_t i = 0; i < NUM_RANDOM_BALLOT_SELECTIONS && ok; i++)
for (uint32_t i = 0; i < NUM_RANDOM_BALLOTS && ok; i++)
{
char *id = testBallots[i].external_identifier;
memory_external_identifiers[i] = id;
if (random_bit())
{
testBallots[i].isCast = true;
casted_ballot_ids[current_cast_index] = testBallots[i].ballotId;
current_cast_index++;
testBallots[i].isSpoiled = false;
casted_ballot_ids[current_cast_index] = id;
printf("Ballot Id: %lu - Cast!\n", testBallots[i].ballotId);
printf("Ballot Id: %s - Cast!\n", casted_ballot_ids[current_cast_index]);
current_cast_index++;
}
else
{
testBallots[i].isCast = false;
testBallots[i].isSpoiled = true;
spoiled_ballot_ids[current_spoiled_index] = testBallots[i].ballotId;
spoiled_ballot_ids[current_spoiled_index] = id;
printf("Ballot Id: %s - Spoiled!\n", spoiled_ballot_ids[current_spoiled_index]);
current_spoiled_index++;
printf("Ballot Id: %lu - Spoiled!\n", testBallots[i].ballotId);
}
}
if ((current_cast_index + current_spoiled_index) != NUM_RANDOM_BALLOT_SELECTIONS)
if ((current_cast_index + current_spoiled_index) != NUM_RANDOM_BALLOTS)
{
ok = false;
}
printf("\n--- Record Ballots (Register, Cast, and Spoil) ---\n\n");
char *ballots_filename;
char *cast_and_spoiled_ballots_filename = NULL;
char *casted_trackers[current_cast_index];
char *spoiled_trackers[current_spoiled_index];
if (ok)
{
// Assigning an output_path fails if this folder doesn't already exist
char *output_path = "./ballots/"; // This outputs to the directy above the cwd.
char *output_prefix = "ballots-";
ok = API_RecordBallots(config.num_selections, current_cast_index, current_spoiled_index,
NUM_RANDOM_BALLOT_SELECTIONS, casted_ballot_ids, spoiled_ballot_ids, encrypted_ballots,
output_path, output_prefix, &ballots_filename, casted_trackers, spoiled_trackers);
char output_prefix[50];
sprintf(output_prefix, "%s_%d_%d_%d", "registered-ballots",
local_time->tm_year + 1900, local_time->tm_mon + 1, local_time->tm_mday);
ok = API_RecordBallots(
config.num_selections,
current_cast_index,
current_spoiled_index,
NUM_RANDOM_BALLOTS,
casted_ballot_ids,
spoiled_ballot_ids,
memory_external_identifiers,
memory_encrypted_ballots,
output_path,
output_prefix,
&cast_and_spoiled_ballots_filename,
casted_trackers,
spoiled_trackers
);
if (ok)
{
printf("Casted Ballot Trackers:\n");
// TODO: assert on outputs not testBallots inputs
printf("\nCast Ballot Trackers:\n");
for (uint32_t i = 0; i < current_cast_index; i++)
{
uint64_t id = casted_ballot_ids[i];
assert(testBallots[id].isCast == true);
printf("\t%ld: %s\n", id, casted_trackers[i]);
printf("\t%s: %s\n", casted_ballot_ids[i], casted_trackers[i]);
}
printf("\nSpoiled Ballot Trackers:\n");
for (uint32_t i = 0; i < current_spoiled_index; i++)
{
uint64_t id = spoiled_ballot_ids[i];
assert(testBallots[id].isSpoiled == true);
printf("\t%ld: %s\n", id, spoiled_trackers[i]);
printf("\t%s: %s\n", spoiled_ballot_ids[i], spoiled_trackers[i]);
}
printf("\nBallot registrations and recording of cast/spoil successful!\nCheck output file \"%s\"\n",
ballots_filename);
cast_and_spoiled_ballots_filename);
}
}
@ -171,12 +300,16 @@ int main()
printf("\n--- Tally & Decrypt Votes ---\n\n");
char *tally_filename;
char *tally_filename = NULL;
uint32_t tally_results[config.num_selections];
if (ok)
{
char *output_path = "./tallies/"; // This outputs to the directy above the cwd.
char *output_prefix = "tally-";
char *output_path = "./tallies/"; // output to the directy above the cwd.
char output_prefix[50];
sprintf(output_prefix, "%s_%d_%d_%d", "tally",
local_time->tm_year + 1900, local_time->tm_mon + 1, local_time->tm_mday);
// copy the threshold number of trustees needed to decrypt
struct trustee_state threshold_trustee_states[MAX_TRUSTEES];
@ -186,10 +319,20 @@ int main()
assert(threshold_trustee_states[i].bytes != NULL);
}
// run the tally
printf("Tallying with %d of %d trustees \n\n", DECRYPTING_TRUSTEES, config.num_trustees);
ok = API_TallyVotes(config, threshold_trustee_states, DECRYPTING_TRUSTEES,
ballots_filename, output_path, output_prefix, &tally_filename, tally_results);
ok = API_TallyVotes(
config,
threshold_trustee_states,
DECRYPTING_TRUSTEES,
cast_and_spoiled_ballots_filename,
output_path,
output_prefix,
&tally_filename,
tally_results
);
if (ok)
{
@ -207,46 +350,108 @@ int main()
// Cleanup
printf("\n--- Cleaning Up Resources ---\n\n");
API_TallyVotes_free(tally_filename);
API_RecordBallots_free(ballots_filename, current_cast_index, current_spoiled_index, casted_trackers, spoiled_trackers);
for (uint64_t i = 0; i < NUM_RANDOM_BALLOT_SELECTIONS && ok; i++)
API_EncryptBallot_free(encrypted_ballots[i], testBallots[i].tracker);
API_RecordBallots_free(
cast_and_spoiled_ballots_filename,
current_cast_index,
current_spoiled_index,
casted_trackers,
spoiled_trackers
);
API_LoadBallots_free(encrypted_ballots_filename);
for (uint32_t i = 0; i < NUM_RANDOM_BALLOTS && ok; i++)
API_EncryptBallot_free(memory_encrypted_ballots[i], testBallots[i].tracker);
API_CreateElection_free(config.joint_key, trustee_states);
printf("\n--- Done! ---\n\n");
if (ok)
return EXIT_SUCCESS;
else
return EXIT_FAILURE;
}
bool result_equals_expected_selections(struct test_ballot *testBallots, uint32_t actual_tally, uint32_t selection_index)
bool result_equals_expected_selections(
struct test_ballot *testBallots, uint32_t actual_tally, uint32_t selection_index)
{
uint32_t expected_tally = 0;
for (uint64_t i = 0; i < NUM_RANDOM_BALLOT_SELECTIONS; i++)
for (uint32_t i = 0; i < NUM_RANDOM_BALLOTS; i++)
{
if (testBallots[i].isCast) {
expected_tally += testBallots[i].selections[selection_index];
}
}
printf("\tselection: %d: expected: %d actual: %d\n", selection_index, expected_tally, actual_tally);
printf("\tselection: %d: expected: %d actual: %d\n",
selection_index, expected_tally, actual_tally);
return expected_tally == actual_tally;
}
bool loaded_ballots_match_encrypted_ballots(
struct register_ballot_message *loaded_ballots,
struct register_ballot_message *encrypted_ballots,
uint32_t ballot_count)
{
printf("\nverifying %d ballots \n", ballot_count);
bool ok = true;
for (uint32_t i = 0; i < ballot_count && ok; i++)
{
ok = Messages_are_equal(&loaded_ballots[i], &encrypted_ballots[i]);
}
if (!ok)
{
printf("loaded ballots did not match the encrypted ballots!");
}
return ok;
}
bool loaded_ballot_identifiers_match_encrypted_ballots(
char **loaded_ballot_identifiers, struct test_ballot *test_ballots, uint32_t ballot_count)
{
printf("\nverifying %d ballot identifiers\n", ballot_count);
bool ok = true;
for (uint32_t i = 0; i < ballot_count && ok; i++)
{
printf("\ncompare_string:\n - expect: %s\n - actual: %s\n",
loaded_ballot_identifiers[i], test_ballots[i].external_identifier);
ok = compare_string(test_ballots[i].external_identifier, loaded_ballot_identifiers[i]);
}
if (!ok)
{
printf("\nloaded ballot identifiers did not match the encrypted ballots!\n");
}
return ok;
}
bool random_bit() { return 1 & rand(); }
int32_t fill_random_ballot(uint8_t *selections)
{
// TODO: Handle null votes / expected num selected
uint32_t selected_count = 0;
for (uint32_t i = 0; i < NUM_SELECTIONS; i++)
{
if (random_bit())
{
// write true
selections[i] = 1;
selected_count++;
}
else
{
// write false
selections[i] = 0;
}
}
@ -260,3 +465,28 @@ int32_t fill_random_ballot(uint8_t *selections)
return selected_count;
}
bool compare_string(char *expected, char *actual)
{
while (*expected == *actual)
{
if (*expected == '\0' || *actual == '\0')
{
break;
}
expected++;
actual++;
}
if (*expected == '\0' && *actual == '\0')
{
printf("compare_string: success\n");
return true;
}
else
{
printf("compare_string: failed!\n");
return false;
}
}

Просмотреть файл

@ -1,50 +0,0 @@
cmake_minimum_required(VERSION 3.13)
project("ElectionGuard Simple Example Client" VERSION 0.0.1 LANGUAGES C)
enable_testing()
set(CMAKE_C_STANDARD 11)
set(CMAKE_C_STANDARD_REQUIRED ON)
set(CMAKE_C_EXTENSIONS ON)
set(CMAKE_MODULE_PATH
${CMAKE_CURRENT_LIST_DIR}/../../cmake
${CMAKE_MODULE_PATH}
)
add_executable(simple
${PROJECT_SOURCE_DIR}/main_params.h
${PROJECT_SOURCE_DIR}/main_voting.h
${PROJECT_SOURCE_DIR}/main_decryption.c
${PROJECT_SOURCE_DIR}/main_keyceremony.c
${PROJECT_SOURCE_DIR}/main.c
${PROJECT_SOURCE_DIR}/main_voting.c
${PROJECT_SOURCE_DIR}/main_keyceremony.h
${PROJECT_SOURCE_DIR}/main_decryption.h
${PROJECT_SOURCE_DIR}/main_rsa.h
${PROJECT_SOURCE_DIR}/main_rsa.c
)
find_package(ElectionGuard REQUIRED)
find_package(GMP REQUIRED)
message(STATUS "GMP library found.")
message(STATUS "GMP include dir is: ${GMP_INCLUDE_DIR}")
message(STATUS "GMP library is: ${GMP_LIBRARY}")
if(APPLE)
target_link_libraries(simple electionguard ${GMP_LIBRARY})
else()
target_link_libraries(simple electionguard gmp)
endif()
if (WIN32)
target_link_libraries(simple Bcrypt)
endif (WIN32)
add_test(NAME simple COMMAND simple)
install(TARGETS simple)

Просмотреть файл

@ -1,13 +0,0 @@
{ stdenv, nix-gitignore, cmake, electionguard }:
stdenv.mkDerivation {
pname = "electionguard-sdk";
version = "0.0.1";
nativeBuildInputs = [ cmake electionguard ];
enableParallelBuilding = true;
doCheck = true;
src = nix-gitignore.gitignoreSourcePure ["*.nix" ../../.gitignore] ./.;
}

Просмотреть файл

@ -1,170 +0,0 @@
#include <stdbool.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <time.h>
#ifdef _MSC_VER
#include <io.h>
#endif
#include <electionguard/max_values.h>
#include "main_decryption.h"
#include "main_keyceremony.h"
#include "main_params.h"
#include "main_rsa.h"
#include "main_voting.h"
/** Create a new temporary file from a template. */
static FILE *fmkstemps(char const *template, const char *mode);
// Election Parameters
uint32_t const NUM_TRUSTEES = 3;
uint32_t const THRESHOLD = 2;
uint32_t const NUM_ENCRYPTERS = 3;
uint32_t const NUM_SELECTIONS = 3;
uint32_t const DECRYPTING_TRUSTEES = 2;
// ^The number of trustees that will participate in decryption. Will fail if this is less than THRESHOLD
// This is a temporary placeholder. In a real election, this should be
// initialized by hashing:
// 1. p (from bignum.h)
// 2. The subgroup order (not yet named in the current implementation)
// 3. generator (from bignum.h)
// 4. NUM_TRUSTEES
// 5. THRESHOLD
// 6. The date of the election
// 7. Jurisdictional information for the election
raw_hash BASE_HASH_CODE = {0, 0xff, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0};
int main()
{
srand(time(NULL));
Crypto_parameters_new();
// Outputs of the key ceremony
struct trustee_state trustee_states[MAX_TRUSTEES];
struct joint_public_key joint_key;
// Key Ceremony
bool ok = key_ceremony(&joint_key, trustee_states);
// Open the voting results file
FILE *voting_results = NULL;
if (ok)
{
//Gcc on windows appears to not support the "x" flag at the time of writing
#ifdef _WIN32
voting_results = fmkstemps("voting_results-XXXXXX", "w+");
#else
voting_results = fmkstemps("voting_results-XXXXXX", "w+x");
#endif
if (voting_results == NULL)
ok = false;
}
// Voting
if (ok)
ok = voting(joint_key, voting_results);
// Open the tally file
FILE *tally = NULL;
// // RSA encrypt decrypt check
// if(ok)
// ok = main_rsa();
if (ok)
{
//Gcc on windows appears to not support the "x" flag at the time of writing
#ifdef _WIN32
tally = fmkstemps("tally-XXXXXX", "w");
#else
tally = fmkstemps("tally-XXXXXX", "wx");
#endif
if (tally == NULL)
ok = false;
}
// Decryption
if (ok)
ok = decryption(voting_results, tally, trustee_states);
// Cleanup
if (voting_results != NULL)
{
fclose(voting_results);
voting_results = NULL;
}
if (tally != NULL)
{
fclose(tally);
tally = NULL;
}
for (uint32_t i = 0; i < NUM_TRUSTEES; i++)
if (trustee_states[i].bytes != NULL)
{
free((void *)trustee_states[i].bytes);
trustee_states[i].bytes = NULL;
}
if (joint_key.bytes != NULL)
{
free((void *)joint_key.bytes);
joint_key.bytes = NULL;
}
Crypto_parameters_free();
if (ok)
return EXIT_SUCCESS;
else
return EXIT_FAILURE;
}
FILE *fmkstemps(char const *template, const char *mode)
{
bool ok = true;
int result_fd = -1;
FILE *result = NULL;
// Duplicate the template. It needs to be mutable for mkstemp.
char *template_mut = strdup(template);
if (template_mut == NULL)
ok = false;
// Create and open the temporary file
if (ok)
{
result_fd = mkstemp(template_mut);
if (-1 == result_fd)
ok = false;
}
// Convert the file descriptor to a FILE*
if (ok)
{
result = fdopen(result_fd, mode);
if (result == NULL)
ok = false;
}
// Free the duplicated template
if (template_mut != NULL)
{
free(template_mut);
template_mut = NULL;
}
return result;
}

Просмотреть файл

@ -1,234 +0,0 @@
#include <inttypes.h>
#include <stdlib.h>
#include <string.h>
#include <electionguard/decryption/coordinator.h>
#include <electionguard/decryption/trustee.h>
#include "main_decryption.h"
#include "main_params.h"
static bool initialize_coordinator(void);
static bool initialize_trustees(struct trustee_state *trustee_states);
static bool tally_voting_records(FILE *in);
static bool decrypt_tally_shares(void);
static bool decrypt_tally_decryption_fragments(
bool *requests_present, struct decryption_fragments_request *requests);
static Decryption_Trustee trustees[MAX_TRUSTEES];
static Decryption_Coordinator coordinator;
bool decryption(FILE *in, FILE *out, struct trustee_state *trustee_states)
{
bool ok = true;
if (ok)
ok = initialize_coordinator();
if (ok)
ok = initialize_trustees(trustee_states);
if (ok)
ok = tally_voting_records(in);
if (ok)
ok = decrypt_tally_shares();
struct decryption_fragments_request requests[MAX_TRUSTEES];
bool request_present[MAX_TRUSTEES];
for (uint32_t i = 0; i < NUM_TRUSTEES; i++)
requests[i] = (struct decryption_fragments_request){.bytes = NULL};
if (ok)
{
struct Decryption_Coordinator_all_shares_received_r result =
Decryption_Coordinator_all_shares_received(coordinator);
if (result.status != DECRYPTION_COORDINATOR_SUCCESS)
ok = false;
else
for (uint32_t i = 0; i < result.num_trustees; i++)
requests[i] = result.requests[i],
request_present[i] = result.request_present[i];
}
if (ok)
ok = decrypt_tally_decryption_fragments(request_present, requests);
if (ok)
{
uint32_t tally_results[NUM_SELECTIONS];
enum Decryption_Coordinator_status status =
Decryption_Coordinator_all_fragments_received(coordinator, out, tally_results);
if (status != DECRYPTION_COORDINATOR_SUCCESS)
ok = false;
for (uint32_t i = 0; i < NUM_SELECTIONS; i++)
{
printf("Tally %u results = %u\n", i, tally_results[i]);
}
}
for (uint32_t i = 0; i < NUM_TRUSTEES; i++)
if (request_present[i])
{
free((void *)requests[i].bytes);
requests[i].bytes = NULL;
}
for (uint32_t i = 0; i < NUM_TRUSTEES; i++)
if (trustees[i] != NULL)
{
Decryption_Trustee_free(trustees[i]);
trustees[i] = NULL;
}
if (coordinator != NULL)
{
Decryption_Coordinator_free(coordinator);
coordinator = NULL;
}
return ok;
}
bool initialize_coordinator(void)
{
bool ok = true;
struct Decryption_Coordinator_new_r result =
Decryption_Coordinator_new(NUM_TRUSTEES, THRESHOLD);
if (result.status != DECRYPTION_COORDINATOR_SUCCESS)
ok = false;
else
coordinator = result.coordinator;
return ok;
}
bool initialize_trustees(struct trustee_state *trustee_states)
{
bool ok = true;
for (uint32_t i = 0; i < NUM_TRUSTEES && ok; i++)
{
struct Decryption_Trustee_new_r result = Decryption_Trustee_new(
NUM_TRUSTEES, THRESHOLD, NUM_SELECTIONS, trustee_states[i], BASE_HASH_CODE);
if (result.status != DECRYPTION_TRUSTEE_SUCCESS)
ok = false;
else
trustees[i] = result.decryptor;
}
return ok;
}
bool tally_voting_records(FILE *in)
{
bool ok = true;
for (uint32_t i = 0; i < NUM_TRUSTEES && ok; i++)
{
int seek_status = fseek(in, 0L, SEEK_SET);
if (seek_status != 0)
ok = false;
if (ok)
{
enum Decryption_Trustee_status status =
Decryption_Trustee_tally_voting_record(trustees[i], in);
if (status != DECRYPTION_TRUSTEE_SUCCESS)
ok = false;
}
}
return ok;
}
bool decrypt_tally_shares(void)
{
bool ok = true;
for (uint32_t i = 0; i < DECRYPTING_TRUSTEES && ok; i++)
{
struct decryption_share share = {.bytes = NULL};
{
struct Decryption_Trustee_compute_share_r result =
Decryption_Trustee_compute_share(trustees[i]);
if (result.status != DECRYPTION_TRUSTEE_SUCCESS)
ok = false;
else
share = result.share;
}
if (ok)
{
enum Decryption_Coordinator_status status =
Decryption_Coordinator_receive_share(coordinator, share);
if (status != DECRYPTION_COORDINATOR_SUCCESS)
ok = false;
}
if (share.bytes != NULL)
{
free((void *)share.bytes);
share.bytes = NULL;
}
}
return ok;
}
bool decrypt_tally_decryption_fragments(
bool *requests_present, struct decryption_fragments_request *requests)
{
bool ok = true;
for (uint32_t i = 0; i < NUM_TRUSTEES && ok; i++)
{
if (requests_present[i])
{
struct decryption_fragments decryption_fragments = {.bytes = NULL};
{
struct Decryption_Trustee_compute_fragments_r result =
Decryption_Trustee_compute_fragments(trustees[i],
requests[i]);
if (result.status != DECRYPTION_TRUSTEE_SUCCESS)
ok = false;
else
decryption_fragments = result.fragments;
}
if (ok)
{
enum Decryption_Coordinator_status status =
Decryption_Coordinator_receive_fragments(
coordinator, decryption_fragments);
if (status != DECRYPTION_COORDINATOR_SUCCESS)
ok = false;
}
if (decryption_fragments.bytes != NULL)
{
free((void *)decryption_fragments.bytes);
decryption_fragments.bytes = NULL;
}
}
}
return ok;
}

Просмотреть файл

@ -1,14 +0,0 @@
#ifndef __MAIN_DECRYPTION_H__
#define __MAIN_DECRYPTION_H__
#include <stdbool.h>
#include <stdint.h>
#include <stdio.h>
#include <electionguard/crypto.h>
#include <electionguard/max_values.h>
#include <electionguard/trustee_state.h>
bool decryption(FILE *in, FILE *out, struct trustee_state *trustee_states);
#endif /* __MAIN_DECRYPTION_H__ */

Просмотреть файл

@ -1,320 +0,0 @@
#include <stdlib.h>
#include <string.h>
#include "main_keyceremony.h"
#include "main_params.h"
// Initialize
static bool initialize_coordinator(void);
static bool initialize_trustees(void);
// Key Generation
static bool generate_keys(void);
static struct all_keys_received_message receive_keys(void);
// Share Generation
static bool generate_shares(struct all_keys_received_message all_keys_received);
static struct all_shares_received_message receive_shares(void);
// Verification
static bool
verify_shares(struct all_shares_received_message all_shares_received);
static struct joint_public_key publish_joint_key(void);
// Export trustees state
static bool export_trustee_states(struct trustee_state *trustee_states);
// Global state
static KeyCeremony_Coordinator coordinator;
static KeyCeremony_Trustee trustees[MAX_TRUSTEES];
bool key_ceremony(struct joint_public_key *joint_key,
struct trustee_state *trustee_states)
{
bool ok = true;
// Initialize
if (ok)
ok = initialize_coordinator();
if (ok)
ok = initialize_trustees();
// Key Ceremony
if (ok)
ok = generate_keys();
struct all_keys_received_message all_keys_received = {.bytes = NULL};
if (ok)
{
all_keys_received = receive_keys();
if (all_keys_received.bytes == NULL)
ok = false;
}
// Share Generation
if (ok)
ok = generate_shares(all_keys_received);
struct all_shares_received_message all_shares_received = {.bytes = NULL};
if (ok)
{
all_shares_received = receive_shares();
if (all_shares_received.bytes == NULL)
ok = false;
}
// Verification
if (ok)
ok = verify_shares(all_shares_received);
*joint_key = (struct joint_public_key){.bytes = NULL};
if (ok)
{
*joint_key = publish_joint_key();
if (joint_key->bytes == NULL)
ok = false;
}
// Export trustee state
if (ok)
ok = export_trustee_states(trustee_states);
// Cleanup
if (all_shares_received.bytes != NULL)
{
free((void *)all_shares_received.bytes);
all_shares_received.bytes = NULL;
}
if (all_keys_received.bytes != NULL)
{
free((void *)all_keys_received.bytes);
all_keys_received.bytes = NULL;
}
for (uint32_t i = 0; i < NUM_TRUSTEES; i++)
{
if (trustees[i] != NULL)
KeyCeremony_Trustee_free(trustees[i]);
}
if (coordinator != NULL)
KeyCeremony_Coordinator_free(coordinator);
return ok;
}
bool initialize_coordinator(void)
{
bool ok = true;
struct KeyCeremony_Coordinator_new_r result =
KeyCeremony_Coordinator_new(NUM_TRUSTEES, THRESHOLD);
if (result.status != KEYCEREMONY_COORDINATOR_SUCCESS)
ok = false;
else
coordinator = result.coordinator;
return ok;
}
bool initialize_trustees(void)
{
bool ok = true;
for (uint32_t i = 0; i < NUM_TRUSTEES && ok; i++)
{
struct KeyCeremony_Trustee_new_r result =
KeyCeremony_Trustee_new(NUM_TRUSTEES, THRESHOLD, i);
if (result.status != KEYCEREMONY_TRUSTEE_SUCCESS)
ok = false;
else
trustees[i] = result.trustee;
}
return ok;
}
bool generate_keys(void)
{
bool ok = true;
for (uint32_t i = 0; i < NUM_TRUSTEES && ok; i++)
{
struct key_generated_message key_generated = {.bytes = NULL};
{
struct KeyCeremony_Trustee_generate_key_r result =
KeyCeremony_Trustee_generate_key(trustees[i], BASE_HASH_CODE);
if (result.status != KEYCEREMONY_TRUSTEE_SUCCESS)
ok = false;
else
key_generated = result.message;
}
if (ok)
{
enum KeyCeremony_Coordinator_status status =
KeyCeremony_Coordinator_receive_key_generated(coordinator,
key_generated);
if (status != KEYCEREMONY_COORDINATOR_SUCCESS)
ok = false;
}
if (key_generated.bytes != NULL)
{
free((void *)key_generated.bytes);
key_generated.bytes = NULL;
}
}
return ok;
}
struct all_keys_received_message receive_keys(void)
{
struct all_keys_received_message message = {.bytes = NULL};
struct KeyCeremony_Coordinator_all_keys_received_r result =
KeyCeremony_Coordinator_all_keys_received(coordinator);
if (result.status == KEYCEREMONY_COORDINATOR_SUCCESS)
message = result.message;
return message;
}
bool generate_shares(struct all_keys_received_message all_keys_received)
{
bool ok = true;
for (uint32_t i = 0; i < NUM_TRUSTEES && ok; i++)
{
struct shares_generated_message shares_generated = {.bytes = NULL};
{
struct KeyCeremony_Trustee_generate_shares_r result =
KeyCeremony_Trustee_generate_shares(trustees[i],
all_keys_received);
if (result.status != KEYCEREMONY_TRUSTEE_SUCCESS)
ok = false;
else
{
shares_generated = result.message;
}
}
if (ok)
{
enum KeyCeremony_Coordinator_status cstatus =
KeyCeremony_Coordinator_receive_shares_generated(
coordinator, shares_generated);
if (cstatus != KEYCEREMONY_COORDINATOR_SUCCESS)
ok = false;
}
if (shares_generated.bytes != NULL)
{
free((void *)shares_generated.bytes);
shares_generated.bytes = NULL;
}
}
return ok;
}
struct all_shares_received_message receive_shares()
{
struct all_shares_received_message message = {.bytes = NULL};
struct KeyCeremony_Coordinator_all_shares_received_r result =
KeyCeremony_Coordinator_all_shares_received(coordinator);
if (result.status == KEYCEREMONY_COORDINATOR_SUCCESS)
message = result.message;
return message;
}
bool verify_shares(struct all_shares_received_message all_shares_received)
{
bool ok = true;
for (uint32_t i = 0; i < NUM_TRUSTEES && ok; i++)
{
struct shares_verified_message shares_verified;
{
struct KeyCeremony_Trustee_verify_shares_r result =
KeyCeremony_Trustee_verify_shares(trustees[i],
all_shares_received);
if (result.status != KEYCEREMONY_TRUSTEE_SUCCESS)
ok = false;
else
shares_verified = result.message;
}
if (ok)
{
enum KeyCeremony_Coordinator_status status =
KeyCeremony_Coordinator_receive_shares_verified(
coordinator, shares_verified);
if (status != KEYCEREMONY_COORDINATOR_SUCCESS)
ok = false;
}
if (shares_verified.bytes != NULL)
{
free((void *)shares_verified.bytes);
shares_verified.bytes = NULL;
}
}
return ok;
}
struct joint_public_key publish_joint_key(void)
{
struct joint_public_key key = {.bytes = NULL};
struct KeyCeremony_Coordinator_publish_joint_key_r cresult =
KeyCeremony_Coordinator_publish_joint_key(coordinator);
if (cresult.status == KEYCEREMONY_COORDINATOR_SUCCESS)
key = cresult.key;
return key;
}
static bool export_trustee_states(struct trustee_state *trustee_states)
{
bool ok = true;
for (uint32_t i = 0; i < NUM_TRUSTEES && ok; i++)
{
struct KeyCeremony_Trustee_export_state_r result =
KeyCeremony_Trustee_export_state(trustees[i]);
if (result.status != KEYCEREMONY_TRUSTEE_SUCCESS)
ok = false;
else
trustee_states[i] = result.state;
}
return ok;
}

Просмотреть файл

@ -1,13 +0,0 @@
#ifndef __MAIN_KEY_CEREMONY_H__
#define __MAIN_KEY_CEREMONY_H__
#include <electionguard/keyceremony/coordinator.h>
#include <electionguard/keyceremony/messages.h>
#include <electionguard/keyceremony/trustee.h>
/* Carry out the key ceremony phase of an election, storing the joint
key and trustee_states in the provided pointers. */
bool key_ceremony(struct joint_public_key *joint_key,
struct trustee_state *trustee_states);
#endif /* __MAIN_KEY_CEREMONY_H__ */

Просмотреть файл

@ -1,14 +0,0 @@
#ifndef __MAIN_PARAMS_H__
#define __MAIN_PARAMS_H__
#include <stdint.h>
#include <electionguard/crypto.h>
extern uint32_t const NUM_TRUSTEES;
extern uint32_t const THRESHOLD;
extern uint32_t const NUM_ENCRYPTERS;
extern uint32_t const NUM_SELECTIONS;
extern uint32_t const DECRYPTING_TRUSTEES;
extern raw_hash BASE_HASH_CODE;
#endif /* __MAIN_PARAMS_H__ */

Просмотреть файл

@ -1,235 +0,0 @@
#include <stdlib.h>
#include <electionguard/voting/tracker.h>
#include "electionguard/voting/encrypter.h"
#include "main_params.h"
#include "main_voting.h"
static bool initialize_encrypters(struct joint_public_key joint_key);
static bool initialize_coordinator(void);
static bool simulate_random_votes(uint32_t encrypter_ix, uint64_t num_ballots);
static Voting_Encrypter *encrypters;
static Voting_Coordinator coordinator;
bool voting(struct joint_public_key joint_key, FILE *out)
{
bool ok = true;
if (ok)
{
encrypters = malloc(NUM_ENCRYPTERS * sizeof(Voting_Encrypter));
if (encrypters == NULL)
ok = false;
}
if (ok)
ok = initialize_encrypters(joint_key);
if (ok)
ok = initialize_coordinator();
if (ok)
{
for (uint32_t i = 0; i < NUM_ENCRYPTERS && ok; i++)
ok = simulate_random_votes(i, 2);
}
if (ok)
{
enum Voting_Coordinator_status status =
Voting_Coordinator_export_ballots(coordinator, out);
if (status != VOTING_COORDINATOR_SUCCESS)
ok = false;
}
if (coordinator != NULL)
{
Voting_Coordinator_free(coordinator);
coordinator = NULL;
}
for (uint32_t i = 0; i < NUM_ENCRYPTERS; i++)
if (encrypters != NULL && encrypters[i] != NULL)
{
Voting_Encrypter_free(encrypters[i]);
encrypters[i] = NULL;
}
if (encrypters != NULL)
{
free(encrypters);
encrypters = NULL;
}
return ok;
}
bool initialize_encrypters(struct joint_public_key joint_key)
{
bool ok = true;
uint8_t id_buf[1];
struct uid uid = {
.len = 1,
.bytes = id_buf,
};
for (uint32_t i = 0; i < NUM_ENCRYPTERS && ok; i++)
{
id_buf[0] = i;
struct Voting_Encrypter_new_r result = Voting_Encrypter_new(
uid, joint_key, NUM_SELECTIONS, BASE_HASH_CODE);
if (result.status != VOTING_ENCRYPTER_SUCCESS)
ok = false;
else
encrypters[i] = result.encrypter;
}
return ok;
}
bool initialize_coordinator(void)
{
bool ok = true;
struct Voting_Coordinator_new_r result =
Voting_Coordinator_new(NUM_SELECTIONS);
if (result.status != VOTING_COORDINATOR_SUCCESS)
ok = false;
else
coordinator = result.coordinator;
return ok;
}
static bool random_bit() { return 1 & rand(); }
static int32_t fill_random_ballot(bool *selections)
{
uint32_t selected_count = 0;
for (uint32_t i = 0; i < NUM_SELECTIONS; i++)
{
if (random_bit())
{
selections[i] = 1;
selected_count++;
}
else
{
selections[i] = 0;
}
}
printf("vote created ");
for (uint32_t i = 0; i < NUM_SELECTIONS; i++)
{
printf("%d ", selections[i]);
}
printf("\n");
return selected_count;
}
bool simulate_random_votes(uint32_t encrypter_ix, uint64_t num_ballots)
{
bool ok = true;
Voting_Encrypter const encrypter = encrypters[encrypter_ix];
for (uint64_t i = 0; i < num_ballots && ok; i++)
{
bool selections[MAX_SELECTIONS];
// we're assuming that the returned number of true selections for this ballot
// is the the correct expected number for this ballot style in order to encrypt it
uint32_t num_selected = fill_random_ballot(selections);
struct register_ballot_message message = {.bytes = NULL};
struct ballot_tracker tracker = {.bytes = NULL};
struct ballot_identifier identifier = {.bytes = NULL};
// Encrypt the ballot
if (ok)
{
struct Voting_Encrypter_encrypt_ballot_r result =
Voting_Encrypter_encrypt_ballot(encrypter, selections, num_selected);
if (result.status != VOTING_ENCRYPTER_SUCCESS)
ok = false;
else
{
message = result.message;
tracker = result.tracker;
identifier = result.id;
}
}
// Print the tracker
if (ok)
{
char *tracker_string = display_ballot_tracker(tracker);
int status = puts(tracker_string);
free(tracker_string);
if (status == EOF)
ok = false;
}
// Register the ballot
if (ok)
{
enum Voting_Coordinator_status status =
Voting_Coordinator_register_ballot(coordinator, message);
if (status != VOTING_COORDINATOR_SUCCESS)
ok = false;
}
// Randomly cast or spoil the ballot
if (ok)
{
enum Voting_Coordinator_status status;
if (random_bit())
{
status =
Voting_Coordinator_cast_ballot(coordinator, identifier);
printf("Cast\n");
}
else
{
status =
Voting_Coordinator_spoil_ballot(coordinator, identifier);
printf("Spoiled\n");
}
if (status != VOTING_COORDINATOR_SUCCESS)
ok = false;
}
if (message.bytes != NULL)
{
free((void *)message.bytes);
message.bytes = NULL;
}
if (tracker.bytes != NULL)
{
free((void *)tracker.bytes);
message.bytes = NULL;
}
if (identifier.bytes != NULL)
{
free((void *)identifier.bytes);
identifier.bytes = NULL;
}
}
return ok;
}

Просмотреть файл

@ -1,9 +0,0 @@
#ifndef __MAIN_VOTING_H__
#define __MAIN_VOTING_H__
#include <electionguard/voting/coordinator.h>
#include <electionguard/voting/encrypter.h>
bool voting(struct joint_public_key joint_key, FILE *out);
#endif /* __MAIN_VOTING_H__ */

Просмотреть файл

@ -0,0 +1,21 @@
API
===
The API is a higher level abstraction around the underlying
Electionguard process that is designed to automate the logical
functions of executing an election.
Create Election
---------------
Encrypt Ballot
--------------
Load Ballots
------------
Record Ballots
--------------
Tally Votes
-----------

Просмотреть файл

@ -7,13 +7,17 @@
#include <electionguard/keyceremony/trustee.h>
/**
* Perform all the steps necessary to initialize a new election from the given config.
* Perform the key ceremony, return joint key assigned to config and assign the trustee_states in the provided pointer. */
* Perform all the steps necessary to initialize
* a new election from the given config.
* Perform the key ceremony, return joint key assigned
* to config and assign the trustee_states in the provided pointer.
*/
bool API_CreateElection(struct api_config *config,
struct trustee_state *trustee_states);
/**
* Free the bytes allocated by CreateElection */
* Free the bytes allocated by CreateElection
*/
void API_CreateElection_free(struct joint_public_key joint_key,
struct trustee_state *trustee_states);

Просмотреть файл

@ -7,17 +7,38 @@
/**
* Encrypts the ballot selections given as an array of booleans,
* the serialized joint public key, and the current number of ballots. */
* the serialized joint public key, and the current number of ballots.
* @param uint8_t *selections_byte_array a byte array representing the selections on the ballot
* @param uint32_t expected_num_selected the expected number of true values in *selections_byte_array
* @param api_config config the election configuration
* @param char *external_identifier an arbitrary value meaninful to the library consumer that is associated
* with each encrypted ballot
* @param char *export_path
* @param char *filename
* @param char **output_filename return value of the generated file.
* when successful, caller is responsible for Freeing output_filename
* @param char **tracker_string representation of the ballot's tracking Id after encryption
*/
bool API_EncryptBallot(uint8_t *selections_byte_array,
uint32_t expected_num_selected,
struct api_config config,
uint64_t *current_num_ballots,
uint64_t *identifier,
char *external_identifier,
struct register_ballot_message *encrypted_ballot_message,
char *export_path,
char *filename,
char **output_filename,
char **tracker_string);
// TODO: API endpoint that does not use the file system
/**
* Free the bytes allocated by EncryptBallot */
* Soft delete a file by renaming it with the current system time
*/
bool API_EncryptBallot_soft_delete_file(char *export_path, char *filename);
/**
* Free the bytes allocated by EncryptBallot
*/
void API_EncryptBallot_free(struct register_ballot_message message,
char *tracker_string);

Просмотреть файл

@ -0,0 +1,35 @@
#ifndef __API_LOAD_BALLOTS_H__
#define __API_LOAD_BALLOTS_H__
#include <electionguard/voting/coordinator.h>
typedef enum API_LoadBallots_status
{
API_LOADBALLOTS_SUCCESS,
API_LOADBALLOTS_INITIALIZATION_ERROR,
API_LOADBALLOTS_INSUFFICIENT_MEMORY,
API_LOADBALLOTS_INVALID_BALLOT_INDEX,
API_LOADBALLOTS_END_OF_FILE,
API_LOADBALLOTS_TIMED_OUT,
API_LOADBALLOTS_IO_ERROR,
API_LOADBALLOTS_SERIALIZE_ERROR,
API_LOADBALLOTS_DESERIALIZE_ERROR,
} API_LoadBallots_status;
/**
* Load a range of ballots from a specified file on the file system
*/
API_LoadBallots_status API_LoadBallots(
uint64_t start_index,
uint64_t count,
uint32_t num_selections,
char *import_filepath,
char **out_external_identifiers,
struct register_ballot_message *out_encrypted_ballots);
/**
* Free the bytes allocated by LoadBallots
*/
API_LoadBallots_status API_LoadBallots_free(char *output_filename);
#endif /* __API_LOAD_BALLOTS_H__ */

Просмотреть файл

@ -7,13 +7,30 @@
/**
* Performs Ballot Registration and Recording Cast/Spoil for the ballot in bulk.
* Operates on the array of encrypted ballot messages, the array of casted ballot ids,
* and the array of spoiled ballot ids. */
* and the array of spoiled ballot ids.
* uint32_t num_selections,
* @param uint32_t num_cast_ballots,
* @param uint32_t num_spoil_ballots,
* @param uint64_t total_num_ballots,
* @param char *cast_ids array pointing to the cast id's in external_identifiers
* @param char *spoil_ids array pointing to the spoiled id's in external_identifiers
* @param char *external_identifiers index-based array pointing to the external identifiers
* associated with encrypted_ballots
* @param struct register_ballot_message *encrypted_ballots array pointing to serialized encrypted ballots
* @param char *export_path path to export the ballots file for use when decrypting
* @param char *filename_prefix previx to use on the file name
* @param char **output_filename return value of the generated file.
* when successful, caller is responsible for Freeing output_filename
* @param char **casted_tracker_strings return value of the trackers that were cast
* @param char **spoiled_tracker_strings return value of the trackers that were spoiled
*/
bool API_RecordBallots(uint32_t num_selections,
uint32_t num_cast_ballots,
uint32_t num_spoil_ballots,
uint64_t total_num_ballots,
uint64_t *cast_ids,
uint64_t *spoil_ids,
uint32_t total_num_ballots,
char **cast_ids,
char **spoil_ids,
char **external_identifiers,
struct register_ballot_message *encrypted_ballots,
char *export_path,
char *filename_prefix,

Просмотреть файл

@ -6,7 +6,10 @@
#include <electionguard/decryption/trustee.h>
/**
* Perform all the steps necessary to tally the encypted ballots file and decrypt the tallies. */
* Perform all the steps necessary to tally the encypted ballots file and decrypt the tallies.
* * @param char **output_filename return value of the generated file.
* when successful, caller is responsible for Freeing output_filename
*/
bool API_TallyVotes(struct api_config config,
struct trustee_state *trustee_states,
uint32_t num_decrypting_trustees,

Просмотреть файл

@ -1,13 +0,0 @@
#include <stdbool.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#ifdef _MSC_VER
#include <io.h>
#endif
FILE *File_new(char const *template);
void File_close(FILE *file);
void File_seek(FILE *file);

Просмотреть файл

@ -7,6 +7,11 @@
// constant expressions, allowing us to use them as array sizes, but
// they are not compiled away, making it easier to debug.
enum MAX_EXTERNAL_IDENIFIER_LENGTH_e
{
MAX_EXTERNAL_ID_LENGTH = 4095
};
/** The maximum number of total trustees. */
enum MAX_TRUSTEES_e
{
@ -14,16 +19,27 @@ enum MAX_TRUSTEES_e
};
/**
* The maximum number of ballots that can be cast by a voting single
* coordinator. */
* The maximum number of ballots that can be
* cast at one time by a voting single coordinator.
*/
enum MAX_BALLOTS_e
{
MAX_BALLOTS = 1000
/** The Maximum ballots for an election
* that one Voting Coordinator can track
*/
MAX_BALLOTS = 10000,
/** The Maximum ballots that can be passed
* to a Voting Coordinator for
* Registration, Casting, or Spoiling at one time
*/
MAX_BALLOT_PAYLOAD = 2000
};
/**
* The maximum number of selections that can be present for a single
* contest. */
* The maximum number of selections that
* can be present for a single contest.
*/
enum MAX_SELECTIONS_e
{
MAX_SELECTIONS = 1000

Просмотреть файл

@ -23,6 +23,9 @@ enum Voting_Coordinator_status
VOTING_COORDINATOR_IO_ERROR,
VOTING_COORDINATOR_SERIALIZE_ERROR,
VOTING_COORDINATOR_DESERIALIZE_ERROR,
VOTING_COORDINATOR_INVALID_BALLOT_INDEX,
VOTING_COORDINATOR_END_OF_FILE,
VOTING_COORDINATOR_ERROR_ALREADY_EXISTS,
};
/************************** INITIALIZATION & FREEING ***************************/
@ -31,7 +34,19 @@ enum Voting_Coordinator_status
// formats so that we can validate that the ballots we receive are
// well-formed?
/** Create a new voting coordinator. */
/**
* Create a new voting coordinator.
*
* This component mutates the state of votes by handling loading ballots,
* registering them, marking them as cast, or spoiled, and caching the state.
*
* The Voting Coordinator can track the state of:
* -- MAX_BALLOTS it can hold in memory and
* -- MAX_BALLOT_PAYLOAD selections and trackers it can buffer before writing to cache
*
* @param uint32_t num_selections the total number of selections
* available on the ballot
*/
struct Voting_Coordinator_new_r Voting_Coordinator_new(uint32_t num_selections);
struct Voting_Coordinator_new_r
@ -40,31 +55,46 @@ struct Voting_Coordinator_new_r
Voting_Coordinator coordinator;
};
/** Free a ballot box. */
/**
* Free the Voting Coordinator
*
* Clears the component state including the tracked state of a ballot box
* and the buffered selections and trackers being held in memory.
*/
void Voting_Coordinator_free(Voting_Coordinator coordinator);
/**
* Clear the selections buffer and drop instance references to any external id's
*/
enum Voting_Coordinator_status Voting_Coordinator_clear_buffer(Voting_Coordinator coordinator);
/****************** REGISTERING, CASTING & SPOILING BALLOTS *******************/
/**
* Register a ballot with the coordinator so that it may be cast or
* spoiled. */
* spoiled.
*/
enum Voting_Coordinator_status
Voting_Coordinator_register_ballot(Voting_Coordinator coordinator,
struct register_ballot_message message);
char *external_identifier,
struct register_ballot_message message,
char **out_ballot_tracker);
/** Mark the ballot specified by ballot_id as cast. */
enum Voting_Coordinator_status
Voting_Coordinator_cast_ballot(Voting_Coordinator coordinator,
struct ballot_identifier ballot_id);
char *external_identifier,
char **out_tracker);
/** Mark the ballot specified by ballot_id as spoiled. */
enum Voting_Coordinator_status
Voting_Coordinator_spoil_ballot(Voting_Coordinator coordinator,
struct ballot_identifier ballot_id);
char *external_identifier,
char **out_tracker);
/** Get the tracker string for the given ballot_id */
char *Voting_Coordinator_get_tracker(Voting_Coordinator coordinator,
struct ballot_identifier ballot_id);
char *external_identifier);
/***************************** EXPORTING BALLOTS ******************************/
@ -72,8 +102,34 @@ char *Voting_Coordinator_get_tracker(Voting_Coordinator coordinator,
// @todo jwaksbaum What format is it writing in?
/** Write all of the cast and spoiled ballots to out. */
/**
* Write all of the cast and spoiled ballots to out to the specified file.
* Clears the Voting Coordinator buffer in the process
*/
enum Voting_Coordinator_status
Voting_Coordinator_export_ballots(Voting_Coordinator coordinator, FILE *out);
Voting_Coordinator_export_buffered_ballots(Voting_Coordinator coordinator, FILE *out);
/**
* Import ballots from the specified file. Expects a file that was build
* With the Voting Encrypter using the format:
* <ballot_id> TAB <encrypted_ballot_message> \n
*
* @see Voting_Encrypter_write_ballot
* @param Voting_Coordinator coordinator the voting coordinator instance
* @param uint64_t start_index the start index to import form the file
* @param uint64_t count the count of ballots to import
* @param FILE *in the file to read
* @param char **out_external_identifiers index-based array of external identifiers
* @param register_ballot_message out_messages index-based collection of register ballot messages
* @return Voting_Coordinator_status indicating success or failure
*/
enum Voting_Coordinator_status
Voting_Coordinator_import_encrypted_ballots(Voting_Coordinator coordinator,
uint64_t start_index,
uint64_t count,
uint32_t num_selections,
FILE *in,
char **out_external_identifiers,
struct register_ballot_message *out_messages);
#endif /* __VOTING_COORDINATOR_H__ */

Просмотреть файл

@ -2,6 +2,8 @@
#define __VOTING_ENCRYPTER_H__
#include <stdbool.h>
#include <stdlib.h>
#include <stdio.h>
#include <electionguard/crypto.h>
#include <electionguard/voting/messages.h>
@ -60,6 +62,7 @@ void Voting_Encrypter_free(Voting_Encrypter encrypter);
* when done, ie. they own them. */
struct Voting_Encrypter_encrypt_ballot_r
Voting_Encrypter_encrypt_ballot(Voting_Encrypter encrypter,
char *external_identifier,
bool const *selections,
uint32_t expected_num_selected);
@ -70,6 +73,14 @@ struct Voting_Encrypter_encrypt_ballot_r
struct ballot_tracker tracker;
struct ballot_identifier id;
};
bool Validate_selections(bool const *selections, uint32_t num_selections, uint32_t expected_num_selected);
bool Validate_selections(
bool const *selections, uint32_t num_selections, uint32_t expected_num_selected);
/** Write a single ballot to out, using the format
* <ballot_id> TAB <encrypted_ballot_message> \n
*/
enum Voting_Encrypter_status
Voting_Encrypter_write_ballot(FILE *out, char *external_identifier,
struct register_ballot_message *encrypted_ballot_message);
#endif /* __VOTING_ENCRYPTER_H__ */

Просмотреть файл

@ -40,4 +40,12 @@ struct ballot_identifier
uint8_t const *bytes;
};
struct external_identifier_message
{
uint64_t len;
uint8_t const *bytes;
};
bool Messages_are_equal(struct register_ballot_message *left_message, struct register_ballot_message *right_message);
#endif /* __VOTING_MESSAGES_H__ */

Просмотреть файл

@ -11,5 +11,5 @@ extraction:
- cmake --version
- cmake -S . -B build
- cmake --build build
- ElectionGuard_DIR="$PWD/build/ElectionGuard" cmake -S examples/simple -B simple_build
- cmake --build simple_build --target simple
- ElectionGuard_DIR="$PWD/build/ElectionGuard" cmake -S examples/api -B api_build
- cmake --build api_build --target api

Просмотреть файл

@ -27,7 +27,7 @@ static bool export_trustee_states(struct trustee_state *trustee_states);
// Global state
static struct api_config api_config;
static KeyCeremony_Coordinator coordinator;
static KeyCeremony_Coordinator _keyceremony_coordinator;
static KeyCeremony_Trustee trustees[MAX_TRUSTEES];
bool API_CreateElection(struct api_config *config,
@ -121,8 +121,10 @@ bool API_CreateElection(struct api_config *config,
KeyCeremony_Trustee_free(trustees[i]);
}
if (coordinator != NULL)
KeyCeremony_Coordinator_free(coordinator);
// Key Ceremony Coordinator is only used in
// this part of the workflow so free it
if (_keyceremony_coordinator != NULL)
KeyCeremony_Coordinator_free(_keyceremony_coordinator);
Crypto_parameters_free();
@ -131,6 +133,8 @@ bool API_CreateElection(struct api_config *config,
void API_CreateElection_free(struct joint_public_key joint_key,
struct trustee_state *trustee_states)
{
if (trustee_states != NULL)
{
for (uint32_t i = 0; i < api_config.num_trustees; i++)
{
@ -140,6 +144,7 @@ void API_CreateElection_free(struct joint_public_key joint_key,
trustee_states[i].bytes = NULL;
}
}
}
if (joint_key.bytes != NULL)
{
@ -158,7 +163,7 @@ bool initialize_coordinator(void)
if (result.status != KEYCEREMONY_COORDINATOR_SUCCESS)
ok = false;
else
coordinator = result.coordinator;
_keyceremony_coordinator = result.coordinator;
return ok;
}
@ -200,8 +205,10 @@ bool generate_keys(void)
if (ok)
{
enum KeyCeremony_Coordinator_status status =
KeyCeremony_Coordinator_receive_key_generated(coordinator,
key_generated);
KeyCeremony_Coordinator_receive_key_generated(
_keyceremony_coordinator,
key_generated
);
if (status != KEYCEREMONY_COORDINATOR_SUCCESS)
ok = false;
}
@ -221,7 +228,7 @@ struct all_keys_received_message receive_keys(void)
struct all_keys_received_message message = {.bytes = NULL};
struct KeyCeremony_Coordinator_all_keys_received_r result =
KeyCeremony_Coordinator_all_keys_received(coordinator);
KeyCeremony_Coordinator_all_keys_received(_keyceremony_coordinator);
if (result.status == KEYCEREMONY_COORDINATOR_SUCCESS)
message = result.message;
@ -254,7 +261,7 @@ bool generate_shares(struct all_keys_received_message all_keys_received)
{
enum KeyCeremony_Coordinator_status cstatus =
KeyCeremony_Coordinator_receive_shares_generated(
coordinator, shares_generated);
_keyceremony_coordinator, shares_generated);
if (cstatus != KEYCEREMONY_COORDINATOR_SUCCESS)
ok = false;
}
@ -274,7 +281,7 @@ struct all_shares_received_message receive_shares()
struct all_shares_received_message message = {.bytes = NULL};
struct KeyCeremony_Coordinator_all_shares_received_r result =
KeyCeremony_Coordinator_all_shares_received(coordinator);
KeyCeremony_Coordinator_all_shares_received(_keyceremony_coordinator);
if (result.status == KEYCEREMONY_COORDINATOR_SUCCESS)
message = result.message;
@ -304,7 +311,7 @@ bool verify_shares(struct all_shares_received_message all_shares_received)
{
enum KeyCeremony_Coordinator_status status =
KeyCeremony_Coordinator_receive_shares_verified(
coordinator, shares_verified);
_keyceremony_coordinator, shares_verified);
if (status != KEYCEREMONY_COORDINATOR_SUCCESS)
ok = false;
}
@ -324,7 +331,7 @@ struct joint_public_key publish_joint_key(void)
struct joint_public_key key = {.bytes = NULL};
struct KeyCeremony_Coordinator_publish_joint_key_r cresult =
KeyCeremony_Coordinator_publish_joint_key(coordinator);
KeyCeremony_Coordinator_publish_joint_key(_keyceremony_coordinator);
if (cresult.status == KEYCEREMONY_COORDINATOR_SUCCESS)
key = cresult.key;

Просмотреть файл

@ -2,22 +2,29 @@
#include <electionguard/api/encrypt_ballot.h>
#include <log.h>
#include "api/base_hash.h"
#include "directory.h"
#include "api/filename.h"
#include "serialize/voting.h"
#include "voting/num_ballots.h"
static bool initialize_encrypter(struct joint_public_key joint_key);
static bool export_ballot(char *export_path, char *filename_prefix, char **output_filename, char *identifier,
struct register_ballot_message *encrypted_ballot_message);
// Global state
static struct api_config api_config;
static Voting_Encrypter encrypter;
static Voting_Encrypter _encrypter;
bool API_EncryptBallot(uint8_t *selections_byte_array,
uint32_t expected_num_selected,
struct api_config config,
uint64_t *current_num_ballots,
uint64_t *identifier,
char *external_identifier,
struct register_ballot_message *encrypted_ballot_message,
char *export_path,
char *filename_prefix,
char **output_filename,
char **tracker_string)
{
bool ok = true;
@ -27,7 +34,6 @@ bool API_EncryptBallot(uint8_t *selections_byte_array,
Crypto_parameters_new();
api_config = config;
create_base_hash_code(api_config);
Voting_num_ballots = *current_num_ballots;
// Convert selections byte array to boolean array
// And validate ballot selections before continuing
@ -40,7 +46,9 @@ bool API_EncryptBallot(uint8_t *selections_byte_array,
// Initialize Encrypter
if (ok)
{
ok = initialize_encrypter(api_config.joint_key);
}
// Encrypt ballot
@ -48,9 +56,15 @@ bool API_EncryptBallot(uint8_t *selections_byte_array,
.id = {.bytes = NULL},
.tracker = {.bytes = NULL},
};
if (ok)
{
result = Voting_Encrypter_encrypt_ballot(encrypter, selections, expected_num_selected);
result = Voting_Encrypter_encrypt_ballot(
_encrypter,
external_identifier,
selections,
expected_num_selected
);
if (result.status != VOTING_ENCRYPTER_SUCCESS)
ok = false;
@ -58,7 +72,7 @@ bool API_EncryptBallot(uint8_t *selections_byte_array,
{
*encrypted_ballot_message = result.message;
// Deserialize the id to get its ulong representation
// Deserialize the id to get its representation
struct ballot_identifier_rep id_rep;
struct serialize_state state = {
.status = SERIALIZE_STATE_READING,
@ -67,16 +81,22 @@ bool API_EncryptBallot(uint8_t *selections_byte_array,
.buf = (uint8_t *)result.id.bytes,
};
Serialize_read_ballot_identifier(&state, &id_rep);
*identifier = id_rep.id;
// Convert tracker to string represntation
*tracker_string = display_ballot_tracker(result.tracker);
}
}
// Voting_Encrypter_encrypt_ballot will increment the global Voting_num_ballots.
// Update the *current_num_ballots param to have new value tracked by caller
*current_num_ballots = Voting_num_ballots;
}
}
// Export to file system
if (ok)
ok = export_ballot(
export_path,
filename_prefix,
output_filename,
external_identifier,
encrypted_ballot_message
);
// Clean up
@ -92,10 +112,10 @@ bool API_EncryptBallot(uint8_t *selections_byte_array,
result.tracker.bytes = NULL;
}
if (encrypter != NULL)
if (_encrypter != NULL)
{
Voting_Encrypter_free(encrypter);
encrypter = NULL;
Voting_Encrypter_free(_encrypter);
_encrypter = NULL;
}
Crypto_parameters_free();
@ -113,7 +133,124 @@ void API_EncryptBallot_free(struct register_ballot_message message,
}
if (tracker_string != NULL)
{
free(tracker_string);
tracker_string = NULL;
}
}
bool API_EncryptBallot_soft_delete_file(char *export_path, char *filename)
{
bool ok = true;
char *default_prefix = "electionguard_encrypted_ballots-";
char *existing_filename = malloc(FILENAME_MAX + 1);
if (existing_filename == NULL)
{
ok = false;
}
char *soft_delete_filename = malloc(FILENAME_MAX + 1);
if (soft_delete_filename == NULL)
{
ok = false;
}
if (ok)
{
ok = generate_filename(export_path, filename, default_prefix, existing_filename);
}
if (ok)
{
ok = generate_unique_filename(export_path, filename, default_prefix, soft_delete_filename);
}
if (ok)
{
uint32_t result = rename(existing_filename, soft_delete_filename);
if (result != 0)
{
// failure to rename is a valid case
//ok = false;
DEBUG_PRINT(("API_EncryptBallot_soft_delete_file: unable to rename the file\n\n"));
}
}
if (!ok)
{
DEBUG_PRINT(("API_EncryptBallot_soft_delete_file: unable to sofdt delete the file\n\n"));
}
free(existing_filename);
free(soft_delete_filename);
return ok;
}
bool export_ballot(char *export_path, char *filename, char **output_filename,
char *identifier,
struct register_ballot_message *encrypted_ballot_message)
{
bool ok = true;
char *default_prefix = "electionguard_encrypted_ballots-";
*output_filename = malloc(FILENAME_MAX + 1);
if (output_filename == NULL)
{
ok = false;
return ok;
}
if (ok)
{
ok = generate_filename(export_path, filename, default_prefix, *output_filename);
DEBUG_PRINT(("API_EncryptBallots: generated filename for export at \"%s\"\n", *output_filename));
}
if (ok && !Directory_exists(export_path))
{
ok = create_directory(export_path);
}
if (ok)
{
FILE *out = fopen(*output_filename, "a+");
if (out == NULL)
{
INFO_PRINT(("API_EncryptBallots: export_ballot error accessing file\n"));
return false;
}
int seek_status = fseek(out, 0, SEEK_END);
if (seek_status != 0)
{
INFO_PRINT(("API_EncryptBallots: export_ballot error seeking file\n"));
fclose(out);
return false;
}
enum Voting_Encrypter_status status =
Voting_Encrypter_write_ballot(out, identifier, encrypted_ballot_message);
if (status != VOTING_ENCRYPTER_SUCCESS)
{
ok = false;
}
if (out != NULL)
{
fclose(out);
out = NULL;
}
}
if (!ok)
{
free(output_filename);
output_filename = NULL;
DEBUG_PRINT(("API_EncryptBallots: error exporting to: %s\n", *output_filename));
}
return ok;
}
bool initialize_encrypter(struct joint_public_key joint_key)
@ -132,7 +269,7 @@ bool initialize_encrypter(struct joint_public_key joint_key)
if (result.status != VOTING_ENCRYPTER_SUCCESS)
ok = false;
else
encrypter = result.encrypter;
_encrypter = result.encrypter;
return ok;
}

Просмотреть файл

@ -4,6 +4,113 @@
#define __USE_SECURE_APIS__
#endif
bool generate_filename(char *path_in, char *prefix_in, char* default_prefix, char *filename_out)
{
bool ok = true;
char prefix[FILENAME_MAX];
char path[FILENAME_MAX];
char *inUsePrefix = default_prefix;
size_t path_len = 0;
size_t prefix_size = 0;
// if path is provided, check the last char in the string to make sure it has the appropriate slash
#ifdef __USE_SECURE_APIS__
errno_t err = strcpy_s(path, FILENAME_MAX, path_in);
if (0 != err)
{
ok = false;
goto Exit;
}
#else
path_len = strlen(path_in);
if (path_len >= FILENAME_MAX)
{
ok = false;
goto Exit;
}
strcpy(path, path_in);
#endif
#ifdef __USE_SECURE_APIS__
path_len = strnlen_s(path, FILENAME_MAX);
if (path_len == 0 || path_len == FILENAME_MAX)
{
ok = false;
goto Exit;
}
#else
path_len = strlen(path);
#endif
if (path_len > 0)
{
char *directory_separator;
#ifdef _WIN32
directory_separator = "\\";
#else
directory_separator = "/";
#endif
char path_end_char = path_in[path_len-1];
if (path_end_char != directory_separator[0])
{
#ifdef __USE_SECURE_APIS__
err = strcat_s(path, FILENAME_MAX, directory_separator);
if (0 != err)
{
ok = false;
goto Exit;
}
#else
path_len += 1;
if (path_len >= FILENAME_MAX)
{
ok = false;
goto Exit;
}
strcat(path, directory_separator);
#endif
}
}
// if prefix is provided for filename, use it, otherwise use the default
prefix_size = strlen(prefix_in);
if (prefix_size > 0)
inUsePrefix = prefix_in;
else
prefix_size = strlen(inUsePrefix);
#ifdef __USE_SECURE_APIS__
err = strcpy_s(prefix, FILENAME_MAX, inUsePrefix);
if (0 != err)
{
ok = false;
goto Exit;
}
#else
if (prefix_size >= FILENAME_MAX)
{
ok = false;
goto Exit;
}
strcpy(prefix, inUsePrefix);
#endif
int32_t status = snprintf(filename_out, FILENAME_MAX, "%s%s", path, prefix);
if (status < 0)
ok = false;
Exit:
return ok;
}
bool generate_unique_filename(char *path_in, char *prefix_in, char* default_prefix, char *filename_out)
{
bool ok = true;

Просмотреть файл

@ -8,6 +8,8 @@
#include <string.h>
#include <time.h>
bool generate_filename(char *path_in, char *prefix_in, char* default_prefix, char *filename_out);
bool generate_unique_filename(char *path_in, char *prefix_in, char* default_prefix, char *filename_out);
#endif /* __API_FILENAME_H__ */

Просмотреть файл

@ -0,0 +1,150 @@
#include <stdlib.h>
#include <electionguard/api/load_ballots.h>
#include <log.h>
#include "api/base_hash.h"
#include "directory.h"
#include "api/filename.h"
static API_LoadBallots_status initialize_coordinator(uint32_t num_selections);
static API_LoadBallots_status load_ballots(uint64_t start_index,
uint64_t count,
uint32_t num_selections,
char *import_filepath,
char **out_external_identifiers,
struct register_ballot_message *out_encrypted_ballots);
// Global state
static Voting_Coordinator _load_coordinator = NULL;
/**
* Load a range of ballots from a specified file on the file system
*/
API_LoadBallots_status API_LoadBallots(
uint64_t start_index,
uint64_t count,
uint32_t num_selections,
char *import_filepath,
char **out_external_identifiers,
struct register_ballot_message *out_encrypted_ballots)
{
enum API_LoadBallots_status status = API_LOADBALLOTS_SUCCESS;
// Set global variables
Crypto_parameters_new();
if (_load_coordinator == NULL)
{
status = initialize_coordinator(num_selections);
}
if (status == API_LOADBALLOTS_SUCCESS)
{
status = load_ballots(
start_index,
count,
num_selections,
import_filepath,
out_external_identifiers,
out_encrypted_ballots
);
}
// Unlike other API Methods, we do not call
// Voting_Coordinator_free, because that component
// is used both when loading ballots and
// during record/cast/spoil.
Crypto_parameters_free();
return status;
}
/**
* Free the bytes allocated by LoadBallots
*/
API_LoadBallots_status API_LoadBallots_free(char *output_filename)
{
enum API_LoadBallots_status status = API_LOADBALLOTS_SUCCESS;
if (output_filename != NULL)
{
free(output_filename);
}
if (_load_coordinator != NULL)
{
Voting_Coordinator_free(_load_coordinator);
_load_coordinator = NULL;
}
return status;
}
API_LoadBallots_status initialize_coordinator(uint32_t num_selections)
{
struct Voting_Coordinator_new_r result =
Voting_Coordinator_new(num_selections);
if (result.status != VOTING_COORDINATOR_SUCCESS)
return API_LOADBALLOTS_INITIALIZATION_ERROR;
else
{
_load_coordinator = result.coordinator;
return API_LOADBALLOTS_SUCCESS;
}
}
API_LoadBallots_status load_ballots(uint64_t start_index,
uint64_t count,
uint32_t num_selections,
char *import_filepath,
char **out_external_identifiers,
struct register_ballot_message *out_encrypted_ballots)
{
// open the file for read
FILE *in = fopen(import_filepath, "r");
if (in == NULL)
{
INFO_PRINT(("API_LoadBallots: load_ballots error accessing file\n"));
return API_LOADBALLOTS_IO_ERROR;
}
int seek_status = fseek(in, 0, SEEK_SET);
if (seek_status != 0)
{
INFO_PRINT(("API_LoadBallots: load_ballots error seeking file\n"));
fclose(in);
return API_LOADBALLOTS_IO_ERROR;
}
enum Voting_Coordinator_status load_result = VOTING_COORDINATOR_SUCCESS;
load_result = Voting_Coordinator_import_encrypted_ballots(
_load_coordinator,
start_index,
count,
num_selections,
in,
out_external_identifiers,
out_encrypted_ballots
);
if (in != NULL)
{
fclose(in);
in = NULL;
}
switch (load_result)
{
case VOTING_COORDINATOR_INSUFFICIENT_MEMORY:
return API_LOADBALLOTS_INSUFFICIENT_MEMORY;
// TODO: other cases
case VOTING_COORDINATOR_SUCCESS:
default:
return API_LOADBALLOTS_SUCCESS;
}
}

Просмотреть файл

@ -2,25 +2,27 @@
#include <electionguard/api/record_ballots.h>
#include <log.h>
#include "api/base_hash.h"
#include "directory.h"
#include "api/filename.h"
#include "serialize/voting.h"
#include "voting/num_ballots.h"
#include "directory.h"
static bool initialize_coordinator(uint32_t num_selections);
static bool get_serialized_ballot_identifier(int64_t ballot_id, struct ballot_identifier *ballot_identifier);
static bool export_ballots(char *export_path, char *filename_prefix, char **output_filename);
// Global state
static Voting_Coordinator coordinator;
static Voting_Coordinator _record_coordinator = NULL;
bool API_RecordBallots(uint32_t num_selections,
uint32_t num_cast_ballots,
uint32_t num_spoil_ballots,
uint64_t total_num_ballots,
uint64_t *cast_ids,
uint64_t *spoil_ids,
uint32_t total_num_ballots,
char **cast_ids,
char **spoil_ids,
char **external_identifiers,
struct register_ballot_message *encrypted_ballots,
char *export_path,
char *filename_prefix,
@ -31,103 +33,100 @@ bool API_RecordBallots(uint32_t num_selections,
bool ok = true;
// Set global variables
Crypto_parameters_new();
Voting_num_ballots = total_num_ballots;
// Initialize Voting Coordinator
if (ok)
if (ok && _record_coordinator == NULL)
{
ok = initialize_coordinator(num_selections);
}
// Register Ballots
for (uint64_t i = 0; i < total_num_ballots && ok; i++)
for (uint32_t i = 0; i < total_num_ballots && ok; i++)
{
#ifdef DEBUG_PRINT
printf("API_RecordBallots :: Voting_Coordinator_register_ballot :: i = %lu :: encrypted_ballots[i].len = %lu\n", i, encrypted_ballots[i].len);
#endif
DEBUG_PRINT(("\nAPI_RecordBallots: register_ballot: id: %s .len = %lu\n", external_identifiers[i], encrypted_ballots[i].len));
char *registered_tracker;
enum Voting_Coordinator_status status =
Voting_Coordinator_register_ballot(coordinator, encrypted_ballots[i]);
Voting_Coordinator_register_ballot(
_record_coordinator,
external_identifiers[i],
encrypted_ballots[i],
&registered_tracker
);
if (status != VOTING_COORDINATOR_SUCCESS)
{
DEBUG_PRINT(("API_RecordBallots: register_ballot: failed!\n"));
ok = false;
}
else
{
DEBUG_PRINT(("API_RecordBallots: register_ballot: success! tracker: %.20s...\n", registered_tracker));
}
}
// Record Casted Ballots
// Record Cast Ballots
for (uint32_t i = 0; i < num_cast_ballots && ok; i++)
{
struct ballot_identifier ballot_identifier;
DEBUG_PRINT(("\nAPI_RecordBallots: cast_ballot : id: %s\n", cast_ids[i]));
ok = get_serialized_ballot_identifier(cast_ids[i], &ballot_identifier);
if (ok)
{
#ifdef DEBUG_PRINT
printf("API_RecordBallots :: Voting_Coordinator_cast_ballot :: i = %u :: cast_ids[i] = %lu\n", i, cast_ids[i]);
#endif
char *cast_tracker;
enum Voting_Coordinator_status status =
Voting_Coordinator_cast_ballot(coordinator, ballot_identifier);
casted_tracker_strings[i] = Voting_Coordinator_get_tracker(coordinator, ballot_identifier);
Voting_Coordinator_cast_ballot(_record_coordinator, cast_ids[i], &cast_tracker);
if (status != VOTING_COORDINATOR_SUCCESS)
{
DEBUG_PRINT(("API_RecordBallots: cast_ballot : failed!\n"));
casted_tracker_strings[i] = NULL;
ok = false;
}
// Free current ballot identifier
if (ballot_identifier.bytes != NULL)
else
{
free((void *)ballot_identifier.bytes);
ballot_identifier.bytes = NULL;
DEBUG_PRINT(("API_RecordBallots: cast_ballot : sucess! tracker: %.20s...\n", cast_tracker));
}
casted_tracker_strings[i] = cast_tracker;
}
// Record Spoiled Ballots
for (uint32_t i = 0; i < num_spoil_ballots && ok; i++)
{
struct ballot_identifier ballot_identifier;
DEBUG_PRINT(("\nAPI_RecordBallots: spoil_ballot: id: %s\n", spoil_ids[i]));
ok = get_serialized_ballot_identifier(spoil_ids[i], &ballot_identifier);
if (ok)
{
#ifdef DEBUG_PRINT
printf("API_RecordBallots :: Voting_Coordinator_spoil_ballot :: i = %u :: spoil_ids[i] = %lu\n", i, spoil_ids[i]);
#endif
char *spoiled_tracker;
enum Voting_Coordinator_status status =
Voting_Coordinator_spoil_ballot(coordinator, ballot_identifier);
spoiled_tracker_strings[i] = Voting_Coordinator_get_tracker(coordinator, ballot_identifier);
Voting_Coordinator_spoil_ballot(_record_coordinator, spoil_ids[i], &spoiled_tracker);
if (status != VOTING_COORDINATOR_SUCCESS)
{
DEBUG_PRINT(("API_RecordBallots: spoil_ballot : failed!\n"));
spoiled_tracker_strings[i] = NULL;
ok = false;
}
// Free current ballot identifier
if (ballot_identifier.bytes != NULL)
else
{
free((void *)ballot_identifier.bytes);
ballot_identifier.bytes = NULL;
DEBUG_PRINT(("API_RecordBallots: spoil_ballot: success! tracker: %.20s...\n", spoiled_tracker));
}
spoiled_tracker_strings[i] = spoiled_tracker;
}
// Export
if (ok)
{
ok = export_ballots(export_path, filename_prefix, output_filename);
}
// Clean up
if (coordinator != NULL)
{
Voting_Coordinator_free(coordinator);
coordinator = NULL;
}
Voting_Coordinator_clear_buffer(_record_coordinator);
// Unlike other API Methods, we do not call
// Voting_Coordinator_free, because that component
// is used both when loading ballots and
// during record/cast/spoil
Crypto_parameters_free();
@ -141,17 +140,32 @@ void API_RecordBallots_free(char *output_filename,
char **spoiled_tracker_strings)
{
if (output_filename != NULL)
{
free(output_filename);
output_filename = NULL;
}
for (uint32_t i = 0; i < num_cast_ballots; i++)
{
if (casted_tracker_strings[i] != NULL)
{
free(casted_tracker_strings[i]);
}
}
for (uint32_t i = 0; i < num_spoil_ballots; i++)
{
if (spoiled_tracker_strings[i] != NULL)
free(spoiled_tracker_strings[i]);
}
if (_record_coordinator != NULL)
{
Voting_Coordinator_free(_record_coordinator);
_record_coordinator = NULL;
}
}
bool initialize_coordinator(uint32_t num_selections)
{
bool ok = true;
@ -162,7 +176,7 @@ bool initialize_coordinator(uint32_t num_selections)
if (result.status != VOTING_COORDINATOR_SUCCESS)
ok = false;
else
coordinator = result.coordinator;
_record_coordinator = result.coordinator;
return ok;
}
@ -201,25 +215,40 @@ bool export_ballots(char *export_path, char *filename_prefix, char **output_file
{
bool ok = true;
char *default_prefix = "electionguard_ballots-";
*output_filename = malloc(FILENAME_MAX * sizeof(char));
ok = generate_unique_filename(export_path, filename_prefix, default_prefix, *output_filename);
#ifdef DEBUG_PRINT
printf("API_RecordBallots :: generated unique filename for export at \"%s\"\n", *output_filename);
#endif
*output_filename = malloc(FILENAME_MAX + 1);
if (output_filename == NULL)
{
ok = false;
return ok;
}
if (ok) {
if (ok)
{
ok = generate_filename(export_path, filename_prefix, default_prefix, *output_filename);
DEBUG_PRINT(("\nAPI_RecordBallots: generated unique filename for export at \"%s\"\n", *output_filename));
}
if (ok && !Directory_exists(export_path))
{
ok = create_directory(export_path);
}
if (ok)
{
FILE *out = fopen(*output_filename, "w+");
if (out == NULL)
{
INFO_PRINT(("API_RecordBallots: error accessing file\n"));
return false;
}
enum Voting_Coordinator_status status =
Voting_Coordinator_export_ballots(coordinator, out);
Voting_Coordinator_export_buffered_ballots(_record_coordinator, out);
if (status != VOTING_COORDINATOR_SUCCESS)
{
ok = false;
}
if (out != NULL)
{
@ -228,5 +257,12 @@ bool export_ballots(char *export_path, char *filename_prefix, char **output_file
}
}
if (!ok)
{
free(output_filename);
output_filename = NULL;
DEBUG_PRINT(("API_RecordBallots: error exporting to: %s\n", *output_filename));
}
return ok;
}

Просмотреть файл

@ -3,6 +3,8 @@
#include <electionguard/api/tally_votes.h>
#include <log.h>
#include "api/base_hash.h"
#include "api/filename.h"
#include "directory.h"
@ -23,7 +25,7 @@ static bool export_tally_votes(char *export_path, char *filename_prefix,
// Global state
static struct api_config api_config;
static Decryption_Coordinator coordinator;
static Decryption_Coordinator _decryption_coordinator;
static Decryption_Trustee decryption_trustees[MAX_TRUSTEES];
bool API_TallyVotes(struct api_config config,
@ -88,10 +90,10 @@ bool API_TallyVotes(struct api_config config,
decryption_trustees[i] = NULL;
}
if (coordinator != NULL)
if (_decryption_coordinator != NULL)
{
Decryption_Coordinator_free(coordinator);
coordinator = NULL;
Decryption_Coordinator_free(_decryption_coordinator);
_decryption_coordinator = NULL;
}
Crypto_parameters_free();
@ -102,8 +104,10 @@ bool API_TallyVotes(struct api_config config,
void API_TallyVotes_free(char *output_filename)
{
if (output_filename != NULL)
{
free(output_filename);
}
}
bool initialize_coordinator(void)
{
@ -115,7 +119,7 @@ bool initialize_coordinator(void)
if (result.status != DECRYPTION_COORDINATOR_SUCCESS)
ok = false;
else
coordinator = result.coordinator;
_decryption_coordinator = result.coordinator;
return ok;
}
@ -198,7 +202,7 @@ bool decrypt_tally_shares()
if (ok)
{
enum Decryption_Coordinator_status status =
Decryption_Coordinator_receive_share(coordinator, share);
Decryption_Coordinator_receive_share(_decryption_coordinator, share);
if (status != DECRYPTION_COORDINATOR_SUCCESS)
ok = false;
}
@ -225,7 +229,7 @@ bool request_missing_trustee_tally_shares(
if (ok)
{
struct Decryption_Coordinator_all_shares_received_r result =
Decryption_Coordinator_all_shares_received(coordinator);
Decryption_Coordinator_all_shares_received(_decryption_coordinator);
if (result.status != DECRYPTION_COORDINATOR_SUCCESS)
ok = false;
@ -271,7 +275,7 @@ bool decrypt_tally_decryption_fragments(
{
enum Decryption_Coordinator_status status =
Decryption_Coordinator_receive_fragments(
coordinator, decryption_fragments);
_decryption_coordinator, decryption_fragments);
if (status != DECRYPTION_COORDINATOR_SUCCESS)
ok = false;
@ -293,25 +297,40 @@ bool export_tally_votes(char *export_path, char *filename_prefix,
{
bool ok = true;
char *default_prefix = "electionguard_tally-";
*output_filename = malloc(FILENAME_MAX * sizeof(char));
ok = generate_unique_filename(export_path, filename_prefix, default_prefix, *output_filename);
#ifdef DEBUG_PRINT
printf("API_TALLYVOTES :: generated unique filename for export at \"%s\"\n", *output_filename);
#endif
*output_filename = malloc(FILENAME_MAX + 1);
if (output_filename == NULL)
{
ok = false;
return ok;
}
if (ok) {
if (ok)
{
ok = generate_unique_filename(export_path, filename_prefix, default_prefix, *output_filename);
DEBUG_PRINT(("API_TALLYVOTES :: generated unique filename for export at \"%s\"\n", *output_filename));
}
if (ok && !Directory_exists(export_path))
{
ok = create_directory(export_path);
}
if (ok)
{
FILE *out = fopen(*output_filename, "w+");
if (out == NULL)
{
INFO_PRINT(("API_TallyVotes: error accessing file\n"));
return false;
}
enum Decryption_Coordinator_status status =
Decryption_Coordinator_all_fragments_received(coordinator, out, tally_results_array);
Decryption_Coordinator_all_fragments_received(_decryption_coordinator, out, tally_results_array);
if (status != DECRYPTION_COORDINATOR_SUCCESS)
{
ok = false;
}
if (out != NULL)
{
@ -320,5 +339,12 @@ bool export_tally_votes(char *export_path, char *filename_prefix,
}
}
if (!ok)
{
free(output_filename);
output_filename = NULL;
DEBUG_PRINT(("API_TallyVotes: error exporting to: %s\n", *output_filename));
}
return ok;
}

Просмотреть файл

@ -104,7 +104,9 @@ void Crypto_parameters_new()
void Crypto_parameters_free()
{
mpz_clear(p);
mpz_clear(q);
mpz_clear(generator);
mpz_clear(bignum_one);
}
void print_base16(const mpz_t z)

Просмотреть файл

@ -983,10 +983,10 @@ bool Crypto_encryption_fprint(FILE *out, const struct encryption_rep *rep)
}
struct Crypto_encrypted_ballot_new_r
Crypto_encrypted_ballot_new(uint32_t num_selections, uint64_t id)
Crypto_encrypted_ballot_new(uint32_t num_selections, uint64_t ballot_id)
{
struct Crypto_encrypted_ballot_new_r result;
result.result.id = id;
result.result.id = ballot_id;
result.result.num_selections = num_selections;
result.result.selections =
malloc(num_selections * sizeof(*result.result.selections));
@ -1025,5 +1025,11 @@ void Crypto_encrypted_ballot_free(struct encrypted_ballot_rep *ballot)
Crypto_encryption_rep_free(&ballot->selections[i]);
}
free(ballot->selections);
if (ballot->dis_proof != NULL)
{
Crypto_dis_proof_free(ballot->dis_proof);
}
Crypto_cp_proof_free(&ballot->cp_proof);
}

Просмотреть файл

@ -242,7 +242,7 @@ void Crypto_encryption_rep_copy(struct encryption_rep *dst,
struct encryption_rep *src);
struct Crypto_encrypted_ballot_new_r
Crypto_encrypted_ballot_new(uint32_t num_selections, uint64_t id);
Crypto_encrypted_ballot_new(uint32_t num_selections, uint64_t ballot_id);
void Crypto_encrypted_ballot_free(struct encrypted_ballot_rep *ballot);
#endif /* __CRYPTO_REPS_H__ */

Просмотреть файл

@ -65,15 +65,26 @@ Decryption_Coordinator_new(uint32_t num_trustees, uint32_t threshold)
result.coordinator->num_trustees * sizeof(bool));
result.coordinator->tallies_initialized = false;
result.coordinator->num_tallies = 0;
}
return result;
}
void Decryption_Coordinator_free(Decryption_Coordinator c) { free(c); }
void Decryption_Coordinator_free(Decryption_Coordinator coordinator)
{
for(uint32_t i = 0; i < coordinator->num_tallies && coordinator->tallies_initialized; i++)
{
Crypto_encryption_rep_free(&coordinator->tallies[i]);
}
coordinator->tallies_initialized = false;
free(coordinator);
}
enum Decryption_Coordinator_status
Decryption_Coordinator_receive_share(Decryption_Coordinator c,
Decryption_Coordinator_receive_share(Decryption_Coordinator coordinator,
struct decryption_share share)
{
enum Decryption_Coordinator_status status = DECRYPTION_COORDINATOR_SUCCESS;
@ -97,39 +108,56 @@ Decryption_Coordinator_receive_share(Decryption_Coordinator c,
Serialize_read_decryption_share(&state, &share_rep);
if (state.status != SERIALIZE_STATE_READING)
{
status = DECRYPTION_COORDINATOR_DESERIALIZE_ERROR;
}
}
// Check that we haven't already seen this trustee
if (status == DECRYPTION_COORDINATOR_SUCCESS)
{
if (share_rep.trustee_index >= c->num_trustees)
if (share_rep.trustee_index >= coordinator->num_trustees)
{
status = DECRYPTION_COORDINATOR_INVALID_TRUSTEE_INDEX;
else if (c->anounced[share_rep.trustee_index])
}
else if (coordinator->anounced[share_rep.trustee_index])
{
status = DECRYPTION_COORDINATOR_DUPLICATE_TRUSTEE_INDEX;
}
}
if (status == DECRYPTION_COORDINATOR_SUCCESS)
{
c->anounced[share_rep.trustee_index] = true;
coordinator->anounced[share_rep.trustee_index] = true;
// We check to see if this is the first time through
if (c->tallies_initialized)
if (coordinator->tallies_initialized)
{
// If it is, we add in the shares for the new tally
if (share_rep.num_tallies != c->num_tallies)
if (share_rep.num_tallies != coordinator->num_tallies)
{
status = DECRYPTION_COORDINATOR_CONFUSED_DECRYPTION_TRUSTEE;
}
for (size_t i = 0; i < share_rep.num_tallies &&
DECRYPTION_COORDINATOR_SUCCESS == status;
for (size_t i = 0; i < share_rep.num_tallies
&& DECRYPTION_COORDINATOR_SUCCESS == status;
i++)
{
mul_mod_p(c->tallies[i].nonce_encoding,
c->tallies[i].nonce_encoding,
share_rep.tally_share[i].nonce_encoding);
if (!(0 == mpz_cmp(c->tallies[i].message_encoding,
share_rep.tally_share[i].message_encoding)))
mul_mod_p(
coordinator->tallies[i].nonce_encoding,
coordinator->tallies[i].nonce_encoding,
share_rep.tally_share[i].nonce_encoding
);
int compare_result = mpz_cmp(
coordinator->tallies[i].message_encoding,
share_rep.tally_share[i].message_encoding
);
if (compare_result != 0)
{
status = DECRYPTION_COORDINATOR_CONFUSED_DECRYPTION_TRUSTEE;
}
// printf("Comparing message encodings\n");
// print_base16(c->tallies[i].message_encoding);
@ -139,19 +167,29 @@ Decryption_Coordinator_receive_share(Decryption_Coordinator c,
else
{
//If this is the first one, we just copy them over
c->num_tallies = share_rep.num_tallies;
coordinator->num_tallies = share_rep.num_tallies;
for (size_t i = 0; i < share_rep.num_tallies; i++)
{
Crypto_encryption_rep_new(&c->tallies[i]);
mpz_set(c->tallies[i].nonce_encoding,
share_rep.tally_share[i].nonce_encoding);
mpz_set(c->tallies[i].message_encoding,
share_rep.tally_share[i].message_encoding);
Crypto_encryption_rep_new(&coordinator->tallies[i]);
mpz_set(
coordinator->tallies[i].nonce_encoding,
share_rep.tally_share[i].nonce_encoding
);
mpz_set(
coordinator->tallies[i].message_encoding,
share_rep.tally_share[i].message_encoding
);
}
c->tallies_initialized = true;
coordinator->tallies_initialized = true;
}
}
// free
for (uint32_t i = 0; i < share_rep.num_tallies; i++)
{
Crypto_encryption_rep_free(&share_rep.tally_share[i]);
}
return status;
}

Просмотреть файл

@ -127,7 +127,7 @@ Decryption_Trustee_new(uint32_t num_trustees, uint32_t threshold,
void Decryption_Trustee_free(Decryption_Trustee decryption_trustee)
{
for (size_t i = 0; i < MAX_SELECTIONS; i++)
for (size_t i = 0; i < decryption_trustee->num_selections; i++)
{
Crypto_encryption_rep_free(&decryption_trustee->tallies[i]);
}
@ -199,8 +199,13 @@ static void Decryption_Trustee_accum_tally(Decryption_Trustee decryption_trustee
struct encryption_rep *selections)
{
for (size_t i = 0; i < decryption_trustee->num_selections; i++)
Crypto_encryption_homomorphic_add(&decryption_trustee->tallies[i], &decryption_trustee->tallies[i],
&selections[i]);
{
Crypto_encryption_homomorphic_add(
&decryption_trustee->tallies[i],
&decryption_trustee->tallies[i],
&selections[i]
);
}
}
enum Decryption_Trustee_status
@ -208,6 +213,7 @@ Decryption_Trustee_tally_voting_record(Decryption_Trustee decryption_trustee, FI
{
enum Decryption_Trustee_status status = DECRYPTION_TRUSTEE_SUCCESS;
// get the number of ballots from the ballots file header
uint64_t num_ballots;
{
int num_read = fscanf(in, "%" PRIu64 "\n", &num_ballots);
@ -215,6 +221,7 @@ Decryption_Trustee_tally_voting_record(Decryption_Trustee decryption_trustee, FI
status = DECRYPTION_TRUSTEE_IO_ERROR;
}
//get the number of selections from the ballots file header
uint64_t num_selections;
{
int num_read = fscanf(in, "%" PRIu64 "\n", &num_selections);
@ -231,18 +238,30 @@ Decryption_Trustee_tally_voting_record(Decryption_Trustee decryption_trustee, FI
bool cast;
struct encryption_rep selections[MAX_SELECTIONS];
for (int j = 0; j < MAX_SELECTIONS; j++)
for (int j = 0; j < decryption_trustee->num_selections; j++)
{
Crypto_encryption_rep_new(&selections[j]);
}
status = Decryption_Trustee_read_ballot(in, &ballot_id, &cast,
decryption_trustee->num_selections, selections);
status = Decryption_Trustee_read_ballot(
in,
&ballot_id,
&cast,
decryption_trustee->num_selections,
selections
);
if (status == DECRYPTION_TRUSTEE_SUCCESS && cast)
{
Decryption_Trustee_accum_tally(decryption_trustee, selections);
}
for (int j = 0; j < decryption_trustee->num_selections; j++)
{
Crypto_encryption_rep_free(&selections[j]);
}
}
return status;
}
@ -313,6 +332,7 @@ Decryption_Trustee_compute_share(Decryption_Trustee decryption_trustee)
for (size_t i = 0; i < decryption_trustee->num_selections; i++)
{
Crypto_encryption_rep_free(&share_rep.tally_share[i]);
Crypto_cp_proof_free(&share_rep.cp_proofs[i]);
}
}

Просмотреть файл

@ -68,3 +68,14 @@ bool create_directory(const char *path)
return true;
}
bool Directory_exists(const char *path)
{
bool exists = false;
struct stat stats = {0};
if (stat(path, &stats) == 0 && S_ISDIR(stats.st_mode))
{
exists = true;
}
return exists;
}

Просмотреть файл

@ -6,4 +6,6 @@
bool create_directory(const char *path);
bool Directory_exists(const char *path);
#endif /* __DIRECTORY_H__ */

Просмотреть файл

@ -1,64 +0,0 @@
#include <stdbool.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#ifdef _MSC_VER
#include <io.h>
#endif
#include <electionguard/file.h>
// Create a new file according to template
FILE *File_new(char const *template)
{
bool ok = true;
int result_fd = -1;
FILE *result = NULL;
#ifdef _WIN32
const char *mode = "w+";
#else
const char *mode = "w+x";
#endif
// Duplicate the template. It needs to be mutable for mkstemp.
char *template_mut = strdup(template);
if (template_mut == NULL)
ok = false;
// Create and open the temporary file
if (ok)
{
result_fd = mkstemp(template_mut);
if (-1 == result_fd)
ok = false;
}
// Convert the file descriptor to a FILE*
if (ok)
{
result = fdopen(result_fd, mode);
if (result == NULL)
ok = false;
}
// Free the duplicated template
if (template_mut != NULL)
{
free(template_mut);
template_mut = NULL;
}
return result;
}
// Close file
void File_close(FILE *file)
{
fclose(file);
}
// Seek file to beginning
void File_seek(FILE *file)
{
int seek_status = fseek(file, 0L, SEEK_SET);
}

32
src/electionguard/log.h Normal file
Просмотреть файл

@ -0,0 +1,32 @@
#ifndef __LOG_H__
#define __LOG_H__
#include <stdarg.h>
#include <stdio.h>
static void log_stdout(const char *fmt, ...)
{
va_list args;
va_start(args, fmt);
vprintf(fmt, args);
va_end(args);
}
static void log_stderr(const char *fmt, ...)
{
va_list args;
va_start(args, fmt);
vfprintf(stderr, fmt, args);
va_end(args);
}
#ifdef DEBUG
#define DEBUG_PRINT(x) do { log_stdout x; } while (0)
#else
#define DEBUG_PRINT(x) do {} while (0)
#endif
#define INFO_PRINT(x) do { log_stdout x; } while (0)
#define ERROR_PRINT(x) do { log_stderr x; } while (0)
#endif /* __LOG_H__ */

Просмотреть файл

@ -4,7 +4,9 @@
#include <assert.h>
/**
* Best-effort function for zeroing a buffer that abstracts the API available for the platform */
* Best-effort function for zeroing a buffer
* that abstracts the API available for the platform
*/
void secure_zero_memory(
#ifdef _WIN32
#elif __STDC_LIB_EXT1__
@ -15,16 +17,24 @@ void secure_zero_memory(
size_t cnt)
{
#ifdef _WIN32
/* For Windows builds, use SecureZeroMemory, which guarantess that it will not be removed during optimization */
/**
* For Windows builds, use SecureZeroMemory,
* which guarantess that it will not be removed during optimization
*/
SecureZeroMemory(ptr, cnt);
#elif __STDC_LIB_EXT1__
/* For C11 (or higher) compilers, use memset_s, which guarantess that it will not be removed during optimization */
/**
* For C11 (or higher) compilers, use memset_s,
* which guarantess that it will not be removed during optimization
*/
int retval = memset_s(ptr, cnt, 0, cnt);
/* Check on debug only */
assert(retval == 0);
#else
/* For any other platforms, use memset with teh argument marked as volatile to try to avoid optimization. Best-effort.*/
/** For any other platforms, use memset with the argument
* marked as volatile to try to avoid optimization. Best-effort.
*/
memset((void*)ptr, 0, cnt);
#endif
}

Просмотреть файл

@ -1,3 +1,4 @@
#include "serialize/crypto.h"
#include "serialize/voting.h"
#include "serialize/builtins.h"
@ -36,3 +37,18 @@ void Serialize_read_ballot_identifier(struct serialize_state *state,
{
Serialize_read_uint64(state, &data->id);
}
bool Serialize_deserialize_register_ballot_message(struct register_ballot_message *ballot_message,
struct encrypted_ballot_rep *out_ballot_rep)
{
struct serialize_state state = {
.status = SERIALIZE_STATE_READING,
.len = ballot_message->len,
.offset = 0,
.buf = (uint8_t *)ballot_message->bytes,
};
Serialize_read_encrypted_ballot(&state, out_ballot_rep);
return state.status == SERIALIZE_STATE_READING;
}

Просмотреть файл

@ -1,17 +1,11 @@
#ifndef __SERIALIZE_VOTING_H__
#define __SERIALIZE_VOTING_H__
#include "crypto_reps.h"
#include "serialize/state.h"
#include "voting/message_reps.h"
void Serialize_reserve_register_ballot(struct serialize_state *state,
struct register_ballot_rep const *data);
void Serialize_write_register_ballot(struct serialize_state *state,
struct register_ballot_rep const *data);
void Serialize_read_register_ballot(struct serialize_state *state,
struct register_ballot_rep *data);
#include <electionguard/voting/messages.h>
void Serialize_reserve_ballot_tracker(struct serialize_state *state,
struct ballot_tracker_rep const *data);
@ -31,4 +25,10 @@ void Serialize_write_ballot_identifier(
void Serialize_read_ballot_identifier(struct serialize_state *state,
struct ballot_identifier_rep *data);
bool Serialize_deserialize_ballot_identifier_message(struct ballot_identifier *ballot_identifier_message,
struct ballot_identifier_rep *out_ballot_identifier_rep);
bool Serialize_deserialize_register_ballot_message(struct register_ballot_message *ballot_message,
struct encrypted_ballot_rep *out_ballot_rep);
#endif /* __SERIALIZE_VOTING_H__ */

1150
src/electionguard/uthash.h Normal file

Разница между файлами не показана из-за своего большого размера Загрузить разницу

Просмотреть файл

@ -0,0 +1,217 @@
#include <stdlib.h>
#include <stdio.h>
#include "voting/ballot_collection.h"
struct ballot_state *ballot_box = NULL;
static enum Ballot_Collection_result Ballot_Collection_update(
char *external_identifier, bool cast, bool spoiled, char **out_tracker);
static enum Ballot_Collection_result Ballot_Collection_assert_can_mutate_state(
struct ballot_state *existing_ballot);
enum Ballot_Collection_result Ballot_Collection_new()
{
// nothing to do, just following the component initialization pattern
return BALLOT_COLLECTION_SUCCESS;
}
enum Ballot_Collection_result Ballot_Collection_free()
{
enum Ballot_Collection_result delete_result = Ballot_Collection_remove_all();
if (delete_result != BALLOT_COLLECTION_SUCCESS)
{
return delete_result;
}
ballot_box = NULL;
return BALLOT_COLLECTION_SUCCESS;
}
uint32_t Ballot_Collection_size()
{
return HASH_COUNT(ballot_box);
}
enum Ballot_Collection_result Ballot_Collection_register_ballot(char *external_identifier, char *tracker, uint32_t registered_index)
{
struct ballot_state *existing_ballot, *new_ballot = NULL;
if (Ballot_Collection_get_ballot(external_identifier, &existing_ballot) == BALLOT_COLLECTION_SUCCESS)
{
return BALLOT_COLLECTION_ERROR_ALREADY_REGISTERED;
}
new_ballot = (struct ballot_state *)malloc(sizeof *new_ballot);
if (new_ballot == NULL)
{
return BALLOT_COLLECTION_ERROR_INSUFFICIENT_MEMORY;
}
new_ballot->external_identifier = external_identifier;
new_ballot->registered_index = registered_index;
new_ballot->registered = true;
new_ballot->cast = false;
new_ballot->spoiled = false;
new_ballot->tracker = tracker;
HASH_ADD_KEYPTR(
hh,
ballot_box,
new_ballot->external_identifier,
strlen(new_ballot->external_identifier),
new_ballot
);
return BALLOT_COLLECTION_SUCCESS;
}
enum Ballot_Collection_result Ballot_Collection_mark_cast(char *external_identifier, char **out_tracker)
{
return Ballot_Collection_update(external_identifier, true, false, out_tracker);
}
enum Ballot_Collection_result Ballot_Collection_mark_spoiled(char *external_identifier, char **out_tracker)
{
return Ballot_Collection_update(external_identifier, false, true, out_tracker);
}
enum Ballot_Collection_result Ballot_Collection_get_ballot(char *external_identifier, struct ballot_state **ballot)
{
struct ballot_state *existing_ballot = NULL;
HASH_FIND_STR(ballot_box, external_identifier, existing_ballot);
if (existing_ballot != NULL)
{
*ballot = existing_ballot;
existing_ballot = NULL;
return BALLOT_COLLECTION_SUCCESS;
}
else
{
return BALLOT_COLLECTION_ERROR_NOT_FOUND;
}
}
enum Ballot_Collection_result Ballot_Collection_remove_ballot(char *external_identifier)
{
struct ballot_state *existing_ballot;
if (Ballot_Collection_get_ballot(external_identifier, &existing_ballot) == BALLOT_COLLECTION_ERROR_NOT_FOUND)
{
return BALLOT_COLLECTION_ERROR_NOT_FOUND;
}
if (existing_ballot == NULL)
{
return BALLOT_COLLECTION_ERROR_UNKNOWN;
}
HASH_DEL(
ballot_box,
existing_ballot
);
free(existing_ballot);
existing_ballot = NULL;
return BALLOT_COLLECTION_SUCCESS;
}
enum Ballot_Collection_result Ballot_Collection_remove_all()
{
struct ballot_state *existing_ballot, *tmp_ballot;
HASH_ITER(hh, ballot_box, existing_ballot, tmp_ballot)
{
HASH_DEL(ballot_box, existing_ballot);
free(existing_ballot);
}
return BALLOT_COLLECTION_SUCCESS;
}
enum Ballot_Collection_result Ballot_Collection_update(
char *external_identifier, bool cast, bool spoiled, char **out_tracker)
{
if (cast == spoiled)
{
return BALLOT_COLLECTION_ERROR_INVALID_ARGUMENT;
}
struct ballot_state *new_ballot_state, *existing_ballot, *tmp = NULL;
if (Ballot_Collection_get_ballot(external_identifier, &existing_ballot) == BALLOT_COLLECTION_ERROR_NOT_FOUND)
{
return BALLOT_COLLECTION_ERROR_NOT_FOUND;
}
if (existing_ballot == NULL)
{
return BALLOT_COLLECTION_ERROR_UNKNOWN;
}
// verify that the existing_ballot is registered and not already cast or spoiled
enum Ballot_Collection_result assert_can_mutuate = Ballot_Collection_assert_can_mutate_state(
existing_ballot);
if (assert_can_mutuate != BALLOT_COLLECTION_SUCCESS)
{
return assert_can_mutuate;
}
// copy the existing state to the new state
new_ballot_state = (struct ballot_state *)malloc(sizeof *new_ballot_state);
if (new_ballot_state == NULL)
{
return BALLOT_COLLECTION_ERROR_INSUFFICIENT_MEMORY;
}
memcpy(new_ballot_state, existing_ballot, sizeof(ballot_state));
new_ballot_state->cast = cast;
new_ballot_state->spoiled = spoiled;
// replace the existing state with the new state
HASH_DEL(
ballot_box,
existing_ballot
);
free(existing_ballot);
existing_ballot = NULL;
HASH_ADD_KEYPTR(
hh,
ballot_box,
new_ballot_state->external_identifier,
strlen(new_ballot_state->external_identifier),
new_ballot_state
);
// assign out parameter
*out_tracker = new_ballot_state->tracker;
return BALLOT_COLLECTION_SUCCESS;
}
enum Ballot_Collection_result Ballot_Collection_assert_can_mutate_state(
struct ballot_state *existing_ballot)
{
if (existing_ballot == NULL)
{
return BALLOT_COLLECTION_ERROR_UNKNOWN;
}
if (existing_ballot->registered == false)
{
return BALLOT_COLLECTION_ERROR_UNKNOWN;
}
if (existing_ballot->cast == true)
{
return BALLOT_COLLECTION_ERROR_ALREADY_CAST;
}
if (existing_ballot->spoiled == true)
{
return BALLOT_COLLECTION_ERROR_ALREADY_SPOILED;
}
return BALLOT_COLLECTION_SUCCESS;
}

Просмотреть файл

@ -0,0 +1,53 @@
#ifndef __BALLOT_COLLECTION_H__
#define __BALLOT_COLLECTION_H__
#include <inttypes.h>
#include <stdbool.h>
#include "uthash.h"
/**
* Representation of a ballot in a ballot box
*/
typedef struct ballot_state {
char *external_identifier;
uint32_t registered_index;
bool registered;
bool cast;
bool spoiled;
char *tracker;
UT_hash_handle hh;
} ballot_state;
enum Ballot_Collection_result
{
BALLOT_COLLECTION_SUCCESS,
BALLOT_COLLECTION_ERROR_ALREADY_REGISTERED,
BALLOT_COLLECTION_ERROR_ALREADY_CAST,
BALLOT_COLLECTION_ERROR_ALREADY_SPOILED,
BALLOT_COLLECTION_ERROR_INSUFFICIENT_MEMORY,
BALLOT_COLLECTION_ERROR_INVALID_ARGUMENT,
BALLOT_COLLECTION_ERROR_NOT_FOUND,
BALLOT_COLLECTION_ERROR_UNKNOWN
};
enum Ballot_Collection_result Ballot_Collection_new();
enum Ballot_Collection_result Ballot_Collection_free();
uint32_t Ballot_Collection_size();
enum Ballot_Collection_result Ballot_Collection_register_ballot(char *external_identifier, char *tracker, uint32_t registered_index);
enum Ballot_Collection_result Ballot_Collection_mark_cast(char *external_identifier, char **out_tracker);
enum Ballot_Collection_result Ballot_Collection_mark_spoiled(char *external_identifier, char **out_tracker);
enum Ballot_Collection_result Ballot_Collection_get_ballot(char *external_identifier, struct ballot_state **ballot);
enum Ballot_Collection_result Ballot_Collection_remove_ballot(char *external_identifier);
enum Ballot_Collection_result Ballot_Collection_remove_all();
#endif /* __BALLOT_COLLECTION_H__ */

Просмотреть файл

@ -8,221 +8,319 @@
#include "serialize/crypto.h"
#include "serialize/voting.h"
#include "sha2-openbsd.h"
#include "voting/ballot_collection.h"
#include "voting/message_reps.h"
#include "voting/num_ballots.h"
// @design jwaksbaum This implementation relies on the fact that the
// Encrypters are generating uids from a shared counter, so we can
// just store the ballots at the index of their uid and they will all
// be contiguous. In a real implementation you would probably need a
// hash table.
// @design mwilhelm This implementation utilizes a hash table to keep
// track of external ballot identifiers (strings) that are already
// registered, cast, or spoiled. Additionally, to support low-memory
// systems it also allows batch-processing when importing ballots.
// there should only ever be one voting coordinator in memory on a system
// and it should retain it's state through the ballot load and cast cycle
/**
* The current state of a voting coordinator
*/
struct Voting_Coordinator_s
{
// The number of selections on each ballot
// The number of selections available on each ballot
uint32_t num_selections;
struct encryption_rep *selections[MAX_BALLOTS];
bool registered[MAX_BALLOTS];
bool cast[MAX_BALLOTS];
bool spoiled[MAX_BALLOTS];
char *tracker[MAX_BALLOTS];
// count of ballots registered
uint32_t registered_num_ballots;
// count of ballots in the buffer
uint32_t buffered_num_ballots;
// external id buffer
char *buffered_external_id[MAX_BALLOT_PAYLOAD];
// selections buffer
struct encryption_rep *selections[MAX_BALLOT_PAYLOAD];
};
static int Voting_Coordinator_ref_count = 0;
// use a static result as a singleton instance
// since only one coordinator should exist
// per application instance
static struct Voting_Coordinator_new_r voting_coordinator_singleton;
struct Voting_Coordinator_new_r Voting_Coordinator_new(uint32_t num_selections)
{
struct Voting_Coordinator_new_r result;
result.status = VOTING_COORDINATOR_SUCCESS;
voting_coordinator_singleton.status = VOTING_COORDINATOR_SUCCESS;
// Allocate the ballot box
result.coordinator = malloc(sizeof(struct Voting_Coordinator_s));
if (result.coordinator == NULL)
result.status = VOTING_COORDINATOR_INSUFFICIENT_MEMORY;
// Initialize the ballot box
if (result.status == VOTING_COORDINATOR_SUCCESS)
// sanity-check if the instance exists and matches this election configuration
if (voting_coordinator_singleton.coordinator != NULL
&& voting_coordinator_singleton.coordinator->num_selections == num_selections)
{
result.coordinator->num_selections = num_selections;
memset(result.coordinator->registered, 0,
sizeof(result.coordinator->registered));
#ifdef DEBUG_PRINT
printf("\nVoting_Coordinator_new: already exists. Returning existing instance\n");
#endif
Voting_Coordinator_ref_count++;
return voting_coordinator_singleton;
}
return result;
}
void Voting_Coordinator_free(Voting_Coordinator ballot_box)
if (voting_coordinator_singleton.coordinator != NULL
&& voting_coordinator_singleton.coordinator->num_selections != num_selections)
{
for(size_t i = 0; i < MAX_BALLOTS; i++)
if (ballot_box->registered[i]) {
for(uint32_t j=0; j<ballot_box->num_selections; j++){
Crypto_encryption_rep_free(&ballot_box->selections[i][j]);
#ifdef DEBUG_PRINT
printf("\nVoting_Coordinator_new: already exists for a different number of selections.\n");
#endif
struct Voting_Coordinator_new_r error_result;
error_result.status = VOTING_COORDINATOR_ERROR_ALREADY_EXISTS;
return error_result;
}
// Allocate the instance
voting_coordinator_singleton.coordinator = malloc(sizeof(struct Voting_Coordinator_s));
if (voting_coordinator_singleton.coordinator == NULL)
{
voting_coordinator_singleton.status = VOTING_COORDINATOR_INSUFFICIENT_MEMORY;
}
// Initialize the instance
if (voting_coordinator_singleton.status == VOTING_COORDINATOR_SUCCESS)
{
voting_coordinator_singleton.coordinator->num_selections = num_selections;
voting_coordinator_singleton.coordinator->registered_num_ballots = 0;
voting_coordinator_singleton.coordinator->buffered_num_ballots = 0;
Ballot_Collection_new();
Voting_Coordinator_ref_count++;
#ifdef DEBUG_PRINT
printf("\nVoting_Coordinator_new: success!\n");
#endif
}
return voting_coordinator_singleton;
}
enum Voting_Coordinator_status Voting_Coordinator_clear_buffer(Voting_Coordinator coordinator)
{
for(uint32_t i = 0; i < coordinator->buffered_num_ballots; i++)
{
// clear the slections buffer
if (coordinator->selections[i] != NULL)
{
for(uint32_t j = 0; j < coordinator->num_selections; j++)
{
Crypto_encryption_rep_free(&coordinator->selections[i][j]);
}
}
free(ballot_box);
// clear references to buffered external_id's
// but don't actually free the strings
if (coordinator->buffered_external_id[i] != NULL)
{
coordinator->buffered_external_id[i] = NULL;
}
}
coordinator->buffered_num_ballots = 0;
return VOTING_COORDINATOR_SUCCESS;
}
void Voting_Coordinator_free(Voting_Coordinator coordinator)
{
if (Voting_Coordinator_ref_count > 0)
{
Voting_Coordinator_ref_count--;
}
#ifdef DEBUG_PRINT
printf("\nVoting_Coordinator_free: active referneces: %u\n", Voting_Coordinator_ref_count);
#endif
if (Voting_Coordinator_ref_count > 0)
{
return;
}
Voting_Coordinator_clear_buffer(coordinator);
Ballot_Collection_free();
coordinator->num_selections = 0;
coordinator->buffered_num_ballots = 0;
coordinator->registered_num_ballots = 0;
voting_coordinator_singleton.status = VOTING_COORDINATOR_SUCCESS;
voting_coordinator_singleton.coordinator = NULL;
free(coordinator);
}
enum Voting_Coordinator_status
Voting_Coordinator_register_ballot(Voting_Coordinator c,
struct register_ballot_message message)
Voting_Coordinator_register_ballot(Voting_Coordinator coordinator,
char *external_identifier,
struct register_ballot_message message,
char **out_ballot_tracker)
{
enum Voting_Coordinator_status status = VOTING_COORDINATOR_SUCCESS;
// Verify the ballot does not already exist
struct ballot_state *existing_ballot = NULL;
if (Ballot_Collection_get_ballot(external_identifier, &existing_ballot
) == BALLOT_COLLECTION_SUCCESS)
{
free(existing_ballot);
return VOTING_COORDINATOR_DUPLICATE_BALLOT;
}
struct encrypted_ballot_rep message_rep;
// Verify we can load another ballot into the ballot state cache
if (Ballot_Collection_size() >= MAX_BALLOTS)
{
return VOTING_COORDINATOR_INVALID_BALLOT_INDEX;
}
// Verify we can load another ballot into the selections buffer cache
if (coordinator->buffered_num_ballots >= MAX_BALLOT_PAYLOAD)
{
return VOTING_COORDINATOR_INSUFFICIENT_MEMORY;
}
// Deserialize the message
if (status == VOTING_COORDINATOR_SUCCESS)
struct encrypted_ballot_rep message_rep;
if (!Serialize_deserialize_register_ballot_message(&message, &message_rep))
{
struct serialize_state state = {
.status = SERIALIZE_STATE_READING,
.len = message.len,
.offset = 0,
.buf = (uint8_t *)message.bytes,
};
Serialize_read_encrypted_ballot(&state, &message_rep);
if (state.status != SERIALIZE_STATE_READING)
status = VOTING_COORDINATOR_DESERIALIZE_ERROR;
return VOTING_COORDINATOR_DESERIALIZE_ERROR;
}
// Ensure we have space for another ballot
if (status == VOTING_COORDINATOR_SUCCESS)
// Verify the message content contains the correct number of selections
if (message_rep.num_selections != coordinator->num_selections)
{
if (message_rep.id >= MAX_BALLOTS)
status = VOTING_COORDINATOR_INVALID_BALLOT_ID;
else if (c->registered[message_rep.id])
status = VOTING_COORDINATOR_DUPLICATE_BALLOT;
else if (message_rep.num_selections != c->num_selections)
status = VOTING_COORDINATOR_INVALID_BALLOT;
return VOTING_COORDINATOR_INVALID_BALLOT;
}
if (status == VOTING_COORDINATOR_SUCCESS)
{
// Move the ballot into the ballot box state
c->selections[message_rep.id] = message_rep.selections;
// Mark it as registered but unspoiled and uncast
c->registered[message_rep.id] = true;
c->cast[message_rep.id] = false;
c->spoiled[message_rep.id] = false;
// Reconstruct the ballot tracker
SHA2_CTX context;
uint8_t *digest_buffer = malloc(sizeof(uint8_t) * SHA256_DIGEST_LENGTH);
if (digest_buffer == NULL)
{
// handle insufficient memory error
return VOTING_COORDINATOR_INSUFFICIENT_MEMORY;
}
SHA256Init(&context);
SHA256Update(&context, message.bytes, message.len);
SHA256Final(digest_buffer, &context);
struct ballot_tracker tracker = {
.len = SHA256_DIGEST_LENGTH,
.bytes = digest_buffer,
};
c->tracker[message_rep.id] = display_ballot_tracker(tracker);
*out_ballot_tracker = display_ballot_tracker(tracker);
// clear the tracker message
if (tracker.bytes != NULL)
{
free((void *)tracker.bytes);
tracker.bytes = NULL;
}
// Move the ballot into the ballot box state (registered)
if (Ballot_Collection_register_ballot(
external_identifier, *out_ballot_tracker, coordinator->registered_num_ballots
) != BALLOT_COLLECTION_SUCCESS)
{
// note: case alrady handled with Ballot_Collection_get_ballot,
// however we respect the failure return response from Ballot collection
// by returning a non-duplicated failure case
return VOTING_COORDINATOR_IO_ERROR;
}
return status;
// cache a handle to the external id for lookups
coordinator->buffered_external_id[coordinator->buffered_num_ballots] = external_identifier;
// cache the ballot selections in the buffer
coordinator->selections[coordinator->buffered_num_ballots] = message_rep.selections;
coordinator->registered_num_ballots++;
coordinator->buffered_num_ballots++;
return VOTING_COORDINATOR_SUCCESS;
}
static enum Voting_Coordinator_status
Voting_Coordinator_assert_registered(Voting_Coordinator coordinator,
struct ballot_identifier ballot_id,
uint64_t *i)
char *external_identifier)
{
enum Voting_Coordinator_status result = VOTING_COORDINATOR_SUCCESS;
struct ballot_state *existing_ballot = NULL;
if (Ballot_Collection_get_ballot(external_identifier, &existing_ballot) != BALLOT_COLLECTION_SUCCESS)
{
return VOTING_COORDINATOR_UNREGISTERED_BALLOT;
}
// Check that the bytes look like what we think a ballot
// identifier should be
if (ballot_id.len != sizeof(uint64_t))
result = VOTING_COORDINATOR_INVALID_BALLOT_ID;
if (existing_ballot->cast || existing_ballot->spoiled)
{
return VOTING_COORDINATOR_DUPLICATE_BALLOT;
}
// "Deserialize" the ballot identifier
if (result == VOTING_COORDINATOR_SUCCESS)
memcpy(i, ballot_id.bytes, ballot_id.len);
// Verify that the ballot identifier is a valid index
if (result == VOTING_COORDINATOR_SUCCESS)
if (*i >= Voting_num_ballots)
result = VOTING_COORDINATOR_INVALID_BALLOT_ID;
// Verify that the ballot is registered
if (result == VOTING_COORDINATOR_SUCCESS)
if (!coordinator->registered[*i])
result = VOTING_COORDINATOR_UNREGISTERED_BALLOT;
// Check that the ballot isn't already cast or spoiled
if (result == VOTING_COORDINATOR_SUCCESS)
//@ assert ballot_box->ballot_ids[i] == ballot->ballot_id;
if (coordinator->cast[*i] || coordinator->spoiled[*i])
result = VOTING_COORDINATOR_DUPLICATE_BALLOT;
return result;
return VOTING_COORDINATOR_SUCCESS;
}
enum Voting_Coordinator_status
Voting_Coordinator_cast_ballot(Voting_Coordinator coordinator,
struct ballot_identifier ballot_id)
char *external_identifier, char **out_tracker)
{
uint64_t i;
enum Ballot_Collection_result result = Ballot_Collection_mark_cast(
external_identifier, out_tracker);
if (result == BALLOT_COLLECTION_SUCCESS)
{
return VOTING_COORDINATOR_SUCCESS;
}
enum Voting_Coordinator_status result =
Voting_Coordinator_assert_registered(coordinator, ballot_id, &i);
// Mark the ballot as cast
if (result == VOTING_COORDINATOR_SUCCESS)
coordinator->cast[i] = true;
return result;
// TODO: map Ballot Colection enums to Voting Coordinator Enums
return VOTING_COORDINATOR_INVALID_BALLOT;
}
enum Voting_Coordinator_status
Voting_Coordinator_spoil_ballot(Voting_Coordinator coordinator,
struct ballot_identifier ballot_id)
char *external_identifier, char **out_tracker)
{
uint64_t i;
enum Ballot_Collection_result result = Ballot_Collection_mark_spoiled(
external_identifier, out_tracker);
if (result == BALLOT_COLLECTION_SUCCESS)
{
return VOTING_COORDINATOR_SUCCESS;
}
enum Voting_Coordinator_status result =
Voting_Coordinator_assert_registered(coordinator, ballot_id, &i);
// Mark the ballot as cast
if (result == VOTING_COORDINATOR_SUCCESS)
coordinator->spoiled[i] = true;
return result;
// TODO: map Ballot Colection enums to Voting Coordinator Enums
return VOTING_COORDINATOR_INVALID_BALLOT;
}
char *Voting_Coordinator_get_tracker(Voting_Coordinator coordinator,
struct ballot_identifier ballot_id)
char *external_identifier)
{
uint64_t i;
enum Voting_Coordinator_status result =
Voting_Coordinator_assert_registered(coordinator, ballot_id, &i);
return coordinator->tracker[i];
struct ballot_state *existing_ballot = NULL;
if (Ballot_Collection_get_ballot(external_identifier, &existing_ballot
) != BALLOT_COLLECTION_SUCCESS)
{
return NULL;
}
/* Write a single ballot to out, using the format
char *result = existing_ballot->tracker;
return result;
}
/* Write a single ballot to the out file to be imported for decryption, using the format
<cast> TAB <id> TAB <selection1> ... \n
*/
static enum Voting_Coordinator_status
Voting_Coordinator_write_ballot(FILE *out, uint64_t ballot_id, bool cast,
Voting_Coordinator_write_ballot(FILE *out, uint32_t registered_ballot_index, bool cast,
uint32_t num_selections, struct encryption_rep *selections)
{
enum Voting_Coordinator_status status = VOTING_COORDINATOR_SUCCESS;
// Write the fixed-length part of the line, ie. everything but the
// selections
{
const char *header_fmt = "%d\t%" PRIu64;
int io_status = fprintf(out, header_fmt, cast, ballot_id);
// Write the fixed-length part of the line,
// ie. everything but the selections
const char *header_fmt = "%d\t%" PRIu32;
int io_status = fprintf(out, header_fmt, cast, registered_ballot_index);
if (io_status < 0)
status = VOTING_COORDINATOR_IO_ERROR;
}
// Write the selections
for (uint32_t i = 0;
@ -248,14 +346,14 @@ Voting_Coordinator_write_ballot(FILE *out, uint64_t ballot_id, bool cast,
return status;
}
enum Voting_Coordinator_status
Voting_Coordinator_export_ballots(Voting_Coordinator c, FILE *out)
static enum Voting_Coordinator_status
Voting_Coordinator_write_ballots_file_header(Voting_Coordinator coordinator, FILE *out)
{
enum Voting_Coordinator_status status = VOTING_COORDINATOR_SUCCESS;
// Write the first line containing the number of ballots
{
int io_status = fprintf(out, "%" PRIu64 "\n", Voting_num_ballots);
int io_status = fprintf(out, "%" PRIu32 "\n", coordinator->registered_num_ballots);
if (io_status < 0)
status = VOTING_COORDINATOR_IO_ERROR;
}
@ -263,16 +361,239 @@ Voting_Coordinator_export_ballots(Voting_Coordinator c, FILE *out)
// Write the second line containing the number of selections per ballot
if (status == VOTING_COORDINATOR_SUCCESS)
{
int io_status = fprintf(out, "%" PRIu32 "\n", c->num_selections);
int io_status = fprintf(out, "%" PRIu32 "\n", coordinator->num_selections);
if (io_status < 0)
status = VOTING_COORDINATOR_IO_ERROR;
}
return status;
}
enum Voting_Coordinator_status
Voting_Coordinator_export_buffered_ballots(Voting_Coordinator coordinator, FILE *out)
{
enum Voting_Coordinator_status status = VOTING_COORDINATOR_SUCCESS;
// ensure the file cursor is at the beginning
int seek_status = fseek(out, 0L, SEEK_SET);
// write the header
status = Voting_Coordinator_write_ballots_file_header(coordinator, out);
if (status != VOTING_COORDINATOR_SUCCESS)
{
return status;
}
// flush the write buffer before reading the file
fflush(out);
// seek to the end of the file
int character, number_of_ballots_written = 0;
while ((character = fgetc(out)) != EOF)
{
if (character == '\n')
{
number_of_ballots_written++;
}
}
uint32_t registered_ballot_index = number_of_ballots_written;
#ifdef DEBUG_PRINT
printf("\nVoting_Coordinator_export: writing out %u ballots\n\n", coordinator->buffered_num_ballots);
#endif
// Write each ballot
for (uint64_t i = 0;
i < Voting_num_ballots && status == VOTING_COORDINATOR_SUCCESS; i++)
for (uint32_t i = 0;
i < coordinator->buffered_num_ballots && status == VOTING_COORDINATOR_SUCCESS;
i++)
{
struct ballot_state *ballot_state = NULL;
if (Ballot_Collection_get_ballot(
coordinator->buffered_external_id[i], &ballot_state
) != BALLOT_COLLECTION_SUCCESS)
{
printf("\n could not find in cache: %s\n", coordinator->buffered_external_id[i]);
return status = VOTING_COORDINATOR_INVALID_BALLOT_ID;
}
#ifdef DEBUG_PRINT
printf("Voting_Coordinator_export: id: %s registered: %d cast: %d spoiled: %d\n",
ballot_state->external_identifier, ballot_state->registered, ballot_state->cast, ballot_state->spoiled);
#endif
status = Voting_Coordinator_write_ballot(
out, i, c->cast[i], c->num_selections, c->selections[i]);
out,
registered_ballot_index,
ballot_state->cast,
coordinator->num_selections,
coordinator->selections[i]
);
registered_ballot_index++;
}
// clear the selections buffer
status = Voting_Coordinator_clear_buffer(coordinator);
return status;
}
static enum Voting_Coordinator_status
Voting_Coordinator_read_ballot(FILE *in,
uint32_t num_selections,
char *out_external_identifier,
struct encryption_rep *out_selections)
{
enum Voting_Coordinator_status status = VOTING_COORDINATOR_SUCCESS;
// get the external ballot Id
{
int num_read = fscanf(in, "%s", out_external_identifier);
if (num_read == EOF)
{
status = VOTING_COORDINATOR_END_OF_FILE;
}
else if (num_read != 1)
{
status = VOTING_COORDINATOR_IO_ERROR;
}
}
for (uint32_t i = 0;
i < num_selections && status == VOTING_COORDINATOR_SUCCESS; i++)
{
// read the nonce encoding
int num_read = fscanf(in, "\t(");
if (0 != num_read)
{
status = VOTING_COORDINATOR_IO_ERROR;
}
if (status == VOTING_COORDINATOR_SUCCESS)
{
if (!mpz_t_fscan(in, out_selections[i].nonce_encoding))
status = VOTING_COORDINATOR_IO_ERROR;
}
// move the cursor to the separator
if (status == VOTING_COORDINATOR_SUCCESS)
{
num_read = fscanf(in, ",");
if (0 != num_read)
status = VOTING_COORDINATOR_IO_ERROR;
}
// read the message encoding
if (status == VOTING_COORDINATOR_SUCCESS)
{
if (!mpz_t_fscan(in, out_selections[i].message_encoding))
status = VOTING_COORDINATOR_IO_ERROR;
}
if (status == VOTING_COORDINATOR_SUCCESS)
{
num_read = fscanf(in, ")");
if (0 != num_read)
status = VOTING_COORDINATOR_IO_ERROR;
}
}
return status;
}
enum Voting_Coordinator_status
Voting_Coordinator_import_encrypted_ballots(Voting_Coordinator coordinator,
uint64_t start_index,
uint64_t count,
uint32_t num_selections,
FILE *in,
char **out_external_identifiers,
struct register_ballot_message *out_messages)
{
enum Voting_Coordinator_status status = VOTING_COORDINATOR_SUCCESS;
// start at the index
if (in == NULL || fseek(in, start_index, SEEK_SET) != 0) {
status = VOTING_COORDINATOR_IO_ERROR;
return status;
}
#ifdef DEBUG_PRINT
printf("Voting_Coordinator_import_encrypted_ballots: attempting to import: %ld\n", count);
#endif
int scanResult = 0;
enum Voting_Coordinator_status load_status = VOTING_COORDINATOR_SUCCESS;
for (uint32_t i = 0; i < count && load_status == VOTING_COORDINATOR_SUCCESS; i++) {
out_external_identifiers[i] = malloc(MAX_EXTERNAL_ID_LENGTH*sizeof(char));
if (out_external_identifiers[i] == NULL)
{
load_status = VOTING_COORDINATOR_INSUFFICIENT_MEMORY;
}
// create an encryption representation
struct encryption_rep selections[num_selections];
for (int j = 0; j < num_selections; j++)
{
Crypto_encryption_rep_new(&selections[j]);
}
// load the data from the row in the file
load_status = Voting_Coordinator_read_ballot(
in,
num_selections,
out_external_identifiers[i],
selections);
#ifdef DEBUG_PRINT
printf("Voting_Coordinator_import_encrypted_ballots: imported: %s\n", out_external_identifiers[i]);
#endif
// reconstruct the original register_ballot_message
struct encrypted_ballot_rep encrypted_ballot;
struct Crypto_encrypted_ballot_new_r result =
Crypto_encrypted_ballot_new(num_selections, i);
encrypted_ballot = result.result;
// TODO: check/convert status from result
encrypted_ballot.selections = selections;
// serialize the encrypted ballot
struct serialize_state state = {
.status = SERIALIZE_STATE_RESERVING,
.len = 0,
.offset = 0,
.buf = NULL
};
Serialize_reserve_encrypted_ballot(&state, &encrypted_ballot);
Serialize_allocate(&state);
Serialize_write_encrypted_ballot(&state, &encrypted_ballot);
if (state.status != SERIALIZE_STATE_WRITING)
status = VOTING_COORDINATOR_SERIALIZE_ERROR;
else
{
// add the serialization to message_out
struct register_ballot_message scanned_message = (struct register_ballot_message)
{
.len = state.len,
.bytes = state.buf,
};
out_messages[start_index + scanResult] = scanned_message;
}
// clean up
// TODO: free encryption rep?
scanResult++;
}
return status;
}

Просмотреть файл

@ -12,9 +12,9 @@
#include "serialize/voting.h"
#include "sha2-openbsd.h"
#include "voting/message_reps.h"
#include "voting/num_ballots.h"
uint64_t Voting_num_ballots = 0;
// count of ballots encrypted with this encrypter
uint64_t _encrypted_ballot_count = 0;
struct Voting_Encrypter_s
{
@ -174,22 +174,32 @@ bool Validate_selections(bool const *selections, uint32_t num_selections, uint32
}
return count == expected_num_selected ? true : false;
}
struct Voting_Encrypter_encrypt_ballot_r
Voting_Encrypter_encrypt_ballot(Voting_Encrypter encrypter,
char *external_identifier,
bool const *selections,
uint32_t expected_num_selected)
{
// TODO: associate the external_identifier with the internal one, possibly via hash
struct Voting_Encrypter_encrypt_ballot_r balotR;
balotR.status = VOTING_ENCRYPTER_SUCCESS;
uint64_t internal_ballot_id = _encrypted_ballot_count;
struct Voting_Encrypter_encrypt_ballot_r ballot_result;
ballot_result.status = VOTING_ENCRYPTER_SUCCESS;
// validate selection
if (!Validate_selections(selections, encrypter->num_selections, expected_num_selected))
balotR.status = VOTING_ENCRYPTER_SELECTION_ERROR;
if (balotR.status == VOTING_ENCRYPTER_SUCCESS)
// Construct the ballot id
{
struct ballot_identifier_rep rep = {.id = Voting_num_ballots};
ballot_result.status = VOTING_ENCRYPTER_SELECTION_ERROR;
}
// TODO: refactor this since this block exists in record_ballots.c
// Construct the ballot id
if (ballot_result.status == VOTING_ENCRYPTER_SUCCESS)
{
struct ballot_identifier_rep rep = {
.id = internal_ballot_id
};
struct serialize_state state = {
.status = SERIALIZE_STATE_RESERVING,
@ -203,10 +213,13 @@ Voting_Encrypter_encrypt_ballot(Voting_Encrypter encrypter,
Serialize_write_ballot_identifier(&state, &rep);
if (state.status != SERIALIZE_STATE_WRITING)
balotR.status = VOTING_ENCRYPTER_SERIALIZE_ERROR;
{
ballot_result.status = VOTING_ENCRYPTER_SERIALIZE_ERROR;
}
else
{
balotR.id = (struct ballot_identifier){
ballot_result.id = (struct ballot_identifier)
{
.len = state.len,
.bytes = state.buf,
};
@ -214,17 +227,22 @@ Voting_Encrypter_encrypt_ballot(Voting_Encrypter encrypter,
}
// Construct the message
// TODO: refactor this out as it is similar to the one in coordinator.c
struct encrypted_ballot_rep encrypted_ballot;
if (balotR.status == VOTING_ENCRYPTER_SUCCESS)
if (ballot_result.status == VOTING_ENCRYPTER_SUCCESS)
{
struct Crypto_encrypted_ballot_new_r result =
// TODO: verify that _encrypted_ballot_count can be duplicated in a single file
// but not in a single instance
struct Crypto_encrypted_ballot_new_r temp_result =
Crypto_encrypted_ballot_new(encrypter->num_selections,
Voting_num_ballots);
encrypted_ballot = result.result;
balotR.status = Voting_Encrypter_Crypto_status_convert(result.status);
internal_ballot_id);
encrypted_ballot = temp_result.result;
// TODO: use this pattern for swith/case conversions?
ballot_result.status = Voting_Encrypter_Crypto_status_convert(temp_result.status);
}
if (balotR.status == VOTING_ENCRYPTER_SUCCESS)
if (ballot_result.status == VOTING_ENCRYPTER_SUCCESS)
{
struct encryption_rep tally;
Crypto_encryption_rep_new(&tally);
@ -234,14 +252,22 @@ Voting_Encrypter_encrypt_ballot(Voting_Encrypter encrypter,
mpz_init(nonce);
mpz_init(aggregate_nonce);
// encrypt the ballot
for (uint32_t i = 0; i < encrypter->num_selections; i++)
{
Crypto_encrypt(
&encrypted_ballot.selections[i], nonce, encrypter->source,
&encrypted_ballot.selections[i],
nonce,
encrypter->source,
&encrypter->joint_key,
selections[i] ? generator /*g^1*/ : bignum_one /*g^0*/);
Crypto_encryption_homomorphic_add(&tally, &tally,
&encrypted_ballot.selections[i]);
selections[i]
? generator /*g^1*/
: bignum_one /*g^0*/
);
Crypto_encryption_homomorphic_add(
&tally, &tally, &encrypted_ballot.selections[i]);
if (i == 0)
{
mpz_set(aggregate_nonce, nonce);
@ -250,55 +276,76 @@ Voting_Encrypter_encrypt_ballot(Voting_Encrypter encrypter,
{
add_mod_q(aggregate_nonce, aggregate_nonce, nonce);
}
Crypto_generate_dis_proof(&encrypted_ballot.dis_proof[i],
encrypter->source, encrypter->base_hash,
encrypter->source,
encrypter->base_hash,
selections[i],
encrypter->joint_key.public_key,
encrypted_ballot.selections[i], nonce);
encrypted_ballot.selections[i],
nonce);
if (!Crypto_check_dis_proof(encrypted_ballot.dis_proof[i],
encrypted_ballot.selections[i],
encrypter->base_hash,
encrypter->joint_key.public_key))
{
balotR.status = VOTING_ENCRYPTER_UNKNOWN_ERROR;
ballot_result.status = VOTING_ENCRYPTER_UNKNOWN_ERROR;
}
}
Crypto_generate_aggregate_cp_proof(
&encrypted_ballot.cp_proof, encrypter->source, aggregate_nonce,
tally, encrypter->base_hash, encrypter->joint_key.public_key);
&encrypted_ballot.cp_proof,
encrypter->source, aggregate_nonce,
tally, encrypter->base_hash,
encrypter->joint_key.public_key
);
if (!Crypto_check_aggregate_cp_proof(encrypted_ballot.cp_proof, tally,
encrypter->base_hash,
encrypter->joint_key.public_key,
expected_num_selected))
{
balotR.status = VOTING_ENCRYPTER_UNKNOWN_ERROR;
ballot_result.status = VOTING_ENCRYPTER_UNKNOWN_ERROR;
}
mpz_clear(nonce);
mpz_clear(aggregate_nonce);
Crypto_encryption_rep_free(&tally);
struct serialize_state state = {.status = SERIALIZE_STATE_RESERVING,
struct serialize_state state = {
.status = SERIALIZE_STATE_RESERVING,
.len = 0,
.offset = 0,
.buf = NULL};
.buf = NULL
};
Serialize_reserve_encrypted_ballot(&state, &encrypted_ballot);
Serialize_allocate(&state);
Serialize_write_encrypted_ballot(&state, &encrypted_ballot);
if (state.status != SERIALIZE_STATE_WRITING)
balotR.status = VOTING_ENCRYPTER_SERIALIZE_ERROR;
ballot_result.status = VOTING_ENCRYPTER_SERIALIZE_ERROR;
else
{
balotR.message = (struct register_ballot_message){
ballot_result.message = (struct register_ballot_message)
{
.len = state.len,
.bytes = state.buf,
};
}
//TODO clear proofs and encrypted ballot
}
else
{
printf("Voting_Encrypter_encrypt_ballot: ERROR! encrypting ballot");
}
// clear proofs and encrypted ballot
Crypto_encrypted_ballot_free(&encrypted_ballot);
// TODO: clear temp_result
// Construct the ballot tracker
if (balotR.status == VOTING_ENCRYPTER_SUCCESS)
if (ballot_result.status == VOTING_ENCRYPTER_SUCCESS)
{
SHA2_CTX context;
uint8_t *digest_buffer = malloc(sizeof(uint8_t) * SHA256_DIGEST_LENGTH);
@ -306,22 +353,87 @@ Voting_Encrypter_encrypt_ballot(Voting_Encrypter encrypter,
if (digest_buffer == NULL)
{
// handle insufficient memory error
balotR.status = VOTING_ENCRYPTER_INSUFFICIENT_MEMORY;
return balotR;
ballot_result.status = VOTING_ENCRYPTER_INSUFFICIENT_MEMORY;
return ballot_result;
}
SHA256Init(&context);
SHA256Update(&context, balotR.message.bytes, balotR.message.len);
SHA256Update(&context, ballot_result.message.bytes, ballot_result.message.len);
SHA256Final(digest_buffer, &context);
balotR.tracker = (struct ballot_tracker){
ballot_result.tracker = (struct ballot_tracker)
{
.len = SHA256_DIGEST_LENGTH,
.bytes = digest_buffer,
};
}
if (balotR.status == VOTING_ENCRYPTER_SUCCESS)
Voting_num_ballots++;
if (ballot_result.status == VOTING_ENCRYPTER_SUCCESS)
_encrypted_ballot_count++;
return balotR;
return ballot_result;
}
enum Voting_Encrypter_status
Voting_Encrypter_write_ballot(FILE *out, char *external_identifier,
struct register_ballot_message *encrypted_ballot_message)
{
enum Voting_Encrypter_status status = VOTING_ENCRYPTER_SUCCESS;
// TODO: dedupe this code. it is here, and in a few other places
struct encrypted_ballot_rep message_rep;
// Deserialize the message
if (status == VOTING_ENCRYPTER_SUCCESS)
{
struct serialize_state state = {
.status = SERIALIZE_STATE_READING,
.len = encrypted_ballot_message->len,
.offset = 0,
.buf = (uint8_t *)encrypted_ballot_message->bytes,
};
Serialize_read_encrypted_ballot(&state, &message_rep);
if (state.status != SERIALIZE_STATE_READING)
status = VOTING_ENCRYPTER_DESERIALIZE_ERROR;
}
// Write the fixed length part of the line
{
const char *header_fmt = "%s\t";
int io_status = fprintf(out, header_fmt, external_identifier);
if (io_status < 0)
status = VOTING_ENCRYPTER_IO_ERROR;
}
// Write the selections
for (uint32_t i = 0;
i < message_rep.num_selections && status == VOTING_ENCRYPTER_SUCCESS; i++) {
if(fprintf(out, "\t") < 1)
status = VOTING_ENCRYPTER_IO_ERROR;
if(VOTING_ENCRYPTER_SUCCESS == status) {
if(!Crypto_encryption_fprint(out, &message_rep.selections[i])) {
status = VOTING_ENCRYPTER_IO_ERROR;
}
}
}
// Write the newline
if (status == VOTING_ENCRYPTER_SUCCESS)
{
int put_status = fputc('\n', out);
if (put_status == EOF)
status = VOTING_ENCRYPTER_IO_ERROR;
}
// free the ballot rep
for (uint32_t i = 0; i < message_rep.num_selections; i++)
{
Crypto_encryption_rep_free(&message_rep.selections[i]);
}
free(message_rep.selections);
return status;
}

Просмотреть файл

@ -4,9 +4,6 @@
#include <stdbool.h>
#include <stddef.h>
#include <electionguard/max_values.h>
#include <electionguard/voting/messages.h>
struct ballot_tracker_rep
{
uint64_t id;

Просмотреть файл

@ -0,0 +1,66 @@
#include <electionguard/voting/messages.h>
#include <gmp.h>
#include "serialize/voting.h"
bool Messages_are_equal(struct register_ballot_message *left_message, struct register_ballot_message *right_message)
{
bool ok = true;
struct encrypted_ballot_rep left_ballot_rep;
ok = Serialize_deserialize_register_ballot_message(left_message, &left_ballot_rep);
struct encrypted_ballot_rep right_vallot_rep;
ok = Serialize_deserialize_register_ballot_message(right_message, &right_vallot_rep);
#ifdef DEBUG_PRINT
printf("\nMessages_are_equal: comparing left_id %lu right_id: %lu:",
left_ballot_rep.id, right_vallot_rep.id);
#endif
ok = left_ballot_rep.id == right_vallot_rep.id
&& left_ballot_rep.num_selections == right_vallot_rep.num_selections;
if (ok)
{
for (uint32_t i = 0; i < left_ballot_rep.num_selections && ok; i++)
{
bool comp_nonce = mpz_cmp(
left_ballot_rep.selections[i].nonce_encoding,
right_vallot_rep.selections[i].nonce_encoding
) == 0;
bool comp_msg = mpz_cmp(
left_ballot_rep.selections[i].message_encoding,
right_vallot_rep.selections[i].message_encoding
) == 0;
#ifdef DEBUG_PRINT
if (!comp_nonce)
{
printf("\nMessages_are_equal: ERROR! nonce_encoding did not match! :\n");
print_base16(left_ballot_rep.selections[i].nonce_encoding);
print_base16(right_vallot_rep.selections[i].nonce_encoding);
}
if (!comp_msg)
{
printf("\nMessages_are_equal: ERROR! message_encoding did not match! :\n");
print_base16(left_ballot_rep.selections[i].message_encoding);
print_base16(right_vallot_rep.selections[i].message_encoding);
}
#endif
ok = comp_nonce && comp_msg;
}
}
if (!ok)
{
#ifdef DEBUG_PRINT
printf("\nMessage_are_equal: false!\n");
#endif
}
return ok;
}

Просмотреть файл

@ -1,9 +0,0 @@
#ifndef __VOTING_NUM_BALLOTS_H__
#define __VOTING_NUM_BALLOTS_H__
#include <stdint.h>
// A hacky way to ensure unique ballot ids for now
extern uint64_t Voting_num_ballots;
#endif /* __VOTING_NUM_BALLOTS_H__ */

Просмотреть файл

@ -8,22 +8,26 @@
const char chars[16] = "2346789BCDFGHJKM";
char const *display_noun(uint8_t a, uint8_t b) {
char const *display_noun(uint8_t a, uint8_t b)
{
return get_noun((uint16_t)a*16 + (b>>4));
}
size_t chunk_len(uint8_t a, uint8_t b, uint8_t c, uint8_t d) {
size_t chunk_len(uint8_t a, uint8_t b, uint8_t c, uint8_t d)
{
// c and d aren't used; they're included in the argument list for consistency with display_chunk
(void)c; (void)d;
return strlen(display_noun(a, b)) + 5/* hex digits */ + 2/* spaces */;
}
size_t display_chunk(char *out, size_t out_len, uint8_t a, uint8_t b, uint8_t c, uint8_t d) {
size_t display_chunk(char *out, size_t out_len, uint8_t a, uint8_t b, uint8_t c, uint8_t d)
{
char const *noun = display_noun(a, b);
const size_t noun_len = strlen(noun);
const size_t space_used = noun_len + 5/* hex digits */ + 2/* spaces */;
assert(space_used <= out_len);
memcpy(out, noun, noun_len);
out[noun_len+0] = ' ';
out[noun_len+1] = chars[(b & 0x0f) >> 0];
@ -35,12 +39,22 @@ size_t display_chunk(char *out, size_t out_len, uint8_t a, uint8_t b, uint8_t c,
return space_used;
}
char *display_ballot_tracker(struct ballot_tracker tracker) {
char *display_ballot_tracker(struct ballot_tracker tracker)
{
// First compute how much space we need to allocate.
size_t len = 0, i;
for(i = 0; i+3 < tracker.len; i += 4)
len += chunk_len(tracker.bytes[i], tracker.bytes[i+1], tracker.bytes[i+2], tracker.bytes[i+3]);
if(i < tracker.len) {
{
len += chunk_len(
tracker.bytes[i],
tracker.bytes[i+1],
tracker.bytes[i+2],
tracker.bytes[i+3]
);
}
if(i < tracker.len)
{
const size_t remaining = tracker.len - i;
len += chunk_len(
remaining > 0 ? tracker.bytes[i+0] : 0,
@ -57,18 +71,33 @@ char *display_ballot_tracker(struct ballot_tracker tracker) {
// Now fill the space.
size_t cur_idx = 0;
for(i = 0; i+3 < tracker.len; i += 4)
cur_idx += display_chunk(result+cur_idx, len-cur_idx, tracker.bytes[i], tracker.bytes[i+1], tracker.bytes[i+2], tracker.bytes[i+3]);
if(i < tracker.len) {
{
cur_idx += display_chunk(
result+cur_idx,
len-cur_idx,
tracker.bytes[i],
tracker.bytes[i+1],
tracker.bytes[i+2],
tracker.bytes[i+3]
);
}
if(i < tracker.len)
{
const size_t remaining = tracker.len - i;
cur_idx += display_chunk(result+cur_idx, len-cur_idx,
cur_idx += display_chunk(
result+cur_idx,
len-cur_idx,
remaining > 0 ? tracker.bytes[i+0] : 0,
remaining > 1 ? tracker.bytes[i+1] : 0,
remaining > 2 ? tracker.bytes[i+2] : 0,
remaining > 3 ? tracker.bytes[i+3] : 0);
remaining > 3 ? tracker.bytes[i+3] : 0
);
}
// How paranoid can we be? Let's find out.
assert(cur_idx == len);
// Null terminator.
result[cur_idx-1] = '\0';

Просмотреть файл

Просмотреть файл