belenios-1.17/0000755000650106067230000000000014115601117012076 5ustar glondusedbelenios-1.17/.gitignore0000644000650106067230000000026014115601117014064 0ustar glondused*~ *.bbl *.blg *.dvi *.install .merlin _build _build-debug _releases _run _testdata venv __pycache__ .hypothesis node_modules geckodriver.log env.sh node_modules *.stories.mjs belenios-1.17/.gitlab-ci.yml0000644000650106067230000001623714115601117014543 0ustar glondusedstages: - test - deploy build_and_test_with_preinstalled_image: stage: test # Image glondu/beleniosbase:YYYYMMDD-N is built by Dockerfile_base_environment image: glondu/beleniosbase:20210709-1 script: # Initialize environment - source ~/env.sh # Run command-line tool tests - make check # Compile belenios - make build-release-server # 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 build_and_test_with_debian_image: stage: test image: ocaml/opam:debian-10-ocaml-4.11 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 jq npm rsync # Install the same Opam packages that opam-bootstrap.sh installs - git -C /home/opam/opam-repository pull origin master - opam update - ./fixup-magic-mime.sh - eval `grep "opam install" ./opam-bootstrap.sh` # Compile belenios - make build-debug-tool - make build-release-server # 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 build_and_run_automated_test_scenarios_with_preinstalled_image: stage: test # Image glondu/beleniosbase-tests:YYYYMMDD-N is built by Dockerfile_test_scenario_environment image: glondu/beleniosbase-tests:20210709-1 script: # Initialize environment - source ~/env.sh # Compile belenios - make build-debug-tool - make build-release-server # 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 1 using responsive booth - BOOTH_VERSION=RESPONSIVE_BOOTH python3 ./tests/selenium/test_scenario_1.py # Run the automated test scenario 4 - python3 ./tests/selenium/test_scenario_4.py # Run the automated test scenario 3 - python3 ./tests/selenium/test_scenario_3.py # Run the automated test scenario 2 - python3 ./tests/selenium/test_scenario_2.py # Run the automated test scenario 1 - python3 ./tests/selenium/test_scenario_1.py build_and_run_automated_test_scenarios_with_preinstalled_image_and_prepared_database: stage: deploy rules: - changes: - VERSION image: glondu/beleniosbase-tests:20210709-1 variables: PREPARED_DATABASE_VERSION: v0.01 script: # Initialize environment - source ~/env.sh # Import prepared database - mkdir -p _run/spool - wget https://gitlab.inria.fr/belenios/belenios-prepared-database/-/archive/$PREPARED_DATABASE_VERSION/belenios-prepared-database-$PREPARED_DATABASE_VERSION.tar.gz && tar -zxf belenios-prepared-database-$PREPARED_DATABASE_VERSION.tar.gz && mv belenios-prepared-database-$PREPARED_DATABASE_VERSION/* _run/spool && rm -rf belenios-prepared-database-$PREPARED_DATABASE_VERSION.tar.gz belenios-prepared-database-$PREPARED_DATABASE_VERSION # Compile belenios - make build-debug-tool - make build-release-server # 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 1 using responsive booth - BOOTH_VERSION=RESPONSIVE_BOOTH python3 ./tests/selenium/test_scenario_1.py # Run the automated test scenario 4 - python3 ./tests/selenium/test_scenario_4.py # Run the automated test scenario 3 - python3 ./tests/selenium/test_scenario_3.py # Run the automated test scenario 2 - python3 ./tests/selenium/test_scenario_2.py # Run the automated test scenario 1 - python3 ./tests/selenium/test_scenario_1.py build_and_run_fuzz_tests_and_monkey_tests_with_preinstalled_image: stage: deploy rules: - changes: - VERSION image: glondu/beleniosbase-tests:20210709-1 variables: WAIT_TIME_BETWEEN_EACH_STEP: "0.02" USE_HEADLESS_BROWSER: "1" START_SERVER: "0" BELENIOS_SENDMAIL: "tests/selenium/tools/sendmail_fake_to_static.sh" SENT_EMAILS_TEXT_FILE_RELATIVE_PATH: "_run/usr/share/belenios-server/mail.txt" FAKE_SENT_EMAILS_FILE_RELATIVE_URL: "static/mail.txt" SENT_EMAILS_TEXT_FILE_ABSOLUTE_PATH: "$CI_PROJECT_DIR/$SENT_EMAILS_TEXT_FILE_RELATIVE_PATH" CLEAN_UP_POLICY: "DO_NOTHING" script: # Initialize environment - source ~/env.sh # Compile belenios - make build-debug-tool - make build-release-server # Create the file where sent emails will be stored - touch $SENT_EMAILS_TEXT_FILE_RELATIVE_PATH # Start belenios web server and tell it to redirect sent emails to `mail.txt` (because of environment variable `BELENIOS_SENDMAIL`) - ./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 # Execute fuzz testing of the main login form - LOGIN_MODE=local python ./tests/selenium/test_fuzz_login.py # # Execute fuzz testing of the "Advanced mode" voting process # - python ./tests/selenium/test_fuzz_vote.py # Execute clicker monkey testing - python ./tests/selenium/test_clicker_monkey.py # # Execute smart monkey testing # - python ./tests/selenium/test_smart_monkey.py # # Execute test scenario 2 with a mix of normal voters and smart monkeys # - NUMBER_OF_INVITED_VOTERS=10 NUMBER_OF_VOTING_VOTERS=5 NUMBER_OF_MONKEY_VOTING_VOTERS=3 NUMBER_OF_VOTING_VOTERS_IN_FIRST_PART=1 NUMBER_OF_REVOTING_VOTERS=1 python ./tests/selenium/test_scenario_2_with_monkeys.py belenios-1.17/.opamrc-nosandbox0000644000650106067230000000013614115601117015351 0ustar glondusedwrap-build-commands: [] wrap-install-commands: [] wrap-remove-commands: [] required-tools: [] belenios-1.17/AUTHORS0000644000650106067230000000166614115601117013157 0ustar glondusedMain developer: - Stéphane Glondu Comments, review: - Pierrick Gaudry - Véronique Cortier Continuous integration, web tests: - Quentin Grimaud (swergas) Logo: - Alicia Filipiak CSS: - Vincent Cheval Main contributors to translations: - Agustin Borgna (Spanish) - Alban Bruder (German) - Alfredo Iafolla (Italian) - Allan Nordhøy (Norwegian Bokmål) - Andrii Shachykov (Ukrainian) - Bernardetta Addis (Italian) - Catalin Dragan (Romanian) - Diego Silva (Portuguese (Brazil)) - Enea Milo (Italian) - J. Lavoie - Janne Peltola (Finnish) - Jannik Dreier (German) - Job Doesburg (Dutch) - Mateusz Hołysz (Polish) - Michal Berg (Czech) - Oscar Yanez (Spanish) - Paulo Figueiredo (Portuguese (Brazil)) - Peter Gasparovic (Slovak) - Quentin Pagès (Occitan) - Robson Takeshi Inoue (Portuguese (Brazil)) - Sergiu Bursuc (Romanian) - Sorin Stratulat (Romanian) - Stavros Christoforou (Greek) - Tomáš Hnyk (Czech) - Vincent Laporte (French, Spanish) belenios-1.17/CHANGES.md0000644000650106067230000003524214115601117013476 0ustar glondused1.17 (2021-09-07) ================= * Properly count and ignore blank votes in Condorcet-Schulze counting method * New version of cryptographic operations, fixing many (theoretical) weaknesses + This new version is called v1 and is used for new elections + The previous version, retroactively called v0, is kept for compatibility but will eventually be removed * Web server: + New voting interface for Condorcet votes + Build a belenios-server executable (with static linking of Eliom modules) + Add a notion of account for administrators; do no longer rely on the authentication method to identify them 1.16 (2021-07-19) ================= * Check secret credentials in belenios-tool (GitHub issue #31) * Optimize log computation during decryption * Majority Judgment: compute explicitly valid and blank votes * Web server: + Port to (cohttp-based) ocsigenserver 3.0.0 + New voting interface for classic and Majority Judgment votes + Add a way to specify counting method of alternative questions (only Majority Judgment is supported at the moment) + Add possibility to customize email sender name (GitHub issue #30) + Add possibility to customize footer + The maximum total weight is now 10^11 (instead of 10^5) + `
` are interpreted as line breaks in election names, descriptions, questions and answers + Add possibility to localize configurable snippets + Better behaviour when sending a confirmation email fails + Many cosmetic changes in administrator's and voter's workflows 1.15 (2021-05-04) ================= * Update OCaml stack to 4.11.2 * Improve support for weighted votes: + Update instructions and interface for editing voters + Update monitoring scripts + Update specification * Add documentation and scripts for deploying using systemd-nspawn * New format for private credentials * Web server: + Check consistency of voter list + Add a generic service for computing and comparing fingerprints + Remove "Archive election" button + Change sign up and password change workflows + New format for voter passwords + Admin home page now shows login form of the first auth system + Add possibility to export auth systems to election administrators + Add authentication by e-mail + Add possibility to remove built-in voter auth systems + Do no longer send automatic warning e-mails + Show cookie disclaimer only when logged in + Add possibility to customize admin home page + Rephrase e-mails sent to voters + Add a template e-mail to be sent to the credential authority * Translations: + Add Greek (el), Dutch (nl), Slovak (sk), Finnish (fi), Polish (pl) 1.14 (2021-02-09) ================= * Add experimental support for weighted votes: a weight can be assigned to a voter with the syntax "address,login,weight" or "address,,weight" 1.13 (2020-12-02) ================= * Update OCaml stack to 4.11.1 * Handle mix of Single and Pedersen trustees thoughout codebase and specification * Add support to apply various counting methods to a set of ballots coming out of an non-homomorphic question, in the command-line tool and the web server: Condorcet-Schulze, Majority Judgment and Single Transferable Vote * Web server: + In threshold mode, add a mandatory server-owned key + Accept only ballots in canonical form + Do not allow change of trustee public key once one has been received + Add Belenios-* headers to all sent e-mails * Translations: + Switch i18n to gettext and use Weblate + Add support for translating the admin interface + Add Norwegian Bokmål (nb), Spanish (es), Ukrainian (uk), Czech (cs), Occitan (oc) * Tests: + Add monkey testing * Contributed scripts: + check_hash.py: use an external reference file with hashes * Switch the build system to dune * Drop support for old-style UUIDs 1.12 (2020-08-31) ================= * Web client: + Harden against browser extensions that pollute the global namespace + Use pristine sources of JavaScript libraries in the build process * Web server: + Do not allow to set credential authority name in automatic mode + Remove useless CalendarLib initialization (it was causing errors in some time zones) * Minor changes in tests and doc 1.11 (2020-05-25) ================= * Update OCaml/Eliom stack to 4.08.1/6.12.0 * In `opam-bootstrap.sh`, enforce a specific revision of opam-repository for reproducibility * Switch to unified trustees: handle trustees with a single kind of file `trustees.json` instead of previously `public_keys.jsons` or `threshold.json` * Add monitoring scripts * Documentation: + Add _Who does what during a Belenios election?_ (in English and French) * Command-line tool: + Add `mktrustees` command to generate `trustees.json` from one of the two previous files + Add `checksums` command to compute all checksums relevant in auditing an election + In `credgen`, do no longer generate file with individual public credential hashes + Add `compute-voters` command to compute the voter list (to be executed by the credential authority) + Add `sha256-b64` command * Web client: + Use JavaScript's native BigInt when available + Import SJCL sources for DFSG compliance * Web server: + Fix a vulnerability in the authentication system + Add a temporary automatic migration procedure of an election pool to unified trustees + Add public names to the election administrator and the credential authority (they are stored in `election.json`) + Add public names to trustees (they are stored in `trustees.json`) + Record shuffling order in `result.json` + Remove the possibility to replace a credential in a validated election + Add (much) more auditing data to election home + Add the possibility to download private credentials in automatic mode + Voters must always log in to confirm their vote + Export `shuffles.jsons` during the shuffling phase, show applied shuffles even before the final result is released + For alternative questions, offer a direct link to results instead of giving a JS query + Allow administrators to log in with their email address * Tests: + Load testing support: - Add Selenium scripts to create and populate an election with many voters + Allow testing with a prepared database 1.10 (2019-12-09) ================= * Add support for non-homomorphic questions (experimental) * Check group membership of signature verification key more often * Command-line tool: + Assume there is no ballots when `ballots.jsons` is missing * Web server: + Move setting of maxrequestbodysizeinmemory to configuration + Rework presentation of links that must be sent to third-parties + Bugfixes in the data policy loop: - its first iteration was done with the wrong spool dir - it died when trying to send warning e-mails + Update JSBN + Importing non-threshold trustees replaces current trustees 1.9.1 (2019-10-24) ================== * Specification: + Link to Meadows instead of eprint (easier to read) + Be more verbose about checks to do during the election * Web server: + Avoid error 500 on "accepted ballots" page when no ballots have been cast + Trim usernames and passwords before checking them + Trim and check CAS server addresses + Case-insensitive comparison of usernames + Set a limit on election names to prevent abuse + Protect third-party pages (creds, trustee) from authenticated users + Avoid error 500 when attempting to authenticate several times in a row * Use opam 2.0.5 in bootstrap script 1.9 (2019-05-28) ================ * Fix use of SOURCE_DATE_EPOCH * Web server: + Fix a bug that seldom caused the server to not perform its partial decryption + Check that cookies are not blocked on ballot submission + Add the possibility to temporarily hide the result from the public 1.8 (2019-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) ================ * Add (optional) contact info in emails sent by the server * Use base 58 tokens as UUIDs for shorter URLs (optional) * Add (optional) return path to mails sent by server * Show personal data processing notice to election administrators * Fix password regeneration when explicit usernames are used * Make the booth independent of the server and usable offline * Internationalize error messages 1.5 (2017-12-13) ================ * Add support for threshold decryption (experimental) * Fix bias in random sampling * Web server: + Add possibility to define the server e-mail address in config + Add possibility to explicitly add the server itself as a trustee + Add possibility to destroy elections in setup mode + Avoid new tabs and use download links + Add config option for "contact us" link on admin login page 1.4 (2017-04-05) ================ * Add a debug mode, which has the possibility to use /dev/urandom as source of entropy * Check encrypted tally in "belenios-tool verify" * Add a sample script to send credentials * Web server: + Introduce a limit on the number of mails sent at once. This effectively limits the number of voters in the general case. + Give a link to the future election to the credential authority and trustees + For each mailto template, add a direct link. This makes life easier for situations where complex mailto links are not supported. 1.3 (2017-02-01) ================ * Add support for blank votes * More diagnostics in verify-diff * Web server: + Do not log out of CAS + Automatically log out after a vote + Add Italian translation 1.2 (2016-10-05) ================ * Change the default group parameters to avoid possible trapdoors. The new ones are generated using FIPS 186-4. * Web server: + The administrator can choose the language(s) of mails sent by the server + The administrator can import trustees from a previous election + Question editor: it is now possible to insert and remove questions and answers anywhere + Add Romanian translation * Command-line tool: + Add --url option to election subcommands (in particular verify) + Add a "verify-diff" command to belenios-tool 1.1 (2016-07-25) ================ * Web server: + Internationalization of voter-facing interfaces - add French and German translations + Add a confirmation page for election finalization + Add cookie disclaimer + Add templates for mails to trustees + Add the Belenios logo and use www.belenios.org in links + Add OpenID Connect authentication for administrators * Command-line tool: + Issue a proper warning when a result is missing + Support result files where decryption factors are not in the same order as trustee public keys 1.0 (2016-04-22) ================ * Many changes in the web server: + Add election_missing_voters: it is now possible to see the list of people who did not vote (new link in election administration page). + Hide the login box when it is not relevant: We do no longer show login links in the top right hand corner of the page. The voter is automatically invited to log in when he is about to cast a vote. + Do no longer show warning when window.crypto is unavailable (this warning appeared on IE8). + In admin page, show tallied elections in a new section. + In admin page, sort (finalized) elections by finalization time. + Add a form to regenerate and mail a password. + Generating trustee keys is more resilient to momentary lack of entropy. + Change default question to make the blank choice explicit. + Print number of accepted ballots on the result page. + Add the possibility to specify a login attached to an email address. E-mail address and logins must be specified in the following way: foo@example.com,login. When login is not specified, the address is used as login. This feature is useful mainly for CAS authentication. + Voters (and passwords) can be imported from another (finalized) election. + Send a confirmation email after a successful vote. + Add a new notion of "archived" elections. + Pretty page for records. + An e-mail address can be attached to trustees. + Do not propose dummy authentication for new elections. 0.2 (2014-04-09) ================ * Major overhaul of the web server: + changes in configuration items + cleaner isolation between elections + add per-site and per-election administration pages + elections imported from the configuration file must be explicitly listed (no more directory scanning) + authentication is more modular + changes in CAS authentication method: - invoke credential requestor with `renew=true` - do not assume CAS paths start with `/cas/` + change in the password authentication method: - the password file must be uploaded via the web server (no more reading on-disk file) before the method is used for the first time + automatic logout after successful ballot casting + online creation of election * Remove hardcoded default group 0.1.1 (2014-02-13) ================== * New subcommands in belenios-tool: "mkelection" and "election vote" * Add a demo (bash) script to simulate a whole election * Prettier URLs for election pseudo-files * Fix compatibility with reverse-proxies 0.1 (2014-01-13) ================ * First public release belenios-1.17/COPYING0000644000650106067230000010333014115601117013131 0ustar glondused GNU AFFERO GENERAL PUBLIC LICENSE Version 3, 19 November 2007 Copyright (C) 2007 Free Software Foundation, Inc. Everyone is permitted to copy and distribute verbatim copies of this license document, but changing it is not allowed. Preamble The GNU Affero General Public License is a free, copyleft license for software and other kinds of works, specifically designed to ensure cooperation with the community in the case of network server software. The licenses for most software and other practical works are designed to take away your freedom to share and change the works. By contrast, our General Public Licenses are intended to guarantee your freedom to share and change all versions of a program--to make sure it remains free software for all its users. When we speak of free software, we are referring to freedom, not price. Our General Public Licenses are designed to make sure that you have the freedom to distribute copies of free software (and charge for them if you wish), that you receive source code or can get it if you want it, that you can change the software or use pieces of it in new free programs, and that you know you can do these things. Developers that use our General Public Licenses protect your rights with two steps: (1) assert copyright on the software, and (2) offer you this License which gives you legal permission to copy, distribute and/or modify the software. A secondary benefit of defending all users' freedom is that improvements made in alternate versions of the program, if they receive widespread use, become available for other developers to incorporate. Many developers of free software are heartened and encouraged by the resulting cooperation. However, in the case of software used on network servers, this result may fail to come about. The GNU General Public License permits making a modified version and letting the public access it on a server without ever releasing its source code to the public. The GNU Affero General Public License is designed specifically to ensure that, in such cases, the modified source code becomes available to the community. It requires the operator of a network server to provide the source code of the modified version running there to the users of that server. Therefore, public use of a modified version, on a publicly accessible server, gives the public access to the source code of the modified version. An older license, called the Affero General Public License and published by Affero, was designed to accomplish similar goals. This is a different license, not a version of the Affero GPL, but Affero has released a new version of the Affero GPL which permits relicensing under this license. The precise terms and conditions for copying, distribution and modification follow. TERMS AND CONDITIONS 0. Definitions. "This License" refers to version 3 of the GNU Affero General Public License. "Copyright" also means copyright-like laws that apply to other kinds of works, such as semiconductor masks. "The Program" refers to any copyrightable work licensed under this License. Each licensee is addressed as "you". "Licensees" and "recipients" may be individuals or organizations. To "modify" a work means to copy from or adapt all or part of the work in a fashion requiring copyright permission, other than the making of an exact copy. The resulting work is called a "modified version" of the earlier work or a work "based on" the earlier work. A "covered work" means either the unmodified Program or a work based on the Program. To "propagate" a work means to do anything with it that, without permission, would make you directly or secondarily liable for infringement under applicable copyright law, except executing it on a computer or modifying a private copy. Propagation includes copying, distribution (with or without modification), making available to the public, and in some countries other activities as well. To "convey" a work means any kind of propagation that enables other parties to make or receive copies. Mere interaction with a user through a computer network, with no transfer of a copy, is not conveying. An interactive user interface displays "Appropriate Legal Notices" to the extent that it includes a convenient and prominently visible feature that (1) displays an appropriate copyright notice, and (2) tells the user that there is no warranty for the work (except to the extent that warranties are provided), that licensees may convey the work under this License, and how to view a copy of this License. If the interface presents a list of user commands or options, such as a menu, a prominent item in the list meets this criterion. 1. Source Code. The "source code" for a work means the preferred form of the work for making modifications to it. "Object code" means any non-source form of a work. A "Standard Interface" means an interface that either is an official standard defined by a recognized standards body, or, in the case of interfaces specified for a particular programming language, one that is widely used among developers working in that language. The "System Libraries" of an executable work include anything, other than the work as a whole, that (a) is included in the normal form of packaging a Major Component, but which is not part of that Major Component, and (b) serves only to enable use of the work with that Major Component, or to implement a Standard Interface for which an implementation is available to the public in source code form. A "Major Component", in this context, means a major essential component (kernel, window system, and so on) of the specific operating system (if any) on which the executable work runs, or a compiler used to produce the work, or an object code interpreter used to run it. The "Corresponding Source" for a work in object code form means all the source code needed to generate, install, and (for an executable work) run the object code and to modify the work, including scripts to control those activities. However, it does not include the work's System Libraries, or general-purpose tools or generally available free programs which are used unmodified in performing those activities but which are not part of the work. For example, Corresponding Source includes interface definition files associated with source files for the work, and the source code for shared libraries and dynamically linked subprograms that the work is specifically designed to require, such as by intimate data communication or control flow between those subprograms and other parts of the work. The Corresponding Source need not include anything that users can regenerate automatically from other parts of the Corresponding Source. The Corresponding Source for a work in source code form is that same work. 2. Basic Permissions. All rights granted under this License are granted for the term of copyright on the Program, and are irrevocable provided the stated conditions are met. This License explicitly affirms your unlimited permission to run the unmodified Program. The output from running a covered work is covered by this License only if the output, given its content, constitutes a covered work. This License acknowledges your rights of fair use or other equivalent, as provided by copyright law. You may make, run and propagate covered works that you do not convey, without conditions so long as your license otherwise remains in force. You may convey covered works to others for the sole purpose of having them make modifications exclusively for you, or provide you with facilities for running those works, provided that you comply with the terms of this License in conveying all material for which you do not control copyright. Those thus making or running the covered works for you must do so exclusively on your behalf, under your direction and control, on terms that prohibit them from making any copies of your copyrighted material outside their relationship with you. Conveying under any other circumstances is permitted solely under the conditions stated below. Sublicensing is not allowed; section 10 makes it unnecessary. 3. Protecting Users' Legal Rights From Anti-Circumvention Law. No covered work shall be deemed part of an effective technological measure under any applicable law fulfilling obligations under article 11 of the WIPO copyright treaty adopted on 20 December 1996, or similar laws prohibiting or restricting circumvention of such measures. When you convey a covered work, you waive any legal power to forbid circumvention of technological measures to the extent such circumvention is effected by exercising rights under this License with respect to the covered work, and you disclaim any intention to limit operation or modification of the work as a means of enforcing, against the work's users, your or third parties' legal rights to forbid circumvention of technological measures. 4. Conveying Verbatim Copies. You may convey verbatim copies of the Program's source code as you receive it, in any medium, provided that you conspicuously and appropriately publish on each copy an appropriate copyright notice; keep intact all notices stating that this License and any non-permissive terms added in accord with section 7 apply to the code; keep intact all notices of the absence of any warranty; and give all recipients a copy of this License along with the Program. You may charge any price or no price for each copy that you convey, and you may offer support or warranty protection for a fee. 5. Conveying Modified Source Versions. You may convey a work based on the Program, or the modifications to produce it from the Program, in the form of source code under the terms of section 4, provided that you also meet all of these conditions: a) The work must carry prominent notices stating that you modified it, and giving a relevant date. b) The work must carry prominent notices stating that it is released under this License and any conditions added under section 7. This requirement modifies the requirement in section 4 to "keep intact all notices". c) You must license the entire work, as a whole, under this License to anyone who comes into possession of a copy. This License will therefore apply, along with any applicable section 7 additional terms, to the whole of the work, and all its parts, regardless of how they are packaged. This License gives no permission to license the work in any other way, but it does not invalidate such permission if you have separately received it. d) If the work has interactive user interfaces, each must display Appropriate Legal Notices; however, if the Program has interactive interfaces that do not display Appropriate Legal Notices, your work need not make them do so. A compilation of a covered work with other separate and independent works, which are not by their nature extensions of the covered work, and which are not combined with it such as to form a larger program, in or on a volume of a storage or distribution medium, is called an "aggregate" if the compilation and its resulting copyright are not used to limit the access or legal rights of the compilation's users beyond what the individual works permit. Inclusion of a covered work in an aggregate does not cause this License to apply to the other parts of the aggregate. 6. Conveying Non-Source Forms. You may convey a covered work in object code form under the terms of sections 4 and 5, provided that you also convey the machine-readable Corresponding Source under the terms of this License, in one of these ways: a) Convey the object code in, or embodied in, a physical product (including a physical distribution medium), accompanied by the Corresponding Source fixed on a durable physical medium customarily used for software interchange. b) Convey the object code in, or embodied in, a physical product (including a physical distribution medium), accompanied by a written offer, valid for at least three years and valid for as long as you offer spare parts or customer support for that product model, to give anyone who possesses the object code either (1) a copy of the Corresponding Source for all the software in the product that is covered by this License, on a durable physical medium customarily used for software interchange, for a price no more than your reasonable cost of physically performing this conveying of source, or (2) access to copy the Corresponding Source from a network server at no charge. c) Convey individual copies of the object code with a copy of the written offer to provide the Corresponding Source. This alternative is allowed only occasionally and noncommercially, and only if you received the object code with such an offer, in accord with subsection 6b. d) Convey the object code by offering access from a designated place (gratis or for a charge), and offer equivalent access to the Corresponding Source in the same way through the same place at no further charge. You need not require recipients to copy the Corresponding Source along with the object code. If the place to copy the object code is a network server, the Corresponding Source may be on a different server (operated by you or a third party) that supports equivalent copying facilities, provided you maintain clear directions next to the object code saying where to find the Corresponding Source. Regardless of what server hosts the Corresponding Source, you remain obligated to ensure that it is available for as long as needed to satisfy these requirements. e) Convey the object code using peer-to-peer transmission, provided you inform other peers where the object code and Corresponding Source of the work are being offered to the general public at no charge under subsection 6d. A separable portion of the object code, whose source code is excluded from the Corresponding Source as a System Library, need not be included in conveying the object code work. A "User Product" is either (1) a "consumer product", which means any tangible personal property which is normally used for personal, family, or household purposes, or (2) anything designed or sold for incorporation into a dwelling. In determining whether a product is a consumer product, doubtful cases shall be resolved in favor of coverage. For a particular product received by a particular user, "normally used" refers to a typical or common use of that class of product, regardless of the status of the particular user or of the way in which the particular user actually uses, or expects or is expected to use, the product. A product is a consumer product regardless of whether the product has substantial commercial, industrial or non-consumer uses, unless such uses represent the only significant mode of use of the product. "Installation Information" for a User Product means any methods, procedures, authorization keys, or other information required to install and execute modified versions of a covered work in that User Product from a modified version of its Corresponding Source. The information must suffice to ensure that the continued functioning of the modified object code is in no case prevented or interfered with solely because modification has been made. If you convey an object code work under this section in, or with, or specifically for use in, a User Product, and the conveying occurs as part of a transaction in which the right of possession and use of the User Product is transferred to the recipient in perpetuity or for a fixed term (regardless of how the transaction is characterized), the Corresponding Source conveyed under this section must be accompanied by the Installation Information. But this requirement does not apply if neither you nor any third party retains the ability to install modified object code on the User Product (for example, the work has been installed in ROM). The requirement to provide Installation Information does not include a requirement to continue to provide support service, warranty, or updates for a work that has been modified or installed by the recipient, or for the User Product in which it has been modified or installed. Access to a network may be denied when the modification itself materially and adversely affects the operation of the network or violates the rules and protocols for communication across the network. Corresponding Source conveyed, and Installation Information provided, in accord with this section must be in a format that is publicly documented (and with an implementation available to the public in source code form), and must require no special password or key for unpacking, reading or copying. 7. Additional Terms. "Additional permissions" are terms that supplement the terms of this License by making exceptions from one or more of its conditions. Additional permissions that are applicable to the entire Program shall be treated as though they were included in this License, to the extent that they are valid under applicable law. If additional permissions apply only to part of the Program, that part may be used separately under those permissions, but the entire Program remains governed by this License without regard to the additional permissions. When you convey a copy of a covered work, you may at your option remove any additional permissions from that copy, or from any part of it. (Additional permissions may be written to require their own removal in certain cases when you modify the work.) You may place additional permissions on material, added by you to a covered work, for which you have or can give appropriate copyright permission. Notwithstanding any other provision of this License, for material you add to a covered work, you may (if authorized by the copyright holders of that material) supplement the terms of this License with terms: a) Disclaiming warranty or limiting liability differently from the terms of sections 15 and 16 of this License; or b) Requiring preservation of specified reasonable legal notices or author attributions in that material or in the Appropriate Legal Notices displayed by works containing it; or c) Prohibiting misrepresentation of the origin of that material, or requiring that modified versions of such material be marked in reasonable ways as different from the original version; or d) Limiting the use for publicity purposes of names of licensors or authors of the material; or e) Declining to grant rights under trademark law for use of some trade names, trademarks, or service marks; or f) Requiring indemnification of licensors and authors of that material by anyone who conveys the material (or modified versions of it) with contractual assumptions of liability to the recipient, for any liability that these contractual assumptions directly impose on those licensors and authors. All other non-permissive additional terms are considered "further restrictions" within the meaning of section 10. If the Program as you received it, or any part of it, contains a notice stating that it is governed by this License along with a term that is a further restriction, you may remove that term. If a license document contains a further restriction but permits relicensing or conveying under this License, you may add to a covered work material governed by the terms of that license document, provided that the further restriction does not survive such relicensing or conveying. If you add terms to a covered work in accord with this section, you must place, in the relevant source files, a statement of the additional terms that apply to those files, or a notice indicating where to find the applicable terms. Additional terms, permissive or non-permissive, may be stated in the form of a separately written license, or stated as exceptions; the above requirements apply either way. 8. Termination. You may not propagate or modify a covered work except as expressly provided under this License. Any attempt otherwise to propagate or modify it is void, and will automatically terminate your rights under this License (including any patent licenses granted under the third paragraph of section 11). However, if you cease all violation of this License, then your license from a particular copyright holder is reinstated (a) provisionally, unless and until the copyright holder explicitly and finally terminates your license, and (b) permanently, if the copyright holder fails to notify you of the violation by some reasonable means prior to 60 days after the cessation. Moreover, your license from a particular copyright holder is reinstated permanently if the copyright holder notifies you of the violation by some reasonable means, this is the first time you have received notice of violation of this License (for any work) from that copyright holder, and you cure the violation prior to 30 days after your receipt of the notice. Termination of your rights under this section does not terminate the licenses of parties who have received copies or rights from you under this License. If your rights have been terminated and not permanently reinstated, you do not qualify to receive new licenses for the same material under section 10. 9. Acceptance Not Required for Having Copies. You are not required to accept this License in order to receive or run a copy of the Program. Ancillary propagation of a covered work occurring solely as a consequence of using peer-to-peer transmission to receive a copy likewise does not require acceptance. However, nothing other than this License grants you permission to propagate or modify any covered work. These actions infringe copyright if you do not accept this License. Therefore, by modifying or propagating a covered work, you indicate your acceptance of this License to do so. 10. Automatic Licensing of Downstream Recipients. Each time you convey a covered work, the recipient automatically receives a license from the original licensors, to run, modify and propagate that work, subject to this License. You are not responsible for enforcing compliance by third parties with this License. An "entity transaction" is a transaction transferring control of an organization, or substantially all assets of one, or subdividing an organization, or merging organizations. If propagation of a covered work results from an entity transaction, each party to that transaction who receives a copy of the work also receives whatever licenses to the work the party's predecessor in interest had or could give under the previous paragraph, plus a right to possession of the Corresponding Source of the work from the predecessor in interest, if the predecessor has it or can get it with reasonable efforts. You may not impose any further restrictions on the exercise of the rights granted or affirmed under this License. For example, you may not impose a license fee, royalty, or other charge for exercise of rights granted under this License, and you may not initiate litigation (including a cross-claim or counterclaim in a lawsuit) alleging that any patent claim is infringed by making, using, selling, offering for sale, or importing the Program or any portion of it. 11. Patents. A "contributor" is a copyright holder who authorizes use under this License of the Program or a work on which the Program is based. The work thus licensed is called the contributor's "contributor version". A contributor's "essential patent claims" are all patent claims owned or controlled by the contributor, whether already acquired or hereafter acquired, that would be infringed by some manner, permitted by this License, of making, using, or selling its contributor version, but do not include claims that would be infringed only as a consequence of further modification of the contributor version. For purposes of this definition, "control" includes the right to grant patent sublicenses in a manner consistent with the requirements of this License. Each contributor grants you a non-exclusive, worldwide, royalty-free patent license under the contributor's essential patent claims, to make, use, sell, offer for sale, import and otherwise run, modify and propagate the contents of its contributor version. In the following three paragraphs, a "patent license" is any express agreement or commitment, however denominated, not to enforce a patent (such as an express permission to practice a patent or covenant not to sue for patent infringement). To "grant" such a patent license to a party means to make such an agreement or commitment not to enforce a patent against the party. If you convey a covered work, knowingly relying on a patent license, and the Corresponding Source of the work is not available for anyone to copy, free of charge and under the terms of this License, through a publicly available network server or other readily accessible means, then you must either (1) cause the Corresponding Source to be so available, or (2) arrange to deprive yourself of the benefit of the patent license for this particular work, or (3) arrange, in a manner consistent with the requirements of this License, to extend the patent license to downstream recipients. "Knowingly relying" means you have actual knowledge that, but for the patent license, your conveying the covered work in a country, or your recipient's use of the covered work in a country, would infringe one or more identifiable patents in that country that you have reason to believe are valid. If, pursuant to or in connection with a single transaction or arrangement, you convey, or propagate by procuring conveyance of, a covered work, and grant a patent license to some of the parties receiving the covered work authorizing them to use, propagate, modify or convey a specific copy of the covered work, then the patent license you grant is automatically extended to all recipients of the covered work and works based on it. A patent license is "discriminatory" if it does not include within the scope of its coverage, prohibits the exercise of, or is conditioned on the non-exercise of one or more of the rights that are specifically granted under this License. You may not convey a covered work if you are a party to an arrangement with a third party that is in the business of distributing software, under which you make payment to the third party based on the extent of your activity of conveying the work, and under which the third party grants, to any of the parties who would receive the covered work from you, a discriminatory patent license (a) in connection with copies of the covered work conveyed by you (or copies made from those copies), or (b) primarily for and in connection with specific products or compilations that contain the covered work, unless you entered into that arrangement, or that patent license was granted, prior to 28 March 2007. Nothing in this License shall be construed as excluding or limiting any implied license or other defenses to infringement that may otherwise be available to you under applicable patent law. 12. No Surrender of Others' Freedom. If conditions are imposed on you (whether by court order, agreement or otherwise) that contradict the conditions of this License, they do not excuse you from the conditions of this License. If you cannot convey a covered work so as to satisfy simultaneously your obligations under this License and any other pertinent obligations, then as a consequence you may not convey it at all. For example, if you agree to terms that obligate you to collect a royalty for further conveying from those to whom you convey the Program, the only way you could satisfy both those terms and this License would be to refrain entirely from conveying the Program. 13. Remote Network Interaction; Use with the GNU General Public License. Notwithstanding any other provision of this License, if you modify the Program, your modified version must prominently offer all users interacting with it remotely through a computer network (if your version supports such interaction) an opportunity to receive the Corresponding Source of your version by providing access to the Corresponding Source from a network server at no charge, through some standard or customary means of facilitating copying of software. This Corresponding Source shall include the Corresponding Source for any work covered by version 3 of the GNU General Public License that is incorporated pursuant to the following paragraph. Notwithstanding any other provision of this License, you have permission to link or combine any covered work with a work licensed under version 3 of the GNU General Public License into a single combined work, and to convey the resulting work. The terms of this License will continue to apply to the part which is the covered work, but the work with which it is combined will remain governed by version 3 of the GNU General Public License. 14. Revised Versions of this License. The Free Software Foundation may publish revised and/or new versions of the GNU Affero General Public License from time to time. Such new versions will be similar in spirit to the present version, but may differ in detail to address new problems or concerns. Each version is given a distinguishing version number. If the Program specifies that a certain numbered version of the GNU Affero General Public License "or any later version" applies to it, you have the option of following the terms and conditions either of that numbered version or of any later version published by the Free Software Foundation. If the Program does not specify a version number of the GNU Affero General Public License, you may choose any version ever published by the Free Software Foundation. If the Program specifies that a proxy can decide which future versions of the GNU Affero General Public License can be used, that proxy's public statement of acceptance of a version permanently authorizes you to choose that version for the Program. Later license versions may give you additional or different permissions. However, no additional obligations are imposed on any author or copyright holder as a result of your choosing to follow a later version. 15. Disclaimer of Warranty. THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING, REPAIR OR CORRECTION. 16. Limitation of Liability. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MODIFIES AND/OR CONVEYS THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGES. 17. Interpretation of Sections 15 and 16. If the disclaimer of warranty and limitation of liability provided above cannot be given local legal effect according to their terms, reviewing courts shall apply local law that most closely approximates an absolute waiver of all civil liability in connection with the Program, unless a warranty or assumption of liability accompanies a copy of the Program in return for a fee. END OF TERMS AND CONDITIONS How to Apply These Terms to Your New Programs If you develop a new program, and you want it to be of the greatest possible use to the public, the best way to achieve this is to make it free software which everyone can redistribute and change under these terms. To do so, attach the following notices to the program. It is safest to attach them to the start of each source file to most effectively state the exclusion of warranty; and each file should have at least the "copyright" line and a pointer to where the full notice is found. Copyright (C) This program is free software: you can redistribute it and/or modify it under the terms of the GNU Affero General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Affero General Public License for more details. You should have received a copy of the GNU Affero General Public License along with this program. If not, see . Also add information on how to contact you by electronic and paper mail. If your software can interact with users remotely through a computer network, you should also make sure that it provides a way for users to get its source. For example, if your program is a web application, its interface could display a "Source" link that leads users to an archive of the code. There are many ways you could offer source, and different solutions will be better for different programs; see section 13 for the specific requirements. You should also get your employer (if you work as a programmer) or school, if any, to sign a "copyright disclaimer" for the program, if necessary. For more information on this, and how to apply and follow the GNU AGPL, see . belenios-1.17/Dockerfile_base_environment0000644000650106067230000000072714115601117017514 0ustar glondusedFROM debian:10 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 libncurses-dev zlib1g-dev libgd-securityimage-perl cracklib-runtime git jq npm rsync 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 belenios-1.17/Dockerfile_test_scenario_environment0000644000650106067230000000170314115601117021437 0ustar glondused# Docker image used as FROM has been generated from ./Dockerfile_base_environment FROM glondu/beleniosbase:20210709-1 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.26.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 belenios-1.17/INSTALL.md0000644000650106067230000002052314115601117013530 0ustar glondusedBelenios compilation instructions ================================= Using containers ---------------- If you are using Linux and have root privileges, you might be interested in our documentation on [deploying Belenios using systemd-nspawn](doc/nspawn/README.md). The rest of this file documents other ways to build Belenios on standard POSIX systems. The easy way ------------ Belenios is written in OCaml and has some dependencies towards third-party OCaml libraries. The easiest and most portable way to compile Belenios from source is to use [OPAM](http://opam.ocamlpro.com/), which is a package manager for OCaml projects. The non-OCaml prerequisites are: * a POSIX system with a C compiler and a `/usr/lib/sendmail` command * 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/) * [m4](https://www.gnu.org/software/m4/) * [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) * [ncurses](http://invisible-island.net/ncurses/) * [GD-SecurityImage](https://metacpan.org/release/GD-SecurityImage) * [cracklib](https://github.com/cracklib/cracklib) * [jq](https://github.com/stedolan/jq) * [npm](https://www.npmjs.com/) 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 bubblewrap 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 jq npm If you are unfamiliar with OCaml or OPAM, we provide an `opam-bootstrap.sh` shell script that creates a whole, hopefully self-contained, OCaml+OPAM install, and then installs all the dependencies of Belenios, everything into a single directory. You can choose the directory by setting the `BELENIOS_SYSROOT` environment variable, or it will take `~/.belenios` by default. Just run: ./opam-bootstrap.sh On a modern desktop system, this needs approximately 30 minutes and 3.3 gigabytes of disk space. If everything goes successfully, follow the given instructions to update your shell environment, then run: make build-release-server and you can skip the next two sections and go directly to the _Documentation_ section. You can also compile a debug version by using: make build-debug-server Note that this version may introduce vulnerabilities and should not be used in production! To make sure everything went well, you can run tests: make check If you are familiar with OCaml, please read the `opam-bootstrap.sh` shell script, or the following two sections to compile Belenios with your existing OCaml installation. Command-line tool ----------------- To compile the command-line tool, you will need: * [OCaml](https://ocaml.org/) * [Dune](https://dune.build/) * [Zarith](https://github.com/ocaml/Zarith) * [Cryptokit](https://github.com/xavierleroy/cryptokit) * [Atdgen](https://github.com/ahrefs/atd) * [Yojson](https://github.com/ocaml-community/yojson) * [Cmdliner](http://erratique.ch/software/cmdliner) With OPAM, these dependencies can be installed with the following command: opam install dune atdgen zarith cryptokit cmdliner Once all the dependencies have been installed, the command-line tool can be compiled with: make It produces a single executable, `belenios-tool`, in the `_build/install/default/bin` directory. You can install it in your `PATH` (which we will assume in the guides), or refer to it with a full path. Web server ---------- The web server has the following additional dependencies: * [Calendar](http://calendar.forge.ocamlcore.org/) * [Eliom](http://ocsigen.org/eliom/) * [Csv](https://github.com/Chris00/ocaml-csv) With OPAM, you can install them with: opam install calendar eliom csv Once all the dependencies have been installed, the Eliom module can be compiled with: make build-release-server It will produce a full installation of Belenios, its libraries and its server, in the `_run/usr` directory. See `demo/ocsigenserver.conf.in` for an ocsigenserver configuration template, and the _Server administrator's guide_ for more information on how to use it. Documentation ------------- 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 install texlive-latex-extra texlive-fonts-recommended texlive-fonts-extra lmodern texlive-science Once all the dependencies have been installed, the documentation can be compiled with: make doc Compiling on Windows using Cygwin --------------------------------- Windows is not yet a fully supported platform, but you can compile at least the command-line tool on Windows + 32-bit [Cygwin](http://cygwin.com/index.html). You might need the following packages: * curl * dos2unix * flexdll * gcc-core * gcc-g++ * git * gmp * libgmp-devel * libncursesw-devel * libpcre-devel * libsqlite3-devel * m4 * make * ocaml * ocaml-base * ocaml-camlp4 * ocaml-compiler-libs * openssh * patch * pkg-config * zlib-devel With these packages installed, you should be able to install OPAM by following its [installation instructions from sources](http://opam.ocaml.org/doc/Install.html#FromSources). Once OPAM is installed, follow the instructions in the _Command-line tool_ section above. Troubleshooting --------------- ### Bootstrap fails if dune is already installed The script `opam-bootstrap.sh` fails when a not suitable version of dune is already installed in your `$PATH`. This is due to [a bug in opam](https://github.com/ocaml/opam/issues/3987). If you face this issue, either uninstall dune before running `opam-bootstrap.sh`, or manage to get opam running by other means, and directly use it to install the dependencies of Belenios. ### Bootstrap fails because of an error with an OPAM package For reproducibility purposes, the `opam-bootstrap.sh` script hardcodes a specific revision of the OPAM repository. However, it may happen that this revision becomes unusable, e.g. the URL of some tarball changes. This may give errors like bad checksums when running the script. To recover from such errors, update your local copy of the OPAM repository with the following commands: source env.sh cd $OPAMROOT/../opam-repository git pull --ff-only opam update then run the `opam install` command that can be found in the `opam-bootstrap.sh` script. ### Missing sources The instructions outlined in this document and in the `opam-bootstrap.sh` script imply downloading files from third-party servers. Sometimes, these servers can be down. For example, you can get: =-=-= Installing ocamlnet.3.7.3 =-=-= ocamlnet.3.7.3 Downloading http://download.camlcity.org/download/ocamlnet-3.7.3.tar.gz [ERROR] http://download.camlcity.org/download/ocamlnet-3.7.3.tar.gz is not available ===== ERROR while installing ocamlnet.3.7.3 ===== Could not get the source for ocamlnet.3.7.3. This can be worked around with the following steps: * source the generated `env.sh` file (you must adapt it if you use an incompatible shell such as tcsh); * download the file from an alternate source (for example [Debian source packages](http://www.debian.org/distrib/packages)); * run `opam pin ` (in the example above, `` would be `ocamlnet`); * resume the installation by running again the `opam install` command found in `opam-bootstrap.sh`; * follow the instructions given at the end of `opam-bootstrap.sh`. ### Errors while compiling ocsigenserver If ocsigenserver fails to install because of a SSL-related error: * edit `opam-bootstrap.sh` by adding ` ssl=0.5.2` to the `opam install` call; * run `./opam-bootstrap.sh`. ### Errors while compiling Belenios itself If you succeeded installing all dependencies, but you get errors while compiling Belenios, maybe you installed an incompatible version of a dependency. The `opam-bootstrap.sh` script is tuned to install only compatible versions; you can have a look at it to get these versions. belenios-1.17/Makefile0000644000650106067230000000476014115601117013545 0ustar glondusedDUNE_DEBUG_ARGS := --build-dir=_build-debug minimal: dune build -p belenios-platform,belenios-platform-native,belenios-lib,belenios-tool build-debug-server: BELENIOS_DEBUG=1 dune build $(DUNE_DEBUG_ARGS) BELENIOS_DEBUG=1 dune exec $(DUNE_DEBUG_ARGS) -- \ src/checki18next/checki18next.exe --dir frontend/translations \ < src/checki18next/reference.json rm -rf _run/usr dune install $(DUNE_DEBUG_ARGS) --destdir=_run --prefix=/usr 2>/dev/null BELENIOS_DEBUG=1 $(MAKE) DESTDIR=../_run/usr/share/belenios-server/frontend -C frontend git archive --prefix=belenios-debug/ HEAD | gzip -9n > _run/usr/share/belenios-server/belenios.tar.gz build-release-server: $(MAKE) clean BELENIOS_DEBUG= dune build --release BELENIOS_DEBUG= dune exec $(DUNE_DEBUG_ARGS) -- \ src/checki18next/checki18next.exe --dir frontend/translations \ < src/checki18next/reference.json rm -rf _run/usr dune install --destdir=_run --prefix=/usr 2>/dev/null BELENIOS_DEBUG= $(MAKE) DESTDIR=../_run/usr/share/belenios-server/frontend -C frontend git archive --prefix="belenios-$(shell git describe --tags)/" HEAD | gzip -9n > _run/usr/share/belenios-server/belenios.tar.gz build-i18next-reference: BELENIOS_DEBUG=1 dune exec $(DUNE_DEBUG_ARGS) -- \ src/checki18next/checki18next.exe --dir frontend/translations --make-reference \ < frontend/translations/en.json > src/checki18next/reference.json build-debug-tool: BELENIOS_DEBUG=1 dune build $(DUNE_DEBUG_ARGS) -p belenios-platform,belenios-platform-native,belenios-lib,belenios-tool rm -rf _run/tool-debug dune install $(DUNE_DEBUG_ARGS) --destdir=_run/tool-debug --prefix=/ belenios-platform belenios-platform-native belenios-lib belenios-tool 2>/dev/null check: $(MAKE) build-debug-tool $(MAKE) -C tests/tool check clean: dune clean dune clean $(DUNE_DEBUG_ARGS) $(MAKE) -C po clean $(MAKE) -C tests/tool clean .PHONY: doc doc: $(MAKE) doc/specification.pdf doc/specification.pdf: doc/specification.tex cd doc && for u in 1 2 3; do pdflatex specification.tex; done release: @if [ `git status --porcelain | grep -v '^?? ' | wc -l ` -eq 0 ]; then \ COMMIT_ID=`git describe --tags`; \ VERSION=`cat VERSION`; \ mkdir -p _releases; \ if [ "$$(printf $$COMMIT_ID | head -c$$(printf $$VERSION | wc -c))" = "$$VERSION" ]; then \ git archive --prefix=belenios-$$COMMIT_ID/ $$COMMIT_ID | gzip -9n > _releases/belenios-$$COMMIT_ID.tar.gz; \ else \ echo "VERSION is not up-to-date!"; exit 1; \ fi; \ else \ echo "The tree is not clean!"; exit 1; \ fi belenios-1.17/README.md0000644000650106067230000000632414115601117013362 0ustar glondusedBelenios ======== Introduction ------------ Belenios is a verifiable voting system that partly implements the Helios-C protocol described [here](http://eprint.iacr.org/2013/177), which is itself derived from [Helios](http://vote.heliosvoting.org). It consists of a command-line tool and a web server. Both use the same backend and can be used to organize elections and perform verifications. They employ messages formatted in a common format, a specification of which is available in doc/specification.tex. Compilation instructions are provided in INSTALL.md. Election overview ----------------- An election involves several roles: an administrator, a credential authority, trustees and voters. For maximum security, each of these roles must be performed by a different entity. An election can be summarized as follows: 1. The administrator initiates the process. 2. The credential authority generates one credential per voter; he sends the private part to each voter and all public parts to the administrator. 3. Each trustee generates a keypair and sends his/her public key to the administrator. 4. The administrator collects all public credentials and trustees' public keys and sets up the election. 5. The administrator opens the election. 6. Each voter votes; the administrator collects, checks and publishes all the ballots. 7. The administrator closes the election. 8. Trustees collectively decrypt the result. 9. The administrator announces the result of the election. The command-line tool --------------------- Each step can be performed with the help of the command-line tool. The tool is also the most convenient way to exercise the verifiability capabilities of the system. More information in doc/tool.md. The web server -------------- The whole process can be executed using the web server. Each step can be done with a browser. In this case, the formal "administrator" role above is typically shared between the server and a human operator. The server can also assume the roles of credential authority and trustee. Therefore, in its simplest (and weakest) form, an election involves only an operator henceforth called "election administrator" (usually distinct from the person who sets up and administrates the server itself) and voters. In its strongest form, an election involves the election administrator, a credential authority, (at least) two trustees and voters. More information in doc/web.md. Legal ----- ### Internal code By "internal code", we mean everything that is not in the `ext/` directory. Copyright © 2012-2021 Inria, CNRS This program is free software: you can redistribute it and/or modify it under the terms of the GNU Affero General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version, with the additional exemption that compiling, linking, and/or using OpenSSL is allowed. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Affero General Public License for more details. ### External code Please refer to each file for accurate copyright and licensing information. belenios-1.17/RELEASE_NOTES.md0000644000650106067230000000543614115601117014460 0ustar glondused1.17 ==== * To use the `belenios-server` executable, the syntax of `ocsigenserver.conf.in` changes. Please review the changes to this file since version 1.16. * With the new version of the crypto, giving all parameters of a group with an external file is no longer supported. Allowed groups are hardcoded in the source code and identified by short strings such as `BELENIOS-2048` or `RFC-3526-2048`. These strings must now be used in the configuration file. * The new notion of administrator accounts: + adds a new `accounts` directory, configured in `ocsigenserver.conf.in` + changes the format of the `owner` field of `draft.json`, `metadata.json` and `deleted.json`, which is now the account id (an integer) for new elections. The old format based on the authentication method will continue to be supported for a while, but this support may be dropped in the future. 1.15 ==== * All authentication systems available for voters must be explicitly listed in the configuration file with the new `` directive. Look at `demo/ocsigenserver.conf.in` for examples. In particular, password and (generic) CAS authentications are not shown by default. 1.11 ==== * The switch to unified trustees changed: + the format of the pool: instead of one of `public_keys.jsons` or `threshold.json`, only a single `trustees.json` is expected. The spool will be automatically converted during the first run of this version of the web server. Next versions will only support the new scheme. + the format of `deleted.json` files: `nb_trustees` and `trustees_threshold` fields have been replaced by a new `trustees` field reflecting `trustees.json` structure. No provisions were made to convert existing files. 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 `` directive in the configuration file) by removing all temporary files (run `rm *.*` in this directory) and private keys (`rm */private_key*.json*`). 1.1 === * To upgrade a web server running version 1.0, you need to delete the Ocsipersist store (by default the `ocsidb` file referred in the configuration file). This will archive all finalized elections, and delete all unfinalized elections (i.e. the elections being prepared). Additionally, you should clean up the data directory (the one referred in the `` directive in the configuration file) by removing all temporary files (run `rm *.*` in this directory) and private keys (`rm */private_key.json`). belenios-1.17/VERSION0000644000650106067230000000000514115601117013141 0ustar glondused1.17 belenios-1.17/api.odocl0000644000650106067230000000005714115601117013673 0ustar glondusedSerializable_t Signatures Group_field Election belenios-1.17/belenios-lib.opam0000644000650106067230000000100414115601117015313 0ustar glondused# This file is generated by dune, edit dune-project instead opam-version: "2.0" version: "1.17" synopsis: "Belenios library" maintainer: ["stephane.glondu@inria.fr"] authors: ["Stéphane Glondu"] license: "AGPL-3" depends: [ "dune" {>= "2.7"} "yojson" {>= "1.7.0"} "atdgen" {>= "2.2.1"} "belenios-platform" {= version} "odoc" {with-doc} ] build: [ ["dune" "subst"] {dev} [ "dune" "build" "-p" name "-j" jobs "@install" "@runtest" {with-test} "@doc" {with-doc} ] ] belenios-1.17/belenios-platform-js.opam0000644000650106067230000000102214115601117017003 0ustar glondused# This file is generated by dune, edit dune-project instead opam-version: "2.0" version: "1.17" synopsis: "JavaScript implementation of the Belenios platform" maintainer: ["stephane.glondu@inria.fr"] authors: ["Stéphane Glondu"] license: "AGPL-3" depends: [ "dune" {>= "2.7"} "js_of_ocaml" {>= "3.7.0"} "js_of_ocaml-ppx" {>= "3.7.0"} "odoc" {with-doc} ] build: [ ["dune" "subst"] {dev} [ "dune" "build" "-p" name "-j" jobs "@install" "@runtest" {with-test} "@doc" {with-doc} ] ] belenios-1.17/belenios-platform-native.opam0000644000650106067230000000075214115601117017666 0ustar glondused# This file is generated by dune, edit dune-project instead opam-version: "2.0" version: "1.17" synopsis: "Native implementation of the Belenios platform" maintainer: ["stephane.glondu@inria.fr"] authors: ["Stéphane Glondu"] license: "AGPL-3" depends: [ "dune" {>= "2.7"} "cryptokit" {>= "1.14"} "odoc" {with-doc} ] build: [ ["dune" "subst"] {dev} [ "dune" "build" "-p" name "-j" jobs "@install" "@runtest" {with-test} "@doc" {with-doc} ] ] belenios-1.17/belenios-platform.opam0000644000650106067230000000070514115601117016400 0ustar glondused# This file is generated by dune, edit dune-project instead opam-version: "2.0" version: "1.17" synopsis: "Definition of the Belenios platform" maintainer: ["stephane.glondu@inria.fr"] authors: ["Stéphane Glondu"] license: "AGPL-3" depends: [ "dune" {>= "2.7"} "odoc" {with-doc} ] build: [ ["dune" "subst"] {dev} [ "dune" "build" "-p" name "-j" jobs "@install" "@runtest" {with-test} "@doc" {with-doc} ] ] belenios-1.17/belenios-server.opam0000644000650106067230000000115314115601117016060 0ustar glondused# This file is generated by dune, edit dune-project instead opam-version: "2.0" version: "1.17" synopsis: "Belenios server" maintainer: ["stephane.glondu@inria.fr"] authors: ["Stéphane Glondu"] license: "AGPL-3" depends: [ "dune" {>= "2.7"} "belenios-platform-native" {= version} "belenios-lib" {= version} "lwt" {>= "5.3.0"} "calendar" {>= "2.04"} "csv" {>= "2.4"} "eliom" {>= "8.4.8"} "ocamlnet" {>= "4.1.9-1"} "odoc" {with-doc} ] build: [ ["dune" "subst"] {dev} [ "dune" "build" "-p" name "-j" jobs "@install" "@runtest" {with-test} "@doc" {with-doc} ] ] belenios-1.17/belenios-tool.opam0000644000650106067230000000103414115601117015525 0ustar glondused# This file is generated by dune, edit dune-project instead opam-version: "2.0" version: "1.17" synopsis: "Belenios command-line tool" maintainer: ["stephane.glondu@inria.fr"] authors: ["Stéphane Glondu"] license: "AGPL-3" depends: [ "dune" {>= "2.7"} "cmdliner" {>= "1.0.4"} "belenios-platform-native" {= version} "belenios-lib" {= version} "odoc" {with-doc} ] build: [ ["dune" "subst"] {dev} [ "dune" "build" "-p" name "-j" jobs "@install" "@runtest" {with-test} "@doc" {with-doc} ] ] belenios-1.17/belenios.opam0000644000650106067230000000076514115601117014564 0ustar glondused# This file is generated by dune, edit dune-project instead opam-version: "2.0" version: "1.17" synopsis: "Belenios meta-package" maintainer: ["stephane.glondu@inria.fr"] authors: ["Stéphane Glondu"] license: "AGPL-3" depends: [ "dune" {>= "2.7"} "belenios-tool" {= version} "belenios-server" {= version} "odoc" {with-doc} ] build: [ ["dune" "subst"] {dev} [ "dune" "build" "-p" name "-j" jobs "@install" "@runtest" {with-test} "@doc" {with-doc} ] ] belenios-1.17/contrib/0000755000650106067230000000000014115601117013536 5ustar glondusedbelenios-1.17/contrib/check_hash.py0000755000650106067230000000370214115601117016175 0ustar glondused#!/usr/bin/env python3 import argparse import os import sys import hashlib import base64 import urllib.request import json def hash_votefile(link, lang): try: head = { 'Accept-Language' : lang } req = urllib.request.Request(link, headers=head) resp = urllib.request.urlopen(req) data = resp.read() except: print("Failed to download vote.html in lang {}. Aborting".format(lang)) sys.exit(1) m = hashlib.sha256() m.update(data) h = m.hexdigest() return h def hash_file(link): try: resp = urllib.request.urlopen(link) data = resp.read() except: print("Failed to download {}. Aborting".format(link)) sys.exit(1) m = hashlib.sha256() m.update(data) h = m.hexdigest() return h parser = argparse.ArgumentParser(description="monitor files served by a Belenios server") parser.add_argument("--url", required=True, help="prefix url of the Belenios server") parser.add_argument("--reference", required=True, help="reference file") parser.add_argument("--output", help="output new reference to this file") args = parser.parse_args() url = args.url.strip("/") fail = False with open(args.reference) as f: reference = json.load(f) new_reference = {} for f, descr in reference.items(): if type(descr) == dict: new_reference[f] = {} for lang, descr in descr.items(): h = hash_votefile(url + f, lang) new_reference[f][lang] = h if h != descr: fail = True print("Wrong hash of {} in {}: got {} but expected {}".format(f, lang, h, descr)) else: h = hash_file(url + f) new_reference[f] = h if h != descr: fail = True print("Wrong hash of static file {}: got {} but expected {}".format(f, h, descr)) if args.output: with open(args.output, mode="w") as f: json.dump(new_reference, f) if fail: sys.exit(1) belenios-1.17/contrib/fill_en_po.ml0000644000650106067230000000232314115601117016176 0ustar glondused(* For each source string that has no translation, copy msgid as translation *) (* Usage: ocaml fill_en_po.ml < en.po | sponge en.po *) #use "topfind";; #require "pcre";; let rex = Pcre.regexp "^msgid (.*)$" let rec process_msgid ic oc = match input_line ic with | exception End_of_file -> () | line -> match Pcre.exec ~rex line with | s -> process_msgstr (Pcre.get_substring s 1) ic oc | exception Not_found -> Printf.fprintf oc "%s\n%!" line; process_msgid ic oc and process_msgstr msgid ic oc = match input_line ic with | exception End_of_file -> failwith "unexpected eof" | line -> if line = "msgstr \"\"" then process_empty msgid ic oc else ( Printf.fprintf oc "msgid %s\n%s\n%!" msgid line; process_msgid ic oc ) and process_empty msgid ic oc = match input_line ic with | exception End_of_file -> Printf.fprintf oc "msgid %s\nmsgstr %s\n%!" msgid msgid; | line -> if line = "" then ( Printf.fprintf oc "msgid %s\nmsgstr %s\n\n%!" msgid msgid; process_msgid ic oc ) else ( Printf.fprintf oc "msgid %s\nmsgstr \"\"\n%s\n" msgid line; process_msgid ic oc ) let () = process_msgid stdin stdout belenios-1.17/contrib/list_live_elections.py0000755000650106067230000000634714115601117020164 0ustar glondused#!/usr/bin/env python3 import argparse import os import sys import re import json import datetime MIN_VOTERS=5 MAX_TALLIED_AGE=7 # expressed in days MAX_FINALIZED_AGE=30 # expressed in days # verb is a global variable, controlled by --verbose def verb_print(str): if (verb): print(str, file=sys.stderr) def all_uuid(path): return [ f for f in os.listdir(path) if os.path.isdir(os.path.join(path, f)) ] def is_draft_or_deleted(elec_path): if os.path.exists(os.path.join(elec_path, "deleted.json")): return True if os.path.exists(os.path.join(elec_path, "draft.json")): return True def is_secure(elec_path): meta = os.path.join(elec_path, "metadata.json") assert os.path.exists(meta) with open(meta,"r") as file: data = json.load(file) if 'cred_authority' in data and data['cred_authority'] != 'server': return True if 'trustees' in data: if len(data['trustees']) > 1 or (not data['server_is_trustee']): return True return False def is_test(elec_path): elec = os.path.join(elec_path, "election.json") if not os.path.exists(elec): print("Can not read election.json in " + elec_path) assert False with open(elec,"r") as file: data = json.load(file) if re.search("test", data['name'], re.IGNORECASE) != None: return True voters = os.path.join(elec_path, "voters.txt") num_voters = sum(1 for line in open(voters, "r")) if num_voters < MIN_VOTERS: return True return False def is_old(elec_path): dates = os.path.join(elec_path, "dates.json") assert os.path.exists(dates) with open(dates,"r") as file: data = json.load(file) if 'archive' in data: return True now = datetime.datetime.now() if 'tally' in data: tallied = data['tally'] tt = datetime.datetime.strptime(tallied, "%Y-%m-%d %H:%M:%S.%f") age = now-tt if age > datetime.timedelta(days=MAX_TALLIED_AGE): return True else: # not tallied, but finalized for a long time ? finalized = data['finalization'] tt = datetime.datetime.strptime(finalized, "%Y-%m-%d %H:%M:%S.%f") age = now-tt if age > datetime.timedelta(days=MAX_FINALIZED_AGE): return True return False parser = argparse.ArgumentParser(description="list elections that are alive and deserve to be monitored") parser.add_argument("spool_directory", help="Spool directory where the elections are stored") parser.add_argument("--verbose", help="explain why elections are discarded on stderr", action="store_true") args = parser.parse_args() verb = args.verbose uuids = all_uuid(args.spool_directory) for uuid in uuids: elec_path = os.path.join(args.spool_directory, uuid) if is_draft_or_deleted(elec_path): verb_print("Election {} is deleted or not yet finalized".format(uuid)) continue if is_test(elec_path): verb_print("Election {} is probably a test election".format(uuid)) continue if not is_secure(elec_path): verb_print("Election {} is in degraded mode".format(uuid)) continue if is_old(elec_path): verb_print("Election {} is old".format(uuid)) continue print(uuid) belenios-1.17/contrib/monitor_elections.py0000755000650106067230000005030614115601117017653 0ustar glondused#!/usr/bin/env python3 import argparse import os import sys import datetime import subprocess import urllib.request import urllib.error import xml.dom.minidom import re import hashlib import base64 import json # Example : # ./monitor_elections.py --uuid aTGmQNj1SXA5JG --url https://belenios.loria.fr/beta/ --wdir /tmp/wdir --checkhash yes # External dependencies: # - belenios-tool # - git # - check_hash.py (from the belenios source dist, in contrib/) # TODO: # - add options --belenios-tool-path and --check-hash-path # - find a way to test that failure are detected # - do a git gc from time to time or at the end (how?) # The status contains: # - a boolean telling whether there was a problem # - a message to be put in the commit log # This is updated along the way with the merge() method. # For the moment, we have msg of type Bytes. Maybe string would be better ? class Status: def __init__(self, fail, commit_msg): self.fail = fail self.msg = commit_msg # failure if at least one failure # concatenate all messages def merge(self, status): self.fail = self.fail or status.fail self.msg = self.msg + status.msg # Default output for the logfile is stdout, i.e. None log_file = None def logme(str): msg = "Log: {}".format(str) if log_file == None: print(msg) else: print(msg, file=log_file) # messages that should also go to stderr: def Elogme(str): logme(str) print("Log: {}".format(str), file=sys.stderr) # If it does not exist, create a fresh directory for an election # and initialize the git. def check_or_create_dir(wdir, uuid): p = os.path.join(wdir, uuid) if not os.path.exists(p): logme("creating directory for election {}".format(uuid)) os.mkdir(p) os.mkdir(os.path.join(p, "new")) if not os.path.exists(os.path.join(p, ".git")): logme("init git for election {}".format(uuid)) subprocess.run(["git", "init", p], capture_output=True) open(os.path.join(p, "fresh"), "w").close() # List of audit files. # When no ballot have been cast yet, ballots.jsons does not exist. # We also put here a fake filename, for the hash of the voterlist. audit_files=['election.json', 'public_creds.txt', 'trustees.json', 'ballots', 'index.html'] optional_audit_files=['ballots.jsons','result.json','shuffles.jsons','hash_voterlist'] def download_audit_data(url, uuid): link = url + '/elections/' + uuid data = dict() status = Status(False, b"") fail = False msg = "" for f in audit_files: try: if f == 'index.html': l = link + '/' else: l = link + '/' + f resp = urllib.request.urlopen(l) data[f]=resp.read() except urllib.error.URLError as e: fail = True msg = msg + "Download {} failed with ret code \"{}\" for election {}\n".format(f, e, uuid) for f in optional_audit_files: try: resp = urllib.request.urlopen(link + '/' + f) data[f]=resp.read() except: data[f]=b'' status = Status(fail, msg.encode()) return status, data # This write data to the directory in order to run verify and # verify-diff. # At first, this goes to a 'new' subdirectory, and once verify-diff has # been run, this is moved to the main directory of the election. def write_and_verify_new_data(wdir, uuid, data): # copy new data in the "new" subdirectory p = os.path.join(wdir, uuid) pnew = os.path.join(p, 'new') for f in audit_files + optional_audit_files: if data[f] != b'': with open(os.path.join(pnew, f), "wb") as newf: newf.write(data[f]) # run belenios-tool verify on it ver = subprocess.run(["belenios-tool", "verify", "--dir={}".format(pnew)], stdout=subprocess.PIPE, stderr=subprocess.STDOUT) if ver.returncode != 0: msg="Error: belenios-tool verify failed on newly downloaded data from election {}, with output {}\n".format(uuid, ver.stdout).encode() return Status(True, msg) else: logme("Successfully verified new data of {}".format(uuid)) # if not the first time, run belenios-tool verify-diff msg = b"" if os.path.exists(os.path.join(p, "fresh")): os.remove(os.path.join(p, "fresh")) else: verdiff = subprocess.run(["belenios-tool", "verify-diff", "--dir1={}".format(p), "--dir2={}".format(pnew)], stdout=subprocess.PIPE, stderr=subprocess.STDOUT) if verdiff.returncode != 0: msg="Error: belenios-tool verify-diff failed on newly downloaded data from election {}, with output {}".format(uuid, verdiff.stdout).encode() return Status(True, msg) if re.search(b"W:", verdiff.stdout) != None: msg = verdiff.stdout logme("Successfully diff-verified new data of {}".format(uuid)) # move new files to main subdirectory for f in audit_files + optional_audit_files: if data[f] != b'': os.rename(os.path.join(pnew, f), os.path.join(p, f)) return Status(False, msg) # Verify that the hash of the ballots shown on the ballot-box web page # are consistent with the json file. def check_hash_ballots(data): dom = xml.dom.minidom.parseString(data['ballots']) list_ballots = dom.getElementsByTagName("li") if len(list_ballots) == 0: return Status(False, b"") if len(list_ballots[0].childNodes) == 2: has_weight = True else: has_weight = False list_hash = [ x.firstChild.firstChild.data for x in list_ballots ] if has_weight: list_weights = [ x.childNodes[1].data for x in list_ballots ] list_weights = [ x[2:-1] for x in list_weights ] list_hash2 = [] list_weights2 = [] for l in data['ballots.jsons'].splitlines(): m = hashlib.sha256() m.update(l) h = base64.b64encode(m.digest()).decode().strip('=') list_hash2.append(h) if has_weight: jsn = json.loads(l) cred = jsn['signature']['public_key'] pat = re.compile(cred + r',(\d+)') m=pat.search(data['public_creds.txt'].decode()) if m: list_weights2.append(m.group(1)) else: # if absent, default weight is 1 list_weights2.append('1') if has_weight: list_hash = [ (list_hash[i],list_weights[i]) for i in range(len(list_hash)) ] list_hash2 = [ (list_hash2[i],list_weights2[i]) for i in range(len(list_hash2)) ] list_hash.sort() list_hash2.sort() if (not list_hash == list_hash2): msg = b"Error: hash of ballots do not correspond!\n" return Status(True, msg) else: logme("Successfully checked hash of ballots of {}".format(uuid)) return Status(False, b"") # Verify that the data printed on the page of the election is # consistent with the other audit files. def check_index_html(data): # when the election is closed, there is a "disabled" attributed # without value that the xml parser does not like. We remove it. st = data['index.html'].decode().replace('disabled>Start', '>Start') # also, the &lang in the link makes the parser crazy. st = st.replace('&lang', 'lang') dom = xml.dom.minidom.parseString(st) fail = False msg = b"" logme("Checking index.html of {} ...".format(uuid)) # fingerprint of the election vs election.json m = hashlib.sha256() m.update(data['election.json'][0:-1]) # remove trailing \n h = base64.b64encode(m.digest()).decode().strip('=') h2 = dom.getElementsByTagName("code")[0].firstChild.data if (not h == h2): msg = "Error: Wrong fingerprint of election {}\n".format(uuid).encode() fail = True else: logme(" election fingerprint ok") # credential fingerprint vs public_creds.txt m = hashlib.sha256() m.update(data['public_creds.txt']) h = base64.b64encode(m.digest()).decode().strip('=') node = [ x.firstChild for x in dom.getElementsByTagName("div") if x.firstChild != None and x.firstChild.nodeType == xml.dom.minidom.Node.TEXT_NODE and re.search("Credentials were generated",x.firstChild.data) != None ] assert len(node) == 1 h2 = node[0].data.split(' ')[-1].strip('.') if (not h == h2): msg = msg + "Error: Wrong credential fingerprint of election {}\n".format(uuid).encode() fail = True else: logme(" cred fingerprint ok") # if weights, check the total/min/max vs content of public_creds.txt node = [ x.firstChild for x in dom.getElementsByTagName("div") if x.firstChild != None and x.firstChild.nodeType == xml.dom.minidom.Node.TEXT_NODE and re.search("The total weight is",x.firstChild.data) != None ] if (len(node) == 1): fail_weights = False pat = re.compile(r'The total weight is (\d+) \(min: (\d+), max: (\d+)\)') mat = pat.match(node[0].data) w_tot = int(mat.group(1)) w_min = int(mat.group(2)) w_max = int(mat.group(3)) c_tot = 0 c_min = 10000000000 c_max = 0 for l in data['public_creds.txt'].splitlines(): wt = l.decode().split(',') if len(wt) == 2: x = int(l.decode().split(',')[1]) else: # if absent, default weight is 1. assert len(wt) == 1; x = 1 c_tot += x if (x < c_min): c_min = x if (x > c_max): c_max = x if (c_tot != w_tot) or (c_min != w_min) or (c_max != w_max): msg = msg + "Error: Wrong stats of weights in election {}\n".format(uuid).encode() logme(" " + str(c_tot) + " " + str(c_min) + " " + str(c_max)) fail = True else: logme(" stats of weights ok") # check that the printed number of voters is consistent with number # of credentials node = [ x.firstChild for x in dom.getElementsByTagName("div") if x.firstChild != None and x.firstChild.nodeType == xml.dom.minidom.Node.TEXT_NODE and re.search("The voter list has",x.firstChild.data) != None ] assert len(node) == 1 pat = re.compile(r'The voter list has (\d+) voter\(s\) and fingerprint ([\w/+]+).') mat = pat.match(node[0].data) nb_voters = int(mat.group(1)) data['hash_voterlist'] = mat.group(2).encode() # for further checks nb_creds = data['public_creds.txt'].decode().count('\n') if (nb_voters != nb_creds): msg = msg + "Error: Number of voters different from number of credentials for election {}\n".format(uuid).encode() logme(" " + str(nb_voters) + " " + str(nb_creds)) fail = True else: logme(" number of voters ok") def hash_pub_key(s): m = hashlib.sha256() m.update(b'\"') m.update(s.encode()) m.update(b'\"') return base64.b64encode(m.digest()).decode().strip('=') # trustees fingerprint vs trustees.json jsn = json.loads(data['trustees.json']) names = [] pks = [] certs = [] for trustee in jsn: if trustee[0] == 'Single': if 'name' in trustee[1]: names.append(trustee[1]['name']) else: names.append("N/A") pks.append(hash_pub_key(trustee[1]['public_key'])) certs.append(None) else: assert trustee[0] == 'Pedersen' for tru in trustee[1]['verification_keys']: if 'name' in tru: names.append(tru['name']) else: names.append("N/A") pks.append(hash_pub_key(tru['public_key'])) for tru in trustee[1]['certs']: m = hashlib.sha256() m.update(tru['message'].encode()) certs.append(base64.b64encode(m.digest()).decode().strip('=')) names2 = [] pks2 = [] certs2 = [] # in index.html, the non-threshold trustees are in the ul list with id "trustees" arr = [ x for x in dom.getElementsByTagName("ul") if x.getAttribute('id') == 'trustees' ] if arr != []: pat = re.compile(r'(^.*) \(([\w+/]*)\)$') for trustee in arr[0].getElementsByTagName("li"): s = trustee.firstChild.data mat = pat.match(s) names2.append(mat.group(1)) pks2.append(mat.group(2)) certs2.append(None) # the threshold trustees are in the ul list with class "trustees_threshold" arr = [ x for x in dom.getElementsByTagName("ul") if x.getAttribute('class') == 'trustees_threshold' ] if arr != []: pat = re.compile(r'(^.*) \(([\w+/]*)\) \[([\w+/]*)\]$') for trustee in arr[0].getElementsByTagName("li"): s = trustee.firstChild.data mat = pat.match(s) names2.append(mat.group(1)) pks2.append(mat.group(2)) certs2.append(mat.group(3)) if ((not names == names2) or (not pks == pks2) or (not certs == certs2)): msg = msg + "Error: Wrong trustees fingerprint of election {}\n".format(uuid).encode() fail = True else: logme(" trustees fingerprints ok") # encrypted tally vs result.json (if present) # (and extract shuffles if present) shuf_array = None if data['result.json'] != b'': jsn = json.loads(data['result.json']) if 'encrypted_tally' in jsn: s = jsn['encrypted_tally'] # convert to a string as Belenios use it for hashing ss = json.JSONEncoder().encode(s).replace(' ', '') m = hashlib.sha256() m.update(ss.encode()) h = base64.b64encode(m.digest()).decode().strip('=') node = [ x.firstChild for x in dom.getElementsByTagName("div") if x.firstChild != None and x.firstChild.nodeType == xml.dom.minidom.Node.TEXT_NODE and re.search("The fingerprint of the encrypted tally", x.firstChild.data) != None ] assert len(node) == 1 h2 = node[0].data.split(' ')[-1].strip('.') if (not h == h2): msg = msg + "Error: Wrong encrypted tally fingerprint of election {}\n".format(uuid).encode() fail = True else: logme(" encrypted tally fingerprint ok") if 'shuffles' in jsn: shuf_array=jsn['shuffles'] elif data['shuffles.jsons'] != b'': # not exactly the same as in result.json: one json per line... shuf_array = [] for line in data['shuffles.jsons'].decode().splitlines(): shuf_array.append(json.loads(line)) # shuffles fingerprint vs result.json or shuffles.jsons (if present) if shuf_array != None: hashs = [] hashs2 = [] for s in shuf_array: # convert to a string as Belenios use it for hashing ss = json.JSONEncoder().encode(s).replace(' ', '') m = hashlib.sha256() m.update(ss.encode()) hashs.append(base64.b64encode(m.digest()).decode().strip('=')) # in index.html, the shuffles are in the ul with id 'shuffles' for shuf in [ x for x in dom.getElementsByTagName("ul") if x.getAttribute('id') == 'shuffles' ][0].getElementsByTagName("li"): s = shuf.firstChild.data # reuse same pattern as for trustees mat = pat.match(s) hashs2.append(mat.group(2)) if (not hashs == hashs2): msg = msg + "Error: Wrong shuffles fingerprint of election {}\n".format(uuid).encode() fail = True else: logme(" shuffles fingerprints ok") if not fail: logme("Successfully checked index.html of {}".format(uuid)) return Status(fail, msg) def commit(wdir, uuid, msg): eldir = os.path.join(wdir, uuid) for f in audit_files + optional_audit_files: if f in data.keys() and data[f] != b'': gitadd = subprocess.run(["git", "-C", eldir, "add", f]) if gitadd.returncode != 0: Elogme("Failed git add {} for election {}".format(f, uuid)) return False gitci = subprocess.run(["git", "-C", eldir, "commit", "-q", "--allow-empty", "--allow-empty-message", "-m", msg.decode()]) if gitci.returncode != 0: Elogme("Failed git commit {} for election {}".format(f, uuid)) return False logme("Successfully added a commit for {}".format(uuid)) return True ############################################# # Parsing a bool is not a built-in of argparse :-( # https://stackoverflow.com/questions/15008758/parsing-boolean-values-with-argparse def str2bool(v): if isinstance(v, bool): return v if v.lower() in ('yes', 'true', 't', 'y', '1'): return True elif v.lower() in ('no', 'false', 'f', 'n', '0'): return False else: raise argparse.ArgumentTypeError('Boolean value expected.') parser = argparse.ArgumentParser(description="monitor Belenios elections") group = parser.add_mutually_exclusive_group(required=True) group.add_argument("--uuidfile", help="file containing uuid's of election to monitor") group.add_argument("--uuid", help="uuid of an election to monitor") parser.add_argument("--url", required=True, help="prefix url (without trailing /elections )") parser.add_argument("--wdir", required=True, help="work dir where logs are kept") parser.add_argument("--checkhash", type=str2bool, nargs='?', const=True, default=True, metavar="yes|no", help="also check static files on the server") parser.add_argument("--hashref", help="reference file for hash, as used by check_hash.py") parser.add_argument("--logfile", help="file to write the non-error logs") args = parser.parse_args() # Set logfile; check permissions if args.logfile: log_file = open(args.logfile, "a") # Build list of uuids if args.uuid: uuids = [ args.uuid ] else: assert (args.uuidfile) uuids = [ ] with open(args.uuidfile, "r") as file: for line in file: uuids.append(line.rstrip()) # check that wdir exists and is r/w if not os.path.isdir(args.wdir) or not os.access(args.wdir, os.W_OK | os.R_OK): print("The wdir {} should read/write accessible".format(args.wdir)) sys.exit(1) # check that a ref file is given if checkhash is set if args.checkhash == True: if not args.hashref: print("If --checkhash is set, a --hashref file should be given") sys.exit(1) logme("[{}] Starting monitoring elections.".format(datetime.datetime.now())) for uuid in uuids: logme("Start monitoring election {}".format(uuid)) check_or_create_dir(args.wdir, uuid) status, data = download_audit_data(args.url.strip("/"), uuid) # if we managed to download stuff, then check what we can if not status.fail: stat = write_and_verify_new_data(args.wdir, uuid, data) status.merge(stat) stat = check_hash_ballots(data) status.merge(stat) stat = check_index_html(data) status.merge(stat) # create the hash_voterlist file, with the value read from index.html # or check that its value is consistent p = os.path.join(args.wdir, uuid, 'hash_voterlist') if os.path.exists(p): with open(p, "rb") as file: oldhash = file.read() if (oldhash != data['hash_voterlist']): status.merge(Status(True, "Error: hash of the voter list changed for election {}".format(uuid).encode())) else: with open(p, "wb") as file: file.write(data['hash_voterlist']) # commit if status.msg != b'': Elogme("Commit log for election {} is {}".format(uuid, status.msg.decode())) commit(args.wdir, uuid, status.msg) # check hash of js files. They do not depend on a particular election, so # we dot it only once if args.checkhash == True: hh = subprocess.run(["check_hash.py", "--url", args.url, "--reference", args.hashref ], stdout=subprocess.PIPE, stderr=subprocess.STDOUT) if hh.returncode != 0: print(hh.stdout.decode()) sys.exit(1) else: logme("Successfully checked hash of static files") if args.logfile: log_file.close() belenios-1.17/contrib/move_deleted_elections.sh0000755000650106067230000000100514115601117020572 0ustar glondused#!/bin/sh set -e SPOOL="$1" TARGET="$2" usage () { echo "Usage: $0 SPOOL TARGET" echo "Moves deleted elections from SPOOL to TARGET." exit 1 } if ! [ -d "$SPOOL" ] || ! [ -d "$TARGET" ]; then usage fi for u in "$SPOOL"/*/deleted.json; do if [ -f "$u" ]; then uuid="${u%/*}" uuid="${uuid##*/}" if [ -d "$TARGET/$uuid" ]; then echo "$TARGET/$uuid already exists" exit 2 else mv "$SPOOL/$uuid" "$TARGET" fi fi done belenios-1.17/contrib/reference_template.json0000644000650106067230000001560614115601117020272 0ustar glondused{ "/static/belenios_jslib.js" : null, "/static/booth.css" : null, "/static/common.css" : null, "/static/encrypting.gif" : null, "/static/frontend/booth/app.css" : null, "/static/frontend/booth/app.mjs" : null, "/static/frontend/booth/app_fallback.js" : null, "/static/frontend/booth/color_utils.mjs" : null, "/static/frontend/booth/components/AllQuestionsWithPagination.mjs" : null, "/static/frontend/booth/components/Breadcrumb.css" : null, "/static/frontend/booth/components/Breadcrumb.mjs" : null, "/static/frontend/booth/components/CandidateWithCheckbox.css" : null, "/static/frontend/booth/components/CandidateWithCheckbox.mjs" : null, "/static/frontend/booth/components/CandidateWithRadio.mjs" : null, "/static/frontend/booth/components/ClassicVoteCandidatesList.css" : null, "/static/frontend/booth/components/ClassicVoteCandidatesList.mjs" : null, "/static/frontend/booth/components/ClassicVoteRecap.mjs" : null, "/static/frontend/booth/components/DisplayDependingOnWindowWidth.mjs" : null, "/static/frontend/booth/components/InputCredentialSection.mjs" : null, "/static/frontend/booth/components/LoadingSpinner.css" : null, "/static/frontend/booth/components/LoadingSpinner.mjs" : null, "/static/frontend/booth/components/MajorityJudgmentVoteBigCandidatesList.css" : null, "/static/frontend/booth/components/MajorityJudgmentVoteBigCandidatesList.mjs" : null, "/static/frontend/booth/components/MajorityJudgmentVoteCandidatesList.mjs" : null, "/static/frontend/booth/components/MajorityJudgmentVoteRecap.mjs" : null, "/static/frontend/booth/components/MajorityJudgmentVoteSmallCandidatesList.css" : null, "/static/frontend/booth/components/MajorityJudgmentVoteSmallCandidatesList.mjs" : null, "/static/frontend/booth/components/NiceButton.css" : null, "/static/frontend/booth/components/NiceButton.mjs" : null, "/static/frontend/booth/components/NiceInput.css" : null, "/static/frontend/booth/components/NiceInput.mjs" : null, "/static/frontend/booth/components/NoUuidSection.mjs" : null, "/static/frontend/booth/components/PageFooter.css" : null, "/static/frontend/booth/components/PageFooter.mjs" : null, "/static/frontend/booth/components/PageHeader.css" : null, "/static/frontend/booth/components/PageHeader.mjs" : null, "/static/frontend/booth/components/QuestionWithVotableAnswers.css" : null, "/static/frontend/booth/components/QuestionWithVotableAnswers.mjs" : null, "/static/frontend/booth/components/ReviewEncryptSection.css" : null, "/static/frontend/booth/components/ReviewEncryptSection.mjs" : null, "/static/frontend/booth/components/VoteNavigation.css" : null, "/static/frontend/booth/components/VoteNavigation.mjs" : null, "/static/frontend/booth/components/WholeVoteRecap.css" : null, "/static/frontend/booth/components/WholeVoteRecap.mjs" : null, "/static/frontend/booth/components/common.css" : null, "/static/frontend/booth/election_utils.mjs" : null, "/static/frontend/booth/i18n_init.mjs" : null, "/static/frontend/booth/majority_judgment_colors.mjs" : null, "/static/frontend/booth/select-css.css" : null, "/static/frontend/booth/shortcuts.js" : null, "/static/frontend/booth/vote.html" : null, "/static/frontend/node_modules/i18next/dist/umd/i18next.min.js" : null, "/static/frontend/node_modules/i18next-http-backend/i18nextHttpBackend.min.js" : null, "/static/frontend/node_modules/react/umd/react.production.min.js" : null, "/static/frontend/node_modules/react-dom/umd/react-dom.production.min.js" : null, "/static/frontend/node_modules/react-i18next/dist/umd/react-i18next.min.js" : null, "/static/frontend/translations/en.json" : null, "/static/frontend/translations/fr.json" : null, "/static/groups/default.json" : null, "/static/groups/rfc3526-2048.json" : null, "/static/locales/admin/cs.json" : null, "/static/locales/admin/de.json" : null, "/static/locales/admin/el.json" : null, "/static/locales/admin/en.json" : null, "/static/locales/admin/es.json" : null, "/static/locales/admin/fr.json" : null, "/static/locales/admin/it.json" : null, "/static/locales/admin/nb.json" : null, "/static/locales/admin/nl.json" : null, "/static/locales/admin/oc.json" : null, "/static/locales/admin/pl.json" : null, "/static/locales/admin/pt_BR.json" : null, "/static/locales/admin/ro.json" : null, "/static/locales/voter/cs.json" : null, "/static/locales/voter/de.json" : null, "/static/locales/voter/el.json" : null, "/static/locales/voter/en.json" : null, "/static/locales/voter/es.json" : null, "/static/locales/voter/fi.json" : null, "/static/locales/voter/fr.json" : null, "/static/locales/voter/it.json" : null, "/static/locales/voter/nb.json" : null, "/static/locales/voter/nl.json" : null, "/static/locales/voter/oc.json" : null, "/static/locales/voter/pl.json" : null, "/static/locales/voter/pt_BR.json" : null, "/static/locales/voter/ro.json" : null, "/static/locales/voter/sk.json" : null, "/static/locales/voter/uk.json" : null, "/static/logo.png" : null, "/static/mime.types" : null, "/static/placeholder.png" : null, "/static/reset.css" : null, "/static/responsive_site.css" : null, "/static/site.css" : null, "/static/style.css" : null, "/static/styled-elements.css" : null, "/static/superfish.css" : null, "/static/tool_js.js" : null, "/static/tool_js_booth.js" : null, "/static/tool_js_credgen.js" : null, "/static/tool_js_fingerprint.js" : null, "/static/tool_js_pd.js" : null, "/static/tool_js_questions.js" : null, "/static/tool_js_shuffle.js" : null, "/static/tool_js_tkeygen.js" : null, "/static/tool_js_ttkeygen.js" : null, "/vote.html" : { "cs" : null, "de" : null, "el" : null, "en" : null, "es" : null, "fi" : null, "fr" : null, "it" : null, "nb" : null, "nl" : null, "oc" : null, "pl" : null, "pt_BR" : null, "ro" : null, "sk" : null, "uk" : null } } belenios-1.17/contrib/send_credentials.py0000755000650106067230000000503514115601117017424 0ustar glondused#!/usr/bin/env python3 import smtplib from email.mime.text import MIMEText from string import Template import time import getpass # In DEGUB mode, emails are sent to this address instead of the true one. # (typically the address of the credential authority) DEBUG=False DEBUG_MAIL='bozo.leclown@example.com' # Edit the following according to your election: FROM='bozo.leclown@example.com' # can be the email of the credential authority SUBJECT='Élection du meilleur cookie: votre matériel de vote' UUID='pmFbhhuxH3k6vc' # Your outgoing email configuration: SMTP='smtp.example.com' username='bozo' password = getpass.getpass("please type your password: ") # name of the file where to read the credentials CODE_FILE='creds.txt' # Edit the email template: TEMPLATE=Template(""" Bonjour, Nous vous invitons à participer à l'élection du meilleur cookie à l'adresse suivante: https://belenios.loria.fr/elections/$UUID/ Vous aurez besoin de vos identifiants LDAP ou de votre login/mot de passe, mais aussi du code de vote personnel (appelé "credential") que voici : $ELECTION_CODE Le scrutin est ouvert du 1 avril à 9h au 2 avril à 18h. Veillez bien à aller au bout des 6 étapes pour que votre vote soit pris en compte. Un mail de confirmation vous sera envoyé. Pour rappel, il y a deux candidats : Maïté et Amandine. Merci de votre participation ========================================================== Hello, You are listed as a voter for the election of the best cookie. Please visit the following link: https://belenios.loria.fr/elections/$UUID/ You will need your LDAP or login / password, and also the following credential (personal code): $ELECTION_CODE The election is open from April 1st, 9am to April 2nd, 6pm. Be sure to go through the 6 steps to ensure that your vote is taken into account. A confirmation email will be sent. Reminder: there are two candidates Maïté and Amandine. Thank you for your participation. """) # Real stuff starts here. Pretty short, isn't it? with open(CODE_FILE) as cf: d = dict(UUID=UUID) s = smtplib.SMTP(SMTP) s.starttls() s.login(username, password) for line in cf: l = line.split() d['ELECTION_CODE']=l[1] msg = MIMEText(TEMPLATE.substitute(d)) email=l[0].split(",")[0] msg['Subject'] = SUBJECT msg['From'] = FROM if DEBUG: msg['To'] = DEBUG_MAIL else: msg['To'] = email s.send_message(msg) time.sleep(0.2) # short delay; might need more for very large election s.quit() belenios-1.17/contrib/stats_on_deleted.sh0000755000650106067230000000206314115601117017416 0ustar glondused#!/bin/bash set -e SPOOL="$1" usage () { echo "Usage: $0 SPOOL" echo "Computes some statistics on deleted elections in SPOOL." exit 1 } if ! [ -d "$SPOOL" ]; then usage fi total_elections=0 total_voters=0 total_ballots=0 dates="" for u in "$SPOOL"/*/deleted.json; do echo "Processing $u ..." eval $(jq --raw-output .nb_voters,.nb_ballots,.date < $u | { read nb_voters read nb_ballots read date echo "let total_voters+=$nb_voters total_ballots+=$nb_ballots 1;" echo "date=\"$date\"" }) let ++total_elections if [ -z "$dates" ]; then dates="$date" else dates="$(printf "%s\n%s" "$dates" "$date")" fi done dates="$(echo "$dates" | sort)" first_election="$(echo "$dates" | head -n1)" last_election="$(echo "$dates" | tail -n1)" echo "Number of elections: $total_elections" echo "Number of voters: $total_voters" echo "Number of ballots: $total_ballots" echo "First election: $first_election" echo "Last election: $last_election" belenios-1.17/demo/0000755000650106067230000000000014115601117013022 5ustar glondusedbelenios-1.17/demo/dune0000644000650106067230000000033614115601117013702 0ustar glondused(install (files (run-server.sh as belenios-start-server) (stop-server.sh as belenios-stop-server)) (section bin) (package belenios-server)) (install (files mime.types) (section share) (package belenios-server)) belenios-1.17/demo/mime.types0000644000650106067230000003570414115601117015050 0ustar glondused# This is a comment. I love comments. # This file controls what Internet media types are sent to the client for # given file extension(s). Sending the correct media type to the client # is important so they know how to handle the content of the file. # Extra types can either be added here or by using an AddType directive # in your config files. For more information about Internet media types, # please read RFC 2045, 2046, 2047, 2048, and 2077. The Internet media type # registry is at . # Belenios needs a custom mimetypes file because the default one does not recognize files with .mjs extension as Javascript files. # MIME type Extensions application/activemessage application/andrew-inset ez application/applefile application/atom+xml atom application/atomicmail application/batch-smtp application/beep+xml application/cals-1840 application/cnrp+xml application/commonground application/cpl+xml application/cybercash application/dca-rft application/dec-dx application/dvcs application/edi-consent application/edifact application/edi-x12 application/eshop application/font-tdpfr application/http application/hyperstudio application/iges application/index application/index.cmd application/index.obj application/index.response application/index.vnd application/iotp application/ipp application/isup application/mac-binhex40 hqx application/mac-compactpro cpt application/macwriteii application/marc application/mathematica application/mathml+xml mathml application/msword doc application/news-message-id application/news-transmission application/ocsp-request application/ocsp-response application/octet-stream bin dms lha lzh exe class so dll dmg application/oda oda application/ogg ogg application/parityfec application/pdf pdf application/pgp-encrypted application/pgp-keys application/pgp-signature application/pkcs10 application/pkcs7-mime application/pkcs7-signature application/pkix-cert application/pkix-crl application/pkixcmp application/postscript ai eps ps application/prs.alvestrand.titrax-sheet application/prs.cww application/prs.nprend application/prs.plucker application/qsig application/rdf+xml rdf application/reginfo+xml application/remote-printing application/riscos application/rtf application/sdp application/set-payment application/set-payment-initiation application/set-registration application/set-registration-initiation application/sgml application/sgml-open-catalog application/sieve application/slate application/smil smi smil application/srgs gram application/srgs+xml grxml application/timestamp-query application/timestamp-reply application/tve-trigger application/vemmi application/vnd.3gpp.pic-bw-large application/vnd.3gpp.pic-bw-small application/vnd.3gpp.pic-bw-var application/vnd.3gpp.sms application/vnd.3m.post-it-notes application/vnd.accpac.simply.aso application/vnd.accpac.simply.imp application/vnd.acucobol application/vnd.acucorp application/vnd.adobe.xfdf application/vnd.aether.imp application/vnd.amiga.ami application/vnd.anser-web-certificate-issue-initiation application/vnd.anser-web-funds-transfer-initiation application/vnd.audiograph application/vnd.blueice.multipass application/vnd.bmi application/vnd.bw-fontobject eot application/vnd.businessobjects application/vnd.canon-cpdl application/vnd.canon-lips application/vnd.cinderella application/vnd.claymore application/vnd.commerce-battelle application/vnd.commonspace application/vnd.contact.cmsg application/vnd.cosmocaller application/vnd.criticaltools.wbs+xml application/vnd.ctc-posml application/vnd.cups-postscript application/vnd.cups-raster application/vnd.cups-raw application/vnd.curl application/vnd.cybank application/vnd.data-vision.rdz application/vnd.dna application/vnd.dpgraph application/vnd.dreamfactory application/vnd.dxr application/vnd.ecdis-update application/vnd.ecowin.chart application/vnd.ecowin.filerequest application/vnd.ecowin.fileupdate application/vnd.ecowin.series application/vnd.ecowin.seriesrequest application/vnd.ecowin.seriesupdate application/vnd.enliven application/vnd.epson.esf application/vnd.epson.msf application/vnd.epson.quickanime application/vnd.epson.salt application/vnd.epson.ssf application/vnd.ericsson.quickcall application/vnd.eudora.data application/vnd.fdf application/vnd.ffsns application/vnd.fints application/vnd.flographit application/vnd.framemaker application/vnd.fsc.weblaunch application/vnd.fujitsu.oasys application/vnd.fujitsu.oasys2 application/vnd.fujitsu.oasys3 application/vnd.fujitsu.oasysgp application/vnd.fujitsu.oasysprs application/vnd.fujixerox.ddd application/vnd.fujixerox.docuworks application/vnd.fujixerox.docuworks.binder application/vnd.fut-misnet application/vnd.grafeq application/vnd.groove-account application/vnd.groove-help application/vnd.groove-identity-message application/vnd.groove-injector application/vnd.groove-tool-message application/vnd.groove-tool-template application/vnd.groove-vcard application/vnd.hbci application/vnd.hhe.lesson-player application/vnd.hp-hpgl application/vnd.hp-hpid application/vnd.hp-hps application/vnd.hp-pcl application/vnd.hp-pclxl application/vnd.httphone application/vnd.hzn-3d-crossword application/vnd.ibm.afplinedata application/vnd.ibm.electronic-media application/vnd.ibm.minipay application/vnd.ibm.modcap application/vnd.ibm.rights-management application/vnd.ibm.secure-container application/vnd.informix-visionary application/vnd.intercon.formnet application/vnd.intertrust.digibox application/vnd.intertrust.nncp application/vnd.intu.qbo application/vnd.intu.qfx application/vnd.irepository.package+xml application/vnd.is-xpr application/vnd.japannet-directory-service application/vnd.japannet-jpnstore-wakeup application/vnd.japannet-payment-wakeup application/vnd.japannet-registration application/vnd.japannet-registration-wakeup application/vnd.japannet-setstore-wakeup application/vnd.japannet-verification application/vnd.japannet-verification-wakeup application/vnd.jisp application/vnd.kde.karbon application/vnd.kde.kchart application/vnd.kde.kformula application/vnd.kde.kivio application/vnd.kde.kontour application/vnd.kde.kpresenter application/vnd.kde.kspread application/vnd.kde.kword application/vnd.kenameaapp application/vnd.koan application/vnd.liberty-request+xml application/vnd.llamagraphics.life-balance.desktop application/vnd.llamagraphics.life-balance.exchange+xml application/vnd.lotus-1-2-3 application/vnd.lotus-approach application/vnd.lotus-freelance application/vnd.lotus-notes application/vnd.lotus-organizer application/vnd.lotus-screencam application/vnd.lotus-wordpro application/vnd.mcd application/vnd.mediastation.cdkey application/vnd.meridian-slingshot application/vnd.micrografx.flo application/vnd.micrografx.igx application/vnd.mif mif application/vnd.minisoft-hp3000-save application/vnd.mitsubishi.misty-guard.trustweb application/vnd.mobius.daf application/vnd.mobius.dis application/vnd.mobius.mbk application/vnd.mobius.mqy application/vnd.mobius.msl application/vnd.mobius.plc application/vnd.mobius.txf application/vnd.mophun.application application/vnd.mophun.certificate application/vnd.motorola.flexsuite application/vnd.motorola.flexsuite.adsi application/vnd.motorola.flexsuite.fis application/vnd.motorola.flexsuite.gotap application/vnd.motorola.flexsuite.kmr application/vnd.motorola.flexsuite.ttc application/vnd.motorola.flexsuite.wem application/vnd.mozilla.xul+xml xul application/vnd.ms-artgalry application/vnd.ms-asf application/vnd.ms-excel xls application/vnd.ms-fontobject eot application/vnd.ms-lrm application/vnd.ms-powerpoint ppt application/vnd.ms-project application/vnd.ms-tnef application/vnd.ms-works application/vnd.ms-wpl application/vnd.mseq application/vnd.msign application/vnd.music-niff application/vnd.musician application/vnd.netfpx application/vnd.noblenet-directory application/vnd.noblenet-sealer application/vnd.noblenet-web application/vnd.novadigm.edm application/vnd.novadigm.edx application/vnd.novadigm.ext application/vnd.obn application/vnd.osa.netdeploy application/vnd.palm application/vnd.pg.format application/vnd.pg.osasli application/vnd.powerbuilder6 application/vnd.powerbuilder6-s application/vnd.powerbuilder7 application/vnd.powerbuilder7-s application/vnd.powerbuilder75 application/vnd.powerbuilder75-s application/vnd.previewsystems.box application/vnd.publishare-delta-tree application/vnd.pvi.ptid1 application/vnd.pwg-multiplexed application/vnd.pwg-xhtml-print+xml application/vnd.quark.quarkxpress application/vnd.rapid application/vnd.s3sms application/vnd.sealed.net application/vnd.seemail application/vnd.shana.informed.formdata application/vnd.shana.informed.formtemplate application/vnd.shana.informed.interchange application/vnd.shana.informed.package application/vnd.smaf application/vnd.sss-cod application/vnd.sss-dtf application/vnd.sss-ntf application/vnd.street-stream application/vnd.svd application/vnd.swiftview-ics application/vnd.triscape.mxs application/vnd.trueapp application/vnd.truedoc application/vnd.ufdl application/vnd.uplanet.alert application/vnd.uplanet.alert-wbxml application/vnd.uplanet.bearer-choice application/vnd.uplanet.bearer-choice-wbxml application/vnd.uplanet.cacheop application/vnd.uplanet.cacheop-wbxml application/vnd.uplanet.channel application/vnd.uplanet.channel-wbxml application/vnd.uplanet.list application/vnd.uplanet.list-wbxml application/vnd.uplanet.listcmd application/vnd.uplanet.listcmd-wbxml application/vnd.uplanet.signal application/vnd.vcx application/vnd.vectorworks application/vnd.vidsoft.vidconference application/vnd.visio application/vnd.visionary application/vnd.vividence.scriptfile application/vnd.vsf application/vnd.wap.sic application/vnd.wap.slc application/vnd.wap.wbxml wbxml application/vnd.wap.wmlc wmlc application/vnd.wap.wmlscriptc wmlsc application/vnd.webturbo application/vnd.wrq-hp3000-labelled application/vnd.wt.stf application/vnd.wv.csp+wbxml application/vnd.xara application/vnd.xfdl application/vnd.yamaha.hv-dic application/vnd.yamaha.hv-script application/vnd.yamaha.hv-voice application/vnd.yellowriver-custom-menu application/voicexml+xml vxml application/watcherinfo+xml application/whoispp-query application/whoispp-response application/wita application/wordperfect5.1 application/x-bcpio bcpio application/x-cdlink vcd application/x-chess-pgn pgn application/x-compress application/x-cpio cpio application/x-csh csh application/x-director dcr dir dxr application/x-dvi dvi application/x-font-ttf ttf application/x-font-woff woff application/x-futuresplash spl application/x-gtar gtar application/x-gzip application/x-hdf hdf application/x-javascript js mjs application/x-koan skp skd skt skm application/x-latex latex application/x-netcdf nc cdf application/x-sh sh application/x-shar shar application/x-shockwave-flash swf application/x-stuffit sit application/x-sv4cpio sv4cpio application/x-sv4crc sv4crc application/x-tar tar application/x-tcl tcl application/x-tex tex application/x-texinfo texinfo texi application/x-troff t tr roff application/x-troff-man man application/x-troff-me me application/x-troff-ms ms application/x-ustar ustar application/x-wais-source src application/x400-bp application/xhtml+xml xhtml xht application/xslt+xml xslt application/xml xml xsl application/xml-dtd dtd application/xml-external-parsed-entity application/zip zip audio/32kadpcm audio/amr audio/amr-wb audio/basic au snd audio/cn audio/dat12 audio/dsr-es201108 audio/dvi4 audio/evrc audio/evrc0 audio/g722 audio/g.722.1 audio/g723 audio/g726-16 audio/g726-24 audio/g726-32 audio/g726-40 audio/g728 audio/g729 audio/g729D audio/g729E audio/gsm audio/gsm-efr audio/l8 audio/l16 audio/l20 audio/l24 audio/lpc audio/midi mid midi kar audio/mpa audio/mpa-robust audio/mp4a-latm audio/mpeg mpga mp2 mp3 audio/parityfec audio/pcma audio/pcmu audio/prs.sid audio/qcelp audio/red audio/smv audio/smv0 audio/telephone-event audio/tone audio/vdvi audio/vnd.3gpp.iufp audio/vnd.cisco.nse audio/vnd.cns.anp1 audio/vnd.cns.inf1 audio/vnd.digital-winds audio/vnd.everad.plj audio/vnd.lucent.voice audio/vnd.nortel.vbk audio/vnd.nuera.ecelp4800 audio/vnd.nuera.ecelp7470 audio/vnd.nuera.ecelp9600 audio/vnd.octel.sbc audio/vnd.qcelp audio/vnd.rhetorex.32kadpcm audio/vnd.vmx.cvsd audio/x-aiff aif aiff aifc audio/x-alaw-basic audio/x-mpegurl m3u audio/x-pn-realaudio ram ra audio/x-pn-realaudio-plugin application/vnd.rn-realmedia rm audio/x-wav wav chemical/x-pdb pdb chemical/x-xyz xyz image/bmp bmp image/cgm cgm image/g3fax image/gif gif image/ief ief image/jpeg jpeg jpg jpe image/naplps image/png png image/prs.btif image/prs.pti image/svg+xml svg image/t38 image/tiff tiff tif image/tiff-fx image/vnd.cns.inf2 image/vnd.djvu djvu djv image/vnd.dwg image/vnd.dxf image/vnd.fastbidsheet image/vnd.fpx image/vnd.fst image/vnd.fujixerox.edmics-mmr image/vnd.fujixerox.edmics-rlc image/vnd.globalgraphics.pgb image/vnd.mix image/vnd.ms-modi image/vnd.net-fpx image/vnd.svf image/vnd.wap.wbmp wbmp image/vnd.xiff image/x-cmu-raster ras image/x-icon ico image/x-portable-anymap pnm image/x-portable-bitmap pbm image/x-portable-graymap pgm image/x-portable-pixmap ppm image/x-rgb rgb image/x-xbitmap xbm image/x-xpixmap xpm image/x-xwindowdump xwd message/delivery-status message/disposition-notification message/external-body message/http message/news message/partial message/rfc822 message/s-http message/sip message/sipfrag model/iges igs iges model/mesh msh mesh silo model/vnd.dwf model/vnd.flatland.3dml model/vnd.gdl model/vnd.gs-gdl model/vnd.gtw model/vnd.mts model/vnd.parasolid.transmit.binary model/vnd.parasolid.transmit.text model/vnd.vtu model/vrml wrl vrml multipart/alternative multipart/appledouble multipart/byteranges multipart/digest multipart/encrypted multipart/form-data multipart/header-set multipart/mixed multipart/parallel multipart/related multipart/report multipart/signed multipart/voice-message text/calendar ics ifb text/css css text/directory text/enriched text/html html htm text/parityfec text/plain asc txt text/prs.lines.tag text/rfc822-headers text/richtext rtx text/rtf rtf text/sgml sgml sgm text/t140 text/tab-separated-values tsv text/uri-list text/vnd.abc text/vnd.curl text/vnd.dmclientscript text/vnd.fly text/vnd.fmi.flexstor text/vnd.in3d.3dml text/vnd.in3d.spot text/vnd.iptc.nitf text/vnd.iptc.newsml text/vnd.latex-z text/vnd.motorola.reflex text/vnd.ms-mediapackage text/vnd.net2phone.commcenter.command text/vnd.sun.j2me.app-descriptor text/vnd.wap.si text/vnd.wap.sl text/vnd.wap.wml wml text/vnd.wap.wmlscript wmls text/x-setext etx text/xml text/xml-external-parsed-entity video/bmpeg video/bt656 video/celb video/dv video/h261 video/h263 video/h263-1998 video/h263-2000 video/jpeg video/mp1s video/mp2p video/mp2t video/mp4v-es video/mpv video/mpeg mpeg mpg mpe video/nv video/parityfec video/pointer video/quicktime qt mov video/smpte292m video/vnd.fvt video/vnd.motorola.video video/vnd.motorola.videop video/vnd.mpegurl mxu m4u video/vnd.nokia.interleaved-multimedia video/vnd.objectvideo video/vnd.vivo video/x-msvideo avi video/x-sgi-movie movie x-conference/x-cooltalk ice belenios-1.17/demo/ocsigenserver.conf.in0000644000650106067230000000632114115601117017156 0ustar glondused 127.0.0.1:8001 _SHAREDIR_/mime.types _VARDIR_/log _VARDIR_/lib _VARDIR_/upload 5120kB 500 _RUNDIR_/ocsigenserver_command utf-8