Compare commits

..

No commits in common. "master" and "v1.0.0" have entirely different histories.

21 changed files with 8732 additions and 24112 deletions

View File

@ -1,54 +0,0 @@
# Changelog
All notable changes to this project will be documented in this file.
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
## [Unreleased]
### Added
- Support for 448 DH function and Blake2s hash function.
### 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
- The dependency on `enacl` is not needed anymore, OTP's `crypto` library now cover all
necessary operations.
## [1.2.0] - 2021-10-28
### Added
### Changed
- Use the new AEAD crypto interface introduced in OTP 22. This makes `enoise` OPT 24 compatible
but it also means it no longer works on OTP 21 and earlier. You can't win them all.
- Fixed ChaChaPoly20 rekey
### Removed
## [1.1.0] - 2020-09-24
### Added
Include [Cacaphony](https://github.com/centromere/cacophony) test vectors.
### Changed
Updated `enacl` to version [1.1.1](https://github.com/jlouis/enacl/releases/tag/v1.1.1).
Fixed some imprecise type specifications.
### Removed
## [1.0.1] - 2018-12-21
### Added
### Changed
Improved argument checks and error handling in handshake (in particular related to empty
hand shake messages).
### Removed
## [1.0] - 2018-10-09
Initial version the following map describe what is supported:
```
#{ hs_pattern => [nn, kn, nk, kk, nx, kx, xn, in, xk, ik, xx, ix]
, hash => [blake2b, sha256, sha512]
, cipher => ['ChaChaPoly', 'AESGCM']
, dh => [dh25519] }
```
[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.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

View File

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

View File

@ -1,14 +1,11 @@
{erl_opts, [debug_info]}. {erl_opts, [debug_info]}.
{plugins, [rebar3_hex]}. {plugins, [rebar3_hex]}.
{deps, [{enacl, "0.17.2"}]}.
{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,
locals_not_used, locals_not_used,
deprecated_function_calls, deprecated_functions]}. deprecated_function_calls, deprecated_functions]}.
{dialyzer, [{warnings, [unknown]}]}.

View File

@ -1 +1,6 @@
[]. {"1.1.0",
[{<<"enacl">>,{pkg,<<"enacl">>,<<"0.17.2">>},0}]}.
[
{pkg_hash,[
{<<"enacl">>, <<"4AD59142943E72D72C56E33C30DEDEF28ADD8EBEE79C51033562B0CB4B93EDE0">>}]}
].

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.2.0"}, {vsn, "1.0.0"},
{registered, []}, {registered, []},
{applications, {applications,
[kernel, [kernel,
stdlib, stdlib,
crypto enacl
]}, ]},
{env,[]}, {env,[]},
{modules, []}, {modules, []},

View File

@ -64,9 +64,11 @@ binary().
send_msg := send_msg_fun(), send_msg := send_msg_fun(),
state := term() }. state := term() }.
%% Noise communication state - used to parameterize a handshake. Consists of a %% Noise communication state - used to parameterize a handshake. Consists of a
%% send function, one receive function, and an internal state. %% send function one receive function and an internal state.
-type noise_split_state() :: enoise_hs_state:noise_split_state(). -type noise_split_state() :: #{ rx := enoise_cipher_state:state(),
tx := enoise_cipher_state:state(),
hs_hash := binary() }.
%% Return value from the final `split' operation. Provides a CipherState for %% Return value from the final `split' operation. Provides a CipherState for
%% receiving and a CipherState transmission. Also includes the final handshake %% receiving and a CipherState transmission. Also includes the final handshake
%% hash for channel binding. %% hash for channel binding.
@ -87,7 +89,8 @@ 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) ->
create_hstate(Options, Role). HState = 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')
@ -108,13 +111,10 @@ 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) ->
case create_hstate(Options, Role) of HState = create_hstate(Options, Role),
{ok, HState} ->
Timeout = proplists:get_value(timeout, Options, infinity), Timeout = proplists:get_value(timeout, Options, infinity),
do_handshake(HState, ComState, Timeout); 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.
@ -140,7 +140,7 @@ connect(TcpSock, Options) ->
%% @end %% @end
-spec accept(TcpSock :: gen_tcp:socket(), -spec accept(TcpSock :: gen_tcp:socket(),
Options :: noise_options()) -> Options :: noise_options()) ->
{ok, noise_socket(), enoise_hs_state:state()} | {error, term()}. {ok, noise_socket()} | {error, term()}.
accept(TcpSock, Options) -> accept(TcpSock, Options) ->
tcp_handshake(TcpSock, responder, Options). tcp_handshake(TcpSock, responder, Options).
@ -272,16 +272,15 @@ 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 = remote_keypair(DH, proplists:get_value(rs, Options, undefined)), RS = proplists:get_value(rs, Options, undefined),
RE = remote_keypair(DH, proplists:get_value(re, Options, undefined)), RE = 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} ->
@ -324,5 +323,3 @@ 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

@ -50,12 +50,11 @@ set_nonce(CState = #noise_cs{}, Nonce) ->
CState#noise_cs{ n = Nonce }. CState#noise_cs{ n = Nonce }.
-spec encrypt_with_ad(CState :: state(), AD :: binary(), PlainText :: binary()) -> -spec encrypt_with_ad(CState :: state(), AD :: binary(), PlainText :: binary()) ->
{ok, state(), binary()} | {error, term()}. {ok, state(), binary()}.
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) ->
CipherText = enoise_crypto:encrypt(Cipher, K, N, AD, PlainText), {ok, CState#noise_cs{ n = N+1 }, enoise_crypto:encrypt(Cipher, K, N, AD, PlainText)}.
{ok, CState#noise_cs{ n = N+1 }, CipherText}.
-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()}.
@ -70,8 +69,6 @@ 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

@ -53,19 +53,15 @@ start_link(TcpSock, Rx, Tx, Owner, {Active0, Buf}) ->
Err Err
end. end.
-spec send(Noise :: pid(), Data :: binary()) -> ok | {error, term()}.
send(Noise, Data) -> send(Noise, Data) ->
gen_server:call(Noise, {send, Data}). gen_server:call(Noise, {send, Data}).
-spec set_active(Noise :: pid(), Active :: true | once) -> ok | {error, term()}.
set_active(Noise, Active) -> set_active(Noise, Active) ->
gen_server:call(Noise, {active, self(), Active}). gen_server:call(Noise, {active, self(), Active}).
-spec close(Noise :: pid()) -> ok | {error, term()}.
close(Noise) -> close(Noise) ->
gen_server:call(Noise, close). gen_server:call(Noise, close).
-spec controlling_process(Noise :: pid(), NewPid :: pid()) -> ok | {error, term()}.
controlling_process(Noise, NewPid) -> controlling_process(Noise, NewPid) ->
gen_server:call(Noise, {controlling_process, self(), NewPid}, 100). gen_server:call(Noise, {controlling_process, self(), NewPid}, 100).

View File

@ -30,17 +30,12 @@
%% 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(Type, Key1, Key2) when Type == dh25519; Type == dh448 -> dh(dh25519, Key1, Key2) ->
dh_(ecdh_type(Type), enoise_keypair:pubkey(Key2), enoise_keypair:seckey(Key1)); enacl:curve25519_scalarmult( 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) ->
@ -59,42 +54,38 @@ 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(), Key :: binary()) -> binary(). -spec rekey(Cipher :: enoise_cipher_state:noise_cipher(),
rekey('ChaChaPoly', K0) -> Key :: binary()) -> binary().
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(), Key :: binary(), -spec encrypt(Cipher :: enoise_cipher_state:noise_cipher(),
Nonce :: non_neg_integer(), Ad :: binary(), PlainText :: binary()) -> binary(). Key :: binary(), Nonce :: non_neg_integer(),
encrypt(Cipher, K, N, Ad, PlainText) -> Ad :: binary(), PlainText :: binary()) ->
{CText, CTag} = crypto:crypto_one_time_aead(cipher(Cipher), K, nonce(Cipher, N), PlainText, Ad, true), binary() | {error, term()}.
<<CText/binary, CTag/binary>>. encrypt('ChaChaPoly', K, N, Ad, PlainText) ->
enacl:aead_chacha20poly1305_encrypt(K, N, Ad, PlainText);
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(), Key :: binary(), -spec decrypt(Cipher ::enoise_cipher_state:noise_cipher(),
Nonce :: non_neg_integer(), AD :: binary(), Key :: binary(), Nonce :: non_neg_integer(),
CipherText :: binary()) -> binary() | {error, term()}. AD :: binary(), CipherText :: binary()) ->
decrypt(Cipher, K, N, Ad, CipherText0) -> binary() | {error, term()}.
decrypt('ChaChaPoly', K, N, Ad, CipherText) ->
enacl:aead_chacha20poly1305_decrypt(K, N, Ad, CipherText);
decrypt('AESGCM', K, N, Ad, CipherText0) ->
CTLen = byte_size(CipherText0) - ?MAC_LEN, CTLen = byte_size(CipherText0) - ?MAC_LEN,
<<CText:CTLen/binary, MAC:?MAC_LEN/binary>> = CipherText0, <<CipherText:CTLen/binary, MAC:?MAC_LEN/binary>> = CipherText0,
case crypto:crypto_one_time_aead(cipher(Cipher), K, nonce(Cipher, N), CText, Ad, MAC, false) of Nonce = <<0:32, N:64>>,
error -> {error, decrypt_failed}; crypto:block_decrypt(aes_gcm, K, Nonce, {Ad, CipherText, MAC}).
Data -> Data
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) ->
crypto:hash(blake2b, Data); {ok, Hash} = enacl:generichash(64, Data), Hash;
hash(sha256, Data) -> hash(sha256, Data) ->
crypto:hash(sha256, Data); crypto:hash(sha256, Data);
hash(sha512, Data) -> hash(sha512, Data) ->

View File

@ -21,13 +21,6 @@
-type noise_dh() :: dh25519 | dh448. -type noise_dh() :: dh25519 | dh448.
-type noise_token() :: s | e | ee | ss | es | se. -type noise_token() :: s | e | ee | ss | es | se.
-type keypair() :: enoise_keypair:keypair(). -type keypair() :: enoise_keypair:keypair().
-type noise_split_state() :: #{ rx := enoise_cipher_state:state(),
tx := enoise_cipher_state:state(),
hs_hash := binary(),
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,10 +32,13 @@
, msgs = [] :: [enoise_protocol:noise_msg()] }). , msgs = [] :: [enoise_protocol:noise_msg()] }).
-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_token/0, state/0]).
-spec init(Protocol :: enoise_protocol:protocol(), Role :: noise_role(), -spec init(Protocol :: string() | enoise_protocol:protocol(),
Prologue :: binary(), Keys :: initial_keys()) -> {ok, state()} | {error, term()}. Role :: noise_role(), Prologue :: binary(),
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),
@ -52,21 +48,13 @@ 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),
pre_mix(PreMsgs, HS). 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).
pre_mix([], HS) -> {ok, HS}; -spec finalize(HS :: state()) -> {ok, map()} | {error, term()}.
pre_mix([{out, [s]} | Msgs], HS = #noise_hs{ s = S }) when S /= undefined ->
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()}.
finalize(HS = #noise_hs{ msgs = [], ss = SS, role = Role }) -> finalize(HS = #noise_hs{ msgs = [], ss = SS, role = Role }) ->
{C1, C2} = enoise_sym_state:split(SS), {C1, C2} = enoise_sym_state:split(SS),
HSHash = enoise_sym_state:h(SS), HSHash = enoise_sym_state:h(SS),
@ -80,7 +68,7 @@ finalize(_) ->
-spec next_message(HS :: state()) -> in | out | done. -spec next_message(HS :: state()) -> in | out | done.
next_message(#noise_hs{ msgs = [{Dir, _} | _] }) -> Dir; next_message(#noise_hs{ msgs = [{Dir, _} | _] }) -> Dir;
next_message(#noise_hs{ }) -> done. next_message(_) -> done.
-spec write_message(HS :: state(), PayLoad :: binary()) -> {ok, state(), binary()}. -spec write_message(HS :: state(), PayLoad :: binary()) -> {ok, state(), binary()}.
write_message(HS = #noise_hs{ msgs = [{out, Msg} | Msgs] }, PayLoad) -> write_message(HS = #noise_hs{ msgs = [{out, Msg} | Msgs] }, PayLoad) ->
@ -92,12 +80,9 @@ write_message(HS = #noise_hs{ msgs = [{out, Msg} | Msgs] }, PayLoad) ->
-spec read_message(HS :: state(), Message :: binary()) -> -spec read_message(HS :: state(), Message :: binary()) ->
{ok, state(), binary()} | {error, term()}. {ok, state(), binary()} | {error, term()}.
read_message(HS = #noise_hs{ msgs = [{in, Msg} | Msgs] }, Message) -> read_message(HS = #noise_hs{ msgs = [{in, Msg} | Msgs] }, Message) ->
case read_message(HS#noise_hs{ msgs = Msgs }, Msg, Message) of {HS1, RestBuf1} = read_message(HS#noise_hs{ msgs = Msgs }, Msg, Message),
{ok, HS1, RestBuf1} -> decrypt_and_hash(HS1, RestBuf1); decrypt_and_hash(HS1, RestBuf1).
Err = {error, _} -> Err
end.
-spec remote_keys(HS :: state()) -> undefined | keypair().
remote_keys(#noise_hs{ rs = RS }) -> remote_keys(#noise_hs{ rs = RS }) ->
RS. RS.
@ -108,12 +93,10 @@ write_message(HS, [Token | Tokens], MsgBuf0) ->
write_message(HS1, Tokens, <<MsgBuf0/binary, MsgBuf1/binary>>). write_message(HS1, Tokens, <<MsgBuf0/binary, MsgBuf1/binary>>).
read_message(HS, [], Data) -> read_message(HS, [], Data) ->
{ok, HS, Data}; {HS, Data};
read_message(HS, [Token | Tokens], Data0) -> read_message(HS, [Token | Tokens], Data0) ->
case read_token(HS, Token, Data0) of {HS1, Data1} = read_token(HS, Token, Data0),
{ok, HS1, Data1} -> read_message(HS1, Tokens, Data1); read_message(HS1, Tokens, Data1).
Err = {error, _} -> Err
end.
write_token(HS = #noise_hs{ e = undefined }, e) -> write_token(HS = #noise_hs{ e = undefined }, e) ->
E = new_key_pair(HS), E = new_key_pair(HS),
@ -132,33 +115,21 @@ write_token(HS, Token) ->
read_token(HS = #noise_hs{ re = undefined, dh = DH }, e, Data0) -> read_token(HS = #noise_hs{ re = undefined, dh = DH }, e, Data0) ->
DHLen = enoise_crypto:dhlen(DH), DHLen = enoise_crypto:dhlen(DH),
case Data0 of <<REPub:DHLen/binary, Data1/binary>> = Data0,
<<REPub:DHLen/binary, Data1/binary>> ->
RE = enoise_keypair:new(DH, REPub), RE = enoise_keypair:new(DH, REPub),
{ok, mix_hash(HS#noise_hs{ re = RE }, REPub), Data1}; {mix_hash(HS#noise_hs{ re = RE }, REPub), Data1};
_ ->
{error, {bad_data, {failed_to_read_token, e, DHLen}}}
end;
read_token(HS = #noise_hs{ rs = undefined, dh = DH }, s, Data0) -> read_token(HS = #noise_hs{ rs = undefined, dh = DH }, s, Data0) ->
DHLen = case has_key(HS) of DHLen = case has_key(HS) of
true -> enoise_crypto:dhlen(DH) + 16; true -> enoise_crypto:dhlen(DH) + 16;
false -> enoise_crypto:dhlen(DH) false -> enoise_crypto:dhlen(DH)
end, end,
case Data0 of <<Temp:DHLen/binary, Data1/binary>> = Data0,
<<Temp:DHLen/binary, Data1/binary>> -> {ok, HS1, RSPub} = decrypt_and_hash(HS, Temp),
case decrypt_and_hash(HS, Temp) of
{ok, HS1, RSPub} ->
RS = enoise_keypair:new(DH, RSPub), RS = enoise_keypair:new(DH, RSPub),
{ok, HS1#noise_hs{ rs = RS }, Data1}; {HS1#noise_hs{ rs = RS }, Data1};
Err = {error, _} ->
Err
end;
_ ->
{error, {bad_data, {failed_to_read_token, s, DHLen}}}
end;
read_token(HS, Token, Data) -> read_token(HS, Token, Data) ->
{K1, K2} = dh_token(HS, Token), {K1, K2} = dh_token(HS, Token),
{ok, mix_key(HS, dh(HS, K1, K2)), Data}. {mix_key(HS, dh(HS, K1, K2)), Data}.
dh_token(#noise_hs{ e = E, re = RE } , ee) -> {E, RE}; dh_token(#noise_hs{ e = E, re = RE } , ee) -> {E, RE};
dh_token(#noise_hs{ e = E, rs = RS, role = initiator }, es) -> {E, RS}; dh_token(#noise_hs{ e = E, rs = RS, role = initiator }, es) -> {E, RS};

View File

@ -30,15 +30,13 @@
%% @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) ->
{Pub, Sec} = new_key_pair(Type), {Sec, Pub} = 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'
%% it will be computed from the `Secret' (using the curve/algorithm %% it will be computed from the `Secret' (using the curve/algorithm
%% indicated by `Type'). %% indicated by `Type').
-spec new(Type :: key_type(), -spec new(Type :: key_type(), Secret :: binary(), Public :: binary() | undefined) -> keypair().
Secret :: binary() | undefined,
Public :: binary() | undefined) -> keypair().
new(Type, Secret, undefined) -> new(Type, Secret, undefined) ->
new(Type, Secret, pubkey_from_secret(Type, Secret)); new(Type, Secret, pubkey_from_secret(Type, Secret));
new(Type, Secret, Public) -> new(Type, Secret, Public) ->
@ -69,14 +67,12 @@ seckey(#kp{ sec = S }) ->
S. S.
%% -- Local functions -------------------------------------------------------- %% -- Local functions --------------------------------------------------------
new_key_pair(Type) when Type == dh25519; Type == dh448 -> new_key_pair(dh25519) ->
crypto:generate_key(ecdh, ecdh_type(Type)); 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) -> new_key_pair(Type) ->
error({unsupported_key_type, Type}). error({unsupported_key_type, Type}).
pubkey_from_secret(Type, Secret) when Type == dh25519; Type == dh448 -> pubkey_from_secret(dh25519, Secret) ->
{Public, Secret} = crypto:generate_key(ecdh, ecdh_type(Type), Secret), enacl:curve25519_scalarmult_base(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, from_name_pattern/1, to_name_pattern/1]). -export([to_name/4]).
-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.
@ -90,7 +90,6 @@ pre_msgs(Role, #noise_protocol{ hs_pattern = Pattern }) ->
{PreMsgs, _Msgs} = protocol(Pattern), {PreMsgs, _Msgs} = protocol(Pattern),
role_adapt(Role, PreMsgs). role_adapt(Role, PreMsgs).
-spec role_adapt(Role :: enoise_hs_state:noise_role(), [noise_msg()]) -> [noise_msg()].
role_adapt(initiator, Msgs) -> role_adapt(initiator, Msgs) ->
Msgs; Msgs;
role_adapt(responder, Msgs) -> role_adapt(responder, Msgs) ->
@ -137,9 +136,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 => [blake2s, blake2b, sha256, sha512] , hash => [blake2b, sha256, sha512]
, cipher => ['ChaChaPoly', 'AESGCM'] , cipher => ['ChaChaPoly', 'AESGCM']
, dh => [dh25519, dh448] , dh => [dh25519]
}. }.
to_name(Pattern, Dh, Cipher, Hash) -> to_name(Pattern, Dh, Cipher, Hash) ->
@ -148,16 +147,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), "_"),
lists:flatten(string:uppercase(Simple) ++ lists:join("+", Rest)). 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(lists:flatten(string:lowercase(Simple) ++ list_to_atom(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

@ -1,27 +0,0 @@
%%%-------------------------------------------------------------------
%%% @copyright (C) 2018, Aeternity Anstalt
%%%-------------------------------------------------------------------
-module(enoise_bad_data_tests).
-include_lib("eunit/include/eunit.hrl").
bad_data_hs_1_test() ->
SrvKeyPair = enoise_keypair:new(dh25519),
Proto = enoise_protocol:to_name(xk, dh25519, 'ChaChaPoly', blake2b),
Opts = [{echos, 1}, {reply, self()}],
Srv = enoise_utils:echo_srv_start(4567, Proto, SrvKeyPair, Opts),
bad_client(4567),
SrvRes =
receive {Srv, server_result, Res0} -> Res0
after 500 -> timeout end,
?assertMatch({error, {bad_data, _}}, SrvRes),
ok.
bad_client(Port) ->
{ok, Sock} = gen_tcp:connect("localhost", Port, [binary, {reuseaddr, true}], 100),
gen_tcp:send(Sock, <<0:256/unit:8>>),
timer:sleep(100),
gen_tcp:close(Sock).

View File

@ -26,14 +26,5 @@ 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,13 +44,6 @@ 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

@ -12,12 +12,12 @@ noise_hs_test_() ->
fun() -> test_utils:noise_test_vectors() end, fun() -> test_utils:noise_test_vectors() end,
fun(_X) -> ok end, fun(_X) -> ok end,
fun(Tests) -> fun(Tests) ->
[ {maps:get(protocol_name, T), fun() -> noise_hs_test(T) end} [ {maps:get(name, T), fun() -> noise_hs_test(T) end}
|| T <- test_utils:noise_test_filter(Tests) ] || T <- test_utils:noise_test_filter(Tests) ]
end end
}. }.
noise_hs_test(V = #{ protocol_name := Name }) -> noise_hs_test(V = #{ name := Name }) ->
Protocol = enoise_protocol:from_name(Name), Protocol = enoise_protocol:from_name(Name),
FixK = fun(undefined) -> undefined; FixK = fun(undefined) -> undefined;
@ -43,8 +43,7 @@ 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 }) ->
{ok, HS} = enoise_hs_state:init(P, R, PL, {SecK(S), SecK(E), PubK(RS), undefined}), 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,18 +7,5 @@
-include_lib("eunit/include/eunit.hrl"). -include_lib("eunit/include/eunit.hrl").
name_test() -> name_test() ->
roundtrip("Noise_XK_25519_ChaChaPoly_SHA512"), ?assertMatch(<<"Noise_XK_25519_ChaChaPoly_SHA512">>,
roundtrip("Noise_NN_25519_AESGCM_BLAKE2b"). enoise_protocol:to_name(enoise_protocol:from_name("Noise_XK_25519_ChaChaPoly_SHA512"))).
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

@ -12,12 +12,12 @@ noise_interactive_test_() ->
fun() -> test_utils:noise_test_vectors() end, fun() -> test_utils:noise_test_vectors() end,
fun(_X) -> ok end, fun(_X) -> ok end,
fun(Tests) -> fun(Tests) ->
[ {maps:get(protocol_name, T), fun() -> noise_interactive(T) end} [ {maps:get(name, T), fun() -> noise_interactive(T) end}
|| T <- test_utils:noise_test_filter(Tests) ] || T <- test_utils:noise_test_filter(Tests) ]
end end
}. }.
noise_interactive(V = #{ protocol_name := Name }) -> noise_interactive(V = #{ name := Name }) ->
Protocol = enoise_protocol:from_name(Name), Protocol = enoise_protocol:from_name(Name),
FixK = fun(undefined) -> undefined; FixK = fun(undefined) -> undefined;
@ -41,9 +41,10 @@ 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, RS}, {prologue, PL}], Opts = [{noise, Protocol}, {s, SecK(S)}, {e, SecK(E)}, {rs, PubK(RS)}, {prologue, PL}],
enoise:handshake(Opts, R) enoise:handshake(Opts, R)
end, end,
{ok, InitHS} = HSInit(Init, initiator), {ok, InitHS} = HSInit(Init, initiator),
@ -68,6 +69,8 @@ noise_interactive([#{ payload := PL0, ciphertext := CT0 } | Msgs], SendHS, RecvH
?assertEqual(HSHash, HSHash1), ?assertEqual(HSHash, HSHash2) ?assertEqual(HSHash, HSHash1), ?assertEqual(HSHash, HSHash2)
end. end.
noise_dh25519_test_() -> noise_dh25519_test_() ->
%% Test vectors from https://raw.githubusercontent.com/rweather/noise-c/master/tests/vector/noise-c-basic.txt %% Test vectors from https://raw.githubusercontent.com/rweather/noise-c/master/tests/vector/noise-c-basic.txt
{setup, {setup,
@ -82,8 +85,8 @@ noise_monitor_test_() ->
{setup, {setup,
fun() -> setup_dh25519() end, fun() -> setup_dh25519() end,
fun(_X) -> ok end, fun(_X) -> ok end,
fun({Tests, SKP, CKP}) -> fun({[T|Tests] = _Tests, SKP, CKP}) ->
[ {T, fun() -> noise_monitor_test(T, SKP, CKP) end} || T <- Tests ] [ {T, fun() -> noise_monitor_test(T, SKP, CKP) end} ]
end end
}. }.
@ -101,7 +104,7 @@ setup_dh25519() ->
noise_test(Conf, SKP, CKP) -> noise_test(Conf, SKP, CKP) ->
#{econn := EConn, echo_srv := EchoSrv} = noise_test_run(Conf, SKP, CKP), #{econn := EConn, echo_srv := EchoSrv} = noise_test_run(Conf, SKP, CKP),
enoise:close(EConn), enoise:close(EConn),
enoise_utils:echo_srv_stop(EchoSrv), echo_srv_stop(EchoSrv),
ok. ok.
noise_test_run(Conf, SKP, CKP) -> noise_test_run(Conf, SKP, CKP) ->
@ -148,12 +151,11 @@ 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, enoise_keypair:pubkey(CKP)}], EchoSrv = echo_srv_start(Port, Protocol, SKP, CKP),
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, enoise_keypair:pubkey(SKP)} || enoise_utils:need_rs(initiator, Conf) ], Opts = [{noise, Protocol}, {s, CKP}] ++ [{rs, SKP} || 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!">>),
@ -174,7 +176,7 @@ noise_test_run_(Conf, SKP, CKP) ->
noise_monitor_test(Conf, SKP, CKP) -> noise_monitor_test(Conf, SKP, CKP) ->
#{ econn := {enoise, EConnPid} #{ econn := {enoise, EConnPid}
, proxy := Proxy , proxy := Proxy
, tcp_sock := _TcpSock } = noise_test_run(Conf, SKP, CKP), , tcp_sock := TcpSock } = noise_test_run(Conf, SKP, CKP),
try proxy_exec(Proxy, fun() -> exit(normal) end) try proxy_exec(Proxy, fun() -> exit(normal) end)
catch catch
error:normal -> error:normal ->
@ -183,6 +185,42 @@ noise_monitor_test(Conf, SKP, CKP) ->
end end
end. end.
echo_srv_start(Port, Protocol, SKP, CPub) ->
Pid = spawn(fun() -> echo_srv(Port, Protocol, SKP, CPub) end),
timer:sleep(10),
Pid.
echo_srv(Port, Protocol, SKP, CPub) ->
TcpOpts = [{active, true}, binary, {reuseaddr, true}],
{ok, LSock} = gen_tcp:listen(Port, TcpOpts),
{ok, TcpSock} = gen_tcp:accept(LSock, 500),
Opts = [{noise, Protocol}, {s, SKP}] ++ [{rs, CPub} || need_rs(responder, Protocol)],
{ok, EConn, _} = enoise:accept(TcpSock, Opts),
gen_tcp:close(LSock),
%% {ok, Msg} = enoise:recv(EConn, 0, 100),
Msg0 = receive {noise, EConn, Data0} -> Data0
after 200 -> error(timeout) end,
ok = enoise:send(EConn, Msg0),
%% {ok, Msg} = enoise:recv(EConn, 0, 100),
Msg1 = receive {noise, EConn, Data1} -> Data1
after 200 -> error(timeout) end,
ok = enoise:send(EConn, Msg1),
ok.
echo_srv_stop(Pid) ->
erlang:exit(Pid, kill).
need_rs(Role, Conf) when is_binary(Conf) -> need_rs(Role, enoise_protocol:from_name(Conf));
need_rs(Role, Protocol) ->
PreMsgs = enoise_protocol:pre_msgs(Role, Protocol),
lists:member({in, [s]}, PreMsgs).
%% Talks to local echo-server (noise-c) %% Talks to local echo-server (noise-c)
%% client_test() -> %% client_test() ->
%% TestProtocol = enoise_protocol:from_name("Noise_XK_25519_ChaChaPoly_BLAKE2b"), %% TestProtocol = enoise_protocol:from_name("Noise_XK_25519_ChaChaPoly_BLAKE2b"),

View File

@ -1,81 +0,0 @@
%%%-------------------------------------------------------------------
%%% @copyright (C) 2018, Aeternity Anstalt
%%%-------------------------------------------------------------------
-module(enoise_utils).
-compile([export_all, nowarn_export_all]).
echo_srv_start(Port, Protocol, SKP, Opts) ->
Pid = spawn(fun() -> echo_srv(Port, Protocol, SKP, Opts) end),
timer:sleep(10),
Pid.
echo_srv_stop(Pid) ->
erlang:exit(Pid, kill).
echo_srv(Port, Protocol, SKP, SrvOpts) ->
TcpOpts = [{active, true}, binary, {reuseaddr, true}],
{ok, LSock} = gen_tcp:listen(Port, TcpOpts),
{ok, TcpSock} = gen_tcp:accept(LSock, 500),
Opts = [{noise, Protocol}, {s, SKP}] ++
[{rs, proplists:get_value(cpub, SrvOpts)} || need_rs(responder, Protocol)],
AcceptRes =
try
enoise:accept(TcpSock, Opts)
catch _:R:S -> gen_tcp:close(TcpSock), {error, {R, S}} end,
gen_tcp:close(LSock),
case AcceptRes of
{ok, EConn, _} -> echo_srv_loop(EConn, SrvOpts);
Err = {error, _} -> srv_reply(Err, SrvOpts)
end.
echo_srv_loop(EConn, SrvOpts) ->
Recv =
case proplists:get_value(mode, SrvOpts, passive) of
passive ->
fun() ->
receive {noise, EConn, Data} -> Data
after 200 -> error(timeout) end
end;
active ->
fun() ->
{ok, Msg} = enoise:recv(EConn, 0, 100),
Msg
end
end,
Echos = proplists:get_value(echos, SrvOpts, 2),
Res =
try
[ begin
Msg = Recv(),
ok = enoise:send(EConn, Msg)
end || _ <- lists:seq(1, Echos) ],
ok
catch _:R -> {error, R} end,
srv_reply(Res, SrvOpts),
enoise:close(EConn),
Res.
srv_reply(Reply, SrvOpts) ->
case proplists:get_value(reply, SrvOpts, undefined) of
undefined -> ok;
Pid -> Pid ! {self(), server_result, Reply}
end.
need_rs(Role, Conf) when is_binary(Conf) ->
need_rs(Role, enoise_protocol:from_name(Conf));
need_rs(Role, Protocol) ->
PreMsgs = enoise_protocol:pre_msgs(Role, Protocol),
lists:member({in, [s]}, PreMsgs).

View File

@ -114,10 +114,9 @@ parse_test_vectors(File) ->
%% Only test supported configurations %% Only test supported configurations
noise_test_filter(Tests0) -> noise_test_filter(Tests0) ->
Tests1 = [ T || T = #{ protocol_name := Name } <- Tests0, supported(Name) ], Tests1 = [ T || T = #{ name := Name } <- Tests0, supported(Name) ],
case length(Tests1) < length(Tests0) of case length(Tests1) < length(Tests0) of
true -> ?debugFmt("WARNING: ~p test vectors out of ~p are unsupported", true -> ?debugFmt("WARNING: ~p test vectors are unsupported", [length(Tests0) - length(Tests1)]);
[length(Tests0) - length(Tests1), length(Tests0)]);
false -> ok false -> ok
end, end,
Tests1. Tests1.

File diff suppressed because it is too large Load Diff