Skip to content

Commit

Permalink
Remove PBC based cryptography
Browse files Browse the repository at this point in the history
fixes #70
  • Loading branch information
xandkar committed Jul 1, 2021
1 parent db7af6a commit d558860
Show file tree
Hide file tree
Showing 11 changed files with 164 additions and 431 deletions.
1 change: 0 additions & 1 deletion rebar.config
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,6 @@
{cover_enabled, true}.
{deps, [
{erlang_tc, ".*", {git, "https://github.com/helium/erlang-tc.git", {branch, "main"}}},
{erlang_tpke, ".*", {git, "https://github.com/helium/erlang-tpke.git", {branch, "master"}}},
{erasure, {git, "https://github.com/helium/erlang-erasure.git", {branch, "master"}}},
{merkerl, "1.0.1"}
]}.
Expand Down
8 changes: 0 additions & 8 deletions rebar.lock
Original file line number Diff line number Diff line change
Expand Up @@ -3,18 +3,10 @@
{git,"https://github.com/helium/erlang-erasure.git",
{ref,"c73354ca9914225a825569693dc72db6691d91f1"}},
0},
{<<"erlang_pbc">>,
{git,"https://github.com/helium/erlang_pbc.git",
{ref,"1d2651ba01ba81b748c553d9f729c0e167eeab72"}},
1},
{<<"erlang_tc">>,
{git,"https://github.com/helium/erlang-tc.git",
{ref,"b3ef1d5541586f5c85b6d231345a921d57be32a3"}},
0},
{<<"erlang_tpke">>,
{git,"https://github.com/helium/erlang-tpke.git",
{ref,"02e955cd16ca31519b1b9aaf46cf98ddc406b469"}},
0},
{<<"merkerl">>,{pkg,<<"merkerl">>,<<"1.0.1">>},0}]}.
[
{pkg_hash,[
Expand Down
1 change: 0 additions & 1 deletion src/hbbft.app.src
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,6 @@
stdlib,
merkerl,
erlang_tc,
erlang_tpke,
erasure
]},
{env,[]},
Expand Down
314 changes: 93 additions & 221 deletions src/hbbft.erl

Large diffs are not rendered by default.

91 changes: 35 additions & 56 deletions src/hbbft_cc.erl
Original file line number Diff line number Diff line change
Expand Up @@ -4,9 +4,9 @@

-record(cc_data, {
state = waiting :: waiting | done,
sk :: tc_key_share:tc_key_share() | tpke_privkey:privkey(),
sk :: tc_key_share:tc_key_share(),
%% Note: sid is assumed to be a unique nonce that serves as name of this common coin
sid :: binary() | erlang_pbc:element(),
sid :: binary(),
n :: pos_integer(),
f :: non_neg_integer(),
shares = maps:new() :: #{non_neg_integer() => {non_neg_integer(), tc_signature_share:sig_share()}}
Expand Down Expand Up @@ -45,13 +45,8 @@ status(CCData) ->
binary(),
pos_integer(),
non_neg_integer()) -> cc_data().
init(KeyShare, Sid0, N, F) ->
Sid = case tc_key_share:is_key_share(KeyShare) of
true ->
Sid0;
false ->
tpke_pubkey:hash_message(tpke_privkey:public_key(KeyShare), Sid0)
end,
init(KeyShare, Sid, N, F) ->
true = tc_key_share:is_key_share(KeyShare),
#cc_data{sk=KeyShare, n=N, f=F, sid=Sid}.

%% Figure12. Bullet2
Expand All @@ -60,15 +55,9 @@ init(KeyShare, Sid0, N, F) ->
get_coin(Data = #cc_data{state=done}) ->
{Data, ok};
get_coin(Data = #cc_data{sk=SK}) ->
case hbbft_utils:curve(SK) of
'BLS12-381' ->
Share = tc_key_share:sign_share(Data#cc_data.sk, Data#cc_data.sid),
{Data, {send, [{multicast, {share, hbbft_utils:sig_share_to_binary('BLS12-381', Share)}}]}};
'SS512' ->
Share = tpke_privkey:sign(Data#cc_data.sk, Data#cc_data.sid),
SerializedShare = hbbft_utils:sig_share_to_binary('SS512', Share),
{Data, {send, [{multicast, {share, SerializedShare}}]}}
end.
'BLS12-381' = hbbft_utils:curve(SK),
Share = tc_key_share:sign_share(Data#cc_data.sk, Data#cc_data.sid),
{Data, {send, [{multicast, {share, hbbft_utils:sig_share_to_binary('BLS12-381', Share)}}]}}.


%% upon receiving at least f + 1 shares, attempt to combine them
Expand All @@ -87,16 +76,14 @@ share(#cc_data{state=done}, _J, _Share) ->
share(Data=#cc_data{sk=SK}, J, Share) ->
case maps:is_key(J, Data#cc_data.shares) of
false ->
Curve = hbbft_utils:curve(SK),
Curve = 'BLS12-381' = hbbft_utils:curve(SK),
DeserializedShare = hbbft_utils:binary_to_sig_share(Curve, SK, Share),
ValidShare = case Curve of
'BLS12-381' ->
tc_key_share:verify_signature_share(Data#cc_data.sk,
DeserializedShare,
Data#cc_data.sid);
'SS512' ->
tpke_pubkey:verify_signature_share(tpke_privkey:public_key(Data#cc_data.sk), DeserializedShare, Data#cc_data.sid)
end,
ValidShare =
tc_key_share:verify_signature_share(
Data#cc_data.sk,
DeserializedShare,
Data#cc_data.sid
),
case ValidShare of
true ->
%% store the deserialized share in the shares map, convenient to use later to verify signature
Expand All @@ -105,26 +92,23 @@ share(Data=#cc_data{sk=SK}, J, Share) ->
case maps:size(NewData#cc_data.shares) > Data#cc_data.f of
true ->
%% combine shares
{ok, Sig} = case Curve of
'BLS12-381' ->
tc_key_share:combine_signature_shares(SK, maps:values(NewData#cc_data.shares));
'SS512' ->
tpke_pubkey:combine_verified_signature_shares(tpke_privkey:public_key(NewData#cc_data.sk), maps:values(NewData#cc_data.shares))
end,
'BLS12-381' = Curve,
{ok, Sig} =
tc_key_share:combine_signature_shares(
SK,
maps:values(NewData#cc_data.shares)
),
%% check if the signature is valid
ValidSignature = case Curve of
'BLS12-381' ->
tc_key_share:verify(NewData#cc_data.sk, Sig, NewData#cc_data.sid);
'SS512' ->
tpke_pubkey:verify_signature(tpke_privkey:public_key(NewData#cc_data.sk), Sig, NewData#cc_data.sid)
end,
case ValidSignature of
case
tc_key_share:verify(
NewData#cc_data.sk,
Sig,
NewData#cc_data.sid
)
of
true ->
%% TODO do something better here!
<<Val:32/integer, _/binary>> = case Curve of
'BLS12-381' -> tc_signature:serialize(Sig);
'SS512' -> erlang_pbc:element_to_binary(Sig)
end,
<<Val:32/integer, _/binary>> = tc_signature:serialize(Sig),
{NewData#cc_data{state=done}, {result, Val}};
false ->
{NewData, ok}
Expand All @@ -150,7 +134,7 @@ serialize(#cc_data{state = State, sid = SID, n = N, sk = SK, f = F, shares = Sha
shares = serialize_shares(hbbft_utils:curve(SK), Shares)
}.

-spec deserialize(cc_serialized_data(), tc_key_share:tc_key_share() | tpke_privkey:privkey()) ->
-spec deserialize(cc_serialized_data(), tc_key_share:tc_key_share()) ->
cc_data().
deserialize(#cc_serialized_data{state = State, sid = SID, n = N, f = F, shares = Shares}, SK) ->
#cc_data{
Expand All @@ -162,22 +146,17 @@ deserialize(#cc_serialized_data{state = State, sid = SID, n = N, f = F, shares =
shares = deserialize_shares(hbbft_utils:curve(SK), SK, Shares)
}.

-spec serialize_shares('BLS12-381' | 'SS512', #{non_neg_integer() => tc_signature_share:sig_share()}) -> #{non_neg_integer() => binary()}.
-spec serialize_shares('BLS12-381', #{non_neg_integer() => tc_signature_share:sig_share()}) -> #{non_neg_integer() => binary()}.
serialize_shares(Curve, Shares) ->
maps:map(fun(_K, V) -> hbbft_utils:sig_share_to_binary(Curve, V) end, Shares).

-spec deserialize_shares('BLS12-381' | 'SS512', tc_key_share:tc_key_share() | tpke_privkey:privkey(), #{non_neg_integer() => binary()}) -> #{non_neg_integer() => tc_signature_share:sig_share()}.
-spec deserialize_shares('BLS12-381', tc_key_share:tc_key_share(), #{non_neg_integer() => binary()}) -> #{non_neg_integer() => tc_signature_share:sig_share()}.
deserialize_shares(Curve, SK, Shares) ->
maps:map(fun(_K, V) -> hbbft_utils:binary_to_sig_share(Curve, SK, V) end, Shares).

serialize_sid(SID) when is_binary(SID) ->
SID;
serialize_sid(SID) ->
erlang_pbc:element_to_binary(SID).
serialize_sid(<<SID/binary>>) ->
SID.

deserialize_sid(SK, SID) ->
case hbbft_utils:curve(SK) of
'BLS12-381' -> SID;
'SS512' -> tpke_pubkey:deserialize_element(tpke_privkey:public_key(SK), SID)
end.

'BLS12-381' = hbbft_utils:curve(SK),
SID.
25 changes: 4 additions & 21 deletions src/hbbft_utils.erl
Original file line number Diff line number Diff line change
Expand Up @@ -19,33 +19,20 @@
sig_share_to_binary('BLS12-381', {ShareIdx, SigShare}) ->
%% Assume less than 256 members in the consensus group
ShareBinary = tc_signature_share:serialize(SigShare),
<<ShareIdx:8/integer-unsigned, ShareBinary/binary>>;
sig_share_to_binary('SS512', {ShareIdx, SigShare}) ->
ShareBinary = erlang_pbc:element_to_binary(SigShare),
<<ShareIdx:8/integer-unsigned, ShareBinary/binary>>.

binary_to_sig_share('BLS12-381', _SK, <<ShareIdx:8/integer-unsigned, ShareBinary/binary>>) ->
SigShare = tc_signature_share:deserialize(ShareBinary),
{ShareIdx, SigShare};
binary_to_sig_share('SS512', SK, <<ShareIdx:8/integer-unsigned, ShareBinary/binary>>) ->
ShareElement = tpke_pubkey:deserialize_element(tpke_privkey:public_key(SK), ShareBinary),
{ShareIdx, ShareElement}.
{ShareIdx, SigShare}.

dec_share_to_binary('BLS12-381', {ShareIdx, DecShare}) ->
%% Assume less than 256 members in the consensus group
ShareBinary = tc_decryption_share:serialize(DecShare),
<<ShareIdx:8/integer-unsigned, ShareBinary/binary>>;
dec_share_to_binary('SS512', {ShareIdx, DecShare}) ->
%% Assume less than 256 members in the consensus group
ShareBinary = erlang_pbc:element_to_binary(DecShare),
<<ShareIdx:8/integer-unsigned, ShareBinary/binary>>.

binary_to_dec_share('BLS12-381', _SK, <<ShareIdx:8/integer-unsigned, ShareBinary/binary>>) ->
DecShare = tc_decryption_share:deserialize(ShareBinary),
{ShareIdx, DecShare};
binary_to_dec_share('SS512', SK, <<ShareIdx:8/integer-unsigned, ShareBinary/binary>>) ->
ShareElement = tpke_pubkey:deserialize_element(tpke_privkey:public_key(SK), ShareBinary),
{ShareIdx, ShareElement}.
{ShareIdx, DecShare}.

%% wrap a subprotocol's outbound messages with a protocol identifier
-spec wrap(Tag :: atom() | {atom(), non_neg_integer()}, [{multicast, Msg :: any()} | {unicast, non_neg_integer(), Msg :: any()}]) -> [{multicast, {Tag, Msg}} | {unicast, non_neg_integer(), {Tag, Msg}}].
Expand All @@ -66,9 +53,5 @@ shuffle(List) ->

-spec curve(KeyShare :: hbbft:key_share()) -> hbbft:curve().
curve(KeyShare) ->
case tc_key_share:is_key_share(KeyShare) of
true ->
'BLS12-381';
false ->
'SS512'
end.
true = tc_key_share:is_key_share(KeyShare),
'BLS12-381'.
53 changes: 12 additions & 41 deletions test/hbbft_SUITE.erl
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@
]).

all() ->
[{group, ss512}, {group, bls12_381}].
[{group, bls12_381}].

test_cases() ->
[
Expand All @@ -40,11 +40,8 @@ test_cases() ->
].

groups() ->
[{ss512, [], test_cases() -- [batch_size_limit_minimal_test]},
{bls12_381, [], test_cases()}].
[{bls12_381, [], test_cases()}].

init_per_group(ss512, Config) ->
[{curve, 'SS512'} | Config];
init_per_group(bls12_381, Config) ->
[{curve, 'BLS12-381'} | Config].

Expand All @@ -56,13 +53,7 @@ init_per_testcase(_, Config) ->
F = N div 4,
Module = hbbft,
BatchSize = 20,
case proplists:get_value(curve, Config, 'BLS12-381') of
'BLS12-381' ->
PrivateKeys = tc_key_share:deal(N, F);
'SS512' ->
{ok, Dealer} = dealer:new(N, F+1, 'SS512'),
{ok, {_PubKey, PrivateKeys}} = dealer:deal(Dealer)
end,
PrivateKeys = tc_key_share:deal(N, F),
[{n, N}, {f, F}, {batchsize, BatchSize}, {module, Module}, {privatekeys, PrivateKeys} | Config].

end_per_testcase(_, _Config) ->
Expand Down Expand Up @@ -282,23 +273,12 @@ two_actors_missing_test(Config) ->
ok.

encrypt_decrypt_test(Config) ->
case proplists:get_value(curve, Config, 'BLS12-381') of
'BLS12-381' ->
PrivateKeys = [SK1 | _RemainingSKs] = proplists:get_value(privatekeys, Config),
PlainText = crypto:strong_rand_bytes(24),
Ciphertext = tc_ciphertext:deserialize(hbbft:encrypt('BLS12-381', hd(PrivateKeys), PlainText)),
DecShares = [tc_key_share:decrypt_share(SK, Ciphertext) || SK <- PrivateKeys],
{ok, Decrypted} = tc_key_share:combine_decryption_shares(SK1, DecShares, Ciphertext),
?assertEqual(PlainText, Decrypted);
'SS512' ->
PrivateKeys = proplists:get_value(privatekeys, Config),
PubKey = tpke_privkey:public_key(hd(PrivateKeys)),
PlainText = crypto:strong_rand_bytes(24),
Enc = hbbft:encrypt('SS512', hd(PrivateKeys), PlainText),
{ok, EncKey} = hbbft:get_encrypted_key(hd(PrivateKeys), Enc),
DecKey = tpke_pubkey:combine_shares(PubKey, EncKey, [ tpke_privkey:decrypt_share(SK, EncKey) || SK <- PrivateKeys]),
?assertEqual(PlainText, hbbft:decrypt(DecKey, Enc))
end,
PrivateKeys = [SK1 | _RemainingSKs] = proplists:get_value(privatekeys, Config),
PlainText = crypto:strong_rand_bytes(24),
Ciphertext = tc_ciphertext:deserialize(hbbft:encrypt('BLS12-381', hd(PrivateKeys), PlainText)),
DecShares = [tc_key_share:decrypt_share(SK, Ciphertext) || SK <- PrivateKeys],
{ok, Decrypted} = tc_key_share:combine_decryption_shares(SK1, DecShares, Ciphertext),
?assertEqual(PlainText, Decrypted),
ok.

start_on_demand_test(Config) ->
Expand Down Expand Up @@ -357,13 +337,7 @@ one_actor_wrong_key_test(Config) ->
Curve = proplists:get_value(curve, Config),
BatchSize = proplists:get_value(batchsize, Config),
PrivateKeys0 = proplists:get_value(privatekeys, Config),
case Curve of
'BLS12-381' ->
PrivateKeys1 = tc_key_share:deal(N, F);
'SS512' ->
{ok, Dealer} = dealer:new(N, F+1, 'SS512'),
{ok, {_PubKey, PrivateKeys1}} = dealer:deal(Dealer)
end,
PrivateKeys1 = tc_key_share:deal(N, F),
%% give actor 1 a completely unrelated key
%% this will prevent it from doing any valid threshold cryptography
%% and thus it will not be able to reach consensus
Expand Down Expand Up @@ -416,11 +390,8 @@ one_actor_corrupted_key_test(Config) ->
%% this will not prevent the actor for encrypting their bundle
%% merely prevent it producing valid decryption shares
%% thus all the actors will be able to converge
{Pos, Val} = case Curve of
'BLS12-381' -> {4, element(4, PK1) + 1000};
'SS512' -> {3, erlang_pbc:element_random(element(3, PK1))}
end,
PK2 = setelement(Pos, PK1, Val),
Pos = 4,
PK2 = setelement(Pos, PK1, element(Pos, PK1) + 1000),
PrivateKeys = [PK2 | PrivateKeys0],

Workers = [
Expand Down
Loading

0 comments on commit d558860

Please sign in to comment.