Skip to content

Commits on Source 2

*~
_build
_run
venv
__pycache__
geckodriver.log
env.sh
stages:
- build_and_run_automated_test_scenarios_with_preinstalled_image
- build_and_test_with_preinstalled_image
- build_and_test_with_debian_image
build_and_test_with_preinstalled_image:
stage: build_and_test_with_preinstalled_image
# Image glondu/beleniosbase:YYYYMMDD-N is built by Dockerfile_base_environment
image: glondu/beleniosbase:20181206-2
script:
# Initialize environment
- source ~/env.sh
# Compile belenios
- BELENIOS_DEBUG=1 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</a>' | wc -l)" != "1" ]; then echo "[First page access] First page access does not show a single '>Belenios</a>' text, but it should" && exit 1; else echo "[First page access] First page access shows a single '>Belenios</a>' text, as expected"; fi
# Run a test of an election
- BELENIOS_DEBUG=1 make check
build_and_test_with_debian_image:
stage: build_and_test_with_debian_image
# Image ocaml/opam2:debian-9-ocaml-4.06 currently has ocaml version 4.06.1 and opam version 2.0.0 (whereas image ocaml/opam2:debian-9 currently has ocaml version 4.07.0)
image: ocaml/opam2:debian-9-ocaml-4.06
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 aspcud libncurses-dev uuid-runtime zlib1g-dev
# Install the same Opam packages that opam-bootstrap.sh installs
- eval `grep "opam install" ./opam-bootstrap.sh`
# Compile belenios
- BELENIOS_DEBUG=1 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</a>' | wc -l)" != "1" ]; then echo "[First page access] First page access does not show a single '>Belenios</a>' text, but it should" && exit 1; else echo "[First page access] First page access shows a single '>Belenios</a>' text, as expected"; fi
# Run a test of an election
# - BELENIOS_DEBUG=1 make check
build_and_run_automated_test_scenarios_with_preinstalled_image:
stage: build_and_run_automated_test_scenarios_with_preinstalled_image
# Image glondu/beleniosbase-tests:YYYYMMDD-N is built by Dockerfile_test_scenario_environment
image: glondu/beleniosbase-tests:20181206-2
script:
# Initialize environment
- source ~/env.sh
# Compile belenios
- BELENIOS_DEBUG=1 make all
# Create a bundled version of belenios (this produces a belenios.tar.gz file, which is needed by the web server)
- make archive
# We don't need to start belenios web server, as it will be started by the automated test
# - ./demo/run-server.sh &
# Create a Python 3 virtual environment, where pip packages will be installed
- python3 -m venv venv
# Step into this new virtual environment
- source venv/bin/activate
# Install pip packages
- pip install -r requirements.txt
# Run the automated test scenario 2
- python3 ./tests/test_scenario_2.py
# Run the automated test scenario 1
- python3 ./tests/test_scenario_1.py
wrap-build-commands: []
wrap-install-commands: []
wrap-remove-commands: []
required-tools: []
1.8 (2018-02-04)
================
* Add the possibility to override sendmail via an environment variable
* Use SOURCE_DATE_EPOCH if available
* Use opam 2.0.0 in bootstrap script
* Web server:
+ Add some automated tests
+ Add the possibility to create administrator accounts
+ Add booth preview
+ Add automatic open / close dates
+ Unhide support for threshold decryption
+ Fixed a bug that caused some elections to not appear in the
administrator's listing when the election pool is big
+ Force the server to be a trustee in basic mode
+ Record in trustee public keys whether the server has the private key
1.7.1 (2018-12-05)
==================
* Do not output spurious empty lines in records file (bugfix: voting
records and missing voters were not working)
* More explicit checklist in election validation page
* Avoid sending password/credential emails when name has not been
edited
* Avoid hidden parameters in some services that are meant to be usable
from non-web clients
1.7 (2018-11-26)
================
* Add automatic data archival/deletion policy
* Do not allow election validation if some items have not been edited
* Trustees can load their private key from a file
* Do no longer rely on Ocsipersist
* Port to OCaml 4.06.1 and Eliom 6.3.0
* Re-seed LwtRandom prng every 30 minutes
* Add a placeholder for warnings/announcements
1.6 (2018-06-13)
================
......
FROM debian:9
RUN apt-get update -qq && apt-get upgrade -qq && apt-get install -qq bubblewrap build-essential libgmp-dev libpcre3-dev pkg-config m4 libssl-dev libsqlite3-dev wget ca-certificates zip unzip aspcud libncurses-dev uuid-runtime zlib1g-dev libgd-securityimage-perl cracklib-runtime git
RUN useradd --create-home belenios
COPY .opamrc-nosandbox /home/belenios/.opamrc
COPY opam-bootstrap.sh /home/belenios
USER belenios
WORKDIR /home/belenios
RUN ./opam-bootstrap.sh
# Docker image used as FROM has been generated from ./Dockerfile_base_environment
FROM glondu/beleniosbase:20181206-2
USER root
# Install firefox-esr (The firefox package is not present in apt repositories of this linux image, so we use firefox-esr)
RUN apt-get install -y -qq firefox-esr
# Install geckodriver (It is needed by selenium to run firefox)
ENV GECKODRIVER_VERSION 0.18.0
RUN 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 && tar -C /opt -zxf /tmp/geckodriver.tar.gz && rm /tmp/geckodriver.tar.gz && mv /opt/geckodriver /opt/geckodriver-$GECKODRIVER_VERSION && chmod 755 /opt/geckodriver-$GECKODRIVER_VERSION && ln -fs /opt/geckodriver-$GECKODRIVER_VERSION /usr/bin/geckodriver
# Install packages required to run the test scenario
RUN apt-get install -y -qq python3 python3-venv
USER belenios
......@@ -13,6 +13,7 @@ OCaml projects.
The non-OCaml prerequisites are:
* a POSIX system with a C compiler
* on Linux, [Bubblewrap](https://github.com/projectatomic/bubblewrap)
* [GMP](http://gmplib.org/)
* [PCRE](http://www.pcre.org/)
* [pkg-config](http://www.freedesktop.org/wiki/Software/pkg-config/)
......@@ -20,16 +21,19 @@ The non-OCaml prerequisites are:
* [SQLite3](https://www.sqlite.org/)
* [OpenSSL](https://www.openssl.org/)
* [Wget](https://www.gnu.org/software/wget/) or [curl](http://curl.haxx.se/)
* [Zip](http://www.info-zip.org/Zip.html)
* [Unzip](http://www.info-zip.org/UnZip.html)
* [aspcud](http://www.cs.uni-potsdam.de/wv/aspcud/) (optional)
* [ncurses](http://invisible-island.net/ncurses/)
* [uuidgen](https://www.kernel.org/pub/linux/utils/util-linux/)
* [GD-SecurityImage](https://metacpan.org/release/GD-SecurityImage)
* [cracklib](http://sourceforge.net/projects/cracklib)
These libraries and tools are pretty common, and might be directly part
of your operating system. On [Debian](http://www.debian.org/) and its
derivatives, they can be installed with the following command:
sudo apt install build-essential libgmp-dev libpcre3-dev pkg-config m4 libssl-dev libsqlite3-dev wget ca-certificates unzip aspcud libncurses-dev uuid-runtime zlib1g-dev
sudo apt install bubblewrap build-essential libgmp-dev libpcre3-dev pkg-config m4 libssl-dev libsqlite3-dev wget ca-certificates zip unzip aspcud libncurses-dev uuid-runtime zlib1g-dev libgd-securityimage-perl cracklib-runtime
If you are unfamiliar with OCaml or OPAM, we provide an
`opam-bootstrap.sh` shell script that creates a whole, hopefully
......@@ -40,8 +44,8 @@ variable, or it will take `~/.belenios` by default. Just run:
./opam-bootstrap.sh
On a modern desktop system, this needs approximately 10 minutes and 1
gigabyte of disk space.
On a modern desktop system, this needs approximately 20 minutes and 2.6
gigabytes of disk space.
If everything goes successfully, follow the given instructions to
update your shell environment, then run:
......@@ -130,25 +134,13 @@ Additionnaly, you will need LaTeX to compile the specification.
On Debian-based systems, you can install the dependencies needed to
compile the documentation with:
sudo apt-get install markdown texlive
sudo apt install markdown texlive
Once all the dependencies have been installed, the documentation can
be compiled with:
make doc
Compilation using only official Debian packages
-----------------------------------------------
At the time of writing (07 Dec 2017), you need the stable version (stretch)
of Debian (or Ubuntu) to be able to compile Belenios using only
official Debian packages. On Ubuntu, you need to enable the "Universe"
repository. Instead of using OPAM, the dependencies of Belenios can
then be installed with:
sudo apt install libatdgen-ocaml-dev libzarith-ocaml-dev libcryptokit-ocaml-dev libuuidm-ocaml-dev libcalendar-ocaml-dev libcmdliner-ocaml-dev
sudo apt install ocsigenserver eliom libcsv-ocaml-dev
Compiling on Windows using Cygwin
---------------------------------
......@@ -188,14 +180,6 @@ tool_ section above.
Troubleshooting
---------------
### OCamlDuce incompatibility
OCamlDuce is an optional transitive dependency of Belenios, but
Belenios does not use it. If OCamlDuce was installed outside of OPAM
(e.g. via your system package manager), you may face issues. You can
work around them by uninstalling OCamlDuce and restarting the
installation procedure.
### Missing sources
The instructions outlined in this document and in the
......
1.7
===
* To upgrade a web server running version 1.6, you need to delete the
Ocsipersist store (by default the `ocsidb` file referred in the
configuration file). This will archive all validated elections, and
delete all draft elections. Additionally, you should clean up the
data directory (the one referred in the `<spool>` directive in the
configuration file) by removing all temporary files (run `rm *.*`
in this directory) and private keys (`rm */private_key*.json*`).
1.1
===
......
<**/*.{ml,mli,byte,native,odoc}>: debug, annot, package(uuidm), package(atdgen), package(yojson)
<src/lib/{platform,belenios_version}.mli>: opaque
<src/platform/native/*>: package(zarith), package(cryptokit)
<src/web/*.{ml,mli,byte,native,odoc}>: thread, package(eliom.server), package(lwt.ppx), package(calendar), package(csv)
<src/tool/tool_cmdline.*>: package(zarith), package(cryptokit), package(cmdliner), use_platform-native
<src/tool/tool_js*> or <src/platform/js/*> or <src/booth/*>: package(js_of_ocaml), syntax(camlp4o), package(js_of_ocaml.syntax), package(lwt.syntax), use_platform-js
<src/tool/tool_js*> or <src/platform/js/*>: package(js_of_ocaml-lwt), package(js_of_ocaml-ppx), package(lwt.ppx), use_platform-js
<**/*serializable_j.ml>: warn(-32)
true: warn(A-4-6-29-44-48), safe_string
true: warn(A-4-6-29-44-45-48), safe_string
<venv>: not_hygienic
......@@ -95,7 +95,7 @@ head -n2 partial_decryptions.tmp > partial_decryptions.jsons
header "Finalize tally"
belenios-tool finalize
belenios-tool validate
header "Perform final verification"
......
......@@ -82,7 +82,7 @@ mv partial_decryptions.tmp partial_decryptions.jsons
header "Finalize tally"
belenios-tool finalize
belenios-tool validate
header "Perform final verification"
......
......@@ -30,12 +30,16 @@
<extension findlib-package="uuidm"/>
<extension findlib-package="atdgen"/>
<extension findlib-package="csv"/>
<extension findlib-package="calendar"/>
<host charset="utf-8" hostfilter="*" defaulthostname="localhost">
<!-- <redirect suburl="^$" dest="http://www.example.org"/> -->
<site path="static" charset="utf-8">
<static dir="_SRCDIR_/_build/src/static" />
</site>
<site path="monitor">
<eliom findlib-package="eliom.server.monitor.start"/>
</site>
<eliom module="_build/src/web/server.cma">
<maxmailsatonce value="1000"/>
<uuid length="14"/>
......@@ -44,11 +48,13 @@
<server mail="noreply@example.org"/>
<auth name="demo"><dummy/></auth>
<auth name="local"><password db="demo/password_db.csv"/></auth>
<auth name="public"><password db="_RUNDIR_/password_db.csv" allowsignups="true"/></auth>
<!-- <auth name="google"><oidc server="https://accounts.google.com" client_id="client-id" client_secret="client-secret"/></auth> -->
<source file="../belenios.tar.gz"/>
<default-group file="demo/groups/default.json"/>
<log file="_RUNDIR_/log/security.log"/>
<spool dir="_RUNDIR_/spool"/>
<warning file="demo/warning.html"/>
</eliom>
</host>
......
......@@ -17,6 +17,8 @@ mkdir -p \
$BELENIOS_RUNDIR/spool \
$BELENIOS_TMPDIR/run
touch $BELENIOS_RUNDIR/password_db.csv
sed \
-e "s@_OCAML_STDLIBDIR_@$OCAML_STDLIBDIR@g" \
-e "s@_TMPDIR_@$BELENIOS_TMPDIR@g" \
......
<div style="background: yellow;">This is the development version!</div>
# 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 aspcud libncurses-dev uuid-runtime 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 aspcud libncurses-dev uuid-runtime 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</a>' | wc -l)" != "1" ]; then echo "[First page access] First page access does not show a single '>Belenios</a>' text, but it should" && exit 1; else echo "[First page access] First page access shows a single '>Belenios</a>' 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 un 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 som 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 an 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 aspcud libncurses-dev uuid-runtime 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</a>' | wc -l)" != "1" ]; then echo "[First page access] First page access does not show a single '>Belenios</a>' text, but it should" && exit 1; else echo "[First page access] First page access shows a single '>Belenios</a>' 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 aspcud libncurses-dev uuid-runtime zlib1g-dev
$ opam install --yes atdgen zarith cryptokit uuidm calendar cmdliner sqlite3 eliom=6.3.0 csv
$ exit
$ docker container ls -a
$ docker container commit <CONTAINER_ID>
# Exemple : docker container commit fe173ea3829c
$ docker image ls
$ docker image tag <IMAGE_ID> <THE_IMAGE_NAME_YOU_WANT>
# Exemple : docker image tag 04bf023e658e beleniosbasewithopamdependencies2
$ docker commit <CONTAINER_ID> <YOUR_DOCKERHUB_USERNAME>/<YOUR_DOCKERHUB_REPOSITORY_NAME>:<TAG_NAME>
# Exemple : docker commit fe173ea3829c swergas/beleniosbase:efa5df3049f736dd34eb8289da730dd709eb99939f6511fa93ae0080a61ce4fb
# Le résultat de la commande est de la forme 'sha256:c3b4dd5f41f071409d6f45f069f5bef7b4eb236d2bfbb457c0be89ae1f8a4139'
$ docker push <hub-user>/<repo-name>:<tag>
# 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 aspcud libncurses-dev uuid-runtime 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</a>' | wc -l)" != "1" ]; then echo "[First page access] First page access does not show a single '>Belenios</a>' text, but it should" && exit 1; else echo "[First page access] First page access shows a single '>Belenios</a>' 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 7 min 31 sec à 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 containe 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 |
# 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. We us it in a virtual environment
- `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
- `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/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
BELENIOS_DEBUG=1 make all
make archive
python3 -m venv venv
source venv/bin/activate
pip install -r requirements.txt
python ./tests/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, step into it, install 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
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/test_scenario_1.py
```
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 "Return to draft page" 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`)
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 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 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 "Return to draft page" 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`)
......@@ -7,7 +7,7 @@
\usepackage{bbm}
\usepackage{hyperref}
\newcommand{\version}{1.5}
\newcommand{\version}{1.6}
\newcommand{\F}{\mathbbm{F}}
\newcommand{\G}{\mathbbm{G}}
......@@ -61,6 +61,20 @@ section~\ref{default-group}.
\section{Parties}
\newcommand{\pk}{\texttt{public\_key}}
\newcommand{\sk}{\texttt{private\_key}}
\newcommand{\proof}{\texttt{proof}}
\newcommand{\iproof}{\texttt{iproof}}
\newcommand{\ciphertext}{\texttt{ciphertext}}
\newcommand{\pklabel}{\textsf{public\_key}}
\newcommand{\pok}{\textsf{pok}}
\newcommand{\challenge}{\textsf{challenge}}
\newcommand{\response}{\textsf{response}}
\newcommand{\alphalabel}{\textsf{alpha}}
\newcommand{\betalabel}{\textsf{beta}}
\newcommand{\Hash}{\mathcal{H}}
\begin{itemize}
\item $\mathcal{S}$: voting server
\item $\mathcal{A}$: server administrator
......@@ -106,7 +120,10 @@ partial decryption.
\item $\mathcal{A}$ checks $k_z$
\end{enumerate}
\item $\mathcal{A}$ combines all the trustee public keys into the election
public key $y$
public key $y$:
\[
y=\prod_{z\in[1\dots m]}\pklabel(k_z)
\]
\end{enumerate}
\subsubsection{Threshold decryption support}
......@@ -142,6 +159,8 @@ a partial decryption.
\end{enumerate}
\item $\mathcal{A}$ extracts encrypted decryption keys $K_1,\dots,K_m$ and
\hyperref[threshold-params]{threshold parameters}
\item $\mathcal{A}$ computes the election public key $y$ as specified
in section~\ref{polynomials}.
\end{enumerate}
\subsection{Vote}
......@@ -195,7 +214,10 @@ $\textsf{field}(o)$ to access the field \textsf{field} of $o$.
\begin{itemize}
\item $\jstring$: JSON string
\item $\uuid$: UUID (see RFC 4122), encoded as a JSON string
\item $\uuid$: UUID (either as defined in RFC 4122, or a string of
Base58 characters\footnote{Base58 characters are:
\texttt{123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz}}
of size at least 14), encoded as a JSON string
\item $\I$: small integer, encoded as a JSON number
\item $\B$: boolean, encoded as a JSON boolean
\item $\N$, $\Z_q$, $\G$: big integer, written in base 10 and encoded as a
......@@ -205,20 +227,6 @@ $\textsf{field}(o)$ to access the field \textsf{field} of $o$.
\subsection{Common structures}
\label{common}
\newcommand{\pk}{\texttt{public\_key}}
\newcommand{\sk}{\texttt{private\_key}}
\newcommand{\proof}{\texttt{proof}}
\newcommand{\iproof}{\texttt{iproof}}
\newcommand{\ciphertext}{\texttt{ciphertext}}
\newcommand{\pklabel}{\textsf{public\_key}}
\newcommand{\pok}{\textsf{pok}}
\newcommand{\challenge}{\textsf{challenge}}
\newcommand{\response}{\textsf{response}}
\newcommand{\alphalabel}{\textsf{alpha}}
\newcommand{\betalabel}{\textsf{beta}}
\newcommand{\Hash}{\mathcal{H}}
\begin{gather*}
\proof=\left\{
\begin{array}{rcl}
......@@ -463,6 +471,11 @@ $s_{ij}=f_i(j)\mod q$ for $j=1,\dotsc,m$. $\mathcal{T}_i$ then fills the
filled with $A_{i0},\dotsc,A_{it}$
\end{itemize}
The public key of the election will be:
\[
y=\prod_{z\in[1\dots m]}g^{f_z(0)}=\prod_{z\in[1\dots m]}A_{z0}
\]
\subsubsection{Vinputs}
\label{vinputs}
......@@ -576,7 +589,8 @@ PBKDF2 (RFC 2898) with:
\begin{itemize}
\item $c$ as password;
\item HMAC-SHA256 (RFC 2104, FIPS PUB 180-2) as pseudorandom function;
\item the $\uuid$ (interpreted as a 16-byte array) of the election as
\item the $\uuid$ (either interpreted as a 16-byte array in the RFC
4122 case, or directly itself in the Base58 case) of the election as
salt;
\item $1000$ iterations
\end{itemize}
......