Compare commits

...

10 Commits

Author SHA1 Message Date
029292817e Merge pull request 'Ditch enacl, support DH448 and Blake2s, and fix types (#14)' (#2) from hanssv-remove-enoise into master
Reviewed-on: #2
2025-03-30 05:02:52 +09:00
Hans Svensson
2b5f08e156 Ditch enacl, support DH448 and Blake2s, and fix types (#14)
* Remove get_stacktrace (deprecated since OTP-24)

* Add DH448 support and switch to crypto:generate_key for DH25519

* Switch to crypto:hash/2 for Blake2b and support Blake2s

* Switch last enacl calls to crypto - no more enacl

* Eqwalizer fixes

Ewqalizer fix

Eqwalizer fix

Eqwalizer fix

Eqwalizer fix

Eqwalizer support

Eqwalizer fix

Fix tests to follow types (remote keys)

* More error handling on setup

* Dialyzer fix

* Write CHANGELOG

* Note about type-checking in README
2025-03-30 05:02:35 +09:00
Ulf Wiger
91916908a0 Revert "Update enacl dep and fix some minor details (#1)"
This reverts commit 479ec7087031b3b69ca2dd9b5c8e471d0a1125cd.
2025-03-29 20:57:45 +01:00
479ec70870 Update enacl dep and fix some minor details (#1)
Co-authored-by: Ulf Wiger <ulf@wiger.net>
Reviewed-on: #1
2025-03-08 00:28:12 +09:00
Hans Svensson
8acbce9269
Merge pull request #13 from aeternity/prepare_1.2.0
Bump version to 1.2.0
2021-10-28 15:46:01 +02:00
Hans Svensson
be39bbc464 Bump version to 1.2.0 2021-10-28 15:35:00 +02:00
Hans Svensson
dd94b371e6
Merge pull request #12 from aeternity/support-otp-24
Use new crypto:block_encrypt api
2021-10-28 15:05:56 +02:00
Hans Svensson
11ca32c72f
Merge pull request #11 from lrascao/fix-rekey
Fix rekey, improve coverage
2021-10-28 15:00:58 +02:00
Sean Hinde
71300ba5b6 Use new crypto:block_encrypt api 2021-10-28 14:54:41 +02:00
Luis Rascao
ffde489e53 Fix rekey, improve coverage
ChaChaPoly key is expected to be 256 bits long. It's safe to disregard
the MAC portion.
2021-04-27 15:40:58 +01:00
17 changed files with 147 additions and 92 deletions

View File

@ -6,15 +6,21 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
## [Unreleased] ## [Unreleased]
### Added ### Added
- Support for 448 DH function and Blake2s hash function.
### Changed ### Changed
- Using `crypto` over `enacl` (and removing a call to `get_stacktrace/1`) makes `enoise`
up to date for (at least) OTP-27.
- Added test dependency `eqwalizer_support` to enable checking types with Eqwalizer.
### Removed ### Removed
- The dependency on `enacl` is not needed anymore, OTP's `crypto` library now cover all
necessary operations.
## [4.3.1] - 2020-04-21 ## [1.2.0] - 2021-10-28
### Added ### Added
### Changed ### Changed
- Fixed included compiler binary file, which was broken due to incorrect local system dependencies. - Use the new AEAD crypto interface introduced in OTP 22. This makes `enoise` OPT 24 compatible
Because the aesophia version hasn't changed, the compiler in this release but it also means it no longer works on OTP 21 and earlier. You can't win them all.
continues to report as `v4.3.0`. - Fixed ChaChaPoly20 rekey
### Removed ### Removed
## [1.1.0] - 2020-09-24 ## [1.1.0] - 2020-09-24
@ -41,7 +47,8 @@ Initial version the following map describe what is supported:
, dh => [dh25519] } , dh => [dh25519] }
``` ```
[Unreleased]: https://github.com/aeternity/aesophia_cli/compare/v1.1.0...HEAD [Unreleased]: https://github.com/aeternity/aesophia_cli/compare/v1.2.0...HEAD
[1.2.0]: https://github.com/aeternity/aesophia_cli/compare/v1.1.0...v1.2.0
[1.1.0]: https://github.com/aeternity/aesophia_cli/compare/v1.0.1...v1.1.0 [1.1.0]: https://github.com/aeternity/aesophia_cli/compare/v1.0.1...v1.1.0
[1.0.1]: https://github.com/aeternity/aesophia_cli/compare/v1.0.0...v1.0.1 [1.0.1]: https://github.com/aeternity/aesophia_cli/compare/v1.0.0...v1.0.1
[1.0.0]: https://github.com/aeternity/enoise/releases/tag/v1.0.0 [1.0.0]: https://github.com/aeternity/enoise/releases/tag/v1.0.0

View File

@ -39,3 +39,9 @@ Test
---- ----
$ rebar3 eunit $ rebar3 eunit
Typecheck
---------
$ rebar3 dialyzer
$ elp --eqwalize-all --rebar

View File

@ -1,8 +1,10 @@
{erl_opts, [debug_info]}. {erl_opts, [debug_info]}.
{plugins, [rebar3_hex]}. {plugins, [rebar3_hex]}.
{deps, [{enacl, "1.1.1"}]}.
{profiles, [{test, [{deps, [{jsx, {git, "https://github.com/talentdeficit/jsx.git", {tag, "2.8.0"}}}]}]} {profiles, [{test, [{deps, [ {jsx, {git, "https://github.com/talentdeficit/jsx.git", {tag, "2.8.0"}}}
, {eqwalizer_support, {git_subdir, "https://github.com/whatsapp/eqwalizer.git", {branch, "main"}, "eqwalizer_support"}}
]}
]}
]}. ]}.
{xref_checks, [undefined_function_calls, undefined_functions, {xref_checks, [undefined_function_calls, undefined_functions,

View File

@ -1,6 +1 @@
{"1.1.0", [].
[{<<"enacl">>,{pkg,<<"enacl">>,<<"1.1.1">>},0}]}.
[
{pkg_hash,[
{<<"enacl">>, <<"F65DC64D9BFF2D8A534CB77AEF14DA5E7A2FA148987D87856F79A4745C9C2627">>}]}
].

View File

@ -1,11 +1,11 @@
{application, enoise, {application, enoise,
[{description, "An Erlang implementation of the Noise protocol"}, [{description, "An Erlang implementation of the Noise protocol"},
{vsn, "1.1.0"}, {vsn, "1.2.0"},
{registered, []}, {registered, []},
{applications, {applications,
[kernel, [kernel,
stdlib, stdlib,
enacl crypto
]}, ]},
{env,[]}, {env,[]},
{modules, []}, {modules, []},

View File

@ -87,8 +87,7 @@ binary().
Role :: enoise_hs_state:noise_role()) -> Role :: enoise_hs_state:noise_role()) ->
{ok, enoise_hs_state:state()} | {error, term()}. {ok, enoise_hs_state:state()} | {error, term()}.
handshake(Options, Role) -> handshake(Options, Role) ->
HState = create_hstate(Options, Role), create_hstate(Options, Role).
{ok, HState}.
%% @doc Do a step (either `{send, Payload}', `{rcvd, EncryptedData}', %% @doc Do a step (either `{send, Payload}', `{rcvd, EncryptedData}',
%% or `done') %% or `done')
@ -109,10 +108,13 @@ step_handshake(HState, Data) ->
ComState :: noise_com_state()) -> ComState :: noise_com_state()) ->
{ok, noise_split_state(), noise_com_state()} | {error, term()}. {ok, noise_split_state(), noise_com_state()} | {error, term()}.
handshake(Options, Role, ComState) -> handshake(Options, Role, ComState) ->
HState = create_hstate(Options, Role), case create_hstate(Options, Role) of
Timeout = proplists:get_value(timeout, Options, infinity), {ok, HState} ->
do_handshake(HState, ComState, Timeout). Timeout = proplists:get_value(timeout, Options, infinity),
do_handshake(HState, ComState, Timeout);
Err = {error, _} ->
Err
end.
%% @doc Upgrades a gen_tcp, or equivalent, connected socket to a Noise socket, %% @doc Upgrades a gen_tcp, or equivalent, connected socket to a Noise socket,
%% that is, performs the client-side noise handshake. %% that is, performs the client-side noise handshake.
@ -270,15 +272,16 @@ create_hstate(Options, Role) ->
enoise_protocol:from_name(X); enoise_protocol:from_name(X);
_ -> NoiseProtocol0 _ -> NoiseProtocol0
end, end,
DH = enoise_protocol:dh(NoiseProtocol),
S = proplists:get_value(s, Options, undefined), S = proplists:get_value(s, Options, undefined),
E = proplists:get_value(e, Options, undefined), E = proplists:get_value(e, Options, undefined),
RS = proplists:get_value(rs, Options, undefined), RS = remote_keypair(DH, proplists:get_value(rs, Options, undefined)),
RE = proplists:get_value(re, Options, undefined), RE = remote_keypair(DH, proplists:get_value(re, Options, undefined)),
enoise_hs_state:init(NoiseProtocol, Role, enoise_hs_state:init(NoiseProtocol, Role,
Prologue, {S, E, RS, RE}). Prologue, {S, E, RS, RE}).
check_gen_tcp(TcpSock) -> check_gen_tcp(TcpSock) ->
case inet:getopts(TcpSock, [mode, packet, active, header, packet_size]) of case inet:getopts(TcpSock, [mode, packet, active, header, packet_size]) of
{ok, TcpOpts} -> {ok, TcpOpts} ->
@ -321,3 +324,5 @@ gen_tcp_rcv_msg({TcpSock, Active, Buf}, Timeout) ->
{error, timeout} {error, timeout}
end. end.
remote_keypair(_DH, undefined) -> undefined;
remote_keypair(DH, RemotePub) when is_binary(RemotePub) -> enoise_keypair:new(DH, RemotePub).

View File

@ -54,12 +54,8 @@ set_nonce(CState = #noise_cs{}, Nonce) ->
encrypt_with_ad(CState = #noise_cs{ k = empty }, _AD, PlainText) -> encrypt_with_ad(CState = #noise_cs{ k = empty }, _AD, PlainText) ->
{ok, CState, PlainText}; {ok, CState, PlainText};
encrypt_with_ad(CState = #noise_cs{ k = K, n = N, cipher = Cipher }, AD, PlainText) -> encrypt_with_ad(CState = #noise_cs{ k = K, n = N, cipher = Cipher }, AD, PlainText) ->
case enoise_crypto:encrypt(Cipher, K, N, AD, PlainText) of CipherText = enoise_crypto:encrypt(Cipher, K, N, AD, PlainText),
Encrypted when is_binary(Encrypted) -> {ok, CState#noise_cs{ n = N+1 }, CipherText}.
{ok, CState#noise_cs{ n = N+1 }, Encrypted};
Err = {error, _} ->
Err
end.
-spec decrypt_with_ad(CState :: state(), AD :: binary(), CipherText :: binary()) -> -spec decrypt_with_ad(CState :: state(), AD :: binary(), CipherText :: binary()) ->
{ok, state(), binary()} | {error, term()}. {ok, state(), binary()} | {error, term()}.
@ -74,6 +70,8 @@ decrypt_with_ad(CState = #noise_cs{ k = K, n = N, cipher = Cipher }, AD, CipherT
end. end.
-spec rekey(CState :: state()) -> state(). -spec rekey(CState :: state()) -> state().
rekey(CState = #noise_cs{ k = empty }) ->
CState;
rekey(CState = #noise_cs{ k = K, cipher = Cipher }) -> rekey(CState = #noise_cs{ k = K, cipher = Cipher }) ->
CState#noise_cs{ k = enoise_crypto:rekey(Cipher, K) }. CState#noise_cs{ k = enoise_crypto:rekey(Cipher, K) }.

View File

@ -29,13 +29,18 @@
%% @doc Perform a Diffie-Hellman calculation with the secret key from `Key1' %% @doc Perform a Diffie-Hellman calculation with the secret key from `Key1'
%% and the public key from `Key2' with algorithm `Algo'. %% and the public key from `Key2' with algorithm `Algo'.
-spec dh(Algo :: enoise_hs_state:noise_dh(), -spec dh(Algo :: enoise_hs_state:noise_dh(),
Key1:: keypair(), Key2 :: keypair()) -> binary(). Key1:: keypair(), Key2 :: keypair()) -> binary().
dh(dh25519, Key1, Key2) -> dh(Type, Key1, Key2) when Type == dh25519; Type == dh448 ->
enacl:curve25519_scalarmult( enoise_keypair:seckey(Key1) dh_(ecdh_type(Type), enoise_keypair:pubkey(Key2), enoise_keypair:seckey(Key1));
, enoise_keypair:pubkey(Key2));
dh(Type, _Key1, _Key2) -> dh(Type, _Key1, _Key2) ->
error({unsupported_diffie_hellman, Type}). error({unsupported_diffie_hellman, Type}).
ecdh_type(dh25519) -> x25519;
ecdh_type(dh448) -> x448.
dh_(DHType, OtherPub, MyPriv) ->
crypto:compute_key(ecdh, OtherPub, MyPriv, DHType).
-spec hmac(Hash :: enoise_sym_state:noise_hash(), -spec hmac(Hash :: enoise_sym_state:noise_hash(),
Key :: binary(), Data :: binary()) -> binary(). Key :: binary(), Data :: binary()) -> binary().
hmac(Hash, Key, Data) -> hmac(Hash, Key, Data) ->
@ -54,43 +59,42 @@ hkdf(Hash, Key, Data) ->
Output3 = hmac(Hash, TempKey, <<Output2/binary, 3:8>>), Output3 = hmac(Hash, TempKey, <<Output2/binary, 3:8>>),
[Output1, Output2, Output3]. [Output1, Output2, Output3].
-spec rekey(Cipher :: enoise_cipher_state:noise_cipher(), -spec rekey(Cipher :: enoise_cipher_state:noise_cipher(), Key :: binary()) -> binary().
Key :: binary()) -> binary() | {error, term()}. rekey('ChaChaPoly', K0) ->
KLen = 32,
<<K:KLen/binary, _/binary>> = encrypt('ChaChaPoly', K0, ?MAX_NONCE, <<>>, <<0:(32*8)>>),
K;
rekey(Cipher, K) -> rekey(Cipher, K) ->
encrypt(Cipher, K, ?MAX_NONCE, <<>>, <<0:(32*8)>>). encrypt(Cipher, K, ?MAX_NONCE, <<>>, <<0:(32*8)>>).
-spec encrypt(Cipher :: enoise_cipher_state:noise_cipher(), -spec encrypt(Cipher :: enoise_cipher_state:noise_cipher(), Key :: binary(),
Key :: binary(), Nonce :: non_neg_integer(), Nonce :: non_neg_integer(), Ad :: binary(), PlainText :: binary()) -> binary().
Ad :: binary(), PlainText :: binary()) -> encrypt(Cipher, K, N, Ad, PlainText) ->
binary() | {error, term()}. {CText, CTag} = crypto:crypto_one_time_aead(cipher(Cipher), K, nonce(Cipher, N), PlainText, Ad, true),
encrypt('ChaChaPoly', K, N, Ad, PlainText) -> <<CText/binary, CTag/binary>>.
Nonce = <<0:32, N:64/little-unsigned-integer>>,
enacl:aead_chacha20poly1305_ietf_encrypt(PlainText, Ad, Nonce, K);
encrypt('AESGCM', K, N, Ad, PlainText) ->
Nonce = <<0:32, N:64>>,
{CipherText, CipherTag} = crypto:block_encrypt(aes_gcm, K, Nonce, {Ad, PlainText}),
<<CipherText/binary, CipherTag/binary>>.
-spec decrypt(Cipher ::enoise_cipher_state:noise_cipher(), -spec decrypt(Cipher ::enoise_cipher_state:noise_cipher(), Key :: binary(),
Key :: binary(), Nonce :: non_neg_integer(), Nonce :: non_neg_integer(), AD :: binary(),
AD :: binary(), CipherText :: binary()) -> CipherText :: binary()) -> binary() | {error, term()}.
binary() | {error, term()}. decrypt(Cipher, K, N, Ad, CipherText0) ->
decrypt('ChaChaPoly', K, N, Ad, CipherText) ->
Nonce = <<0:32, N:64/little-unsigned-integer>>,
enacl:aead_chacha20poly1305_ietf_decrypt(CipherText, Ad, Nonce, K);
decrypt('AESGCM', K, N, Ad, CipherText0) ->
CTLen = byte_size(CipherText0) - ?MAC_LEN, CTLen = byte_size(CipherText0) - ?MAC_LEN,
<<CipherText:CTLen/binary, MAC:?MAC_LEN/binary>> = CipherText0, <<CText:CTLen/binary, MAC:?MAC_LEN/binary>> = CipherText0,
Nonce = <<0:32, N:64>>, case crypto:crypto_one_time_aead(cipher(Cipher), K, nonce(Cipher, N), CText, Ad, MAC, false) of
case crypto:block_decrypt(aes_gcm, K, Nonce, {Ad, CipherText, MAC}) of
error -> {error, decrypt_failed}; error -> {error, decrypt_failed};
Data -> Data Data -> Data
end. end.
nonce('ChaChaPoly', N) -> <<0:32, N:64/little-unsigned-integer>>;
nonce('AESGCM', N) -> <<0:32, N:64/big-unsigned-integer>>.
cipher('ChaChaPoly') -> chacha20_poly1305;
cipher('AESGCM') -> aes_256_gcm.
-spec hash(Hash :: enoise_sym_state:noise_hash(), Data :: binary()) -> binary(). -spec hash(Hash :: enoise_sym_state:noise_hash(), Data :: binary()) -> binary().
hash(blake2s, Data) ->
crypto:hash(blake2s, Data);
hash(blake2b, Data) -> hash(blake2b, Data) ->
Hash = enacl:generichash(64, Data), Hash; crypto:hash(blake2b, Data);
hash(sha256, Data) -> hash(sha256, Data) ->
crypto:hash(sha256, Data); crypto:hash(sha256, Data);
hash(sha512, Data) -> hash(sha512, Data) ->

View File

@ -26,6 +26,8 @@
hs_hash := binary(), hs_hash := binary(),
final_state => state() }. final_state => state() }.
-type optional_key() :: undefined | keypair().
-type initial_keys() :: {optional_key(), optional_key(), optional_key(), optional_key()}.
-record(noise_hs, { ss :: enoise_sym_state:state() -record(noise_hs, { ss :: enoise_sym_state:state()
, s :: keypair() | undefined , s :: keypair() | undefined
@ -39,11 +41,8 @@
-opaque state() :: #noise_hs{}. -opaque state() :: #noise_hs{}.
-export_type([noise_dh/0, noise_role/0, noise_split_state/0, noise_token/0, state/0]). -export_type([noise_dh/0, noise_role/0, noise_split_state/0, noise_token/0, state/0]).
-spec init(Protocol :: string() | enoise_protocol:protocol(), -spec init(Protocol :: enoise_protocol:protocol(), Role :: noise_role(),
Role :: noise_role(), Prologue :: binary(), Prologue :: binary(), Keys :: initial_keys()) -> {ok, state()} | {error, term()}.
Keys :: term()) -> state().
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}) -> init(Protocol, Role, Prologue, {S, E, RS, RE}) ->
SS0 = enoise_sym_state:init(Protocol), SS0 = enoise_sym_state:init(Protocol),
SS1 = enoise_sym_state:mix_hash(SS0, Prologue), SS1 = enoise_sym_state:mix_hash(SS0, Prologue),
@ -53,11 +52,19 @@ init(Protocol, Role, Prologue, {S, E, RS, RE}) ->
, dh = enoise_protocol:dh(Protocol) , dh = enoise_protocol:dh(Protocol)
, msgs = enoise_protocol:msgs(Role, Protocol) }, , msgs = enoise_protocol:msgs(Role, Protocol) },
PreMsgs = enoise_protocol:pre_msgs(Role, Protocol), PreMsgs = enoise_protocol:pre_msgs(Role, Protocol),
lists:foldl(fun({out, [s]}, HS0) -> mix_hash(HS0, enoise_keypair:pubkey(S)); pre_mix(PreMsgs, HS).
({out, [e]}, HS0) -> mix_hash(HS0, enoise_keypair:pubkey(E));
({in, [s]}, HS0) -> mix_hash(HS0, enoise_keypair:pubkey(RS)); pre_mix([], HS) -> {ok, HS};
({in, [e]}, HS0) -> mix_hash(HS0, enoise_keypair:pubkey(RE)) pre_mix([{out, [s]} | Msgs], HS = #noise_hs{ s = S }) when S /= undefined ->
end, HS, PreMsgs). pre_mix(Msgs, mix_hash(HS, enoise_keypair:pubkey(S)));
pre_mix([{out, [e]} | Msgs], HS = #noise_hs{ e = E }) when E /= undefined ->
pre_mix(Msgs, mix_hash(HS, enoise_keypair:pubkey(E)));
pre_mix([{in, [s]} | Msgs], HS = #noise_hs{ rs = RS }) when RS /= undefined ->
pre_mix(Msgs, mix_hash(HS, enoise_keypair:pubkey(RS)));
pre_mix([{in, [e]} | Msgs], HS = #noise_hs{ re = RE }) when RE /= undefined ->
pre_mix(Msgs, mix_hash(HS, enoise_keypair:pubkey(RE)));
pre_mix(_Msg, _HS) ->
{error, invalid_noise_setup}.
-spec finalize(HS :: state()) -> {ok, noise_split_state()} | {error, term()}. -spec finalize(HS :: state()) -> {ok, noise_split_state()} | {error, term()}.
finalize(HS = #noise_hs{ msgs = [], ss = SS, role = Role }) -> finalize(HS = #noise_hs{ msgs = [], ss = SS, role = Role }) ->
@ -90,7 +97,7 @@ read_message(HS = #noise_hs{ msgs = [{in, Msg} | Msgs] }, Message) ->
Err = {error, _} -> Err Err = {error, _} -> Err
end. end.
-spec remote_keys(HS :: state()) -> keypair(). -spec remote_keys(HS :: state()) -> undefined | keypair().
remote_keys(#noise_hs{ rs = RS }) -> remote_keys(#noise_hs{ rs = RS }) ->
RS. RS.

View File

@ -30,7 +30,7 @@
%% @doc Generate a new keypair of type `Type'. %% @doc Generate a new keypair of type `Type'.
-spec new(Type :: key_type()) -> keypair(). -spec new(Type :: key_type()) -> keypair().
new(Type) -> new(Type) ->
{Sec, Pub} = new_key_pair(Type), {Pub, Sec} = new_key_pair(Type),
#kp{ type = Type, sec = Sec, pub = Pub }. #kp{ type = Type, sec = Sec, pub = Pub }.
%% @doc Create a new keypair of type `Type'. If `Public' is `undefined' %% @doc Create a new keypair of type `Type'. If `Public' is `undefined'
@ -69,12 +69,14 @@ seckey(#kp{ sec = S }) ->
S. S.
%% -- Local functions -------------------------------------------------------- %% -- Local functions --------------------------------------------------------
new_key_pair(dh25519) -> new_key_pair(Type) when Type == dh25519; Type == dh448 ->
KeyPair = enacl:crypto_sign_ed25519_keypair(), crypto:generate_key(ecdh, ecdh_type(Type));
{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) -> new_key_pair(Type) ->
error({unsupported_key_type, Type}). error({unsupported_key_type, Type}).
pubkey_from_secret(dh25519, Secret) -> pubkey_from_secret(Type, Secret) when Type == dh25519; Type == dh448 ->
enacl:curve25519_scalarmult_base(Secret). {Public, Secret} = crypto:generate_key(ecdh, ecdh_type(Type), Secret),
Public.
ecdh_type(dh25519) -> x25519;
ecdh_type(dh448) -> x448.

View File

@ -19,7 +19,7 @@
, to_name/1]). , to_name/1]).
-ifdef(TEST). -ifdef(TEST).
-export([to_name/4]). -export([to_name/4, from_name_pattern/1, to_name_pattern/1]).
-endif. -endif.
-type noise_pattern() :: nn | kn | nk | kk | nx | kx | xn | in | xk | ik | xx | ix. -type noise_pattern() :: nn | kn | nk | kk | nx | kx | xn | in | xk | ik | xx | ix.
@ -137,9 +137,9 @@ supported_dh(Dh) ->
-spec supported() -> map(). -spec supported() -> map().
supported() -> supported() ->
#{ hs_pattern => [nn, kn, nk, kk, nx, kx, xn, in, xk, ik, xx, ix] #{ hs_pattern => [nn, kn, nk, kk, nx, kx, xn, in, xk, ik, xx, ix]
, hash => [blake2b, sha256, sha512] , hash => [blake2s, blake2b, sha256, sha512]
, cipher => ['ChaChaPoly', 'AESGCM'] , cipher => ['ChaChaPoly', 'AESGCM']
, dh => [dh25519] , dh => [dh25519, dh448]
}. }.
to_name(Pattern, Dh, Cipher, Hash) -> to_name(Pattern, Dh, Cipher, Hash) ->
@ -148,16 +148,16 @@ to_name(Pattern, Dh, Cipher, Hash) ->
to_name_pattern(Atom) -> to_name_pattern(Atom) ->
[Simple | Rest] = string:lexemes(atom_to_list(Atom), "_"), [Simple | Rest] = string:lexemes(atom_to_list(Atom), "_"),
string:uppercase(Simple) ++ lists:join("+", Rest). lists:flatten(string:uppercase(Simple) ++ lists:join("+", Rest)).
from_name_pattern(String) -> from_name_pattern(String) ->
[Init | Mod2] = string:lexemes(String, "+"), [Init | Mod2] = string:lexemes(String, "+"),
{Simple, Mod1} = lists:splitwith(fun(C) -> C >= $A andalso C =< $Z end, Init), {Simple, Mod1} = lists:splitwith(fun(C) -> C >= $A andalso C =< $Z end, Init),
list_to_atom(string:lowercase(Simple) ++ list_to_atom(lists:flatten(string:lowercase(Simple) ++
case Mod1 of case Mod1 of
"" -> ""; "" -> "";
_ -> "_" ++ lists:join([Mod1 | Mod2], "_") _ -> "_" ++ lists:join("_", [Mod1 | Mod2])
end). end)).
to_name_dh(dh25519) -> "25519"; to_name_dh(dh25519) -> "25519";
to_name_dh(dh448) -> "448". to_name_dh(dh448) -> "448".

View File

@ -26,5 +26,14 @@ chachapoly_test() ->
enoise_cipher_state:decrypt_with_ad(CS1, AD, <<CipherText/binary, MAC/binary>>), enoise_cipher_state:decrypt_with_ad(CS1, AD, <<CipherText/binary, MAC/binary>>),
?assertMatch(PlainText, PlainText0), ?assertMatch(PlainText, PlainText0),
% rekey test
CS4 = enoise_cipher_state:rekey(CS1),
{ok, _CS5, <<CipherText1:CTLen/binary, MAC1:MACLen/binary>>} =
enoise_cipher_state:encrypt_with_ad(CS4, AD, PlainText),
{ok, _CS6, <<PlainText1:PTLen/binary>>} =
enoise_cipher_state:decrypt_with_ad(CS4, AD, <<CipherText1/binary, MAC1/binary>>),
?assertMatch(PlainText, PlainText1),
ok. ok.

View File

@ -44,6 +44,13 @@ chachapoly_test() ->
enoise_crypto:decrypt('ChaChaPoly', Key, Nonce, AD, <<CipherText/binary, MAC/binary>>), enoise_crypto:decrypt('ChaChaPoly', Key, Nonce, AD, <<CipherText/binary, MAC/binary>>),
?assertMatch(PlainText, PlainText0), ?assertMatch(PlainText, PlainText0),
Key1 = enoise_crypto:rekey('ChaChaPoly', Key),
<<CipherText1:CTLen/binary, MAC1:MACLen/binary>> =
enoise_crypto:encrypt('ChaChaPoly', Key1, Nonce, AD, PlainText),
<<PlainText1:PTLen/binary>> =
enoise_crypto:decrypt('ChaChaPoly', Key1, Nonce, AD, <<CipherText1/binary, MAC1/binary>>),
?assertMatch(PlainText, PlainText1),
ok. ok.
blake2b_test() -> blake2b_test() ->

View File

@ -43,7 +43,8 @@ noise_test(_Name, Protocol, Init, Resp, Messages, HSHash) ->
SecK = fun(undefined) -> undefined; (Sec) -> enoise_keypair:new(DH, Sec, undefined) end, SecK = fun(undefined) -> undefined; (Sec) -> enoise_keypair:new(DH, Sec, undefined) end,
PubK = fun(undefined) -> undefined; (Pub) -> enoise_keypair:new(DH, Pub) end, PubK = fun(undefined) -> undefined; (Pub) -> enoise_keypair:new(DH, Pub) end,
HSInit = fun(P, R, #{ e := E, s := S, rs := RS, prologue := PL }) -> HSInit = fun(P, R, #{ e := E, s := S, rs := RS, prologue := PL }) ->
enoise_hs_state:init(P, R, PL, {SecK(S), SecK(E), PubK(RS), undefined}) {ok, HS} = enoise_hs_state:init(P, R, PL, {SecK(S), SecK(E), PubK(RS), undefined}),
HS
end, end,
InitHS = HSInit(Protocol, initiator, Init), InitHS = HSInit(Protocol, initiator, Init),

View File

@ -7,5 +7,18 @@
-include_lib("eunit/include/eunit.hrl"). -include_lib("eunit/include/eunit.hrl").
name_test() -> name_test() ->
?assertMatch(<<"Noise_XK_25519_ChaChaPoly_SHA512">>, roundtrip("Noise_XK_25519_ChaChaPoly_SHA512"),
enoise_protocol:to_name(enoise_protocol:from_name("Noise_XK_25519_ChaChaPoly_SHA512"))). roundtrip("Noise_NN_25519_AESGCM_BLAKE2b").
name2_test() ->
Name = "Noise_NXpsk2_25519_AESGCM_SHA512",
?assertError({name_not_recognized, Name}, enoise_protocol:from_name(Name)).
name_pattern_test() ->
Pat = "XKfallback+psk0",
RoundPat = enoise_protocol:to_name_pattern(enoise_protocol:from_name_pattern(Pat)),
?assertEqual(Pat, RoundPat).
roundtrip(Name) ->
ExpectedName = iolist_to_binary(Name),
?assertMatch(ExpectedName, enoise_protocol:to_name(enoise_protocol:from_name(Name))).

View File

@ -41,10 +41,9 @@ noise_interactive(V = #{ protocol_name := Name }) ->
noise_interactive(_Name, Protocol, Init, Resp, Messages, HSHash) -> noise_interactive(_Name, Protocol, Init, Resp, Messages, HSHash) ->
DH = enoise_protocol:dh(Protocol), DH = enoise_protocol:dh(Protocol),
SecK = fun(undefined) -> undefined; (Sec) -> enoise_keypair:new(DH, Sec, undefined) end, SecK = fun(undefined) -> undefined; (Sec) -> enoise_keypair:new(DH, Sec, undefined) end,
PubK = fun(undefined) -> undefined; (Pub) -> enoise_keypair:new(DH, Pub) end,
HSInit = fun(#{ e := E, s := S, rs := RS, prologue := PL }, R) -> HSInit = fun(#{ e := E, s := S, rs := RS, prologue := PL }, R) ->
Opts = [{noise, Protocol}, {s, SecK(S)}, {e, SecK(E)}, {rs, PubK(RS)}, {prologue, PL}], Opts = [{noise, Protocol}, {s, SecK(S)}, {e, SecK(E)}, {rs, RS}, {prologue, PL}],
enoise:handshake(Opts, R) enoise:handshake(Opts, R)
end, end,
{ok, InitHS} = HSInit(Init, initiator), {ok, InitHS} = HSInit(Init, initiator),
@ -149,12 +148,12 @@ noise_test_run_(Conf, SKP, CKP) ->
Protocol = enoise_protocol:from_name(Conf), Protocol = enoise_protocol:from_name(Conf),
Port = 4556, Port = 4556,
SrvOpts = [{echos, 2}, {cpub, CKP}], SrvOpts = [{echos, 2}, {cpub, enoise_keypair:pubkey(CKP)}],
EchoSrv = enoise_utils:echo_srv_start(Port, Protocol, SKP, SrvOpts), EchoSrv = enoise_utils:echo_srv_start(Port, Protocol, SKP, SrvOpts),
{ok, TcpSock} = gen_tcp:connect("localhost", Port, [{active, once}, binary, {reuseaddr, true}], 100), {ok, TcpSock} = gen_tcp:connect("localhost", Port, [{active, once}, binary, {reuseaddr, true}], 100),
Opts = [{noise, Protocol}, {s, CKP}] ++ [{rs, SKP} || enoise_utils:need_rs(initiator, Conf) ], Opts = [{noise, Protocol}, {s, CKP}] ++ [{rs, enoise_keypair:pubkey(SKP)} || enoise_utils:need_rs(initiator, Conf) ],
{ok, EConn, _} = enoise:connect(TcpSock, Opts), {ok, EConn, _} = enoise:connect(TcpSock, Opts),
ok = enoise:send(EConn, <<"Hello World!">>), ok = enoise:send(EConn, <<"Hello World!">>),

View File

@ -26,7 +26,7 @@ echo_srv(Port, Protocol, SKP, SrvOpts) ->
AcceptRes = AcceptRes =
try try
enoise:accept(TcpSock, Opts) enoise:accept(TcpSock, Opts)
catch _:R -> gen_tcp:close(TcpSock), {error, {R, erlang:get_stacktrace()}} end, catch _:R:S -> gen_tcp:close(TcpSock), {error, {R, S}} end,
gen_tcp:close(LSock), gen_tcp:close(LSock),