Refactor keypair into separate module enoise_keypair
This commit is contained in:
+1
-1
@@ -25,7 +25,7 @@
|
||||
-record(enoise, { pid }).
|
||||
|
||||
-type noise_key() :: binary().
|
||||
-type noise_keypair() :: term().
|
||||
-type noise_keypair() :: enoise_keypair:keypair().
|
||||
|
||||
-type noise_options() :: [noise_option()].
|
||||
-type noise_option() :: {noise, noise_protocol_option()} %% Required
|
||||
|
||||
+9
-19
@@ -14,30 +14,23 @@
|
||||
, hashlen/1
|
||||
, hkdf/3
|
||||
, hmac/3
|
||||
, new_key_pair/1
|
||||
, pad/3
|
||||
, pub_key/1
|
||||
, rekey/2
|
||||
]).
|
||||
|
||||
-record(key_pair, { puk, pik }).
|
||||
|
||||
-opaque key_pair() :: #key_pair{}.
|
||||
|
||||
-export_type([key_pair/0]).
|
||||
|
||||
-define(MAC_LEN, 16).
|
||||
|
||||
-spec new_key_pair(Algo :: enoise_hs_state:noise_dh()) -> key_pair().
|
||||
new_key_pair(dh25519) ->
|
||||
KeyPair = enacl:crypto_sign_ed25519_keypair(),
|
||||
#key_pair{ puk = enacl:crypto_sign_ed25519_public_to_curve25519(maps:get(public, KeyPair))
|
||||
, pik = enacl:crypto_sign_ed25519_secret_to_curve25519(maps:get(secret, KeyPair)) }.
|
||||
-type keypair() :: enoise_keypair:keypair().
|
||||
|
||||
%% @doc Perform a Diffie-Hellman calculation with the secret key from `Key1'
|
||||
%% and the public key from `Key2' with algorithm `Algo'.
|
||||
-spec dh(Algo :: enoise_hs_state:noise_dh(),
|
||||
PrivKey :: key_pair(), PubKey :: binary()) -> binary().
|
||||
dh(dh25519, KeyPair, PubKey) ->
|
||||
enacl:curve25519_scalarmult(KeyPair#key_pair.pik, PubKey).
|
||||
Key1:: keypair(), Key2 :: keypair()) -> binary().
|
||||
dh(dh25519, Key1, Key2) ->
|
||||
enacl:curve25519_scalarmult( enoise_keypair:seckey(Key1)
|
||||
, enoise_keypair:pubkey(Key2));
|
||||
dh(Type, _Key1, _Key2) ->
|
||||
error({unsupported_diffie_hellman, Type}).
|
||||
|
||||
-spec hmac(Hash :: enoise_sym_state:noise_hash(),
|
||||
Key :: binary(), Data :: binary()) -> binary().
|
||||
@@ -107,9 +100,6 @@ pad(Data, MinSize, PadByte) ->
|
||||
<<Data/binary, PadData/binary>>
|
||||
end.
|
||||
|
||||
-spec pub_key(KeyPair :: key_pair()) -> binary().
|
||||
pub_key(#key_pair{ puk = PubKey }) -> PubKey.
|
||||
|
||||
-spec hashlen(Hash :: enoise_sym_state:noise_hash()) -> non_neg_integer().
|
||||
hashlen(sha256) -> 32;
|
||||
hashlen(sha512) -> 64;
|
||||
|
||||
+27
-26
@@ -11,12 +11,13 @@
|
||||
-type noise_role() :: initiator | responder.
|
||||
-type noise_dh() :: dh25519 | dh448.
|
||||
-type noise_token() :: s | e | ee | ss | es | se.
|
||||
-type keypair() :: enoise_keypair:keypair().
|
||||
|
||||
-record(noise_hs, { ss :: enoise_sym_state:state()
|
||||
, s :: enoise_crypto:key_pair() | undefined
|
||||
, e :: enoise_crypto:key_pair() | undefined
|
||||
, rs :: binary() | undefined
|
||||
, re :: binary() | undefined
|
||||
, s :: keypair() | undefined
|
||||
, e :: keypair() | undefined
|
||||
, rs :: keypair() | undefined
|
||||
, re :: keypair() | undefined
|
||||
, role = initiator :: noise_role()
|
||||
, dh = dh25519 :: noise_dh()
|
||||
, msgs = [] :: [enoise_protocol:noise_msg()] }).
|
||||
@@ -24,7 +25,8 @@
|
||||
-export_type([noise_dh/0, noise_role/0, noise_token/0]).
|
||||
|
||||
-spec init(Protocol :: string() | enoise_protocol:protocol(),
|
||||
Role :: noise_role(), Prologue :: binary(), Keys :: term()) -> #noise_hs{}.
|
||||
Role :: noise_role(), Prologue :: binary(),
|
||||
Keys :: term()) -> #noise_hs{}.
|
||||
init(ProtocolName, Role, Prologue, Keys) when is_list(ProtocolName) ->
|
||||
init(enoise_protocol:from_name(ProtocolName), Role, Prologue, Keys);
|
||||
init(Protocol, Role, Prologue, {S, E, RS, RE}) ->
|
||||
@@ -36,10 +38,10 @@ init(Protocol, Role, Prologue, {S, E, RS, RE}) ->
|
||||
, dh = enoise_protocol:dh(Protocol)
|
||||
, msgs = enoise_protocol:msgs(Role, Protocol) },
|
||||
PreMsgs = enoise_protocol:pre_msgs(Role, Protocol),
|
||||
lists:foldl(fun({out, [s]}, HS0) -> mix_hash(HS0, enoise_crypto:pub_key(S));
|
||||
({out, [e]}, HS0) -> mix_hash(HS0, enoise_crypto:pub_key(E));
|
||||
({in, [s]}, HS0) -> mix_hash(HS0, RS);
|
||||
({in, [e]}, HS0) -> mix_hash(HS0, RE)
|
||||
lists:foldl(fun({out, [s]}, HS0) -> mix_hash(HS0, enoise_keypair:pubkey(S));
|
||||
({out, [e]}, HS0) -> mix_hash(HS0, enoise_keypair:pubkey(E));
|
||||
({in, [s]}, HS0) -> mix_hash(HS0, enoise_keypair:pubkey(RS));
|
||||
({in, [e]}, HS0) -> mix_hash(HS0, enoise_keypair:pubkey(RE))
|
||||
end, HS, PreMsgs).
|
||||
|
||||
finalize(#noise_hs{ msgs = [], ss = SS, role = Role }) ->
|
||||
@@ -79,30 +81,32 @@ read_message(HS, [Token | Tokens], Data0) ->
|
||||
|
||||
write_token(HS = #noise_hs{ e = undefined }, e) ->
|
||||
E = new_key_pair(HS),
|
||||
PubE = enoise_crypto:pub_key(E),
|
||||
PubE = enoise_keypair:pubkey(E),
|
||||
{mix_hash(HS#noise_hs{ e = E }, PubE), PubE};
|
||||
%% Should only apply during test - TODO: secure this
|
||||
write_token(HS = #noise_hs{ e = E }, e) ->
|
||||
PubE = enoise_crypto:pub_key(E),
|
||||
PubE = enoise_keypair:pubkey(E),
|
||||
{mix_hash(HS, PubE), PubE};
|
||||
write_token(HS = #noise_hs{ s = S }, s) ->
|
||||
{ok, HS1, Msg} = encrypt_and_hash(HS, enoise_crypto:pub_key(S)),
|
||||
{ok, HS1, Msg} = encrypt_and_hash(HS, enoise_keypair:pubkey(S)),
|
||||
{HS1, Msg};
|
||||
write_token(HS, Token) ->
|
||||
{K1, K2} = dh_token(HS, Token),
|
||||
{mix_key(HS, dh(HS, K1, K2)), <<>>}.
|
||||
|
||||
read_token(HS = #noise_hs{ re = undefined }, e, Data0) ->
|
||||
DHLen = dhlen(HS),
|
||||
<<RE:DHLen/binary, Data1/binary>> = Data0,
|
||||
{mix_hash(HS#noise_hs{ re = RE }, RE), Data1};
|
||||
read_token(HS = #noise_hs{ rs = undefined }, s, Data0) ->
|
||||
read_token(HS = #noise_hs{ re = undefined, dh = DH }, e, Data0) ->
|
||||
DHLen = enoise_crypto:dhlen(DH),
|
||||
<<REPub:DHLen/binary, Data1/binary>> = Data0,
|
||||
RE = enoise_keypair:new(DH, REPub),
|
||||
{mix_hash(HS#noise_hs{ re = RE }, REPub), Data1};
|
||||
read_token(HS = #noise_hs{ rs = undefined, dh = DH }, s, Data0) ->
|
||||
DHLen = case has_key(HS) of
|
||||
true -> dhlen(HS) + 16;
|
||||
false -> dhlen(HS)
|
||||
true -> enoise_crypto:dhlen(DH) + 16;
|
||||
false -> enoise_crypto:dhlen(DH)
|
||||
end,
|
||||
<<Temp:DHLen/binary, Data1/binary>> = Data0,
|
||||
{ok, HS1, RS} = decrypt_and_hash(HS, Temp),
|
||||
{ok, HS1, RSPub} = decrypt_and_hash(HS, Temp),
|
||||
RS = enoise_keypair:new(DH, RSPub),
|
||||
{HS1#noise_hs{ rs = RS }, Data1};
|
||||
read_token(HS, Token, Data) ->
|
||||
{K1, K2} = dh_token(HS, Token),
|
||||
@@ -117,13 +121,10 @@ dh_token(#noise_hs{ s = S, rs = RS } , ss) -> {S, RS}.
|
||||
|
||||
%% Local wrappers
|
||||
new_key_pair(#noise_hs{ dh = DH }) ->
|
||||
enoise_crypto:new_key_pair(DH).
|
||||
enoise_keypair:new(DH).
|
||||
|
||||
dh(#noise_hs{ dh = DH }, KeyPair, PubKey) ->
|
||||
enoise_crypto:dh(DH, KeyPair, PubKey).
|
||||
|
||||
dhlen(#noise_hs{ dh = DH }) ->
|
||||
enoise_crypto:dhlen(DH).
|
||||
dh(#noise_hs{ dh = DH }, Key1, Key2) ->
|
||||
enoise_crypto:dh(DH, Key1, Key2).
|
||||
|
||||
has_key(#noise_hs{ ss = SS }) ->
|
||||
CS = enoise_sym_state:cipher_state(SS),
|
||||
|
||||
@@ -0,0 +1,77 @@
|
||||
%%% ------------------------------------------------------------------
|
||||
%%% @copyright 2018, Aeternity Anstalt
|
||||
%%%
|
||||
%%% @doc Module is an abstract data type for a key pair.
|
||||
%%%
|
||||
%%% @end
|
||||
%%% ------------------------------------------------------------------
|
||||
|
||||
-module(enoise_keypair).
|
||||
|
||||
-export([ key_type/1
|
||||
, new/1
|
||||
, new/2
|
||||
, new/3
|
||||
, pubkey/1
|
||||
, seckey/1
|
||||
]).
|
||||
|
||||
-type key_type() :: dh25519 | dh448.
|
||||
|
||||
-record(kp, { type :: key_type()
|
||||
, sec :: binary() | undefined
|
||||
, pub :: binary() }).
|
||||
|
||||
-opaque keypair() :: #kp{}.
|
||||
%% Abstract keypair holding a secret key/public key pair and its type.
|
||||
|
||||
-export_type([keypair/0]).
|
||||
|
||||
%% @doc Generate a new keypair of type `Type'.
|
||||
-spec new(Type :: key_type()) -> keypair().
|
||||
new(Type) ->
|
||||
{Sec, Pub} = new_key_pair(Type),
|
||||
#kp{ type = Type, sec = Sec, pub = Pub }.
|
||||
|
||||
%% @doc Create a new keypair of type `Type'. If `Public' is `undefined'
|
||||
%% it will be computed from the `Secret' (using the curve/algorithm
|
||||
%% indicated by `Type').
|
||||
-spec new(Type :: key_type(), Secret :: binary(), Public :: binary() | undefined) -> keypair().
|
||||
new(Type, Secret, undefined) ->
|
||||
new(Type, Secret, pubkey_from_secret(Type, Secret));
|
||||
new(Type, Secret, Public) ->
|
||||
#kp{ type = Type, sec = Secret, pub = Public }.
|
||||
|
||||
%% @doc Define a "public only" keypair - holding just a public key and
|
||||
%% `undefined' for secret key.
|
||||
-spec new(Type :: key_type(), Public :: binary()) -> keypair().
|
||||
new(Type, Public) ->
|
||||
#kp{ type = Type, sec = undefined, pub = Public }.
|
||||
|
||||
%% @doc Accessor function - return the key type of the key pair.
|
||||
-spec key_type(KeyPair :: keypair()) -> key_type().
|
||||
key_type(#kp{ type = T }) ->
|
||||
T.
|
||||
|
||||
%% @doc Accessor function - return the public key of the key pair.
|
||||
-spec pubkey(KeyPair :: keypair()) -> binary().
|
||||
pubkey(#kp{ pub = P }) ->
|
||||
P.
|
||||
|
||||
%% @doc Accessor function - return the secret key of the key pair.
|
||||
-spec seckey(KeyPair :: keypair()) -> binary().
|
||||
seckey(#kp{ sec = undefined }) ->
|
||||
error(keypair_is_public_only);
|
||||
seckey(#kp{ sec = S }) ->
|
||||
S.
|
||||
|
||||
%% -- Local functions --------------------------------------------------------
|
||||
new_key_pair(dh25519) ->
|
||||
KeyPair = enacl:crypto_sign_ed25519_keypair(),
|
||||
{enacl:crypto_sign_ed25519_secret_to_curve25519(maps:get(secret, KeyPair)),
|
||||
enacl:crypto_sign_ed25519_public_to_curve25519(maps:get(public, KeyPair))};
|
||||
new_key_pair(Type) ->
|
||||
error({unsupported_key_type, Type}).
|
||||
|
||||
pubkey_from_secret(dh25519, Secret) ->
|
||||
enacl:curve25519_scalarmult_base(Secret).
|
||||
Reference in New Issue
Block a user