From d55886033c66ea4d38fd63472c0782a83ce81d18 Mon Sep 17 00:00:00 2001 From: Siraaj Khandkar Date: Thu, 1 Jul 2021 11:24:33 -0400 Subject: [PATCH] Remove PBC based cryptography fixes #70 --- rebar.config | 1 - rebar.lock | 8 - src/hbbft.app.src | 1 - src/hbbft.erl | 314 +++++++++---------------------- src/hbbft_cc.erl | 91 ++++----- src/hbbft_utils.erl | 25 +-- test/hbbft_SUITE.erl | 53 ++---- test/hbbft_cc_SUITE.erl | 51 +---- test/hbbft_distributed_SUITE.erl | 20 +- test/hbbft_test_utils.erl | 18 +- test/hbbft_worker.erl | 13 +- 11 files changed, 164 insertions(+), 431 deletions(-) diff --git a/rebar.config b/rebar.config index d8b35ec..8add6d1 100644 --- a/rebar.config +++ b/rebar.config @@ -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"} ]}. diff --git a/rebar.lock b/rebar.lock index 2488c37..61db0b2 100644 --- a/rebar.lock +++ b/rebar.lock @@ -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,[ diff --git a/src/hbbft.app.src b/src/hbbft.app.src index aa2d444..8eded42 100644 --- a/src/hbbft.app.src +++ b/src/hbbft.app.src @@ -7,7 +7,6 @@ stdlib, merkerl, erlang_tc, - erlang_tpke, erasure ]}, {env,[]}, diff --git a/src/hbbft.erl b/src/hbbft.erl index 3092c6c..3d47014 100644 --- a/src/hbbft.erl +++ b/src/hbbft.erl @@ -25,8 +25,6 @@ -ifdef(TEST). -export([ encrypt/3, - get_encrypted_key/2, - decrypt/2, encode_list/1, abstraction_breaking_set_acs_results/2, abstraction_breaking_set_enc_keys/2 @@ -34,7 +32,7 @@ -endif. -type acs_results() :: [{non_neg_integer(), binary()}]. --type enc_keys() :: #{non_neg_integer() => tc_ciphertext:ciphertext() | tpke_pubkey:ciphertext()}. +-type enc_keys() :: #{non_neg_integer() => tc_ciphertext:ciphertext()}. -record(hbbft_data, { batch_size :: pos_integer(), @@ -55,19 +53,19 @@ enc_keys = #{} :: enc_keys(), dec_shares = #{} :: #{ {non_neg_integer(), non_neg_integer()} => - {boolean() | undefined, {non_neg_integer(), tc_decryption_share:dec_share()} | erlang_pbc:element()} + {boolean() | undefined, {non_neg_integer(), tc_decryption_share:dec_share()}} }, decrypted = #{} :: #{non_neg_integer() => [binary()]}, - sig_shares = #{} :: #{non_neg_integer() => {non_neg_integer(), tc_signature_share:sig_share() | erlang_pbc:element()}}, - thingtosign :: undefined | binary() | erlang_pbc:element(), + sig_shares = #{} :: #{non_neg_integer() => {non_neg_integer(), tc_signature_share:sig_share()}}, + thingtosign :: undefined | binary(), stampfun :: undefined | {atom(), atom(), list()}, stamps = [] :: [{non_neg_integer(), binary()}], failed_combine = [] :: [non_neg_integer()], failed_decrypt = [] :: [non_neg_integer()] }). --type curve() :: 'SS512' | 'BLS12-381'. --type key_share() :: undefined | tc_key_share:tc_key_share() | tpke_privkey:privkey(). +-type curve() :: 'BLS12-381'. +-type key_share() :: undefined | tc_key_share:tc_key_share(). -type hbbft_data() :: #hbbft_data{}. -type acs_msg() :: {{acs, non_neg_integer()}, hbbft_acs:msgs()}. -type dec_msg() :: {dec, Round :: non_neg_integer(), ActorID :: non_neg_integer(), SerializedShare :: binary()}. @@ -270,20 +268,14 @@ input(Data = #hbbft_data{buf = _Buf}, _Txn) when is_binary(_Txn) -> %% should not be placed in TransactionsToRemove. Once this returns, the user should call next_round/1. -spec finalize_round(hbbft_data(), [binary()], binary()) -> {hbbft_data(), {send, [hbbft_utils:multicast(sign_msg())]}}. -finalize_round(Data, TransactionsToRemove, ThingToSign0) -> +finalize_round(#hbbft_data{curve='BLS12-381'}=Data, TransactionsToRemove, ThingToSign) -> NewBuf = lists:filter( fun(Item) -> not lists:member(Item, TransactionsToRemove) end, Data#hbbft_data.buf ), - {SigShare, ThingToSign} = case Data#hbbft_data.curve of - 'BLS12-381' -> - {tc_key_share:sign_share(Data#hbbft_data.key_share, ThingToSign0), ThingToSign0}; - 'SS512' -> - HashThing = tpke_pubkey:hash_message(tpke_privkey:public_key(Data#hbbft_data.key_share), ThingToSign0), - {tpke_privkey:sign(Data#hbbft_data.key_share, HashThing), HashThing} - end, + SigShare = tc_key_share:sign_share(Data#hbbft_data.key_share, ThingToSign), BinSigShare = hbbft_utils:sig_share_to_binary(Data#hbbft_data.curve, SigShare), %% multicast the signature to everyone {Data#hbbft_data{thingtosign = ThingToSign, buf = NewBuf}, @@ -385,7 +377,7 @@ buf(Buf, Data) -> handle_msg(Data = #hbbft_data{round = R}, _J, {{acs, R2}, _ACSMsg}) when R2 > R -> %% ACS requested we defer this message for now {Data, defer}; -handle_msg(Data = #hbbft_data{round = R}, J, {{acs, R}, ACSMsg}) -> +handle_msg(Data = #hbbft_data{round = R, curve = 'BLS12-381'}, J, {{acs, R}, ACSMsg}) -> %% ACS message for this round case hbbft_acs:handle_msg(Data#hbbft_data.acs, J, ACSMsg) of ignore -> @@ -401,27 +393,11 @@ handle_msg(Data = #hbbft_data{round = R}, J, {{acs, R}, ACSMsg}) -> {Replies, Results, EncKeys} = lists:foldl( fun({I, Result}, {RepliesAcc, ResultsAcc, EncKeysAcc} = Acc) -> %% this function will validate the ciphertext is consistent with our key - {EncKey, KeyIsValid} = case Data#hbbft_data.curve of - 'BLS12-381' -> - EncKey0 = tc_ciphertext:deserialize(Result), - {EncKey0, tc_ciphertext:verify(EncKey0)}; - 'SS512' -> - case get_encrypted_key(Data#hbbft_data.key_share, Result) of - {ok, EncKey0} -> - {EncKey0, true}; - error -> - {nothing, false} - end - end, - case KeyIsValid of + EncKey = tc_ciphertext:deserialize(Result), + case tc_ciphertext:verify(EncKey) of true -> %% we've validated the ciphertext, this is now safe to do - Share = case Data#hbbft_data.curve of - 'BLS12-381' -> - tc_key_share:decrypt_share(Data#hbbft_data.key_share, EncKey); - 'SS512' -> - tpke_privkey:decrypt_share(Data#hbbft_data.key_share, EncKey) - end, + Share = tc_key_share:decrypt_share(Data#hbbft_data.key_share, EncKey), SerializedShare = hbbft_utils:dec_share_to_binary(Data#hbbft_data.curve, Share), {[ {multicast, {dec, Data#hbbft_data.round, I, SerializedShare}} @@ -450,16 +426,12 @@ handle_msg(Data = #hbbft_data{round = R}, J, {{acs, R}, ACSMsg}) -> -> case maps:find(I, EncKeys) of {ok, EncKey} -> - Valid = case Data#hbbft_data.curve of - 'BLS12-381' -> - tc_key_share:verify_decryption_share( - Data#hbbft_data.key_share, - Share, - EncKey - ); - 'SS512' -> - tpke_pubkey:verify_share(tpke_privkey:public_key(Data#hbbft_data.key_share), Share, EncKey) - end, + Valid = + tc_key_share:verify_decryption_share( + Data#hbbft_data.key_share, + Share, + EncKey + ), {Valid, Share}; error -> %% this is a share for an RBC we will never decode @@ -493,7 +465,7 @@ handle_msg(Data = #hbbft_data{round = R}, J, {{acs, R}, ACSMsg}) -> end; handle_msg(Data = #hbbft_data{round = R}, _J, {dec, R2, _I, _Share}) when R2 > R -> {Data, defer}; -handle_msg(Data = #hbbft_data{round = R, curve = Curve, key_share = SK}, J, {dec, R, I, Share}) -> +handle_msg(Data = #hbbft_data{round = R, curve = Curve = 'BLS12-381', key_share = SK}, J, {dec, R, I, Share}) -> %% check if we have enough to decode the bundle %% have we already decrypted for this instance? @@ -524,16 +496,12 @@ handle_msg(Data = #hbbft_data{round = R, curve = Curve, key_share = SK}, J, {dec case maps:find(I1, Data#hbbft_data.enc_keys) of {ok, EncKey} -> %% we validated the ciphertext above so we don't need to re-check it here - Valid = case Data#hbbft_data.curve of - 'BLS12-381' -> - tc_key_share:verify_decryption_share( - Data#hbbft_data.key_share, - AShare, - EncKey - ); - 'SS512' -> - tpke_pubkey:verify_share(tpke_privkey:public_key(Data#hbbft_data.key_share), AShare, EncKey) - end, + Valid = + tc_key_share:verify_decryption_share( + Data#hbbft_data.key_share, + AShare, + EncKey + ), {Valid, AShare}; error -> %% this is a share for an RBC we will never decode @@ -578,63 +546,42 @@ handle_msg(Data = #hbbft_data{round = R, curve = Curve, key_share = SK}, J, {dec false -> {Data#hbbft_data{dec_shares = NewShares}, ok} end; - Decrypted0 -> - Decrypted = case Data#hbbft_data.curve of - 'BLS12-381' -> - %% the ciphertext is direct in this mode - Decrypted0; - 'SS512' -> - %% we decrypted the key only - {I, Enc} = lists:keyfind(I, 1, Data#hbbft_data.acs_results), - decrypt(Decrypted0, Enc) - end, - case Decrypted of - error -> - %% this only happens for SS512 - %% can't decrypt, consider this ACS a failure - %% just declare this ACS returned an empty list because we had - %% f+1 valid shares but the resulting decryption key was unusuable to decrypt - %% the transaction bundle + Decrypted -> + #hbbft_data{batch_size = B, n = N} = Data, + case decode_list(Decrypted, []) of + [_Stamp | Transactions] when length(Transactions) > (B div N) -> + % Batch exceeds agreed-upon size. + % Ignoring this proposal. + check_completion( + Data#hbbft_data{ + dec_shares = + NewShares, + decrypted = + maps:put(I, [], Data#hbbft_data.decrypted) + } + ); + [Stamp | Transactions] -> + NewDecrypted = maps:put( + I, + Transactions, + Data#hbbft_data.decrypted + ), + Stamps = [{I, Stamp} | Data#hbbft_data.stamps], + check_completion(Data#hbbft_data{ + dec_shares = NewShares, + decrypted = NewDecrypted, + stamps = Stamps + }); + {error, _} -> + %% this is an invalid proposal. Because the shares are verifiable + %% we know that everyone will fail to decrypt so we declare this as an empty share, + %% as in the decryption failure case above, and continue + %% TODO track failed decodes like we track failed decrypts NewDecrypted = maps:put(I, [], Data#hbbft_data.decrypted), - check_completion(Data#hbbft_data{dec_shares=NewShares, decrypted=NewDecrypted, - failed_decrypt=[I|Data#hbbft_data.failed_decrypt]}); - _ -> - #hbbft_data{batch_size = B, n = N} = Data, - case decode_list(Decrypted, []) of - [_Stamp | Transactions] when length(Transactions) > (B div N) -> - % Batch exceeds agreed-upon size. - % Ignoring this proposal. - check_completion( - Data#hbbft_data{ - dec_shares = - NewShares, - decrypted = - maps:put(I, [], Data#hbbft_data.decrypted) - } - ); - [Stamp | Transactions] -> - NewDecrypted = maps:put( - I, - Transactions, - Data#hbbft_data.decrypted - ), - Stamps = [{I, Stamp} | Data#hbbft_data.stamps], - check_completion(Data#hbbft_data{ - dec_shares = NewShares, - decrypted = NewDecrypted, - stamps = Stamps - }); - {error, _} -> - %% this is an invalid proposal. Because the shares are verifiable - %% we know that everyone will fail to decrypt so we declare this as an empty share, - %% as in the decryption failure case above, and continue - %% TODO track failed decodes like we track failed decrypts - NewDecrypted = maps:put(I, [], Data#hbbft_data.decrypted), - check_completion(Data#hbbft_data{ - dec_shares = NewShares, - decrypted = NewDecrypted - }) - end + check_completion(Data#hbbft_data{ + dec_shares = NewShares, + decrypted = NewDecrypted + }) end end; false -> @@ -646,51 +593,42 @@ handle_msg(Data = #hbbft_data{round = R, thingtosign = ThingToSign}, _J, {sign, ThingToSign == undefined orelse R2 > R -> {Data, defer}; -handle_msg(Data = #hbbft_data{round = R, thingtosign = ThingToSign, curve = Curve, key_share = SK}, J, {sign, R, BinShare}) when +handle_msg(Data = #hbbft_data{round = R, thingtosign = ThingToSign, curve = Curve = 'BLS12-381', key_share = SK}, J, {sign, R, BinShare}) when ThingToSign /= undefined -> %% messages related to signing the final block for this round, see finalize_round for more information %% Note: this is an extension to the HoneyBadger BFT specification Share = hbbft_utils:binary_to_sig_share(Curve, SK, BinShare), %% verify the share - ValidShare = case Data#hbbft_data.curve of - 'BLS12-381' -> - tc_key_share:verify_signature_share(Data#hbbft_data.key_share, Share, ThingToSign); - 'SS512' -> - tpke_pubkey:verify_signature_share(tpke_privkey:public_key(Data#hbbft_data.key_share), Share, ThingToSign) - end, - case ValidShare of + case + tc_key_share:verify_signature_share( + Data#hbbft_data.key_share, + Share, + ThingToSign + ) + of true -> NewSigShares = maps:put(J, Share, Data#hbbft_data.sig_shares), %% check if we have at least f+1 shares case maps:size(NewSigShares) > Data#hbbft_data.f andalso not Data#hbbft_data.sent_sig of true -> %% ok, we have enough people agreeing with us we can combine the signature shares - {ok, Sig} = case Data#hbbft_data.curve of - 'BLS12-381' -> - tc_key_share:combine_signature_shares( - Data#hbbft_data.key_share, - maps:values(NewSigShares)); - 'SS512' -> - tpke_pubkey:combine_verified_signature_shares(tpke_privkey:public_key(Data#hbbft_data.key_share), maps:values(NewSigShares)) - end, - ValidSignature = case Data#hbbft_data.curve of - 'BLS12-381' -> - tc_key_share:verify(Data#hbbft_data.key_share, Sig, ThingToSign); - 'SS512' -> - tpke_pubkey:verify_signature(tpke_privkey:public_key(Data#hbbft_data.key_share), Sig, ThingToSign) - end, + {ok, Sig} = + tc_key_share:combine_signature_shares( + Data#hbbft_data.key_share, + maps:values(NewSigShares) + ), + ValidSignature = + tc_key_share:verify( + Data#hbbft_data.key_share, + Sig, + ThingToSign + ), case ValidSignature of true -> - SerializedSig = case Data#hbbft_data.curve of - 'BLS12-381' -> - tc_signature:serialize(Sig); - 'SS512' -> - erlang_pbc:element_to_binary(Sig) - end, %% verified signature, send the signature {Data#hbbft_data{sig_shares = NewSigShares, sent_sig = true}, - {result, {signature, SerializedSig}}}; + {result, {signature, tc_signature:serialize(Sig)}}}; false -> %% must have duplicate signature shares, keep waiting {Data#hbbft_data{sig_shares = NewSigShares}, ok} @@ -755,40 +693,9 @@ maybe_start_acs( {Data, ok} end. --spec encrypt('SS512', tpke_pubkey:pubkey(), binary()) -> binary(); - ('BLS12-381', tc_key_share:tc_key_share(), binary()) -> binary(). +-spec encrypt('BLS12-381', tc_key_share:tc_key_share(), binary()) -> binary(). encrypt('BLS12-381', SK, Bin) -> - tc_ciphertext:serialize(tc_key_share:encrypt(SK, Bin)); -encrypt('SS512', SK, Bin) -> - PK = tpke_privkey:public_key(SK), - %% generate a random AES key and IV - Key = crypto:strong_rand_bytes(32), - IV = crypto:strong_rand_bytes(16), - %% encrypt that random AES key with the HBBFT replica set's public key - %% the result of the encryption is a 3-tuple that contains 2 PBC Elements and a 32 byte binary - %% we need to encode all this crap into a binary value that we can unpack again sanely - EncryptedKey = tpke_pubkey:encrypt(PK, Key), - EncryptedKeyBin = tpke_pubkey:ciphertext_to_binary(EncryptedKey), - %% encrypt the bundle with AES-GCM and put the IV and the encrypted key in the Additional Authenticated Data (AAD) - AAD = <>, - {CipherText, CipherTag} = ?ENCRYPT(Key, IV, AAD, Bin, 16), - %% assemble a final binary packet - <>. - --spec get_encrypted_key(tpke_privkey:privkey(), binary()) -> {ok, tpke_pubkey:ciphertext()} | error. -get_encrypted_key(SK, <<_IV:16/binary, EncKeySize:16/integer-unsigned, EncKey:EncKeySize/binary, _/binary>>) -> - PubKey = tpke_privkey:public_key(SK), - try tpke_pubkey:binary_to_ciphertext(EncKey, PubKey) of - CipherText -> - {ok, CipherText} - catch error:inconsistent_ciphertext -> - error - end. - --spec decrypt(binary(), binary()) -> binary() | error. -decrypt(Key, Bin) -> - <> = Bin, - ?DECRYPT(Key, IV, <>, CipherText, Tag). + tc_ciphertext:serialize(tc_key_share:encrypt(SK, Bin)). -spec serialize(hbbft_data()) -> {#{atom() => binary() | map()}, binary() | tc_key_share:tc_key_share()}. @@ -801,15 +708,9 @@ serialize(Data) -> serialize(#hbbft_data{key_share = SK} = Data, false) -> %% dont serialize the private key {serialize_hbbft_data(Data), SK}; -serialize(#hbbft_data{key_share = SK, curve=Curve} = Data, true) -> +serialize(#hbbft_data{key_share = SK, curve='BLS12-381'} = Data, true) -> %% serialize the private key as well - SerSK = case Curve of - 'BLS12-381' -> - tc_key_share:serialize(SK); - 'SS512' -> - tpke_privkey:serialize(SK) - end, - {serialize_hbbft_data(Data), SerSK}. + {serialize_hbbft_data(Data), tc_key_share:serialize(SK)}. -spec deserialize(#{atom() => binary() | map()}, tc_key_share:tc_key_share()) -> hbbft_data(). deserialize(M0, SK) -> @@ -844,14 +745,7 @@ deserialize(M0, SK) -> Curve = hbbft_utils:curve(SK), EncKeys = maps:map( - fun(_, Ciphertext) -> - case Curve of - 'BLS12-381' -> - tc_ciphertext:deserialize(Ciphertext); - 'SS512' -> - tpke_pubkey:binary_to_ciphertext(Ciphertext, tpke_privkey:public_key(SK)) - end - end, + fun(_, Ciphertext) -> tc_ciphertext:deserialize(Ciphertext) end, EncKeys0 ), #hbbft_data{ @@ -925,13 +819,7 @@ serialize_hbbft_data(#hbbft_data{ j => J, sent_sig => SentSig, acs_results => ACSResults, - enc_keys => maps:map(fun(_, Ciphertext) -> case Curve of - 'BLS12-381' -> - tc_ciphertext:serialize(Ciphertext); - 'SS512' -> - tpke_pubkey:ciphertext_to_binary(Ciphertext) - end - end, EncKeys), + enc_keys => maps:map(fun(_, Ciphertext) -> tc_ciphertext:serialize(Ciphertext) end, EncKeys), dec_shares => maps:map( fun(_, {Valid, Share}) -> {Valid, hbbft_utils:dec_share_to_binary(Curve, Share)} end, DecShares @@ -993,34 +881,18 @@ check_completion(Data) -> tc_key_share:tc_key_share(), [{non_neg_integer(), tc_decryption_share:dec_share()}], tc_ciphertext:ciphertext() - ) -> undefined | binary(); - ( - 'SS512', - pos_integer(), - tpke_privkey:privkey(), - [{non_neg_integer(), erlang_pbc:element()}], - erlang_pbc:element() ) -> undefined | binary(). -combine_shares(Curve, F, SK, SharesForThisBundle, Ciphertext) -> +combine_shares('BLS12-381', F, SK, SharesForThisBundle, Ciphertext) -> %% only use valid shares so an invalid share doesn't corrupt our result ValidSharesForThisBundle = [S || {true, S} <- SharesForThisBundle], case length(ValidSharesForThisBundle) > F of true -> - case Curve of - 'BLS12-381' -> - {ok, Bin} = tc_key_share:combine_decryption_shares( - SK, - ValidSharesForThisBundle, - Ciphertext - ), - Bin; - 'SS512' -> - tpke_pubkey:combine_shares( - tpke_privkey:public_key(SK), - Ciphertext, - ValidSharesForThisBundle - ) - end; + {ok, Bin} = tc_key_share:combine_decryption_shares( + SK, + ValidSharesForThisBundle, + Ciphertext + ), + Bin; false -> %% not enough valid shares to bother trying to combine them undefined diff --git a/src/hbbft_cc.erl b/src/hbbft_cc.erl index f4d47e5..ab01e08 100644 --- a/src/hbbft_cc.erl +++ b/src/hbbft_cc.erl @@ -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()}} @@ -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 @@ -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 @@ -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 @@ -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! - <> = case Curve of - 'BLS12-381' -> tc_signature:serialize(Sig); - 'SS512' -> erlang_pbc:element_to_binary(Sig) - end, + <> = tc_signature:serialize(Sig), {NewData#cc_data{state=done}, {result, Val}}; false -> {NewData, ok} @@ -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{ @@ -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. 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. diff --git a/src/hbbft_utils.erl b/src/hbbft_utils.erl index 3728f57..d7ac93a 100644 --- a/src/hbbft_utils.erl +++ b/src/hbbft_utils.erl @@ -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), - <>; -sig_share_to_binary('SS512', {ShareIdx, SigShare}) -> - ShareBinary = erlang_pbc:element_to_binary(SigShare), <>. binary_to_sig_share('BLS12-381', _SK, <>) -> SigShare = tc_signature_share:deserialize(ShareBinary), - {ShareIdx, SigShare}; -binary_to_sig_share('SS512', SK, <>) -> - 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), - <>; -dec_share_to_binary('SS512', {ShareIdx, DecShare}) -> - %% Assume less than 256 members in the consensus group - ShareBinary = erlang_pbc:element_to_binary(DecShare), <>. binary_to_dec_share('BLS12-381', _SK, <>) -> DecShare = tc_decryption_share:deserialize(ShareBinary), - {ShareIdx, DecShare}; -binary_to_dec_share('SS512', SK, <>) -> - 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}}]. @@ -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'. diff --git a/test/hbbft_SUITE.erl b/test/hbbft_SUITE.erl index 2707dcc..f8ae999 100644 --- a/test/hbbft_SUITE.erl +++ b/test/hbbft_SUITE.erl @@ -21,7 +21,7 @@ ]). all() -> - [{group, ss512}, {group, bls12_381}]. + [{group, bls12_381}]. test_cases() -> [ @@ -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]. @@ -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) -> @@ -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) -> @@ -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 @@ -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 = [ diff --git a/test/hbbft_cc_SUITE.erl b/test/hbbft_cc_SUITE.erl index c21c95d..2f8d545 100644 --- a/test/hbbft_cc_SUITE.erl +++ b/test/hbbft_cc_SUITE.erl @@ -1,6 +1,5 @@ -module(hbbft_cc_SUITE). --include_lib("common_test/include/ct.hrl"). -include_lib("eunit/include/eunit.hrl"). -export([all/0, groups/0, init_per_group/2, end_per_group/2, init_per_testcase/2, end_per_testcase/2]). @@ -15,7 +14,7 @@ ]). all() -> - [{group, ss512}, {group, bls12_381}]. + [{group, bls12_381}]. test_cases() -> [ @@ -29,11 +28,8 @@ test_cases() -> ]. groups() -> - [{ss512, [], test_cases()}, - {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]. @@ -44,15 +40,7 @@ init_per_testcase(_, Config) -> N = list_to_integer(os:getenv("N", "34")), F = N div 4, Module = hbbft_cc, - - case proplists:get_value(curve, Config, 'BLS12-381') of - 'BLS12-381' -> - KeyShares = tc_key_share:deal(N, F); - 'SS512' -> - {ok, Dealer} = dealer:new(N, F+1, 'SS512'), - {ok, {_PubKey, KeyShares}} = dealer:deal(Dealer) - end, - + KeyShares = tc_key_share:deal(N, F), [{n, N}, {f, F}, {key_shares, KeyShares}, {module, Module} | Config]. end_per_testcase(_, _Config) -> @@ -160,17 +148,10 @@ too_many_dead_test(Config) -> key_mismatch_f9_test(Config) -> N = proplists:get_value(n, Config), F = proplists:get_value(f, Config), - Curve = proplists:get_value(curve, Config), + 'BLS12-381' = proplists:get_value(curve, Config), Module = proplists:get_value(module, Config), PrivateKeys = proplists:get_value(key_shares, Config), - PrivateKeys2 = case Curve of - 'BLS12-381' -> - tc_key_share:deal(N, F); - 'SS512' -> - {ok, Dealer} = dealer:new(N, F+1, 'SS512'), - {ok, {_PubKey, KeyShares}} = dealer:deal(Dealer), - KeyShares - end, + PrivateKeys2 = tc_key_share:deal(N, F), Sid = crypto:strong_rand_bytes(32), %% choose 20 from pk1 %% choose 17 from pk2 @@ -194,17 +175,10 @@ key_mismatch_f9_test(Config) -> key_mismatch_f10_test(Config) -> N = proplists:get_value(n, Config), F = proplists:get_value(f, Config) + 1, - Curve = proplists:get_value(curve, Config), + 'BLS12-381' = proplists:get_value(curve, Config), Module = proplists:get_value(module, Config), PrivateKeys = proplists:get_value(key_shares, Config), - PrivateKeys2 = case Curve of - 'BLS12-381' -> - tc_key_share:deal(N, F); - 'SS512' -> - {ok, Dealer} = dealer:new(N, F+1, 'SS512'), - {ok, {_PubKey, KeyShares}} = dealer:deal(Dealer), - KeyShares - end, + PrivateKeys2 = tc_key_share:deal(N, F), Sid = crypto:strong_rand_bytes(32), InitialStates = [hbbft_cc:init(Sk, Sid, N, F) || Sk <- lists:sublist(PrivateKeys, N-F) ++ lists:sublist(PrivateKeys2, F)], StatesWithId = lists:zip(lists:seq(0, N - 1), InitialStates), @@ -227,17 +201,10 @@ key_mismatch_f10_test(Config) -> mixed_keys_test(Config) -> N = proplists:get_value(n, Config), F = proplists:get_value(f, Config), - Curve = proplists:get_value(curve, Config), + 'BLS12-381' = proplists:get_value(curve, Config), Module = proplists:get_value(module, Config), PrivateKeys = proplists:get_value(key_shares, Config), - PrivateKeys2 = case Curve of - 'BLS12-381' -> - tc_key_share:deal(N, F); - 'SS512' -> - {ok, Dealer} = dealer:new(N, F+1, 'SS512'), - {ok, {_PubKey, KeyShares}} = dealer:deal(Dealer), - KeyShares - end, + PrivateKeys2 = tc_key_share:deal(N, F), Sid = crypto:strong_rand_bytes(32), InitialState1 = [hbbft_cc:init(Sk, Sid, N, F) || Sk <- PrivateKeys], diff --git a/test/hbbft_distributed_SUITE.erl b/test/hbbft_distributed_SUITE.erl index 889acee..7af38da 100644 --- a/test/hbbft_distributed_SUITE.erl +++ b/test/hbbft_distributed_SUITE.erl @@ -1,8 +1,5 @@ -module(hbbft_distributed_SUITE). --include_lib("common_test/include/ct.hrl"). --include_lib("kernel/include/inet.hrl"). - -export([ groups/0, init_per_group/2, @@ -17,14 +14,11 @@ -export([simple_test/1, serialization_test/1, partition_test/1, partition_and_filter_test/1]). all() -> - [{group, ss512}, {group, bls12_381}]. + [{group, bls12_381}]. groups() -> - [{ss512, [], test_cases()}, - {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]. @@ -497,11 +491,5 @@ keyshares(Config) -> Nodes = proplists:get_value(nodes, Config), N = length(Nodes), F = (N div 3), - case proplists:get_value(curve, Config, 'BLS12-381') of - 'BLS12-381' -> - KeyShares = tc_key_share:deal(N, F); - 'SS512' -> - {ok, Dealer} = dealer:new(N, F+1, 'SS512'), - {ok, {_PubKey, KeyShares}} = dealer:deal(Dealer) - end, - KeyShares. + 'BLS12-381' = proplists:get_value(curve, Config), + tc_key_share:deal(N, F). diff --git a/test/hbbft_test_utils.erl b/test/hbbft_test_utils.erl index a30aeb0..86b2fd8 100644 --- a/test/hbbft_test_utils.erl +++ b/test/hbbft_test_utils.erl @@ -2,21 +2,11 @@ -export([serialize_key/2, deserialize_key/1, do_send_outer/4, shuffle/1, random_n/2, enumerate/1, merge_replies/3]). -serialize_key(Curve, SK) -> - case Curve of - 'BLS12-381' -> - {Curve, tc_key_share:serialize(SK)}; - 'SS512' -> - {Curve, tpke_privkey:serialize(SK)} - end. +serialize_key('BLS12-381'=Curve, SK) -> + {Curve, tc_key_share:serialize(SK)}. -deserialize_key({Curve, SerKey}) -> - case Curve of - 'BLS12-381' -> - tc_key_share:deserialize(SerKey); - 'SS512' -> - tpke_privkey:deserialize(SerKey) - end. +deserialize_key({'BLS12-381', SerKey}) -> + tc_key_share:deserialize(SerKey). % TODO Type of Acc elements % TODO Type of States elements diff --git a/test/hbbft_worker.erl b/test/hbbft_worker.erl index 3d12d97..c527a1d 100644 --- a/test/hbbft_worker.erl +++ b/test/hbbft_worker.erl @@ -92,16 +92,9 @@ verify_block_fit([A, B | _], KeyShare) -> end. verify_block_signature(KeyShare, A) -> - case tc_key_share:is_key_share(KeyShare) of - true -> - Signature = tc_signature:deserialize(A#block.signature), - tc_key_share:verify(KeyShare, Signature, term_to_binary(A#block{signature= <<>>})); - false -> - PubKey = tpke_privkey:public_key(KeyShare), - HM = tpke_pubkey:hash_message(PubKey, term_to_binary(A#block{signature= <<>>})), - Signature = tpke_pubkey:deserialize_element(PubKey, A#block.signature), - tpke_pubkey:verify_signature(PubKey, Signature, HM) - end. + true = tc_key_share:is_key_share(KeyShare), + Signature = tc_signature:deserialize(A#block.signature), + tc_key_share:verify(KeyShare, Signature, term_to_binary(A#block{signature= <<>>})). block_transactions(Block) ->