Skip to content

Commit

Permalink
Generate verifiable election before publishing results (#174)
Browse files Browse the repository at this point in the history
  • Loading branch information
David Morcillo authored Apr 22, 2021
1 parent 563f388 commit 6fdb321
Show file tree
Hide file tree
Showing 31 changed files with 9,371 additions and 11,373 deletions.
17 changes: 15 additions & 2 deletions .github/workflows/server-workflow.yml
Original file line number Diff line number Diff line change
Expand Up @@ -283,7 +283,7 @@ jobs:
mkdir -p voting_schemes/electionguard/ruby-adapter/public/assets/electionguard
cp -r /cache/python-to-js/electionguard-assets/* voting_schemes/electionguard/ruby-adapter/public/assets/electionguard
- name: Install system dependencies
run: sudo apt-get install -y libpq-dev libgtk2.0-0 libgtk-3-0 libgbm-dev libnotify-dev libgconf-2-4 libnss3 libxss1 libasound2 libxtst6 xauth xvfb
run: sudo apt-get update && sudo apt-get install -y libpq-dev libgtk2.0-0 libgtk-3-0 libgbm-dev libnotify-dev libgconf-2-4 libnss3 libxss1 libasound2 libxtst6 xauth xvfb
- name: Cache Ruby dependencies
uses: actions/cache@v2
env:
Expand Down Expand Up @@ -314,13 +314,26 @@ jobs:
cd bulletin_board/server
bundler exec rails db:create
bundler exec rails db:migrate
- name: Install Chrome
run: |
sudo apt-get update
sudo apt-get install -y unzip xvfb libxi6 libgconf-2-4
sudo curl -sS -o - https://dl-ssl.google.com/linux/linux_signing_key.pub | apt-key add
sudo echo "deb [arch=amd64] http://dl.google.com/linux/chrome/deb/ stable main" >> /etc/apt/sources.list.d/google-chrome.list
sudo apt-get -y update
sudo apt-get -y install google-chrome-stable
wget https://chromedriver.storage.googleapis.com/90.0.4430.24/chromedriver_linux64.zip
unzip chromedriver_linux64.zip
sudo mv chromedriver /usr/bin/chromedriver
sudo chown root:root /usr/bin/chromedriver
sudo chmod +x /usr/bin/chromedriver
- name: Run e2e tests
run: |
cd bulletin_board/server
npm run e2e:install
bundle exec rails s -e test -p 5017 &
sleep 5
npm run e2e:tests
npm run e2e:tests -- --browser chrome --headless
- name: Upload artifacts (screenshots)
if: ${{ always() }}
uses: actions/upload-artifact@v2
Expand Down
4 changes: 4 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,10 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0

## Unreleased

## Changed

- Tally and Key Ceremony components API have been changed a bit. The `setupElection` method should be called before binding the UI elements to the component.

## [0.19.0] - 2021-04-16

## Added
Expand Down
46 changes: 15 additions & 31 deletions bulletin_board/js-client/src/key-ceremony/key-ceremony.component.js
Original file line number Diff line number Diff line change
@@ -1,52 +1,36 @@
import { Client } from "../client/client";
import { Election } from "../election/election";
import { Trustee } from "../trustee/trustee";
// Components
import { TrusteeComponent } from "../trustee/trustee.component";

/**
* This class is used to bind any UI elements to a key ceremony process.
*/
export class KeyCeremonyComponent {
export class KeyCeremonyComponent extends TrusteeComponent {
/**
* Initialises the class with the given params.
* Setup the election for the trustee.
*
* @param {Object} params - An object that contains the initialization params.
* - {Object} bulletinBoardClientParams - An object to configure the bulletin board client.
* - {String} authorityPublicKeyJSON - The authority identification public key.
* - {String} electionUniqueId - The unique identifier of an election.
* - {String} trusteeUniqueId - The unique identifier of a trustee.
* - {Object} trusteeIdentificationKeys - An object that contains both the public and private key for
* the corresponding trustee.
* - {Object} trusteeWrapperAdapter - An object to interact with the trustee wrapper.
* @constructor
* - {Number} authorizationExpirationTimestamp - The timestamp until the authorization header is no longer valid.
*
* @returns {Promise<undefined>}
*/
constructor({
setupElection({
bulletinBoardClientParams,
authorityPublicKeyJSON,
electionUniqueId,
trusteeUniqueId,
trusteeIdentificationKeys,
trusteeWrapperAdapter,
authorizationExpirationTimestamp,
}) {
const bulletinBoardClient = new Client(bulletinBoardClientParams);

this.election = new Election({
uniqueId: electionUniqueId,
bulletinBoardClient,
return this.setupElectionWithTypesFilter({
electionUniqueId,
bulletinBoardClientParams,
authorizationExpirationTimestamp,
typesFilter: [
"create_election",
"start_key_ceremony",
"key_ceremony",
"end_key_ceremony",
],
});

this.trustee = new Trustee({
uniqueId: trusteeUniqueId,
bulletinBoardClient,
authorityPublicKeyJSON,
identificationKeys: trusteeIdentificationKeys,
election: this.election,
wrapperAdapter: trusteeWrapperAdapter,
});
}

/**
Expand Down Expand Up @@ -101,7 +85,7 @@ export class KeyCeremonyComponent {
onBackupNeeded();
onBindBackupButton(
backupData,
`${this.trustee.uniqueId}-election-${this.election.uniqueId}.bak`,
`${this.trustee.uniqueId}-election-${this.trustee.election.uniqueId}.bak`,
async () => {
onBackupStarted();
await keyCeremonySetup.next();
Expand Down
44 changes: 14 additions & 30 deletions bulletin_board/js-client/src/tally/tally.component.js
Original file line number Diff line number Diff line change
@@ -1,36 +1,29 @@
import { Client } from "../client/client";
import { Election } from "../election/election";
import { Trustee } from "../trustee/trustee";
// Components
import { TrusteeComponent } from "../trustee/trustee.component";

/**
* This class is used to bind any UI elements to a tally process.
*/
export class TallyComponent {
export class TallyComponent extends TrusteeComponent {
/**
* Initialises the class with the given params.
* Setup the election for the trustee.
*
* @param {Object} params - An object that contains the initialization params.
* - {Object} bulletinBoardClientParams - An object to configure the bulletin board client.
* - {String} authorityPublicKeyJSON - The authority identification public key.
* - {String} electionUniqueId - The unique identifier of an election.
* - {String} trusteeUniqueId - The unique identifier of a trustee.
* - {Object} trusteeIdentificationKeys - An object that contains both the public and private key for
* the corresponding trustee.
* - {Object} trusteeWrapperAdapter - An object to interact with the trustee wrapper.
* @constructor
* - {Number} authorizationExpirationTimestamp - The timestamp until the authorization header is no longer valid.
*
* @returns {Promise<undefined>}
*/
constructor({
setupElection({
bulletinBoardClientParams,
authorityPublicKeyJSON,
electionUniqueId,
trusteeUniqueId,
trusteeIdentificationKeys,
trusteeWrapperAdapter,
authorizationExpirationTimestamp,
}) {
const bulletinBoardClient = new Client(bulletinBoardClientParams);

this.election = new Election({
uniqueId: electionUniqueId,
bulletinBoardClient,
return this.setupElectionWithTypesFilter({
electionUniqueId,
bulletinBoardClientParams,
authorizationExpirationTimestamp,
typesFilter: [
"create_election",
"start_key_ceremony",
Expand All @@ -41,15 +34,6 @@ export class TallyComponent {
"end_tally",
],
});

this.trustee = new Trustee({
uniqueId: trusteeUniqueId,
bulletinBoardClient,
authorityPublicKeyJSON,
identificationKeys: trusteeIdentificationKeys,
election: this.election,
wrapperAdapter: trusteeWrapperAdapter,
});
}

/**
Expand Down
94 changes: 94 additions & 0 deletions bulletin_board/js-client/src/trustee/trustee.component.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,94 @@
import { Trustee } from "./trustee";
import { Client } from "../client/client";
import { Election } from "../election/election";

/**
* This class is used to bind any UI elements to a trustee process.
* @abstract
*/
export class TrusteeComponent {
/**
* Initialises the class with the given params.
* @param {Object} params - An object that contains the initialization params.
* - {String} authorityPublicKeyJSON - The authority identification public key.
* - {String} trusteeUniqueId - The unique identifier of a trustee.
* - {Object} trusteeIdentificationKeys - An object that contains both the public and private key for
* the corresponding trustee.
* - {Object} trusteeWrapperAdapter - An object to interact with the trustee wrapper.
* @constructor
*/
constructor({
authorityPublicKeyJSON,
trusteeUniqueId,
trusteeIdentificationKeys,
trusteeWrapperAdapter,
}) {
this.trustee = new Trustee({
uniqueId: trusteeUniqueId,
authorityPublicKeyJSON,
identificationKeys: trusteeIdentificationKeys,
wrapperAdapter: trusteeWrapperAdapter,
});
}

/**
* Setup the election for the trustee.
*
* @abstract
* @returns {Promise<undefined>}
*/
async setupElection() {
throw new Error("not implemented");
}

/**
* Setup the election for the trustee.
*
* @param {Object} params - An object that contains the initialization params.
* - {Object} bulletinBoardClientParams - An object to configure the bulletin board client.
* - {String} electionUniqueId - The unique identifier of an election.
* - {Number} authorizationExpirationTimestamp - The timestamp until the authorization header is no longer valid.
* - {Array<String>} typesFilter - A collection of message ids to be included in the log entry query.
*
* @returns {Promise<undefined>}
*/
async setupElectionWithTypesFilter({
bulletinBoardClientParams,
electionUniqueId,
authorizationExpirationTimestamp,
typesFilter,
}) {
const [authorityId] = electionUniqueId.split(".");
const trusteeUniqueIdHeader = `${authorityId}.${this.trustee.uniqueId}`;
const authorizationHeader = await this.trustee.signMessage({
trustee_unique_id: trusteeUniqueIdHeader,
exp: authorizationExpirationTimestamp,
});

const bulletinBoardClient = new Client({
...bulletinBoardClientParams,
headers: {
Authorization: authorizationHeader,
TrusteeUniqueId: trusteeUniqueIdHeader,
},
});

const election = new Election({
uniqueId: electionUniqueId,
bulletinBoardClient,
typesFilter,
});

this.trustee.election = election;
}

/**
* Bind UI events to the key ceremony process.
*
* @abstract
* @returns {Promise<undefined>}
*/
async bindEvents() {
throw new Error("not implemented");
}
}
15 changes: 6 additions & 9 deletions bulletin_board/js-client/src/trustee/trustee.js
Original file line number Diff line number Diff line change
Expand Up @@ -16,28 +16,22 @@ export class Trustee {
* @constructor
* @param {Object} params - An object that contains the initialization params.
* - {String} uniqueId - The trustee identifier.
* - {Client} bulletinBoardClient - An instance of the Bulletin Board Client
* - {String} authorityPublicKeyJSON - The authority identification public key.
* - {Object} identificationKeys - A object that contains both the public and private key for
* the corresponding trustee.
* - {Object} election - An object that interacts with a specific election
* to get some data and perform the key ceremony.
* - {Object} wrapperAdapter - An object to interact with the trustee wrapper.
* - {Object?} options - An optional object with some extra options.
*/
constructor({
uniqueId,
bulletinBoardClient,
authorityPublicKeyJSON,
identificationKeys,
election,
wrapperAdapter,
options,
}) {
this.uniqueId = uniqueId;
this.bulletinBoardClient = bulletinBoardClient;
this.identificationKeys = identificationKeys;
this.election = election;
this.election = null;
this.options = options || { waitUntilNextCheck: WAIT_TIME_MS };
this.wrapperAdapter = wrapperAdapter;
this.parser = new MessageParser({ authorityPublicKeyJSON });
Expand All @@ -55,6 +49,9 @@ export class Trustee {
* @returns {Promise<undefined>}
*/
async setup() {
if (this.election === null) {
throw new Error("election is not set.");
}
await this.wrapperAdapter.setup();
return this.election.subscribeToLogEntriesChanges();
}
Expand Down Expand Up @@ -242,7 +239,7 @@ export class Trustee {
async processKeyCeremonyStep(message) {
if (message && !this.isMessageAlreadyLogged(message)) {
const signedData = await this.signMessage(message);
return this.bulletinBoardClient.processKeyCeremonyStep({
return this.election.bulletinBoardClient.processKeyCeremonyStep({
messageId: message.message_id,
signedData,
});
Expand All @@ -259,7 +256,7 @@ export class Trustee {
async processTallyStep(message) {
if (message && !this.isMessageAlreadyLogged(message)) {
const signedData = await this.signMessage(message);
return this.bulletinBoardClient.processTallyStep({
return this.election.bulletinBoardClient.processTallyStep({
messageId: message.message_id,
signedData,
});
Expand Down
Loading

0 comments on commit 6fdb321

Please sign in to comment.