Commit e96d4387 authored by Stephane Glondu's avatar Stephane Glondu

Add support for threshold decryption (backend)

 - no encryption for now
 - no support in belenios-tool.html
parent f366ac80
...@@ -6,6 +6,7 @@ all: ...@@ -6,6 +6,7 @@ all:
check: all check: all
demo/demo.sh demo/demo.sh
demo/demo-threshold.sh
clean: clean:
ocamlbuild -clean ocamlbuild -clean
......
#!/bin/bash
set -e
export BELENIOS_USE_URANDOM=1
BELENIOS=${BELENIOS:-$PWD}
belenios-tool () {
$BELENIOS/_build/belenios-tool "$@"
}
header () {
echo
echo "=-=-= $1 =-=-="
echo
}
header "Setup election"
UUID=`uuidgen`
echo "UUID of the election is $UUID"
DIR=$BELENIOS/demo/data/$UUID
mkdir $DIR
cd $DIR
# Common options
uuid="--uuid $UUID"
group="--group $BELENIOS/demo/groups/default.json"
# Generate credentials
belenios-tool credgen $uuid $group --count 5
mv *.pubcreds public_creds.txt
mv *.privcreds private_creds.txt
# Generate trustee keys
ttkeygen () {
belenios-tool threshold-trustee-keygen $group "$@"
}
ttkeygen --step 1
ttkeygen --step 1
ttkeygen --step 1
cat *.cert > certs.jsons
ttkeygen --certs certs.jsons --step 2
for u in *.key; do
ttkeygen --certs certs.jsons --key $u --step 3 --threshold 2
done > polynomials.jsons
ttkeygen --certs certs.jsons --step 4 --polynomials polynomials.jsons
for u in *.key; do
b=${u%.key}
ttkeygen --certs certs.jsons --key $u --step 5 < $b.vinput > $b.voutput
done
cat *.voutput | ttkeygen --certs certs.jsons --step 6 --polynomials polynomials.jsons > threshold.json
# Generate election parameters
belenios-tool mkelection $uuid $group --template $BELENIOS/demo/templates/questions.json
header "Simulate votes"
cat > votes.txt <<EOF
[[1,0],[1,0,0]]
[[1,0],[0,1,0]]
[[0,1],[0,0,1]]
[[1,0],[1,0,0]]
[[0,0],[0,1,0]]
EOF
paste private_creds.txt votes.txt | while read id cred vote; do
belenios-tool vote --privcred <(echo "$cred") --ballot <(echo "$vote")
echo "Voter $id voted" >&2
echo >&2
done > ballots.tmp
mv ballots.tmp ballots.jsons
header "Perform verification"
belenios-tool verify
header "Simulate and verify update"
tdir="$(mktemp -d)"
cp election.json threshold.json public_creds.txt "$tdir"
head -n3 ballots.jsons > "$tdir/ballots.jsons"
belenios-tool verify-diff --dir1="$tdir" --dir2=.
rm -rf "$tdir"
header "Perform decryption"
for u in *.key; do
belenios-tool threshold-decrypt --key $u --decryption-key ${u%.key}.dkey
echo >&2
done > partial_decryptions.tmp
head -n2 partial_decryptions.tmp > partial_decryptions.jsons
header "Finalize tally"
belenios-tool finalize
header "Perform final verification"
belenios-tool verify
echo
echo "=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-="
echo
echo "The simulated election was successful! Its result can be seen in"
echo " $DIR/result.json"
echo
echo "=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-="
echo
...@@ -123,6 +123,17 @@ module Array = struct ...@@ -123,6 +123,17 @@ module Array = struct
let ssplit a = let ssplit a =
mmap fst a, mmap snd a mmap fst a, mmap snd a
let findi f a =
let n = Array.length a in
let rec loop i =
if i < n then
match f i a.(i) with
| None -> loop (i+1)
| Some _ as x -> x
else None
in loop 0
end end
module String = struct module String = struct
......
...@@ -40,6 +40,7 @@ module Array : sig ...@@ -40,6 +40,7 @@ module Array : sig
val mmap3 : ('a -> 'b -> 'c -> 'd) -> val mmap3 : ('a -> 'b -> 'c -> 'd) ->
'a array array -> 'b array array -> 'c array array -> 'd array array 'a array array -> 'b array array -> 'c array array -> 'd array array
val ssplit : ('a * 'b) array array -> 'a array array * 'b array array val ssplit : ('a * 'b) array array -> 'a array array * 'b array array
val findi : (int -> 'a -> 'b option) -> 'a array -> 'b option
end end
module String : sig module String : sig
......
...@@ -563,11 +563,10 @@ module MakeElection (G : GROUP) (M : RANDOM) = struct ...@@ -563,11 +563,10 @@ module MakeElection (G : GROUP) (M : RANDOM) = struct
type result = elt Serializable_t.result type result = elt Serializable_t.result
let combine_factors num_tallied encrypted_tally partial_decryptions = type combinator = factor array -> elt array array
let dummy = Array.mmap (fun _ -> G.one) encrypted_tally in
let factors = Array.fold_left (fun a b -> let combine_factors num_tallied encrypted_tally partial_decryptions combinator =
Array.mmap2 ( *~ ) a b.decryption_factors let factors = combinator partial_decryptions in
) dummy partial_decryptions in
let results = Array.mmap2 (fun {beta; _} f -> let results = Array.mmap2 (fun {beta; _} f ->
beta / f beta / f
) encrypted_tally factors in ) encrypted_tally factors in
...@@ -588,17 +587,14 @@ module MakeElection (G : GROUP) (M : RANDOM) = struct ...@@ -588,17 +587,14 @@ module MakeElection (G : GROUP) (M : RANDOM) = struct
let result = Array.mmap log results in let result = Array.mmap log results in
{num_tallied; encrypted_tally; partial_decryptions; result} {num_tallied; encrypted_tally; partial_decryptions; result}
let check_result pks r = let check_result combinator pks r =
let {encrypted_tally; partial_decryptions; result; _} = r in let {encrypted_tally; partial_decryptions; result; _} = r in
check_ciphertext encrypted_tally && check_ciphertext encrypted_tally &&
(* decryption factors may be not in the same order as pks! *) (* decryption factors may be not in the same order as pks! *)
Array.forall (fun pk -> Array.forall (fun pd ->
Array.exists (check_factor encrypted_tally pk) partial_decryptions Array.exists (fun pk -> check_factor encrypted_tally pk pd) pks
) pks && ) partial_decryptions &&
let dummy = Array.mmap (fun _ -> G.one) encrypted_tally in let factors = combinator partial_decryptions in
let factors = Array.fold_left (fun a b ->
Array.mmap2 ( *~ ) a b.decryption_factors
) dummy partial_decryptions in
let results = Array.mmap2 (fun {beta; _} f -> let results = Array.mmap2 (fun {beta; _} f ->
beta / f beta / f
) encrypted_tally factors in ) encrypted_tally factors in
......
...@@ -128,3 +128,71 @@ type 'a result = { ...@@ -128,3 +128,71 @@ type 'a result = {
partial_decryptions : 'a partial_decryption list <ocaml repr="array">; partial_decryptions : 'a partial_decryption list <ocaml repr="array">;
result : plaintext; result : plaintext;
} }
(** {2 Channel messages support} *)
type 'a raw_channel_msg = {
recipient : 'a;
message : string;
} <ocaml field_prefix="raw_">
type channel_msg = {
message : string; (* raw_channel_msg *)
signature : proof;
} <ocaml field_prefix="channel_">
(** {2 Threshold decryption support} *)
type 'a cert_keys = {
verification : 'a;
encryption : 'a;
} <ocaml field_prefix="cert_">
type cert = {
keys : string; (* cert_keys *)
signature : proof;
} <ocaml field_prefix="cert_">
type certs = {
certs : cert list <ocaml repr="array">;
}
type raw_polynomial = number list <ocaml repr="array">
type 'a raw_coefexps = 'a list <ocaml repr="array">
type coefexps = {
coefexps : string; (* raw_coefexps *)
signature : proof;
} <ocaml field_prefix="ce_">
type secret = {
secret : number;
}
type polynomial = {
polynomial : string; (* sent raw_polynomial *)
secrets : string list <ocaml repr="array">; (* sent secrets *)
coefexps : coefexps;
} <ocaml field_prefix="p_">
type vinput = {
polynomial : string; (* sent raw_polynomial *)
secrets : string list <ocaml repr="array">; (* sent secrets *)
coefexps : coefexps list <ocaml repr="array">;
} <ocaml field_prefix="vi_">
type partial_decryption_key = {
decryption_key : number;
} <ocaml field_prefix="pdk_">
type 'a voutput = {
private_key : string; (* sent partial_decryption_key *)
public_key : 'a trustee_public_key;
} <ocaml field_prefix="vo_">
type 'a threshold_parameters = {
threshold : int;
certs : cert list <ocaml repr="array">;
coefexps : coefexps list <ocaml repr="array">;
verification_keys : 'a trustee_public_key list <ocaml repr="array">;
} <ocaml field_prefix="t_">
...@@ -223,13 +223,56 @@ module type ELECTION = sig ...@@ -223,13 +223,56 @@ module type ELECTION = sig
(** The election result. It contains the needed data to validate the (** The election result. It contains the needed data to validate the
result from the encrypted tally. *) result from the encrypted tally. *)
val combine_factors : int -> ciphertext -> factor array -> result type combinator = factor array -> elt array array
val combine_factors : int -> ciphertext -> factor array -> combinator -> result
(** Combine the encrypted tally and the factors from all trustees to (** Combine the encrypted tally and the factors from all trustees to
produce the election result. The first argument is the number of produce the election result. The first argument is the number of
tallied ballots. May raise [Invalid_argument]. *) tallied ballots. May raise [Invalid_argument]. *)
val check_result : public_key array -> result -> bool val check_result : combinator -> public_key array -> result -> bool
val extract_tally : result -> plaintext val extract_tally : result -> plaintext
(** Extract the plaintext result of the election. *) (** Extract the plaintext result of the election. *)
end end
module type PKI = sig
type 'a m
type private_key
type public_key
val genkey : unit -> string m
val derive_sk : string -> private_key
val derive_dk : string -> private_key
val sign : private_key -> string -> proof m
val verify : public_key -> string -> proof -> bool
val encrypt : public_key -> string -> string m
val decrypt : private_key -> string -> string
val make_cert : sk:private_key -> dk:private_key -> cert m
val verify_cert : cert -> bool
end
module type CHANNELS = sig
type 'a m
type private_key
type public_key
val send : private_key -> public_key -> string -> string m
val recv : private_key -> public_key -> string -> string
end
module type PEDERSEN = sig
type 'a m
type elt
val step1 : unit -> (string * cert) m
val step2 : certs -> unit
val step3 : certs -> string -> int -> polynomial m
val step4 : certs -> polynomial array -> vinput array
val step5 : certs -> string -> vinput -> elt voutput m
val step6 : certs -> polynomial array -> elt voutput array -> elt threshold_parameters
val check : elt threshold_parameters -> bool
val combine : elt threshold_parameters -> elt
type checker = elt -> elt partial_decryption -> bool
val combine_factors : checker -> elt threshold_parameters -> elt partial_decryption array -> elt array array
end
This diff is collapsed.
...@@ -43,5 +43,32 @@ module MakeSimpleDistKeyGen (G : GROUP) (M : RANDOM) : sig ...@@ -43,5 +43,32 @@ module MakeSimpleDistKeyGen (G : GROUP) (M : RANDOM) : sig
val combine : G.t trustee_public_key array -> G.t val combine : G.t trustee_public_key array -> G.t
(** Combine all public key shares into an election public key. *) (** Combine all public key shares into an election public key. *)
val combine_factors : G.t partial_decryption array -> G.t array array
end end
(** Simple distributed generation of an election public key. *) (** Simple distributed generation of an election public key. *)
module MakePKI (G : GROUP) (M : RANDOM) :
PKI with type 'a m = 'a M.t
and type private_key = Z.t
and type public_key = G.t
module MakeChannels (G : GROUP) (M : RANDOM)
(P : PKI with type 'a m = 'a M.t
and type private_key = Z.t
and type public_key = G.t) :
CHANNELS with type 'a m = 'a P.m
and type private_key = P.private_key
and type public_key = P.public_key
exception PedersenFailure of string
module MakePedersen (G : GROUP) (M : RANDOM)
(P : PKI with type 'a m = 'a M.t
and type private_key = Z.t
and type public_key = G.t)
(C : CHANNELS with type 'a m = 'a M.t
and type private_key = Z.t
and type public_key = G.t) :
PEDERSEN with type 'a m = 'a M.t
and type elt = G.t
This diff is collapsed.
...@@ -27,6 +27,7 @@ open Common ...@@ -27,6 +27,7 @@ open Common
module type PARAMS = sig module type PARAMS = sig
val election : string val election : string
val get_public_keys : unit -> string array option val get_public_keys : unit -> string array option
val get_threshold : unit -> string option
val get_public_creds : unit -> string Stream.t option val get_public_creds : unit -> string Stream.t option
val get_ballots : unit -> string Stream.t option val get_ballots : unit -> string Stream.t option
val get_result : unit -> string option val get_result : unit -> string option
...@@ -36,6 +37,7 @@ end ...@@ -36,6 +37,7 @@ end
module type S = sig module type S = sig
val vote : string option -> int array array -> string val vote : string option -> int array array -> string
val decrypt : string -> string val decrypt : string -> string
val tdecrypt : string -> string -> string
val finalize : string array -> string val finalize : string array -> string
val verify : unit -> unit val verify : unit -> unit
end end
...@@ -60,32 +62,42 @@ module Make (P : PARSED_PARAMS) : S = struct ...@@ -60,32 +62,42 @@ module Make (P : PARSED_PARAMS) : S = struct
module M = Election.MakeSimpleMonad(G) module M = Election.MakeSimpleMonad(G)
module E = Election.MakeElection(G)(M);; module E = Election.MakeElection(G)(M);;
module KG = Trustees.MakeSimpleDistKeyGen (G) (M)
module P = Trustees.MakePKI (G) (M)
module C = Trustees.MakeChannels (G) (M) (P)
module KP = Trustees.MakePedersen (G) (M) (P) (C)
(* Load and check trustee keys, if present *) (* Load and check trustee keys, if present *)
module KG = Trustees.MakeSimpleDistKeyGen(G)(M);; let threshold =
match get_threshold () with
| None -> None
| Some x -> Some (threshold_parameters_of_string G.read x)
let public_keys_with_pok = let public_keys_with_pok =
get_public_keys () |> option_map @@ match threshold with
Array.map (trustee_public_key_of_string G.read) | None ->
get_public_keys () |> option_map @@
Array.map (trustee_public_key_of_string G.read)
| Some t -> Some t.t_verification_keys
let () = let () =
match public_keys_with_pok with match public_keys_with_pok, threshold with
| Some pks -> | Some pks, None ->
assert (Array.forall KG.check pks); assert (Array.forall KG.check pks);
let y' = KG.combine pks in let y' = KG.combine pks in
assert G.(election.e_params.e_public_key =~ y') assert G.(election.e_params.e_public_key =~ y')
| None -> () | _ -> ()
let public_keys = let public_keys =
option_map ( option_map (
Array.map (fun pk -> pk.trustee_public_key) Array.map (fun pk -> pk.trustee_public_key)
) public_keys_with_pok ) public_keys_with_pok
(* Finish setting up the election *) let pks = lazy (match public_keys with
| Some pks -> pks
let pks = match public_keys with | None -> failwith "missing public keys")
| Some pks -> pks
| None -> failwith "missing public keys"
(* Load ballots, if present *) (* Load ballots, if present *)
...@@ -155,7 +167,7 @@ module Make (P : PARSED_PARAMS) : S = struct ...@@ -155,7 +167,7 @@ module Make (P : PARSED_PARAMS) : S = struct
let decrypt privkey = let decrypt privkey =
let sk = number_of_string privkey in let sk = number_of_string privkey in
let pk = G.(g **~ sk) in let pk = G.(g **~ sk) in
if Array.forall (fun x -> not G.(x =~ pk)) pks then ( if Array.forall (fun x -> not G.(x =~ pk)) (Lazy.force pks) then (
print_msg "W: your key is not present in public_keys.jsons"; print_msg "W: your key is not present in public_keys.jsons";
); );
let tally = Lazy.force encrypted_tally in let tally = Lazy.force encrypted_tally in
...@@ -163,24 +175,60 @@ module Make (P : PARSED_PARAMS) : S = struct ...@@ -163,24 +175,60 @@ module Make (P : PARSED_PARAMS) : S = struct
assert (E.check_factor tally pk factor); assert (E.check_factor tally pk factor);
string_of_partial_decryption G.write factor string_of_partial_decryption G.write factor
let tdecrypt key pdk =
let sk = P.derive_sk key and dk = P.derive_dk key in
let vk = G.(g **~ sk) in
let pdk = C.recv dk vk pdk in
let pdk = (partial_decryption_key_of_string pdk).pdk_decryption_key in
let pvk = G.(g **~ pdk) in
(match threshold with
| None -> print_msg "W: threshold parameters are missing"
| Some t ->
if Array.forall (fun x ->
not G.(x.trustee_public_key =~ pvk)
) t.t_verification_keys then
print_msg "W: your key is not present in threshold parameters"
);
let tally = Lazy.force encrypted_tally in
let factor = E.compute_factor tally pdk () in
assert (E.check_factor tally pvk factor);
string_of_partial_decryption G.write factor
let finalize factors = let finalize factors =
let factors = Array.map (partial_decryption_of_string G.read) factors in let factors = Array.map (partial_decryption_of_string G.read) factors in
let tally = Lazy.force encrypted_tally in let tally = Lazy.force encrypted_tally in
assert (Array.forall2 (E.check_factor tally) pks factors); let checker = E.check_factor tally in
let result = E.combine_factors (M.cardinal ()) tally factors in let combinator =
assert (E.check_result pks result); match threshold with
| None ->
assert (Array.forall2 checker (Lazy.force pks) factors);
KG.combine_factors
| Some t -> KP.combine_factors checker t
in
let result = E.combine_factors (M.cardinal ()) tally factors combinator in
assert (E.check_result combinator (Lazy.force pks) result);
string_of_result G.write result string_of_result G.write result
let verify () = let verify () =
(match threshold with
| Some t ->
assert (KP.check t);
assert G.(election.e_params.e_public_key =~ KP.combine t)
| None -> ignore (Lazy.force pks)
);
(match Lazy.force ballots_check with (match Lazy.force ballots_check with
| Some () -> () | Some () -> ()
| None -> print_msg "W: no ballots to check" | None -> print_msg "W: no ballots to check"
); );
(match get_result () with (match get_result () with
| Some result -> | Some result ->
let result = result_of_string G.read result in let result = result_of_string G.read result in
assert (Lazy.force encrypted_tally = result.encrypted_tally); assert (Lazy.force encrypted_tally = result.encrypted_tally);
assert (E.check_result pks result) let combinator = match threshold with
| None -> KG.combine_factors
| Some t -> KP.combine_factors (E.check_factor result.encrypted_tally) t
in
assert (E.check_result combinator (Lazy.force pks) result)
| None -> print_msg "W: no result to check" | None -> print_msg "W: no result to check"
); );
print_msg "I: all checks passed" print_msg "I: all checks passed"
......
module type PARAMS = sig module type PARAMS = sig
val election : string val election : string
val get_public_keys : unit -> string array option val get_public_keys : unit -> string array option
val get_threshold : unit -> string option
val get_public_creds : unit -> string Stream.t option val get_public_creds : unit -> string Stream.t option
val get_ballots : unit -> string Stream.t option val get_ballots : unit -> string Stream.t option
val get_result : unit -> string option val get_result : unit -> string option
...@@ -10,6 +11,7 @@ end ...@@ -10,6 +11,7 @@ end
module type S = sig module type S = sig
val vote : string option -> int array array -> string val vote : string option -> int array array -> string
val decrypt : string -> string val decrypt : string -> string
val tdecrypt : string -> string -> string
val finalize : string array -> string val finalize : string array -> string
val verify : unit -> unit val verify : unit -> unit
end end
......
...@@ -166,6 +166,7 @@ module Mkelection = struct ...@@ -166,6 +166,7 @@ module Mkelection = struct
let template = get_textarea "mkelection_template" let template = get_textarea "mkelection_template"
let get_public_keys () = let get_public_keys () =
Some (get_textarea "mkelection_pks" |> split_lines |> Array.of_list) Some (get_textarea "mkelection_pks" |> split_lines |> Array.of_list)
let get_threshold () = None
end in end in
let module X = (val make (module P : PARAMS) : S) in let module X = (val make (module P : PARAMS) : S) in
set_textarea "mkelection_output" (X.mkelection ()) set_textarea "mkelection_output" (X.mkelection ())
...@@ -185,6 +186,8 @@ module ToolElection = struct ...@@ -185,6 +186,8 @@ module ToolElection = struct
let pks = Array.of_list raw in let pks = Array.of_list raw in
if Array.length pks = 0 then None else Some pks if Array.length pks = 0 then None else Some pks
let get_threshold () = None
let get_public_creds () = let get_public_creds () =
let raw = get_textarea "election_pubcreds" |> split_lines in let raw = get_textarea "election_pubcreds" |> split_lines in
match raw with match raw with
......
...@@ -28,6 +28,7 @@ module type PARAMS = sig ...@@ -28,6 +28,7 @@ module type PARAMS = sig
val group : string val group : string
val template : string val template : string
val get_public_keys : unit -> string array option val get_public_keys : unit -> string array option
val get_threshold : unit -> string option
end end
module type S = sig module type S = sig
...@@ -39,6 +40,7 @@ module type PARSED_PARAMS = sig ...@@ -39,6 +40,7 @@ module type PARSED_PARAMS = sig
val template : template val template : template
module G : GROUP module G : GROUP
val get_public_keys : unit -> G.t trustee_public_key array option val get_public_keys : unit -> G.t trustee_public_key array option
val get_threshold : unit -> G.t threshold_parameters option
end end
let parse_params p = let parse_params p =
...@@ -54,6 +56,10 @@ let parse_params p = ...@@ -54,6 +56,10 @@ let parse_params p =
match P.get_public_keys () with match P.get_public_keys () with
| None -> None | None -> None
| Some xs -> Some (Array.map (trustee_public_key_of_string G.read) xs) | Some xs -> Some (Array.map (trustee_public_key_of_string G.read) xs)
let get_threshold () =
match P.get_threshold () with
| None -> None
| Some t -> Some (threshold_parameters_of_string G.read t)
end end
in (module R : PARSED_PARAMS) in (module R : PARSED_PARAMS)
...@@ -66,14 +72,21 @@ module Make (P : PARSED_PARAMS) : S = struct ...@@ -66,14 +72,21 @@ module Make (P : PARSED_PARAMS) : S = struct
(* Setup trustees *) (* Setup trustees *)
module KG = Trustees.MakeSimpleDistKeyGen(G)(M);; let y =
match get_threshold () with
let public_keys = | None ->
match get_public_keys () with let public_keys =
| Some keys -> keys match get_public_keys () with
| None -> failwith "trustee keys are missing" | Some keys -> keys
| None -> failwith "trustee keys are missing"
let y = KG.combine public_keys in
let module K = Trustees.MakeSimpleDistKeyGen (G) (M) in
K.combine public_keys
| Some t ->
let module P = Trustees.MakePKI (G) (M) in
let module C = Trustees.MakeChannels (G) (M) (P) in
let module K = Trustees.MakePedersen (G) (M) (P) (C) in
K.combine t
(* Setup election *) (* Setup election *)
......
...@@ -3,6 +3,7 @@ module type PARAMS = sig ...@@ -3,6 +3,7 @@ module type PARAMS = sig
val group : string val group : string
val template : string val template : string
val get_public_keys : unit -> string array option val get_public_keys : unit -> string array option
val get_threshold : unit -> string option
end end
module type S = sig module type S = sig
......
...@@ -37,6 +37,9 @@ let lines_of_file fname = ...@@ -37,6 +37,9 @@ let lines_of_file fname =
let string_of_file f = let string_of_file f =
lines_of_file f |> stream_to_list |> String.concat "\n" lines_of_file f |> stream_to_list |> String.concat "\n"
let string_of_file_opt filename =
if Sys.file_exists filename then Some (string_of_file filename) else None
let load_from_file of_string filename =