�Q���<��J-ò����#�I)�$��1[�k���o ��3����,����\�é�',��L�L��t�6��}g�ZW٣J��Fs/#����㒤^��=Z�T*!��!��0`�$''���Os:@U)�z�@rr2F��~���w��زeRRRj%���`�Xp��!���B$�m۶(//G~~~�K����ฤ��y��2�dE��*�vm,�,XhL��esK5�S�Ee�8x�˲�� �N� o�us�J�_��C�$=In�%$�F�7Z���*]l���>�ã�%]��y�R��w�X�!�*Gb�X�yw��PW��q�^�z3��������{�)��ϲ�ǵL:���*�>}�`�С4h>����Zg�V�M�P���駟�c�6�&MBAArrr�JRY,>|aaa�i�z��ѣGQPPPg��q1@k�n�gb�>0���W"��� aaY������C��������/�͉o恝y�V1'�Xq|�b1E�� ��z�%�\��pN�{��i�k�sq�H��~��֭�B��ܹs������ϳ���������Я_�'n߱,��۷��ݻ���
������5�cݺuX�x18fϞ�O>��;O --
_}��/_ooo̟?.�T*E}���$���l�>} �Z#�4�#*^z�UVIEEEX�p!6m�1b�f�����,̜9111�H$X�jƌ77�:�$A�{�z�A�P(Dppp��OcK6DU�R�$@x<RRRp��E�S#i��rq��1�8q�۷DŽ ХK�Z�
99u���$I�ڵk`Y�[���CNN.^�X'd��AT%���z8bU�d���HMM���+�u]} �����a4�P(PXX��Aq�s�"�0�Ʊc����x���a6��T�4M�h4"..�����Ӑ������gډa��5K�B�effbѢE8s�z��~�����@ x�-�"�eY�^t **
s��ŭ[�0|�p�\�ݻw�x�?�X5����S��h�z�j�h�����{g��$$$��ٳ`Y��C�+B����o�x���EFF���[DFF���`�ĉX�z5|||��`�(
999�E��b����M��%,��y�Nnn.�͛��]��o߾������4����v�Z��2V�t:���C��(8@IDATV?�3^L&�Y�ӱ6��!�ry�D"���s�H�I�HIIAJJ
ڵk����l�������_c�,[ㄅ:������oP�{�UmbbbvVVV�N���byno��;w3f���ݻ_H�� ������SSS'>|�&������m۶������x����E�a$&&V|Q�0�b֬Y�U�V
/��nr����Ǎ7QQQ=KJJ��z��W�z��b.//_{�̙�V�
:�b��&��7o��&z\\�z������'��(���x���;�iӦwnݺU:}���ݻw7��4�8^�q�ȑ��G�5���-�r�M^�?,��w�;���<<ȸ�����o�7~�H��5EGGoزe�O^^����R
�V��?��ԩS��h4��;v�x9L��a�:u*����yVVV#��0�d2�ڹ(�@�R]����v��kK�.�W_}��٬/� �����?�fsQTT�W[�l�T��,�6�|!��h��̌�:}�t�̙3������+�W.o����w(�9r������������t��h�t���������ul�{! ((111 ���'K$��)�Ց
gQ(7ފ��Ϭ\WPP�W�t"h�?�۹s���To�N7U��?uEQ())�*,,�6m�����o��6l�I�j���%߶mۏ~3f�v�@0� AM�>�ʕ+�^����u{��M�M�U����_ޕ���4Mt��U�'I��媒���{���[�z�Os�̱M���<!!!-<==��0k4���۷�-^�x���ߊ���������m�l�S���5j�
r��Շ�I���&�l��l���W��L ��BIEND�B`�belenios-1.4+dfsg/src/static/placeholder.png000066400000000000000000000200251307140314400211370ustar00rootroot00000000000000�PNG
IHDR�ZI���bKGD������� pHYs��tIME�6��}��IDATx��wtTe��?�Ν>�J*!����E�Bi����"�8�XV^dWv�]\EW|-��.HA:�T!�H�$�L����c��` $0�s8��-�>�{��=*nr��B�P:G������;6c{�����9d)eb�$�6�#%:����o�L��pv��|�dv�����I�j�d��X>�#C�bI0��>�� ��n��|`'�I���Ʉjԁ�3��M1k��j$L�����#�eo�?����M����=yV�z��{"p��%h%��vW�mA���f���o��L�����4�ޤ^l���H��<ޯ�����y�{���W:�@���$4p5o�H1�_��P�t�L�&�����C)u{Xp�
>�����svd�Y��� `T��ȶ�6�.`l�FA�Ume����S����p�
.�Y����c�$�yJ
�X��]J��Cx�sx�Jz��Y3^�,���ӥ���'�o�I��/��4�HF�-ȸ ��A�1�U|v�,)ѡ�j��ށ�R���##�xgW��%��P�:d��8���Z��\��'�s�f��s�J$Ѭ�.H���`��X��ݹ��.���bl� (�
�|�t���dQDP@W�MY|2�Ǎ�������Ǵi�8xЯ���u�ĻY���c0��� ::EQPP���@�zPD�"��ϋ����x$��k��Ȓ�%gD�9��_Kk-D�$<��r
x<�V+���l۶��s������G�w#���_�;��l6�PA@PTn��%� �))�� ���C�`�>�,��G�cv��3أ��뗓=;;����r�ʠīϸP}}�����`2���� z=��N��P�����Pd�
���
��KB�E�B��"z=x�!��N$�gX��|a�~�Y
DQ���(�8p�ɓ'�Ļa2�'O�n�ܹlݺ��o���
z�J�F�I��������4{%��bcc���(֙AT�r9Pۭ(����B�v"K$�
�ZP�ΐȋ�@�eT*���DEEѻwo�/_�����L�:5(�g,]��VKXXf���D������rQj��gƳ��n5i0��iT�*V�ȥ{����/'"<�݊*:���n��� U.>���ע(���(**��p����/����>�;�$���O?����d�E��ꐝ�ͫ��ʞ={ۆ$�p�j#V�E6�.`HR_����?���t�ڍ��xdYF�y�Z-8"bd/zK.�Z�+4�J��PA���e���g�O�>lܸ1H�k�F�x����˗c00����K��$��館������ù3*�VC�^���e��\z�G��l!} �^֟-������G��I����#�2ڢ<�^� P��% B��RpҤIA�]�����[oѴiS�z�eI������M���q��d\\��^@��%�z=-o�@��1t�P�=JYfmb�I0��K�glNZF��t/��d>�*p���L�:�8A@ŀ-ۻwo��� ��eee�k���H^^���u&�*��j1����Ӈ;��!��5����/�͟��(����-a&B4kw�$������7�%a2����W�ެV+{��eԨQt�ڵ^�{�mѢǏ��v�
>�Q�Fa6� A�˶�j"a.%EE�����k��56�P��R�IA�-��#&��ϛ��|�r���q�˽�ǃ�f�����Ç�ں��?��h�b��bŊ��~��1|�p�F# '��!�"�fݢ��=> =���fYJ�s�[��㯁s��}ڷo�Z��,�egg���ƌ3*�;v,�:uB��b�Za� �F���hD�Ng�e�����t�g�}Fll,��JU�$E�˅����<��#t��@��\^�p�B���0�ͨT�Z]�n�c�Z���fܸq��Ϛ5���0L&�$a�۱��<��3A�]m�Ue�Y,��Tx̜9�����ï�����ߧ�f����Wfr{�����^o�����민����3����+�����N��t]��$"� Ȳ�����v�hѢJ��Ν�-�܂�hD���) +�@�ł�VR^�P��Ya����RRR��͛+e'f̘ADD�����A�]C�]�ǃ��dٲex��@Կ[�n����DDD I�_�ii)�?>�m�������_�~���_҃�w$^�^/����L�n��>e�z��$I5|-TqU���륰����}��Wi�o�AHH&� �Z]�{��픕������{��,�t:9q���͛7�͟?����:��]
gϞ%##��>���������4oޜ���Z_�f�QVV�s�=$��M�
�|>dY&//�5k���L&/^�$IDFF֙',�"���Bqq1cƌ�������h4b*�t\�=��vl6'N�7Ļ�7�T*T* �3���2A`�ʕ�{a�D�v툊������r��F���qn��Oz�(�G�槟~
�?k�,4hpM�*�9�1�Ɠa�s�����6�9�O\�Z�%�v��ȱp_R�
�ШDvd[�8��~8ʧ�����w�z��K/��~�kՈ��蟫� ����'���yeN���٪p`�]��5��u�Q�Y�۳+��BBBx��X�n�����#�x<���V���x�
bcc��D�-�Vߓ�=�әyg�4oH�V��s�N�^gʜ�\��ڿ�~�3z�^>�߁�� f��\���@���k�������5��µj�[�-���E�[C��(D��"��ad=ڇ�V���
�Ƈ������<�:���!��4(w՛�REf�\^��χ�z��3��\�)@�N�GV�)��}�1�kԀ�&������g�s���X���;�[WK�
R]��ʶb}@DD�z�"..��� �Ǫ+wze�JF�N���'6��^mIY����yX]^dEA��l��^�܌�G���.<�� ;�Zs�'�N�y�����v'�L:�40�M�w���S<��������?^0(�]�����Kk���u����n����J���O��xjzV����0���7�?/�Wa�=���
�F�^K��C�V�QR��_.
~�.Z-���]Z0%�p�T�G�I�n�z�!]_��a�iifb�&���2��(�`��?��������v�yꩧ���c���>}���I��:}ry���l��'������ܗv�
id����/�Х���ǩm��I�Z5�&=��ޚ�w�|��B��,��!�?UrGL��R幵 _L]_
[*11��S�2e�+u(�0�M�~�4D#�`rL�IW��m��Q�u�x�۽5:'L�&cLo�o�O�
�;\�%7����a�q����C5]c�������E��0u�T�|�I����e���Y+�
�2����t_��<������=����y��n�����,Ԉ���s.+�kh���W
����]����&���l�>�R����^� ���|{:�^/++��ӧ����;2}�t&L��N��j)0��K||<�
�S�Nu�1�7�Q%
�؞$��]d�9IY��Z����㩁5DQ�������i������9�֭�G�:�����}�v/^�Z�����鈎��j�"I��Ν;�R�8r�H�;4�V������44jij$Ѭ�t|r�.rN�* �$�`�#���p��_�2��)����~��9��"R��U��Ofl�F���!V;�ɡ��Z)vy�,�����o;�����/��AR��(��B?+������h�������.P�<��E2]#��i�U�j�b�X���`������QTTtY�.��*�]����4F�A�~�HMM��O?%==�V��b|>6l`���FڷoOII ���W]�UgG�(O�o��NZG�ѪDb��((�<>|�B��EZ^#����(�M*�����#{oԡED�F�Q�O��9Y���ν�tUo䆟��@��+~!\>w��ɱ���i���çsl�e;��^m(.���(������}U�C�O�N�>}x衇���{ٸq#+W��M�R�n�$I�������d����?NNNYYY�%�|>7ndРAH�D�ټy3999WM�],XQ����/ ���d���>Eᐥ�l�����cs+v��b�gx�x�;K������`RI4
�!+`qz�(�qoy�m�]���փ�&�k\8���vΈ#HII L�ٷo��Z�v�y�'�������c�ҥ:t��[�r��4i҄��|<�sE~�!�q�9���`00`�l66l���B����$ ������Z��@TT���dff�s��J/^�R1l�0?)V��ta�r�r�x��'�Mا�-���h��l̛7�I�&q��Ə�̙3�ҥK��r���EEE<��3̘1��-[V�B5
gϞ�r_m�
��ʦM��e�Z̀jTZU!�w��Y��]�\�W�(--����f֬Y�>}�q��1}�tZ�ly���v;���*s�̡���^x�?��O$''�I<��|���$Y�~�.��S��>���'O^|��z6O��/)UQ�1g��N��Z�����W_�h_<�^ϡC���?��̙3ILLd���L�2����:)iE���L��Οj2�0�F��o�ox❯����2e
�|� #F����^��?q�'Nd��Ř�f�|�MF�Mll�U�$� p��)8�{���~9G}T�7ԄnA�j������?"B�^W��زe۶m�cǎ<��ct�ڕ7�|�����.�<��(�m��VKVV?���U ��;�*IW!M*�#VEJY�IKKc߾}�s�=u}���� ���?�v�)..&77����A��z��$�-[�Ю];���?Z�Vu���f�ʕddd�i��رc\Q&F��k6�2H��Po�����ڵ���T>�����^���-x=CQ� �w�0�|^x��9°a�x�7�ٳ���~OǪ�}87m�(����ٳiժ��G����Y����EQ�8�q�B �$]w��A�wh4�;ƻ�����Q�Ռ7�ٳg���tE�J�"++뚨@��DÆ
���_�m�Z�nh�Z�())�o�������8��^�6mJqq1999���={�0dȐj��������dee]�W����ի���j�b�`6�뼆MEDQ$==���t:t�����iݺ5_|�_}�U�=ϊ��ת���b�wk�^��[�~=}��%..�F�!\�ؽ{7{��o߾u�8�堢��������5k�sa2�X�p!-[�$%%嚨^�J�,ˬ_�>��zAEйb%�6m�ԿC}��U�VѢE~���ϟOAAA` ћٖ��v�� �C=��%^��1�a������t]I��©S���O8r�H�Ο�/$�5@jj*�6m��xk��:_�Z�p�jvv6ӧO�7��~������+H�h�">��SΞ=[�RGա���~����z
��\�%K���wCd.*�`|�����ҨQ�@�S}���,c��())aҤI���+�����L��cMϞ=ٶm[@�ƫ��d���TO�:�+������/{݊����m�6z��P�������]��j�(�t:���eΜ9�74�nH�l�~�m��baѢE���ϸ\��F�*�#G;v,�=�\�c�Y �9d����K���h�� ��ZAp�\����c��.]�͊��:��t�f���=z�������3a�&N�H�-��q���X�����#G"IR��[Qѕ�������ɓ����!=� �j@�����*�Ç�l6_�,��(�f͚ߔ+���p��.=1��K���8�����T*
����駟f�������x�"!!��0z�h�z}����x<�߿�ٳgWڷjժ��%^�8� ����X,�l�J�k+���HII |���̞=��>8��pʕ���4l�A��l|��gL�:��͛���Ho�UàA���5jDJJJ%iW���ă"� �"� ��f����`A�f��IEND�B`�belenios-1.4+dfsg/src/static/site.css000066400000000000000000000010141307140314400176220ustar00rootroot00000000000000@import url('style.css');
@import url('superfish.css');
#page-title-home {
height:223px;
}
ol {
list-style: decimal;
}
ul {
list-style: disc;
}
#wrapper {
min-height: 0px;
font-size: 16px;
}
#header {
color: #ffffff;
height: auto;
}
#main {
border-radius: 0px 0px 0px 0px;
padding: 10px;
}
#footer #bottom {
line-height: 3em;
}
#header a {
color: #ffffff;
}
button {
cursor: pointer;
}
.current_step {
text-align: center;
font-size: 28px;
padding-bottom: 28px;
}
belenios-1.4+dfsg/src/static/vote.html.itarget000066400000000000000000000002041307140314400214450ustar00rootroot00000000000000sjcl.js
jsbn.js
jsbn2.js
random.js
booth.js
encrypting.gif
reset.css
styled-elements.css
style.css
superfish.css
booth.css
site.css
belenios-1.4+dfsg/src/tool/000077500000000000000000000000001307140314400156365ustar00rootroot00000000000000belenios-1.4+dfsg/src/tool/belenios-tool.html000066400000000000000000000152311307140314400213010ustar00rootroot00000000000000
Belenios Tool
Belenios Tool
Unit tests
Trustee key generation
Generated key identifier
Generated secret key
Generated public key
Credential management
Generate anonymous credentials
Number of credentials to generate:
Generate credentials with identity matching
Generated private credentials
Generated public credentials
Fingerprints of generated public credentials
Check a credential
Credential:
Election management
Election parameters
Trustee public keys
Public credentials
Ballot creation
Secret credential:
Plaintext choices:
Ballots
Partial decryption
Private key:
Election result
Partial decryptions:
belenios-1.4+dfsg/src/tool/tool_cmdline.ml000066400000000000000000000430661307140314400206510ustar00rootroot00000000000000(**************************************************************************)
(* BELENIOS *)
(* *)
(* Copyright © 2012-2016 Inria *)
(* *)
(* 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. *)
(* *)
(* You should have received a copy of the GNU Affero General Public *)
(* License along with this program. If not, see *)
(* . *)
(**************************************************************************)
open Serializable_j
open Common
open Cmdliner
let stream_to_list s =
let res = ref [] in
Stream.iter (fun x -> res := x :: !res) s;
List.rev !res
let lines_of_file fname =
let ic = open_in fname in
Stream.from (fun _ ->
try Some (input_line ic)
with End_of_file -> close_in ic; None
)
let string_of_file f =
lines_of_file f |> stream_to_list |> String.concat "\n"
let load_from_file of_string filename =
if Sys.file_exists filename then (
Printf.eprintf "I: loading %s...\n%!" (Filename.basename filename);
Some (lines_of_file filename |> stream_to_list |> List.rev_map of_string)
) else None
let ( / ) = Filename.concat
let download dir url file =
let url = if url.[String.length url - 1] = '/' then url else url ^ "/" in
Printf.eprintf "I: downloading %s...\n%!" file;
let target = dir / file in
let command =
Printf.sprintf "curl --silent --fail \"%s%s\" > \"%s\"" url file target
in
let r = Sys.command command in
if r <> 0 then (Sys.remove target; false) else true
let rm_rf dir =
let files = Sys.readdir dir in
Array.iter (fun f -> Unix.unlink (dir / f)) files;
Unix.rmdir dir
exception Cmdline_error of string
let failcmd fmt = Printf.ksprintf (fun x -> raise (Cmdline_error x)) fmt
let common_man = [
`S "MORE INFORMATION";
`P "This command is part of the Belenios command-line tool.";
`P "To get more help on a specific subcommand, run:";
`P "$(b,belenios-tool) $(i,COMMAND) $(b,--help)";
`P "See $(i,http://www.belenios.org/).";
]
let get_mandatory_opt name = function
| Some x -> x
| None -> failcmd "%s is mandatory" name
let wrap_main f =
try
let () = f () in `Ok ()
with
| Cmdline_error e -> `Error (true, e)
| Failure e -> `Error (false, e)
| e -> `Error (false, Printexc.to_string e)
module type CMDLINER_MODULE = sig
val cmds : (unit Cmdliner.Term.t * Cmdliner.Term.info) list
end
let group_t =
let doc = "Take group parameters from file $(docv)." in
Arg.(value & opt (some file) None & info ["group"] ~docv:"GROUP" ~doc)
let uuid_t =
let doc = "UUID of the election." in
Arg.(value & opt (some string) None & info ["uuid"] ~docv:"UUID" ~doc)
let dir_t, optdir_t =
let doc = "Use directory $(docv) for reading and writing election files." in
let the_info = Arg.info ["dir"] ~docv:"DIR" ~doc in
Arg.(value & opt dir Filename.current_dir_name the_info),
Arg.(value & opt (some dir) None the_info)
let url_t =
let doc = "Download election files from $(docv)." in
let the_info = Arg.info ["url"] ~docv:"URL" ~doc in
Arg.(value & opt (some string) None the_info)
module Tkeygen : CMDLINER_MODULE = struct
open Tool_tkeygen
let main group =
wrap_main (fun () ->
let module P = struct
let group = get_mandatory_opt "--group" group |> string_of_file
end in
let module R = (val make (module P : PARAMS) : S) in
let kp = R.trustee_keygen () in
Printf.printf "I: keypair %s has been generated\n%!" kp.R.id;
let pubkey = "public", kp.R.id ^ ".pubkey", 0o444, kp.R.pub in
let privkey = "private", kp.R.id ^ ".privkey", 0o400, kp.R.priv in
let save (kind, filename, perm, thing) =
let oc = open_out_gen [Open_wronly; Open_creat] perm filename in
output_string oc thing;
output_char oc '\n';
close_out oc;
Printf.printf "I: %s key saved to %s\n%!" kind filename;
(* set permissions in the unlikely case where the file already existed *)
Unix.chmod filename perm
in
save pubkey;
save privkey
)
let tkeygen_cmd =
let doc = "generate a trustee key" in
let man = [
`S "DESCRIPTION";
`P "This command is run by a trustee to generate a share of an election key. Such a share consists of a private key and a public key with a certificate. Generated files are stored in the current directory with a name that starts with $(i,ID), where $(i,ID) is a short fingerprint of the public key. The private key is stored in $(i,ID.privkey) and must be secured by the trustee. The public key is stored in $(i,ID.pubkey) and must be sent to the election administrator.";
] @ common_man in
Term.(ret (pure main $ group_t)),
Term.info "trustee-keygen" ~doc ~man
let cmds = [tkeygen_cmd]
end
module Election : CMDLINER_MODULE = struct
open Tool_election
module MakeGetters (X : sig val dir : string end) = struct
let get_public_keys () =
load_from_file (fun x -> x) (X.dir/"public_keys.jsons") |>
option_map Array.of_list
let get_public_creds () =
let file = "public_creds.txt" in
Printf.eprintf "I: loading %s...\n%!" file;
try Some (lines_of_file (X.dir / file))
with _ -> None
let get_ballots () =
let file = "ballots.jsons" in
Printf.eprintf "I: loading %s...\n%!" file;
try Some (lines_of_file (X.dir / file))
with _ -> None
let get_result () =
load_from_file (fun x -> x) (X.dir/"result.json") |> function
| None -> None
| Some [r] -> Some r
| _ -> failwith "invalid result"
let print_msg = prerr_endline
end
let main url dir action =
wrap_main (fun () ->
let dir, cleanup = match url, dir with
| Some _, None ->
let tmp = Filename.temp_file "belenios" "" in
Unix.unlink tmp;
Unix.mkdir tmp 0o700;
tmp, true
| None, None -> Filename.current_dir_name, false
| _, Some d -> d, false
in
Printf.eprintf "I: using directory %s\n%!" dir;
let () =
match url with
| None -> ()
| Some u ->
if not (
download dir u "election.json" &&
download dir u "public_keys.jsons" &&
download dir u "public_creds.txt" &&
download dir u "ballots.jsons" &&
download dir u "result.json"
) then
Printf.eprintf "W: some errors occurred while downloading\n%!";
in
let module P : PARAMS = struct
include MakeGetters (struct let dir = dir end)
let election =
let fname = dir/"election.json" in
load_from_file (fun x -> x) fname |>
function
| Some [e] -> e
| None -> failcmd "could not read %s" fname
| _ -> Printf.ksprintf failwith "invalid election file: %s" fname
end in
let module X = (val make (module P : PARAMS) : S) in
begin match action with
| `Vote (privcred, ballot) ->
let ballot =
match load_from_file plaintext_of_string ballot with
| Some [b] -> b
| _ -> failwith "invalid plaintext ballot file"
and privcred =
match load_from_file (fun x -> x) privcred with
| Some [cred] -> cred
| _ -> failwith "invalid credential"
in
print_endline (X.vote (Some privcred) ballot)
| `Decrypt privkey ->
let privkey =
match load_from_file (fun x -> x) privkey with
| Some [privkey] -> privkey
| _ -> failwith "invalid private key"
in
print_endline (X.decrypt privkey)
| `Verify -> X.verify ()
| `Finalize ->
let factors =
let fname = dir/"partial_decryptions.jsons" in
match load_from_file (fun x -> x) fname with
| Some factors -> Array.of_list factors
| None -> failwith "cannot load partial decryptions"
in
let oc = open_out (dir/"result.json") in
output_string oc (X.finalize factors);
output_char oc '\n';
close_out oc
end;
if cleanup then rm_rf dir
)
let privcred_t =
let doc = "Read private credential from file $(docv)." in
let the_info = Arg.info ["privcred"] ~docv:"PRIV_CRED" ~doc in
Arg.(value & opt (some file) None the_info)
let privkey_t =
let doc = "Read private key from file $(docv)." in
let the_info = Arg.info ["privkey"] ~docv:"PRIV_KEY" ~doc in
Arg.(value & opt (some file) None the_info)
let ballot_t =
let doc = "Read ballot choices from file $(docv)." in
let the_info = Arg.info ["ballot"] ~docv:"BALLOT" ~doc in
Arg.(value & opt (some file) None the_info)
let vote_cmd =
let doc = "create a ballot" in
let man = [
`S "DESCRIPTION";
`P "This command creates a ballot and prints it on standard output.";
] @ common_man in
let main = Term.pure (fun u d p b ->
let p = get_mandatory_opt "--privcred" p in
let b = get_mandatory_opt "--ballot" b in
main u d (`Vote (p, b))
) in
Term.(ret (main $ url_t $ optdir_t $ privcred_t $ ballot_t)),
Term.info "vote" ~doc ~man
let verify_cmd =
let doc = "verify election data" in
let man = [
`S "DESCRIPTION";
`P "This command performs all possible verifications.";
] @ common_man in
Term.(ret (pure main $ url_t $ optdir_t $ pure `Verify)),
Term.info "verify" ~doc ~man
let decrypt_cmd =
let doc = "perform partial decryption" in
let man = [
`S "DESCRIPTION";
`P "This command is run by each trustee to perform a partial decryption.";
] @ common_man in
let main = Term.pure (fun u d p ->
let p = get_mandatory_opt "--privkey" p in
main u d (`Decrypt p)
) in
Term.(ret (main $ url_t $ optdir_t $ privkey_t)),
Term.info "decrypt" ~doc ~man
let finalize_cmd =
let doc = "finalizes an election" in
let man = [
`S "DESCRIPTION";
`P "This command reads partial decryptions done by trustees from file $(i,partial_decryptions.jsons), checks them, combines them into the final tally and prints the result to standard output.";
`P "The result structure contains partial decryptions itself, so $(i,partial_decryptions.jsons) can be discarded afterwards.";
] @ common_man in
Term.(ret (pure main $ url_t $ optdir_t $ pure `Finalize)),
Term.info "finalize" ~doc ~man
let cmds = [vote_cmd; verify_cmd; decrypt_cmd; finalize_cmd]
end
module Credgen : CMDLINER_MODULE = struct
open Tool_credgen
let params_priv = "private credentials with ids", ".privcreds", 0o400
let params_pub = "public credentials", ".pubcreds", 0o444
let params_hash = "hashed public credentials with ids", ".hashcreds", 0o400
let save (info, ext, perm) basename things =
let fname = basename ^ ext in
let oc = open_out_gen [Open_wronly; Open_creat; Open_excl] perm fname in
let count = ref 0 in
List.iter (fun x ->
incr count;
output_string oc x;
output_string oc "\n";
) things;
close_out oc;
Printf.printf "%d %s saved to %s\n%!" !count info fname
let main group dir uuid count file derive =
wrap_main (fun () ->
let module P = struct
let group = get_mandatory_opt "--group" group |> string_of_file
let uuid = get_mandatory_opt "--uuid" uuid
end in
let module R = (val make (module P : PARAMS) : S) in
let action =
match count, file, derive with
| Some n, None, None ->
if n < 1 then (
failcmd "the argument of --count must be a positive number"
) else `Generate (generate_ids n)
| None, Some f, None -> `Generate (lines_of_file f |> stream_to_list)
| None, None, Some c -> `Derive c
| _, _, _ ->
failcmd "--count, --file and --derive are mutually exclusive"
in
match action with
| `Derive c ->
print_endline (R.derive c)
| `Generate ids ->
let privs, pubs, hashs =
List.fold_left (fun (privs, pubs, hashs) id ->
let priv, pub, hash = R.generate () in
let priv = id ^ " " ^ priv and hash = id ^ " " ^ hash in
priv::privs, pub::pubs, hash::hashs
) ([], [], []) ids
in
let timestamp = Printf.sprintf "%.0f" (Unix.time ()) in
let base = dir / timestamp in
save params_priv base (List.rev privs);
save params_pub base (List.sort compare pubs);
save params_hash base (List.rev hashs)
)
let count_t =
let doc = "Generate $(docv) credentials." in
let the_info = Arg.info ["count"] ~docv:"N" ~doc in
Arg.(value & opt (some int) None the_info)
let file_t =
let doc = "Read identities from $(docv) and generate an additional $(i,T.hashcreds) with identities associated with hashed public credentials. These hashed public credentials are used by the hotline to update a public credential on the web server. One credential will be generated for each line of $(docv)." in
let the_info = Arg.info ["file"] ~docv:"FILE" ~doc in
Arg.(value & opt (some file) None the_info)
let derive_t =
let doc = "Derive the public key associated to a specific $(docv)." in
let the_info = Arg.info ["derive"] ~docv:"PRIVATE_CRED" ~doc in
Arg.(value & opt (some string) None the_info)
let credgen_cmd =
let doc = "generate credentials" in
let man = [
`S "DESCRIPTION";
`P "This command is run by a credential authority to generate credentials for a specific election. The generated private credentials are stored in $(i,T.privcreds), where $(i,T) is a timestamp. $(i,T.privcreds) contains one credential per line. Each voter must be sent a credential, and $(i,T.privcreds) must be destroyed after dispatching is done. The associated public keys are stored in $(i,T.pubcreds) and must be sent to the election administrator.";
] @ common_man in
Term.(ret (pure main $ group_t $ dir_t $ uuid_t $ count_t $ file_t $ derive_t)),
Term.info "credgen" ~doc ~man
let cmds = [credgen_cmd]
end
module Mkelection : CMDLINER_MODULE = struct
open Tool_mkelection
let main dir group uuid template =
wrap_main (fun () ->
let module P = struct
let group = get_mandatory_opt "--group" group |> string_of_file
let uuid = get_mandatory_opt "--uuid" uuid
let template = get_mandatory_opt "--template" template |> string_of_file
let get_public_keys () =
Some (lines_of_file (dir / "public_keys.jsons") |> stream_to_list |> Array.of_list)
end in
let module R = (val make (module P : PARAMS) : S) in
let params = R.mkelection () in
let oc = open_out (dir / "election.json") in
output_string oc params;
output_char oc '\n';
close_out oc
)
let template_t =
let doc = "Read election template from file $(docv)." in
Arg.(value & opt (some file) None & info ["template"] ~docv:"TEMPLATE" ~doc)
let mkelection_cmd =
let doc = "create an election public parameter file" in
let man = [
`S "DESCRIPTION";
`P "This command reads and checks $(i,public_keys.jsons). It then computes the global election public key and generates an $(i,election.json) file.";
] @ common_man in
Term.(ret (pure main $ dir_t $ group_t $ uuid_t $ template_t)),
Term.info "mkelection" ~doc ~man
let cmds = [mkelection_cmd]
end
module Verifydiff : CMDLINER_MODULE = struct
open Tool_verifydiff
let main dir1 dir2 =
wrap_main (fun () ->
match dir1, dir2 with
| Some dir1, Some dir2 -> verifydiff dir1 dir2
| _, _ -> failcmd "--dir1 or --dir2 is missing"
)
let dir1_t =
let doc = "First directory to compare." in
Arg.(value & opt (some dir) None & info ["dir1"] ~docv:"DIR1" ~doc)
let dir2_t =
let doc = "Second directory to compare." in
Arg.(value & opt (some dir) None & info ["dir2"] ~docv:"DIR2" ~doc)
let verifydiff_cmd =
let doc = "verify an election directory update" in
let man = [
`S "DESCRIPTION";
`P "This command is run by an auditor on two directories $(i,DIR1) and $(i,DIR2). It checks that $(i,DIR2) is a valid update of $(i,DIR1).";
] @ common_man in
Term.(ret (pure main $ dir1_t $ dir2_t)),
Term.info "verify-diff" ~doc ~man
let cmds = [verifydiff_cmd]
end
let cmds = Tkeygen.cmds @ Election.cmds @ Credgen.cmds @ Mkelection.cmds @ Verifydiff.cmds
let default_cmd =
let open Belenios_version in
let version = Printf.sprintf "%s (%s)" version build in
let version = if debug then version ^ " [debug]" else version in
let doc = "election management tool" in
let man = common_man in
Term.(ret (pure (`Help (`Pager, None)))),
Term.info "belenios-tool" ~version ~doc ~man
let () =
match Term.eval_choice default_cmd cmds with
| `Error _ -> exit 1
| _ -> exit 0
belenios-1.4+dfsg/src/tool/tool_cmdline.mli000066400000000000000000000000261307140314400210070ustar00rootroot00000000000000(* empty interface *)
belenios-1.4+dfsg/src/tool/tool_credgen.ml000066400000000000000000000061721307140314400206420ustar00rootroot00000000000000(**************************************************************************)
(* BELENIOS *)
(* *)
(* Copyright © 2012-2016 Inria *)
(* *)
(* 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. *)
(* *)
(* You should have received a copy of the GNU Affero General Public *)
(* License along with this program. If not, see *)
(* . *)
(**************************************************************************)
open Platform
open Signatures
open Common
module type PARAMS = sig
val uuid : string
val group : string
end
module type S = sig
val derive : string -> string
val generate : unit -> string * string * string
end
module type PARSED_PARAMS = sig
val uuid : Uuidm.t
module G : GROUP
end
let parse_params p =
let module P = (val p : PARAMS) in
let module R = struct
let uuid =
match Uuidm.of_string P.uuid with
| Some u -> u
| None -> Printf.ksprintf failwith "%s is not a valid UUID" P.uuid
module G = (val Group.of_string P.group : GROUP)
end
in (module R : PARSED_PARAMS)
module Make (P : PARSED_PARAMS) : S = struct
open P
module CG = Credential.MakeGenerate (Election.MakeSimpleMonad (G))
module CD = Credential.MakeDerive (G)
let derive x =
let x = CD.derive uuid x in
let y = G.(g **~ x) in
G.to_string y
let compute_pub_and_hash priv =
let pub = derive priv in
let hashed = sha256_hex pub in
priv, pub, hashed
let generate () =
CG.generate () () |> compute_pub_and_hash
end
let make params =
let module P = (val parse_params params : PARSED_PARAMS) in
let module R = Make (P) in
(module R : S)
let int_length n =
string_of_int n |> String.length
let rec find_first n first =
if int_length first = int_length (first + n) then first
else find_first n (10 * first)
let generate_ids n =
(* choose the first id so that they all have the same length *)
let first = find_first n 1 in
let last = first + n - 1 in
let rec loop last accu =
if last < first then accu
else loop (last-1) (string_of_int last :: accu)
in loop last []
belenios-1.4+dfsg/src/tool/tool_credgen.mli000066400000000000000000000004021307140314400210010ustar00rootroot00000000000000module type PARAMS = sig
val uuid : string
val group : string
end
module type S = sig
val derive : string -> string
val generate : unit -> string * string * string
end
val make : (module PARAMS) -> (module S)
val generate_ids : int -> string list
belenios-1.4+dfsg/src/tool/tool_election.ml000066400000000000000000000143761307140314400210420ustar00rootroot00000000000000(**************************************************************************)
(* BELENIOS *)
(* *)
(* Copyright © 2012-2016 Inria *)
(* *)
(* 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. *)
(* *)
(* You should have received a copy of the GNU Affero General Public *)
(* License along with this program. If not, see *)
(* . *)
(**************************************************************************)
open Platform
open Serializable_j
open Signatures
open Common
module type PARAMS = sig
val election : string
val get_public_keys : unit -> string array option
val get_public_creds : unit -> string Stream.t option
val get_ballots : unit -> string Stream.t option
val get_result : unit -> string option
val print_msg : string -> unit
end
module type S = sig
val vote : string option -> int array array -> string
val decrypt : string -> string
val finalize : string array -> string
val verify : unit -> unit
end
module type PARSED_PARAMS = sig
include PARAMS
include ELECTION_DATA
end
let parse_params p =
let module P = (val p : PARAMS) in
let params = Group.election_params_of_string P.election in
let module R = struct
include P
include (val params : ELECTION_DATA)
end in
(module R : PARSED_PARAMS)
module Make (P : PARSED_PARAMS) : S = struct
open P
module M = Election.MakeSimpleMonad(G)
module E = Election.MakeElection(G)(M);;
(* Load and check trustee keys, if present *)
module KG = Election.MakeSimpleDistKeyGen(G)(M);;
let public_keys_with_pok =
get_public_keys () |> option_map @@
Array.map (trustee_public_key_of_string G.read)
let () =
match public_keys_with_pok with
| Some pks ->
assert (Array.forall KG.check pks);
let y' = KG.combine pks in
assert G.(election.e_params.e_public_key =~ y')
| None -> ()
let public_keys =
option_map (
Array.map (fun pk -> pk.trustee_public_key)
) public_keys_with_pok
(* Finish setting up the election *)
let pks = match public_keys with
| Some pks -> pks
| None -> failwith "missing public keys"
(* Load ballots, if present *)
module GSet = Map.Make (G)
let public_creds = lazy (
get_public_creds () |> option_map (fun creds ->
let res = ref GSet.empty in
Stream.iter (fun x -> res := GSet.add (G.of_string x) false !res) creds;
res
)
)
let ballots = lazy (
get_ballots () |> option_map (fun ballots ->
let res = ref [] in
Stream.iter (fun x ->
res := (ballot_of_string G.read x, sha256_b64 x) :: !res
) ballots;
List.rev !res
)
)
let check_signature_present = lazy (
match Lazy.force public_creds with
| Some creds -> (fun b ->
match b.signature with
| Some s ->
(try
if GSet.find s.s_public_key !creds then false
else (creds := GSet.add s.s_public_key true !creds; true)
with Not_found -> false)
| None -> false
)
| None -> (fun _ -> true)
)
let cast (b, hash) =
if Lazy.force check_signature_present b && E.check_ballot election b
then M.cast b ()
else Printf.ksprintf failwith "ballot %s failed tests" hash
let ballots_check = lazy (
Lazy.force ballots |> option_map (List.iter cast)
)
let encrypted_tally = lazy (
match Lazy.force ballots_check with
| None -> failwith "ballots.jsons is missing"
| Some () ->
M.fold (fun () b t ->
M.return (E.combine_ciphertexts (E.extract_ciphertext b) t)
) (E.neutral_ciphertext election) ()
)
let vote privcred ballot =
let sk =
privcred |> option_map (fun cred ->
let module CD = Credential.MakeDerive (G) in
CD.derive election.e_params.e_uuid cred
)
in
let b = E.create_ballot election ?sk (E.make_randomness election ()) ballot () in
assert (E.check_ballot election b);
string_of_ballot G.write b
let decrypt privkey =
let sk = number_of_string privkey in
let pk = G.(g **~ sk) in
if Array.forall (fun x -> not G.(x =~ pk)) pks then (
print_msg "W: your key is not present in public_keys.jsons";
);
let tally = Lazy.force encrypted_tally in
let factor = E.compute_factor tally sk () in
assert (E.check_factor tally pk factor);
string_of_partial_decryption G.write factor
let finalize factors =
let factors = Array.map (partial_decryption_of_string G.read) factors in
let tally = Lazy.force encrypted_tally in
assert (Array.forall2 (E.check_factor tally) pks factors);
let result = E.combine_factors (M.cardinal ()) tally factors in
assert (E.check_result pks result);
string_of_result G.write result
let verify () =
(match Lazy.force ballots_check with
| Some () -> ()
| None -> print_msg "W: no ballots to check"
);
(match get_result () with
| Some result ->
let result = result_of_string G.read result in
assert (Lazy.force encrypted_tally = result.encrypted_tally);
assert (E.check_result pks result)
| None -> print_msg "W: no result to check"
);
print_msg "I: all checks passed"
end
let make params =
let module P = (val parse_params params : PARSED_PARAMS) in
let module R = Make (P) in
(module R : S)
belenios-1.4+dfsg/src/tool/tool_election.mli000066400000000000000000000007761307140314400212120ustar00rootroot00000000000000module type PARAMS = sig
val election : string
val get_public_keys : unit -> string array option
val get_public_creds : unit -> string Stream.t option
val get_ballots : unit -> string Stream.t option
val get_result : unit -> string option
val print_msg : string -> unit
end
module type S = sig
val vote : string option -> int array array -> string
val decrypt : string -> string
val finalize : string array -> string
val verify : unit -> unit
end
val make : (module PARAMS) -> (module S)
belenios-1.4+dfsg/src/tool/tool_js.ml000066400000000000000000000227371307140314400176540ustar00rootroot00000000000000(**************************************************************************)
(* BELENIOS *)
(* *)
(* Copyright © 2012-2016 Inria *)
(* *)
(* 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. *)
(* *)
(* You should have received a copy of the GNU Affero General Public *)
(* License along with this program. If not, see *)
(* . *)
(**************************************************************************)
open Platform
open Serializable_j
open Tool_js_common
let install_handler (id, handler) =
let f _ =
begin try handler () with e ->
let msg = "Unexpected error: " ^ Printexc.to_string e in
alert msg
end;
Js._false
in
Js.Opt.iter
(document##getElementById (Js.string id))
(fun e -> e##onclick <- Dom_html.handler f)
module Tests = struct
let unit_tests () =
let a = "13133254971699857128" and b = "31748915560162976106" in
let c = Z.of_string a and d = Z.of_string b in
let ntests = ref 0 in
let check name f =
if not (f ()) then Printf.ksprintf failwith "test %s failed" name;
incr ntests
in
check "ZERO" (fun () -> Z.to_string Z.zero = "0");
check "ONE" (fun () -> Z.to_string Z.one = "1");
let string_roundtrip a c () = a = Z.to_string c in
check "string_roundtrip_a" (string_roundtrip a c);
check "string_roundtrip_b" (string_roundtrip b d);
let operator op expected () = expected = Z.to_string (op c d) in
check "add" (operator Z.( + ) "44882170531862833234");
check "mul" (operator Z.( * ) "416966603126589360375328894595477783568");
check "sub" (operator Z.( - ) "-18615660588463118978");
let a = 132180439 and b = 41907500 in
let c = Z.of_int a and d = Z.of_int b in
let int_roundtrip a c () = a = Z.to_int c in
check "int_roundtrip_a" (int_roundtrip a c);
check "int_roundtrip_b" (int_roundtrip b d);
let m = Z.of_int 181944121 in
check "mod" (fun () -> Z.to_int Z.((c * d) mod m) = 30881634);
check "erem" (fun () -> Z.to_int Z.((zero - c * d) mod m) = 151062487);
check "powm" (fun () -> Z.to_int (Z.powm c d m) = 81171525);
check "invert" (fun () -> Z.to_int (Z.invert c m) = 54455411);
check "prime" (fun () -> Z.probab_prime m 5 > 0);
check "eq" (fun () -> Z.(c =% c));
check "neq" (fun () -> Z.(not (c =% d)));
check "geq" (fun () -> Z.geq c d);
check "lt" (fun () -> Z.lt d c);
let i = Z.of_string "272660753928370030481696309961224617984" in
check "bit_length" (fun () -> Z.bit_length i = 128);
let j = Z.of_bits "\x81\xab\xd3\xed\x0b\x19\x2e\x40\x7a\xca" in
let k = Z.of_string "956173156978067279948673" in
check "of_bits" (fun () -> Z.(j =% k));
Printf.ksprintf alert "%d tests were successful!" !ntests
let cmds = ["do_unit_tests", unit_tests]
end
module Tkeygen = struct
open Tool_tkeygen
let tkeygen () =
let module P : PARAMS = struct
let group = get_textarea "election_group"
end in
let module X = (val make (module P : PARAMS) : S) in
let open X in
let {id; priv; pub} = trustee_keygen () in
set_textarea "tkeygen_id" id;
set_textarea "tkeygen_secret" priv;
set_textarea "tkeygen_public" pub
let cmds = ["do_tkeygen", tkeygen]
end
let split_lines str =
let str = str ^ "\n" in
let n = String.length str in
let rec loop accu i =
if i < n
then (
let j = String.index_from str i '\n' in
let line = String.sub str i (j-i) in
let accu = if line = "" then accu else line :: accu in
loop accu (j+1)
) else List.rev accu
in loop [] 0
module Credgen = struct
open Tool_credgen
let derive () =
let module P : PARAMS = struct
let uuid = get_textarea "election_uuid"
let group = get_textarea "election_group"
end in
let module X = (val make (module P : PARAMS) : S) in
let cred = get_textarea "credgen_derive_input" in
set_textarea "credgen_derive_output" (X.derive cred)
let generate ids =
let module P : PARAMS = struct
let uuid = get_textarea "election_uuid"
let group = get_textarea "election_group"
end in
let module X = (val make (module P : PARAMS) : S) in
let privs, pubs, hashs =
List.fold_left (fun (privs, pubs, hashs) id ->
let priv, pub, hash = X.generate () in
let priv = id ^ " " ^ priv and hash = id ^ " " ^ hash in
priv::privs, pub::pubs, hash::hashs
) ([], [], []) ids
in
set_textarea "credgen_generated_creds"
(privs |> List.rev |> String.concat "\n");
set_textarea "credgen_generated_pks"
(pubs |> List.sort compare |> String.concat "\n");
set_textarea "credgen_generated_hashed"
(hashs |> List.rev |> String.concat "\n")
let generate_n () =
get_textarea "credgen_number" |>
int_of_string |> generate_ids |> generate
let generate_ids () =
get_textarea "credgen_ids" ^ "\n" |>
split_lines |> generate
let cmds = [
"do_credgen_derive", derive;
"do_credgen_generate", generate_n;
"do_credgen_ids", generate_ids;
]
end
module Mkelection = struct
open Tool_mkelection
let mkelection () =
let module P : PARAMS = struct
let uuid = get_textarea "election_uuid"
let group = get_textarea "election_group"
let template = get_textarea "mkelection_template"
let get_public_keys () =
Some (get_textarea "mkelection_pks" |> split_lines |> Array.of_list)
end in
let module X = (val make (module P : PARAMS) : S) in
set_textarea "mkelection_output" (X.mkelection ())
let cmds = [
"do_mkelection", mkelection;
]
end
module ToolElection = struct
open Tool_election
module Getters = struct
let get_public_keys () =
let raw = get_textarea "election_pks" |> split_lines in
let pks = Array.of_list raw in
if Array.length pks = 0 then None else Some pks
let get_public_creds () =
let raw = get_textarea "election_pubcreds" |> split_lines in
match raw with
| [] -> None
| _ -> Some (Stream.of_list raw)
let get_ballots () =
let raw = get_textarea "election_ballots" |> split_lines in
match raw with
| [] -> None
| _ -> Some (Stream.of_list raw)
let get_result () =
let raw = get_textarea "election_result" |> split_lines in
match raw with
| [] -> None
| [r] -> Some r
| _ -> invalid_arg "invalid result"
let print_msg x = alert x
end
let get_election () =
let raw = get_textarea "election_params" in
match split_lines raw with
| [e] -> e
| _ -> invalid_arg "invalid election parameters"
let create_ballot () =
let module P : PARAMS = struct
let election = get_election ()
include Getters
end in
let choices = get_textarea "election_choices" |> plaintext_of_string in
let privcred = get_textarea "election_privcred" in
let module X = (val make (module P : PARAMS) : S) in
set_textarea "election_ballot" (X.vote (Some privcred) choices)
let verify () =
let module P : PARAMS = struct
let election = get_election ()
include Getters
end in
let module X = (val make (module P : PARAMS) : S) in
X.verify ()
let decrypt () =
let module P : PARAMS = struct
let election = get_election ()
include Getters
end in
let module X = (val make (module P : PARAMS) : S) in
let privkey = get_textarea "election_privkey" in
set_textarea "election_pd" (X.decrypt privkey)
let finalize () =
let module P : PARAMS = struct
let election = get_election ()
include Getters
end in
let module X = (val make (module P : PARAMS) : S) in
let factors = get_textarea "election_factors" |> split_lines in
set_textarea "election_result" (X.finalize (Array.of_list factors))
let cmds = [
"do_encrypt", create_ballot;
"do_verify", verify;
"do_decrypt", decrypt;
"do_finalize", finalize;
]
end
let int_of_quad str =
let ( ! ) x = int_of_char str.[x] in
(((((!0 lsl 8) lor !1) lsl 8) lor !2) lsl 8) lor !3
let new_uuid () =
let seed = Array.init 16 (fun _ ->
random_string secure_rng 4 |> int_of_quad
) in
let s = Random.State.make seed in
let uuid = Uuidm.v4_gen s () in
set_textarea "election_uuid" (Uuidm.to_string uuid)
let cmds =
["new_uuid", new_uuid] @
Tests.cmds @
Tkeygen.cmds @
Credgen.cmds @
Mkelection.cmds @
ToolElection.cmds
let install_handlers () =
List.iter install_handler cmds
let () =
Dom_html.window##onload <- Dom_html.handler (fun _ ->
install_handlers ();
Js._false
)
belenios-1.4+dfsg/src/tool/tool_js.mli000066400000000000000000000000261307140314400200100ustar00rootroot00000000000000(* empty interface *)
belenios-1.4+dfsg/src/tool/tool_js_common.ml000066400000000000000000000047011307140314400212130ustar00rootroot00000000000000(**************************************************************************)
(* BELENIOS *)
(* *)
(* Copyright © 2012-2016 Inria *)
(* *)
(* 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. *)
(* *)
(* You should have received a copy of the GNU Affero General Public *)
(* License along with this program. If not, see *)
(* . *)
(**************************************************************************)
let document = Dom_html.window##document
let alert s : unit =
let open Js.Unsafe in
fun_call (variable "alert") [| s |> Js.string |> inject |]
let get_textarea id =
let res = ref None in
Js.Opt.iter
(document##getElementById (Js.string id))
(fun e ->
Js.Opt.iter
(Dom_html.CoerceTo.textarea e)
(fun x -> res := Some (Js.to_string (x##value)))
);
match !res with
| None -> raise Not_found
| Some x -> x
let set_textarea id z =
Js.Opt.iter
(document##getElementById (Js.string id))
(fun e ->
Js.Opt.iter
(Dom_html.CoerceTo.textarea e)
(fun x -> x##value <- Js.string z)
)
let get_input id =
let res = ref None in
Js.Opt.iter
(document##getElementById (Js.string id))
(fun e ->
Js.Opt.iter
(Dom_html.CoerceTo.input e)
(fun x -> res := Some (Js.to_string (x##value)))
);
match !res with
| None -> raise Not_found
| Some x -> x
belenios-1.4+dfsg/src/tool/tool_js_credgen.ml000066400000000000000000000070211307140314400213300ustar00rootroot00000000000000(**************************************************************************)
(* BELENIOS *)
(* *)
(* Copyright © 2012-2016 Inria *)
(* *)
(* 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. *)
(* *)
(* You should have received a copy of the GNU Affero General Public *)
(* License along with this program. If not, see *)
(* . *)
(**************************************************************************)
open Tool_js_common
open Tool_credgen
let generate _ =
let ids =
let raw = get_textarea "voters" in
let rec loop i accu =
if i >= 0 then
let j = try String.rindex_from raw i '\n' with Not_found -> -1 in
loop (j-1) (String.sub raw (j+1) (i-j) :: accu)
else
accu
in loop (String.length raw - 1) []
in
let module P : PARAMS = struct
let uuid = get_textarea "uuid"
let group = get_textarea "group"
end in
let module X = (val make (module P : PARAMS) : S) in
let privs, pubs, hashs =
List.fold_left
(fun (privs, pubs, hashs) id ->
let priv, pub, hash = X.generate () in
let priv = id ^ " " ^ priv and hash = id ^ " " ^ hash in
priv::privs, pub::pubs, hash::hashs
) ([], [], []) ids
in
let text_pks = pubs |> List.sort compare |> String.concat "\n" in
set_textarea "pks" text_pks;
let text_creds = (privs |> List.rev |> String.concat "\n") ^ "\n" in
let data_creds = (Js.string "data:text/plain,")##concat (Js.encodeURI (Js.string text_creds)) in
ignore (Dom_html.window##open_ (data_creds, Js.string "creds", Js.null));
let text_hashed = (hashs |> List.rev |> String.concat "\n") ^ "\n" in
let data_hashed = (Js.string "data:text/plain,")##concat (Js.encodeURI (Js.string text_hashed)) in
ignore (Dom_html.window##open_ (data_hashed, Js.string "hashed", Js.null));
alert "New windows (or tabs) were open with private credentials and credential hashes. Please save them before submitting public credentials!";
Js._false
let fill_interactivity _ =
Js.Opt.iter
(document##getElementById (Js.string "interactivity"))
(fun e ->
let x = document##createElement (Js.string "div") in
Dom.appendChild e x;
let b = document##createElement (Js.string "button") in
let t = document##createTextNode (Js.string "Generate") in
b##onclick <- Dom_html.handler generate;
Dom.appendChild b t;
Dom.appendChild x b;
);
Js._false
let () =
Dom_html.window##onload <- Dom_html.handler fill_interactivity;
belenios-1.4+dfsg/src/tool/tool_js_pd.ml000066400000000000000000000104101307140314400203200ustar00rootroot00000000000000(**************************************************************************)
(* BELENIOS *)
(* *)
(* Copyright © 2012-2016 Inria *)
(* *)
(* 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. *)
(* *)
(* You should have received a copy of the GNU Affero General Public *)
(* License along with this program. If not, see *)
(* . *)
(**************************************************************************)
open Platform
open Serializable_j
open Tool_js_common
let election = ref None
let encrypted_tally = ref None
let ( >>= ) = Js.Opt.bind
let wrap f x =
(try
Js.Opt.case (f x)
(fun () -> failwith "Unexpected error")
(fun () -> ())
with
| Failure s -> alert s
| e ->
Printf.ksprintf
alert "Unexpected error: %s" (Printexc.to_string e)
); Js._false
let basic_check_private_key s =
let n = String.length s in
let rec leading i =
if i < n then
match s.[i] with
| '"' -> middle (i+1)
| _ -> failwith "Must start with a double quote"
else failwith "Too short"
and middle i =
if i < n then
match s.[i] with
| '0'..'9' -> ending (i+1)
| _ -> failwith "Must have at least one digit"
else failwith "Too short"
and ending i =
if i < n then
match s.[i] with
| '0'..'9' -> ending (i+1)
| '"' -> (if i+1 < n then failwith "Must end with a double quote")
| c -> Printf.ksprintf failwith "Illegal character: %c" c
else failwith "Must end with a double quote"
in leading 0
let compute_partial_decryption _ =
Js.Opt.option !election >>= fun e ->
let election = Group.election_params_of_string e in
let module P = (val election) in
let module M = Election.MakeSimpleMonad (P.G) in
let module E = Election.MakeElection (P.G) (M) in
Js.Opt.option !encrypted_tally >>= fun e ->
let encrypted_tally = encrypted_tally_of_string P.G.read e in
document##getElementById (Js.string "private_key") >>= fun e ->
Dom_html.CoerceTo.input e >>= fun e ->
let pk_str = Js.to_string e##value in
basic_check_private_key pk_str;
let private_key =
try number_of_string pk_str
with e ->
Printf.ksprintf
failwith "Error in format of private key: %s" (Printexc.to_string e)
in
let factor = E.compute_factor encrypted_tally private_key () in
set_textarea "pd" (string_of_partial_decryption P.G.write factor);
Js.some ()
let compute_hash () =
let _ =
Js.Opt.option !encrypted_tally >>= fun e ->
let hash = sha256_b64 e in
document##getElementById (Js.string "hash") >>= fun e ->
let t = document##createTextNode (Js.string hash) in
Dom.appendChild e t;
Js.null
in Js._false
let main _ =
let _ =
document##getElementById (Js.string "compute") >>= fun e ->
Dom_html.CoerceTo.button e >>= fun e ->
e##onclick <- Dom_html.handler (wrap compute_partial_decryption);
Js.null
in
let _ =
Lwt.async (fun () ->
let open XmlHttpRequest in
lwt e = get "../encrypted_tally.json" in
encrypted_tally := Some e.content;
lwt e = get "../election.json" in
election := Some e.content;
Lwt.return (compute_hash ()))
in
Js._false
let () =
Dom_html.window##onload <- Dom_html.handler main
belenios-1.4+dfsg/src/tool/tool_js_questions.ml000066400000000000000000000252341307140314400217610ustar00rootroot00000000000000(**************************************************************************)
(* BELENIOS *)
(* *)
(* Copyright © 2012-2016 Inria *)
(* *)
(* 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. *)
(* *)
(* You should have received a copy of the GNU Affero General Public *)
(* License along with this program. If not, see *)
(* . *)
(**************************************************************************)
open Serializable_j
open Tool_js_common
let (>>=) = Js.Opt.bind
let return = Js.Opt.return
let handler f = Dom_html.handler (fun e -> ignore (f e); Js._false)
(* Getting the OCaml structure out of the DOM *)
let extractAnswer a =
Dom_html.CoerceTo.input a >>= fun x ->
return (Js.to_string (x##value))
let extractQuestion q =
Dom_html.CoerceTo.input q >>= fun x ->
let q_question = Js.to_string (x##value) in
q##parentNode >>= fun p1 ->
p1##parentNode >>= fun p2 ->
Dom.CoerceTo.element p2 >>= fun p2 ->
let p2 = Dom_html.element p2 in
let numeric selector error_msg =
p2##querySelector (Js.string selector) >>= fun x ->
Dom_html.CoerceTo.input x >>= fun x ->
let x = Js.to_string x##value in
try return (int_of_string x)
with _ -> failwith (error_msg ^ ": " ^ x ^ ".")
in
p2##querySelector (Js.string ".question_blank") >>= fun q_blank ->
Dom_html.CoerceTo.input q_blank >>= fun q_blank ->
let q_blank = if Js.to_bool q_blank##checked then Some true else None in
numeric ".question_min" "Invalid minimum number of choices" >>= fun q_min ->
numeric ".question_max" "Invalid maximum number of choices" >>= fun q_max ->
if not (q_min <= q_max) then
failwith "Minimum number of choices must be less than or equal to maximum number of choices!";
let answers = p2##querySelectorAll (Js.string ".question_answer") in
let q_answers =
Array.init
(answers##length)
(fun i ->
let a = answers##item (i) >>= extractAnswer in
Js.Opt.get a (fun () -> failwith "extractQuestion"))
in
return {q_question; q_blank; q_min; q_max; q_answers}
let extractTemplate () =
let t_name = get_input "election_name" in
let t_description = get_textarea "election_description" in
let questions = document##querySelectorAll (Js.string ".question_question") in
let t_questions =
Array.init
(questions##length)
(fun i ->
let q = questions##item (i) >>= extractQuestion in
Js.Opt.get q (fun () -> failwith "extractTemplate"))
in
{t_name; t_description; t_questions}
(* Injecting the OCaml structure into the DOM *)
let rec createAnswer a =
let container = Dom_html.createDiv document in
let t = document##createTextNode (Js.string "Answer: ") in
let u = Dom_html.createInput document in
u##className <- Js.string "question_answer";
u##value <- Js.string a;
u##size <- 60;
Dom.appendChild container t;
Dom.appendChild container u;
let btn_text = document##createTextNode (Js.string "Remove") in
let btn = Dom_html.createButton document in
let f _ =
container##parentNode >>= fun x ->
Dom.removeChild x container;
return ()
in
btn##onclick <- handler f;
Dom.appendChild btn btn_text;
Dom.appendChild container btn;
let insert_text = document##createTextNode (Js.string "Insert") in
let insert_btn = Dom_html.createButton document in
let f _ =
let x = createAnswer "" in
container##parentNode >>= fun p ->
Dom.insertBefore p x (Js.some container);
return ()
in
insert_btn##onclick <- handler f;
Dom.appendChild insert_btn insert_text;
Dom.appendChild container insert_btn;
container
let rec createQuestion q =
let container = Dom_html.createDiv document in
(* question text and remove/insert buttons *)
let x = Dom_html.createDiv document in
let t = document##createTextNode (Js.string "Question: ") in
Dom.appendChild x t;
let h_question = Dom_html.createInput document in
Dom.appendChild x h_question;
h_question##className <- Js.string "question_question";
h_question##size <- 60;
h_question##value <- Js.string q.q_question;
let remove_text = document##createTextNode (Js.string "Remove") in
let remove_btn = Dom_html.createButton document in
let f _ =
container##parentNode >>= fun x ->
Dom.removeChild x container;
return ()
in
remove_btn##onclick <- handler f;
Dom.appendChild remove_btn remove_text;
Dom.appendChild x remove_btn;
let insert_text = document##createTextNode (Js.string "Insert") in
let insert_btn = Dom_html.createButton document in
let f _ =
let x = createQuestion {q_question=""; q_blank=None; q_min=0; q_max=1; q_answers=[||]} in
container##parentNode >>= fun p ->
Dom.insertBefore p x (Js.some container);
return ()
in
insert_btn##onclick <- handler f;
Dom.appendChild insert_btn insert_text;
Dom.appendChild x insert_btn;
Dom.appendChild container x;
(* properties *)
let x = Dom_html.createDiv document in
let t = document##createTextNode (Js.string "The voter has to choose between ") in
Dom.appendChild x t;
let h_min = Dom_html.createInput document in
Dom.appendChild x h_min;
h_min##className <- Js.string "question_min";
h_min##size <- 5;
h_min##value <- Js.string (string_of_int q.q_min);
let t = document##createTextNode (Js.string " and ") in
Dom.appendChild x t;
let h_max = Dom_html.createInput document in
Dom.appendChild x h_max;
h_max##className <- Js.string "question_max";
h_max##size <- 5;
h_max##value <- Js.string (string_of_int q.q_max);
let t = document##createTextNode (Js.string " answers.") in
Dom.appendChild x t;
Dom.appendChild container x;
(* is blank allowed? *)
let x = Dom_html.createDiv document in
let h_blank = Dom_html.createInput ~_type:(Js.string "checkbox") document in
h_blank##className <- Js.string "question_blank";
h_blank##checked <- Js.(match q.q_blank with Some true -> _true | _ -> _false);
Dom.appendChild x h_blank;
let t = document##createTextNode (Js.string "Blank vote is allowed") in
Dom.appendChild x t;
Dom.appendChild container x;
(* answers *)
let h_answers = Dom_html.createDiv document in
h_answers##className <- Js.string "question_answers";
Dom.appendChild container h_answers;
Array.iter
(fun a ->
let x = createAnswer a in
Dom.appendChild h_answers x)
q.q_answers;
(* button for adding answer *)
let x = Dom_html.createDiv document in
let b = Dom_html.createButton document in
let t = document##createTextNode (Js.string "Add an answer") in
let f _ =
let x = createAnswer "" in
Dom.appendChild h_answers x
in
b##onclick <- handler f;
Dom.appendChild b t;
Dom.appendChild x b;
Dom.appendChild container x;
(* horizontal rule *)
let x = Dom_html.createHr document in
Dom.appendChild container x;
(* return *)
container
let createTemplate template =
let container = Dom_html.createDiv document in
(* name *)
let x = Dom_html.createDiv document in
x##style##display <- Js.string "none";
let t = document##createTextNode (Js.string "Name of the election: ") in
Dom.appendChild x t;
let h_name = Dom_html.createInput document in
h_name##id <- Js.string "election_name";
h_name##value <- Js.string template.t_name;
Dom.appendChild x h_name;
Dom.appendChild container x;
(* description *)
let x = Dom_html.createDiv document in
x##style##display <- Js.string "none";
let y = Dom_html.createDiv document in
let t = document##createTextNode (Js.string "Description:") in
Dom.appendChild y t;
Dom.appendChild x y;
let y = Dom_html.createDiv document in
let h_description = Dom_html.createTextarea document in
h_description##id <- Js.string "election_description";
h_description##value <- Js.string template.t_description;
h_description##cols <- 80;
Dom.appendChild y h_description;
Dom.appendChild x y;
Dom.appendChild container x;
(* questions *)
let x = Dom_html.createDiv document in
let h_questions_div = Dom_html.createDiv document in
h_questions_div##id <- Js.string "election_questions";
Dom.appendChild x h_questions_div;
Dom.appendChild container x;
Array.iter
(fun q ->
let x = createQuestion q in
Dom.appendChild h_questions_div x)
template.t_questions;
(* button for adding question *)
let x = Dom_html.createDiv document in
let b = Dom_html.createButton document in
let t = document##createTextNode (Js.string "Add a question") in
let f _ =
let x = createQuestion {q_question=""; q_blank=None; q_min=0; q_max=1; q_answers=[||]} in
Dom.appendChild h_questions_div x
in
b##onclick <- handler f;
Dom.appendChild b t;
Dom.appendChild x b;
Dom.appendChild container x;
(* button for submitting *)
let x = Dom_html.createHr document in
Dom.appendChild container x;
let x = Dom_html.createDiv document in
let b = Dom_html.createButton document in
let t = document##createTextNode (Js.string "Save changes") in
let f _ =
try
let template = extractTemplate () in
set_textarea "questions" (string_of_template template);
document##querySelector (Js.string "form") >>= fun x ->
Dom_html.CoerceTo.form x >>= fun x ->
x##submit ();
return ()
with Failure e ->
alert e;
return ()
in
b##onclick <- handler f;
Dom.appendChild b t;
Dom.appendChild x b;
Dom.appendChild container x;
(* return *)
container
(* Entry point *)
let fill_interactivity _ =
document##getElementById (Js.string "interactivity") >>= fun e ->
let t = template_of_string (get_textarea "questions") in
let div = createTemplate t in
Dom.appendChild e div;
document##querySelector (Js.string "form") >>= fun x ->
x##style##display <- Js.string "none";
return ()
let () =
Dom_html.window##onload <- handler fill_interactivity;
belenios-1.4+dfsg/src/tool/tool_js_tkeygen.ml000066400000000000000000000047751307140314400214040ustar00rootroot00000000000000(**************************************************************************)
(* BELENIOS *)
(* *)
(* Copyright © 2012-2016 Inria *)
(* *)
(* 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. *)
(* *)
(* You should have received a copy of the GNU Affero General Public *)
(* License along with this program. If not, see *)
(* . *)
(**************************************************************************)
open Tool_js_common
open Tool_tkeygen
let tkeygen _ =
let module P : PARAMS = struct
let group = get_textarea "group"
end in
let module X = (val make (module P : PARAMS) : S) in
let open X in
let {id; priv; pub} = trustee_keygen () in
let data_uri = (Js.string "data:application/json,")##concat (Js.encodeURI (Js.string priv)) in
ignore (Dom_html.window##open_ (data_uri, Js.string id, Js.null));
set_textarea "pk" pub;
alert "The private key has been open in a new window (or tab). Please save it before submitting the public key!";
Js._false
let fill_interactivity _ =
Js.Opt.iter
(document##getElementById (Js.string "interactivity"))
(fun e ->
let b = document##createElement (Js.string "button") in
let t = document##createTextNode (Js.string "Generate a new keypair") in
b##onclick <- Dom_html.handler tkeygen;
Dom.appendChild b t;
Dom.appendChild e b;
);
Js._false
let () =
Dom_html.window##onload <- Dom_html.handler fill_interactivity;
belenios-1.4+dfsg/src/tool/tool_mkelection.ml000066400000000000000000000064511307140314400213650ustar00rootroot00000000000000(**************************************************************************)
(* BELENIOS *)
(* *)
(* Copyright © 2012-2016 Inria *)
(* *)
(* 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. *)
(* *)
(* You should have received a copy of the GNU Affero General Public *)
(* License along with this program. If not, see *)
(* . *)
(**************************************************************************)
open Serializable_j
open Signatures
open Common
module type PARAMS = sig
val uuid : string
val group : string
val template : string
val get_public_keys : unit -> string array option
end
module type S = sig
val mkelection : unit -> string
end
module type PARSED_PARAMS = sig
val uuid : Uuidm.t
val template : template
module G : GROUP
val get_public_keys : unit -> G.t trustee_public_key array option
end
let parse_params p =
let module P = (val p : PARAMS) in
let module R = struct
let uuid =
match Uuidm.of_string P.uuid with
| Some u -> u
| None -> Printf.ksprintf failwith "%s is not a valid UUID" P.uuid
let template = template_of_string P.template
module G = (val Group.of_string P.group : GROUP)
let get_public_keys () =
match P.get_public_keys () with
| None -> None
| Some xs -> Some (Array.map (trustee_public_key_of_string G.read) xs)
end
in (module R : PARSED_PARAMS)
module Make (P : PARSED_PARAMS) : S = struct
open P
(* Setup group *)
module M = Election.MakeSimpleMonad(G);;
(* Setup trustees *)
module KG = Election.MakeSimpleDistKeyGen(G)(M);;
let public_keys =
match get_public_keys () with
| Some keys -> keys
| None -> failwith "trustee keys are missing"
let y = KG.combine public_keys
(* Setup election *)
let params = {
e_description = template.t_description;
e_name = template.t_name;
e_public_key = {wpk_group = G.group; wpk_y = y};
e_questions = template.t_questions;
e_uuid = uuid;
}
(* Generate and serialize election.json *)
let mkelection () =
string_of_params (write_wrapped_pubkey G.write_group G.write) params
end
let make params =
let module P = (val parse_params params : PARSED_PARAMS) in
let module R = Make (P) in
(module R : S)
belenios-1.4+dfsg/src/tool/tool_mkelection.mli000066400000000000000000000003671307140314400215360ustar00rootroot00000000000000module type PARAMS = sig
val uuid : string
val group : string
val template : string
val get_public_keys : unit -> string array option
end
module type S = sig
val mkelection : unit -> string
end
val make : (module PARAMS) -> (module S)
belenios-1.4+dfsg/src/tool/tool_tkeygen.ml000066400000000000000000000053371307140314400207030ustar00rootroot00000000000000(**************************************************************************)
(* BELENIOS *)
(* *)
(* Copyright © 2012-2016 Inria *)
(* *)
(* 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. *)
(* *)
(* You should have received a copy of the GNU Affero General Public *)
(* License along with this program. If not, see *)
(* . *)
(**************************************************************************)
open Platform
open Serializable_j
open Signatures
open Common
module type PARAMS = sig
val group : string
end
module type S = sig
type keypair = { id : string; priv : string; pub : string }
val trustee_keygen : unit -> keypair
end
module type PARSED_PARAMS = sig
module G : GROUP
end
let parse_params p =
let module P = (val p : PARAMS) in
let module R = struct
module G = (val Group.of_string P.group : GROUP)
end
in (module R : PARSED_PARAMS)
module Make (P : PARSED_PARAMS) : S = struct
open P
(* Setup group *)
module M = Election.MakeSimpleMonad(G);;
(* Generate key *)
module KG = Election.MakeSimpleDistKeyGen(G)(M);;
type keypair = { id : string; priv : string; pub : string }
let trustee_keygen () =
let private_key, public_key = KG.generate_and_prove () () in
assert (KG.check public_key);
let id = String.sub
(sha256_hex (G.to_string public_key.trustee_public_key))
0 8 |> String.uppercase
in
let priv = string_of_number private_key in
let pub = string_of_trustee_public_key G.write public_key in
{id; priv; pub}
end
let make params =
let module P = (val parse_params params : PARSED_PARAMS) in
let module R = Make (P) in
(module R : S)
belenios-1.4+dfsg/src/tool/tool_tkeygen.mli000066400000000000000000000003321307140314400210420ustar00rootroot00000000000000module type PARAMS = sig
val group : string
end
module type S = sig
type keypair = { id : string; priv : string; pub : string }
val trustee_keygen : unit -> keypair
end
val make : (module PARAMS) -> (module S)
belenios-1.4+dfsg/src/tool/tool_verifydiff.ml000066400000000000000000000173241307140314400213710ustar00rootroot00000000000000(**************************************************************************)
(* BELENIOS *)
(* *)
(* Copyright © 2012-2016 Inria *)
(* *)
(* 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. *)
(* *)
(* You should have received a copy of the GNU Affero General Public *)
(* License along with this program. If not, see *)
(* . *)
(**************************************************************************)
open Signatures
open Serializable_j
let stream_to_list s =
let res = ref [] in
Stream.iter (fun x -> res := x :: !res) s;
List.rev !res
let lines_of_file fname =
let ic = open_in fname in
Stream.from (fun _ ->
try Some (input_line ic)
with End_of_file -> close_in ic; None
)
let string_of_file f =
lines_of_file f |> stream_to_list |> String.concat "\n"
let load_from_file of_string filename =
if Sys.file_exists filename then (
Some (lines_of_file filename |> stream_to_list |> List.rev_map of_string)
) else None
let ( / ) = Filename.concat
type verifydiff_error =
| ElectionMismatch
| PublicKeysMismatch
| MissingPublicKeys
| InvalidPublicKeys
| PublicKeyMismatch
| MissingCredentials
| InvalidCredential
| CredentialsMismatch
| MissingBallots
| InvalidBallot
| DuplicateBallot
| BallotSignedByInvalidKey
| DecreasingBallots
| BallotSignedByReplacedKey
exception VerifydiffError of verifydiff_error
let explain_error = function
| ElectionMismatch -> "election mismatch"
| PublicKeysMismatch -> "public keys mismatch"
| MissingPublicKeys -> "missing public keys"
| InvalidPublicKeys -> "invalid public keys"
| PublicKeyMismatch -> "public key mismatch"
| MissingCredentials -> "missing credentials"
| InvalidCredential -> "invalid credential"
| CredentialsMismatch -> "credentials mismatch"
| MissingBallots -> "missing ballots"
| InvalidBallot -> "invalid ballot"
| DuplicateBallot -> "duplicate ballot"
| BallotSignedByInvalidKey -> "ballot signed by invalid key"
| DecreasingBallots -> "decreasing ballots"
| BallotSignedByReplacedKey -> "ballot signed by replaced key"
let () =
Printexc.register_printer (function
| VerifydiffError e -> Some ("verify-diff error: " ^ explain_error e)
| _ -> None)
let verifydiff dir1 dir2 =
(* the elections must be the same *)
let election = string_of_file (dir1 / "election.json") in
let () =
let election2 = string_of_file (dir2 / "election.json") in
if election2 <> election then raise (VerifydiffError ElectionMismatch)
in
(* the public keys must be the same *)
let pks = load_from_file (fun x -> x) (dir1 / "public_keys.jsons") in
let () =
let pks2 = load_from_file (fun x -> x) (dir2 / "public_keys.jsons") in
if pks2 <> pks then raise (VerifydiffError PublicKeysMismatch)
in
(* the public keys must be valid *)
let module ED = (val Group.election_params_of_string election) in
let open ED in
let module M = Election.MakeSimpleMonad (G) in
let module E = Election.MakeElection (G) (M) in
let module KG = Election.MakeSimpleDistKeyGen (G) (M) in
let pks = match pks with
| None -> raise (VerifydiffError MissingPublicKeys)
| Some pks -> List.map (trustee_public_key_of_string G.read) pks
in
let () =
if not (List.for_all KG.check pks) then
raise (VerifydiffError InvalidPublicKeys)
in
(* the public keys must correspond to the public key of election *)
let y = KG.combine (Array.of_list pks) in
let () =
if not G.(election.e_params.e_public_key =~ y) then
raise (VerifydiffError PublicKeyMismatch)
in
(* load both public_creds.txt and check that their contents is valid *)
let module GSet = Set.Make (G) in
let creds dir =
match load_from_file G.of_string (dir / "public_creds.txt") with
| None -> raise (VerifydiffError MissingCredentials)
| Some creds ->
if not (List.for_all G.check creds) then
raise (VerifydiffError InvalidCredential);
List.fold_left (fun accu x -> GSet.add x accu) GSet.empty creds
in
let creds1 = creds dir1 and creds2 = creds dir2 in
(* both public_creds.txt have the same cardinal *)
let () =
if GSet.cardinal creds1 <> GSet.cardinal creds2 then
raise (VerifydiffError CredentialsMismatch)
in
(* compute credentials that have been replaced *)
let creds_replaced =
GSet.fold (fun x accu ->
if not (GSet.mem x creds2) then GSet.add x accu else accu
) creds1 GSet.empty
in
(* issue a warning when credentials have changed *)
let () =
if not (GSet.is_empty creds_replaced) then
Printf.eprintf "W: credentials have changed\n%!"
in
(* load both ballots.jsons and check that their contents is valid *)
let module GMap = Map.Make (G) in
let ballots dir =
match load_from_file (ballot_of_string G.read) (dir / "ballots.jsons") with
| None -> raise (VerifydiffError MissingBallots)
| Some ballots ->
if not (List.for_all (E.check_ballot election) ballots) then
raise (VerifydiffError InvalidBallot);
(* return the set of ballots indexed by the public keys used to sign *)
List.fold_left (fun accu x ->
match x.signature with
| None -> raise (VerifydiffError InvalidBallot)
| Some s -> if GMap.mem s.s_public_key accu then
raise (VerifydiffError DuplicateBallot)
else GMap.add s.s_public_key x accu
) GMap.empty ballots
in
let ballots1 = ballots dir1 and ballots2 = ballots dir2 in
(* each ballot is signed with a valid key *)
let check_keys ballots creds =
GMap.for_all (fun pk _ -> GSet.mem pk creds) ballots
in
let () =
if not (check_keys ballots1 creds1 && check_keys ballots2 creds2) then
raise (VerifydiffError BallotSignedByInvalidKey)
in
(* the set of ballots increases *)
let () =
if not (GMap.for_all (fun pk _ -> GMap.mem pk ballots2) ballots1) then
raise (VerifydiffError DecreasingBallots)
in
let () =
let n = GMap.cardinal ballots2 - GMap.cardinal ballots1 in
if n > 0 then Printf.eprintf "I: %d new ballot(s)\n%!" n
in
(* the keys of modified ballots have not been replaced *)
let () =
if not (GMap.for_all (fun pk ballot1 ->
let ballot2 = GMap.find pk ballots2 in
ballot1 = ballot2 || not (GSet.mem pk creds_replaced)
) ballots1)
then raise (VerifydiffError BallotSignedByReplacedKey)
in
let () =
let n = GMap.fold (fun pk ballot1 accu ->
let ballot2 = GMap.find pk ballots2 in
if ballot1 <> ballot2 then accu + 1 else accu
) ballots1 0
in
if n > 0 then Printf.eprintf "W: %d ballot(s) have been replaced\n%!" n
in
Printf.eprintf "I: all tests passed!\n%!"
belenios-1.4+dfsg/src/tool/tool_verifydiff.mli000066400000000000000000000037441307140314400215430ustar00rootroot00000000000000(**************************************************************************)
(* BELENIOS *)
(* *)
(* Copyright © 2012-2016 Inria *)
(* *)
(* 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. *)
(* *)
(* You should have received a copy of the GNU Affero General Public *)
(* License along with this program. If not, see *)
(* . *)
(**************************************************************************)
type verifydiff_error =
| ElectionMismatch
| PublicKeysMismatch
| MissingPublicKeys
| InvalidPublicKeys
| PublicKeyMismatch
| MissingCredentials
| InvalidCredential
| CredentialsMismatch
| MissingBallots
| InvalidBallot
| DuplicateBallot
| BallotSignedByInvalidKey
| DecreasingBallots
| BallotSignedByReplacedKey
exception VerifydiffError of verifydiff_error
val explain_error : verifydiff_error -> string
val verifydiff : string -> string -> unit
belenios-1.4+dfsg/src/web/000077500000000000000000000000001307140314400154365ustar00rootroot00000000000000belenios-1.4+dfsg/src/web/server.mllib000066400000000000000000000006401307140314400177650ustar00rootroot00000000000000src/platform/native/Belenios_version
src/platform/native/Platform
Serializable_builtin_t
Serializable_builtin_j
Serializable_j
Common
Group_field
Group
Election
Credential
Web_l10n_en
Web_l10n_fr
Web_l10n_de
Web_l10n_ro
Web_l10n_it
Web_i18n
Web_serializable_builtin_t
Web_serializable_builtin_j
Web_serializable_j
Web_common
Web_persist
Web_services
Web_state
Web_templates
Web_auth
Web_election
Web_site
Web_main
belenios-1.4+dfsg/src/web/web_auth.ml000066400000000000000000000321301307140314400175650ustar00rootroot00000000000000(**************************************************************************)
(* BELENIOS *)
(* *)
(* Copyright © 2012-2016 Inria *)
(* *)
(* 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. *)
(* *)
(* You should have received a copy of the GNU Affero General Public *)
(* License along with this program. If not, see *)
(* . *)
(**************************************************************************)
open Lwt
open Eliom_service
open Platform
open Web_serializable_j
open Web_common
open Web_state
open Web_services
let next_lf str i =
try Some (String.index_from str i '\n')
with Not_found -> None
let configure x =
let auth_config =
List.map (fun {auth_system; auth_instance; auth_config} ->
auth_instance, (auth_system, List.map snd auth_config)
) x
in
Web_persist.set_auth_config "" auth_config |> Lwt_main.run;
List.iter (fun {auth_system; auth_config; _} ->
match auth_system with
| "password" ->
let table = Ocsipersist.open_table "password_site" in
(match auth_config with
| [] -> ()
| ["db", file] ->
Ocsigen_messages.console (fun () ->
Printf.sprintf "Loading passwords from file %s" file
);
let db = Csv.load file in
List.iter (function
| username :: salt :: password :: _ ->
Ocsipersist.add table username (salt, password) |> Lwt_main.run
| _ -> failwith ("error while loading " ^ file)) db
| _ -> failwith "error in passwords configuration")
| _ -> ()
) x
let scope = Eliom_common.default_session_scope
let auth_env = Eliom_reference.eref ~scope None
let default_cont uuid () =
match%lwt cont_pop () with
| Some f -> f ()
| None ->
match uuid with
| None ->
Eliom_registration.Redirection.send Web_services.admin
| Some u ->
Eliom_registration.Redirection.send (preapply Web_services.election_home (u, ()))
(** Dummy authentication *)
let dummy_handler () name =
match%lwt Eliom_reference.get auth_env with
| None -> failwith "dummy handler was invoked without environment"
| Some (uuid, service) ->
Eliom_reference.set user (Some {uuid; service; name}) >>
Eliom_reference.unset auth_env >>
default_cont uuid ()
let () = Eliom_registration.Any.register ~service:dummy_post dummy_handler
(** Password authentication *)
let password_handler () (name, password) =
let%lwt uuid, service =
match%lwt Eliom_reference.get auth_env with
| None -> failwith "password handler was invoked without environment"
| Some x -> return x
in
let table =
"password_" ^
match uuid with
| None -> "site"
| Some u ->
let u = Uuidm.to_string u in
underscorize u
in
let table = Ocsipersist.open_table table in
let%lwt salt, hashed =
try%lwt Ocsipersist.find table name
with Not_found -> fail_http 401
in
if sha256_hex (salt ^ password) = hashed then
Eliom_reference.set user (Some {uuid; service; name}) >>
Eliom_reference.unset auth_env >>
default_cont uuid ()
else
fail_http 401
let () = Eliom_registration.Any.register ~service:password_post password_handler
(** CAS authentication *)
let cas_server = Eliom_reference.eref ~scope None
let login_cas = Eliom_service.Http.service
~path:["auth"; "cas"]
~get_params:Eliom_parameter.(opt (string "ticket"))
()
let cas_self =
(* lazy so rewrite_prefix is called after server initialization *)
lazy (Eliom_uri.make_string_uri
~absolute:true
~service:(preapply login_cas None)
() |> rewrite_prefix)
let parse_cas_validation info =
match next_lf info 0 with
| Some i ->
(match String.sub info 0 i with
| "yes" -> `Yes
(match next_lf info (i+1) with
| Some j -> Some (String.sub info (i+1) (j-i-1))
| None -> None)
| "no" -> `No
| _ -> `Error `Parsing)
| None -> `Error `Parsing
let get_cas_validation server ticket =
let url =
let cas_validate = Http.external_service
~prefix:server
~path:["validate"]
~get_params:Eliom_parameter.(string "service" ** string "ticket")
()
in
let service = preapply cas_validate (Lazy.force cas_self, ticket) in
Eliom_uri.make_string_uri ~absolute:true ~service ()
in
let%lwt reply = Ocsigen_http_client.get_url url in
match reply.Ocsigen_http_frame.frame_content with
| Some stream ->
let%lwt info = Ocsigen_stream.(string_of_stream 1000 (get stream)) in
Ocsigen_stream.finalize stream `Success >>
return (parse_cas_validation info)
| None -> return (`Error `Http)
let cas_handler ticket () =
let%lwt uuid, service =
match%lwt Eliom_reference.get auth_env with
| None -> failwith "cas handler was invoked without environment"
| Some x -> return x
in
match ticket with
| Some x ->
let%lwt server =
match%lwt Eliom_reference.get cas_server with
| None -> failwith "cas handler was invoked without a server"
| Some x -> return x
in
(match%lwt get_cas_validation server x with
| `Yes (Some name) ->
Eliom_reference.set user (Some {uuid; service; name}) >>
default_cont uuid ()
| `No -> fail_http 401
| `Yes None | `Error _ -> fail_http 502)
| None ->
Eliom_reference.unset cas_server >>
Eliom_reference.unset auth_env >>
default_cont uuid ()
let () = Eliom_registration.Any.register ~service:login_cas cas_handler
let cas_login_handler config () =
match config with
| [server] ->
Eliom_reference.set cas_server (Some server) >>
let cas_login = Http.external_service
~prefix:server
~path:["login"]
~get_params:Eliom_parameter.(string "service")
()
in
let service = preapply cas_login (Lazy.force cas_self) in
Eliom_registration.Redirection.send service
| _ -> failwith "cas_login_handler invoked with bad config"
(** OpenID Connect (OIDC) authentication *)
let oidc_state = Eliom_reference.eref ~scope None
let login_oidc = Eliom_service.Http.service
~path:["auth"; "oidc"]
~get_params:Eliom_parameter.any
()
let oidc_self =
lazy (Eliom_uri.make_string_uri
~absolute:true
~service:(preapply login_oidc [])
() |> rewrite_prefix)
let oidc_get_userinfo ocfg info =
let info = oidc_tokens_of_string info in
let access_token = info.oidc_access_token in
let url = ocfg.userinfo_endpoint in
let headers = Http_headers.(
add (name "Authorization") ("Bearer " ^ access_token) empty
) in
let%lwt reply = Ocsigen_http_client.get_url ~headers url in
match reply.Ocsigen_http_frame.frame_content with
| Some stream ->
let%lwt info = Ocsigen_stream.(string_of_stream 10000 (get stream)) in
Ocsigen_stream.finalize stream `Success >>
let x = oidc_userinfo_of_string info in
return (Some (match x.oidc_email with Some x -> x | None -> x.oidc_sub))
| None -> return None
let oidc_get_name ocfg client_id client_secret code =
let content = [
"code", code;
"client_id", client_id;
"client_secret", client_secret;
"redirect_uri", Lazy.force oidc_self;
"grant_type", "authorization_code";
] in
let%lwt reply = Ocsigen_http_client.post_urlencoded_url ~content ocfg.token_endpoint in
match reply.Ocsigen_http_frame.frame_content with
| Some stream ->
let%lwt info = Ocsigen_stream.(string_of_stream 10000 (get stream)) in
Ocsigen_stream.finalize stream `Success >>
oidc_get_userinfo ocfg info
| None -> return None
let oidc_handler params () =
let%lwt uuid, service =
match%lwt Eliom_reference.get auth_env with
| None -> failwith "oidc handler was invoked without environment"
| Some x -> return x
in
let code = try Some (List.assoc "code" params) with Not_found -> None in
let state = try Some (List.assoc "state" params) with Not_found -> None in
match code, state with
| Some code, Some state ->
let%lwt ocfg, client_id, client_secret, st =
match%lwt Eliom_reference.get oidc_state with
| None -> failwith "oidc handler was invoked without a state"
| Some x -> return x
in
Eliom_reference.unset oidc_state >>
Eliom_reference.unset auth_env >>
if state <> st then fail_http 401 else
(match%lwt oidc_get_name ocfg client_id client_secret code with
| Some name ->
Eliom_reference.set user (Some {uuid; service; name}) >>
default_cont uuid ()
| None -> fail_http 401)
| _, _ -> default_cont uuid ()
let () = Eliom_registration.Any.register ~service:login_oidc oidc_handler
let get_oidc_configuration server =
let url = server ^ "/.well-known/openid-configuration" in
let%lwt reply = Ocsigen_http_client.get_url url in
match reply.Ocsigen_http_frame.frame_content with
| Some stream ->
let%lwt info = Ocsigen_stream.(string_of_stream 10000 (get stream)) in
Ocsigen_stream.finalize stream `Success >>
return (oidc_configuration_of_string info)
| None -> fail_http 404
let split_prefix_path url =
let n = String.length url in
let i = String.rindex url '/' in
String.sub url 0 i, [String.sub url (i+1) (n-i-1)]
let oidc_login_handler config () =
match config with
| [server; client_id; client_secret] ->
let%lwt ocfg = get_oidc_configuration server in
let%lwt state = generate_token () in
Eliom_reference.set oidc_state (Some (ocfg, client_id, client_secret, state)) >>
let prefix, path = split_prefix_path ocfg.authorization_endpoint in
let auth_endpoint = Http.external_service ~prefix ~path
~get_params:Eliom_parameter.(string "redirect_uri" **
string "response_type" ** string "client_id" **
string "scope" ** string "state" ** string "prompt")
()
in
let service = preapply auth_endpoint
(Lazy.force oidc_self, ("code", (client_id, ("openid email", (state, "consent")))))
in
Eliom_registration.Redirection.send service
| _ -> failwith "oidc_login_handler invoked with bad config"
(** Generic authentication *)
let get_login_handler service uuid auth_system config =
Eliom_reference.set auth_env (Some (uuid, service)) >>
match auth_system with
| "dummy" -> Web_templates.login_dummy () >>= Eliom_registration.Html5.send
| "cas" -> cas_login_handler config ()
| "password" -> Web_templates.login_password () >>= Eliom_registration.Html5.send
| "oidc" -> oidc_login_handler config ()
| _ -> fail_http 404
let login_handler service uuid =
let myself service =
match uuid with
| None -> preapply site_login service
| Some u -> preapply election_login ((u, ()), service)
in
match%lwt Eliom_reference.get user with
| Some _ ->
cont_push (fun () -> Eliom_registration.Redirection.send (myself service)) >>
Web_templates.already_logged_in () >>= Eliom_registration.Html5.send
| None ->
let uuid_or_empty = match uuid with
| None -> ""
| Some u -> Uuidm.to_string u
in
let%lwt c = Web_persist.get_auth_config uuid_or_empty in
match service with
| Some s ->
let%lwt auth_system, config =
try return @@ List.assoc s c
with Not_found -> fail_http 404
in
get_login_handler s uuid auth_system config
| None ->
match c with
| [s, _] -> Eliom_registration.Redirection.send (myself (Some s))
| _ ->
let builder =
match uuid with
| None -> fun s ->
preapply Web_services.site_login (Some s)
| Some u -> fun s ->
preapply Web_services.election_login ((u, ()), Some s)
in
Web_templates.login_choose (List.map fst c) builder () >>=
Eliom_registration.Html5.send
let logout_handler () =
Eliom_reference.unset Web_state.user >>
match%lwt cont_pop () with
| Some f -> f ()
| None -> Eliom_registration.Redirection.send Web_services.home
let () = Eliom_registration.Any.register ~service:site_login
(fun service () -> login_handler service None)
let () = Eliom_registration.Any.register ~service:logout
(fun () () -> logout_handler ())
let () = Eliom_registration.Any.register ~service:election_login
(fun ((uuid, ()), service) () -> login_handler service (Some uuid))
belenios-1.4+dfsg/src/web/web_auth.mli000066400000000000000000000001021307140314400177300ustar00rootroot00000000000000open Web_serializable_t
val configure : auth_config list -> unit
belenios-1.4+dfsg/src/web/web_common.ml000066400000000000000000000157521307140314400201270ustar00rootroot00000000000000(**************************************************************************)
(* BELENIOS *)
(* *)
(* Copyright © 2012-2016 Inria *)
(* *)
(* 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. *)
(* *)
(* You should have received a copy of the GNU Affero General Public *)
(* License along with this program. If not, see *)
(* . *)
(**************************************************************************)
open Lwt
open Platform
open Common
open Web_serializable_builtin_t
open Web_serializable_j
let spool_dir = ref "."
let make_rng = Lwt_preemptive.detach (fun () ->
pseudo_rng (random_string secure_rng 16)
)
module type LWT_RANDOM = Signatures.RANDOM with type 'a t = 'a Lwt.t
module type LWT_RNG = sig
val rng : rng Lwt.t
end
module MakeLwtRandom (X : LWT_RNG) = struct
type 'a t = 'a Lwt.t
let return = Lwt.return
let bind = Lwt.bind
let fail = Lwt.fail
let random q =
let size = Z.bit_length q / 8 + 1 in
let%lwt rng = X.rng in
let r = random_string rng size in
return Z.(of_bits r mod q)
end
type error =
| Serialization of exn
| ProofCheck
| ElectionClosed
| MissingCredential
| InvalidCredential
| RevoteNotAllowed
| ReusedCredential
| WrongCredential
| UsedCredential
| CredentialNotFound
| UnauthorizedVoter
exception Error of error
let fail e = Lwt.fail (Error e)
let explain_error = function
| Serialization e ->
Printf.sprintf "your ballot has a syntax error (%s)" (Printexc.to_string e)
| ProofCheck -> "some proofs failed verification"
| ElectionClosed -> "the election is closed"
| MissingCredential -> "a credential is missing"
| InvalidCredential -> "your credential is invalid"
| RevoteNotAllowed -> "you are not allowed to revote"
| ReusedCredential -> "your credential has already been used"
| WrongCredential -> "you are not allowed to vote with this credential"
| UsedCredential -> "the credential has already been used"
| CredentialNotFound -> "the credential has not been found"
| UnauthorizedVoter -> "you are not allowed to vote"
let security_logfile = ref None
let open_security_log f =
let%lwt () =
match !security_logfile with
| Some ic -> Lwt_io.close ic
| None -> return ()
in
let%lwt ic = Lwt_io.(
open_file ~flags:Unix.(
[O_WRONLY; O_APPEND; O_CREAT]
) ~perm:0o600 ~mode:output f
) in
security_logfile := Some ic;
return ()
let security_log s =
match !security_logfile with
| None -> return ()
| Some ic -> Lwt_io.atomic (fun ic ->
Lwt_io.write ic (
string_of_datetime (now ())
) >>
Lwt_io.write ic ": " >>
Lwt_io.write_line ic (s ()) >>
Lwt_io.flush ic
) ic
let fail_http status =
[%lwt raise (
Ocsigen_extensions.Ocsigen_http_error
(Ocsigen_cookies.empty_cookieset, status)
)]
let forbidden () = fail_http 403
let rewrite_fun = ref (fun x -> x)
let rewrite_prefix x = !rewrite_fun x
let set_rewrite_prefix ~src ~dst =
let nsrc = String.length src in
let f x =
let n = String.length x in
if n >= nsrc && String.sub x 0 nsrc = src then
dst ^ String.sub x nsrc (n-nsrc)
else x
in rewrite_fun := f
type election_file =
| ESRaw
| ESKeys
| ESCreds
| ESBallots
| ESVoters
| ESRecords
| ESETally
| ESResult
let election_file_of_string = function
| "election.json" -> ESRaw
| "public_keys.jsons" -> ESKeys
| "public_creds.txt" -> ESCreds
| "ballots.jsons" -> ESBallots
| "records" -> ESRecords
| "voters.txt" -> ESVoters
| "encrypted_tally.json" -> ESETally
| "result.json" -> ESResult
| x -> invalid_arg ("election_dir_item: " ^ x)
let string_of_election_file = function
| ESRaw -> "election.json"
| ESKeys -> "public_keys.jsons"
| ESCreds -> "public_creds.txt"
| ESBallots -> "ballots.jsons"
| ESRecords -> "records"
| ESVoters -> "voters.txt"
| ESETally -> "encrypted_tally.json"
| ESResult -> "result.json"
let election_file = Eliom_parameter.user_type
~of_string:election_file_of_string
~to_string:string_of_election_file
let uuid_of_string x =
match Uuidm.of_string x with
| Some x -> x
| None -> Printf.ksprintf invalid_arg "invalid UUID [%s]" x
let uuid =
let of_string x = uuid_of_string x
and to_string x = Uuidm.to_string x
in Eliom_parameter.user_type ~of_string ~to_string
let b58_digits = "123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz"
let token_length = 14
let prng = lazy (pseudo_rng (random_string secure_rng 16))
let random_char () =
let%lwt rng =
if Lazy.is_val prng then return (Lazy.force prng) else
Lwt_preemptive.detach (fun () -> Lazy.force prng) ()
in
return (int_of_char (random_string rng 1).[0])
let generate_token () =
let res = Bytes.create token_length in
let rec loop i =
if i < token_length then (
let%lwt digit = random_char () in
let digit = digit mod 58 in
Bytes.set res i b58_digits.[digit];
loop (i+1)
) else return (Bytes.to_string res)
in loop 0
let string_of_user {user_domain; user_name} =
user_domain ^ ":" ^ user_name
let underscorize x =
String.map (function '-' -> '_' | c -> c) x
let send_email recipient subject body =
let contents =
Netsendmail.compose
~from_addr:("Belenios public server", "noreply@belenios.org")
~to_addrs:[recipient, recipient]
~in_charset:`Enc_utf8 ~out_charset:`Enc_utf8
~subject body
in
let rec loop () =
try%lwt
Lwt_preemptive.detach Netsendmail.sendmail contents
with Unix.Unix_error (Unix.EAGAIN, _, _) ->
Lwt_unix.sleep 1. >> loop ()
in loop ()
let split_identity x =
let n = String.length x in
try
let i = String.index x ',' in
String.sub x 0 i, String.sub x (i+1) (n-i-1)
with Not_found ->
x, x
let available_languages = ["en"; "fr"; "de"; "ro"; "it"]
let get_languages xs =
match xs with
| None -> ["en"]
| Some xs -> xs
let string_of_languages xs =
String.concat " " (get_languages xs)
let languages_of_string x =
Some (Pcre.split x)
belenios-1.4+dfsg/src/web/web_common.mli000066400000000000000000000070641307140314400202750ustar00rootroot00000000000000(**************************************************************************)
(* BELENIOS *)
(* *)
(* Copyright © 2012-2016 Inria *)
(* *)
(* 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. *)
(* *)
(* You should have received a copy of the GNU Affero General Public *)
(* License along with this program. If not, see *)
(* . *)
(**************************************************************************)
open Web_serializable_t
val spool_dir : string ref
val make_rng : unit -> Platform.rng Lwt.t
(** Create a pseudo random number generator initialized by a 128-bit
secure random seed. *)
module type LWT_RANDOM = Signatures.RANDOM with type 'a t = 'a Lwt.t
module type LWT_RNG = sig
val rng : Platform.rng Lwt.t
end
module MakeLwtRandom (X : LWT_RNG) : LWT_RANDOM
(** Lwt-compatible random number generation. *)
type error =
| Serialization of exn
| ProofCheck
| ElectionClosed
| MissingCredential
| InvalidCredential
| RevoteNotAllowed
| ReusedCredential
| WrongCredential
| UsedCredential
| CredentialNotFound
| UnauthorizedVoter
exception Error of error
val fail : error -> 'a Lwt.t
val explain_error : error -> string
val open_security_log : string -> unit Lwt.t
(** Set the path to the security logger. *)
val security_log : (unit -> string) -> unit Lwt.t
(** Add an entry to the security log. *)
val fail_http : int -> 'a Lwt.t
val forbidden : unit -> 'a Lwt.t
val rewrite_prefix : string -> string
val set_rewrite_prefix : src:string -> dst:string -> unit
type election_file =
| ESRaw
| ESKeys
| ESCreds
| ESBallots
| ESVoters
| ESRecords
| ESETally
| ESResult
val election_file_of_string : string -> election_file
val string_of_election_file : election_file -> string
val election_file :
string ->
(election_file, [ `WithoutSuffix ],
[ `One of election_file ] Eliom_parameter.param_name)
Eliom_parameter.params_type
val uuid_of_string : string -> Uuidm.t
val uuid :
string ->
(Uuidm.t, [ `WithoutSuffix ],
[ `One of Uuidm.t ] Eliom_parameter.param_name)
Eliom_parameter.params_type
val generate_token : unit -> string Lwt.t
val string_of_user : user -> string
val underscorize : string -> string
val send_email : string -> string -> string -> unit Lwt.t
val split_identity : string -> string * string
val available_languages : string list
val get_languages : string list option -> string list
val string_of_languages : string list option -> string
val languages_of_string : string -> string list option
belenios-1.4+dfsg/src/web/web_election.ml000066400000000000000000000215071307140314400204340ustar00rootroot00000000000000(**************************************************************************)
(* BELENIOS *)
(* *)
(* Copyright © 2012-2016 Inria *)
(* *)
(* 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. *)
(* *)
(* You should have received a copy of the GNU Affero General Public *)
(* License along with this program. If not, see *)
(* . *)
(**************************************************************************)
open Lwt
open Platform
open Serializable_j
open Signatures
open Common
open Web_serializable_j
open Web_signatures
open Web_common
let ( / ) = Filename.concat
module Make (D : ELECTION_DATA) (M : RANDOM with type 'a t = 'a Lwt.t) : WEB_ELECTION = struct
let uuid = Uuidm.to_string D.election.e_params.e_uuid
module G = D.G
module E = Election.MakeElection (G) (M)
module B : WEB_BALLOT_BOX = struct
let uuid_u = underscorize uuid
let ballots_table = Ocsipersist.open_table ("ballots_" ^ uuid_u)
let records_table = Ocsipersist.open_table ("records_" ^ uuid_u)
let cred_table = Ocsipersist.open_table ("creds_" ^ uuid_u)
let inject_cred cred =
try%lwt
let%lwt _ = Ocsipersist.find cred_table cred in
failwith "trying to add duplicate credential"
with Not_found ->
Ocsipersist.add cred_table cred None
let send_confirmation_email user email hash =
let title = D.election.e_params.e_name in
let x = (D.election.e_params.e_uuid, ()) in
let url1 = Eliom_uri.make_string_uri ~absolute:true
~service:Web_services.election_pretty_ballots x |> rewrite_prefix
in
let url2 = Eliom_uri.make_string_uri ~absolute:true
~service:Web_services.election_home x |> rewrite_prefix
in
let%lwt language = Eliom_reference.get Web_state.language in
let module L = (val Web_i18n.get_lang language) in
let subject = Printf.sprintf L.mail_confirmation_subject title in
let body = Printf.sprintf L.mail_confirmation user title hash url1 url2 in
send_email email subject body
let do_cast rawballot (user, date) =
let voters = Lwt_io.lines_of_file (!spool_dir / uuid / "voters.txt") in
let%lwt voters = Lwt_stream.to_list voters in
let%lwt email, login =
let rec loop = function
| x :: xs ->
let email, login = split_identity x in
if login = user.user_name then return (email, login) else loop xs
| [] -> fail UnauthorizedVoter
in loop voters
in
let user = string_of_user user in
let%lwt state = Web_persist.get_election_state uuid in
let voting_open = state = `Open in
if not voting_open then fail ElectionClosed else return () >>
if String.contains rawballot '\n' then (
fail (Serialization (Invalid_argument "multiline ballot"))
) else return () >>
let%lwt ballot =
try Lwt.return (ballot_of_string G.read rawballot)
with e -> fail (Serialization e)
in
let%lwt credential =
match ballot.signature with
| Some s -> Lwt.return (G.to_string s.s_public_key)
| None -> fail MissingCredential
in
let%lwt old_cred =
try%lwt Ocsipersist.find cred_table credential
with Not_found -> fail InvalidCredential
and old_record =
try%lwt
let%lwt x = Ocsipersist.find records_table user in
Lwt.return (Some x)
with Not_found -> Lwt.return None
in
match old_cred, old_record with
| None, None ->
(* first vote *)
if E.check_ballot D.election ballot then (
let hash = sha256_b64 rawballot in
Ocsipersist.add cred_table credential (Some hash) >>
Ocsipersist.add ballots_table hash rawballot >>
Ocsipersist.add records_table user (date, credential) >>
send_confirmation_email login email hash >>
return hash
) else (
fail ProofCheck
)
| Some h, Some (_, old_credential) ->
(* revote *)
if credential = old_credential then (
if E.check_ballot D.election ballot then (
Ocsipersist.remove ballots_table h >>
let hash = sha256_b64 rawballot in
Ocsipersist.add cred_table credential (Some hash) >>
Ocsipersist.add ballots_table hash rawballot >>
Ocsipersist.add records_table user (date, credential) >>
send_confirmation_email login email hash >>
return hash
) else (
fail ProofCheck
)
) else (
security_log (fun () ->
Printf.sprintf "%s attempted to revote with already used credential %s" user credential
) >> fail WrongCredential
)
| None, Some _ ->
security_log (fun () ->
Printf.sprintf "%s attempted to revote using a new credential %s" user credential
) >> fail RevoteNotAllowed
| Some _, None ->
security_log (fun () ->
Printf.sprintf "%s attempted to vote with already used credential %s" user credential
) >> fail ReusedCredential
let do_update_cred ~old ~new_ =
match%lwt Ocsipersist.fold_step (fun k v x ->
if sha256_hex k = old then (
match v with
| Some _ -> fail UsedCredential
| None -> return (Some k)
) else return x
) cred_table None with
| None -> fail CredentialNotFound
| Some x ->
Ocsipersist.remove cred_table x >>
Ocsipersist.add cred_table new_ None
let do_write f =
Lwt_io.(with_file ~mode:Output (!spool_dir / uuid / string_of_election_file f))
let do_write_ballots () =
do_write ESBallots (fun oc ->
Ocsipersist.iter_step (fun _ x ->
Lwt_io.write_line oc x
) ballots_table
)
let do_write_creds () =
do_write ESCreds (fun oc ->
Ocsipersist.iter_step (fun x _ ->
Lwt_io.write_line oc x
) cred_table
)
let do_write_records () =
do_write ESRecords (fun oc ->
Ocsipersist.iter_step (fun u (d, _) ->
Printf.sprintf "%s %S\n" (string_of_datetime d) u |>
Lwt_io.write oc
) records_table
)
let mutex = Lwt_mutex.create ()
let cast rawballot (user, date) =
Lwt_mutex.with_lock mutex (fun () ->
let%lwt r = do_cast rawballot (user, date) in
do_write_ballots () >>
do_write_records () >>
return r
)
let update_cred ~old ~new_ =
Lwt_mutex.with_lock mutex (fun () ->
let%lwt r = do_update_cred ~old ~new_ in
do_write_creds () >> return r
)
let update_files () =
Lwt_mutex.with_lock mutex (fun () ->
do_write_ballots () >>
do_write_records () >>
do_write_creds ()
)
let compute_encrypted_tally () =
let%lwt num_tallied, tally =
Ocsipersist.fold_step
(fun _ rawballot (n, accu) ->
let ballot = ballot_of_string G.read rawballot in
let ciphertext = E.extract_ciphertext ballot in
return (n + 1, E.combine_ciphertexts accu ciphertext))
ballots_table (0, E.neutral_ciphertext D.election)
in
let tally = string_of_encrypted_tally G.write tally in
Lwt_mutex.with_lock mutex (fun () ->
do_write ESETally (fun oc ->
Lwt_io.write oc tally
)
) >>
return (num_tallied, sha256_b64 tally, tally)
end
end
belenios-1.4+dfsg/src/web/web_election.mli000066400000000000000000000032011307140314400205740ustar00rootroot00000000000000(**************************************************************************)
(* BELENIOS *)
(* *)
(* Copyright © 2012-2016 Inria *)
(* *)
(* 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. *)
(* *)
(* You should have received a copy of the GNU Affero General Public *)
(* License along with this program. If not, see *)
(* . *)
(**************************************************************************)
open Signatures
open Web_signatures
module Make (D : ELECTION_DATA) (M : RANDOM with type 'a t = 'a Lwt.t) : WEB_ELECTION
belenios-1.4+dfsg/src/web/web_i18n.ml000066400000000000000000000005321307140314400174040ustar00rootroot00000000000000let get_lang = function
| "fr" -> (module Web_l10n_fr : Web_i18n_sig.LocalizedStrings)
| "de" -> (module Web_l10n_de : Web_i18n_sig.LocalizedStrings)
| "ro" -> (module Web_l10n_ro : Web_i18n_sig.LocalizedStrings)
| "it" -> (module Web_l10n_it : Web_i18n_sig.LocalizedStrings)
| _ -> (module Web_l10n_en : Web_i18n_sig.LocalizedStrings)
belenios-1.4+dfsg/src/web/web_i18n.mli000066400000000000000000000001001307140314400175440ustar00rootroot00000000000000val get_lang : string -> (module Web_i18n_sig.LocalizedStrings)
belenios-1.4+dfsg/src/web/web_i18n_sig.mli000066400000000000000000000113661307140314400204260ustar00rootroot00000000000000(**************************************************************************)
(* BELENIOS *)
(* *)
(* Copyright © 2012-2016 Inria *)
(* *)
(* 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. *)
(* *)
(* You should have received a copy of the GNU Affero General Public *)
(* License along with this program. If not, see *)
(* . *)
(**************************************************************************)
module type LocalizedStrings = sig
val lang : string
val start : string
val advanced_mode : string
val see_accepted_ballots : string
val belenios_booth : string
val here : string
val question_header : string
val at_least : string
val at_most : string
val previous : string
val next : string
val nothing : string
val enter_cred : string
val invalid_cred : string
val input_credential : string
val answer_to_questions : string
val review_and_encrypt : string
val authenticate : string
val confirm : string
val done_ : string
val booth_step1 : string
val booth_step2 : string
val booth_step3 : string
val booth_step5 : string
val booth_step6 : string
val input_your_credential : string
val wait_while_encrypted : string
val encrypting : string
val restart : string
val successfully_encrypted : string
val not_cast_yet : string
val qmark : string
val your_tracker_is : string
val we_invite_you_to_save_it : string
val continue : string
val election_uuid : string
val election_fingerprint : string
val i_am : string
val and_ : string
val i_cast_my_vote : string
val please_login_to_confirm : string
val your_ballot_for : string
val has_been_received : string
val nobody_can_see : string
val go_back_to_election : string
val has_been_accepted : string
val you_can_check_its_presence : string
val ballot_box : string
val anytime_during_the_election : string
val confirmation_email : string
val thank_you_for_voting : string
val is_rejected_because : string
val fail : string
val administer_elections : string
val administer_this_election : string
val powered_by : string
val get_the_source_code : string
val audit_data : string
val parameters : string
val public_credentials : string
val trustee_public_keys : string
val ballots : string
val election_server : string
val accepted_ballots : string
val ballots_have_been_accepted_so_far : string
val ballots_have_been_accepted : string
val ballots_have_been_accepted_and : string
val have_been_tallied : string
val username : string
val password : string
val login : string
val password_login : string
val you_must_accept_cookies : string
val accept : string
val not_yet_open : string
val come_back_later : string
val cookies_are_blocked : string
val please_enable_them : string
val election_currently_closed : string
val election_closed_being_tallied : string
val the : string
val encrypted_tally : string
val hash_is : string
val election_has_been_tallied : string
val election_archived : string
val number_accepted_ballots : string
val you_can_also_download : string
val result_with_crypto_proofs : string
val blank_vote : string
val no_other_blank : string
val mail_password_subject : (string -> 'f, 'b, 'c, 'e, 'e, 'f) format6
val mail_password : (string -> string -> string -> string -> 'f, 'b, 'c, 'e, 'e, 'f) format6
val mail_credential_subject : (string -> 'f, 'b, 'c, 'e, 'e, 'f) format6
val mail_credential : (string -> string -> string -> string -> 'f, 'b, 'c, 'e, 'e, 'f) format6
val mail_confirmation_subject : (string -> 'f, 'b, 'c, 'e, 'e, 'f) format6
val mail_confirmation : (string -> string -> string -> string -> string -> 'f, 'b, 'c, 'e, 'e, 'f) format6
end
belenios-1.4+dfsg/src/web/web_l10n_de.ml000066400000000000000000000202041307140314400200450ustar00rootroot00000000000000(**************************************************************************)
(* BELENIOS *)
(* *)
(* Copyright © 2012-2016 Inria *)
(* *)
(* 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. *)
(* *)
(* You should have received a copy of the GNU Affero General Public *)
(* License along with this program. If not, see *)
(* . *)
(**************************************************************************)
let lang = "de"
let start = "Start"
let advanced_mode = "Erweiterter Modus"
let see_accepted_ballots = "angenommene Stimmen anzeigen"
let belenios_booth = "Belenios Wahlkabine"
let here = "hier"
let question_header = "Frage #%d von %d — wählen Sie zwischen %d und %d Antworten aus"
let at_least = "Sie müssen mindestens %d Antworten auswählen"
let at_most = "Sie müssen maximal %d Antworten auswählen"
let previous = "Zurück"
let next = "Weiter"
let nothing = "(nichts)"
let enter_cred = "Bitte geben Sie Ihre Wählernummer ein:"
let invalid_cred = "Falsche Wählernummer!"
let input_credential = "Wählernummer eingeben"
let answer_to_questions = "Fragen beantworten"
let review_and_encrypt = "Überprüfen und verschlüsseln"
let authenticate = "Authentifizieren"
let confirm = "Bestätigen"
let done_ = "Fertig"
let booth_step1 = "Schritt 1/6: Wählernummer eingeben"
let booth_step2 = "Schritt 2/6: Fragen beantworten"
let booth_step3 = "Schritt 3/6: Überprüfen und verschlüsseln"
let booth_step5 = "Schritt 5/6: Bestätigen"
let booth_step6 = "Schritt 6/6: "
let input_your_credential = "Bitte Wählernummer eingeben "
let wait_while_encrypted = "Bitte warten, Ihre Stimme wird verschlüsselt..."
let encrypting = "Verschlüssele..."
let restart = "Erneut beginnen"
let successfully_encrypted = "Ihre Stimme wurde erfolgreich verschlüsselt, "
let not_cast_yet = "aber noch nicht abgeschickt"
let qmark = "!"
let your_tracker_is = "Ihre Stimmennummer ist "
let we_invite_you_to_save_it = "Bitte Speichern Sie sie ab um später zu überprüfen, dass Ihre Stimme gezählt wurde."
let continue = "Weiter"
let election_uuid = "Eindeutige Nummer der Abstimmung: "
let election_fingerprint = "Fingerabdruck der Abstimmung: "
let i_am = "Ich bin "
let and_ = " und "
let i_cast_my_vote = "ich schicke meine Stimme ab"
let please_login_to_confirm = "Bitte melden Sie sich an um Ihre Stime zu bestätigen."
let your_ballot_for = "Ihre Stimme für "
let has_been_received = " wurde empfangen, aber noch nicht gespeichert. "
let nobody_can_see = "Hinweis: Ihre Stimme ist verschlüsselt und niemand kann ihren Inhalt sehen."
let go_back_to_election = "Zurück zur Wahl"
let has_been_accepted = " wurde angenommen."
let you_can_check_its_presence = "Sie können jederzeit überprüfen, dass Ihre Stimme in der "
let ballot_box = "Wahlurne"
let anytime_during_the_election = " vorhanden ist."
let confirmation_email = " Sie erhalten eine Bestätigung per E-Mail."
let thank_you_for_voting = "Vielen Dank für Ihre Stimme!"
let is_rejected_because = " wurde abgelehnt, da "
let fail = "FEHLER!"
let administer_elections = "Abstimmung verwalten"
let administer_this_election = "Diese Abstimmung verwalten"
let powered_by = "Powered by "
let get_the_source_code = "Den Quellcode herunterladen"
let audit_data = "Auditdaten: "
let parameters = "Parameter"
let public_credentials = "Öffentliche Daten"
let trustee_public_keys = "Öffentliche Schlüssel der Treuhänder"
let ballots = "Stimmen"
let election_server = "Wahlserver"
let accepted_ballots = "Angenommene Stimmen"
let ballots_have_been_accepted_so_far = " Stimmen wurden bis jetzt angenommen."
let ballots_have_been_accepted = " Stimmen wurden angenommen."
let ballots_have_been_accepted_and = " Stimmen wurden angenommen, und "
let have_been_tallied = " wurden gezählt."
let username = "Benutzername:"
let password = "Passwort:"
let login = "Login"
let password_login = "Login mit Passwort"
let you_must_accept_cookies = "Um diese Seite benutzen zu können, müssen Sie Cookies aktivieren. "
let accept = "Bestätigen"
let not_yet_open = "Entschuldigung, die Abstimmung ist noch nicht geöffnet."
let come_back_later = "Diese Abstimmung gibt es noch nicht. Bitte kommen Sie später wieder."
let cookies_are_blocked = "Cookies sind deaktiviert"
let please_enable_them = "Ihr Browser nimmt keine Cookies an, bitte aktivieren Sie diese."
let election_currently_closed = "Diese Abstimmung ist beendet."
let election_closed_being_tallied = "Diese Abstimmung ist beendet und wird ausgezählt."
let the = "Der Hash des "
let encrypted_tally = "verschlüsselten Ergebnisses"
let hash_is = " ist "
let election_has_been_tallied = "Diese Abstimmung wurde ausgezählt."
let election_archived = "Diese Abstimmung wurde archiviert."
let number_accepted_ballots = "Anzahl angenommener Stimmen: "
let you_can_also_download = "Sie können außerdem das "
let result_with_crypto_proofs = "Ergbnis mit den kryptographischen Beweisen herunterladen"
let blank_vote = "ungültige Stimme"
let no_other_blank = "Bei einer ungültigen Stimme sind keine anderen Auswahlmöglichkeiten vorhanden."
let mail_password_subject : ('a, 'b, 'c, 'd, 'e, 'f) format6 =
"Ihr Passwort für die Abstimmung %s"
let mail_password : ('a, 'b, 'c, 'd, 'e, 'f) format6 =
"Sie sind für die folgende Abstimmung als Wähler eingetragen:
%s
Am Ende der Mail finden Sie Ihren Benutzername und Ihr Passwort. Um
abzustimmen benötigen sie außerdem noch Ihre Wählernummer, die Ihnen
in einer seperaten Mail zugestellt wird. Obwohl Passwort und
Wählernummer ähnlich aussehen, erfüllen sie zwei verschiedene Zwecke:
die Wählernummer wird für die Verschlüsselung Ihrer Stimme in der
virtuellen Wahlkabine benötigt, mit dem Passwort können Sie
anschließend Ihre verschlüsselte Stimme auf den Wahlserver übertragen.
Benutzername: %s
Passwort: %s
Website der Abstimmung: %s
Sie können so oft abstimmen wie Sie wollen, nur die letzte Stimme zählt."
let mail_credential_subject : ('a, 'b, 'c, 'd, 'e, 'f) format6 =
"Ihre Wählernummer für die Abstimmung %s"
let mail_credential : ('a, 'b, 'c, 'd, 'e, 'f) format6 =
"Sie sind für die folgende Abstimmung als Wähler eingetragen:
%s
Am Ende der Mail finden Sie Ihren Benutzername und Ihre Wählernummer. Um
abzustimmen benötigen sie außerdem noch Ihr Passwort, die Ihnen
in einer seperaten Mail zugestellt wird. Obwohl Passwort und
Wählernummer ähnlich aussehen, erfüllen sie zwei verschiedene Zwecke:
die Wählernummer wird für die Verschlüsselung Ihrer Stimme in der
virtuellen Wahlkabine benötigt, mit dem Passwort können Sie
anschließend Ihre verschlüsselte Stimme auf den Wahlserver übertragen.
Benutzername: %s
Wählernummer: %s
Website der Abstimmung: %s
Sie können so oft abstimmen wie Sie wollen, nur die letzte Stimme zählt."
let mail_confirmation_subject : ('a, 'b, 'c, 'd, 'e, 'f) format6 =
"Ihre Stimme zur Abstimmung %s"
let mail_confirmation : ('a, 'b, 'c, 'd, 'e, 'f) format6 =
"%s,
Ihre Stimme zur Abstimmung
%s
wurde angenommen. Ihre Stimmennummer ist:
%s
Mit dieser Nummer können Sie überprüfen, ob sich Ihre Stimme in der
Wahlurne befindet:
%s
Das Ergebnis wird auf der Website der Abstimmung veröffentlicht:
%s
-- \nBelenios"
belenios-1.4+dfsg/src/web/web_l10n_de.mli000066400000000000000000000000461307140314400202200ustar00rootroot00000000000000include Web_i18n_sig.LocalizedStrings
belenios-1.4+dfsg/src/web/web_l10n_en.ml000066400000000000000000000165431307140314400200720ustar00rootroot00000000000000(**************************************************************************)
(* BELENIOS *)
(* *)
(* Copyright © 2012-2016 Inria *)
(* *)
(* 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. *)
(* *)
(* You should have received a copy of the GNU Affero General Public *)
(* License along with this program. If not, see *)
(* . *)
(**************************************************************************)
let lang = "en"
let start = "Start"
let advanced_mode = "Advanced mode"
let see_accepted_ballots = "See accepted ballots"
let belenios_booth = "Belenios Booth"
let here = "here"
let question_header = "Question #%d of %d — select between %d and %d answer(s)"
let at_least = "You must select at least %d answer(s)"
let at_most = "You must select at most %d answer(s)"
let previous = "Previous"
let next = "Next"
let nothing = "(nothing)"
let enter_cred = "Please enter your credential:"
let invalid_cred = "Invalid credential!"
let input_credential = "Input credential"
let answer_to_questions = "Answer to questions"
let review_and_encrypt = "Review and encrypt"
let authenticate = "Authenticate"
let confirm = "Confirm"
let done_ = "Done"
let booth_step1 = "Step 1/6: Input your credential"
let booth_step2 = "Step 2/6: Answer to questions"
let booth_step3 = "Step 3/6: Review and encrypt"
let booth_step5 = "Step 5/6: Confirm"
let booth_step6 = "Step 6/6: "
let input_your_credential = "Input your credential "
let wait_while_encrypted = "Please wait while your ballot is being encrypted..."
let encrypting = "Encrypting..."
let restart = "Restart"
let successfully_encrypted = "Your ballot has been successfully encrypted, "
let not_cast_yet = "but has not been cast yet"
let qmark = "!"
let your_tracker_is = "Your smart ballot tracker is "
let we_invite_you_to_save_it = "We invite you to save it in order to check later that it is taken into account."
let continue = "Continue"
let election_uuid = "Election UUID: "
let election_fingerprint = "Election fingerprint: "
let i_am = "I am "
let and_ = " and "
let i_cast_my_vote = "I cast my vote"
let please_login_to_confirm = "Please log in to confirm your vote."
let your_ballot_for = "Your ballot for "
let has_been_received = " has been received, but not recorded yet. "
let nobody_can_see = "Note: your ballot is encrypted and nobody can see its contents."
let go_back_to_election = "Go back to election"
let has_been_accepted = " has been accepted."
let you_can_check_its_presence = "You can check its presence in the "
let ballot_box = "ballot box"
let anytime_during_the_election = " anytime during the election."
let confirmation_email = " A confirmation e-mail has been sent to you."
let thank_you_for_voting = "Thank you for voting!"
let is_rejected_because = " is rejected, because "
let fail = "FAIL!"
let administer_elections = "Administer elections"
let administer_this_election = "Administer this election"
let powered_by = "Powered by "
let get_the_source_code = "Get the source code"
let audit_data = "Audit data: "
let parameters = "parameters"
let public_credentials = "public credentials"
let trustee_public_keys = "trustee public keys"
let ballots = "ballots"
let election_server = "Election server"
let accepted_ballots = "Accepted ballots"
let ballots_have_been_accepted_so_far = " ballot(s) have been accepted so far."
let ballots_have_been_accepted = " ballot(s) have been accepted."
let ballots_have_been_accepted_and = " ballot(s) have been accepted, and "
let have_been_tallied = " have been tallied."
let username = "Username:"
let password = "Password:"
let login = "Login"
let password_login = "Password login"
let you_must_accept_cookies = "To use this site, you must accept cookies. "
let accept = "Accept"
let not_yet_open = "Sorry, this election is not yet open"
let come_back_later = "This election does not exist yet. Please come back later."
let cookies_are_blocked = "Cookies are blocked"
let please_enable_them = "Your browser seems to block cookies. Please enable them."
let election_currently_closed = "This election is currently closed."
let election_closed_being_tallied = "The election is closed and being tallied."
let the = " The "
let encrypted_tally = "encrypted tally"
let hash_is = " hash is "
let election_has_been_tallied = "This election has been tallied."
let election_archived = "This election is archived."
let number_accepted_ballots = "Number of accepted ballots: "
let you_can_also_download = "You can also download the "
let result_with_crypto_proofs = "result with cryptographic proofs"
let blank_vote = "Blank vote"
let no_other_blank = "No other choices are allowed when voting blank"
let mail_password_subject : ('a, 'b, 'c, 'd, 'e, 'f) format6 =
"Your password for election %s"
let mail_password : ('a, 'b, 'c, 'd, 'e, 'f) format6 =
"You are listed as a voter for the election
%s
You will find below your login and password. To cast a vote, you will
also need a credential, sent in a separate email. Be careful,
passwords and credentials look similar but play different roles. You
will be asked to enter your credential before entering the voting
booth. Login and passwords are required once your ballot is ready to
be cast.
Username: %s
Password: %s
Page of the election: %s
Note that you are allowed to vote several times. Only the last vote
counts."
let mail_credential_subject : ('a, 'b, 'c, 'd, 'e, 'f) format6 =
"Your credential for election %s"
let mail_credential : ('a, 'b, 'c, 'd, 'e, 'f) format6 =
"You are listed as a voter for the election
%s
You will find below your login and credential. To cast a vote, you will
also need a password, sent in a separate email. Be careful,
passwords and credentials look similar but play different roles. You
will be asked to enter your credential before entering the voting
booth. Login and passwords are required once your ballot is ready to
be cast.
Username: %s
Credential: %s
Page of the election: %s
Note that you are allowed to vote several times. Only the last vote
counts."
let mail_confirmation_subject : ('a, 'b, 'c, 'd, 'e, 'f) format6 =
"Your vote for election %s"
let mail_confirmation : ('a, 'b, 'c, 'd, 'e, 'f) format6 =
"Dear %s,
Your vote for election
%s
has been recorded. Your smart ballot tracker is
%s
You can check its presence in the ballot box, accessible at
%s
Results will be published on the election page
%s
-- \nBelenios"
belenios-1.4+dfsg/src/web/web_l10n_en.mli000066400000000000000000000000461307140314400202320ustar00rootroot00000000000000include Web_i18n_sig.LocalizedStrings
belenios-1.4+dfsg/src/web/web_l10n_fr.ml000066400000000000000000000205021307140314400200650ustar00rootroot00000000000000(**************************************************************************)
(* BELENIOS *)
(* *)
(* Copyright © 2012-2016 Inria *)
(* *)
(* 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. *)
(* *)
(* You should have received a copy of the GNU Affero General Public *)
(* License along with this program. If not, see *)
(* . *)
(**************************************************************************)
let lang = "fr"
let start = "Commencer"
let advanced_mode = "Mode avancé"
let see_accepted_ballots = "Voir les bulletins acceptés"
let belenios_booth = "Isoloir Belenios"
let here = "ici"
let question_header = "Question %d/%d — sélectionnez entre %d et %d réponse(s)"
let at_least = "Vous devez sélectionner au moins %d réponse(s)"
let at_most = "Vous devez sélectionner au plus %d réponse(s)"
let previous = "Précédent"
let next = "Suivant"
let nothing = "(rien)"
let enter_cred = "Veuillez entrer votre code de vote :"
let invalid_cred = "Code de vote invalide !"
let input_credential = "Saisie du code de vote"
let answer_to_questions = "Réponse aux questions"
let review_and_encrypt = "Récapitulatif et chiffrement"
let authenticate = "Authentification"
let confirm = "Confirmation"
let done_ = "Terminé"
let booth_step1 = "Étape 1/6 : Saisie du code de vote"
let booth_step2 = "Étape 2/6 : Réponse aux questions"
let booth_step3 = "Étape 3/6 : Récapitulatif et chiffrement"
let booth_step5 = "Étape 5/6 : Confirmation"
let booth_step6 = "Étape 6/6 : "
let input_your_credential = "Saisissez votre code de vote "
let wait_while_encrypted = "Veuillez patienter, le chiffrement de votre bulletin est en cours..."
let encrypting = "Chiffrement en cours..."
let restart = "Recommencer"
let successfully_encrypted = "Votre bulletin a été chiffré avec succès, "
let not_cast_yet = "mais n'a pas encore été déposé dans l'urne"
let qmark = " !"
let your_tracker_is = "Votre numéro de suivi est "
let we_invite_you_to_save_it = "Nous vous invitons à le sauvegarder afin de vérifier ultérieurement que votre vote est bien pris en compte"
let continue = "Continuer"
let election_uuid = "UUID de l'élection : "
let election_fingerprint = "Empreinte de l'élection : "
let i_am = "Je suis "
let and_ = " et "
let i_cast_my_vote = "je dépose mon bulletin dans l'urne"
let please_login_to_confirm = "Veuillez vous connecter pour confirmer votre vote"
let your_ballot_for = "Votre bulletin pour "
let has_been_received = " a été reçu, mais pas encore pris en compte. "
let nobody_can_see = "Note: votre bulletin est chiffré et personne ne peut voir son contenu."
let go_back_to_election = "Retourner à la page d'accueil de l'élection"
let has_been_accepted = " a été accepté."
let you_can_check_its_presence = "Vous pouvez vérifier sa présence dans l'"
let ballot_box = "urne"
let anytime_during_the_election = " à tout moment pendant l'élection."
let confirmation_email = " Un e-mail de confirmation vous a été envoyé."
let thank_you_for_voting = "Merci pour votre participation !"
let is_rejected_because = " est refusé, parce que "
let fail = "ÉCHEC !"
let administer_elections = "Administrer des élections"
let administer_this_election = "Administrer cette élection"
let powered_by = "Propulsé par "
let get_the_source_code = "Obtenir le code source"
let audit_data = "Données d'audit : "
let parameters = "paramètres"
let public_credentials = "clés de vérification"
let trustee_public_keys = "clés publiques"
let ballots = "bulletins"
let election_server = "Serveur d'élections"
let accepted_ballots = "Bulletins acceptés"
let ballots_have_been_accepted_so_far = " bulletin(s) ont été accepté(s) jusqu'à présent."
let ballots_have_been_accepted = " bulletin(s) ont été accepté(s)."
let ballots_have_been_accepted_and = " bulletin(s) ont été accepté(s), et "
let have_been_tallied = " ont été compté(s)."
let username = "Nom d'utilisateur :"
let password = "Mot de passe :"
let login = "Se connecter"
let password_login = "Connexion par mot de passe"
let you_must_accept_cookies = "Pour utiliser ce site, vous devez accepter les cookies. "
let accept = "Accepter"
let not_yet_open = "Désolé, cette élection n'est pas encore ouverte"
let come_back_later = "Cette élection n'existe pas encore. Veuillez revenir plus tard."
let cookies_are_blocked = "Les cookies sont bloqués"
let please_enable_them = "Votre navigateur semble bloquer les cookies. Veuillez les activer."
let election_currently_closed = "Cette élection est actuellement fermée."
let election_closed_being_tallied = "L'élection est fermée et en cours de dépouillement."
let the = " L'empreinte du "
let encrypted_tally = "résultat chiffré"
let hash_is = " est "
let election_has_been_tallied = "Cette élection a été dépouillée."
let election_archived = "Cette élection est archivée."
let number_accepted_ballots = "Nombre de bulletins acceptés : "
let you_can_also_download = "Vous pouvez également télécharger le "
let result_with_crypto_proofs = "résultat avec les preuves cryptographiques"
let blank_vote = "Vote blanc"
let no_other_blank = "Vous ne pouvez pas sélectionner d'autres choix lors d'un vote blanc"
let mail_password_subject : ('a, 'b, 'c, 'd, 'e, 'f) format6 =
"Votre mot de passe pour l'élection %s"
let mail_password : ('a, 'b, 'c, 'd, 'e, 'f) format6 =
"Vous êtes enregistré(e) en tant qu'électeur(trice) pour l'élection
%s
Veuillez trouver ci-dessous votre nom d'utilisateur et votre mot de
passe. Pour soumettre un bulletin, vous aurez également besoin d'un
code de vote, envoyé dans un e-mail séparé. Soyez attentif(ve), le mot
de passe et le code de vote se ressemblent mais jouent des rôles
différents. Le système vous demandera votre code de vote dès l'entrée
dans l'isoloir virtuel. Le nom d'utilisateur et le mot de passe sont
nécessaires lorsque votre bulletin est prêt à être soumis.
Nom d'utilisateur : %s
Mot de passe : %s
Page de l'élection : %s
Notez que vous pouvez voter plusieurs fois. Seul le dernier vote est
pris en compte."
let mail_credential_subject : ('a, 'b, 'c, 'd, 'e, 'f) format6 =
"Votre code de vote pour l'élection %s"
let mail_credential : ('a, 'b, 'c, 'd, 'e, 'f) format6 =
"Vous êtes enregistré(e) en tant qu'électeur(trice) pour l'élection
%s
Veuillez trouver ci-dessous votre nom d'utilisateur et votre code de
vote. Pour soumettre un bulletin, vous aurez également besoin d'un
mot de passe, envoyé dans un e-mail séparé. Soyez attentif(ve), le mot
de passe et le code de vote se ressemblent mais jouent des rôles
différents. Le système vous demandera votre code de vote dès l'entrée
dans l'isoloir virtuel. Le nom d'utilisateur et le mot de passe sont
nécessaires lorsque votre bulletin est prêt à être soumis.
Nom d'utilisateur : %s
Code de vote : %s
Page de l'élection : %s
Notez que vous pouvez voter plusieurs fois. Seul le dernier vote est
pris en compte."
let mail_confirmation_subject : ('a, 'b, 'c, 'd, 'e, 'f) format6 =
"Votre vote pour l'élection %s"
let mail_confirmation : ('a, 'b, 'c, 'd, 'e, 'f) format6 =
"%s,
Votre vote pour l'élection
%s
a été enregistré. Votre numéro de suivi est
%s
Vous pouvez vérifier sa présence dans l'urne, accessible au
%s
Les résultats seront publiés sur la page de l'élection
%s
-- \nBelenios"
belenios-1.4+dfsg/src/web/web_l10n_fr.mli000066400000000000000000000000461307140314400202370ustar00rootroot00000000000000include Web_i18n_sig.LocalizedStrings
belenios-1.4+dfsg/src/web/web_l10n_it.ml000066400000000000000000000203401307140314400200720ustar00rootroot00000000000000(**************************************************************************)
(* BELENIOS *)
(* *)
(* Copyright © 2012-2017 Inria *)
(* *)
(* 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. *)
(* *)
(* You should have received a copy of the GNU Affero General Public *)
(* License along with this program. If not, see *)
(* . *)
(**************************************************************************)
let lang = "it"
let start = "Cominciare"
let advanced_mode = "Modo avanzato"
let see_accepted_ballots = "Consultare le schede elettorali accettate"
let belenios_booth = "Cabina elettorale Belenios"
let here = "qui"
let question_header = "Domanda %d/%d — selezionare tra %d e %d risposta(e)"
let at_least = "Lei deve selezionare almeno %d risposta(e)"
let at_most = "Lei deve selezionare al massimo %d risposta(e)"
let previous = "Precedente"
let next = "Seguente"
let nothing = "(niente)"
let enter_cred = "Si prega di inserire il codice di voto :"
let invalid_cred = "Codice di voto non valido !"
let input_credential = "Inserire il codice di voto"
let answer_to_questions = "Risposta alle domande"
let review_and_encrypt = "Riepilogo e cifratura"
let authenticate = "Autenticazione"
let confirm = "Conferma"
let done_ = "Finito"
let booth_step1 = "Fase 1/6 : Inserire il codice di voto"
let booth_step2 = "Fase 2/6 : Risposta alle domande"
let booth_step3 = "Fase 3/6 : Riepilogo e cifratura"
let booth_step5 = "Fase 5/6 : Conferma"
let booth_step6 = "Fase 6/6 : "
let input_your_credential = "Inserisca il suo codice di voto "
let wait_while_encrypted = "Si prega di pazientare, la cifratura della sua scheda elettorale è in corso..."
let encrypting = "Cifratura in corso..."
let restart = "Ricominciare"
let successfully_encrypted = "La sua scheda elettorale è stata cifrata con successo, "
let not_cast_yet = "ma non è stata ancora depositata nell'urna"
let qmark = " !"
let your_tracker_is = "Il suo codice di verifica è "
let we_invite_you_to_save_it = "La preghiamo di registrarlo per potere verificare ulteriormente se il suo voto è stato preso in considerazione."
let continue = "Proseguire"
let election_uuid = "UUID dell'elezione : "
let election_fingerprint = "Impronta dell'elezione : "
let i_am = "Sono "
let and_ = " e "
let i_cast_my_vote = "depongo la mia scheda elettorale nell'urna"
let please_login_to_confirm = "La preghiamo di connettersi per confermare il suo voto"
let your_ballot_for = "La sua scheda elettorale per "
let has_been_received = " è stata ricevuta, ma non è ancora presa in considerazione. "
let nobody_can_see = "Nota: la sua scheda è cifrata e nessuno può consultarla."
let go_back_to_election = "Tornare alla pagina iniziale dell'elezione"
let has_been_accepted = " è stata accettata."
let you_can_check_its_presence = "È possibile verificare la sua presenza nell'"
let ballot_box = "urna"
let anytime_during_the_election = " ad ogni momento durante l'elezione."
let confirmation_email = " Le è stata spedita una email di conferma."
let thank_you_for_voting = "La ringraziamo per la sua partecipazione !"
let is_rejected_because = "è rifiutato, perché"
let fail = "FALLIMENTO !"
let administer_elections = "Amministrare delle elezioni"
let administer_this_election = "Amministrare questa elezione"
let powered_by = "Utilizza "
let get_the_source_code = "Ottenere il codice sorgente"
let audit_data = "Dati d'audit : "
let parameters = "parametri"
let public_credentials = "chiavi di verificazione"
let trustee_public_keys = "chiavi pubbliche"
let ballots = "schede elettorali"
let election_server = "Server delle elezioni"
let accepted_ballots = "Schede elettorali accettate"
let ballots_have_been_accepted_so_far = " scheda(e) accettata(e) finora."
let ballots_have_been_accepted = " scheda(e) accettata(e)."
let ballots_have_been_accepted_and = " scheda(e) accettata(e), e"
let have_been_tallied = " sono state contate"
let username = "Nome utente :"
let password = "Password :"
let login = "Connettersi"
let password_login = "Connessione tramite la password"
let you_must_accept_cookies = "Per navigare sul sito, è necessario attivare i cookies. "
let accept = "Accettare"
let not_yet_open = "Spiacente, quest'elezione non è ancora aperta"
let come_back_later = "Quest'elezione ancora non esiste. La preghiamo di consultare ulteriormente."
let cookies_are_blocked = "I cookies sono bloccati"
let please_enable_them = "Il suo browser non accetta i cookies. Si prega di attivarli."
let election_currently_closed = "Questa elezione è chiusa per ora."
let election_closed_being_tallied = "L'elezione è chiusa ed il conteggio è in corso."
let the = " L'impronta del "
let encrypted_tally = "risultato cifrato"
let hash_is = " è "
let election_has_been_tallied = "Questa elezione è stata conteggiata."
let election_archived = "Questa elezione è archiviata."
let number_accepted_ballots = "Numero di schede accettate : "
let you_can_also_download = "È possibile scaricare il "
let result_with_crypto_proofs = "risultato con le prove crittografiche"
let blank_vote = "Scheda bianca"
let no_other_blank = "Nessun'altra scelta è autorizzata quando la scheda è bianca"
let mail_password_subject : ('a, 'b, 'c, 'd, 'e, 'f) format6 =
"La sua password per l'elezione %s"
let mail_password : ('a, 'b, 'c, 'd, 'e, 'f) format6 =
"Lei è registrato(a) in quanto elettore(trice) per l'elezione
%s
Si prega di trovare qui sotto il suo nome di utente e la sua password.
Per presentare una scheda elettorale, avrà bisogno di un codice di
voto, spedito in una email separata. Faccia attenzione, la password e
il codice di voto sono simili ma hanno un ruolo diverso. Il sistema le
domanderà il suo codice di voto non appena entrato(a) nella cabina
elettorale virtuale. Il nome di utente e la password sono necessari
quando la sua scheda è pronta per essere presentata.
Nome utente : %s
Password : %s
Pagina dell'elezione : %s
Si nota che lei può votare più volte. Ma soltanto l'ultimo voto è
preso in considerazione."
let mail_credential_subject : ('a, 'b, 'c, 'd, 'e, 'f) format6 =
"Il suo codice di voto per l'elezione %s"
let mail_credential : ('a, 'b, 'c, 'd, 'e, 'f) format6 =
"Lei è registrato(a) in quanto elettore(trice) per l'elezione
%s
Si prega di trovare qui sotto il suo nome di utente e il suo codice di
voto. Per presentare una scheda elettorale, avrà bisogno di una
password, spedita in una email separata. Faccia attenzione, la
password e il codice di voto sono simili ma hanno un ruolo diverso. Il
sistema le domanderà il suo codice di voto non appena entrato(a) nella
cabina elettorale virtuale. Il nome di utente e la password sono
necessari quando la sua scheda è pronta per essere presentata.
Nome utente : %s
Codice di voto : %s
Pagina dell'elezione : %s
Si nota che lei può votare più volte. Ma soltanto l'ultimo voto è
preso in considerazione."
let mail_confirmation_subject : ('a, 'b, 'c, 'd, 'e, 'f) format6 =
"La sua scheda per l'elezione %s"
let mail_confirmation : ('a, 'b, 'c, 'd, 'e, 'f) format6 =
"%s,
La sua scheda per l'elezione
%s
è stata registrata. Il suo codice di verifica è
%s
Può verificare la sua presenza nell'urna, accessibile su
%s
I risultati saranno pubblicati sulla pagina dell'elezione
%s
-- \nBelenios"
belenios-1.4+dfsg/src/web/web_l10n_it.mli000066400000000000000000000000461307140314400202440ustar00rootroot00000000000000include Web_i18n_sig.LocalizedStrings
belenios-1.4+dfsg/src/web/web_l10n_ro.ml000066400000000000000000000202251307140314400201000ustar00rootroot00000000000000(**************************************************************************)
(* BELENIOS *)
(* *)
(* Copyright © 2012-2016 Inria *)
(* *)
(* 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. *)
(* *)
(* You should have received a copy of the GNU Affero General Public *)
(* License along with this program. If not, see *)
(* . *)
(**************************************************************************)
let lang = "ro"
let start = "Start"
let advanced_mode = "Mod avansat"
let see_accepted_ballots = "Consultă buletinele de vot acceptate"
let belenios_booth = "Cabina de vot Belenios"
let here = "aici"
let question_header = "Întrebarea #%d din %d — selectați între %d și %d rspuns(uri)"
let at_least = "Trebuie să selectați cel puțin %d răspuns(uri)"
let at_most = "Trebuie să selectați cel mult %d răspuns(uri)"
let previous = "Precedent"
let next = "Următor"
let nothing = "(nimic)"
let enter_cred = "Vă rugăm să introduceți codul de votare:"
let invalid_cred = "Cod de votare invalid !"
let input_credential = "Introdu codul de votare"
let answer_to_questions = "Răspunde la întrebări"
let review_and_encrypt = "Rezumat și criptare"
let authenticate = "Autentificare"
let confirm = "Confirmare"
let done_ = "Terminare"
let booth_step1 = "Pas 1/6: Introduceți codul de votare"
let booth_step2 = "Pas 2/6: Răspunde la întrebări"
let booth_step3 = "Pas 3/6: Rezumat și criptare"
let booth_step5 = "Pas 5/6: Confirmare"
let booth_step6 = "Pas 6/6: "
let input_your_credential = "Introduceți codul vostru de votare "
let wait_while_encrypted = "Vă rugăm să așteptați criptarea buletinului vostru de vot..."
let encrypting = "Criptare în curs..."
let restart = "Reîncepe"
let successfully_encrypted = "Buletinul vostru de vot a fost criptat cu succes, "
let not_cast_yet = "dar încă nu a fost depus în urna de vot"
let qmark = "!"
let your_tracker_is = "Numărul vostru de identificare este "
let we_invite_you_to_save_it = "Vă invităm să-l salvați pentru a verifica ulterior că votul vostru este luat în considerare."
let continue = "Continuă"
let election_uuid = "Codul UUID al alegerii: "
let election_fingerprint = "Amprenta digitală al alegerii: "
let i_am = "Eu sunt "
let and_ = " și "
let i_cast_my_vote = "Am depus votul meu"
let please_login_to_confirm = "Vă rugăm să vă logați pentru a confirma votul vostru."
let your_ballot_for = "Buletinul de vot pentru "
let has_been_received = " a fost primit, dar nu a fost încă înregistrat. "
let nobody_can_see = "Notă: buletinul de vot este criptat și nimeni nu-i poate vedea conținutul."
let go_back_to_election = "Întoarcete la pagina de start a alegerii"
let has_been_accepted = " a fost acceptat."
let you_can_check_its_presence = "Puteți verifica prezența în "
let ballot_box = "urna de vot"
let anytime_during_the_election = " în orice moment al alegerii."
let confirmation_email = " Un e-mail de confirmare v-a fost trimis."
let thank_you_for_voting = "Vă mulțumim pentru participare!"
let is_rejected_because = " este resprins, deoarece "
let fail = "EȘEC!"
let administer_elections = "Administrează alegerile"
let administer_this_election = "Administrează această alegere"
let powered_by = "Realizat de "
let get_the_source_code = "Obține codul sursă"
let audit_data = "Date de audit: "
let parameters = "parametrii"
let public_credentials = "chei de verificare"
let trustee_public_keys = "cheia publică"
let ballots = "buletine de vot"
let election_server = "Server al alegerii"
let accepted_ballots = "Buletinele de vot acceptate"
let ballots_have_been_accepted_so_far = " buletin(e) de vot au fost acceptat(e) până în prezent."
let ballots_have_been_accepted = " buletin(e) de vot au fost acceptat(e)."
let ballots_have_been_accepted_and = " buletin(e) de vot au fost acceptat(e), și "
let have_been_tallied = " au fost contorizate."
let username = "Nume utilizator:"
let password = "Parola:"
let login = "Conectare"
let password_login = "Conectare folosind parola"
let you_must_accept_cookies = "Pentru a utiliza acest site, trebuie să acceptați cookie-uri. "
let accept = "Accept"
let not_yet_open = "Din păcate, această alegere nu este încă deschisă"
let come_back_later = "Acesta alegere nu există încă. Vă rugăm să reveniți mai târziu."
let cookies_are_blocked = "Cookie-urile sunt blocate"
let please_enable_them = "Browser-ul dumneavoastră pare să blocheze cookie-urile. Vă rugăm să le activați."
let election_currently_closed = "Această alegere este în prezent închisă."
let election_closed_being_tallied = "Această alegere este închisă și în curs de contorizare."
let the = " Amprenta "
let encrypted_tally = "rezultatului criptat"
let hash_is = " este "
let election_has_been_tallied = "Această alegere a fost contorizata."
let election_archived = "Această alegere este arhivată."
let number_accepted_ballots = "Numărul buletinelor de vot acceptate: "
let you_can_also_download = "De asemenea, puteți descărca "
let result_with_crypto_proofs = "rezultat cu dovada criptografică"
let blank_vote = "Vot alb"
let no_other_blank = "Nu puteți selecta o altă opțiune la un vot alb"
let mail_password_subject : ('a, 'b, 'c, 'd, 'e, 'f) format6 =
"Parola voastră pentru alegere %s"
let mail_password : ('a, 'b, 'c, 'd, 'e, 'f) format6 =
"Sunteți înregistrat(ă) ca votant(ă) pentru alegerea
%s
Mai jos veți găsi numele de utilizator și parola. Pentru a
depune votul vostru, vă trebuie un cod de votare, ce va fi
trimis într-un e-mail separat. Aveți grijă, parola și codul
de votare arată similare, dar joacă roluri diferite. Sistemul
va solicita codul de votare la intrarea în cabina de vot.
Numele de utilizator și parola sunt necesare atunci când
buletinul de vot este gata pentru depunere.
Nume utilizator: %s
Parola: %s
Pagina alegerii: %s
Rețineți că este posibil să votați de mai multe ori.
Numai ultimul vot va fi luat în considerare."
let mail_credential_subject : ('a, 'b, 'c, 'd, 'e, 'f) format6 =
"Codul vostru de votare pentru alegere %s"
let mail_credential : ('a, 'b, 'c, 'd, 'e, 'f) format6 =
"Sunteți înregistrat(ă) ca votant(ă) pentru alegerea
%s
Mai jos veți găsi numele de utilizator și codul de votare.
Pentru a depune votul vostru, vă trebuie o parolă, ce va fi
trimisă într-un e-mail separat. Aveți grijă, parola și codul
de votare arată similare, dar joacă roluri diferite. Sistemul
va solicita codul de votare la intrarea în cabina de vot.
Numele de utilizator și parola sunt necesare atunci când
buletinul de vot este gata pentru depunere.
Nume utilizator: %s
Cod de votare: %s
Pagina alegerii: %s
Rețineți că este posibil să votați de mai multe ori.
Numai ultimul vot va fi luat în considerare."
let mail_confirmation_subject : ('a, 'b, 'c, 'd, 'e, 'f) format6 =
"Votul vostru pentru alegerea %s"
let mail_confirmation : ('a, 'b, 'c, 'd, 'e, 'f) format6 =
"%s,
Votul vostru pentru alegerea
%s
a fost înregistrat. Numărul vostru de identificare este
%s
Puteți verifica prezența acestuia în urma de vot, accesibilă la
%s
Rezultatele vor fi publicate pe pagina de alegere
%s
-- \nBelenios"
belenios-1.4+dfsg/src/web/web_l10n_ro.mli000066400000000000000000000000461307140314400202500ustar00rootroot00000000000000include Web_i18n_sig.LocalizedStrings
belenios-1.4+dfsg/src/web/web_main.ml000066400000000000000000000070371307140314400175600ustar00rootroot00000000000000(**************************************************************************)
(* BELENIOS *)
(* *)
(* Copyright © 2012-2016 Inria *)
(* *)
(* 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. *)
(* *)
(* You should have received a copy of the GNU Affero General Public *)
(* License along with this program. If not, see *)
(* . *)
(**************************************************************************)
open Lwt
open Common
open Web_serializable_j
open Web_common
(** Global initialization *)
(* FIXME: the following should be in configuration file... but
doesn't work *)
let () = Ocsigen_config.set_maxrequestbodysizeinmemory 128000
let () = CalendarLib.Time_Zone.(change Local)
(** Parse configuration from *)
let spool_dir = ref None
let source_file = ref None
let auth_instances = ref []
let () =
Eliom_config.get_config () |>
let open Simplexmlparser in
List.iter @@ function
| PCData x ->
Ocsigen_extensions.Configuration.ignore_blank_pcdata ~in_tag:"belenios" x
| Element ("log", ["file", file], []) ->
Lwt_main.run (open_security_log file)
| Element ("source", ["file", file], []) ->
source_file := Some file
| Element ("maxmailsatonce", ["value", limit], []) ->
Web_site.maxmailsatonce := int_of_string limit
| Element ("spool", ["dir", dir], []) ->
spool_dir := Some dir
| Element ("rewrite-prefix", ["src", src; "dst", dst], []) ->
set_rewrite_prefix ~src ~dst
| Element ("auth", ["name", auth_instance],
[Element (auth_system, auth_config, [])]) ->
let i = {auth_system; auth_instance; auth_config} in
auth_instances := i :: !auth_instances
| Element (tag, _, _) ->
Printf.ksprintf failwith
"invalid configuration for tag %s in belenios"
tag
(** Parse configuration from other sources *)
let file_exists x =
try%lwt
Lwt_unix.(access x [R_OK]) >>
return true
with _ ->
return false
let%lwt source_file =
match !source_file with
| Some f ->
let%lwt b = file_exists f in
if b then (
return f
) else (
Printf.ksprintf failwith "file %s does not exist" f
)
| None -> failwith "missing in configuration"
let spool_dir =
match !spool_dir with
| Some d -> d
| None -> failwith "missing in configuration"
(** Build up the site *)
let () = Web_site.source_file := source_file
let () = Web_common.spool_dir := spool_dir
let () = Web_auth.configure (List.rev !auth_instances)
belenios-1.4+dfsg/src/web/web_main.mli000066400000000000000000000000261307140314400177200ustar00rootroot00000000000000(* empty interface *)
belenios-1.4+dfsg/src/web/web_persist.ml000066400000000000000000000145131307140314400203220ustar00rootroot00000000000000(**************************************************************************)
(* BELENIOS *)
(* *)
(* Copyright © 2012-2016 Inria *)
(* *)
(* 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. *)
(* *)
(* You should have received a copy of the GNU Affero General Public *)
(* License along with this program. If not, see *)
(* . *)
(**************************************************************************)
open Lwt
open Platform
open Serializable_j
open Common
open Web_serializable_j
open Web_common
let ( / ) = Filename.concat
let get_election_result uuid =
try%lwt
Lwt_io.chars_of_file (!spool_dir / uuid / "result.json") |>
Lwt_stream.to_string >>= fun x ->
return @@ Some (result_of_string (Yojson.Safe.from_lexbuf ~stream:true) x)
with _ -> return_none
type election_state =
[ `Open
| `Closed
| `EncryptedTally of int * int * string
| `Tallied of plaintext
| `Archived
]
let election_states = Ocsipersist.open_table "election_states"
let get_election_state x =
try%lwt Ocsipersist.find election_states x
with Not_found -> return `Archived
let set_election_state x s =
Ocsipersist.add election_states x s
let past = datetime_of_string "\"2015-10-01 00:00:00.000000\""
let set_election_date uuid d =
let dates = { e_finalization = d } in
Lwt_io.(with_file Output (!spool_dir / uuid / "dates.json") (fun oc ->
write_line oc (string_of_election_dates dates)
))
let get_election_date uuid =
try%lwt
Lwt_io.chars_of_file (!spool_dir / uuid / "dates.json") |>
Lwt_stream.to_string >>= fun x ->
let dates = election_dates_of_string x in
return dates.e_finalization
with _ ->
return past
let election_pds = Ocsipersist.open_table "election_pds"
let get_partial_decryptions x =
try%lwt Ocsipersist.find election_pds x
with Not_found -> return []
let set_partial_decryptions x pds =
Ocsipersist.add election_pds x pds
let auth_configs = Ocsipersist.open_table "auth_configs"
let get_auth_config x =
try%lwt Ocsipersist.find auth_configs x
with Not_found -> return []
let set_auth_config x c =
Ocsipersist.add auth_configs x c
let get_raw_election uuid =
try%lwt
let lines = Lwt_io.lines_of_file (!spool_dir / uuid / "election.json") in
begin match%lwt Lwt_stream.to_list lines with
| x :: _ -> return @@ Some x
| [] -> return_none
end
with _ -> return_none
let empty_metadata = {
e_owner = None;
e_auth_config = None;
e_cred_authority = None;
e_trustees = None;
e_languages = None;
}
let return_empty_metadata = return empty_metadata
let get_election_metadata uuid =
try%lwt
Lwt_io.chars_of_file (!spool_dir / uuid / "metadata.json") |>
Lwt_stream.to_string >>= fun x ->
return @@ metadata_of_string x
with _ -> return_empty_metadata
let get_elections_by_owner user =
Lwt_unix.files_of_directory !spool_dir |>
Lwt_stream.filter_s (fun x ->
if x = "." || x = ".." then return false else
let%lwt metadata = get_election_metadata x in
match metadata.e_owner with
| Some o -> return (o = user)
| None -> return false
) |> Lwt_stream.to_list
let get_voters uuid =
try%lwt
let lines = Lwt_io.lines_of_file (!spool_dir / uuid / "voters.txt") in
let%lwt lines = Lwt_stream.to_list lines in
return @@ Some lines
with _ -> return_none
let get_passwords uuid =
let csv =
try Some (Csv.load (!spool_dir / uuid / "passwords.csv"))
with _ -> None
in
match csv with
| None -> return_none
| Some csv ->
let res = List.fold_left (fun accu line ->
match line with
| [login; salt; hash] ->
SMap.add login (salt, hash) accu
| _ -> accu
) SMap.empty csv in
return @@ Some res
let get_public_keys uuid =
try%lwt
let lines = Lwt_io.lines_of_file (!spool_dir / uuid / "public_keys.jsons") in
let%lwt lines = Lwt_stream.to_list lines in
return @@ Some lines
with _ -> return_none
module Ballots = Map.Make (String)
module BallotsCacheTypes = struct
type key = string
type value = string Ballots.t
end
module BallotsCache = Ocsigen_cache.Make (BallotsCacheTypes)
let raw_get_ballots_archived uuid =
try%lwt
let ballots = Lwt_io.lines_of_file (!spool_dir / uuid / "ballots.jsons") in
Lwt_stream.fold (fun b accu ->
let hash = sha256_b64 b in
Ballots.add hash b accu
) ballots Ballots.empty
with _ -> return Ballots.empty
let archived_ballots_cache =
new BallotsCache.cache raw_get_ballots_archived 10
let get_ballot_hashes ~uuid =
match%lwt get_election_state uuid with
| `Archived ->
let%lwt ballots = archived_ballots_cache#find uuid in
Ballots.bindings ballots |> List.map fst |> return
| _ ->
let table = Ocsipersist.open_table ("ballots_" ^ underscorize uuid) in
Ocsipersist.fold_step (fun hash _ accu ->
return (hash :: accu)
) table [] >>= (fun x -> return @@ List.rev x)
let get_ballot_by_hash ~uuid ~hash =
match%lwt get_election_state uuid with
| `Archived ->
let%lwt ballots = archived_ballots_cache#find uuid in
(try Some (Ballots.find hash ballots) with Not_found -> None) |> return
| _ ->
let table = Ocsipersist.open_table ("ballots_" ^ underscorize uuid) in
try%lwt Ocsipersist.find table hash >>= (fun x -> return @@ Some x)
with Not_found -> return_none
belenios-1.4+dfsg/src/web/web_persist.mli000066400000000000000000000053671307140314400205020ustar00rootroot00000000000000(**************************************************************************)
(* BELENIOS *)
(* *)
(* Copyright © 2012-2016 Inria *)
(* *)
(* 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. *)
(* *)
(* You should have received a copy of the GNU Affero General Public *)
(* License along with this program. If not, see *)
(* . *)
(**************************************************************************)
open Serializable_t
open Common
open Web_serializable_t
type election_state =
[ `Open
| `Closed
| `EncryptedTally of int * int * string
| `Tallied of plaintext
| `Archived
]
val get_election_state : string -> election_state Lwt.t
val set_election_state : string -> election_state -> unit Lwt.t
val get_election_date : string -> datetime Lwt.t
val set_election_date : string -> datetime -> unit Lwt.t
val get_partial_decryptions : string -> (int * string) list Lwt.t
val set_partial_decryptions : string -> (int * string) list -> unit Lwt.t
val get_auth_config : string -> (string * (string * string list)) list Lwt.t
val set_auth_config : string -> (string * (string * string list)) list -> unit Lwt.t
val get_raw_election : string -> string option Lwt.t
val get_election_metadata : string -> metadata Lwt.t
val get_election_result : string -> Yojson.Safe.json result option Lwt.t
val get_elections_by_owner : user -> string list Lwt.t
val get_voters : string -> string list option Lwt.t
val get_passwords : string -> (string * string) SMap.t option Lwt.t
val get_public_keys : string -> string list option Lwt.t
val get_ballot_hashes : uuid:string -> string list Lwt.t
val get_ballot_by_hash : uuid:string -> hash:string -> string option Lwt.t
belenios-1.4+dfsg/src/web/web_serializable.atd000066400000000000000000000067301307140314400214410ustar00rootroot00000000000000(**************************************************************************)
(* BELENIOS *)
(* *)
(* Copyright © 2012-2016 Inria *)
(* *)
(* 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. *)
(* *)
(* You should have received a copy of the GNU Affero General Public *)
(* License along with this program. If not, see *)
(* . *)
(**************************************************************************)
(** {1 Predefined types} *)
type uuid = abstract
type string_set = abstract
type datetime = abstract
type template = abstract
(** {1 Web-specific types} *)
type randomness = {
randomness : string;
}
type user = {
domain : string;
name : string;
}
type auth_config = {
auth_system : string;
auth_instance : string;
auth_config : (string * string) list;
}
type metadata = {
?owner: user option;
?auth_config: auth_config list option;
?cred_authority : string option;
?trustees : string list option;
?languages : string list option;
}
type election_dates = {
finalization : datetime;
}
(** {1 Types related to elections being prepared} *)
type setup_voter = {
id : string;
?password : (string * string) option;
}
type setup_trustee = {
id : string;
token : string;
public_key : string;
}
type setup_election = {
owner : user;
group : string;
voters : setup_voter list;
questions : template;
public_keys : setup_trustee list;
metadata : metadata;
public_creds : string;
public_creds_received : bool;
}
(** {1 OpenID Connect-related types} *)
type oidc_configuration = {
authorization_endpoint : string;
token_endpoint : string;
userinfo_endpoint : string;
}
type oidc_tokens = {
access_token : string;
token_type : string;
id_token : string;
}
type oidc_userinfo = {
sub : string;
?email : string option;
}
belenios-1.4+dfsg/src/web/web_serializable_builtin_j.ml000066400000000000000000000037131307140314400233360ustar00rootroot00000000000000(**************************************************************************)
(* BELENIOS *)
(* *)
(* Copyright © 2012-2016 Inria *)
(* *)
(* 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. *)
(* *)
(* You should have received a copy of the GNU Affero General Public *)
(* License along with this program. If not, see *)
(* . *)
(**************************************************************************)
open Web_serializable_builtin_t
(** {1 Serializers for type datetime} *)
let write_datetime buf n =
Bi_outbuf.add_char buf '"';
Bi_outbuf.add_string buf (raw_string_of_datetime n);
Bi_outbuf.add_char buf '"'
let datetime_of_json = function
| `String s -> raw_datetime_of_string s
| _ -> invalid_arg "datetime_of_json: a string was expected"
let read_datetime state buf =
datetime_of_json (Yojson.Safe.from_lexbuf ~stream:true state buf)
belenios-1.4+dfsg/src/web/web_serializable_builtin_j.mli000066400000000000000000000033171307140314400235070ustar00rootroot00000000000000(**************************************************************************)
(* BELENIOS *)
(* *)
(* Copyright © 2012-2016 Inria *)
(* *)
(* 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. *)
(* *)
(* You should have received a copy of the GNU Affero General Public *)
(* License along with this program. If not, see *)
(* . *)
(**************************************************************************)
open Web_serializable_builtin_t
(** {1 Serializers for type datetime} *)
val write_datetime : Bi_outbuf.t -> datetime -> unit
val read_datetime : Yojson.Safe.lexer_state -> Lexing.lexbuf -> datetime
belenios-1.4+dfsg/src/web/web_serializable_builtin_t.ml000066400000000000000000000050211307140314400233420ustar00rootroot00000000000000(**************************************************************************)
(* BELENIOS *)
(* *)
(* Copyright © 2012-2016 Inria *)
(* *)
(* 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. *)
(* *)
(* You should have received a copy of the GNU Affero General Public *)
(* License along with this program. If not, see *)
(* . *)
(**************************************************************************)
open CalendarLib
let datetime_format = "%Y-%m-%d %H:%M:%S"
type datetime = Fcalendar.Precise.t * string option
let now () = CalendarLib.Fcalendar.Precise.now (), None
let raw_string_of_datetime (n, s) =
match s with
| Some s -> s
| None ->
let n = Fcalendar.Precise.to_gmt n in
let a = Printer.Precise_Fcalendar.sprint datetime_format n in
let ts = Printf.sprintf "%.6f" (Fcalendar.Precise.to_unixfloat n) in
let i = String.index ts '.' in
let b = String.sub ts i (String.length ts - i) in
a ^ b
let raw_datetime_of_string s =
let i = String.index s '.' in
let l = Printer.Precise_Fcalendar.from_fstring datetime_format (String.sub s 0 i) in
let l = Fcalendar.Precise.from_gmt l in
let r = float_of_string ("0" ^ String.sub s i (String.length s-i)) in
(Fcalendar.Precise.add l (Fcalendar.Precise.Period.second r), Some s)
let datetime_compare (a, _) (b, _) =
CalendarLib.Fcalendar.Precise.compare a b
let format_datetime fmt (a, _) =
CalendarLib.Printer.Precise_Fcalendar.sprint fmt a
belenios-1.4+dfsg/src/web/web_serializable_builtin_t.mli000066400000000000000000000033651307140314400235240ustar00rootroot00000000000000(**************************************************************************)
(* BELENIOS *)
(* *)
(* Copyright © 2012-2016 Inria *)
(* *)
(* 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. *)
(* *)
(* You should have received a copy of the GNU Affero General Public *)
(* License along with this program. If not, see *)
(* . *)
(**************************************************************************)
type datetime
val now : unit -> datetime
val raw_string_of_datetime : datetime -> string
val raw_datetime_of_string : string -> datetime
val datetime_compare : datetime -> datetime -> int
val format_datetime : string -> datetime -> string
belenios-1.4+dfsg/src/web/web_services.ml000066400000000000000000000200301307140314400204430ustar00rootroot00000000000000(**************************************************************************)
(* BELENIOS *)
(* *)
(* Copyright © 2012-2016 Inria *)
(* *)
(* 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. *)
(* *)
(* You should have received a copy of the GNU Affero General Public *)
(* License along with this program. If not, see *)
(* . *)
(**************************************************************************)
open Eliom_service
open Eliom_service.Http
open Eliom_parameter
open Web_common
let home = service ~path:[""] ~get_params:unit ()
let admin = service ~path:["admin"] ~get_params:unit ()
let site_login = service ~path:["login"] ~get_params:(opt (string "service")) ()
let logout = service ~path:["logout"] ~get_params:unit ()
let source_code = service ~path:["belenios.tar.gz"] ~get_params:unit ()
let get_randomness = service ~path:["get-randomness"] ~get_params:unit ()
let tool = preapply (static_dir ()) ["static"; "belenios-tool.html"]
let election_setup_new = post_coservice ~csrf_safe:true ~fallback:admin ~post_params:(radio string "credmgmt" ** radio string "auth" ** string "cas_server") ()
let election_setup_pre = service ~path:["setup"; "new"] ~get_params:unit ()
let election_setup = service ~path:["setup"; "election"] ~get_params:(uuid "uuid") ()
let election_setup_questions = service ~path:["setup"; "questions"] ~get_params:(uuid "uuid") ()
let election_setup_questions_post = post_coservice ~fallback:election_setup_questions ~post_params:(string "questions") ()
let election_setup_description = post_coservice ~fallback:election_setup ~post_params:(string "name" ** string "description") ()
let election_setup_languages = post_coservice ~fallback:election_setup ~post_params:(string "languages") ()
let election_setup_voters = service ~path:["setup"; "voters"] ~get_params:(uuid "uuid") ()
let election_setup_voters_add = post_service ~fallback:election_setup_voters ~post_params:(string "voters") ()
let election_setup_voters_remove = post_coservice ~fallback:election_setup_voters ~post_params:(string "voter") ()
let election_setup_voters_passwd = post_coservice ~fallback:election_setup_voters ~post_params:(string "voter") ()
let election_setup_trustee_add = post_coservice ~fallback:election_setup ~post_params:(string "id") ()
let election_setup_trustee_del = post_coservice ~fallback:election_setup ~post_params:(int "index") ()
let election_setup_credential_authority = service ~path:["setup"; "credential-authority"] ~get_params:(uuid "uuid") ()
let election_setup_credentials = service ~path:["setup"; "credentials"] ~get_params:(string "token") ()
let election_setup_credentials_download = service ~path:["setup"; "public_creds.txt"] ~get_params:(string "token") ()
let election_setup_credentials_post = post_coservice ~fallback:election_setup_credentials ~post_params:(string "public_creds") ()
let election_setup_credentials_post_file = post_coservice ~fallback:election_setup_credentials ~post_params:(file "public_creds") ()
let election_setup_credentials_server = post_coservice ~fallback:election_setup ~post_params:unit ()
let election_setup_trustees = service ~path:["setup"; "trustees"] ~get_params:(uuid "uuid") ()
let election_setup_trustee = service ~path:["setup"; "trustee"] ~get_params:(string "token") ()
let election_setup_trustee_post = post_coservice ~fallback:election_setup_trustee ~post_params:(string "public_key") ()
let election_setup_confirm = service ~path:["setup"; "confirm"] ~get_params:(uuid "uuid") ()
let election_setup_create = post_coservice ~csrf_safe:true ~fallback:election_setup ~post_params:unit ()
let election_setup_auth_genpwd = post_coservice ~fallback:election_setup ~post_params:unit ()
let election_setup_import = service ~path:["setup"; "import"] ~get_params:(uuid "uuid") ()
let election_setup_import_post = post_coservice ~fallback:election_setup_import ~post_params:(uuid "from") ()
let election_setup_import_trustees = service ~path:["setup"; "import-trustees"] ~get_params:(uuid "uuid") ()
let election_setup_import_trustees_post = post_coservice ~fallback:election_setup_import_trustees ~post_params:(uuid "from") ()
let election_home = service ~path:["elections"] ~get_params:(suffix (uuid "uuid" ** suffix_const "")) ()
let set_cookie_disclaimer = coservice' ~get_params:unit ()
let election_admin = service ~path:["elections"] ~get_params:(suffix (uuid "uuid" ** suffix_const "admin")) ()
let election_regenpwd = service ~path:["elections"] ~get_params:(suffix (uuid "uuid" ** suffix_const "regenpwd")) ()
let election_regenpwd_post = post_coservice ~fallback:election_regenpwd ~post_params:(string "user") ()
let election_login = service ~path:["elections"] ~get_params:(suffix_prod (uuid "uuid" ** suffix_const "login") (opt (string "service"))) ()
let election_open = post_coservice ~fallback:election_admin ~post_params:unit ()
let election_close = post_coservice ~fallback:election_admin ~post_params:unit ()
let election_archive = post_coservice ~fallback:election_admin ~post_params:unit ()
let election_update_credential = service ~path:["elections"] ~get_params:(suffix (uuid "uuid" ** suffix_const "update-cred")) ()
let election_update_credential_post = post_service ~fallback:election_update_credential ~post_params:(string "old_credential" ** string "new_credential") ()
let election_vote = service ~path:["elections"] ~get_params:(suffix (uuid "uuid" ** suffix_const "vote")) ()
let election_cast = service ~path:["elections"] ~get_params:(suffix (uuid "uuid" ** suffix_const "cast")) ()
let election_cast_post = post_service ~fallback:election_cast ~post_params:(opt (string "encrypted_vote") ** opt (file "encrypted_vote_file")) ()
let election_cast_confirm = post_coservice ~csrf_safe:true ~fallback:election_cast ~post_params:unit ()
let election_pretty_ballots = service ~path:["elections"] ~get_params:(suffix (uuid "uuid" ** suffix_const "ballots")) ()
let election_pretty_ballot = service ~path:["elections"] ~get_params:(suffix_prod (uuid "uuid" ** suffix_const "ballot") (string "hash")) ()
let election_pretty_records = service ~path:["elections"] ~get_params:(suffix (uuid "uuid" ** suffix_const "pretty-records")) ()
let election_missing_voters = service ~path:["elections"] ~get_params:(suffix (uuid "uuid" ** suffix_const "missing")) ()
let election_compute_encrypted_tally = post_coservice ~csrf_safe:true ~fallback:election_admin ~post_params:unit ()
let election_tally_trustees = service ~path:["elections"] ~get_params:(suffix (uuid "uuid" ** suffix_const "trustees" ** int "trustee_id")) ()
let election_tally_trustees_post = post_service ~fallback:election_tally_trustees ~post_params:(string "partial_decryption") ()
let election_tally_release = post_service ~fallback:election_admin ~post_params:unit ()
let election_dir = service ~path:["elections"] ~get_params:(suffix (uuid "uuid" ** election_file "file")) ()
let dummy_post = post_coservice' ~post_params:(string "username") ()
let password_post = post_coservice' ~post_params:(string "username" ** string "password") ()
let set_language = coservice' ~get_params:(string "lang") ()
belenios-1.4+dfsg/src/web/web_signatures.mli000066400000000000000000000060351307140314400211660ustar00rootroot00000000000000(**************************************************************************)
(* BELENIOS *)
(* *)
(* Copyright © 2012-2016 Inria *)
(* *)
(* 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. *)
(* *)
(* You should have received a copy of the GNU Affero General Public *)
(* License along with this program. If not, see *)
(* . *)
(**************************************************************************)
open Signatures
open Web_serializable_t
module type AUTH_SERVICES = sig
val get_auth_systems : unit -> string list Lwt.t
val get_user : unit -> user option Lwt.t
end
module type AUTH_LINKS = sig
val login :
string option ->
(unit, unit,
[< Eliom_service.service_method > `Get ],
[< Eliom_service.attached > `Attached ],
[< Eliom_service.service_kind > `Service ],
[ `WithoutSuffix ], unit, unit,
[< Eliom_service.registrable > `Unregistrable ],
[> Eliom_service.http_service ])
Eliom_service.service
val logout :
(unit, unit,
[< Eliom_service.service_method > `Get ],
[< Eliom_service.attached > `Attached ],
[< Eliom_service.service_kind > `Service ],
[ `WithoutSuffix ], unit, unit,
[< Eliom_service.registrable > `Unregistrable ],
[> Eliom_service.http_service ])
Eliom_service.service
end
type content =
Eliom_registration.browser_content Eliom_registration.kind Lwt.t
module type WEB_BALLOT_BOX = sig
val cast : string -> user * datetime -> string Lwt.t
val inject_cred : string -> unit Lwt.t
val update_files : unit -> unit Lwt.t
val update_cred : old:string -> new_:string -> unit Lwt.t
val compute_encrypted_tally : unit -> (int * string * string) Lwt.t
(** Computes and writes to disk the encrypted tally. Returns the
number of ballots and the hash of the encrypted tally. *)
end
module type WEB_ELECTION = sig
module G : GROUP
module E : ELECTION with type elt = G.t and type 'a m = 'a Lwt.t
module B : WEB_BALLOT_BOX
end
belenios-1.4+dfsg/src/web/web_site.ml000066400000000000000000001550561307140314400176050ustar00rootroot00000000000000(**************************************************************************)
(* BELENIOS *)
(* *)
(* Copyright © 2012-2016 Inria *)
(* *)
(* 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. *)
(* *)
(* You should have received a copy of the GNU Affero General Public *)
(* License along with this program. If not, see *)
(* . *)
(**************************************************************************)
open Lwt
open Platform
open Serializable_j
open Signatures
open Common
open Web_serializable_builtin_t
open Web_serializable_j
open Web_common
open Web_services
let source_file = ref "belenios.tar.gz"
let maxmailsatonce = ref 1000
let ( / ) = Filename.concat
module PString = String
open Eliom_service
open Eliom_registration
module LwtRandom = MakeLwtRandom (struct let rng = make_rng () end)
(* Table with elections in setup mode. *)
let election_stable = Ocsipersist.open_table "site_setup"
(* Table with tokens given to trustees. *)
let election_pktokens = Ocsipersist.open_table "site_pktokens"
(* Table with tokens given to credential authorities. *)
let election_credtokens = Ocsipersist.open_table "site_credtokens"
module T = Web_templates
let raw_find_election uuid =
let%lwt raw_election = Web_persist.get_raw_election uuid in
match raw_election with
| Some raw_election ->
return (Group.election_params_of_string raw_election)
| _ -> Lwt.fail Not_found
module WCacheTypes = struct
type key = string
type value = (module ELECTION_DATA)
end
module WCache = Ocsigen_cache.Make (WCacheTypes)
let find_election =
let cache = new WCache.cache raw_find_election 100 in
fun x -> cache#find x
let get_setup_election uuid_s =
let%lwt se = Ocsipersist.find election_stable uuid_s in
return (setup_election_of_string se)
let set_setup_election uuid_s se =
Ocsipersist.add election_stable uuid_s (string_of_setup_election se)
let dump_passwords dir table =
Lwt_io.(with_file Output (dir / "passwords.csv") (fun oc ->
Ocsipersist.iter_step (fun voter (salt, hashed) ->
write_line oc (voter ^ "," ^ salt ^ "," ^ hashed)
) table
))
let finalize_election uuid se =
let uuid_s = Uuidm.to_string uuid in
(* voters *)
let () =
if se.se_voters = [] then failwith "no voters"
in
(* passwords *)
let () =
match se.se_metadata.e_auth_config with
| Some [{auth_system = "password"; _}] ->
if not @@ List.for_all (fun v -> v.sv_password <> None) se.se_voters then
failwith "some passwords are missing"
| _ -> ()
in
(* credentials *)
let () =
if not se.se_public_creds_received then
failwith "public credentials are missing"
in
(* trustees *)
let group = Group.of_string se.se_group in
let module G = (val group : GROUP) in
let module KG = Election.MakeSimpleDistKeyGen (G) (LwtRandom) in
let%lwt trustees, public_keys, private_key =
match se.se_public_keys with
| [] ->
let%lwt private_key, public_key = KG.generate_and_prove () in
return (None, [public_key], Some private_key)
| _ :: _ ->
return (
Some (List.map (fun {st_id; _} -> st_id) se.se_public_keys),
(List.map
(fun {st_public_key; _} ->
if st_public_key = "" then failwith "some public keys are missing";
trustee_public_key_of_string G.read st_public_key
) se.se_public_keys),
None)
in
let y = KG.combine (Array.of_list public_keys) in
(* election parameters *)
let metadata = { se.se_metadata with e_trustees = trustees } in
let template = se.se_questions in
let params = {
e_description = template.t_description;
e_name = template.t_name;
e_public_key = {wpk_group = G.group; wpk_y = y};
e_questions = template.t_questions;
e_uuid = uuid;
} in
let raw_election = string_of_params (write_wrapped_pubkey G.write_group G.write) params in
(* write election files to disk *)
let dir = !spool_dir / uuid_s in
let create_file fname what xs =
Lwt_io.with_file
~flags:(Unix.([O_WRONLY; O_NONBLOCK; O_CREAT; O_TRUNC]))
~perm:0o600 ~mode:Lwt_io.Output (dir / fname)
(fun oc ->
Lwt_list.iter_s
(fun v ->
Lwt_io.write oc (what v) >>
Lwt_io.write oc "\n") xs)
in
Lwt_unix.mkdir dir 0o700 >>
create_file "public_keys.jsons" (string_of_trustee_public_key G.write) public_keys >>
create_file "voters.txt" (fun x -> x.sv_id) se.se_voters >>
create_file "metadata.json" string_of_metadata [metadata] >>
create_file "election.json" (fun x -> x) [raw_election] >>
(* construct Web_election instance *)
let election = Group.election_params_of_string raw_election in
let module W = Web_election.Make ((val election)) (LwtRandom) in
(* set up authentication *)
let%lwt () =
match metadata.e_auth_config with
| None -> return ()
| Some xs ->
let auth_config =
List.map (fun {auth_system; auth_instance; auth_config} ->
auth_instance, (auth_system, List.map snd auth_config)
) xs
in
Web_persist.set_auth_config uuid_s auth_config
in
(* inject credentials *)
let%lwt () =
let fname = !spool_dir / uuid_s ^ ".public_creds.txt" in
Lwt_io.lines_of_file fname |>
Lwt_stream.iter_s W.B.inject_cred >>
W.B.update_files () >>
Lwt_unix.unlink fname
in
(* create file with private key, if any *)
let%lwt () =
match private_key with
| None -> return_unit
| Some x -> create_file "private_key.json" string_of_number [x]
in
(* clean up setup database *)
Ocsipersist.remove election_credtokens se.se_public_creds >>
Lwt_list.iter_s
(fun {st_token; _} ->
Ocsipersist.remove election_pktokens st_token)
se.se_public_keys >>
Ocsipersist.remove election_stable uuid_s >>
(* inject passwords *)
(match metadata.e_auth_config with
| Some [{auth_system = "password"; _}] ->
let table = "password_" ^ underscorize uuid_s in
let table = Ocsipersist.open_table table in
Lwt_list.iter_s
(fun v ->
let _, login = split_identity v.sv_id in
match v.sv_password with
| Some x -> Ocsipersist.add table login x
| None -> return_unit
) se.se_voters >>
dump_passwords (!spool_dir / uuid_s) table
| _ -> return_unit) >>
(* finish *)
Web_persist.set_election_state uuid_s `Open >>
Web_persist.set_election_date uuid_s (now ())
let cleanup_table ?uuid_s table =
let table = Ocsipersist.open_table table in
match uuid_s with
| None ->
let%lwt indexes = Ocsipersist.fold_step (fun k _ accu ->
return (k :: accu)) table []
in
Lwt_list.iter_s (Ocsipersist.remove table) indexes
| Some u -> Ocsipersist.remove table u
let cleanup_file f =
try%lwt Lwt_unix.unlink f
with _ -> return_unit
let archive_election uuid_s =
let uuid_u = underscorize uuid_s in
let%lwt () = cleanup_table ~uuid_s "election_states" in
let%lwt () = cleanup_table ~uuid_s "election_pds" in
let%lwt () = cleanup_table ~uuid_s "auth_configs" in
let%lwt () = cleanup_table ("password_" ^ uuid_u) in
let%lwt () = cleanup_table ("records_" ^ uuid_u) in
let%lwt () = cleanup_table ("creds_" ^ uuid_u) in
let%lwt () = cleanup_table ("ballots_" ^ uuid_u) in
let%lwt () = cleanup_file (!spool_dir / uuid_s / "private_key.json") in
return_unit
let () = Any.register ~service:home
(fun () () ->
Eliom_reference.unset Web_state.cont >>
Redirection.send admin
)
let get_finalized_elections_by_owner u =
let%lwt elections, tallied, archived =
Web_persist.get_elections_by_owner u >>=
Lwt_list.fold_left_s (fun accu uuid_s ->
let%lwt w = find_election uuid_s in
let%lwt state = Web_persist.get_election_state uuid_s in
let%lwt date = Web_persist.get_election_date uuid_s in
let elections, tallied, archived = accu in
match state with
| `Tallied _ -> return (elections, (date, w) :: tallied, archived)
| `Archived -> return (elections, tallied, (date, w) :: archived)
| _ -> return ((date, w) :: elections, tallied, archived)
) ([], [], [])
in
let sort l =
List.sort (fun (x, _) (y, _) -> datetime_compare x y) l |>
List.map (fun (_, x) -> x)
in
return (sort elections, sort tallied, sort archived)
let () = Html5.register ~service:admin
(fun () () ->
let cont () = Redirection.send admin in
Eliom_reference.set Web_state.cont [cont] >>
let%lwt site_user = Web_state.get_site_user () in
let%lwt elections =
match site_user with
| None -> return None
| Some u ->
let%lwt elections, tallied, archived = get_finalized_elections_by_owner u in
let%lwt setup_elections =
Ocsipersist.fold_step (fun k v accu ->
let v = setup_election_of_string v in
if v.se_owner = u
then return ((uuid_of_string k, v.se_questions.t_name) :: accu)
else return accu
) election_stable []
in
return @@ Some (elections, tallied, archived, setup_elections)
in
T.admin ~elections ()
)
let () = File.register
~service:source_code
~content_type:"application/x-gzip"
(fun () () -> return !source_file)
let do_get_randomness =
let prng = Lazy.from_fun (Lwt_preemptive.detach (fun () ->
pseudo_rng (random_string secure_rng 16)
)) in
let mutex = Lwt_mutex.create () in
fun () ->
Lwt_mutex.with_lock mutex (fun () ->
let%lwt prng = Lazy.force prng in
return (random_string prng 32)
)
let b64_encode_compact x =
Cryptokit.(transform_string (Base64.encode_compact ()) x)
let () = String.register
~service:get_randomness
(fun () () ->
let%lwt r = do_get_randomness () in
b64_encode_compact r |>
(fun x -> string_of_randomness { randomness=x }) |>
(fun x -> return (x, "application/json"))
)
let generate_uuid = Uuidm.v4_gen (Random.State.make_self_init ())
let create_new_election owner cred auth =
let e_cred_authority = match cred with
| `Automatic -> Some "server"
| `Manual -> None
in
let e_auth_config = match auth with
| `Password -> Some [{auth_system = "password"; auth_instance = "password"; auth_config = []}]
| `Dummy -> Some [{auth_system = "dummy"; auth_instance = "dummy"; auth_config = []}]
| `CAS server -> Some [{auth_system = "cas"; auth_instance = "cas"; auth_config = ["server", server]}]
in
let uuid = generate_uuid () in
let uuid_s = Uuidm.to_string uuid in
let%lwt token = generate_token () in
let se_metadata = {
e_owner = Some owner;
e_auth_config;
e_cred_authority;
e_trustees = None;
e_languages = Some ["en"; "fr"];
} in
let question = {
q_answers = [| "Answer 1"; "Answer 2"; "Blank" |];
q_blank = None;
q_min = 1;
q_max = 1;
q_question = "Question 1?";
} in
let se_questions = {
t_description = "Description of the election.";
t_name = "Name of the election";
t_questions = [| question |];
} in
let se = {
se_owner = owner;
se_group = "{\"g\":\"2402352677501852209227687703532399932712287657378364916510075318787663274146353219320285676155269678799694668298749389095083896573425601900601068477164491735474137283104610458681314511781646755400527402889846139864532661215055797097162016168270312886432456663834863635782106154918419982534315189740658186868651151358576410138882215396016043228843603930989333662772848406593138406010231675095763777982665103606822406635076697764025346253773085133173495194248967754052573659049492477631475991575198775177711481490920456600205478127054728238140972518639858334115700568353695553423781475582491896050296680037745308460627\",\"p\":\"20694785691422546401013643657505008064922989295751104097100884787057374219242717401922237254497684338129066633138078958404960054389636289796393038773905722803605973749427671376777618898589872735865049081167099310535867780980030790491654063777173764198678527273474476341835600035698305193144284561701911000786737307333564123971732897913240474578834468260652327974647951137672658693582180046317922073668860052627186363386088796882120769432366149491002923444346373222145884100586421050242120365433561201320481118852408731077014151666200162313177169372189248078507711827842317498073276598828825169183103125680162072880719\",\"q\":\"78571733251071885079927659812671450121821421258408794611510081919805623223441\"}"; (* generated by fips.sage *)
se_voters = [];
se_questions;
se_public_keys = [];
se_metadata;
se_public_creds = token;
se_public_creds_received = false;
} in
let%lwt () = set_setup_election uuid_s se in
let%lwt () = Ocsipersist.add election_credtokens token uuid_s in
return (preapply election_setup uuid)
let () = Html5.register ~service:election_setup_pre
(fun () () -> T.election_setup_pre ())
let () = Redirection.register ~service:election_setup_new
(fun () (credmgmt, (auth, cas_server)) ->
match%lwt Web_state.get_site_user () with
| Some u ->
let%lwt credmgmt = match credmgmt with
| Some "auto" -> return `Automatic
| Some "manual" -> return `Manual
| _ -> fail_http 400
in
let%lwt auth = match auth with
| Some "password" -> return `Password
| Some "dummy" -> return `Dummy
| Some "cas" -> return @@ `CAS cas_server
| _ -> fail_http 400
in
create_new_election u credmgmt auth
| None -> forbidden ())
let generic_setup_page f uuid () =
match%lwt Web_state.get_site_user () with
| Some u ->
let uuid_s = Uuidm.to_string uuid in
let%lwt se = get_setup_election uuid_s in
if se.se_owner = u
then f uuid se ()
else forbidden ()
| None -> forbidden ()
let () = Html5.register ~service:election_setup
(generic_setup_page T.election_setup)
let () = Html5.register ~service:election_setup_trustees
(generic_setup_page T.election_setup_trustees)
let () = Html5.register ~service:election_setup_credential_authority
(generic_setup_page T.election_setup_credential_authority)
let election_setup_mutex = Lwt_mutex.create ()
let handle_setup f uuid x =
match%lwt Web_state.get_site_user () with
| Some u ->
let uuid_s = Uuidm.to_string uuid in
Lwt_mutex.with_lock election_setup_mutex (fun () ->
let%lwt se = get_setup_election uuid_s in
if se.se_owner = u then (
try%lwt
let%lwt cont = f se x u uuid in
set_setup_election uuid_s se >>
cont ()
with e ->
let service = preapply election_setup uuid in
T.generic_page ~title:"Error" ~service (Printexc.to_string e) () >>= Html5.send
) else forbidden ()
)
| None -> forbidden ()
let redir_preapply s u () = Redirection.send (preapply s u)
let () =
Any.register
~service:election_setup_languages
(handle_setup
(fun se languages _ uuid ->
let langs = languages_of_string languages in
match langs with
| None -> assert false
| Some [] ->
return (fun () ->
let service = preapply election_setup uuid in
T.generic_page ~title:"Error" ~service
"You must select at least one language!" () >>= Html5.send
)
| Some ls ->
let unavailable =
List.filter (fun x ->
not (List.mem x available_languages)
) ls
in
match unavailable with
| [] ->
se.se_metadata <- {
se.se_metadata with
e_languages = langs
};
return (redir_preapply election_setup uuid)
| l :: _ ->
return (fun () ->
let service = preapply election_setup uuid in
T.generic_page ~title:"Error" ~service
("No such language: " ^ l) () >>= Html5.send
)
))
let () =
Any.register
~service:election_setup_description
(handle_setup
(fun se (name, description) _ uuid ->
se.se_questions <- {se.se_questions with
t_name = name;
t_description = description;
};
return (redir_preapply election_setup uuid)))
let generate_password langs title url id =
let email, login = split_identity id in
let%lwt salt = generate_token () in
let%lwt password = generate_token () in
let hashed = sha256_hex (salt ^ password) in
let bodies = List.map (fun lang ->
let module L = (val Web_i18n.get_lang lang) in
Printf.sprintf L.mail_password title login password url
) langs in
let body = PString.concat "\n\n----------\n\n" bodies in
let body = body ^ "\n\n-- \nBelenios" in
let subject =
let lang = List.hd langs in
let module L = (val Web_i18n.get_lang lang) in
Printf.sprintf L.mail_password_subject title
in
send_email email subject body >>
return (salt, hashed)
let handle_password se uuid ~force voters =
if List.length voters > !maxmailsatonce then
Lwt.fail (Failure (Printf.sprintf "Cannot send passwords, there are too many voters (max is %d)" !maxmailsatonce))
else
let title = se.se_questions.t_name in
let url = Eliom_uri.make_string_uri ~absolute:true ~service:election_home
(uuid, ()) |> rewrite_prefix
in
let langs = get_languages se.se_metadata.e_languages in
Lwt_list.iter_s (fun id ->
match id.sv_password with
| Some _ when not force -> return_unit
| None | Some _ ->
let%lwt x = generate_password langs title url id.sv_id in
return (id.sv_password <- Some x)
) voters >>
return (fun () ->
let service = preapply election_setup uuid in
T.generic_page ~title:"Success" ~service
"Passwords have been generated and mailed!" () >>= Html5.send)
let () =
Any.register
~service:election_setup_auth_genpwd
(handle_setup
(fun se () _ uuid ->
handle_password se uuid ~force:false se.se_voters))
let () =
Any.register
~service:election_regenpwd
(fun (uuid, ()) () ->
T.regenpwd uuid () >>= Html5.send)
let () =
Any.register
~service:election_regenpwd_post
(fun (uuid, ()) user ->
let uuid_s = Uuidm.to_string uuid in
let%lwt w = find_election uuid_s in
let%lwt metadata = Web_persist.get_election_metadata uuid_s in
let module W = (val w) in
let%lwt site_user = Web_state.get_site_user () in
match site_user with
| Some u when metadata.e_owner = Some u ->
let table = "password_" ^ underscorize uuid_s in
let table = Ocsipersist.open_table table in
let title = W.election.e_params.e_name in
let url = Eliom_uri.make_string_uri
~absolute:true ~service:election_home
(uuid, ()) |> rewrite_prefix
in
let service = preapply election_admin (uuid, ()) in
begin try%lwt
let%lwt _ = Ocsipersist.find table user in
let langs = get_languages metadata.e_languages in
let%lwt x = generate_password langs title url user in
Ocsipersist.add table user x >>
dump_passwords (!spool_dir / uuid_s) table >>
T.generic_page ~title:"Success" ~service
("A new password has been mailed to " ^ user ^ ".") ()
>>= Html5.send
with Not_found ->
T.generic_page ~title:"Error" ~service
(user ^ " is not a registered user for this election.") ()
>>= Html5.send
end
| _ -> forbidden ()
)
let () =
Html5.register
~service:election_setup_questions
(fun uuid () ->
match%lwt Web_state.get_site_user () with
| Some u ->
let uuid_s = Uuidm.to_string uuid in
let%lwt se = get_setup_election uuid_s in
if se.se_owner = u
then T.election_setup_questions uuid se ()
else forbidden ()
| None -> forbidden ()
)
let () =
Any.register
~service:election_setup_questions_post
(handle_setup
(fun se x _ uuid ->
se.se_questions <- template_of_string x;
return (redir_preapply election_setup uuid)))
let () =
Html5.register
~service:election_setup_voters
(fun uuid () ->
match%lwt Web_state.get_site_user () with
| Some u ->
let uuid_s = Uuidm.to_string uuid in
let%lwt se = get_setup_election uuid_s in
if se.se_owner = u
then T.election_setup_voters uuid se !maxmailsatonce ()
else forbidden ()
| None -> forbidden ()
)
(* see http://www.regular-expressions.info/email.html *)
let identity_rex = Pcre.regexp
~flags:[`CASELESS]
"^[A-Z0-9._%+-]+@[A-Z0-9.-]+\\.[A-Z]{2,7}(,[A-Z0-9._%+-]+)?$"
let is_identity x =
try ignore (Pcre.pcre_exec ~rex:identity_rex x); true
with Not_found -> false
let email_rex = Pcre.regexp
~flags:[`CASELESS]
"^[A-Z0-9._%+-]+@[A-Z0-9.-]+\\.[A-Z]{2,7}$"
let is_email x =
try ignore (Pcre.pcre_exec ~rex:email_rex x); true
with Not_found -> false
module SSet = Set.Make (PString)
let merge_voters a b f =
let existing = List.fold_left (fun accu sv ->
SSet.add sv.sv_id accu
) SSet.empty a in
let _, res = List.fold_left (fun (existing, accu) sv_id ->
if SSet.mem sv_id existing then
(existing, accu)
else
(SSet.add sv_id existing, {sv_id; sv_password = f sv_id} :: accu)
) (existing, List.rev a) b in
List.rev res
let () =
Any.register
~service:election_setup_voters_add
(handle_setup
(fun se x _ uuid ->
if se.se_public_creds_received then forbidden () else (
let xs = Pcre.split x in
let () =
try
let bad = List.find (fun x -> not (is_identity x)) xs in
Printf.ksprintf failwith "%S is not a valid identity" bad
with Not_found -> ()
in
se.se_voters <- merge_voters se.se_voters xs (fun _ -> None);
return (redir_preapply election_setup_voters uuid))))
let () =
Any.register
~service:election_setup_voters_remove
(handle_setup
(fun se voter _ uuid ->
if se.se_public_creds_received then forbidden () else (
se.se_voters <- List.filter (fun v ->
v.sv_id <> voter
) se.se_voters;
return (redir_preapply election_setup_voters uuid))))
let () =
Any.register ~service:election_setup_voters_passwd
(handle_setup
(fun se voter _ uuid ->
let voter = List.filter (fun v -> v.sv_id = voter) se.se_voters in
handle_password se uuid ~force:true voter))
let () =
Any.register
~service:election_setup_trustee_add
(fun uuid st_id ->
if is_email st_id then
match%lwt Web_state.get_site_user () with
| Some u ->
let uuid_s = Uuidm.to_string uuid in
Lwt_mutex.with_lock election_setup_mutex (fun () ->
let%lwt se = get_setup_election uuid_s in
if se.se_owner = u
then (
let%lwt st_token = generate_token () in
let trustee = {st_id; st_token; st_public_key = ""} in
se.se_public_keys <- se.se_public_keys @ [trustee];
set_setup_election uuid_s se >>
Ocsipersist.add election_pktokens st_token uuid_s
) else forbidden ()
) >>
Redirection.send (preapply election_setup_trustees uuid)
| None -> forbidden ()
else
let msg = st_id ^ " is not a valid e-mail address!" in
let service = preapply election_setup_trustees uuid in
T.generic_page ~title:"Error" ~service msg () >>= Html5.send
)
let () =
Redirection.register
~service:election_setup_trustee_del
(fun uuid index ->
match%lwt Web_state.get_site_user () with
| Some u ->
let uuid_s = Uuidm.to_string uuid in
Lwt_mutex.with_lock election_setup_mutex (fun () ->
let%lwt se = get_setup_election uuid_s in
if se.se_owner = u
then (
let trustees, old =
se.se_public_keys |>
List.mapi (fun i x -> i, x) |>
List.partition (fun (i, _) -> i <> index) |>
(fun (x, y) -> List.map snd x, List.map snd y)
in
se.se_public_keys <- trustees;
set_setup_election uuid_s se >>
Lwt_list.iter_s (fun {st_token; _} ->
Ocsipersist.remove election_pktokens st_token
) old
) else forbidden ()
) >>
return (preapply election_setup_trustees uuid)
| None -> forbidden ()
)
let () =
Html5.register
~service:election_setup_credentials
(fun token () ->
let%lwt uuid = Ocsipersist.find election_credtokens token in
let%lwt se = get_setup_election uuid in
let uuid = match Uuidm.of_string uuid with
| None -> failwith "invalid UUID extracted from credtokens"
| Some u -> u
in
T.election_setup_credentials token uuid se ()
)
let () =
File.register
~service:election_setup_credentials_download
~content_type:"text/plain"
(fun token () ->
let%lwt uuid = Ocsipersist.find election_credtokens token in
return (!spool_dir / uuid ^ ".public_creds.txt")
)
let wrap_handler f =
try%lwt f ()
with
| e -> T.generic_page ~title:"Error" (Printexc.to_string e) () >>= Html5.send
let handle_credentials_post token creds =
let%lwt uuid = Ocsipersist.find election_credtokens token in
let%lwt se = get_setup_election uuid in
if se.se_public_creds_received then forbidden () else
let module G = (val Group.of_string se.se_group : GROUP) in
let fname = !spool_dir / uuid ^ ".public_creds.txt" in
Lwt_mutex.with_lock
election_setup_mutex
(fun () ->
Lwt_io.with_file
~flags:(Unix.([O_WRONLY; O_NONBLOCK; O_CREAT; O_TRUNC]))
~perm:0o600 ~mode:Lwt_io.Output fname
(fun oc -> Lwt_io.write_chars oc creds)
) >>
let%lwt () =
let i = ref 1 in
Lwt_stream.iter
(fun x ->
try
let x = G.of_string x in
if not (G.check x) then raise Exit;
incr i
with _ ->
Printf.ksprintf failwith "invalid credential at line %d" !i)
(Lwt_io.lines_of_file fname)
in
let () = se.se_metadata <- {se.se_metadata with e_cred_authority = None} in
let () = se.se_public_creds_received <- true in
set_setup_election uuid se >>
T.generic_page ~title:"Success" ~service:home
"Credentials have been received and checked!" () >>= Html5.send
let () =
Any.register
~service:election_setup_credentials_post
(fun token creds ->
let s = Lwt_stream.of_string creds in
wrap_handler (fun () -> handle_credentials_post token s))
let () =
Any.register
~service:election_setup_credentials_post_file
(fun token creds ->
let s = Lwt_io.chars_of_file creds.Ocsigen_extensions.tmp_filename in
wrap_handler (fun () -> handle_credentials_post token s))
module CG = Credential.MakeGenerate (LwtRandom)
let () =
Any.register
~service:election_setup_credentials_server
(handle_setup (fun se () _ uuid ->
let nvoters = List.length se.se_voters in
if nvoters > !maxmailsatonce then
Lwt.fail (Failure (Printf.sprintf "Cannot send credentials, there are too many voters (max is %d)" !maxmailsatonce))
else if nvoters = 0 then
Lwt.fail (Failure "No voters")
else
if se.se_public_creds_received then forbidden () else
let () = se.se_metadata <- {se.se_metadata with
e_cred_authority = Some "server"
} in
let uuid_s = Uuidm.to_string uuid in
let title = se.se_questions.t_name in
let url = Eliom_uri.make_string_uri
~absolute:true ~service:election_home
(uuid, ()) |> rewrite_prefix
in
let module S = Set.Make (PString) in
let module G = (val Group.of_string se.se_group : GROUP) in
let module CD = Credential.MakeDerive (G) in
let%lwt creds =
Lwt_list.fold_left_s (fun accu v ->
let email, login = split_identity v.sv_id in
let%lwt cred = CG.generate () in
let pub_cred =
let x = CD.derive uuid cred in
let y = G.(g **~ x) in
G.to_string y
in
let langs = get_languages se.se_metadata.e_languages in
let bodies = List.map (fun lang ->
let module L = (val Web_i18n.get_lang lang) in
Printf.sprintf L.mail_credential title login cred url
) langs in
let body = PString.concat "\n\n----------\n\n" bodies in
let body = body ^ "\n\n-- \nBelenios" in
let subject =
let lang = List.hd langs in
let module L = (val Web_i18n.get_lang lang) in
Printf.sprintf L.mail_credential_subject title
in
let%lwt () = send_email email subject body in
return @@ S.add pub_cred accu
) S.empty se.se_voters
in
let creds = S.elements creds in
let fname = !spool_dir / uuid_s ^ ".public_creds.txt" in
let%lwt () =
Lwt_io.with_file
~flags:(Unix.([O_WRONLY; O_NONBLOCK; O_CREAT; O_TRUNC]))
~perm:0o600 ~mode:Lwt_io.Output fname
(fun oc ->
Lwt_list.iter_s (Lwt_io.write_line oc) creds)
in
se.se_public_creds_received <- true;
return (fun () ->
let service = preapply election_setup uuid in
T.generic_page ~title:"Success" ~service
"Credentials have been generated and mailed!" () >>= Html5.send)))
let () =
Html5.register
~service:election_setup_trustee
(fun token () ->
let%lwt uuid = Ocsipersist.find election_pktokens token in
let%lwt se = get_setup_election uuid in
let uuid = match Uuidm.of_string uuid with
| None -> failwith "invalid UUID extracted from pktokens"
| Some u -> u
in
T.election_setup_trustee token uuid se ()
)
let () =
Any.register
~service:election_setup_trustee_post
(fun token public_key ->
wrap_handler
(fun () ->
let%lwt uuid = Ocsipersist.find election_pktokens token in
Lwt_mutex.with_lock
election_setup_mutex
(fun () ->
let%lwt se = get_setup_election uuid in
let t = List.find (fun x -> token = x.st_token) se.se_public_keys in
let module G = (val Group.of_string se.se_group : GROUP) in
let pk = trustee_public_key_of_string G.read public_key in
let module KG = Election.MakeSimpleDistKeyGen (G) (LwtRandom) in
if not (KG.check pk) then failwith "invalid public key";
(* we keep pk as a string because of G.t *)
t.st_public_key <- public_key;
set_setup_election uuid se
) >> T.generic_page ~title:"Success"
"Your key has been received and checked!"
() >>= Html5.send
)
)
let () =
Any.register
~service:election_setup_confirm
(fun uuid () ->
match%lwt Web_state.get_site_user () with
| None -> forbidden ()
| Some u ->
let uuid_s = Uuidm.to_string uuid in
let%lwt se = get_setup_election uuid_s in
if se.se_owner <> u then forbidden () else
T.election_setup_confirm uuid se () >>= Html5.send)
let () =
Any.register
~service:election_setup_create
(fun uuid () ->
match%lwt Web_state.get_site_user () with
| None -> forbidden ()
| Some u ->
begin try%lwt
let uuid_s = Uuidm.to_string uuid in
Lwt_mutex.with_lock election_setup_mutex (fun () ->
let%lwt se = get_setup_election uuid_s in
if se.se_owner <> u then forbidden () else
finalize_election uuid se >>
Redirection.send (preapply election_admin (uuid, ()))
)
with e ->
T.new_election_failure (`Exception e) () >>= Html5.send
end
)
let () =
Html5.register
~service:election_setup_import
(fun uuid () ->
let%lwt site_user = Web_state.get_site_user () in
match site_user with
| None -> forbidden ()
| Some u ->
let%lwt se = get_setup_election (Uuidm.to_string uuid) in
let%lwt elections = get_finalized_elections_by_owner u in
T.election_setup_import uuid se elections ())
let () =
Any.register
~service:election_setup_import_post
(handle_setup
(fun se from _ uuid ->
let from_s = Uuidm.to_string from in
let%lwt voters = Web_persist.get_voters from_s in
let%lwt passwords = Web_persist.get_passwords from_s in
let get_password =
match passwords with
| None -> fun _ -> None
| Some p -> fun sv_id ->
let _, login = split_identity sv_id in
try Some (SMap.find login p)
with Not_found -> None
in
match voters with
| Some voters ->
if se.se_public_creds_received then forbidden () else (
se.se_voters <- merge_voters se.se_voters voters get_password;
return (redir_preapply election_setup_voters uuid))
| None ->
return (fun () -> T.generic_page ~title:"Error"
~service:(preapply election_setup_voters uuid)
(Printf.sprintf
"Could not retrieve voter list from election %s"
from_s)
() >>= Html5.send)))
let () =
Html5.register ~service:election_setup_import_trustees
(fun uuid () ->
let%lwt site_user = Web_state.get_site_user () in
match site_user with
| None -> forbidden ()
| Some u ->
let%lwt se = get_setup_election (Uuidm.to_string uuid) in
let%lwt elections = get_finalized_elections_by_owner u in
T.election_setup_import_trustees uuid se elections ())
exception TrusteeImportError of string
let () =
Any.register ~service:election_setup_import_trustees_post
(handle_setup
(fun se from _ uuid ->
let uuid_s = Uuidm.to_string uuid in
let from_s = Uuidm.to_string from in
let%lwt metadata = Web_persist.get_election_metadata from_s in
let%lwt public_keys = Web_persist.get_public_keys from_s in
try%lwt
match metadata.e_trustees, public_keys with
| Some ts, Some pks when List.length ts = List.length pks ->
let%lwt trustees =
List.combine ts pks
|> Lwt_list.map_p
(fun (st_id, st_public_key) ->
let%lwt st_token = generate_token () in
return {st_id; st_token; st_public_key})
in
let () =
(* check that imported keys are valid *)
let module G = (val Group.of_string se.se_group : GROUP) in
let module KG = Election.MakeSimpleDistKeyGen (G) (LwtRandom) in
if not @@ List.for_all (fun t ->
let pk = t.st_public_key in
let pk = trustee_public_key_of_string G.read pk in
KG.check pk) trustees then
raise (TrusteeImportError "Imported keys are invalid for this election!")
in
se.se_public_keys <- se.se_public_keys @ trustees;
Lwt_list.iter_s (fun {st_token; _} ->
Ocsipersist.add election_pktokens st_token uuid_s
) trustees >>
return (redir_preapply election_setup_trustees uuid)
| _, _ ->
[%lwt raise (TrusteeImportError "Could not retrieve trustees from selected election!")]
with
| TrusteeImportError msg ->
return (fun () ->
T.generic_page ~title:"Error"
~service:(preapply election_setup_trustees uuid)
msg () >>= Html5.send)))
let () =
Any.register
~service:election_home
(fun (uuid, ()) () ->
let uuid_s = Uuidm.to_string uuid in
try%lwt
let%lwt w = find_election uuid_s in
let module W = (val w) in
Eliom_reference.unset Web_state.ballot >>
let cont () =
Redirection.send
(Eliom_service.preapply
election_home (W.election.e_params.e_uuid, ()))
in
Eliom_reference.set Web_state.cont [cont] >>
match%lwt Eliom_reference.get Web_state.cast_confirmed with
| Some result ->
Eliom_reference.unset Web_state.cast_confirmed >>
Eliom_reference.unset Web_state.user >>
T.cast_confirmed (module W) ~result () >>= Html5.send
| None ->
let%lwt state = Web_persist.get_election_state uuid_s in
T.election_home (module W) state () >>= Html5.send
with Not_found ->
let%lwt lang = Eliom_reference.get Web_state.language in
let module L = (val Web_i18n.get_lang lang) in
T.generic_page ~title:L.not_yet_open
~service:(preapply election_home (uuid, ()))
L.come_back_later ()
>>= Html5.send)
let () =
Any.register ~service:set_cookie_disclaimer
(fun () () ->
Eliom_reference.set Web_state.show_cookie_disclaimer false >>
let%lwt cont = Web_state.cont_pop () in
match cont with
| Some f -> f ()
| None ->
let%lwt lang = Eliom_reference.get Web_state.language in
let module L = (val Web_i18n.get_lang lang) in
T.generic_page ~title:L.cookies_are_blocked
L.please_enable_them ()
>>= Html5.send)
let () =
Any.register
~service:election_admin
(fun (uuid, ()) () ->
let uuid_s = Uuidm.to_string uuid in
let%lwt w = find_election uuid_s in
let%lwt metadata = Web_persist.get_election_metadata uuid_s in
let%lwt site_user = Web_state.get_site_user () in
let module W = (val w) in
match site_user with
| Some u when metadata.e_owner = Some u ->
let%lwt state = Web_persist.get_election_state uuid_s in
T.election_admin w metadata state () >>= Html5.send
| _ ->
let cont () =
Redirection.send (Eliom_service.preapply election_admin (uuid, ()))
in
Eliom_reference.set Web_state.cont [cont] >>
Redirection.send (Eliom_service.preapply site_login None)
)
let election_set_state state (uuid, ()) () =
let uuid_s = Uuidm.to_string uuid in
let%lwt w = find_election uuid_s in
let%lwt metadata = Web_persist.get_election_metadata uuid_s in
let module W = (val w) in
let%lwt () =
match%lwt Web_state.get_site_user () with
| Some u when metadata.e_owner = Some u -> return ()
| _ -> forbidden ()
in
let%lwt () =
match%lwt Web_persist.get_election_state uuid_s with
| `Open | `Closed -> return ()
| _ -> forbidden ()
in
let state = if state then `Open else `Closed in
Web_persist.set_election_state uuid_s state >>
Redirection.send (preapply election_admin (uuid, ()))
let () = Any.register ~service:election_open (election_set_state true)
let () = Any.register ~service:election_close (election_set_state false)
let () = Any.register ~service:election_archive (fun (uuid, ()) () ->
let uuid_s = Uuidm.to_string uuid in
let%lwt w = find_election uuid_s in
let%lwt metadata = Web_persist.get_election_metadata uuid_s in
let%lwt site_user = Web_state.get_site_user () in
let module W = (val w) in
match site_user with
| Some u when metadata.e_owner = Some u ->
archive_election uuid_s >>
Redirection.send (Eliom_service.preapply election_admin (uuid, ()))
| _ -> forbidden ()
)
let () =
Any.register
~service:election_update_credential
(fun (uuid, ()) () ->
let uuid_s = Uuidm.to_string uuid in
let%lwt w = find_election uuid_s in
let%lwt metadata = Web_persist.get_election_metadata uuid_s in
let%lwt site_user = Web_state.get_site_user () in
let module W = (val w) in
match site_user with
| Some u ->
if metadata.e_owner = Some u then (
T.update_credential (module W) () >>= Html5.send
) else (
forbidden ()
)
| _ -> forbidden ())
let () =
Any.register
~service:election_update_credential_post
(fun (uuid, ()) (old, new_) ->
let uuid_s = Uuidm.to_string uuid in
let%lwt w = find_election uuid_s in
let%lwt metadata = Web_persist.get_election_metadata uuid_s in
let module W = (val w) in
let%lwt site_user = Web_state.get_site_user () in
let module WE = Web_election.Make (W) (LwtRandom) in
match site_user with
| Some u ->
if metadata.e_owner = Some u then (
try%lwt
WE.B.update_cred ~old ~new_ >>
String.send ("OK", "text/plain")
with Error e ->
String.send ("Error: " ^ explain_error e, "text/plain")
) >>= (fun x -> return @@ cast_unknown_content_kind x)
else (
forbidden ()
)
| _ -> forbidden ())
let () =
Any.register
~service:election_vote
(fun (_, ()) () ->
Eliom_reference.unset Web_state.ballot >>
Web_templates.booth () >>= Html5.send)
let () =
Any.register
~service:election_cast
(fun (uuid, ()) () ->
let uuid_s = Uuidm.to_string uuid in
let%lwt w = find_election uuid_s in
let module W = (val w) in
let cont () =
Redirection.send
(Eliom_service.preapply
election_cast (W.election.e_params.e_uuid, ()))
in
Eliom_reference.set Web_state.cont [cont] >>
match%lwt Eliom_reference.get Web_state.ballot with
| Some b -> T.cast_confirmation (module W) (sha256_b64 b) () >>= Html5.send
| None -> T.cast_raw (module W) () >>= Html5.send)
let () =
Any.register
~service:election_cast_post
(fun (uuid, ()) (ballot_raw, ballot_file) ->
let uuid_s = Uuidm.to_string uuid in
let%lwt w = find_election uuid_s in
let module W = (val w) in
let%lwt user = Web_state.get_election_user uuid in
let%lwt the_ballot = match ballot_raw, ballot_file with
| Some ballot, None -> return ballot
| None, Some fi ->
let fname = fi.Ocsigen_extensions.tmp_filename in
Lwt_stream.to_string (Lwt_io.chars_of_file fname)
| _, _ -> fail_http 400
in
let the_ballot = PString.trim the_ballot in
let cont () =
Redirection.send
(Eliom_service.preapply
Web_services.election_cast (W.election.e_params.e_uuid, ()))
in
Eliom_reference.set Web_state.cont [cont] >>
Eliom_reference.set Web_state.ballot (Some the_ballot) >>
match user with
| None ->
Redirection.send
(Eliom_service.preapply
Web_services.election_login
((W.election.e_params.e_uuid, ()), None))
| Some _ -> cont ())
let () =
Any.register
~service:election_cast_confirm
(fun (uuid, ()) () ->
let uuid_s = Uuidm.to_string uuid in
let%lwt w = find_election uuid_s in
let module W = (val w) in
let module WE = Web_election.Make (W) (LwtRandom) in
match%lwt Eliom_reference.get Web_state.ballot with
| Some the_ballot ->
begin
Eliom_reference.unset Web_state.ballot >>
match%lwt Web_state.get_election_user uuid with
| Some u ->
let record = u, now () in
let%lwt result =
try%lwt
let%lwt hash = WE.B.cast the_ballot record in
return (`Valid hash)
with Error e -> return (`Error e)
in
Eliom_reference.set Web_state.cast_confirmed (Some result) >>
Redirection.send
(Eliom_service.preapply
election_home (W.election.e_params.e_uuid, ()))
| None -> forbidden ()
end
| None -> fail_http 404)
let () =
Any.register
~service:election_pretty_ballots
(fun (uuid, ()) () ->
let uuid_s = Uuidm.to_string uuid in
let%lwt w = find_election uuid_s in
let%lwt ballots = Web_persist.get_ballot_hashes uuid_s in
let%lwt result = Web_persist.get_election_result uuid_s in
T.pretty_ballots w ballots result () >>= Html5.send)
let () =
Any.register
~service:election_pretty_ballot
(fun ((uuid, ()), hash) () ->
let uuid_s = Uuidm.to_string uuid in
let%lwt ballot = Web_persist.get_ballot_by_hash ~uuid:uuid_s ~hash in
match ballot with
| None -> fail_http 404
| Some b ->
String.send (b, "application/json") >>=
(fun x -> return @@ cast_unknown_content_kind x))
let () =
let rex = Pcre.regexp "\".*\" \".*:(.*)\"" in
Any.register
~service:election_missing_voters
(fun (uuid, ()) () ->
let uuid_s = Uuidm.to_string uuid in
let%lwt w = find_election uuid_s in
let%lwt metadata = Web_persist.get_election_metadata uuid_s in
let module W = (val w) in
let%lwt () =
match%lwt Web_state.get_site_user () with
| Some u when metadata.e_owner = Some u -> return ()
| _ -> forbidden ()
in
let voters = Lwt_io.lines_of_file
(!spool_dir / uuid_s / string_of_election_file ESVoters)
in
let module S = Set.Make (PString) in
let%lwt voters = Lwt_stream.fold (fun v accu ->
let _, login = split_identity v in
S.add login accu
) voters S.empty in
let records = Lwt_io.lines_of_file
(!spool_dir / uuid_s / string_of_election_file ESRecords)
in
let%lwt voters = Lwt_stream.fold (fun r accu ->
let s = Pcre.exec ~rex r in
let v = Pcre.get_substring s 1 in
S.remove v accu
) records voters in
let buf = Buffer.create 128 in
S.iter (fun v ->
Buffer.add_string buf v;
Buffer.add_char buf '\n'
) voters;
String.send (Buffer.contents buf, "text/plain"))
let () =
let rex = Pcre.regexp "\"(.*)\\..*\" \".*:(.*)\"" in
Any.register ~service:election_pretty_records
(fun (uuid, ()) () ->
let uuid_s = Uuidm.to_string uuid in
let%lwt w = find_election uuid_s in
let%lwt metadata = Web_persist.get_election_metadata uuid_s in
let module W = (val w) in
let%lwt () =
match%lwt Web_state.get_site_user () with
| Some u when metadata.e_owner = Some u -> return_unit
| _ -> forbidden ()
in
let records = Lwt_io.lines_of_file
(!spool_dir / uuid_s / string_of_election_file ESRecords)
in
let%lwt records = Lwt_stream.fold (fun r accu ->
let s = Pcre.exec ~rex r in
let date = Pcre.get_substring s 1 in
let voter = Pcre.get_substring s 2 in
(date, voter) :: accu
) records [] in
T.pretty_records w (List.rev records) () >>= Html5.send
)
let () =
Any.register
~service:election_tally_trustees
(fun (uuid, ((), trustee_id)) () ->
let uuid_s = Uuidm.to_string uuid in
let%lwt w = find_election uuid_s in
let module W = (val w) in
let%lwt () =
match%lwt Web_persist.get_election_state uuid_s with
| `EncryptedTally _ -> return ()
| _ -> fail_http 404
in
let%lwt pds = Web_persist.get_partial_decryptions uuid_s in
if List.mem_assoc trustee_id pds then (
T.generic_page ~title:"Error"
"Your partial decryption has already been received and checked!"
() >>= Html5.send
) else (
T.tally_trustees (module W) trustee_id () >>= Html5.send
))
let () =
Any.register
~service:election_tally_trustees_post
(fun (uuid, ((), trustee_id)) partial_decryption ->
let uuid_s = Uuidm.to_string uuid in
let%lwt () =
match%lwt Web_persist.get_election_state uuid_s with
| `EncryptedTally _ -> return ()
| _ -> forbidden ()
in
let%lwt pds = Web_persist.get_partial_decryptions uuid_s in
let%lwt () =
if List.mem_assoc trustee_id pds then forbidden () else return ()
in
let%lwt () =
if trustee_id > 0 then return () else fail_http 404
in
let%lwt w = find_election uuid_s in
let module W = (val w) in
let module E = Election.MakeElection (W.G) (LwtRandom) in
let pks = !spool_dir / uuid_s / string_of_election_file ESKeys in
let pks = Lwt_io.lines_of_file pks in
let%lwt () = Lwt_stream.njunk (trustee_id-1) pks in
let%lwt pk = Lwt_stream.peek pks in
let%lwt () = Lwt_stream.junk_while (fun _ -> true) pks in
let%lwt pk =
match pk with
| None -> fail_http 404
| Some x -> return x
in
let pk = trustee_public_key_of_string W.G.read pk in
let pk = pk.trustee_public_key in
let pd = partial_decryption_of_string W.G.read partial_decryption in
let et = !spool_dir / uuid_s / string_of_election_file ESETally in
let%lwt et = Lwt_io.chars_of_file et |> Lwt_stream.to_string in
let et = encrypted_tally_of_string W.G.read et in
if E.check_factor et pk pd then (
let pds = (trustee_id, partial_decryption) :: pds in
let%lwt () = Web_persist.set_partial_decryptions uuid_s pds in
T.generic_page ~title:"Success"
"Your partial decryption has been received and checked!" () >>=
Html5.send
) else (
let service = preapply election_tally_trustees (uuid, ((), trustee_id)) in
T.generic_page ~title:"Error" ~service
"The partial decryption didn't pass validation!" () >>=
Html5.send
))
let handle_election_tally_release (uuid, ()) () =
let uuid_s = Uuidm.to_string uuid in
let%lwt w = find_election uuid_s in
let%lwt metadata = Web_persist.get_election_metadata uuid_s in
let module W = (val w) in
let module E = Election.MakeElection (W.G) (LwtRandom) in
let%lwt () =
match%lwt Web_state.get_site_user () with
| Some u when metadata.e_owner = Some u -> return ()
| _ -> forbidden ()
in
let%lwt npks, ntallied =
match%lwt Web_persist.get_election_state uuid_s with
| `EncryptedTally (npks, ntallied, _) -> return (npks, ntallied)
| _ -> forbidden ()
in
let%lwt pds = Web_persist.get_partial_decryptions uuid_s in
let%lwt pds =
try
return @@ Array.init npks (fun i ->
List.assoc (i+1) pds |> partial_decryption_of_string W.G.read
)
with Not_found -> fail_http 404
in
let%lwt et =
!spool_dir / uuid_s / string_of_election_file ESETally |>
Lwt_io.chars_of_file |> Lwt_stream.to_string >>=
wrap1 (encrypted_tally_of_string W.G.read)
in
let result = E.combine_factors ntallied et pds in
let%lwt () =
let open Lwt_io in
with_file
~mode:Output (!spool_dir / uuid_s / string_of_election_file ESResult)
(fun oc -> Lwt_io.write_line oc (string_of_result W.G.write result))
in
let%lwt () = Web_persist.set_election_state uuid_s (`Tallied result.result) in
Eliom_service.preapply
election_home (W.election.e_params.e_uuid, ()) |>
Redirection.send
let () =
Any.register
~service:election_tally_release
handle_election_tally_release
let content_type_of_file = function
| ESRaw -> "application/json; charset=utf-8"
| ESKeys | ESBallots | ESETally | ESResult -> "application/json"
| ESCreds | ESRecords | ESVoters -> "text/plain"
let handle_pseudo_file uuid_s w f site_user =
let module W = (val w : ELECTION_DATA) in
let confidential =
match f with
| ESRaw | ESKeys | ESBallots | ESETally | ESResult | ESCreds -> false
| ESRecords | ESVoters -> true
in
let%lwt () =
if confidential then (
let%lwt metadata = Web_persist.get_election_metadata uuid_s in
match site_user with
| Some u when metadata.e_owner = Some u -> return ()
| _ -> forbidden ()
) else return ()
in
let content_type = content_type_of_file f in
File.send ~content_type (!spool_dir / uuid_s / string_of_election_file f)
let () =
Any.register
~service:election_dir
(fun (uuid, f) () ->
let uuid_s = Uuidm.to_string uuid in
let%lwt w = find_election uuid_s in
let%lwt site_user = Web_state.get_site_user () in
let module W = (val w) in
handle_pseudo_file uuid_s w f site_user)
let () =
Any.register
~service:election_compute_encrypted_tally
(fun (uuid, ()) () ->
let uuid_s = Uuidm.to_string uuid in
let%lwt w = find_election uuid_s in
let%lwt metadata = Web_persist.get_election_metadata uuid_s in
let module W = (val w) in
let module WE = Web_election.Make (W) (LwtRandom) in
let%lwt () =
match%lwt Web_state.get_site_user () with
| Some u when metadata.e_owner = Some u -> return ()
| _ -> forbidden ()
in
let%lwt () =
match%lwt Web_persist.get_election_state uuid_s with
| `Closed -> return ()
| _ -> forbidden ()
in
let%lwt nb, hash, tally = WE.B.compute_encrypted_tally () in
let pks = !spool_dir / uuid_s / string_of_election_file ESKeys in
let pks = Lwt_io.lines_of_file pks in
let npks = ref 0 in
let%lwt () = Lwt_stream.junk_while (fun _ -> incr npks; true) pks in
Web_persist.set_election_state uuid_s (`EncryptedTally (!npks, nb, hash)) >>
(* compute partial decryption and release tally
if the (single) key is known *)
let skfile = !spool_dir / uuid_s / "private_key.json" in
if !npks = 1 && Sys.file_exists skfile then (
let%lwt sk = Lwt_io.lines_of_file skfile |> Lwt_stream.to_list in
let sk = match sk with
| [sk] -> number_of_string sk
| _ -> failwith "several private keys are available"
in
let tally = encrypted_tally_of_string WE.G.read tally in
let%lwt pd = WE.E.compute_factor tally sk in
let pd = string_of_partial_decryption WE.G.write pd in
Web_persist.set_partial_decryptions uuid_s [1, pd] >>
handle_election_tally_release (uuid, ()) ()
) else Redirection.send (preapply election_admin (uuid, ())))
let () =
Any.register ~service:set_language
(fun lang () ->
Eliom_reference.set Web_state.language lang >>
let%lwt cont = Web_state.cont_pop () in
match cont with
| Some f -> f ()
| None -> Redirection.send home)
belenios-1.4+dfsg/src/web/web_site.mli000066400000000000000000000031001307140314400177340ustar00rootroot00000000000000(**************************************************************************)
(* BELENIOS *)
(* *)
(* Copyright © 2012-2016 Inria *)
(* *)
(* 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. *)
(* *)
(* You should have received a copy of the GNU Affero General Public *)
(* License along with this program. If not, see *)
(* . *)
(**************************************************************************)
val source_file : string ref
val maxmailsatonce : int ref
belenios-1.4+dfsg/src/web/web_state.ml000066400000000000000000000062421307140314400177510ustar00rootroot00000000000000(**************************************************************************)
(* BELENIOS *)
(* *)
(* Copyright © 2012-2016 Inria *)
(* *)
(* 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. *)
(* *)
(* You should have received a copy of the GNU Affero General Public *)
(* License along with this program. If not, see *)
(* . *)
(**************************************************************************)
open Lwt
open Web_serializable_t
type user = {
uuid: Uuidm.t option;
service : string;
name : string;
}
let scope = Eliom_common.default_session_scope
let show_cookie_disclaimer = Eliom_reference.eref ~scope true
let user = Eliom_reference.eref ~scope None
let get_site_user () =
match%lwt Eliom_reference.get user with
| None -> return None
| Some u ->
match u.uuid with
| None ->
return @@ Some {
user_domain = u.service;
user_name = u.name;
}
| Some _ -> return None
let get_election_user uuid =
match%lwt Eliom_reference.get user with
| None -> return None
| Some u ->
match u.uuid with
| None -> return None
| Some uuid' ->
if Uuidm.equal uuid uuid' then
return @@ Some {
user_domain = u.service;
user_name = u.name
}
else
return None
let cont = Eliom_reference.eref ~scope []
let cont_push f =
let open Eliom_reference in
let%lwt fs = get cont in
set cont (f :: fs)
let cont_pop () =
let open Eliom_reference in
let%lwt fs = get cont in
match fs with
| f :: fs -> set cont fs >> return (Some f)
| [] -> return None
let ballot = Eliom_reference.eref ~scope None
let cast_confirmed = Eliom_reference.eref ~scope None
let get_default_language () =
let ri = Eliom_request_info.get_ri () in
let lazy langs = Ocsigen_request_info.accept_language ri in
match langs with
| [] -> "en"
| (lang, _) :: _ ->
let n =
try String.index lang '-'
with Not_found -> String.length lang
in
String.sub lang 0 n
let language = Eliom_reference.eref_from_fun ~scope get_default_language
belenios-1.4+dfsg/src/web/web_state.mli000066400000000000000000000042641307140314400201240ustar00rootroot00000000000000(**************************************************************************)
(* BELENIOS *)
(* *)
(* Copyright © 2012-2016 Inria *)
(* *)
(* 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. *)
(* *)
(* You should have received a copy of the GNU Affero General Public *)
(* License along with this program. If not, see *)
(* . *)
(**************************************************************************)
open Web_signatures
type user = {
uuid: Uuidm.t option;
service : string;
name : string;
}
val show_cookie_disclaimer : bool Eliom_reference.eref
val user : user option Eliom_reference.eref
val get_site_user : unit -> Web_serializable_t.user option Lwt.t
val get_election_user : Uuidm.t -> Web_serializable_t.user option Lwt.t
val cont : (unit -> content) list Eliom_reference.eref
val cont_push : (unit -> content) -> unit Lwt.t
val cont_pop : unit -> (unit -> content) option Lwt.t
val ballot : string option Eliom_reference.eref
val cast_confirmed : [ `Error of Web_common.error | `Valid of string ] option Eliom_reference.eref
val language : string Eliom_reference.eref
belenios-1.4+dfsg/src/web/web_templates.ml000066400000000000000000001752211307140314400206330ustar00rootroot00000000000000(**************************************************************************)
(* BELENIOS *)
(* *)
(* Copyright © 2012-2016 Inria *)
(* *)
(* 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. *)
(* *)
(* You should have received a copy of the GNU Affero General Public *)
(* License along with this program. If not, see *)
(* . *)
(**************************************************************************)
open Lwt
open Serializable_j
open Signatures
open Common
open Web_serializable_j
open Web_signatures
open Web_common
open Web_services
open Eliom_content.Html5.F
(* TODO: these pages should be redesigned *)
let site_title = "Election Server"
let admin_background = " background: #FF9999;"
let format_user ~site u =
em [pcdata (if site then string_of_user u else u.user_name)]
let make_login_box ~site auth links =
let style = if site then admin_background else "" in
let style = "float: right; text-align: right;" ^ style in
let module S = (val auth : AUTH_SERVICES) in
let module L = (val links : AUTH_LINKS) in
let%lwt user = S.get_user () in
let%lwt auth_systems = S.get_auth_systems () in
let body =
match user with
| Some user ->
[
div [
pcdata "Logged in as ";
format_user ~site user;
pcdata ".";
];
div [
a ~a:[a_id "logout"] ~service:L.logout [pcdata "Log out"] ();
pcdata ".";
];
]
| None ->
if site then
[
div [
pcdata "Not logged in.";
];
let auth_systems =
auth_systems |>
List.map (fun name ->
a ~a:[a_id ("login_" ^ name)]
~service:(L.login (Some name)) [pcdata name] ()
) |> list_join (pcdata ", ")
in
div (
[pcdata "Log in: ["] @ auth_systems @ [pcdata "]"]
);
]
else []
in
match body with
| [] -> return None
| _::_ -> return (Some (div ~a:[a_style style] body))
module Site_links = struct
let login x = Eliom_service.preapply site_login x
let logout = Eliom_service.preapply logout ()
end
module Site_auth = struct
let get_user () = Web_state.get_site_user ()
let get_auth_systems () =
let%lwt l = Web_persist.get_auth_config "" in
return (List.map fst l)
end
let site_links = (module Site_links : AUTH_LINKS)
let site_auth = (module Site_auth : AUTH_SERVICES)
let site_login_box () =
make_login_box ~site:true site_auth site_links
let belenios_url = Eliom_service.Http.external_service
~prefix:"http://www.belenios.org"
~path:[]
~get_params:Eliom_parameter.unit
()
let base ~title ?login_box ~content ?(footer = div []) ?uuid () =
let%lwt language = Eliom_reference.get Web_state.language in
let module L = (val Web_i18n.get_lang language) in
let administer =
match uuid with
| None ->
a ~service:admin [pcdata L.administer_elections] ()
| Some uuid ->
a ~service:election_admin [pcdata L.administer_this_election] (uuid, ())
in
let login_box = match login_box with
| None ->
div ~a:[a_style "float: right; padding: 10px;"] [
img ~a:[a_height 70] ~alt:""
~src:(uri_of_string (fun () -> "/static/placeholder.png")) ();
]
| Some x -> x
in
Lwt.return (html ~a:[a_dir `Ltr; a_xml_lang L.lang]
(head (Eliom_content.Html5.F.title (pcdata title)) [
script (pcdata "window.onbeforeunload = function () {};");
link ~rel:[`Stylesheet] ~href:(uri_of_string (fun () -> "/static/site.css")) ();
])
(body [
div ~a:[a_id "wrapper"] [
div ~a:[a_id "header"] [
div [
div ~a:[a_style "float: left; padding: 10px;"] [
a ~service:home [
img ~alt:L.election_server ~a:[a_height 70]
~src:(uri_of_string (fun () -> "/static/logo.png")) ();
] ();
];
login_box;
h1 ~a:[a_style "text-align: center; padding: 20px;"] [pcdata title];
div ~a:[a_style "clear: both;"] [];
];
];
div ~a:[a_id "main"] content;
div ~a:[a_id "footer"; a_style "text-align: center;" ] [
div ~a:[a_id "bottom"] [
footer;
pcdata L.powered_by;
a ~service:belenios_url [pcdata "Belenios"] ();
pcdata ". ";
a ~service:source_code [pcdata L.get_the_source_code] ();
pcdata ". ";
administer;
pcdata ".";
]
]]
]))
let format_election election =
let module W = (val election : ELECTION_DATA) in
let e = W.election.e_params in
let service = election_admin in
li [
a ~service [pcdata e.e_name] (e.e_uuid, ());
]
let admin ~elections () =
let title = site_title ^ " — Administration" in
match elections with
| None ->
let content = [
div [
pcdata "To administer an election, you need to ";
a ~service:site_login [pcdata "log in"] None;
pcdata ". If you do not have an account, ";
pcdata "please send an email to contact@belenios.org.";
]
] in
let%lwt login_box = site_login_box () in
base ~title ?login_box ~content ()
| Some (elections, tallied, archived, setup_elections) ->
let elections =
match elections with
| [] -> p [pcdata "You own no such elections!"]
| _ -> ul @@ List.map format_election elections
in
let tallied =
match tallied with
| [] -> p [pcdata "You own no such elections!"]
| _ -> ul @@ List.map format_election tallied
in
let archived =
match archived with
| [] -> p [pcdata "You own no such elections!"]
| _ -> ul @@ List.map format_election archived
in
let setup_elections =
match setup_elections with
| [] -> p [pcdata "You own no such elections!"]
| _ -> ul @@
List.map (fun (k, title) ->
li [a ~service:election_setup [pcdata title] k]
) setup_elections
in
let content = [
div [
div [
a ~service:election_setup_pre [
pcdata "Prepare a new election";
] ();
];
div [br ()];
h2 [pcdata "Elections being prepared"];
setup_elections;
div [br ()];
h2 [pcdata "Elections you can administer"];
elections;
div [br ()];
h2 [pcdata "Tallied elections"];
tallied;
div [br ()];
h2 [pcdata "Archived elections"];
archived;
];
] in
let%lwt login_box = site_login_box () in
base ~title ?login_box ~content ()
let make_button ~service ~disabled contents =
let uri = Eliom_uri.make_string_uri ~service () in
Printf.ksprintf Unsafe.data (* FIXME: unsafe *)
""
uri (if disabled then " disabled" else "")
contents
let a_mailto ~dest ~subject ~body contents =
let uri = Printf.sprintf "mailto:%s?subject=%s&body=%s" dest
(Netencoding.Url.encode ~plus:false subject)
(Netencoding.Url.encode ~plus:false body)
in
Printf.ksprintf Unsafe.data "%s"
uri contents
let new_election_failure reason () =
let title = "Create new election" in
let reason =
match reason with
| `Exists -> pcdata "An election with the same UUID already exists."
| `Exception e -> pcdata @@ Printexc.to_string e
in
let content = [
div [
p [pcdata "The creation failed."];
p [reason];
]
] in
let%lwt login_box = site_login_box () in
base ~title ?login_box ~content ()
let generic_page ~title ?service message () =
let proceed = match service with
| None -> pcdata ""
| Some service ->
div [
a ~service [pcdata "Proceed"] ();
]
in
let content = [
p [pcdata message];
proceed;
] in
base ~title ~content ()
let election_setup_pre () =
let title = "Prepare a new election" in
let cred_info = Eliom_service.Http.external_service
~prefix:"http://www.belenios.org"
~path:["setup.php"]
~get_params:Eliom_parameter.unit
()
in
let form =
post_form ~service:election_setup_new
(fun (credmgmt, (auth, cas_server)) ->
[
fieldset
~legend:(legend [
pcdata "Credential management (";
a ~service:cred_info [pcdata "more info"] ();
pcdata ")";
])
[
div [
string_radio ~checked:true ~name:credmgmt ~value:"auto" ();
pcdata " Automatic (degraded mode - credentials will be handled by the server)";
];
div [
string_radio ~name:credmgmt ~value:"manual" ();
pcdata " Manual (safe mode - a third party will handle the credentials)";
];
];
fieldset
~legend:(legend [pcdata "Authentication"])
[
div [
string_radio ~checked:true ~name:auth ~value:"password" ();
pcdata " Password (passwords will be emailed to voters)";
];
div [
string_radio ~name:auth ~value:"cas" ();
pcdata " CAS (external authentication server), server address: ";
string_input ~input_type:`Text ~name:cas_server ();
pcdata " (for example: https://cas.inria.fr/cas)";
];
];
div [
string_input ~input_type:`Submit ~value:"Proceed" ();
];
]
) ()
in
let content = [
form
] in
let%lwt login_box = site_login_box () in
base ~title ?login_box ~content ()
let election_setup uuid se () =
let title = "Preparation of election " ^ se.se_questions.t_name in
let form_languages =
post_form ~service:election_setup_languages
(fun languages ->
[
div [
pcdata "Languages: ";
string_input ~name:languages ~input_type:`Text
~value:(string_of_languages se.se_metadata.e_languages) ();
pcdata " (Available languages: ";
pcdata (string_of_languages (Some available_languages));
pcdata ")";
];
div [
pcdata "(This is a space-separated list of languages that will be used in emails sent by the server.)";
];
div [
string_input ~input_type:`Submit ~value:"Save changes" ();
];
]) uuid
in
let div_languages =
div [
h2 [pcdata "Languages"];
form_languages;
]
in
let form_description =
post_form ~service:election_setup_description
(fun (name, description) ->
[
div [
pcdata "Name of the election: ";
string_input ~name:name
~input_type:`Text ~value:se.se_questions.t_name ();
];
div [
div [pcdata "Description of the election: "];
div [
textarea ~name:description ~a:[a_cols 80]
~value:se.se_questions.t_description ();
];
];
div [
string_input ~input_type:`Submit ~value:"Save changes" ();
];
]
) uuid
in
let div_description =
div [
h2 [pcdata "Name and description of the election"];
form_description;
]
in
let has_credentials = match se.se_metadata.e_cred_authority with
| None -> false
| Some _ -> true
in
let auth = match se.se_metadata.e_auth_config with
| Some [{auth_system = "password"; _}] -> `Password
| Some [{auth_system = "dummy"; _}] -> `Dummy
| Some [{auth_system = "cas"; auth_config = ["server", server]; _}] -> `CAS server
| _ -> failwith "unknown authentication scheme in election_setup"
in
let div_auth =
div [
h2 [pcdata "Authentication"];
match auth with
| `Password ->
div [
pcdata "Authentication scheme: password ";
post_form ~service:election_setup_auth_genpwd
(fun () ->
[string_input ~input_type:`Submit ~value:"Generate and mail missing passwords" ()]
) uuid;
]
| `Dummy ->
div [
pcdata "Authentication scheme: dummy"
]
| `CAS server ->
div [
pcdata "Authentication scheme: CAS with server ";
pcdata server;
]
]
in
let div_questions =
div [
h2 [
a ~a:[a_id "edit_questions"] ~service:election_setup_questions
[pcdata "Edit questions"]
uuid;
]
]
in
let div_voters =
div [
h2 [
a ~a:[a_id "edit_voters"] ~service:election_setup_voters
[pcdata "Edit voters"]
uuid
];
div [
pcdata @@ string_of_int @@ List.length se.se_voters;
pcdata " voter(s) registered";
];
]
in
let div_trustees =
div [
h2 [pcdata "Trustees"];
div [
pcdata "By default, the election server manages the keys of the ";
pcdata "election. If you do not wish the server to store any keys, ";
pcdata "click ";
a ~service:election_setup_trustees [pcdata "here"] uuid;
pcdata "."];
]
in
let div_credentials =
div [
h2 [pcdata "Credentials"];
if se.se_public_creds_received then (
div [
pcdata "Credentials have already been generated!"
]
) else (
div [
pcdata "Warning: this will freeze the voter list!";
if has_credentials then (
post_form ~service:election_setup_credentials_server
(fun () ->
[string_input ~input_type:`Submit ~value:"Generate on server" ()]
) uuid
) else (
div [
a ~service:election_setup_credential_authority [pcdata "Credential management"] uuid;
]
);
]
)
]
in
let link_confirm = div [
h2 [pcdata "Finalize creation"];
a ~service:election_setup_confirm [pcdata "Create election"] uuid;
] in
let content = [
div_description;
hr ();
div_languages;
hr ();
div_questions;
hr ();
div_voters;
hr ();
div_credentials;
hr ();
div_auth;
hr ();
div_trustees;
hr ();
link_confirm;
] in
let%lwt login_box = site_login_box () in
base ~title ?login_box ~content ()
let mail_trustee_generation : ('a, 'b, 'c, 'd, 'e, 'f) format6 =
"Dear trustee,
You will find below the link to generate your private decryption key, used to tally the election.
%s
Here's the instructions:
1. click on the link
2. click on \"generate a new key pair\"
3. your private key will appear in another window or tab. Make sure
you SAVE IT properly otherwise it will not possible to tally and the
election will be canceled.
4. in the first window, click on \"submit\" to send the public part of
your key, used encrypt the votes. For verification purposes, you
should save this part (that starts with {\"pok\":{\"challenge\":\") ), for
example sending yourself an email.
Regarding your private key, it is crucial you save it (otherwise the
election will be canceled) and store it securely (if your private key
is known together with the private keys of the other trustees, then
vote privacy is no longer guaranteed). We suggest two options:
1. you may store the key on a USB stick and store it in a safe.
2. Or you may simply print it and store it in a safe.
Of course, more cryptographic solutions are welcome as well.
Thank you for your help,
-- \nThe election administrator."
let election_setup_trustees uuid se () =
let title = "Trustees for election " ^ se.se_questions.t_name in
let form_trustees_add =
post_form
~service:election_setup_trustee_add
(fun name ->
[
pcdata "Trustee's e-mail address: ";
string_input ~input_type:`Text ~name ();
string_input ~input_type:`Submit ~value:"Add" ();
]
) uuid
in
let mk_form_trustee_del value =
post_form
~service:election_setup_trustee_del
(fun name ->
[
int_input ~input_type:`Hidden ~name ~value ();
string_input ~input_type:`Submit ~value:"Remove" ();
]) uuid
in
let trustees = match se.se_public_keys with
| [] -> pcdata ""
| ts ->
table (
tr [
th [pcdata "Trustee"];
th [pcdata "Mail"];
th [pcdata "Link"];
th [pcdata "Done?"];
th [pcdata "Remove"];
] ::
List.mapi (fun i t ->
tr [
td [
pcdata t.st_id;
];
td [
let uri = rewrite_prefix @@ Eliom_uri.make_string_uri
~absolute:true ~service:election_setup_trustee t.st_token
in
let body = Printf.sprintf mail_trustee_generation uri in
let subject = "Link to generate the decryption key" in
a_mailto ~dest:t.st_id ~subject ~body "Mail"
];
td [
a ~service:election_setup_trustee [pcdata "Link"] t.st_token;
];
td [
pcdata (if t.st_public_key = "" then "No" else "Yes");
];
td [mk_form_trustee_del i];
]
) ts
)
in
let div_content =
div [
div [pcdata "If you do not wish the server to store any keys, you may nominate trustees. In that case, each trustee will create her own secret key. Be careful, once the election is over, you will need the contribution of each trustee to compute the result!"];
br ();
trustees;
(if se.se_public_keys <> [] then
div [
pcdata "There is one link per trustee. Send each trustee her link.";
br ();
br ();
]
else pcdata "");
form_trustees_add;
]
in
let import_link = div [
a ~service:Web_services.election_setup_import_trustees
[pcdata "Import trustees from another election"] uuid
]
in
let back_link = div [
a ~service:Web_services.election_setup
[pcdata "Go back to election setup"] uuid;
] in
let content = [
div_content;
import_link;
back_link;
] in
let%lwt login_box = site_login_box () in
base ~title ?login_box ~content ()
let election_setup_credential_authority _ se () =
let title = "Credentials for election " ^ se.se_questions.t_name in
let content = [
div [
pcdata "Please send the credential authority the following link:";
];
ul [
li [
a
~service:election_setup_credentials
[
pcdata @@ rewrite_prefix @@ Eliom_uri.make_string_uri
~absolute:true
~service:election_setup_credentials
se.se_public_creds
]
se.se_public_creds;
];
];
div [
pcdata "Note that this authority will have to send each credential to each voter herself.";
];
] in
let%lwt login_box = site_login_box () in
base ~title ?login_box ~content ()
let election_setup_questions uuid se () =
let title = "Questions for election " ^ se.se_questions.t_name in
let form =
let value = string_of_template se.se_questions in
post_form
~service:election_setup_questions_post
(fun name ->
[
div [pcdata "Questions:"];
div [textarea ~a:[a_id "questions"; a_rows 5; a_cols 80] ~name ~value ()];
div [string_input ~input_type:`Submit ~value:"Save changes" ()]])
uuid
in
let interactivity =
div
~a:[a_id "interactivity"]
[
script ~a:[a_src (uri_of_string (fun () -> "../static/sjcl.js"))] (pcdata "");
script ~a:[a_src (uri_of_string (fun () -> "../static/jsbn.js"))] (pcdata "");
script ~a:[a_src (uri_of_string (fun () -> "../static/jsbn2.js"))] (pcdata "");
script ~a:[a_src (uri_of_string (fun () -> "../static/random.js"))] (pcdata "");
script ~a:[a_src (uri_of_string (fun () -> "../static/tool_js_questions.js"))] (pcdata "");
]
in
let content = [
interactivity;
form;
] in
let%lwt login_box = site_login_box () in
base ~title ?login_box ~content ()
let election_setup_voters uuid se maxvoters () =
let title = "Voters for election " ^ se.se_questions.t_name in
let form =
post_form
~service:election_setup_voters_add
(fun name ->
[
div [textarea ~a:[a_rows 20; a_cols 50] ~name ()];
div [string_input ~input_type:`Submit ~value:"Add" ()]])
uuid
in
let mk_remove_button id =
post_form
~service:election_setup_voters_remove
(fun name ->
[
string_input ~input_type:`Hidden ~name ~value:id ();
string_input ~input_type:`Submit ~value:"Remove" ();
]
) uuid
in
let has_passwords = match se.se_metadata.e_auth_config with
| Some [{auth_system = "password"; _}] -> true
| _ -> false
in
let mk_regen_passwd value =
post_form ~service:election_setup_voters_passwd
~a:[a_style "display: inline;"]
(fun name ->
[
string_input ~input_type:`Hidden ~name ~value ();
string_input ~input_type:`Submit ~value:"Send again" ();
]
) uuid
in
let format_password_cell x = match x.sv_password with
| Some _ -> [pcdata "Yes "; mk_regen_passwd x.sv_id]
| None -> [pcdata "No"]
in
let voters =
List.map (fun v ->
tr (
[td [pcdata v.sv_id]] @
(if has_passwords then [td (format_password_cell v)] else []) @
(if se.se_public_creds_received then [] else [td [mk_remove_button v.sv_id]])
)
) se.se_voters
in
let form_passwords =
if has_passwords then
post_form ~service:election_setup_auth_genpwd
(fun () ->
[string_input ~input_type:`Submit ~value:"Generate and mail missing passwords" ()]
) uuid
else pcdata ""
in
let voters =
match voters with
| [] -> div [pcdata "No voters"]
| _ :: _ ->
div [
form_passwords;
br ();
table
(tr (
[th [pcdata "Identity"]] @
(if has_passwords then [th [pcdata "Password sent?"]] else []) @
(if se.se_public_creds_received then [] else [th [pcdata "Remove"]])
) :: voters)
]
in
let back = div [
a ~service:Web_services.election_setup [pcdata "Return to setup page"] uuid;
] in
let div_add =
if se.se_public_creds_received then
pcdata ""
else
div [
div [
pcdata "Please enter the identities of voters to add, one per line (max ";
pcdata (string_of_int maxvoters);
pcdata "):"
];
form;
div [
b [pcdata "Note:"];
pcdata " An identity is either an e-mail address, or \"address,login\",";
pcdata " where \"address\" is an e-mail address and \"login\" the";
pcdata " associated login for authentication.";
];
]
in
let div_import = div [
a ~service:election_setup_import
[pcdata "Import voters from another election"]
uuid
] in
let content = [
back;
div_import;
br ();
voters;
div_add;
] in
let%lwt login_box = site_login_box () in
base ~title ?login_box ~content ()
let election_setup_credentials token uuid se () =
let title = "Credentials for election " ^ se.se_questions.t_name in
let div_link =
let url = Eliom_uri.make_string_uri ~absolute:true
~service:election_home (uuid, ()) |> rewrite_prefix
in
div [
pcdata "The link to the election will be:";
ul [li [pcdata url]];
]
in
let form_textarea =
post_form
~service:election_setup_credentials_post
(fun name ->
[div
[div [pcdata "Public credentials:"];
div [textarea ~a:[a_id "pks"; a_rows 5; a_cols 40] ~name ()];
div [string_input ~input_type:`Submit ~value:"Submit" ()]]])
token
in
let disclaimer =
p
[
b [pcdata "Note:"];
pcdata " submitting a large (> 200) number of credentials using the above form may fail; in this case, you have to use the command-line tool and the form below.";
]
in
let form_file =
post_form
~service:election_setup_credentials_post_file
(fun name ->
[div
[h2 [pcdata "Submit by file"];
div [pcdata "Use this form to upload public credentials generated with the command-line tool."];
div [file_input ~name ()];
div [string_input ~input_type:`Submit ~value:"Submit" ()]]])
token
in
let div_download =
p [a ~service:election_setup_credentials_download
[pcdata "Download current file"]
token]
in
let group =
let name : 'a Eliom_parameter.param_name = Obj.magic "group" in
let value = se.se_group in
div
~a:[a_style "display:none;"]
[
div [pcdata "UUID:"];
div [textarea ~a:[a_id "uuid"; a_rows 1; a_cols 40; a_readonly `ReadOnly] ~name ~value:(Uuidm.to_string uuid) ()];
div [pcdata "Group parameters:"];
div [textarea ~a:[a_id "group"; a_rows 5; a_cols 40; a_readonly `ReadOnly] ~name ~value ()];
]
in
let voters =
let name : 'a Eliom_parameter.param_name = Obj.magic "voters" in
let value = String.concat "\n" (List.map (fun x -> x.sv_id) se.se_voters) in
div [
div [pcdata "List of voters:"];
div [textarea ~a:[a_id "voters"; a_rows 5; a_cols 40; a_readonly `ReadOnly] ~name ~value ()];
]
in
let interactivity =
div
~a:[a_id "interactivity"]
[
script ~a:[a_src (uri_of_string (fun () -> "../static/sjcl.js"))] (pcdata "");
script ~a:[a_src (uri_of_string (fun () -> "../static/jsbn.js"))] (pcdata "");
script ~a:[a_src (uri_of_string (fun () -> "../static/jsbn2.js"))] (pcdata "");
script ~a:[a_src (uri_of_string (fun () -> "../static/random.js"))] (pcdata "");
script ~a:[a_src (uri_of_string (fun () -> "../static/tool_js_credgen.js"))] (pcdata "");
]
in
let div_textarea = div [group; voters; interactivity; form_textarea; disclaimer] in
let content =
if se.se_public_creds_received then (
[
div [pcdata "Credentials have already been generated!"];
]
) else (
[
div_link;
div_download;
div_textarea;
form_file;
]
) in
base ~title ~content ()
let election_setup_trustee token uuid se () =
let title = "Trustee for election " ^ se.se_questions.t_name in
let div_link =
let url = Eliom_uri.make_string_uri ~absolute:true
~service:election_home (uuid, ()) |> rewrite_prefix
in
div [
pcdata "The link to the election will be:";
ul [li [pcdata url]];
]
in
let form =
let trustee = List.find (fun x -> x.st_token = token) se.se_public_keys in
let value = trustee.st_public_key in
let service = Eliom_service.preapply election_setup_trustee_post token in
post_form
~service
(fun name ->
[
div [
div [pcdata "Public key:"];
div [textarea ~a:[a_rows 5; a_cols 40; a_id "pk"] ~name ~value ()];
div [string_input ~input_type:`Submit ~value:"Submit" ()];
]
]
) ()
in
let group =
let name : 'a Eliom_parameter.param_name = Obj.magic "group" in
let value = se.se_group in
div
~a:[a_style "display:none;"]
[
div [pcdata "Group parameters:"];
div [textarea ~a:[a_id "group"; a_rows 5; a_cols 40; a_readonly `ReadOnly] ~name ~value ()];
]
in
let interactivity =
div
~a:[a_id "interactivity"]
[
script ~a:[a_src (uri_of_string (fun () -> "../static/sjcl.js"))] (pcdata "");
script ~a:[a_src (uri_of_string (fun () -> "../static/jsbn.js"))] (pcdata "");
script ~a:[a_src (uri_of_string (fun () -> "../static/jsbn2.js"))] (pcdata "");
script ~a:[a_src (uri_of_string (fun () -> "../static/random.js"))] (pcdata "");
script ~a:[a_src (uri_of_string (fun () -> "../static/tool_js_tkeygen.js"))] (pcdata "");
]
in
let content = [
div_link;
group;
interactivity;
form;
] in
base ~title ~content ()
let election_setup_importer ~service ~title uuid (elections, tallied, archived) () =
let format_election election =
let module W = (val election : ELECTION_DATA) in
let name = W.election.e_params.e_name in
let uuid_s = Uuidm.to_string W.election.e_params.e_uuid in
let form = post_form ~service
(fun from ->
[
div [pcdata name; pcdata " ("; pcdata uuid_s; pcdata ")"];
div [
user_type_input Uuidm.to_string
~input_type:`Hidden
~name:from
~value:W.election.e_params.e_uuid ();
string_input ~input_type:`Submit ~value:"Import from this election" ();
]
]
) uuid
in
li [form]
in
let itemize xs = match xs with
| [] -> p [pcdata "You own no such elections!"]
| _ -> ul @@ List.map format_election xs
in
let content = [
h2 [pcdata "Elections you can administer"];
itemize elections;
h2 [pcdata "Tallied elections"];
itemize tallied;
h2 [pcdata "Archived elections"];
itemize archived;
] in
let%lwt login_box = site_login_box () in
base ~title ?login_box ~content ()
let election_setup_import uuid se elections =
let title = "Election " ^ se.se_questions.t_name ^ " — Import voters from another election" in
let service = election_setup_import_post in
election_setup_importer ~service ~title uuid elections
let election_setup_import_trustees uuid se elections =
let title = "Election " ^ se.se_questions.t_name ^ " — Import trustees from another election" in
let service = election_setup_import_trustees_post in
election_setup_importer ~service ~title uuid elections
let election_setup_confirm uuid se () =
let title = "Election " ^ se.se_questions.t_name ^ " — Finalize creation" in
let voters = Printf.sprintf "%d voter(s)" (List.length se.se_voters) in
let ready = not (se.se_voters = []) in
let ready, passwords =
match se.se_metadata.e_auth_config with
| Some [{auth_system = "password"; _}] ->
if List.for_all (fun v -> v.sv_password <> None) se.se_voters then ready, "OK"
else false, "Missing"
| _ -> ready, "Not applicable"
in
let ready, credentials =
if se.se_public_creds_received then
ready, if se.se_metadata.e_cred_authority = None then "Received" else "Sent"
else false, "Missing"
in
let ready, trustees =
match se.se_public_keys with
| [] -> ready, "OK"
| _ :: _ ->
if List.for_all (fun {st_public_key; _} ->
st_public_key <> ""
) se.se_public_keys then ready, "OK" else false, "Missing"
in
let div_trustee_warning =
match se.se_public_keys with
| [] ->
div [
b [pcdata "Warning:"];
pcdata " No trustees were set. This means that the server will manage the election key by itself.";
]
| _ :: _ -> pcdata ""
in
let table_checklist = table [
tr [
td [pcdata "Voters?"];
td [pcdata voters];
];
tr [
td [pcdata "Passwords?"];
td [pcdata passwords];
];
tr [
td [pcdata "Credentials?"];
td [pcdata credentials];
];
tr [
td [pcdata "Trustees?"];
td [pcdata trustees];
]
] in
let checklist = div [
h2 [pcdata "Checklist"];
table_checklist;
div_trustee_warning;
] in
let form_create =
if ready then
post_form
~service:election_setup_create
(fun () ->
[div
[h2 [pcdata "Finalize creation"];
string_input ~input_type:`Submit ~value:"Create election" ();
pcdata " (Warning: this action is irreversible.)";
]]
) uuid
else div []
in
let back = div [
a ~service:Web_services.election_setup [pcdata "Return to setup page"] uuid;
] in
let content = [
back;
checklist;
form_create;
] in
let%lwt login_box = site_login_box () in
base ~title ?login_box ~content ()
let election_login_box w =
let module W = (val w : ELECTION_DATA) in
let module A = struct
let get_user () =
Web_state.get_election_user W.election.e_params.e_uuid
let get_auth_systems () =
let uuid_s = Uuidm.to_string W.election.e_params.e_uuid in
let%lwt l = Web_persist.get_auth_config uuid_s in
return @@ List.map fst l
end in
let auth = (module A : AUTH_SERVICES) in
let module L = struct
let login x =
Eliom_service.preapply
election_login
((W.election.e_params.e_uuid, ()), x)
let logout =
Eliom_service.preapply logout ()
end in
let links = (module L : AUTH_LINKS) in
fun () -> make_login_box ~site:false auth links
let file w x =
let module W = (val w : ELECTION_DATA) in
Eliom_service.preapply
election_dir
(W.election.e_params.e_uuid, x)
let audit_footer w =
let%lwt language = Eliom_reference.get Web_state.language in
let module L = (val Web_i18n.get_lang language) in
let module W = (val w : ELECTION_DATA) in
return @@ div ~a:[a_style "line-height:1.5em;"] [
div [
div [
pcdata L.election_fingerprint;
code [ pcdata W.election.e_fingerprint ];
];
div [
pcdata L.audit_data;
a ~service:(file w ESRaw) [
pcdata L.parameters
] ();
pcdata ", ";
a ~service:(file w ESKeys) [
pcdata L.trustee_public_keys
] ();
pcdata ", ";
a ~service:(file w ESCreds) [
pcdata L.public_credentials
] ();
pcdata ", ";
a ~service:(file w ESBallots) [
pcdata L.ballots
] ();
pcdata ".";
];
]
]
let rec list_concat elt = function
| x :: ((_ :: _) as xs) -> x :: elt :: (list_concat elt xs)
| ([_] | []) as xs -> xs
let election_home w state () =
let%lwt language = Eliom_reference.get Web_state.language in
let module L = (val Web_i18n.get_lang language) in
let module W = (val w : ELECTION_DATA) in
let params = W.election.e_params in
let state_ =
match state with
| `Closed ->
[
pcdata " ";
b [pcdata L.election_currently_closed];
]
| `Open -> []
| `EncryptedTally (_, _, hash) ->
[
pcdata " ";
b [pcdata L.election_closed_being_tallied];
pcdata L.the;
a
~service:election_dir
[pcdata L.encrypted_tally]
(W.election.e_params.e_uuid, ESETally);
pcdata L.hash_is;
b [pcdata hash];
pcdata ".";
]
| `Tallied _ ->
[
pcdata " ";
b [pcdata L.election_has_been_tallied];
]
| `Archived ->
[
pcdata " ";
b [pcdata L.election_archived];
]
in
let ballots_link =
p ~a:[a_style "text-align:center;"] [
a
~a:[a_style "font-size:25px;"]
~service:election_pretty_ballots [
pcdata L.see_accepted_ballots
] (params.e_uuid, ())
]
in
let%lwt footer = audit_footer w in
let go_to_the_booth =
let disabled = match state with
| `Open -> false
| _ -> true
in
div ~a:[a_style "text-align:center;"] [
div [
make_button
~service:(Eliom_service.preapply election_vote (params.e_uuid, ()))
~disabled L.start;
];
div [
a
~service:(Eliom_service.preapply election_cast (params.e_uuid, ()))
[pcdata L.advanced_mode] ();
];
]
in
let%lwt middle =
let uuid = Uuidm.to_string params.e_uuid in
let%lwt result = Web_persist.get_election_result uuid in
match result with
| Some r ->
let result = r.result in
let questions = Array.to_list W.election.e_params.e_questions in
return @@ div [
ul (List.mapi (fun i x ->
let answers = Array.to_list x.q_answers in
let answers = match x.q_blank with
| Some true -> L.blank_vote :: answers
| _ -> answers
in
let answers = List.mapi (fun j x ->
tr [td [pcdata x]; td [pcdata @@ string_of_int result.(i).(j)]]
) answers in
let answers =
match answers with
| [] -> pcdata ""
| y :: ys ->
match x.q_blank with
| Some true -> table (ys @ [y])
| _ -> table (y :: ys)
in
li [
pcdata x.q_question;
answers;
]
) questions);
div [
pcdata L.number_accepted_ballots;
pcdata (string_of_int r.num_tallied);
];
div [
pcdata L.you_can_also_download;
a ~service:election_dir
[pcdata L.result_with_crypto_proofs]
(W.election.e_params.e_uuid, ESResult);
pcdata ".";
];
]
| None -> return go_to_the_booth
in
let languages =
div ~a:[a_class ["languages"]]
(list_concat (pcdata " ") @@ List.map (fun lang ->
a ~service:set_language [pcdata lang] lang
) available_languages)
in
let%lwt scd = Eliom_reference.get Web_state.show_cookie_disclaimer in
let cookie_disclaimer =
if scd then
div
~a:[a_style "border-style: solid; border-width: 1px;"]
[
pcdata L.you_must_accept_cookies;
a ~service:set_cookie_disclaimer [pcdata L.accept] ();
]
else pcdata ""
in
let content = [
cookie_disclaimer;
languages;
p state_;
br ();
middle;
br ();
ballots_link;
] in
let%lwt login_box = election_login_box w () in
let uuid = params.e_uuid in
base ~title:params.e_name ?login_box ~content ~footer ~uuid ()
let mail_trustee_tally : ('a, 'b, 'c, 'd, 'e, 'f) format6 =
"Dear trustee,
The election is now closed. Here's the link to proceed to tally:
%s
Here's the instructions:
1. Follow the link.
2. Enter your private decryption key in the first box and click on
\"generate decryption factors\"
3. The second box is now filled with crypto material. Please press the
button \"submit\".
Thank you again for your help,
-- \nThe election administrator."
let election_admin w metadata state () =
let module W = (val w : ELECTION_DATA) in
let title = W.election.e_params.e_name ^ " — Administration" in
let uuid_s = Uuidm.to_string W.election.e_params.e_uuid in
let state_form checked =
let service, value, msg =
if checked then
election_close, "Close election",
"The election is open. Voters can vote. "
else
election_open, "Open election",
"The election is closed. No one can vote. "
in
post_form
~service
(fun () ->
[
pcdata msg;
string_input ~input_type:`Submit ~value ();
]) (W.election.e_params.e_uuid, ())
in
let%lwt state_div =
match state with
| `Open ->
return @@ div [
state_form true;
]
| `Closed ->
return @@ div [
state_form false;
br ();
post_form
~service:election_compute_encrypted_tally
(fun () ->
[string_input
~input_type:`Submit
~value:"Tally election"
();
pcdata " Warning: this action is irreversible; the election will be definitively closed.";
]) (W.election.e_params.e_uuid, ());
]
| `EncryptedTally (npks, _, hash) ->
let%lwt pds = Web_persist.get_partial_decryptions uuid_s in
let trustees =
let rec loop i ts =
if i <= npks then
match ts with
| t :: ts -> (Some t, i) :: (loop (i+1) ts)
| [] -> (None, i) :: (loop (i+1) ts)
else []
in
match metadata.e_trustees with
| None -> loop 1 []
| Some ts -> loop 1 ts
in
let trustees =
List.map
(fun (name, trustee_id) ->
let service = election_tally_trustees in
let x = (W.election.e_params.e_uuid, ((), trustee_id)) in
let uri = rewrite_prefix @@ Eliom_uri.make_string_uri
~absolute:true ~service x
in
let link_content, dest = match name with
| None -> uri, "toto@example.org"
| Some name -> name, name
in
tr [
td [pcdata link_content];
td [
let body = Printf.sprintf mail_trustee_tally uri in
let subject = "Link to tally the election" in
a_mailto ~dest ~subject ~body "Mail"
];
td [
a ~service [pcdata "Link"] x;
];
td [
pcdata (if List.mem_assoc trustee_id pds then "Yes" else "No")
];
]
) trustees
in
let release_form =
post_form
~service:election_tally_release
(fun () ->
[string_input
~input_type:`Submit
~value:"Compute the result"
()
]) (W.election.e_params.e_uuid, ())
in
return @@ div [
div [
pcdata "The ";
a
~service:election_dir
[pcdata "encrypted tally"]
(W.election.e_params.e_uuid, ESETally);
pcdata " has been computed. Its hash is ";
b [pcdata hash];
pcdata ".";
];
div [
div [pcdata "We are now waiting for trustees..."];
table
(tr [
th [pcdata "Trustee"];
th [pcdata "Mail"];
th [pcdata "Link"];
th [pcdata "Done?"];
] :: trustees)
];
release_form;
]
| `Tallied _ ->
return @@ div [
pcdata "This election has been tallied.";
]
| `Archived ->
return @@ div [
pcdata "This election is archived.";
]
in
let div_archive = match state with
| `Archived -> pcdata ""
| _ -> div [
br ();
hr ();
post_form ~service:election_archive (fun () ->
[
string_input ~input_type:`Submit ~value:"Archive election" ();
pcdata " Warning: this action is irreversible. Archiving an election makes it read-only; in particular, the election will be definitively closed (no vote submission, no tally).";
]
) (W.election.e_params.e_uuid, ());
]
in
let uuid = W.election.e_params.e_uuid in
let update_credential =
match metadata.e_cred_authority with
| Some "server" ->
pcdata ""
| _ ->
div [
a ~service:election_update_credential [pcdata "Update a credential"] (uuid, ());
];
in
let content = [
div [
a ~service:Web_services.election_home [pcdata "Election home"] (uuid, ());
];
update_credential;
div [
a ~service:election_dir [pcdata "Voter list"] (uuid, ESVoters);
];
div [
a ~service:election_pretty_records [pcdata "Voting records"] (uuid, ());
];
div [
a ~service:election_missing_voters [pcdata "Missing voters"] (uuid, ());
];
div [
a ~service:election_regenpwd [pcdata "Regenerate and mail a password"] (uuid, ());
];
div [state_div];
div_archive;
] in
let%lwt login_box = site_login_box () in
base ~title ?login_box ~content ()
let update_credential w () =
let module W = (val w : ELECTION_DATA) in
let params = W.election.e_params in
let form = post_form ~service:election_update_credential_post
(fun (old, new_) ->
[
div [
p [
pcdata "\
This form allows you to change a single credential at \
a time. To get the hash of a credential, run the \
following command:\
";
];
pre [
pcdata "printf old-credential | sha256sum";
];
p [
pcdata "In the above command, ";
code [pcdata "old-credential"];
pcdata " should look like a big number written in base 10.";
];
];
p [
pcdata "Hash of the old credential: ";
string_input ~name:old ~input_type:`Text ~a:[a_size 64] ();
];
p [
pcdata "New credential: ";
string_input ~name:new_ ~input_type:`Text ~a:[a_size 617] ();
];
p [string_input ~input_type:`Submit ~value:"Submit" ()];
]
) (params.e_uuid, ())
in
let content = [
form;
] in
let%lwt login_box = site_login_box () in
let uuid = W.election.e_params.e_uuid in
base ~title:params.e_name ?login_box ~content ~uuid ()
let regenpwd uuid () =
let form = post_form ~service:election_regenpwd_post
(fun user ->
[
div [
pcdata "Username: ";
string_input ~name:user ~input_type:`Text ();
];
div [string_input ~input_type:`Submit ~value:"Submit" ()];
]
) (uuid, ())
in
let content = [ form ] in
let title = "Regenerate and mail password" in
let%lwt login_box = site_login_box () in
base ~title ?login_box ~content ~uuid ()
let cast_raw w () =
let module W = (val w : ELECTION_DATA) in
let params = W.election.e_params in
let form_rawballot = post_form ~service:election_cast_post
(fun (name, _) ->
[
div [pcdata "Please paste your encrypted ballot in JSON format in the following box:"];
div [textarea ~a:[a_rows 10; a_cols 40] ~name ()];
div [string_input ~input_type:`Submit ~value:"Submit" ()];
]
) (params.e_uuid, ())
in
let form_upload = post_form ~service:election_cast_post
(fun (_, name) ->
[
div [pcdata "Alternatively, you can also upload a file containing your ballot:"];
div [
pcdata "File: ";
file_input ~name ();
];
div [string_input ~input_type:`Submit ~value:"Submit" ()];
]
) (params.e_uuid, ())
in
let intro = div [
div [
pcdata "You can create an encrypted ballot by using the command line tool ";
pcdata "(available in the ";
a ~service:source_code [pcdata "sources"] ();
pcdata "), or its ";
a ~service:(Eliom_service.static_dir ()) [
pcdata "web interface";
] ["static"; "belenios-tool.html"];
pcdata ". A specification of encrypted ballots is also available in the ";
pcdata "sources.";
];
div [
a ~service:Web_services.election_home
[pcdata "Back to election home"] (params.e_uuid, ());
];
] in
let content = [
intro;
h3 [ pcdata "Submit by copy/paste" ];
form_rawballot;
h3 [ pcdata "Submit by file" ];
form_upload;
] in
let%lwt login_box = election_login_box w () in
let uuid = W.election.e_params.e_uuid in
let%lwt footer = audit_footer w in
base ~title:params.e_name ?login_box ~content ~uuid ~footer ()
let cast_confirmation w hash () =
let%lwt language = Eliom_reference.get Web_state.language in
let module L = (val Web_i18n.get_lang language) in
let module W = (val w : ELECTION_DATA) in
let%lwt user = Web_state.get_election_user W.election.e_params.e_uuid in
let params = W.election.e_params in
let name = params.e_name in
let user_div = match user with
| Some u ->
post_form ~service:election_cast_confirm (fun () -> [
p ~a:[a_style "text-align: center; padding: 10px;"] [
pcdata L.i_am;
format_user ~site:false u;
pcdata L.and_;
string_input
~a:[a_style "font-size: 20px; cursor: pointer;"]
~input_type:`Submit ~value:L.i_cast_my_vote ();
pcdata ".";
]
]) (params.e_uuid, ())
| None ->
div [
pcdata L.please_login_to_confirm;
]
in
let progress = div ~a:[a_style "text-align:center;margin-bottom:20px;"] [
pcdata L.input_credential;
pcdata " — ";
pcdata L.answer_to_questions;
pcdata " — ";
pcdata L.review_and_encrypt;
pcdata " — ";
pcdata L.authenticate;
pcdata " — ";
b [pcdata L.confirm];
pcdata " — ";
pcdata L.done_;
hr ();
] in
let content = [
progress;
div ~a:[a_class ["current_step"]] [
pcdata L.booth_step5;
];
p [
pcdata L.your_ballot_for;
em [pcdata name];
pcdata L.has_been_received;
pcdata L.your_tracker_is;
b [pcdata hash];
pcdata ".";
br ();
];
br ();
p [pcdata L.nobody_can_see];
user_div;
p [
(let service =
Eliom_service.preapply
Web_services.election_home (W.election.e_params.e_uuid, ())
in
a ~service [
pcdata L.go_back_to_election
] ());
pcdata ".";
];
] in
let uuid = params.e_uuid in
base ~title:name ~content ~uuid ()
let cast_confirmed w ~result () =
let%lwt language = Eliom_reference.get Web_state.language in
let module L = (val Web_i18n.get_lang language) in
let module W = (val w : ELECTION_DATA) in
let params = W.election.e_params in
let name = params.e_name in
let progress = div ~a:[a_style "text-align:center;margin-bottom:20px;"] [
pcdata L.input_credential;
pcdata " — ";
pcdata L.answer_to_questions;
pcdata " — ";
pcdata L.review_and_encrypt;
pcdata " — ";
pcdata L.authenticate;
pcdata " — ";
pcdata L.confirm;
pcdata " — ";
b [pcdata L.done_];
hr ();
] in
let result, step_title =
match result with
| `Valid hash ->
[pcdata L.has_been_accepted;
pcdata " ";
pcdata L.your_tracker_is;
b [pcdata hash];
pcdata ". ";
pcdata L.you_can_check_its_presence;
a ~service:election_pretty_ballots [pcdata L.ballot_box] (params.e_uuid, ());
pcdata L.anytime_during_the_election;
pcdata L.confirmation_email;
], L.thank_you_for_voting
| `Error e ->
[pcdata L.is_rejected_because;
pcdata (Web_common.explain_error e);
pcdata ".";
], L.fail
in
let content = [
progress;
div ~a:[a_class ["current_step"]] [
pcdata L.booth_step6;
pcdata step_title;
];
p ([
pcdata L.your_ballot_for;
em [pcdata name];
] @ result);
p
[a
~service:Web_services.election_home
[pcdata L.go_back_to_election]
(params.e_uuid, ())];
] in
let uuid = params.e_uuid in
base ~title:name ~content ~uuid ()
let pretty_ballots w hashes result () =
let%lwt language = Eliom_reference.get Web_state.language in
let module L = (val Web_i18n.get_lang language) in
let module W = (val w : ELECTION_DATA) in
let params = W.election.e_params in
let title = params.e_name ^ " — " ^ L.accepted_ballots in
let nballots = ref 0 in
let hashes = List.sort compare_b64 hashes in
let ballots =
List.map
(fun h ->
incr nballots;
li
[a
~service:election_pretty_ballot
[pcdata h]
((params.e_uuid, ()), h)]
) hashes
in
let links =
p
[a
~service:Web_services.election_home
[pcdata L.go_back_to_election]
(params.e_uuid, ())]
in
let number = match !nballots, result with
| n, None ->
div [
pcdata (string_of_int n);
pcdata L.ballots_have_been_accepted_so_far;
]
| n, Some r when n = r.num_tallied ->
div [
pcdata (string_of_int n);
pcdata L.ballots_have_been_accepted;
]
| n, Some r -> (* should not happen *)
div [
pcdata (string_of_int n);
pcdata L.ballots_have_been_accepted_and;
pcdata (string_of_int r.num_tallied);
pcdata L.have_been_tallied;
]
in
let content = [
number;
ul ballots;
links;
] in
let%lwt login_box = election_login_box w () in
let uuid = params.e_uuid in
base ~title ?login_box ~content ~uuid ()
let pretty_records w records () =
let module W = (val w : ELECTION_DATA) in
let uuid = W.election.e_params.e_uuid in
let title = W.election.e_params.e_name ^ " — Records" in
let records = List.map (fun (date, voter) ->
tr [td [pcdata date]; td [pcdata voter]]
) records in
let table = match records with
| [] -> div [pcdata "Nobody voted!"]
| _ ->
div [
table
(tr [th [pcdata "Date/Time (UTC)"]; th [pcdata "Username"]]
:: records);
]
in
let content = [
div [
pcdata "You can also access the ";
a ~service:election_dir [pcdata "raw data"] (uuid, ESRecords);
pcdata ".";
];
table;
] in
let%lwt login_box = site_login_box () in
base ~title ?login_box ~content ()
let tally_trustees w trustee_id () =
let module W = (val w : ELECTION_DATA) in
let params = W.election.e_params in
let title =
params.e_name ^ " — Partial decryption #" ^ string_of_int trustee_id
in
let content = [
p [pcdata "It is now time to compute your partial decryption factors."];
p [
pcdata "The hash of the encrypted tally is ";
b [span ~a:[a_id "hash"] []];
pcdata "."
];
div ~a:[a_id "input_private_key"] [
p [pcdata "Please enter your private key:"];
input
~a:[a_id "private_key"; a_size 80]
~input_type:`Text
();
button
~a:[a_id "compute"]
~button_type:`Button
[pcdata "Compute decryption factors"];
];
div ~a:[a_id "pd_done"] [
post_form
~service:election_tally_trustees_post
(fun pd ->
[
div [
textarea
~a:[a_rows 5; a_cols 40; a_id "pd"]
~name:pd
();
];
div [string_input ~input_type:`Submit ~value:"Submit" ()];
]
) (params.e_uuid, ((), trustee_id));
];
div [
script ~a:[a_src (uri_of_string (fun () -> "../../../static/sjcl.js"))] (pcdata "");
script ~a:[a_src (uri_of_string (fun () -> "../../../static/jsbn.js"))] (pcdata "");
script ~a:[a_src (uri_of_string (fun () -> "../../../static/jsbn2.js"))] (pcdata "");
script ~a:[a_src (uri_of_string (fun () -> "../../../static/random.js"))] (pcdata "");
script ~a:[a_src (uri_of_string (fun () -> "../../../static/tool_js_pd.js"))] (pcdata "");
]
] in
let uuid = params.e_uuid in
base ~title ~content ~uuid ()
let already_logged_in () =
let title = "Already logged in" in
let content = [
div [
pcdata "You are already logged in as an administrator or on another election. You have to ";
a ~service:logout [pcdata "log out"] ();
pcdata " first."];
] in
base ~title ~content ()
let login_choose auth_systems service () =
let auth_systems =
auth_systems |>
List.map (fun name ->
a ~service:(service name) [pcdata name] ()
) |> list_join (pcdata ", ")
in
let content = [
div [p (
[pcdata "Please log in: ["] @ auth_systems @ [pcdata "]"]
)]
] in
base ~title:"Log in" ~content ()
let login_dummy () =
let title, field_name, input_type =
"Dummy login", "Username:", `Text
in
let form = post_form ~service:dummy_post
(fun name ->
[
tablex [tbody [
tr [
th [label ~a:[a_for name] [pcdata field_name]];
td [string_input ~a:[a_maxlength 50] ~input_type ~name ()];
]]
];
div [
string_input ~input_type:`Submit ~value:"Login" ();
]
]) ()
in
let content = [
form;
] in
base ~title ~content ()
let login_password () =
let%lwt language = Eliom_reference.get Web_state.language in
let module L = (val Web_i18n.get_lang language) in
let form = post_form ~service:password_post
(fun (llogin, lpassword) ->
[
tablex [tbody [
tr [
th [label ~a:[a_for llogin] [pcdata L.username]];
td [string_input ~a:[a_maxlength 50] ~input_type:`Text ~name:llogin ()];
];
tr [
th [label ~a:[a_for lpassword] [pcdata L.password]];
td [string_input ~a:[a_maxlength 50] ~input_type:`Password ~name:lpassword ()];
];
]];
div [
string_input ~input_type:`Submit ~value:L.login ();
]
]) ()
in
let content = [
form;
] in
base ~title:L.password_login ~content ()
let booth () =
let%lwt language = Eliom_reference.get Web_state.language in
let module L = (val Web_i18n.get_lang language) in
let head = head (title (pcdata L.belenios_booth)) [
link ~rel:[`Stylesheet] ~href:(uri_of_string (fun () -> "/static/booth.css")) ();
script ~a:[a_src (uri_of_string (fun () -> "/static/sjcl.js"))] (pcdata "");
script ~a:[a_src (uri_of_string (fun () -> "/static/jsbn.js"))] (pcdata "");
script ~a:[a_src (uri_of_string (fun () -> "/static/jsbn2.js"))] (pcdata "");
script ~a:[a_src (uri_of_string (fun () -> "/static/random.js"))] (pcdata "");
script ~a:[a_src (uri_of_string (fun () -> "/static/booth.js"))] (pcdata "");
] in
let election_loader =
let name : 'a Eliom_parameter.param_name = Obj.magic "election_params" in
div ~a:[a_id "election_loader"; a_style "display:none;"] [
h1 [pcdata "Election loader"];
pcdata "Election parameters:";
div [textarea ~name ~a:[a_id "election_params"; a_rows 1; a_cols 80] ()];
div [button ~button_type:`Button ~a:[a_id "load_election"] [pcdata "Load election"]];
]
in
let text_choices =
let name : 'a Eliom_parameter.param_name = Obj.magic "choices" in
textarea ~name ~a:[a_id "choices"; a_rows 1; a_cols 80; a_readonly `ReadOnly] ()
in
let ballot_form =
post_form ~a:[a_id "ballot_form"] ~service:election_cast_post
(fun (encrypted_vote, _) -> [
div ~a:[a_style "display:none;"] [
pcdata "Encrypted ballot:";
div [
textarea
~a:[a_id "ballot"; a_rows 1; a_cols 80; a_readonly `ReadOnly]
~name:encrypted_vote ();
];
];
p [
pcdata L.successfully_encrypted;
b [pcdata L.not_cast_yet];
pcdata L.qmark;
];
p [
pcdata L.your_tracker_is;
span ~a:[a_id "ballot_tracker"] [];
];
p [
pcdata L.we_invite_you_to_save_it;
];
br ();
string_input ~input_type:`Submit ~value:L.continue ~a:[a_style "font-size:30px;"] ();
br (); br ();
])
(Uuidm.nil, ())
in
let main =
div ~a:[a_id "main"] [
div ~a:[a_style "text-align:center; margin-bottom:20px;"] [
span ~a:[a_id "progress1"; a_style "font-weight:bold;"] [pcdata L.input_credential];
pcdata " — ";
span ~a:[a_id "progress2"] [pcdata L.answer_to_questions];
pcdata " — ";
span ~a:[a_id "progress3"] [pcdata L.review_and_encrypt];
pcdata " — ";
span ~a:[a_id "progress4"] [pcdata L.authenticate];
pcdata " — ";
span ~a:[a_id "progress5"] [pcdata L.confirm];
pcdata " — ";
span ~a:[a_id "progress6"] [pcdata L.done_];
hr ();
];
div ~a:[a_id "intro"; a_style "text-align:center;"] [
div ~a:[a_class ["current_step"]] [
pcdata L.booth_step1;
];
br (); br ();
p ~a:[a_id "input_code"; a_style "font-size:20px;"] [
pcdata L.input_your_credential;
];
br (); br ();
];
div ~a:[a_id "question_div"; a_style "display:none;"] [
div ~a:[a_class ["current_step"]] [
pcdata L.booth_step2;
];
];
div ~a:[a_id "plaintext_div"; a_style "display:none;"] [
div ~a:[a_class ["current_step"]] [
pcdata L.booth_step3;
];
div ~a:[a_id "pretty_choices"] [];
div ~a:[a_style "display:none;"] [
pcdata "Plaintext raw ballot:";
div [text_choices];
];
div ~a:[a_style "text-align:center;"] [
div ~a:[a_id "encrypting_div"] [
p [pcdata L.wait_while_encrypted];
img ~src:(uri_of_string (fun () -> "/static/encrypting.gif")) ~alt:L.encrypting ();
];
div ~a:[a_id "ballot_div"; a_style "display:none;"] [ballot_form];
Unsafe.data ("");
br (); br ();
];
];
]
in
let booth_div =
div ~a:[a_id "booth_div"; a_style "display:none;"] [
div ~a:[a_id "header"] [
div ~a:[a_style "float: left; padding: 15px;"] [
img ~alt:L.election_server ~a:[a_height 70]
~src:(uri_of_string (fun () -> "/static/logo.png")) ();
];
div ~a:[a_style "float: right; padding: 15px;"] [
img ~alt:"" ~a:[a_height 70]
~src:(uri_of_string (fun () -> "/static/placeholder.png")) ();
];
div ~a:[a_style "text-align:center; padding: 20px;"] [
h1 ~a:[a_id "election_name"] [];
p ~a:[a_id "election_description"] [];
];
div ~a:[a_style "clear: both;"] [];
];
main;
div ~a:[a_id "footer"] [
div ~a:[a_id "bottom"] [
div [
pcdata L.election_uuid;
span ~a:[a_id "election_uuid"] [];
];
div [
pcdata L.election_fingerprint;
span ~a:[a_id "election_fingerprint"] [];
];
];
];
div ~a:[a_style "display:none;"] [
span ~a:[a_id "str_here"] [pcdata L.here];
span ~a:[a_id "question_header"] [pcdata L.question_header];
span ~a:[a_id "at_least"] [pcdata L.at_least];
span ~a:[a_id "at_most"] [pcdata L.at_most];
span ~a:[a_id "str_previous"] [pcdata L.previous];
span ~a:[a_id "str_next"] [pcdata L.next];
span ~a:[a_id "str_nothing"] [pcdata L.nothing];
span ~a:[a_id "enter_cred"] [pcdata L.enter_cred];
span ~a:[a_id "invalid_cred"] [pcdata L.invalid_cred];
span ~a:[a_id "str_blank_vote"] [pcdata L.blank_vote];
span ~a:[a_id "no_other_blank"] [pcdata L.no_other_blank];
];
]
in
let body = body [
div ~a:[a_id "wrapper"] [
election_loader;
booth_div;
];
] in
return @@ html ~a:[a_dir `Ltr; a_xml_lang L.lang] head body
belenios-1.4+dfsg/src/web/web_templates.mli000066400000000000000000000127501307140314400210010ustar00rootroot00000000000000(**************************************************************************)
(* BELENIOS *)
(* *)
(* Copyright © 2012-2016 Inria *)
(* *)
(* 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. *)
(* *)
(* You should have received a copy of the GNU Affero General Public *)
(* License along with this program. If not, see *)
(* . *)
(**************************************************************************)
open Serializable_t
open Web_serializable_t
open Signatures
val admin : elections:((module ELECTION_DATA) list * (module ELECTION_DATA) list * (module ELECTION_DATA) list * (Uuidm.t * string) list) option -> unit -> [> `Html ] Eliom_content.Html5.F.elt Lwt.t
val new_election_failure : [ `Exists | `Exception of exn ] -> unit -> [> `Html ] Eliom_content.Html5.F.elt Lwt.t
val generic_page : title:string ->
?service:(unit, unit, [< Eliom_service.get_service_kind ],
[< Eliom_service.attached ],
[< Eliom_service.service_kind ], [< Eliom_service.suff ],
'a, unit, [< Eliom_service.registrable ],
[< Eliom_service.non_ocaml_service ])
Eliom_service.service ->
string -> unit -> [> `Html ] Eliom_content.Html5.F.elt Lwt.t
val election_setup_pre : unit -> [> `Html ] Eliom_content.Html5.F.elt Lwt.t
val election_setup : Uuidm.t -> setup_election -> unit -> [> `Html ] Eliom_content.Html5.F.elt Lwt.t
val election_setup_voters : Uuidm.t -> setup_election -> int -> unit -> [> `Html ] Eliom_content.Html5.F.elt Lwt.t
val election_setup_questions : Uuidm.t -> setup_election -> unit -> [> `Html ] Eliom_content.Html5.F.elt Lwt.t
val election_setup_credential_authority : Uuidm.t -> setup_election -> unit -> [> `Html ] Eliom_content.Html5.F.elt Lwt.t
val election_setup_credentials : string -> Uuidm.t -> setup_election -> unit -> [> `Html ] Eliom_content.Html5.F.elt Lwt.t
val election_setup_trustees : Uuidm.t -> setup_election -> unit -> [> `Html ] Eliom_content.Html5.F.elt Lwt.t
val election_setup_trustee : string -> Uuidm.t -> setup_election -> unit -> [> `Html ] Eliom_content.Html5.F.elt Lwt.t
val election_setup_import : Uuidm.t -> setup_election -> (module ELECTION_DATA) list * (module ELECTION_DATA) list * (module ELECTION_DATA) list -> unit -> [> `Html ] Eliom_content.Html5.F.elt Lwt.t
val election_setup_import_trustees : Uuidm.t -> setup_election -> (module ELECTION_DATA) list * (module ELECTION_DATA) list * (module ELECTION_DATA) list -> unit -> [> `Html ] Eliom_content.Html5.F.elt Lwt.t
val election_setup_confirm : Uuidm.t -> setup_election -> unit -> [> `Html ] Eliom_content.Html5.F.elt Lwt.t
val election_home : (module ELECTION_DATA) -> Web_persist.election_state -> unit -> [> `Html ] Eliom_content.Html5.F.elt Lwt.t
val election_admin : (module ELECTION_DATA) -> Web_serializable_j.metadata -> Web_persist.election_state -> unit -> [> `Html ] Eliom_content.Html5.F.elt Lwt.t
val update_credential : (module ELECTION_DATA) -> unit -> [> `Html ] Eliom_content.Html5.F.elt Lwt.t
val regenpwd : Uuidm.t -> unit -> [> `Html ] Eliom_content.Html5.F.elt Lwt.t
val cast_raw : (module ELECTION_DATA) -> unit -> [> `Html ] Eliom_content.Html5.F.elt Lwt.t
val cast_confirmation : (module ELECTION_DATA) -> string -> unit -> [> `Html ] Eliom_content.Html5.F.elt Lwt.t
val cast_confirmed : (module ELECTION_DATA) -> result:[< `Error of Web_common.error | `Valid of string ] -> unit -> [> `Html ] Eliom_content.Html5.F.elt Lwt.t
val pretty_ballots : (module ELECTION_DATA) -> string list -> Yojson.Safe.json result option -> unit -> [> `Html ] Eliom_content.Html5.F.elt Lwt.t
val pretty_records : (module ELECTION_DATA) -> (string * string) list -> unit -> [> `Html ] Eliom_content.Html5.F.elt Lwt.t
val tally_trustees : (module ELECTION_DATA) -> int -> unit -> [> `Html ] Eliom_content.Html5.F.elt Lwt.t
val already_logged_in :
unit -> [> `Html ] Eliom_content.Html5.F.elt Lwt.t
val login_choose :
string list ->
(string -> (unit, unit, [< Eliom_service.get_service_kind ],
[< Eliom_service.attached ], [< Eliom_service.service_kind ],
[< Eliom_service.suff ], 'a, unit,
[< Eliom_service.registrable ],
[< Eliom_service.non_ocaml_service ])
Eliom_service.service) ->
unit -> [> `Html ] Eliom_content.Html5.F.elt Lwt.t
val login_dummy : unit -> [> `Html ] Eliom_content.Html5.F.elt Lwt.t
val login_password : unit -> [> `Html ] Eliom_content.Html5.F.elt Lwt.t
val booth : unit -> [> `Html ] Eliom_content.Html5.F.elt Lwt.t