Skip to content

Commit

Permalink
Incredible refactor to make threshold decryption actually *work*
Browse files Browse the repository at this point in the history
  • Loading branch information
Vagabond committed Apr 3, 2018
1 parent f776c64 commit b2bd3c8
Show file tree
Hide file tree
Showing 8 changed files with 74 additions and 40 deletions.
31 changes: 19 additions & 12 deletions eqc/decrypt_shares_eqc.erl
Original file line number Diff line number Diff line change
Expand Up @@ -12,12 +12,12 @@ prop_decrypt_shares() ->
{ok, _Group} = dealer:group(),
{ok, G1, G2, PubKey, PrivateKeys} = dealer:deal(),

{FailPubKey, FailPKeys} = case Fail of
{FailG1, FailPubKey, FailPKeys} = case Fail of
wrong_key ->
{ok, _, _, FPk, PKs} = dealer:deal(),
{FPk, PKs};
{ok, G1_1, _, FPk, PKs} = dealer:deal(),
{G1_1, FPk, PKs};
_ ->
{PubKey, PrivateKeys}
{G1, PubKey, PrivateKeys}
end,

Message = crypto:hash(sha256, crypto:strong_rand_bytes(12)),
Expand All @@ -29,10 +29,16 @@ prop_decrypt_shares() ->
end,

CipherText = tpke_pubkey:encrypt(PubKey, G1, Message),
FailCipherText = tpke_pubkey:encrypt(FailPubKey, G1, FailMessage),
FailCipherText = tpke_pubkey:encrypt(FailPubKey, FailG1, FailMessage),

GoodShares = [ tpke_privkey:decrypt_share(SK, CipherText) || SK <- PrivateKeys ],
FailShares = [ tpke_privkey:decrypt_share(SK, FailCipherText) || SK <- FailPKeys ],
GoodShares = [ tpke_privkey:decrypt_share(SK, G1, CipherText) || SK <- PrivateKeys ],

FailShares = case Fail of
wrong_message ->
[ tpke_privkey:decrypt_share(SK, G1, FailCipherText) || SK <- FailPKeys ];
_ ->
[ tpke_privkey:decrypt_share(SK, G1, CipherText) || SK <- FailPKeys ]
end,

Shares = case Fail of
duplicate_shares ->
Expand All @@ -51,16 +57,17 @@ prop_decrypt_shares() ->
VerifiedCipherText = tpke_pubkey:verify_ciphertext(PubKey, G1, CipherText),
FailVerifiedCipherText = tpke_pubkey:verify_ciphertext(PubKey, G1, FailCipherText),
VerifiedShares = lists:all(fun(X) -> X end, [tpke_pubkey:verify_share(PubKey, G2, Share, CipherText) || Share <- Shares]),
VerifiedCombinedShares = Message == tpke_pubkey:combine_shares(PubKey, CipherText, Shares),
VerifiedCombinedShares = tpke_pubkey:combine_shares(PubKey, G1, CipherText, Shares),
io:format("Fail ~p ~p~n", [Fail, VerifiedCombinedShares == Message]),

?WHENFAIL(begin
io:format("Shares ~p~n", [Shares])
end,
conjunction([
{verify_ciphertext, eqc:equals(true, VerifiedCipherText)},
{dont_verify_wrong_ciphertext, eqc:equals((Fail == none orelse Fail == duplicate_shares), FailVerifiedCipherText)},
{verify_ciphertext, VerifiedCipherText},
{dont_verify_wrong_ciphertext, eqc:equals((Fail /= wrong_key), FailVerifiedCipherText)},
{verify_share, eqc:equals((Fail == none orelse Fail == duplicate_shares), VerifiedShares)},
{verify_combine_shares, eqc:equals((Fail == none), VerifiedCombinedShares)}
{verify_combine_shares, eqc:equals((Fail == none), Message == VerifiedCombinedShares)}
]))
end).

Expand All @@ -75,4 +82,4 @@ gen_curve() ->
elements(['SS512']).

gen_failure_mode() ->
elements([none, duplicate_shares]).%, wrong_message, wrong_key]).
elements([none, wrong_key, wrong_message, duplicate_shares]).
11 changes: 6 additions & 5 deletions eqc/secret_key_match_eqc.erl
Original file line number Diff line number Diff line change
Expand Up @@ -2,16 +2,17 @@

-include_lib("eqc/include/eqc.hrl").

-define(GROUP, erlang_pbc:group_new('SS512')).
-define(ELEMENT, erlang_pbc:element_new('Zr', ?GROUP)).

-export([prop_secret_key_match/0]).

prop_secret_key_match() ->
?FORALL(Coefficients, gen_coefficients(),
?FORALL(N, gen_coefficients(),
begin
Group =erlang_pbc:group_new('SS512'),
Element = erlang_pbc:element_new('Zr', Group),
Coefficients = [ erlang_pbc:element_random(Element) || _ <- lists:seq(1, N)],
Secret = hd(Coefficients),
FirstSecret = tpke_pubkey:f(0, Coefficients),
FirstSecret = dealer:share_secret(0, Coefficients),
?WHENFAIL(begin
io:format("Secret ~p~n", [erlang_pbc:element_to_string(Secret)]),
io:format("FirstSecret ~p~n", [erlang_pbc:element_to_string(FirstSecret)])
Expand All @@ -22,4 +23,4 @@ prop_secret_key_match() ->
end).

gen_coefficients() ->
?SUCHTHAT(L, list(erlang_pbc:element_random(?ELEMENT)), length(L) > 0).
?SUCHTHAT(L, int(), L > 0).
1 change: 1 addition & 0 deletions rebar.config
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
{erl_opts, [debug_info]}.
{cover_enabled, true}.
{deps, [
{erlang_pbc, ".*", {git, "https://github.com/helium/erlang_pbc.git", {branch, "master"}}}
]}.
Expand Down
2 changes: 1 addition & 1 deletion rebar.lock
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
[{<<"erlang_pbc">>,
{git,"https://github.com/helium/erlang_pbc.git",
{ref,"c569b4277c302bf8abbb9e936bdc5d92b65cedd3"}},
{ref,"e0b62f61445a30eeea02e7399a6139f89e8099bd"}},
0}].
2 changes: 1 addition & 1 deletion src/dealer.erl
Original file line number Diff line number Diff line change
Expand Up @@ -42,7 +42,7 @@ handle_call(deal, _From, #state{group=Group, adversaries=Adversaries, players=Pl
G1 = erlang_pbc:element_from_hash(erlang_pbc:element_new('G1', Group), crypto:strong_rand_bytes(32)),
G2 = case erlang_pbc:pairing_is_symmetric(Group) of
true -> G1;
false -> erlang_pbc:element_from_hash(erlang_pbc:element_new('G2', Group), crypto:strong_rand_bytes(12))
false -> erlang_pbc:element_from_hash(erlang_pbc:element_new('G2', Group), crypto:strong_rand_bytes(32))
end,
%% pre-process them for faster exponents later
erlang_pbc:element_pp_init(G1),
Expand Down
13 changes: 10 additions & 3 deletions src/tpke_privkey.erl
Original file line number Diff line number Diff line change
Expand Up @@ -10,14 +10,21 @@

-export_type([privkey/0]).

-export([init/3, decrypt_share/2, sign/2]).
-export([init/3, decrypt_share/3, sign/2]).

init(PubKey, SecretKey, SecretKeyIndex) ->
#privkey{pubkey=PubKey, secret_key=SecretKey, secret_key_index=SecretKeyIndex}.

decrypt_share(PrivKey, {U, _V, _W}) ->
Share = erlang_pbc:element_pow(U, PrivKey#privkey.secret_key),
decrypt_share(PrivKey, G1, {U, V, W}) ->
H = tpke_pubkey:hashH(U, V),
Share = case erlang_pbc:element_cmp(erlang_pbc:element_pairing(G1, W), erlang_pbc:element_pairing(U, H)) of
true ->
erlang_pbc:element_mul(PrivKey#privkey.secret_key, U);
false ->
'?'
end,
{PrivKey#privkey.secret_key_index, Share}.


sign(PrivKey, H) ->
{PrivKey#privkey.secret_key_index, erlang_pbc:element_pow(H, PrivKey#privkey.secret_key)}.
48 changes: 33 additions & 15 deletions src/tpke_pubkey.erl
Original file line number Diff line number Diff line change
Expand Up @@ -10,27 +10,39 @@
-type pubkey() :: #pubkey{}.

-export_type([pubkey/0]).
-export([init/4, lagrange/4, encrypt/3, verify_ciphertext/3, verify_share/4, combine_shares/3, hash_message/2, verify_signature/4, combine_signature_shares/2, verify_signature_share/4]).
-export([init/4, lagrange/4, encrypt/3, verify_ciphertext/3, verify_share/4, combine_shares/4, hash_message/2, verify_signature/4, combine_signature_shares/2, verify_signature_share/4]).

-export([hashH/2]).

init(Players, K, VK, VKs) ->
#pubkey{players=Players, k=K, verification_key=VK, verification_keys=VKs}.

encrypt(PubKey, G1, Message) when is_binary(Message) ->
32 = byte_size(Message),
R = erlang_pbc:element_new('Zr', PubKey#pubkey.verification_key),
U = erlang_pbc:element_pow(G1, R),
V = xor_bin(Message, hashG(erlang_pbc:element_pow(PubKey#pubkey.verification_key, R))),
W = erlang_pbc:element_pow(hashH(U, V), R),
R = erlang_pbc:element_random(erlang_pbc:element_new('Zr', PubKey#pubkey.verification_key)),
U = erlang_pbc:element_mul(R, G1),
V = xor_bin(hashG(erlang_pbc:element_mul(R, PubKey#pubkey.verification_key)), Message),
W = erlang_pbc:element_mul(R, hashH(U, V)),
{U, V, W}.

verify_ciphertext(_PubKey, G1, {U, V, W}) ->
H = hashH(U, V),
erlang_pbc:element_cmp(erlang_pbc:element_pairing(G1, W), erlang_pbc:element_pairing(U, H)).

verify_share(PubKey, G2, {Index, Share}, {U, _V, _W}) ->
verify_share(PubKey, G2, {Index, Share}, {U, V, W}) ->
true = 0 =< Index andalso Index < PubKey#pubkey.players,
Y_i = lists:nth(Index+1, PubKey#pubkey.verification_keys),
erlang_pbc:element_cmp(erlang_pbc:element_pairing(Share, G2), erlang_pbc:element_pairing(U, Y_i)).
H = hashH(U, V),
case erlang_pbc:element_cmp(erlang_pbc:element_pairing(G2, W), erlang_pbc:element_pairing(U, H)) of
true when Share == '?' ->
false;
true ->
Yi = lists:nth(Index+1, PubKey#pubkey.verification_keys),
erlang_pbc:element_cmp(erlang_pbc:element_pairing(G2, Share), erlang_pbc:element_pairing(U, Yi));
false when Share == '?' ->
true;
false ->
false
end.

verify_signature_share(PubKey, G2, {Index, Share}, H) ->
true = 0 =< Index andalso Index < PubKey#pubkey.players,
Expand All @@ -42,19 +54,25 @@ verify_signature(PubKey, G2, Signature, H) ->
B = erlang_pbc:element_pairing(H, PubKey#pubkey.verification_key),
erlang_pbc:element_cmp(A, B).

combine_shares(PubKey, {U, V, _W}, Shares) ->
combine_shares(PubKey, G2, {U, V, W}, Shares) ->
{Indices, _} = lists:unzip(Shares),
Set = ordsets:from_list(Indices),
MySet = ordsets:from_list(lists:seq(0, PubKey#pubkey.players - 1)),
true = ordsets:is_subset(Set, MySet),

One = erlang_pbc:element_set(erlang_pbc:element_new('Zr', U), 1),

Bleh = [ erlang_pbc:element_pow(Share, lagrange(PubKey, One, Set, Index)) || {Index, Share} <- Shares],
Res = lists:foldl(fun(E, Acc) ->
erlang_pbc:element_mul(E, Acc)
end, 1, Bleh),
xor_bin(hashG(Res), V).
H = hashH(U, V),
case erlang_pbc:element_cmp(erlang_pbc:element_pairing(G2, W), erlang_pbc:element_pairing(U, H)) of
true ->
Bleh = [ erlang_pbc:element_pow(Share, lagrange(PubKey, One, Set, Index)) || {Index, Share} <- Shares],
Res = lists:foldl(fun(E, Acc) ->
erlang_pbc:element_mul(Acc, E)
end, One, Bleh),
xor_bin(hashG(Res), V);
false ->
undefined
end.

combine_signature_shares(PubKey, Shares) ->
{Indices, _} = lists:unzip(Shares),
Expand All @@ -67,7 +85,7 @@ combine_signature_shares(PubKey, Shares) ->
Bleh = [ erlang_pbc:element_pow(Share, lagrange(PubKey, One, Set, Index)) || {Index, Share} <- Shares],
lists:foldl(fun(E, Acc) ->
erlang_pbc:element_mul(E, Acc)
end, 1, Bleh).
end, One, Bleh).

hash_message(PubKey, Msg) ->
Res = erlang_pbc:element_from_hash(erlang_pbc:element_new('G1', PubKey#pubkey.verification_key), Msg),
Expand Down
6 changes: 3 additions & 3 deletions test/dealer_test.erl
Original file line number Diff line number Diff line change
Expand Up @@ -41,11 +41,11 @@ threshold_decrypt_test_() ->
CipherText = tpke_pubkey:encrypt(PubKey, G1, Message),
%% verify ciphertext
?assert(tpke_pubkey:verify_ciphertext(PubKey, G1, CipherText)),
Shares = [ tpke_privkey:decrypt_share(SK, CipherText) || SK <- PrivateKeys ],
Shares = [ tpke_privkey:decrypt_share(SK, G1, CipherText) || SK <- PrivateKeys ],
%% verify share
?assert(lists:all(fun(X) -> X end, [tpke_pubkey:verify_share(PubKey, G2, Share, CipherText) || Share <- Shares])),
?assert(lists:all(fun(X) -> X end, [tpke_pubkey:verify_share(PubKey, G1, Share, CipherText) || Share <- Shares])),
%% verify combine_shares
?assertEqual(Message, tpke_pubkey:combine_shares(PubKey, CipherText, dealer:random_n(K, Shares)))
?assertEqual(Message, tpke_pubkey:combine_shares(PubKey, G1, CipherText, dealer:random_n(K, Shares)))
end
end,
{foreach, fun() -> ok end, fun(_) -> gen_server:stop(dealer) end, [
Expand Down

0 comments on commit b2bd3c8

Please sign in to comment.