belenios-1.17/doc/ 0000755 0006501 0606723 00000000000 14115601117 012643 5 ustar glondu sed belenios-1.17/doc/.gitignore 0000644 0006501 0606723 00000000036 14115601117 014632 0 ustar glondu sed *.aux
*.log
*.out
*.toc
*.pdf
belenios-1.17/doc/Continuous_Integration.md 0000644 0006501 0606723 00000045014 14115601117 017702 0 ustar glondu sed # Documentation of the exploratory work done to enable Continuous Integration on Belenios
## How to activate Continuous Integration on the Belenios project, using Gitlab-CI
There are two possibilities:
* Keep the current main git repository on Github, and create on Gitlab a repository that would be a mirror of this repository, dedicated to Continuous Integration
* Migrate the current main git repository to Gitlab, and activate Continuous Integration on it
The two following headings detail these two procedures.
Before or after this, we have to make a `.gitlab-ci.yml` file appear in the main git repository, by accepting [the dedicated pull-request](https://github.com/glondu/belenios/pull/2) ("merge request" in the Gitlab jargon), or by creating this file directly from the Gitlab user interface.
### How to create a mirror repository on Gitlab
* Go to https://gitlab.com and log in
* On the home page ("Projects - Dashboard"), click on the "New project" button (https://gitlab.com/projects/new)
* Click on the "CI/CD for external repo" tab, then on "Repo by URL"
* In the "Git repository URL" field, write `https://gforge.inria.fr/anonscm/git/belenios/belenios.git`
* In the "Project name" field, write something like "belenios-ci"
* Click on button "Create project"
Gitlab will then regularly obtain new commits from the main repository to the mirror repository (and start a Runner that will execute continuous integration jobs if there are some).
#### How to force a refresh of the repository
If, at any moment, recent commits are not yet reflected, it is possible to ask Gitlab to immediately get new changes:
* On the project page, go to "Settings"
* then "Repositories"
* then in section "Mirorring repositories", click on button "Expand"
* in the "Mirrored repositories", click on the refresh icon.
### How to migrate the current git repository of Belenios to a Gitlab instance
* Go to the URL of your gitlab instance (or https://gitlab.com/ for the official one), and log in
* On the home page ("Projects - Dashboard"), click on the "New project" button (https://gitlab.com/projects/new)
* Follow instructions about migration of a repository
## How to execute the Gitlab-CI runner on your local machine
Install docker locally if it's not done yet.
Install `gitlab-runner` by following instructions of this page: https://docs.gitlab.com/runner/install/
Then, go to your Belenios repository folder, and run `gitlab-runner exec docker {job_name}` (where `{job_name}` is the name of the Continuous Integration job, that is the name of the key in the file `.gitlab-ci.yml` for the job that you want to run, e.g. `build`).
## How to use Docker locally to design the sequence of commands that we will write in the .gitlab-ci.yml file
Exemple:
```
$ docker run -ti ocaml/opam2:debian-9
opam@89b04864d029:~/opam-repository$ opam --version
2.0.0
opam@89b04864d029:~/opam-repository$ ocaml --version
The OCaml toplevel, version 4.06.1
opam@89b04864d029:~/opam-repository$ pwd
/home/opam/opam-repository
opam@89b04864d029:~/opam-repository$ ls
CHANGES.md CONTRIBUTING.md COPYING README.md compilers packages repo version
opam@89b04864d029:~/opam-repository$ cd
opam@89b04864d029:~$ ls
opam-repository
opam@89b04864d029:~$ git clone https://gitlab.com/swergas/belenios-ci.git
opam@89b04864d029:~$ cd belenios-ci
opam@89b04864d029:~$ sudo apt install build-essential libgmp-dev libpcre3-dev pkg-config m4 libssl-dev libsqlite3-dev wget ca-certificates unzip libncurses-dev zlib1g-dev -y
opam@89b04864d029:~$ eval `grep "opam install" ./opam-bootstrap.sh`
opam@89b04864d029:~$ make all
```
## Exploration of different build strategies to optimize total execution time of Belenios' Continuous Integration pipeline
We have tried several installation methods of Belenios, to iteratively reduce total build time. The next sections explain these different installation methods, from the most classical to the most optimized.
### First strategy: Classical installation of Belenios from the official Docker image of Debian 9
To install Belenios in the classical way, that is by following as close as possible the installation procedure of the file `INSTALL.md` of the Belenios repository, we start from a Docker image of Debian 9 (the official image `debian:9` of Docker Hub).
As soon as the Gitlab-CI runner has started the container of this image, it downloads automatically the content of the Belenios repository, positionned at the commit being currently verified.
On this container, we are going to install Debian packages that are necessary to the installation of Belenios.
Then we are going to execute the `opam-bootstrap.sh` script, that installs OCaml, Opam, and the Opam packages that Belenios needs (and using version numbers that satisfy compatibility criteria of Belenios).
Lastly, we compile Belenios (via the `make all` command), and we run some early tests (`make check`, that executes a test election and verifies its coherence, as well as running the Belenios web server and executing a test checking that a given text content is present in the web page that is displayed).
The content of the `.gitlab-ci.yml` is then the following:
```
stages:
- build
build:
stage: build
image: debian:9
script:
# Install required packages
- apt-get update -qq && apt-get install -y -qq build-essential libgmp-dev libpcre3-dev pkg-config m4 libssl-dev libsqlite3-dev wget ca-certificates unzip libncurses-dev zlib1g-dev git
# Install Opam via opam-bootstrap.sh, as recommended in INSTALL.md
- ./opam-bootstrap.sh
# Post-Opam installation procedure
- source ./env.sh
- eval `opam config env`
# Compile belenios
- make all
# Run a test of an election
- make check
# Create a bundled version of belenios (this produces a belenios.tar.gz file, which is needed by the web server)
- make archive
# Start belenios web server
- ./demo/run-server.sh &
# Access the localhost web page, print page output for debug purposes, and check validity of page output
- first_access_index_page_output=$(wget --retry-connrefused --no-check-certificate -T 30 http://localhost:8001 -O-)
- echo $first_access_index_page_output
- if [ "$(echo \"$first_access_index_page_output\" | grep '>Belenios' | wc -l)" != "1" ]; then echo "[First page access] First page access does not show a single '>Belenios' text, but it should" && exit 1; else echo "[First page access] First page access shows a single '>Belenios' text, as expected"; fi
```
The total duration of this Continuous Integration script is 24 min 07 sec on its first execution, and 23 min 29 sec on its second execution. All details are on [this page](https://gitlab.com/swergas/swergas-belenios-ci/pipelines).
### Second strategy: Installation of Belenios from a Docker image that already has OCaml and Opam installed
The classical Continuous Integration script seen in previous section downloads OCaml and Opam from their sources, compiles them and installs them. This step lasts several minutes (around 6 minutes, mainly during the construction of the "world"). It is possible to use a Docker image that has already pre-installed these tools on a linux distribution. The downloaded Docker image will then be a bit heavier than a classical linux distribution, but we save computation time.
OCaml developers maintain Docker Hub repositories `ocaml/opam` and `ocaml/opam2` (respectively for Opam 1.x.x and Opam 2.x.x), that contain images of several different linux distributions (including Debian 9), on which some versions of OCaml and Opam have been installed.
The classical installtion procedure of Belenios currently uses OCaml 4.02.3 and Opam 1.2.2. I have not been able to make work the installation of the Opam packages with a Docker image that has this version of Opam installed, because of a dependencies resolution error. But, with an image that has Opam 2.0.0 installed, there was no problem, so I have chosen this version (image `ocaml/opam2:debian-9`).
Regarding script `.gitlab-ci.yml`, the difference with the classical installation procedure (that uses a Debian 9 image), resides in the fact that instead of executing the whole `opam-bootstrap.sh` script, we execute only the part of it that installs Opam packages: ```eval `grep "opam install" ./opam-bootstrap.sh` ```
The content of the `.gitlab-ci.yml` is then the following:
```
stages:
- build
build:
stage: build
# Image ocaml/opam2:debian-9 currently has ocaml version 4.06.1 and opam version 2.0.0
image: ocaml/opam2:debian-9
script:
# Install required packages
- sudo apt-get update -qq && sudo apt-get install -y -qq build-essential libgmp-dev libpcre3-dev pkg-config m4 libssl-dev libsqlite3-dev wget ca-certificates unzip libncurses-dev zlib1g-dev
# Install the same Opam packages that opam-bootstrap.sh installs
- eval `grep "opam install" ./opam-bootstrap.sh`
# Compile belenios
- make all
# Create a bundled version of belenios (this produces a belenios.tar.gz file, which is needed by the web server)
- make archive
# Start belenios web server
- ./demo/run-server.sh &
# Access the localhost web page, print page output for debug purposes, and check validity of page output
- first_access_index_page_output=$(wget --retry-connrefused --no-check-certificate -T 30 http://localhost:8001 -O-)
- echo $first_access_index_page_output
- if [ "$(echo \"$first_access_index_page_output\" | grep '>Belenios' | wc -l)" != "1" ]; then echo "[First page access] First page access does not show a single '>Belenios' text, but it should" && exit 1; else echo "[First page access] First page access shows a single '>Belenios' text, as expected"; fi
# Run a test of an election
- make check
```
The Gitalb-CI runner takes around 2 minutes to get the `ocaml/opam2:debian-9` image on the first time (and takes 1 min 50 sec on the second time, so there is probably no caching of Docker images by the Gitlab-CI runner from one execution to the other).
Then, as previously, it clones the git repository.
At 2 min 42 sec, it begins installing apt-get packages.
At 3 min 34 sec, all Opam packages have been downloaded, and some of them are already installed.
At 15 min 47 sec, all Opam packages are installed (their installation order varies from one execution to the other, but we find globally the same ones at the end of the list). During the second exeuction, this step is finised at 15 min 52 sec. We remark that packages `ocsigenserver` and `eliom` are the last 2 ones to install and are the ones that thake the longest time to install. The next step is called `make all`.
At 16 min 28 sec, the runner starts executing the `make archive` step.
Total duration of the first launch (without the `make check` step): 16 min 57 sec.
Total duration of the second launch (with the `make check` step): 17 min 31 sec.
Conclusion: Moving from the `debian:9` image to the`ocaml/opam2:debian-9` image, we moved from a total execution time of 23 min 30 sec to 17 min 31 sec, that is a diminution of 6 minutes (diminution of 25.53 %).
### Third strategy: Using a custom Docker image, that has also pre-installed the apt-get packages and the Opam packages
A possibility to reduce again the total execution time of the Continuous Integration script, consists in creating a Docker image based upon `ocaml/opam2:debian-9`, that has already executed the steps of installing the `apt-get` packages, as well as the Opam packages, which would make a difference of approximately 13 min 05 sec (estimated by this calculation: 15 min 47 sec - 2 min 42 sec).
Note that this solution has a non negligeable drawback, that is that we have to re-create a new Docker image and reference it in the `.gitlab-ci.yml` file, every time we change the apt depencencies and/or the Opam dependencies in the code (adding dependencies, removing dependencies, changing version numbers of some dependencies, changing version numbers of OCaml or Opam).
So I have created the [swergas/beleniosbase](https://hub.docker.com/r/swergas/beleniosbase/) repository on Docker Hub, where I have placed such an image, using as image tag the checksum of the file `opam-bootstrap.sh`. This way, when the code of Belenios changes the version numbers of Ocaml or Opam that it requires, or that the list of its required Opam packages changes, we will be able to make correspond to it a unique Docker image name.
Here is the procedure to create such a Docker image and publish it on Docker Hub:
```
$ sha256sum ./opam-bootstrap.sh
efa5df3049f736dd34eb8289da730dd709eb99939f6511fa93ae0080a61ce4fb ./opam-bootstrap.sh
$ docker container run -ti ocaml/opam2:debian-9 /bin/bash
$ sudo apt-get update -qq && sudo apt-get install -y -qq build-essential libgmp-dev libpcre3-dev pkg-config m4 libssl-dev libsqlite3-dev wget ca-certificates unzip libncurses-dev zlib1g-dev
$ opam install --yes atdgen zarith cryptokit calendar cmdliner sqlite3 eliom=6.3.0 csv
$ exit
$ docker container ls -a
$ docker container commit
# Exemple : docker container commit fe173ea3829c
$ docker image ls
$ docker image tag
# Exemple : docker image tag 04bf023e658e beleniosbasewithopamdependencies2
$ docker commit /:
# Exemple : docker commit fe173ea3829c swergas/beleniosbase:efa5df3049f736dd34eb8289da730dd709eb99939f6511fa93ae0080a61ce4fb
# Le résultat de la commande est de la forme 'sha256:c3b4dd5f41f071409d6f45f069f5bef7b4eb236d2bfbb457c0be89ae1f8a4139'
$ docker push /:
# Exemple : docker push swergas/beleniosbase:efa5df3049f736dd34eb8289da730dd709eb99939f6511fa93ae0080a61ce4fb
# Lorsque ce n'est pas la première fois qu'on envoie l'image, le résultat de cette commande est :
# The push refers to repository [docker.io/swergas/beleniosbase]
# e496191c6f91: Pushed
# 932b4034c234: Layer already exists
# aa956106affb: Layer already exists
# 7fc2f0c53c72: Layer already exists
# 555b3e37ead3: Layer already exists
# b9190cafe4f2: Layer already exists
# 0e6751af6de3: Layer already exists
# d04afccd7138: Layer already exists
# b28ef0b6fef8: Layer already exists
# efa5df3049f736dd34eb8289da730dd709eb99939f6511fa93ae0080a61ce4fb: digest: sha256:f508797f44a37314b96120f46537fcf426995490f6f4f318db2e2662b45cb860 size: 2221
```
We can then reference this Docker image name in our `.gitlab-ci.yml` file and comment or remove the installation steps that are not necessary anymore:
```
stages:
- build
build:
stage: build
# Image ocaml/opam2:debian-9 currently has ocaml version 4.06.1 and opam version 2.0.0
# image: ocaml/opam2:debian-9
# Image swergas/beleniosbase:efa5df3049f736dd34eb8289da730dd709eb99939f6511fa93ae0080a61ce4fb is based on ocaml/opam2:debian-9 with all pre-installed apt packages and opam packages that are required by Belenios
image: swergas/beleniosbase:efa5df3049f736dd34eb8289da730dd709eb99939f6511fa93ae0080a61ce4fb
script:
# Install required packages
# - sudo apt-get update -qq && sudo apt-get install -y -qq build-essential libgmp-dev libpcre3-dev pkg-config m4 libssl-dev libsqlite3-dev wget ca-certificates unzip libncurses-dev zlib1g-dev
# Install the same Opam packages that opam-bootstrap.sh installs
# - eval `grep "opam install" ./opam-bootstrap.sh`
# Compile belenios
- make all
# Create a bundled version of belenios (this produces a belenios.tar.gz file, which is needed by the web server)
- make archive
# Start belenios web server
- ./demo/run-server.sh &
# Access the localhost web page, print page output for debug purposes, and check validity of page output
- first_access_index_page_output=$(wget --retry-connrefused --no-check-certificate -T 30 http://localhost:8001 -O-)
- echo $first_access_index_page_output
- if [ "$(echo \"$first_access_index_page_output\" | grep '>Belenios' | wc -l)" != "1" ]; then echo "[First page access] First page access does not show a single '>Belenios' text, but it should" && exit 1; else echo "[First page access] First page access shows a single '>Belenios' text, as expected"; fi
# Run a test of an election
- make check
```
At 2 min 50 sec, the runner has finished downloading the Docker image and starts cloning the repository.
At 3 min 53 sec, it has begun running the `make all` command.
At 4 min 24 sec, it has finished running the `make all` command and has begun running the `make check` command.
Total execution time: 11 min 25 sec.
Moving from image `ocaml/opam2:debian-9` to `swergas/beleniosbase:efa5df3049f736dd34eb8289da730dd709eb99939f6511fa93ae0080a61ce4fb` changed total execution time from 17 min 31 sec to 11 min 25 sec, that is a diminution of 6 min 06 sec (diminution of 34.82 %).
### Using urandom instead of random
When trying to run the CI jobs, [we noticed that sometimes jobs were finishing in a timeout error](https://github.com/glondu/belenios/pull/2#issuecomment-422412068). By default, `belenios-tool` uses secure random (`/dev/random`), which may exhaust the entropy pool when it is run many times (which is the case with `make check`). The `BELENIOS_DEBUG` environment variable at build time triggers a different code path that uses `/dev/urandom` instead.
So we decided to use urandom instead of random in our Continuous Integration scripts. This makes CI jobs more reliable and reduces total execution time even more (13 minutes 13 seconds on a `ocaml/opam2:debian-9-ocaml-4.06` image, and 5 minutes 33 seconds with the `swergas/beleniosbase:efa5df3049f736dd34eb8289da730dd709eb99939f6511fa93ae0080a61ce4fb` image).
### Using a fixed version of Ocaml
We [noticed](https://github.com/glondu/belenios/pull/2#issuecomment-423271172) that Docker image `ocaml/opam2:debian-9` regularly gets re-built using a different version of OCaml (it is a kind of `latest` tag for the debian distribution). Some of these builds contain a version of OCaml that breaks the build script. So, in order to use an image that always has a version of OCaml that we know works, we stabilize the version number used, by using an image name that mentions precisely the OCaml version used, for example `ocaml/opam2:debian-9-ocaml-4.06` instead of just `ocaml/opam2:debian-9`.
### Recap of total execution durations
| Docker image | Using fast random | Total duration |
| ------------------------------- |:-----------------:|:---------------:|
| debian:9 | no | 23 min 29 sec |
| ocaml/opam2:debian-9-ocaml-4.06 | no | 17 min 31 sec |
| swergas/-checksum- | no | 11 min 25 sec |
| ocaml/opam2:debian-9-ocaml-4.06 | yes | 13 min 13 sec |
| swergas/-checksum- | yes | 05 min 33 sec |
belenios-1.17/doc/automated_tests.md 0000644 0006501 0606723 00000045070 14115601117 016400 0 ustar glondu sed # Documentation related to automated tests of Belenios
Automated tests are stored in the `tests` directory.
Technologies used to run these tests are:
- `python3`: Python 3 (version >= 3.6). We use it in a virtual environment (using Python's `venv` module)
- `pip`: Python's package manager. We use it to install Python packages such as `selenium` (`pip` installs packages mentioned in `requirements.txt`)
- `selenium`: Selenium's Python API ([documentation](https://selenium-python.readthedocs.io/))
- `firefox`: The browser we use to run tests with Selenium. We can use standard Firefox, or `firefox-esr`, depending what is available on the system and which Firefox version is compatible with Selenium at the moment
- `geckodriver`: A Firefox driver for Selenium. Its role is to translate commands received from Selenium using a specific protocol into concrete actions to trigger in the Firefox browser, and to trigger these actions.
- `unittest`: Python's standard test framework
These automated tests start the Belenios demo server (`demo/run-server.sh`), with the `BELENIOS_SENDMAIL` environment variable defined as the path to a fake `sendmail` executable (similar to a mock, provided in `tests/selenium/tools/sendmail_fake.sh`). This way, Belenios server does not return an error when trying to send emails in the test environment (that has no `sendmail` installed nor configured), and the fake `sendmail` executable makes it possible to verify what emails have been sent and read their content, simply by reading the log file where it redirects all its input (we use `/tmp/sendmail_fake` as location for this log file).
Note: For example, during election creation procedure, a step sends emails to voters. If at this moment, a `sendmail` binary is not properly installed and configured (or replaced by a mock), the web page displays the following error message: `Netchannels.Command_failure(WEXITED 126)`
When these automated tests start running, and when they end, they clean up Belenios database: Belenios database consists in directories and files under the `_run/spool` directory, for each election. So these are deleted during test setup. Belenios demo server stores initial admin users logins and passwords in `demo/password_db.csv`. This file is not deleted during test setup, and its contents are used to log in the adminstrator during the test and have this administrator create an election.
Automated tests can be executed manually, or via Continuous Integration. Next sub-sections explain how to execute them in each of these 2 contexts.
## Executing automated test suites manually on your local machine
You can execute a test suite by running its python script from your Python virtual environment, given you have already installed its `pip` requirements, `firefox` and `geckodriver`. Automated tests need that Belenios server has already been compiled. So if you have not done any of these, this is the sequence of commands that you can execute:
```
export GECKODRIVER_VERSION=0.18.0
wget --no-verbose -O /tmp/geckodriver.tar.gz https://github.com/mozilla/geckodriver/releases/download/v$GECKODRIVER_VERSION/geckodriver-v$GECKODRIVER_VERSION-linux64.tar.gz && rm -rf /opt/geckodriver && sudo tar -C /opt -zxf /tmp/geckodriver.tar.gz && rm /tmp/geckodriver.tar.gz && sudo mv /opt/geckodriver /opt/geckodriver-$GECKODRIVER_VERSION && sudo chmod 755 /opt/geckodriver-$GECKODRIVER_VERSION && sudo ln -fs /opt/geckodriver-$GECKODRIVER_VERSION /usr/bin/geckodriver
sudo apt-get install -y -qq python3 python3-venv firefox
source ./env.sh
make build-debug-server
make build-debug-tool
python3 -m venv venv
source venv/bin/activate
pip install --upgrade pip
pip install -r requirements.txt
python ./tests/selenium/test_scenario_1.py
```
Note: Depending on the version of `firefox` that you use, you may need to adjust the version of `geckodriver` to download and install. Please refer to this compatibility table: https://firefox-source-docs.mozilla.org/testing/geckodriver/geckodriver/Support.html
Note: The `make all` command exits in error if `ocamlbuild` finds compiled files (including `.so` shared libraries) other than the ones it expects. So, make sure that you run this command before creation of the Python virtual environment and installation of `pip` requirements. If you want to re-compile Belenios after this, you can simply delete the `venv` folder.
Note: We cannot run the Belenios server from a folder that is not the git repository root, otherwise it exits immediately in an error: `This script should be run from the root of the (built) source tree!`.
## Executing automated tests using Continuous Integration
For this, we run a Docker image (built from `Dockerfile_test_scenario_environment`) that preinstalls compatible versions of `firefox-esr`, `geckodriver`, `python`, and python virtual environment.
File `.gitlab-ci.yml` defines a task `build_and_run_automated_test_scenario_1_with_preinstalled_image` that uses this docker image, compiles belenios, creates a python virtual environment, steps into it, installs pip required packages, and then executes the automated test suite.
Note: You can execute this Continuous Integration task as if you were running GitlabCI, by executing the following command:
```
gitlab-runner exec docker build_and_run_automated_test_scenario_1_with_preinstalled_image
```
Note: The Docker image has been built and pushed to Docker Hub using the following commands:
```
docker build -t glondu/beleniosbase:20181206-1 -f Dockerfile_base_environment .
docker build -t glondu/beleniosbase-tests:20181206-1 -f Dockerfile_test_scenario_environment .
sudo docker push glondu/beleniosbase-tests:20181206-1
```
We use `YYYYMMDD-N` for docker-tagging, where `YYYYMMDD` is the build date, and `N` is a sequence number.
## Customizing configuration variables of Test Scenario 1
Test `test_scenario_1.py` executes using some default configuration that can be customized, by setting some environment variables when executing the script.
The list of configuration variables is:
- `RANDOM_SEED`: An integer used as seed for the random number generator of Python. By default, a random seed is used. Seed used is displayed at every run of the test, so you can re-run the test using the same random seed by setting this configuration variable to the value displayed.
- `USE_HEADLESS_BROWSER`: Set this to non-zero (True) if you run this test in Continuous Integration (it has no graphical display). Set this to 0 (False) if you want to see the browser open its graphical user interface and visually track progress ot the test. By default, the falue is True.
- `WAIT_TIME_BETWEEN_EACH_STEP`: In seconds (float). Time we wait between each action that we tell Selenium driver to do in the browser. Set to 0 if you don't need to have the time to visually follow progress of actions in the browser
- `EXPLICIT_WAIT_TIMEOUT`: In seconds. Maximum duration Selenium driver will wait for appearance of a specific DOM element expected in the page (for example when transitioning from a page to another). This referes to Selenium's "Explicit Wait" concept
- `SENT_EMAILS_TEXT_FILE_ABSOLUTE_PATH`
- `NUMBER_OF_INVITED_VOTERS`: This is N in description of Scenario 1. N is between 6 (quick test) and 1000 (load testing)
- `NUMBER_OF_VOTING_VOTERS`: This is K in description of Scenario 1. K is between 6 (quick test) and 1000 (load testing). K <= N. (Some invited voters don't vote, this is abstention, and its value is N - K)
- `NUMBER_OF_REVOTING_VOTERS`: This is L in description of Scenario 1. L <= K
- `NUMBER_OF_REGENERATED_PASSWORD_VOTERS`: This is M in description of Scenario 1. M <= K
- `SENT_EMAILS_TEXT_FILE_ABSOLUTE_PATH`: By default, this is "/tmp/sendmail_fake"
- `ADMINISTRATOR_USERNAME`: By default, this value comes from file `demo/password_db.csv`, first row, first column
- `ADMINISTRATOR_PASSWORD`: By default, this value comes from file `demo/password_db.csv`, first row, 4th column
- `BOOTH_VERSION`: The version of the web booth to be used by automatic voters. Default is `CLASSIC_BOOTH`, which is the original booth and is the most widely translated, but handles only the classic type of questions, which is checkboxes or radio buttons. Another accepted value is `RESPONSIVE_BOOTH`, which is translated in fewer languages but handles questions of type classic and alternative (more precisely Majority Judgement and Preference Voting)
Other automated tests (files `test_scenario_2.py`, `test_scenario_3.py`, etc) also have configuration variables, and some of them are in common. Here is an example of how you can set configuration variables and execute the test in your terminal:
```
RANDOM_SEED=222 WAIT_TIME_BETWEEN_EACH_STEP=1.2 USE_HEADLESS_BROWSER=0 NUMBER_OF_INVITED_VOTERS=4 python3 ./tests/selenium/test_scenario_1.py
```
## Prepared database
You can construct a prepared database (contents of folder `_run/spool`) with several elections and invited voters, and optionnaly with some voters who have already submitted their ballot. Building such a database, loading it into Belenios and executing automated tests on it make it possible to evaluate any difference in behaviour or in duration between an "empty" Belenios server and a "crowded" one.
File `.gitlab-ci.yml` defines a task `build_and_run_automated_test_scenarios_with_preinstalled_image_and_prepared_database` that downloads a prepared database and places it into `_run/spool` before executing tests. This task is only executed once in a while (at every new release of Belenios), not at every commit.
Here is how you can build your own prepared database (for example if you want to add new elections to the prepared database used in this task):
- Execute the Python/Selenium script which creates an election, invites some voters, generates their ballots, and stores them into a CSV file: See section `Execute the script that creates the election and generates ballot files and the aggregated votes CSV file`
- Execute the JMeter script which, for each row of the previously generated CSV file, submits a voter's ballot to the election: See section `Load testing: Executing scenario of vote with already prepared ballots, using JMeter`
You can now execute automated tests locally and analyse their results. If you want to update or replace the prepared database used in the Continuous Integration / Continuous Delivery pipeline, download its compressed file, unzip its contents and merge them into your local `_run/spool` folder. Then compress the whole folder and upload its compressed version to a public URL and replace the URL in `.gitlab-ci.yml` by your own.
Good to know: If you version the prepared database using a git repository like gitlab or github, the VCS platform automatically generates a compressed file associated to latest commit on master branch and to commits which have tags. So you don't need to compress and upload the file yourself, just copy its URL and paste it into `.gitlab-ci.yml`.
## Monkey testing and fuzz testing
### Notes related to monkey testing and fuzz testing
Several tests have a same general behaviour in common, which depends on the value you set in some environment variables when you execute the test. Here are notes about some of them:
- `WAIT_TIME_BETWEEN_EACH_STEP`: Do not set a value below 0.02 seconds, otherwise hypothesis test becomes flaky.
- `START_SERVER`: Can be set to `0` or `1` to respectively use an already running Belenios server or let the script start its own Belenios server.
- `ELECTION_ID`: If you do not provide a value to this variable, the test will try to create an election, invite voters, and fill other variables from it with correct values extracted from data obtained during the creation of the election (`VOTER_USERNAME`, `VOTER_PASSWORD`, `VOTER_CREDENTIAL`). If you do provide a value to `ELECTION_ID`, you should also provide a value to these other variables.
- `USE_HEADLESS_BROWSER`: Can be set to `0` or `1` to respectively display the Graphical User Interface of the browser while executing the test (this is a useful visual feedback for debugging), or to execute its headless mode which opens no window. If you set it to `0`, make sure that you don't change focus during execution (with your mouse or while switching windows on your computer), as it interferes with the automatic instructions that the browser receives from Selenium (for example, detection of Alerts becomes flaky).
### Fuzz testing of the main login form
You can execute test `test_fuzz_login.py` the following way:
```
START_SERVER=1 LOGIN_MODE="local" USE_HEADLESS_BROWSER=0 WAIT_TIME_BETWEEN_EACH_STEP=0.02 python ./tests/selenium/test_fuzz_login.py
```
### Fuzz testing of the "Advanced mode" voting process
Fuzz testing of the "Advanced mode" voting process consists in:
- opening the URL of an election
- clicking on the "Advanced mode" link
- trying to vote with a dumb monkey who fuzz tests with random input as ballot, submits it and verifies that he receives a failure message saying that ballot content is invalid
- and then trying to vote with a smart monkey who fuzz tests with a properly structured but still incorrect ballot (this enables him to move to the login step)
- logging in using a correct combination of username and password
- verifying that user arrives on Step 5
- clicking on confirm ballot subscription button
- verifying that user arrives on Step 6, and that it displays a failure message (because ballot content is invalid)
This test is implemented in file `test_fuzz_vote.py`. It can be executed either on an already existing election, or it can create a new election itself and then execute the test. Here are examples of both ways of using it:
- Let the script create its own election
```
SENT_EMAILS_TEXT_FILE_ABSOLUTE_PATH=/path/to/your/repository/_build/src/static/mail.txt FAKE_SENT_EMAILS_FILE_RELATIVE_URL=static/mail.txt WAIT_TIME_BETWEEN_EACH_STEP=0.02 USE_HEADLESS_BROWSER=0 START_SERVER=0 python ./tests/selenium/test_fuzz_vote.py
```
- Use an already existing election. For this, you provide the election ID as well as a username and password for a voter who has been already invited.
```
WAIT_TIME_BETWEEN_EACH_STEP=0.02 USE_HEADLESS_BROWSER=0 START_SERVER=0 ELECTION_ID=4qjJRMg4b26ax5 VOTER_USERNAME=nrmt1fl7z05zaqnn0luo@mailinator.com VOTER_PASSWORD=LLP3269TVNDMF6 python ./tests/selenium/test_fuzz_vote.py
```
### Clicker Monkey testing
There are 2 tests in `test_clicker_monkey.py`:
- `test_clicker_monkey_on_election_home()`: Clicker Monkey goes to the home page of the election, and clicks randomly on links and buttons, up to 200 times. It uses a fence function to ignore pages which match certain criteria: these are pages which are out of the scope of analysis (pages outside of the Belenios server domain or sub-domain). On every other page, it verifies that the page is not an unexpected error page. It randomly clicks on the "Previous" button of the browser (which goes back on the previous page).
- `test_sometimes_smart_monkey_votes()`: It starts like previous test, but for only up to 50 clicks. Then, the visitor does a normal vote and verifies everything went correctly. This test verifies that a single visitor who does random actions at the beginning of his visit can still complete a vote without problem.
Here is an example of how to execute these tests:
```
START_SERVER=0 CLEAN_UP_POLICY=DO_NOTHING SENT_EMAILS_TEXT_FILE_ABSOLUTE_PATH=/path/to/_build/src/static/mail.txt WAIT_TIME_BETWEEN_EACH_STEP=0.02 USE_HEADLESS_BROWSER=0 python ./tests/selenium/test_clicker_monkey.py
```
### Smart Monkey testing
File `test_smart_monkey.py` executes a very smart monkey, who has memory and who knows perfectly the state machine of the voting process. Smart monkey receives at initialization some data to store in its memory (username, password, credential) and during the vote process, it will also store the smart ballot tracker which gets displayed and then check on the following pages that it does still appear and that its value is the same as the initial one. The smart monkey constantly knows which state of the state machine corresponds to the current page he is seeing, and what is the list of possible actions to do on this page. It decides randomly which action to do. Among these actions are clicking on a link or a button, as well as filling a form (or modal) with wrong data or with correct data. It can also decide to click on the "Previous" button of the browser, especially when it arrives on a dead end (a page with no available action from it, for example an "Unauthorized" page). After each action that the smart monkey does, it then verifies that the page he arrives on corresponds to the expected destination state of this transition in the state machine (and not to another page or to an error page). It stops running only once he has completed a vote. Then, the test verifies that the smart monkey's smart ballot tracker is displayed on the ballot box page.
This whole test verifies that a user who has a very random behaviour, but who really wants to vote correctly, can correctly vote.
You can execute it the following way:
```
ELECTION_ID=Vq7erXgTVs983H VOTER_USERNAME=3gzyo249nqgpjhx1puae@mailinator.com VOTER_PASSWORD=dtx96KfMEuHqxd VOTER_CREDENTIAL=cbsopJ6QxpeLAyh START_SERVER=0 CLEAN_UP_POLICY=DO_NOTHING SENT_EMAILS_TEXT_FILE_ABSOLUTE_PATH=/home/quentin/prog/gitlab.inria.fr/belenios-forks/belenios-swergas/_build/src/static/mail.txt WAIT_TIME_BETWEEN_EACH_STEP=0.02 USE_HEADLESS_BROWSER=0 python ./tests/selenium/test_smart_monkey.py
```
### Test Scenario 2 with a mix of normal voters and Smart Monkeys
File `test_scenario_2_with_monkeys.py` executes a variation on "Scenario 2: Vote with vote codes in manual mode and manual trustees" (which is implemented in `test_scenario_2.py`). This test creates an election with trustees, invites voters, and makes several voters vote. A first part of voters execute a normal vote. A second part of voters are Smart Monkeys, with a behaviour described in previous "Smart Monkey testing" section. A third part of voters execute also a normal vote. Some randomly selected voters revote. Then administrator starts tallying the election, trustees do the validation, and administrator completes the tallying of the election. Administrator verifies that global vote results corresponds to the expected ones. Several verifications of the consistency of the election occur during the whole process, exactly like in the initial Scenario 2 test.
This whole test verifies that despite having some smart monkey voters, other voters can vote correctly and the whole election continues to work as expected and to produce expected vote results.
You can execute it the following way:
```
USE_HEADLESS_BROWSER=0 NUMBER_OF_INVITED_VOTERS=30 NUMBER_OF_VOTING_VOTERS=15 NUMBER_OF_MONKEY_VOTING_VOTERS=7 NUMBER_OF_VOTING_VOTERS_IN_FIRST_PART=4 NUMBER_OF_REVOTING_VOTERS=1 python3 ./tests/selenium/test_scenario_2_with_monkeys.py
```
belenios-1.17/doc/election_test_scenario_1_specification.md 0000644 0006501 0606723 00000021741 14115601117 023036 0 ustar glondu sed Scenario 1: Simple vote, in "fully automated" mode
=================================
## Introduction and parameters
Protagonists to emulate: election administrator, `K` electors, an auditor.
Administrator uses only her browser.
Electors use their browser and read emails sent by the server.
`L` electors re-vote (with `L <= K`)
`M` electors ask administrator to re-generate their password, and vote with their re-generated password (with `M <= K`).
The auditor makes web requests, has a persistent state, and runs the commandline version of the Belenios tool.
Auditor makes web requests, has a persistent state, and runs the commandline version of the Belenios tool.
Authentication of administrator and electors are done using a login / password combination.
Examples of parameters sizes: `N` and `K` would be between 6 (quick test) and 1000 (load test)
## Note about verification
Verifications all along the process is done using command line tools `belenios-tool verify` and `belenios-tool verify-diff`:
- `belenios-tool verify` does a static verification (it verifies that vote data at current time is coherent)
- `belenios-tool verify-diff` does a dynamic verification (it verifies that current state of vote data is a possible/legitimate evolution of a vote data snapshot that has been saved during a previous step of the process)
## Detailed steps of the Test Scenario 1 process
- Starting setup of the election (action of the administrator)
- Creation of the draft election
- Alice has been given administrator rights on an online voting app called Belenios. She goes to check out its homepage and logs in.
- She clicks on the "Prepare a new election" link
- (She keeps default values on the form: Credential management is automatic (not manual), and Authentication method is Password, not CAS)
- She clicks on the "Proceed" button (this redirects to the "Preparation of election" page)
- She changes values of fields name and description of the election
- She clicks on the "Save changes button" (the one that is next to the election description field)
- Edition of election's questions
- She clicks on the "Edit questions" link, to write her own questions
- She arrives on the Questions page. She checks that the page title is correct
- She removes answer 3
- She clicks on the "Save changes" button (this redirects to the "Preparation of election" page)
- Setting election's voters
- She clicks on the "Edit voters" link, to then type the list of voters
- She types `N` e-mail addresses (the list of invited voters)
- She clicks on the "Add" button to submit changes
- She clicks on "Go back to election draft" link
- She clicks on button "Generate on server"
- (Server sends emails to voters.) She checks that server does not show any error that would happen when trying to send these emails (this can happen if sendmail is not configured)
- We do a sanity check that server has really tried to send emails. (For this, we look for email addresses in the temporary file where our fake sendmail executable redirects its inputs to.)
- She clicks on the "Proceed" link
- In "Authentication" section, she clicks on the "Generate and mail missing passwords" button
- She checks that the page contains expected confirmation text, instead of an error
- She clicks on the "Proceed" link
- Finalize creation of election
- In "Validate creation" section, she clicks on the "Create election" link
- (She arrives on the "Checklist" page, that lists all main parameters of the election for review, and that flags incoherent or misconfigured parameters. For example, in this test scenario, it displays 2 warnings: "Warning: No trustees were set. This means that the server will manage the election key by itself.", and "Warning: No contact was set!")
- In the "Validate creation" section, she clicks on the "Create election" button
- (She arrives back on the "My test election for Scenario 1 — Administration" page. Its contents have changed. There is now a text saying "The election is open. Voters can vote.", and there are now buttons "Close election", "Archive election", "Delete election")
- She remembers the URL of the voting page, that is where the "Election home" link points to
- She checks that a "Close election" button is present (but she does not click on it)
- Log out and close the browser window
- Regenerating electors' lost passwords (for M electors) (action of the administrator)
- Alice has been contacted by some voters who say they lost their password. She wants to re-generate their passwords and have the platform send them by email. For this, she logs in as administrator.
- She remembers the list of voters who contacted her and said they lost their password.
- She selects the election that she wants to edit
- She arrives to the election administration page. For each voter of the M selected voters:
- She clicks on the "Regenerate and mail a password" link
- She types the e-mail address of the voter in the "Username" field
- She clicks on the "Submit" button
- She checks that the page shows a confirmation message similar to "A new password has been mailed to name@email.com"
- She clicks on the "Proceed" link (She arrives back to the election administration page)
- We do a sanity check that server has really tried to send these emails, and to these users only.
- She logs out and closes the browser window
- Verify election consistency (using command line tool `belenios_tool verify`)
- All voting electors cast their vote (`K` electors vote). We check vote data consistency for every batch of `X` votes (using `belenios_tool verify-diff` and a snapshot of election data copied in previous batch). For each batch of `X` voters:
- Create election data snapshot
- Current batch of electors vote. For each voter of this batch:
- Bob checks that he has received 2 emails containing an invitation to vote and all necessary credentials (election page URL, username, password). He goes to the election page URL.
- He clicks on the "Start" button
- A loading screen appears, then another screen appears. He clicks on the "Here" button
- A modal opens (it is an HTML modal created using Window.prompt()), with an input field. He types his credential.
- He fills his votes to each answer of the question (for each displayed checkbox, he decides to mark it or leave it empty)
- He clicks on the "Next" button
- He remembers the smart ballot tracker that is displayed
- He clicks on the "Continue" button
- He types his voter username and password, and submits the form
- He checks that the smart ballot tracker value that appears on screen is the same as the one he noted
- He clicks on the "I cast my vote" button
- He clicks on the "ballot box" link
- He checks that his smart ballot tracker appears in the list
- He closes the window (there is no log-out link, because user is not logged in: credentials are not remembered)
- He checks his mailbox to find a new email with confirmation of his vote, and verifies the value of the smart ballot tracker written in this email is the same as the one he noted.
- Verify election consistency (using `belenios_tool verify-diff`)
- Delete election data snapshot
- Verify election consistency (using command line tool `belenios_tool verify`)
- Create election data snapshot
- All electors who want to change their vote re-vote (`L` electors re-vote)
- We re-apply the same procedure as listed in previous step, except we use the set of `L` re-voters instead of the set of `K` voters
- Verify election consistency (using `belenios_tool verify-diff` and the snapshot created right before re-votes)
- Delete election data snapshot
- Verify election consistency (using command line tool `belenios_tool verify`)
- Administrator does tallying of the election
- Alice goes to the election page
- She clicks on the "Administer this election" link
- She logs in as administrator
- She clicks on the "Close election" button
- She clicks on the "Proceed to vote counting" button
- She checks consistency of the election result
- She checks that the number of accepted ballots is the same as the number of voters who voted
- For each available answer in the question, she checks that the total number of votes in favor of Answer X displayed in result page is the same as the sum of votes for Answer X in all votes of voters who voted that have been randomly generated in advance
- She checks that each ballot content corresponds to content that of this vote that has been randomly generated in advance
- Verify election consistency (using command line tool `belenios_tool verify`)
- One voter tries to revote and faces an error message "Your ballot for Test vote after close is rejected, because the election is closed." after authentication
belenios-1.17/doc/election_test_scenario_2_specification.md 0000644 0006501 0606723 00000027251 14115601117 023041 0 ustar glondu sed Scenario 2: Vote with vote codes in manual mode and manual trustees
=================================
## Introduction and parameters
Protagonists to emulate: election administrator, credential authority, 2 trustees, `K` electors, an auditor.
Administrator and trustees uses only thier browser. Credential authority uses her browser and sends emails.
Electors use their browser and read emails sent by the server and by the credential authority.
`L` electors re-vote (with `L <= K`)
`M` electors ask administrator to re-generate their password, and vote with their re-generated password (with `M <= K`).
The auditor makes web requests, has a persistent state, and runs the commandline version of the Belenios tool.
Auditor makes web requests, has a persistent state, and runs the commandline version of the Belenios tool.
Authentication of administrator and electors are done using a login / password combination.
Examples of parameters sizes: `N` and `K` would be between 6 (quick test) and 1000 (load test)
## Note about verification
Verifications all along the process is done using command line tools `belenios-tool verify` and `belenios-tool verify-diff`:
- `belenios-tool verify` does a static verification (it verifies that vote data at current time is coherent)
- `belenios-tool verify-diff` does a dynamic verification (it verifies that current state of vote data is a possible/legitimate evolution of a vote data snapshot that has been saved during a previous step of the process)
## Detailed steps of the Test Scenario 2 process
- Starting setup of the election (action of the administrator)
- Creation of the draft election
- Alice has been given administrator rights on an online voting app called Belenios. She goes to check out its homepage and logs in.
- She clicks on the "Prepare a new election" link
- She picks the Credential management method: manual
- (She keeps default value for Authentication method: it is Password, not CAS)
- She clicks on the "Proceed" button (this redirects to the "Preparation of election" page)
- She changes values of fields name and description of the election
- She clicks on the "Save changes button" (the one that is next to the election description field)
- She remembers the URL of the draft election administration page
- Edition of election's questions
- She clicks on the "Edit questions" link, to write her own questions
- She arrives on the Questions page. She checks that the page title is correct
- She removes answer 3
- She clicks on the "Save changes" button (this redirects to the "Preparation of election" page)
- Setting election's voters
- She clicks on the "Edit voters" link, to then type the list of voters
- She types `N` e-mail addresses (the list of invited voters)
- She clicks on the "Add" button to submit changes
- She clicks on "Go back to election draft" link
- In "Authentication" section, she clicks on the "Generate and mail missing passwords" button
- She checks that the page contains expected confirmation text, instead of an error
- She clicks on the "Proceed" link
- In "Credentials" section, she clicks on "Credential management" link
- She remembers the link displayed
- She sends the remembered link to the credential authority by email
- She logs out and closes the browser
- Credential authority sends credentials to electors
- Cecily, the Credential Authority, receives the email sent by Alice, and opens the link in it
- She remembers what the link to the election will be, so that she will be able to send it to voters by email with their private credential
- She clicks on the "Generate" button
- She clicks on the "private credentials" and "public credentials" links and downloads these files. (Files are by default downloaded using filenames `creds.txt` and `public_creds.txt` respectively)
- She clicks on the "Submit public credentials" button
- She checks that redirected page shows correct confirmation sentence
- She closes the window
- She reads the private credentials file (`creds.txt`) and sends credential emails to voters
- Continuing setup of the election: Administrator invites trustees
- Administrator logs in and goes to the election draft page
- In the trustees section, she clicks on the "here" link
- She adds two trustees (their email address), and remembers the link she will send to each trustee
- She sends to each trustee an email containing their own link
- She logs out and closes the window
- Trustees generate election private keys. Each trustee (Tom and Taylor) will do the following process:
- Trustee opens link that has been sent to him by election administrator
- He checks that the page content shows the same election URL as the one the administrator saw
- He clicks on the "Generate a new keypair" button
- He clicks on the "private key" and "public key" links, to download the private key and the public key (files are respectively saved by default as `private_key.json` and `public_key.json`)
- He clicks on the "Submit public key" button
- He checks that the next page shows the expected confirmation sentence
- He closes the window
- Administrator completes setup of the election
- Alice, as an administrator of an election, wants to finalize her draft election creation, to start the vote. She opens a browser and logs in as administrator
- She goes to the draft election administration page
- In "Validate creation" section, she clicks on the "Create election" link
- (She arrives on the "Checklist" page, that lists all main parameters of the election for review, and that flags incoherent or misconfigured parameters. For example, in this test scenario, it displays 2 warnings: "Warning: No trustees were set. This means that the server will manage the election key by itself.", and "Warning: No contact was set!")
- In the "Validate creation" section, she clicks on the "Create election" button
- (She arrives back on the "My test election for Scenario 1 — Administration" page. Its contents have changed. There is now a text saying "The election is open. Voters can vote.", and there are now buttons "Close election", "Archive election", "Delete election")
- She remembers the URL of the voting page, that is where the "Election home" link points to
- She checks that a "Close election" button is present (but she does not click on it)
- She logs out and closes the window
- Verify election consistency (using command line tool `belenios_tool verify`)
- All voting electors cast their vote (`K` electors vote). We check vote data consistency for every batch of `X` votes (using `belenios_tool verify-diff` and a snapshot of election data copied in previous batch). For each batch of `X` voters:
- Create election data snapshot
- Current batch of electors vote. For each voter of this batch:
- Bob checks that he has received 2 emails containing an invitation to vote and all necessary credentials (election page URL, username, password). He goes to the election page URL.
- He clicks on the "Start" button
- A loading screen appears, then another screen appears. He clicks on the "Here" button
- A modal opens (it is an HTML modal created using Window.prompt()), with an input field. He types his credential.
- He fills his votes to each answer of the question (for each displayed checkbox, he decides to mark it or leave it empty)
- He clicks on the "Next" button
- He remembers the smart ballot tracker that is displayed
- He clicks on the "Continue" button
- He types his voter username and password, and submits the form
- He checks that the smart ballot tracker value that appears on screen is the same as the one he noted
- He clicks on the "I cast my vote" button
- He clicks on the "ballot box" link
- He checks that his smart ballot tracker appears in the list
- He closes the window (there is no log-out link, because user is not logged in: credentials are not remembered)
- He checks his mailbox to find a new email with confirmation of his vote, and verifies the value of the smart ballot tracker written in this email is the same as the one he noted.
- Verify election consistency (using `belenios_tool verify-diff`)
- Delete election data snapshot
- Verify election consistency (using command line tool `belenios_tool verify`)
- Create election data snapshot
- All electors who want to change their vote re-vote (`L` electors re-vote)
- We re-apply the same procedure as listed in previous step, except we use the set of `L` re-voters instead of the set of `K` voters
- Verify election consistency (using `belenios_tool verify-diff` and the snapshot created right before re-votes)
- Delete election data snapshot
- Verify election consistency (using command line tool `belenios_tool verify`)
- Administrator starts tallying of the election
- Alice goes to the election page
- She clicks on the "Administer this election" link
- She logs in as administrator
- She clicks on the "Close election" button
- She clicks on the "Proceed to vote counting" button
- She remembers the encrypted tally hash
- She remembers the link to send to each trustee, so they can tally the election
- She sends to each trustee an email containing their own link
- She logs out and closes the window
- Trustees do tallying (partial decryption). Each trustee (Tom and Taylor) will do the following process:
- He opens the link that Alice (the election administrator) has sent to him
- We verify that the encrypted election hash is the same as the one that has been displayed to election administrator
- He verifies that the "private key" input field is empty (at the beginning)
- He clicks on the "Browse..." button and selects his private key file (initially downloaded as `private_key.json` by default)
- He waits until the "private key" input field (that has id "#private_key") becomes not empty anymore. This is because once the user has selected the file to upload, the Javascript code in the page detects that a file has been selected, reads it, and fills "private key" input field with file's contents. The computation triggered by click on the "Compute decryption factors" button will use the value of this field, not directly the uploaded file contents.
- He clicks on the "Compute decryption factors" button
- He checks that the text field below (used as visual feedback) now contains text
- He clicks on the "Submit" button
- He checks that next screen contains a confirmation sentence
- He closes the window
- Administrator finished tallying of the election
- Alice goes to the election page
- She clicks on the "Administer this election" link
- She logs in as administrator
- She checks that encrypted tally hash is still the same as the first time it has been displayed to her
- She checks that the "DONE?" column of each trustee is to "Yes"
- She clicks on the "Compute the result" button
- She checks consistency of the election result
- She checks that the number of accepted ballots is the same as the number of voters who voted
- For each available answer in the question, she checks that the total number of votes in favor of Answer X displayed in result page is the same as the sum of votes for Answer X in all votes of voters who voted that have been randomly generated in advance
- She checks that each ballot content corresponds to content that of this vote that has been randomly generated in advance
- Verify election consistency (using command line tool `belenios_tool verify`)
belenios-1.17/doc/election_test_scenario_3_specification.md 0000644 0006501 0606723 00000021174 14115601117 023040 0 ustar glondu sed Scenario 3: Simple vote, in "fully automated" mode, with non-homomorphic question
=================================
## Introduction and parameters
Protagonists to emulate: election administrator, `K` electors, an auditor.
Administrator uses only her browser.
Electors use their browser and read emails sent by the server.
`L` electors re-vote (with `L <= K`)
`M` electors ask administrator to re-generate their password, and vote with their re-generated password (with `M <= K`).
The auditor makes web requests, has a persistent state, and runs the commandline version of the Belenios tool.
Auditor makes web requests, has a persistent state, and runs the commandline version of the Belenios tool.
Authentication of administrator and electors are done using a login / password combination.
Examples of parameters sizes: `N` and `K` would be between 6 (quick test) and 1000 (load test)
## Note about verification
Verifications all along the process is done using command line tools `belenios-tool verify` and `belenios-tool verify-diff`:
- `belenios-tool verify` does a static verification (it verifies that vote data at current time is coherent)
- `belenios-tool verify-diff` does a dynamic verification (it verifies that current state of vote data is a possible/legitimate evolution of a vote data snapshot that has been saved during a previous step of the process)
## Detailed steps of the Test Scenario 3 process
- Starting setup of the election (action of the administrator)
- Creation of the draft election
- Alice has been given administrator rights on an online voting app called Belenios. She goes to check out its homepage and logs in.
- She clicks on the "Prepare a new election" link
- (She keeps default values on the form: Credential management is automatic (not manual), and Authentication method is Password, not CAS)
- She clicks on the "Proceed" button (this redirects to the "Preparation of election" page)
- She changes values of fields name and description of the election
- She clicks on the "Save changes button" (the one that is next to the election description field)
- Edition of election's questions
- She clicks on the "Edit questions" link, to write her own questions
- She arrives on the Questions page. She checks that the page title is correct
- She clicks on "Tick the box to activate this mode."
- She clicks on "Alternative"
- She removes answer 3
- She clicks on the "Save changes" button (this redirects to the "Preparation of election" page)
- Setting election's voters
- She clicks on the "Edit voters" link, to then type the list of voters
- She types `N` e-mail addresses (the list of invited voters)
- She clicks on the "Add" button to submit changes
- She clicks on "Go back to election draft" link
- She clicks on button "Generate on server"
- (Server sends emails to voters.) She checks that server does not show any error that would happen when trying to send these emails (this can happen if sendmail is not configured)
- We do a sanity check that server has really tried to send emails. (For this, we look for email addresses in the temporary file where our fake sendmail executable redirects its inputs to.)
- She clicks on the "Proceed" link
- In "Authentication" section, she clicks on the "Generate and mail missing passwords" button
- She checks that the page contains expected confirmation text, instead of an error
- She clicks on the "Proceed" link
- Finalize creation of election
- In "Validate creation" section, she clicks on the "Create election" link
- (She arrives on the "Checklist" page, that lists all main parameters of the election for review, and that flags incoherent or misconfigured parameters. For example, in this test scenario, it displays 2 warnings: "Warning: No trustees were set. This means that the server will manage the election key by itself.", and "Warning: No contact was set!")
- In the "Validate creation" section, she clicks on the "Create election" button
- (She arrives back on the "My test election for Scenario 1 — Administration" page. Its contents have changed. There is now a text saying "The election is open. Voters can vote.", and there are now buttons "Close election", "Archive election", "Delete election")
- She remembers the URL of the voting page, that is where the "Election home" link points to
- She checks that a "Close election" button is present (but she does not click on it)
- Log out and close the browser window
- Regenerating electors' lost passwords (for M electors) (action of the administrator)
- Alice has been contacted by some voters who say they lost their password. She wants to re-generate their passwords and have the platform send them by email. For this, she logs in as administrator.
- She remembers the list of voters who contacted her and said they lost their password.
- She selects the election that she wants to edit
- She arrives to the election administration page. For each voter of the M selected voters:
- She clicks on the "Regenerate and mail a password" link
- She types the e-mail address of the voter in the "Username" field
- She clicks on the "Submit" button
- She checks that the page shows a confirmation message similar to "A new password has been mailed to name@email.com"
- She clicks on the "Proceed" link (She arrives back to the election administration page)
- We do a sanity check that server has really tried to send these emails, and to these users only.
- She logs out and closes the browser window
- Verify election consistency (using command line tool `belenios_tool verify`)
- All voting electors cast their vote (`K` electors vote). We check vote data consistency for every batch of `X` votes (using `belenios_tool verify-diff` and a snapshot of election data copied in previous batch). For each batch of `X` voters:
- Create election data snapshot
- Current batch of electors vote. For each voter of this batch:
- Bob checks that he has received 2 emails containing an invitation to vote and all necessary credentials (election page URL, username, password). He goes to the election page URL.
- He clicks on the "Start" button
- A loading screen appears, then another screen appears. He clicks on the "Here" button
- A modal opens (it is an HTML modal created using Window.prompt()), with an input field. He types his credential.
- He fills his votes to each answer of the question (for each displayed input field, he decides to set it to 1 or leave it at zero)
- He clicks on the "Next" button
- He remembers the smart ballot tracker that is displayed
- He clicks on the "Continue" button
- He types his voter username and password, and submits the form
- He checks that the smart ballot tracker value that appears on screen is the same as the one he noted
- He clicks on the "I cast my vote" button
- He clicks on the "ballot box" link
- He checks that his smart ballot tracker appears in the list
- He closes the window (there is no log-out link, because user is not logged in: credentials are not remembered)
- He checks his mailbox to find a new email with confirmation of his vote, and verifies the value of the smart ballot tracker written in this email is the same as the one he noted.
- Verify election consistency (using `belenios_tool verify-diff`)
- Delete election data snapshot
- Verify election consistency (using command line tool `belenios_tool verify`)
- Create election data snapshot
- All electors who want to change their vote re-vote (`L` electors re-vote)
- We re-apply the same procedure as listed in previous step, except we use the set of `L` re-voters instead of the set of `K` voters
- Verify election consistency (using `belenios_tool verify-diff` and the snapshot created right before re-votes)
- Delete election data snapshot
- Verify election consistency (using command line tool `belenios_tool verify`)
- Administrator does tallying of the election
- Alice goes to the election page
- She clicks on the "Administer this election" link
- She logs in as administrator
- She clicks on the "Close election" button
- She clicks on the "Proceed to vote counting" button
- She clicks on the "Proceed to decryption" button
- Verify election consistency (using command line tool `belenios_tool verify`)
- One voter tries to revote and faces an error message "Your ballot for Test vote after close is rejected, because the election is closed." after authentication
belenios-1.17/doc/election_test_scenario_4_specification.md 0000644 0006501 0606723 00000036777 14115601117 023060 0 ustar glondu sed Scenario 4: Vote with vote codes in manual mode and manual trustees, using a threshold for trustees
=================================
## Introduction and parameters
This scenario is adapted from scenario 2.
Protagonists to emulate: election administrator, credential authority, `T` trustees, `K` electors, an auditor.
Administrator and trustees uses only thier browser. Credential authority uses her browser and sends emails.
Electors use their browser and read emails sent by the server and by the credential authority.
`L` electors re-vote (with `L <= K`)
`M` electors ask administrator to re-generate their password, and vote with their re-generated password (with `M <= K`).
A threshold of `U` trustees are needed (among all `T` trustees, with `U <= T`) to validate the vote.
The auditor makes web requests, has a persistent state, and runs the commandline version of the Belenios tool.
Auditor makes web requests, has a persistent state, and runs the commandline version of the Belenios tool.
Authentication of administrator and electors are done using a login / password combination.
Examples of parameters sizes: `N` and `K` would be between 6 (quick test) and 1000 (load test)
## Note about verification
Verifications all along the process is done using command line tools `belenios-tool verify` and `belenios-tool verify-diff`:
- `belenios-tool verify` does a static verification (it verifies that vote data at current time is coherent)
- `belenios-tool verify-diff` does a dynamic verification (it verifies that current state of vote data is a possible/legitimate evolution of a vote data snapshot that has been saved during a previous step of the process)
## Detailed steps of the Test Scenario 4 process
- Starting setup of the election (action of the administrator)
- Creation of the draft election
- Alice has been given administrator rights on an online voting app called Belenios. She goes to check out its homepage and logs in.
- She clicks on the "Prepare a new election" link
- She picks the Credential management method: manual
- (She keeps default value for Authentication method: it is Password, not CAS)
- She clicks on the "Proceed" button (this redirects to the "Preparation of election" page)
- In the "Name and description of the election" section, she changes values of fields name and description of the election
- She clicks on the "Save changes button" (the one that is next to the election description field)
- In the "Contact" section, the changes values of field "Contact:", and clicks on the "Save changes button" of this section
- She remembers the URL of the draft election administration page
- Edition of election's questions
- She clicks on the "Edit questions" link, to write her own questions
- She arrives on the Questions page. She checks that the page title is correct
- She removes answer 3
- She clicks on the "Save changes" button (this redirects to the "Preparation of election" page)
- Setting election's voters
- She clicks on the "Edit voters" link, to then type the list of voters
- She types `N` e-mail addresses (the list of invited voters)
- She clicks on the "Add" button to submit changes
- She clicks on "Go back to election draft" link
- In "Authentication" section, she clicks on the "Generate and mail missing passwords" button
- She checks that the page contains expected confirmation text, instead of an error
- She clicks on the "Proceed" link
- In "Credentials" section, she clicks on "Credential management" link
- She remembers the link displayed
- She sends the remembered link to the credential authority by email
- She logs out and closes the browser
- Credential authority sends credentials to electors
- Cecily, the Credential Authority, receives the email sent by Alice, and opens the link in it
- She remembers what the link to the election will be, so that she will be able to send it to voters by email with their private credential
- She clicks on the "Generate" button
- She clicks on the "private credentials" and "public credentials" links and downloads these files. (Files are by default downloaded using filenames `creds.txt` and `public_creds.txt` respectively)
- She clicks on the "Submit public credentials" button
- She checks that redirected page shows correct confirmation sentence
- She closes the window
- She reads the private credentials file (`creds.txt`) and sends credential emails to voters
- Continuing setup of the election: Administrator invites trustees and sets threshold
- Administrator logs in and goes to the election draft page
- In the "Trustees" section, she clicks on the "here" link
- She clicks on the "threshold mode" link
- She adds `T` trustees (their email address), and remembers the link she will send to each trustee
- In the field next to "Threshold:", she types `U`, and clicks on the "Set" button
- (She checks that in the table, the "STATE" column is "1a" on every row)
- She sends to each trustee an email containing their own link
- She logs out and closes the window
- Trustees initialization step 1/3: Trustees generate election private keys. Each of the `T` trustees will do the following process:
- Trustee opens link that has been sent to him by election administrator
- He checks that the page content shows the same election URL as the one the administrator saw
- He clicks on the "Generate private key" button
- He clicks on the "private key" link, to download the private key (file is saved by default as `private_key.txt`)
- He clicks on the "Submit" button
- He checks that the next page shows the expected confirmation sentence (If trustee was the last one in the list, he checks that page contains text "Now, all the certificates of the trustees have been generated. Proceed to generate your share of the decryption key."
, else he checks for sentence "Waiting for the other trustees... Reload the page to check progress.")
- He closes the window
- (Administrator logs in, selects the election by clicking on its link, and in the "Trustees" section clicks on "here". She checks that in the table on the current trustee row, the "STATE" column is now "1b" instead of "1a")
- (Administrator logs in, selects the election by clicking on its link, and in the "Trustees" section clicks on "here". She checks that in the table on every row, the "STATE" column is now "2a")
- Trustees initialization step 2/3: Trustees generate their share of the decryption key. Each of the `T` trustees will do the following process:
- Trustee opens link that has been sent to him by election administrator
- He checks that the page content shows the same election URL as the one the administrator saw
- He checks the presence of text "Now, all the certificates of the trustees have been generated. Proceed to generate your share of the decryption key."
- In field next to "Enter your private key:", he types the content of the `private_key.txt` file he downloaded
- He clicks on the "Proceed" button
- He waits until the text field next to "Data:" contains text, and clicks on the "Submit" button
- If he is not the last trustee in the list, he checks that the next page contains text "Waiting for the other trustees... Reload the page to check progress.". Else, he checks that the next page contains text "Now, all the trustees have generated their secret shares. Proceed to the final checks so that the election can be validated."
- He closes the window
- (Administrator logs in, selects the election by clicking on its link, and in the "Trustees" section clicks on "here". She checks that in the table on the current trustee row, the "STATE" column is now "2b" instead of "2a")
- Trustees initialization step 3/3: Trustees do the final checks so that the election can be validated. Each of the `T` trustees will do the following process:
- Trustee opens link that has been sent to him by election administrator
- He checks that the page content shows the same election URL as the one the administrator saw
- He checks the presence of text "Step 3/3"
- In field next to "Enter your private key:", he types the content of the `private_key.txt` file he downloaded
- He clicks on the "Proceed" button
- He waits until the text field next to "Data:" contains text, and clicks on the "Submit" button
- He checks that the next page contains text "Your job in the key establishment protocol is done!"
- He clicks on the "public key" link and downloads the file (file is saved by default as `public_key.json`)
- He closes the window
- (Administrator logs in, selects the election by clicking on its link, and in the "Trustees" section clicks on "here". She checks that in the table on the current trustee row, the "STATE" column is now "3b" instead of "3a")
- Administrator completes setup of the election
- Alice, as an administrator of an election, wants to finalize her draft election creation, to start the vote. She opens a browser and logs in as administrator
- She goes to the draft election administration page
- In the "Trustees" section, she clicks on "here". She checks that in the table on all rows, the "STATE" column is now "done"
- She clicks on the "Go back to election draft" link
- In "Validate creation" section, she clicks on the "Create election" link
- (She arrives on the "Checklist" page, that lists all main parameters of the election for review, and that flags incoherent or misconfigured parameters.)
- She checks the presence of text "election ready"
- In the "Validate creation" section, she clicks on the "Create election" button
- (She arrives back on the "My test election for Scenario 1 — Administration" page. Its contents have changed. There is now a text saying "The election is open. Voters can vote.", and there are now buttons "Close election", "Archive election", "Delete election")
- She remembers the URL of the voting page, that is where the "Election home" link points to
- She checks that a "Close election" button is present (but she does not click on it)
- She logs out and closes the window
- Verify election consistency (using command line tool `belenios_tool verify`)
- All voting electors cast their vote (`K` electors vote). We check vote data consistency for every batch of `X` votes (using `belenios_tool verify-diff` and a snapshot of election data copied in previous batch). For each batch of `X` voters:
- Create election data snapshot
- Current batch of electors vote. For each voter of this batch:
- Bob checks that he has received 2 emails containing an invitation to vote and all necessary credentials (election page URL, username, password). He goes to the election page URL.
- He clicks on the "Start" button
- A loading screen appears, then another screen appears. He clicks on the "Here" button
- A modal opens (it is an HTML modal created using Window.prompt()), with an input field. He types his credential.
- He fills his votes to each answer of the question (for each displayed checkbox, he decides to mark it or leave it empty)
- He clicks on the "Next" button
- He remembers the smart ballot tracker that is displayed
- He clicks on the "Continue" button
- He types his voter username and password, and submits the form
- He checks that the smart ballot tracker value that appears on screen is the same as the one he noted
- He clicks on the "I cast my vote" button
- He clicks on the "ballot box" link
- He checks that his smart ballot tracker appears in the list
- He closes the window (there is no log-out link, because user is not logged in: credentials are not remembered)
- He checks his mailbox to find a new email with confirmation of his vote, and verifies the value of the smart ballot tracker written in this email is the same as the one he noted.
- Verify election consistency (using `belenios_tool verify-diff`)
- Delete election data snapshot
- Verify election consistency (using command line tool `belenios_tool verify`)
- Create election data snapshot
- All electors who want to change their vote re-vote (`L` electors re-vote)
- We re-apply the same procedure as listed in previous step, except we use the set of `L` re-voters instead of the set of `K` voters
- Verify election consistency (using `belenios_tool verify-diff` and the snapshot created right before re-votes)
- Delete election data snapshot
- Verify election consistency (using command line tool `belenios_tool verify`)
- Administrator starts tallying of the election
- Alice goes to the election page
- She clicks on the "Administer this election" link
- She logs in as administrator
- She clicks on the "Close election" button
- She clicks on the "Proceed to vote counting" button
- She checks the presence of text "Awaiting trustees… At least ${U} trustee(s) must act."
- She checks that in the table on every content row, the "DONE?" column is "No"
- She remembers the encrypted tally hash
- She remembers the link to send to each trustee, so they can tally the election
- She sends to each trustee an email containing their own link
- She logs out and closes the window
- Trustees do tallying (partial decryption). Each trustee (Tom and Taylor) will do the following process:
- He opens the link that Alice (the election administrator) has sent to him
- We verify that the encrypted election hash is the same as the one that has been displayed to election administrator
- He verifies that the "private key" input field is empty (at the beginning)
- He clicks on the "Browse..." button and selects his private key file (initially downloaded as `private_key.json` by default)
- He waits until the "private key" input field (that has id "#private_key") becomes not empty anymore. This is because once the user has selected the file to upload, the Javascript code in the page detects that a file has been selected, reads it, and fills "private key" input field with file's contents. The computation triggered by click on the "Compute decryption factors" button will use the value of this field, not directly the uploaded file contents.
- He clicks on the "Compute decryption factors" button
- He checks that the text field below (used as visual feedback) now contains text
- He clicks on the "Submit" button
- He checks that next screen contains a confirmation sentence
- He closes the window
- Administrator finished tallying of the election
- Alice goes to the election page
- She clicks on the "Administer this election" link
- She logs in as administrator
- She checks that encrypted tally hash is still the same as the first time it has been displayed to her
- She checks that the "DONE?" column of each trustee is to "Yes"
- She clicks on the "Compute the result" button
- She checks consistency of the election result
- She checks that the number of accepted ballots is the same as the number of voters who voted
- For each available answer in the question, she checks that the total number of votes in favor of Answer X displayed in result page is the same as the sum of votes for Answer X in all votes of voters who voted that have been randomly generated in advance
- She checks that each ballot content corresponds to content that of this vote that has been randomly generated in advance
- Verify election consistency (using command line tool `belenios_tool verify`)
belenios-1.17/doc/fips.sage 0000644 0006501 0606723 00000003655 14115601117 014456 0 ustar glondu sed import hashlib
######################################################################
### Generate p and q according to FIPS 186-4, section A.1.1.2
L = 2048
N = 256
seedlen = 358 # not used
outlen = 256
seed = "Belenios: Verifiable online voting system "
def stringToInt(string):
seq = [ ord(x) for x in string]
res = ZZ(seq, 256)
return res
def intToString(number):
seq = number.digits(base=256)
seq = [ chr(x) for x in seq ]
return reduce(lambda a, b: a + b, seq)
# hash integer to an integer with SHA256
def Hash(number):
string = intToString(number)
return int(hashlib.sha256(string).hexdigest(), 16)
assert seedlen >= N
n = (L/outlen).n().ceiling() - 1
b = L-1-(n*outlen)
count = 0
found = False
while True:
domain_parameter_seed = stringToInt(seed + str(count))
U = Hash(domain_parameter_seed) % 2^(N-1)
q = 2^(N-1) + U + 1 - (U % 2)
count += 1
if q.is_prime():
break
offset = 1
counter = 0
while counter < 4*L:
V = [ Hash(domain_parameter_seed + offset + j) for j in range(0,n+1) ]
W = V[0]
for j in range(1, n):
W += V[j]*2^(outlen*j)
W += (V[n] % 2^b)*2^(n*outlen)
X = W + 2^(L-1)
c = X % (2*q)
p = X - (c-1)
if p > 2^(L-1) and p.is_pseudoprime():
found = True
break
offset = offset + n + 1
counter += 1
if found:
print "p = " + str(p)
print "q = " + str(q)
print "domain_parameter_seed = " + str(domain_parameter_seed)
print "counter = " + str(counter)
else:
print "Not found"
######################################################################
### Generate g according to section A.2.3
index = 0
N = q.nbits()
e = (p-1)//q
count = 1
U = count + 2^16*(index + 2^16*(0x6767656E + 2^32*domain_parameter_seed))
W = Hash(U)
g = int(GF(p, proof=False)(W)^e)
assert g >= 2
print "g = " + str(g)
print "Checking primality of p (not only pseudo-primality), this will take some time..."
assert p.is_prime()
belenios-1.17/doc/frontend_booth.md 0000644 0006501 0606723 00000010157 14115601117 016203 0 ustar glondu sed # Documentation about the frontend folder and its autonomous booth page
The `frontend` folder contains a booth page, with its Javascript application and several User Interface components meant to be used in [Belenios](https://www.belenios.org/). They are implemented using Javascript ES6 and React (without JSX nor transpilation), and use i18next for internationalization.
## Using it: Executing the booth
### Download required packages
Go into the `frontend` directory: `cd frontend`
Install npm, optionnaly using nvm (if you choose to use nvm, don't forget to execute `nvm use current` before executing the following commands)
Then install the minimum dependencies required for this project (React, i18next, etc): `npm install`
### Execute an HTTP server to display the vote.html page
The `frontend/booth/vote.html` page is autonomous as it can be accessed without any instance of the Belenios server running: it simply need to refer to an election.json file located in a folder named after the uuid of the election. But this page uses Javascript Modules, and as stated in the [Mozilla Developer Network page about Javascript Modules](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Guide/Modules): "You need to pay attention to local testing — if you try to load the HTML file locally (i.e. with a `file://` URL), you'll run into CORS errors due to JavaScript module security requirements. You need to do your testing through a server.".
So you need to execute an HTTP server to display this `vote.html` page. You can run any HTTP server using the `frontend` folder as root, but a shortcut command using Python3's default HTTP server is provided in `package.json`, so with your terminal located in the `frontend` folder, simply execute the following command:
`npm start`
Then you can point your browser at http://localhost:8000/booth/vote.html
If you create an `elections` folder, containing a folder named after the uuid of your election, containing a valid `election.json` file, then you can test how this election displays on the booth page by pointing your browser at http://localhost:8000/booth/vote.html#uuid=__my_uuid__&lang=fr
For the moment, supported languages (in hash parameter of the URL) are "en" and "fr", with the default being "en".
## Creating a build and installing it somewhere
Execute `make` to create a build in the `frontend/build` folder.
Execute `make DESTDIR=../_run/usr/share/belenios-server` to create a build in the `../_run/usr/share/belenios-server/frontend` folder. This is what the `build-*-server` targets do in the `Makefile` at the root of the Belenios repository.
## Contributing to the development of the frontend vote application
### React controlled versus uncontrolled components for input fields
In React we chose to use [uncontrolled components](https://en.reactjs.org/docs/uncontrolled-components.html) for input fields (instead of controlled components), so that it remains easy (or feasable) to test the frontend vote application using end-to-end tests which control the browser (we use Selenium for this).
### Extracting translatable strings from Javascript files
We use `i18next` and `react-i18next` libraries for internationalization. To extract translatable strings from Javascript files and merge them into the already existing `frontend/translations/*.json` translation files of each language, you can execute the following command:
`npm run i18n-extract`
### Reduce loading time by telling the browser early which Javascript files to download
We use HTML tags `` to tell the browser to download all necessary Javascript files, instead of downloading in a waterfall way (downloading `app.mjs` then parsing it and then downloading the files it imports, continuing recursively). So if you add a Javascript file to the vote application (for example you create a new component), make sure to add it to the list of preloaded files in `vote.html` and in its `vote_development.html` counterpart. You can use the following command to generate the whole list:
`cd booth; ls -1 *.mjs components/*.mjs | grep -v ".stories.mjs" | sed -e 's/\(.*\)//' > preload.txt` belenios-1.17/doc/instructions-en.md 0000644 0006501 0606723 00000040023 14115601117 016330 0 ustar glondu sed Who does what during a Belenios election ?
=============
Introduction
------------
Belenios proposes a verifiable e-voting system. Each voter can check that
her ballot is indeed in the ballot box, and any third-party can verify
that the claimed result corresponds to the ballots present in the ballot
box, and that these ballots come from legitimate voters. The vote secrecy
is ensured via the splitting of the decryption key between several
authorities (for instance, some members of the committee in charge of the
election) with a threshold mechanism (for instance, 3 among 5 are enough
to decrypt).
But the nice security properties hold only if everyone performs all the
verifications that they are supposed to do. This document lists in
detail, for each role in the election (voter, administrator, etc) what
must be done at each step.
Instructions for the voter
--------------------------
When a voter wants to vote online, his computer encrypts his choices
(with a Javascript program) and prints a `smart ballot tracker` which is
a fingerprint of the ballot. This `smart ballot tracker` is also sent by
email to the voter when he has completed the voting procedure.
In order to check that his ballot is indeed taken into account, the voter
must check that his `smart ballot tracker` is present in the ballot box
by visiting the link `See accepted ballots` on the main page of the
election. The voter must also vehemently protest if he receives a
confirmation email that contains a `smart ballot tracker` that is
different from the one that was printed on the screen during the voting
phase. Somebody probably managed to add a ballot in his name. This can
for instance be a hint of an attack by a system administrator who has
access to the mailbox of the voter, in the case where both the login /
password and the credential are sent to the same address.
Note: a voter can vote several times. Only the *last* vote is taken into
account. The voter must check that the last `smart ballot tracker` is in
the ballot box. The previous smart ballot trackers are removed from the
box.
A voter can also verify the whole voting process. Instead of checking only
the presence of his ballot in the ballot box, he can verify the validity
of all the ballots, he can monitor the ballot box to check that no ballot
disappears (except in case of revote), and finally check that the claim
result corresponds to the ballots in the ballot box. For all of this, the
voter can follow the auditor instructions below.
Instructions for the trustees
-----------------------------
During the setup of the election, it is expected that the trustee saves:
- her decryption key (or PKI key, in threshold mode) (file `
private_key.json` or `private_key.txt`). **This key must be saved in a
secure place** (encrypted container, USB stick stored in a closed place,
etc) because it protects the secret of the votes (in combination with
the decryption keys of the other trustees);
- the `url` of the election;
- (in threshold mode) the fingerprint of her PKI public key, simply
called `public key` below;
- the fingerprint of her verification key associated to her decryption
key `verification key`.
As soon as the election is ready, it is expected that the trustee checks:
- that her `verification key` is present on the election page, aside her
name;
- (in threshold mode) that her PKI `public key` is present on the
election page, aside her name.
After the closing of the election, the trustee participates to the
computation of the tally. In the case of an election with alternative
voting (ordering of the candidates, giving them a grade), the tally
starts by a shuffle phase. For this it is expected from the trustee that
she
- saves the fingerprint of the ballot box after her shuffle: `fingerprint
of your shuffle`;
- checks immediately thereafter that this fingerprint is present on the
page of the election (to ensure that her shuffle has not be ignored).
In all cases, the tally then proceeds with a phase where the trustee uses
her private key to decrypt the result. It is expected that the trustee:
- checks (only for alternative voting) the fingerprint of her shuffle
`fingerprint of your shuffle` as saved in the previous step is present
on the page of the election, aside her name. If this is not the case,
the private key must not be used.
- saves the fingerprint of the ballot box to be decrypted: `fingerprint
of the encrypted tally`.
Once the tally is finished, the results are published on the page of the
election. It is expected that the trustee checks that the following data
are present on this page, each time associated to her name:
- (in threshold mode) her PKI `public key`;
- her `verification key`;
- (for alternative voting) the fingerprint of her shuffle;
- the fingerprint of the ballot box to be decrypted `fingerprint of the
encrypted tally` (in order to check that her private key has not be
used to decrypt something else).
Instructions for the credential authority
-----------------------------------------
During the setup of the election, it is expected that the credential
authority saves:
- the list of the private credentials: the `creds.txt` file. **This file
must be saved in a secure place** (encrypted container, USB stick
stored in a closed place, etc) because it is a protection against
ballot stuffing. It will also allow to send again the credential to a
voter who has lost it;
- the `url` of the election;
- the voter list `voters.txt`. The credential authority must verify with
the committee in charge of the election that this list is correct,
as well as the weight of each voter in case of a weighted vote;
- the fingerprint of the voter list: `fingerprint of voters`;
- the `fingerprint of the public credentials`.
The credential authority is in charge of sending the credentials to the
voters. She must include the `url` of the election in the message that
she sends (by email, by postal mail). For sending the credentials by
email, it is possible to use the `contrib/send_credentials.py ` program
included in the Belenios sources (see the auditor section below for how
to get the sources). After editing this program according to your
settings you can run it :
contrib/send_credentials.py
As soon as the election is open, and at the end of the election, it is
expected that the credential authority:
- verifies that the number of voters corresponds to the voter list used
during the setup, as well as the total weight of the election in
case of a weighted vote, and that the fingerprint of the voter list corresponds
to the fingerprint saved before, for instance using one of the
commands suggested [here](#hash).
- verifies that the fingerprint of the list of the public credentials
corresponds to the one displayed close to his name.
- during the election, the credential authority can, when a voter asks,
send him again his private credential if he lost it.
At the end of the election, in order to validate the records, it is
expected that the credential authority :
- verifies that the voting records given by the administrator corresponds
to the ballots in the ballot box. This verification can be done with
the command:
belenios-tool compute-voters --privcreds /path/to/creds.txt --url https://url/to/election
The list output by this command must coincide with the one given by the
administrator (maybe in a different order).
Once the election is finished and validated, it is expected that the
credential authority:
- destroys the file `creds.txt`. Indeed, this file gives the link between
a voter and his (encrypted) ballot. This link could compromise the vote
secrecy in the long term, for instance if the encryption keys become
too small for the computing power in the future (or if a quantum
computer becomes available...)
Instructions for the committee in charge of the election
--------------------------------------------------------
At the very least, the election committee visits the page of the election
once it is open and checks that:
- the number of voters corresponds to the voter list;
- the value `voter list fingerprint` published corresponds to the one
that is given (by the system or by the administrator of the election).
This fingerprint can be computed using one of the
commands suggested [here](#hash).
- the voter list `voters.txt` corresponds to the legitimate voters,
with the right weight in case of a weighted vote.
- the list of questions and possible answers correspond to what is
expected. These questions and answers are also in the `election.json`
file that can be obtained by clicking on `parameters` in the footer of
the election page.
Ideally, the election committee also performs the tasks of an auditor or
commissions someone for doing it (a sysadmin of the organization, for
instance).
Instructions for the auditor
----------------------------
Anyone who knows the `url` of the election can be an auditor. The
security of Belenios relies on the fact that the verifications described
below are performed by at least one honest person. In order to do these
tests, the auditor must use some software. We describe here how to run the
verifications using `belenios-tool` the sources of which are available
from [Gitlab Inria](https://gitlab.inria.fr/belenios/belenios) and that
can be installed under Linux Debian/Ubuntu using `sudo apt install
belenios-tool`.
Note: these verifications are also run automatically by our servers for
the elections that are setup with a maximum security level (external
credential authority and at least two external trustees).
During and after the election, the auditor has access to the following
files (from the page of the election):
* `election.json`: parameters of the election;
* `trustees.json`: verification keys of the trustees, and their PKI
public keys in threshold mode;
* `public_creds.txt`: public parts of the credentials;
* `ballots.jsons`: ballots that have been accepted for inclusion in the
ballot box.
During the election, it is expected that the auditor:
- verifies that the printed number of voters is correct and that the
fingerprint of the voter list is correct (if she has access to this
information). See the instructions for the election committee.
- verifies that the file of public parts of the credentials
`public_creds.txt` corresponds to the fingerprint of the public
credentials displayed on the election main page, for example using
one of the commands suggested [here](#hash).
- verifies that the number of voters is equal to the number of public
credentials in `public_creds.txt`.
- verifies the consistency of the ballot box. By copying the 4 files
listed above in a directory `/path/to/election`, the following command
runs all the required verifications:
belenios-tool verify --dir /path/to/election
- verifies that the ballot box evolves in a way that is in accordance
with the protocol : no ballot must disappear unless it is replaced by
another ballot coming from the same credential (case of voter who re-votes).
For this, download again the 4 files in a new directory
`/path/to/election/new` and run the command:
belenios-tool verify-diff --dir1 /path/to/election --dir2 /path/to/election/new
- verifies that the page given to the voters and the associated resources
(images, css, Javascript files) do not change. The Javascript programs
must correspond to the one obtained after compiling Belenios sources.
The program `contrib/check_hash.py` given in the sources does this
automatically:
contrib/check_hash.py --url https://url/to/server
Note that the url is the one of the server and not the one of the
election; for instance `--url https://belenios.loria.fr`.
After the election, the auditor has also access to the `result.json`
file. It is expected that the auditor:
- run again both verifications mentioned above to verify the consistency
of the final ballot box and the consistency with the last saved ballot
box.
- verify that the result mentioned in the `result.json` file corresponds
to the result published on the page of the election. In fact, this
verification must be done manually.
- verify that the fingerprints that are present in these files correspond
to what is published on the election page as read by the voters and the
other participants.
For this last point and all the other tasks (except for the manual
verification of the claimed result), a software tool is distributed with
the Belenios sources. It requires that `belenios-tool` is compiled and
installed properly. Then the auditor must create a directory `workdir`
where the election audit information will be stored as long as they are
downloaded, in the form of a `git` repository. During the audit phase,
the following command must be frequently run:
contrib/monitor_elections.py --uuid --url https://url/to/server --wdir
and it will download the current audit data, verify them, and compare
them with the previous one.
It is possible to redirect the messages with the `--logfile` option. Then
only abnormal behaviours are reported on `stdout/stderr`, which makes it
possible to run the command from a `crontab` and to be warned in case of
problem.
Note: If the `belenios-tool` command-line tool is used, the trust in
the audit verifications partly relies on the trust in this tool. It is
possible to write independent verification software following Belenios
specification available [here](https://www.belenios.org/specification.pdf).
Instructions for the administrator of the election
--------------------------------------------------
It might look strange but the administrator of the election has not so
many verifications to perform. This comes from the fact that the Belenios
voting system is designed so that we don't have to trust the
administrator. Security relies on the combined verifications of the
different participants: trustees, credential authority, election
committee, and auditors.
The important points for the administrator are the following:
- to obtain the voter list, as a list of valid email addresses, one
address by voter. In case of a weighted vote, voters may be assigned
a different weight. This list must be validated by the electoral
committee.
- to check and check again these email addresses before starting the
election (and even before the sending of the credentials in automatic
mode). Once the setup of the election is finished, it is not possible
to modify them and there is no alert in case a message could not be
sent.
- to verify that all the participants use the same `url` for the
election.
- If the administrator did not commission someone as a credential authority,
she must download the list of private credentials (`Download private
credentials`) in order to be able to send again the credential to a
voter who has lost it. For better security, though, it is preferred to
commission a third party to play the role of credential authority.
For getting the best security level, the administrator must have:
- a person (the credential authority) in charge of generating the
credentials and sending them to the voters (the server can do this
itself, but this opens the possibility of a ballot stuffing attack).
- several trustees in charge of protecting vote secrecy: in order to decrypt
the individual ballots it is then required to attack all of them (or at
least a proportion of them, in threshold mode).
How to compute the fingerprint of a file?
-------------------------------------------------------
To compute the fingerprint of a file, you need to use the same hash
algorithm as the one used in Belenios. We provide here possible
solutions with command lines. We use the file `voters.txt` as example
but of course you may replace it by any other file.
sha256sum voters.txt | xxd -p -r | base64 | tr -d "="
(or `shasum -a256` instead of `sha256sum` for example on MacOS)
cat voters.txt | python3 -c "import hashlib,base64,sys;m=hashlib.sha256();m.update(sys.stdin.read().encode());print(base64.b64encode(m.digest()).decode().strip('='))"
You may also use
[the online tool](https://belenios.loria.fr/compute-fingerprint)
supported by Belenios.
belenios-1.17/doc/instructions-fr.md 0000644 0006501 0606723 00000043245 14115601117 016346 0 ustar glondu sed Qui fait quoi dans une élection Belenios ?
=============
Introduction
------------
Belenios propose un système de vote vérifiable. Chaque votant peut
s'assurer que son bulletin est bien dans l'urne, n'importe quel tiers peut
vérifier que le résultat proclamé correspond aux bulletins dans l'urne
et que ceux-ci proviennent d'électeurs légitimes. Le secret du vote
est assuré à travers le partage de la clé de déchiffrement entre
plusieurs autorités (par exemple des membres du comité électoral) avec
un système de seuil (par exemple 3 parmi 5 suffisent à déchiffrer).
Encore faut-il que chacun procède aux vérifications prévues par le
système de vote. Ce document détaille, pour chaque rôle d'une élection
(votant, administrateur, etc.), ce qui doit être fait à chaque étape.
Instructions pour l'électeur
-------------------------
Lorsque l'électeur vote en ligne, son ordinateur chiffre ses choix (à
l'aide d'un programme JavaScript) et affiche à l'électeur un `numéro
de suivi`, qui est une empreinte du bulletin. Ce `numéro de suivi` est
également envoyé par mail lorsque l'électeur a fini de voter.
Pour s'assurer que son bulletin de vote est bien pris en compte,
l'électeur doit s'assurer que son `numéro de suivi` apparait bien dans
l'urne en consultant la page `voir les bulletins acceptés` sur la page
d'accueil de l'élection. L'électeur doit également protester vivement
s'il reçoit un mail de confirmation sans avoir voté ou s'il reçoit un
mail de confirmation avec un `numéro de suivi` différent de celui
affiché à l'écran pendant la phase de vote. Quelqu'un a
probablement réussi à ajouter un bulletin en son nom. Cela peut être par exemple
l'indice d'une attaque par un administrateur système ayant accès au mail de
l'électeur si le login et mot de passe ainsi que le code de vote sont
envoyés sur la même adresse.
Note : un électeur peut voter à nouveau. Seul le *dernier* vote est pris
en compte. L'électeur doit s'assurer que le dernier `numéro de suivi`
reçu est bien dans l'urne. Les numéros de suivi précédents sont
supprimés.
Un électeur peut également vérifier l'intégralité du processus du
vote. C'est à dire qu'au lieu de simplement vérifier la présence de
son bulletin dans l'urne, il peut vérifier la conformité de tous les
bulletins, monitorer l'urne pour vérifier qu'aucun bulletin ne
disparait et enfin s'assurer que le résultat proclamé correspond aux
bulletins dans l'urne. Pour ce faire, il doit suivre les instructions
de l'auditeur.
Instructions pour les autorités de déchiffrement
------------------------------------------
Pendant la préparation de l'élection, il est attendu que l'autorité de
déchiffrement sauvegarde :
- sa clé de déchiffrement (ou clé privée de PKI, en mode threshold)
(fichier `private_key.json` ou `private_key.txt`). **Cette clé doit
être conservée dans un lieu sûr** (container chiffré, clé USB placée
dans un endroit fermé, etc) car elle protège le secret du vote (en
combinaison avec les autres clés de déchiffrement);
- l'`url` de l'élection;
- (en mode threshold) l'empreinte de sa clé publique de PKI `clé publique`;
- l'empreinte de sa clé vérification associée à sa clé de
déchiffrement `clé de vérification`.
Dès que l'élection est prête, il est attendu que l'autorité de
déchiffrement vérifie :
- que sa clé de vérification `clé de vérification` apparait bien sur la page d'accueil de
l'élection, à côté de son nom.
- (en mode threshold) que sa clé publique de PKI `clé publique` apparait bien sur la page d'accueil de
l'élection, à côté de son nom;
Après la fermeture de l'élection, l'autorité de déchiffrement
participe au dépouillement. Dans le cas d'une élection de
type vote alternatif (classement des candidats, ou attribution d'une
note), le dépouillement commence par une phase de mélange.
Pour cette étape, il est attendu que l'autorité de
déchiffrement :
- sauvegarde l'empreinte de l'urne mélangée : `empreinte de votre mélange`;
- et vérifie immédiatement sa présence sur la page d'accueil de
l'élection (pour s'assurer que son
mélange n'a pas été ignoré).
Dans tous les cas, le dépouillement comporte ensuite une étape où
l'autorité de déchiffrement utilise sa clé privée pour procéder à
l'ouverture de l'urne. Il est attendu que l'autorité de
déchiffrement :
- vérifie que (seulement dans le mode vote alternatif) l'empreinte de l'urne mélangée à l'étape
précédente : `empreinte de votre mélange` apparait sur la page d'accueil de l'élection, à côté du nom
de l'autorité. La clé de déchiffrement ne doit pas être entrée si ce
n'est pas le cas.
- sauvegarde l'empreinte de l'urne à déchiffrer : `empreinte du
résultat chiffré`.
Une fois le dépouillement terminé, les résultats sont proclamés sur la
page d'accueil de l'élection. Il est attendu que l'autorité de
déchiffrement vérifie que les données suivantes apparaissent sur la
page, à chaque fois associées à son nom :
- (en mode threshold) sa clé publique de PKI `clé publique`;
- sa clé de vérification `clé de vérification`;
- (en mode vote alternatif) l'empreinte de son mélange;
- l'empreinte de l'urne à déchiffrer : `empreinte du
résultat chiffré`
(pour vérifier que sa clé de déchiffrement n'a pas été utilisée pour
déchiffrer une autre donnée).
Instructions pour l'autorité de génération de codes de vote
----------------------------------------------------
Pendant la préparation de l'élection, il est attendu que l'autorité de
de génération des codes de vote sauvegarde :
- la liste des codes de vote privés : fichier `creds.txt`. **Cette
liste doit conservée dans un lieu sûr** (container chiffré, clé USB placée
dans un endroit fermé, etc) car elle protège contre le bourrage
d'urne. Elle permet également le renvoi de code de vote en cas de
perte par l'électeur;
- l'`url` de l'élection;
- la liste électorale `voters.txt`. L'autorité de génération
de codes de vote doit vérifier
auprès de la commission électorale que cette liste électorale est
correcte, ainsi que le nombre de voix attribuées à chaque électeur
dans le cas d'un vote pondéré;
- l'empreinte de la liste électorale : `Empreinte de la liste électorale`;
- l'empreinte de la liste des codes de vote publics : `Empreinte de la partie publique des codes de vote`.
L'autorité de génération de codes de vote a en charge l'envoi des
codes de vote à chaque électeur. Elle précise alors l'`url` de
l'élection dans le courrier ou mail accompagnant cet envoi. Pour
envoyer les codes de vote, il est possible d'utiliser le programme
`contrib/send_credentials.py ` fourni dans les sources de Belenios (voir
section auditeur pour obtenir les sources) en l'éditant au préalable
pour le paramétrer correctement.
contrib/send_credentials.py
Dès que l'élection est ouverte ainsi qu'à la fin de l'élection, il est attendu que l'autorité de
génération de codes de vote :
- vérifie que le nombre d'électeurs correspond à la liste électorale
reçue, ainsi que le nombre total de voix dans le cadre d'un vote
pondéré, et que
l'empreinte de la liste électorale correspond à l'empreinte
enregistrée, par exemple avec l'une des commandes décrites [ici](#hash).
- vérifie que l'empreinte de la liste des codes de vote publics
enregistrée correspond à celle affichée à côté de son nom.
- pendant l'élection, l'autorité de génération de codes de vote peut, à
la demande d'un électeur, lui renvoyer son code de vote privé s'il
l'a perdu.
À la fin de l'élection et à des fins de validation, il est attendu que l'autorité de
génération de codes de vote :
- vérifie que la liste d'émargement donnée par l'administrateur
correspond à la liste des bulletins dans l'urne. Cette vérification
peut être effectuée à l'aide de la commande:
belenios-tool compute-voters --privcreds /path/to/creds.txt --url https://url/to/election
La liste obtenue doit coïncider (à l'ordre près) avec celle fournie par
l'administrateur.
Une fois l'élection terminée et validée, il est attendu que l'autorité de
génération de codes de vote :
- détruise le fichier `creds.txt`. En effet, ce fichier permet de faire
le lien entre un électeur et son bulletin (chiffré). Ce lien
pourrait compromettre l'anonymat du vote à long terme, par exemple
si les clés de chiffrement utilisées deviennent trop faibles pour la
puissance de calcul dans le futur (ou ordinateur quantique, ...).
Instructions pour la commission électorale
-------------------------------------
A minima, la commission électorale consulte la page d'accueil de l'élection dès
qu'elle ouverte et vérifie que :
- le nombre d'électeurs affiché correspond à la liste électorale;
- la valeur `Empreinte de la liste électorale` affichée correspond
à l'empreinte de la liste électorale `voters.txt` fournie
(par le système informatique ou l'administrateur de l'élection). Le
calcul de l'empreinte peut être fait avec l'une des commandes
décrites [ici](#hash).
- la liste électorale `voters.txt` correspond bien aux électeurs
légitimes, avec le nombre de voix associé dans le cadre d'un vote pondéré.
- la liste des questions et des réponses correspond bien à ce qui a
été déterminé pour ce scrutin. Les questions et les réponses
associées apparaissent dans le fichier `election.json`. Ce fichier peut
être obtenu en cliquant sur
`paramètres` dans le bandeau en bas de la page d'accueil de l'élection.
Idéalement, la commission électorale accomplit également le travail de
l'auditeur ou mandate une personne pour le faire (des services
informatiques par exemple).
Instructions pour l'auditeur
-------------------------
Tout le monde connaissant l'`url` de l'élection peut être auditeur. La sécurité de Belenios repose sur
le fait que les vérifications décrites ci-dessous sont effectuées par
au moins une personne de confiance. Pour effectuer ces tests, des
logiciels sont nécessaires. Nous décrivons ici comment exécuter les
vérifications en utilisant `belenios-tool` dont les sources sont
disponibles à partir du [Gitlab Inria](https://gitlab.inria.fr/belenios/belenios) et qui est installable sous Linux Debian/Ubuntu avec `sudo apt install belenios-tool`.
Note : ces vérifications sont effectuées de façon automatique par nos
serveurs pour les élections mises en place avec un niveau de sécurité
maximal (générateur de code tiers et au moins deux autorités de
déchiffrement extérieures).
Pendant et après l'élection, l'auditeur a accès aux fichiers suivants (depuis
la page d'accueil de l'élection) :
* `election.json`: paramètres de l'élection;
* `trustees.json`: clés de vérification des autorités de
déchiffrement ainsi que leurs clés publiques en mode threshold;
* `public_creds.txt`: parties publiques des codes de vote;
* `ballots.jsons`: bulletins acceptés.
Pendant l'élection, il est attendu que l'auditeur :
- vérifie que le nombre d'électeurs affiché est conforme et que l'empreinte de la liste
électorale correspond à la liste électorale (s'il a
accès à ces informations). Cf instructions pour la commission
électorale.
- vérifie que le nombre d'électeurs est égal au nombre de codes de
vote publics dans `public_creds.txt`.
- vérifie que le fichier des parties publiques des codes de vote
`public_creds.txt` correspond à l'empreinte des codes de vote
affichée sur la page d'accueil, par exemple en utilisant l'une des commandes suggérées [ici](#hash).
- vérifie la cohérence de l'urne. En copiant les 4 fichiers listés
ci-dessus dans un répertoire `/path/to/election`, la commande suivante
exécute toutes les vérifications nécessaires :
belenios-tool verify --dir /path/to/election
- vérifie que l'urne évolue de façon cohérente : aucun bulletin ne
disparait à moins qu'il soit remplacé par un bulletin provenant du
même code de vote (cas de revote d'un électeur). Pour cela, télécharger
de nouveau les fichiers dans un autre répertoire `/path/to/election/new`
et lancer la commande :
belenios-tool verify-diff --dir1 /path/to/election --dir2 /path/to/election/new
- vérifie que la page web servie aux électeurs pour voter ainsi que
ses ressources (images, style css, code JavaScript) ne change pas.
Le code doit correspondre à celui obtenu après
compilation des sources de Belenios. Le programme
`contrib/check_hash.py` fourni dans les sources fait ceci
automatiquement:
contrib/check_hash.py --url https://url/to/server
Notons que l'url est celle du serveur et non celle de l'élection ; par
exemple `--url https://belenios.loria.fr`.
Après l'élection, l'auditeur a également accès au fichier
`result.json`. Il est attendu que l'auditeur :
- exécute à nouveau les deux vérifications expliquées ci-dessus pour
s'assurer de la cohérence de l'urne finale et de sa cohérence
vis-à-vis du dernier enregistrement de l'urne par l'auditeur.
- vérifie que le résultat indiqué dans le fichier `result.json`
correspond au résultat affiché sur la page d'accueil de l'élection.
Actuellement, cette vérification doit être faite manuellement.
- vérifie que les données de contrôle publiées dans ces fichiers
correspondent à ce qui est affiché sur la page d'accueil de
l'élection et lu par les électeurs et les autres acteurs de
l'élection.
Pour ce dernier point ainsi que toutes les autres tâches de l'auditeur
(sauf la vérification du résultat affichée),
un outil est fourni dans les sources de Belenios. Cet outil suppose que
`belenios-tool` soit compilé et installé. Ensuite, l'auditeur doit créer
un dossier `workdir` où seront stockées les informations d'audit d'une
élection au fur et à mesure des téléchargements sous forme d'un dépôt
`git`. Pendant toute la phase d'audit, il faut lancer régulièrement la
commande :
contrib/monitor_elections.py --uuid --url https://url/to/server --wdir
ce qui télécharge les données, les vérifie, et les compare avec les
données précédemment téléchargées.
Il est possible de rediriger les messages avec l'option `--logfile`.
Alors, seules les anomalies seront rapportées sur `stdout/stderr`, ce qui
permet de lancer la commande depuis un `crontab` et d'être alerté en cas
de problème.
Note : Si l'outil en ligne de commande `belenios-tool` est utilisé, la
confiance dans les tests effectués repose en partie dans la confiance
en l'outil. Il est possible d'implémenter son propre logiciel de
vérification à partir des spécifications de Belenios, disponibles [ici](https://www.belenios.org/specification.pdf).
Instructions pour l'administrateur de l'élection
-----------------------------------------
Cela peut sembler curieux mais l'administrateur de l'élection a peu de
vérifications à effectuer. Cela s'explique par le fait que le système
de vote Belenios est conçu de manière à être sûr sans faire confiance
à l'administrateur de l'élection. La sécurité de Belenios repose à
l'inverse sur les vérifications mutuelles des différents acteurs :
autorités de déchiffrement, générateur de code de vote, commission
électorale et auditeur.
Les points importants pour l'administrateur sont les suivants :
- obtenir la liste électorale sous la forme d'une liste d'adresses
mail valides, une adresse par électeur. Dans le cas d'un vote
pondéré, un nombre de voix différent peut être attribué à chaque
électeur. Cette liste doit être
validée par la commission électorale.
- vérifier et revérifier les adresses email rentrées pour les
électeurs avant de lancer l'élection (et même avant l'envoi des
codes en vote en mode automatique). Il n'est pas possible de
modifier ces adresses ensuite et il n'y a pas d'alerte en cas de
problème d'envoi.
- s'assurer que tous les acteurs utilisent bien la même `url` pour
l'élection.
- si l'administrateur de l'élection n'a pas chargé une
personne de générer les codes de vote, il doit penser à télécharger
la liste des codes de vote (`Télécharger les parties privées des codes de vote`) pour
pouvoir renvoyer son code de vote à un électeur qui l'aurait
perdu. D'un point de vue sécurité, il est cependant préférable de
déléguer la génération des codes de vote à une tierce personne.
Pour un niveau de sécurité maximal, l'administrateur de l'élection
doit disposer :
- d'une personne en charge de générer les codes de vote et les envoyer par mail
aux électeurs (par défaut, c'est fait par la plateforme de vote, ce
qui prête plus le flanc à une attaque de type bourrage d'urne).
- de plusieurs autorités de déchiffrement en charge de protéger le
secret du vote : il faut les attaquer toutes (ou un quorum d'entre
elles) pour être capable de déchiffrer individuellement les bulletins.
Comment calculer l'empreinte d'un fichier ?
---------------------------------------------------------
Pour calculer l'empreinte d'un fichier, vous devez utiliser la même
fonction de hachage que celle utilisée dans Belenios. Nous proposons
ici plusieurs solutions pour calculer cette empreinte en ligne de
commande. Nous utilisons le fichier `voters.txt` en exemple mais vous
pouvez bien sûr le remplacer par un autre fichier.
sha256sum voters.txt | xxd -p -r | base64 | tr -d "="
(ou bien `shasum -a256` au lieu de `sha256sum` par exemple sur
MacOS)
ou encore :
cat voters.txt | python3 -c "import hashlib,base64,sys;m=hashlib.sha256();m.update(sys.stdin.read().encode());print(base64.b64encode(m.digest()).decode().strip('='))"
Vous pouvez également utiliser [l'outil en ligne](https://belenios.loria.fr/compute-fingerprint) mis à disposition par
Belenios.
belenios-1.17/doc/load_testing.md 0000644 0006501 0606723 00000007411 14115601117 015644 0 ustar glondu sed # Load testing
To set up a load test that will make fake users submit encrypted ballot files, you need to generate these ballot files in advance. A script which does this automatically is available at `tests/selenium/load_testing_set_up.py`. Here is an example of usage.
## Set up a load test
### Compile Belenios with debug flag
You need to compile `belenios-tool` with `BELENIOS_DEBUG=1` environment variable, and execute `belenios-tool` with `BELENIOS_USE_URANDOM=1` environment variable, in order to speed up generation of a high amount of ballots files. In these conditions, `belenios-tool` generates random numbers using `/dev/urandom` instead of `/dev/random`, which is much faster and reduces a lot the probability to exhaust the random numbers pool, which would lead to timeout errors when executing `belenios-tool` automatically (using `tests/selenium/load_testing_set_up.py`).
```
make clean
BELENIOS_DEBUG=1 make all
```
### Start the server
If the Application Under Test is your local Belenios server:
In a local terminal, start your Belenios server:
```
BELENIOS_SENDMAIL=tests/selenium/tools/sendmail_fake_to_static.sh demo/run-server.sh
```
Else (the Application Under Test is a distant Belenios server):
SSH into the server machine and start the Belenios server by executing the same kind of command, making sure that the fake sendmail command writes into a text file that is publicly accessible by a URL.
Make sure you have an administrator user account on this Belenios server, so that you can create an election.
### Execute the script that creates the election and generates ballot files and the aggregated votes CSV file
In another local terminal, run:
```
cd /path/to/belenios/git/repository
source venv/bin/activate
```
If the Application Under Test is your local Belenios server:
Adapt and execute the script:
````
BELENIOS_USE_URANDOM=1 USE_HEADLESS_BROWSER=0 SENT_EMAILS_TEXT_FILE_ABSOLUTE_PATH=/path/to/_build/src/static/mail.txt FAKE_SENT_EMAILS_FILE_RELATIVE_URL=static/mail.txt FAKE_SENDMAIL_EXECUTABLE_FILE_PATH_RELATIVE_TO_GIT_REPOSITORY=tests/selenium/tools/sendmail_fake_to_static.sh NUMBER_OF_INVITED_VOTERS=500 NUMBER_OF_VOTING_VOTERS=250 python ./tests/selenium/load_testing_set_up.py
````
Else (the Application Under Test is a distant Belenios server):
Adapt and execute the script:
```
SERVER_URL=https://belenios2.lhs.loria.fr BELENIOS_USE_URANDOM=1 USE_HEADLESS_BROWSER=0 LOGIN_MODE=public ADMINISTRATOR_USERNAME=your_belenios_administrator_user_login ADMINISTRATOR_PASSWORD=your_belenios_administrator_user_password SENT_EMAILS_TEXT_FILE_ABSOLUTE_PATH=/path/to/belenios/git/repository/_build/src/static/mail.txt FAKE_SENT_EMAILS_FILE_RELATIVE_URL=mail/mails.txt FAKE_SENDMAIL_EXECUTABLE_FILE_PATH_RELATIVE_TO_GIT_REPOSITORY=tests/selenium/tools/sendmail_fake_to_static.sh NUMBER_OF_INVITED_VOTERS=500 NUMBER_OF_VOTING_VOTERS=250 python ./tests/selenium/load_testing_set_up.py
```
This prints in the output several things including the ID of the election. Copy it for later. This also creates files `voter_row_{i}_crypted_ballot.json`, `voter_row_{i}_privcred.txt`, `voter_row_{i}_uncrypted_ballot.json` for each voter `{i}`. This also creates file `all_votes.csv`.
## Execute the load test with a low amount of virtual users
You can now open JMeter, load the load testing script, set its configuration parameters (server URL and election ID), and execute the load test and check that everything works as expected.
## Execute a real load test by increasing the number of virtual users
Now, you can run the redo the whole process with a higher number of voters (change the value of `NUMBER_OF_INVITED_VOTERS` and `NUMBER_OF_VOTING_VOTERS` when executing the python script). When executing a real load test, it is recommended to execute the CLI version of Jmeter instead of its GUI.
belenios-1.17/doc/nspawn/ 0000755 0006501 0606723 00000000000 14115601117 014151 5 ustar glondu sed belenios-1.17/doc/nspawn/README.md 0000644 0006501 0606723 00000010371 14115601117 015432 0 ustar glondu sed Deploying Belenios using systemd-nspawn
=======================================
Introduction
------------
This file documents a technique with a (relatively) small runtime
footprint on the deployment server. With this technique, the
"development" and the "deployment" environments are distinguished and
can be on different machines. The benefit is that the development
environment takes more than 3.5 GB while the deployment environment
takes a few hundreds MB. Several instances can be deployed on the same
machine. It is expected that these instances listen on localhost, and
that a reverse-proxy on the host exposes them on different vhosts or
directories of some vhost, with TLS. Configuring the reverse-proxy is
out of the scope of this document.
This technique requires Linux and root privileges. If you don't trust
something, it is suggested to use a virtual machine.
Building the development environment
------------------------------------
This requires `debootstrap` and `systemd-nspawn` (provided by the
`systemd-container` package in Debian).
You must also enable and start `systemd-resolved` on the host, with
the following commands:
systemctl enable systemd-resolved
systemctl start systemd-resolved
The `belenios-stage1.sh` script (which must be run as root) takes a
directory as argument and bootstraps in it a Debian distribution with
the appropriate prerequisites, and runs `opam-bootstrap.sh` inside it.
One can log into the resulting environment with the command:
systemd-nspawn --directory=/path/to/development/environment --user=belenios
To work on Belenios inside this environment, one must source the
`/home/belenios/opam-env.sh` file. One can then clone Belenios sources
(they are expected in `/home/belenios/belenios`), and build them using
`make build-release-server` (or `make build-debug-server`).
Building the deployment environment
-----------------------------------
The deployment environment must be built from inside the development
environment created above. It is a squashfs image of a root file
system.
The image is built with the `belenios-stage3.sh` script, which creates
`/target/rootfs.squashfs`.
To run the script from the host, use the following command:
systemd-nspawn --directory=/path/to/development/environment /home/belenios/belenios/doc/nspawn/belenios-stage3.sh
Deploying
---------
We assume the deployment server runs Linux, systemd and has
`systemd-nspawn` installed. We also assume that `systemd-resolved` is
running, and an MTA is installed and listening on localhost.
When deploying on a server for the first time, run:
* `mkdir /srv/belenios-containers`
* `cp belenios-nspawn /srv/belenios-containers`
* `cp belenios-container@.service /etc/systemd/system`
You might want to update `belenios-nspawn` and
`belenios-container@.service` as Belenios evolves.
To deploy an instance named `main`:
* create a directory `/srv/belenios-containers/main`
* copy there `rootfs.squashfs`
* create a `belenios` sub-directory belonging to user 1000 and
group 1000
* create there an `ocsigenserver.conf.in` file (you can take the one
in Belenios sources as example)
Beware, the `belenios` directory and its contents must belong to user
and group 1000, which correspond to user and group `belenios` inside
the deployment environment, but may appear as different names on the
host.
You can then run the instance with:
systemctl start belenios-container@main.service
You should now be able to browse to your instance!
Of course, you can run other `systemctl` commands such as `status`,
`enable`, etc.
Troubleshooting
---------------
### Cannot connect to Belenios web server
You can open a shell inside the container with (replace `main` with
the name of your instance):
machinectl shell belenios-main
The systemd unit running the web server (inside the container) is
called `belenios.service` so you can, for example, run:
systemctl status belenios
to see if the unit is running and debug it if needed. You can also use
`journalctl` or read Belenios-specific logs in `/var/belenios/log`.
### Mails are not delivered
Be sure the MTA on the host works properly. Its logs can be helpful in
debugging mail delivery problems.
Usually, the `return-path` setting in `ocsigenserver.conf.in` must be
set to a valid e-mail address.
belenios-1.17/doc/nspawn/belenios-container@.service 0000644 0006501 0606723 00000001222 14115601117 021410 0 ustar glondu sed [Unit]
Description=Belenios Container %i
Wants=modprobe@loop.service
PartOf=machines.target
Before=machines.target
After=network.target systemd-resolved.service modprobe@loop.service
RequiresMountsFor=/srv
[Service]
ExecStart=/srv/belenios-containers/belenios-nspawn /srv/belenios-containers/%i --quiet --keep-unit --boot --link-journal=try-guest
KillMode=mixed
Type=notify
RestartForceExitStatus=133
SuccessExitStatus=133
Slice=machine.slice
Delegate=yes
TasksMax=16384
WatchdogSec=3min
DevicePolicy=closed
DeviceAllow=char-pts rw
DeviceAllow=/dev/loop-control rw
DeviceAllow=block-loop rw
DeviceAllow=block-blkext rw
[Install]
WantedBy=machines.target
belenios-1.17/doc/nspawn/belenios-nspawn 0000755 0006501 0606723 00000000276 14115601117 017210 0 ustar glondu sed #!/bin/sh
set -e
DIR="$1"
shift
NAME="${DIR##*/}"
exec systemd-nspawn --image=$DIR/rootfs.squashfs --overlay=+/var::/var --bind=$DIR/belenios:/var/belenios --machine=belenios-$NAME "$@"
belenios-1.17/doc/nspawn/belenios-stage1.sh 0000755 0006501 0606723 00000001217 14115601117 017473 0 ustar glondu sed #!/bin/sh
set -e
SRC="$(dirname $0)"
DIR="$1"
if [ -e "$DIR" ]; then
if [ -d "$DIR" ]; then
FILES=$(ls -1qAH -- "$DIR")
if ! [ -z "$FILES" ]; then
echo "$DIR is not an empty directory!"
exit 1
fi
else
echo "$DIR exists and is not a directory!"
fi
fi
echo "Debootstrapping..."
debootstrap --merged-usr --variant=buildd buster "$DIR"
echo 'APT::Install-Recommends "false";' >> "$DIR/etc/apt/apt.conf"
cp "$SRC/belenios-stage2.sh" "$DIR"
cp "$SRC/../../opam-bootstrap.sh" "$DIR"
systemd-nspawn --directory="$DIR" /belenios-stage2.sh
rm -f "$DIR/belenios-stage2.sh" "$DIR/opam-bootstrap.sh"
belenios-1.17/doc/nspawn/belenios-stage2.sh 0000755 0006501 0606723 00000001154 14115601117 017474 0 ustar glondu sed #!/bin/sh
set -e
echo "Installing Debian prerequisites..."
apt-get update -qq && apt-get upgrade -qq
apt-get install -qq netbase build-essential libgmp-dev libpcre3-dev pkg-config m4 libssl-dev libsqlite3-dev wget ca-certificates zip unzip libncurses-dev zlib1g-dev libgd-securityimage-perl cracklib-runtime git jq npm rsync debootstrap squashfs-tools
apt-get clean
echo "Running opam-bootstrap.sh..."
useradd --create-home belenios
su - belenios -c "env BELENIOS_OPAM_INIT_ARGS=--disable-sandboxing /opam-bootstrap.sh"
su - belenios -c "head -n2 env.sh > opam-env.sh && . ./opam-env.sh && opam env >> opam-env.sh"
belenios-1.17/doc/nspawn/belenios-stage3.sh 0000755 0006501 0606723 00000006402 14115601117 017476 0 ustar glondu sed #!/bin/sh
set -e
DIR=/target
if [ -e "$DIR" ]; then
if [ -d "$DIR" ]; then
FILES=$(ls -1qAH -- "$DIR")
if ! [ -z "$FILES" ]; then
echo "$DIR is not an empty directory!"
exit 1
fi
else
echo "$DIR exists and is not a directory!"
fi
fi
if ! [ -d "/home/belenios" ]; then
echo "This script must be run from a root created with belenios-stage1.sh!"
exit 1
fi
mkdir -p "$DIR"
cd /
echo "Debootstrapping..."
debootstrap --merged-usr --variant=minbase --include=systemd,dbus buster "$DIR/rootfs"
echo 'APT::Install-Recommends "false";' >> "$DIR/rootfs/etc/apt/apt.conf"
ln -sfT /usr/lib/systemd/resolv.conf "$DIR/rootfs/etc/resolv.conf"
echo belenios > "$DIR/rootfs/etc/hostname"
cat > "$DIR/rootfs/etc/hosts" < "$DIR/rootfs/etc/logrotate.d/belenios" < /tmp/belenios/ocsigenserver_command
endscript
}
EOF
cat > "$DIR/rootfs/home/belenios/belenios-env.sh" < "$DIR/rootfs/etc/systemd/system/belenios.service" <