Add tests at gen_tcp-level
Move data structure tests to enoise_hs_state_tests
This commit is contained in:
parent
955e5c2f32
commit
866b4421b3
85
test/enoise_hs_state_tests.erl
Normal file
85
test/enoise_hs_state_tests.erl
Normal file
@ -0,0 +1,85 @@
|
||||
%%%-------------------------------------------------------------------
|
||||
%%% @copyright (C) 2018, Aeternity Anstalt
|
||||
%%%-------------------------------------------------------------------
|
||||
|
||||
-module(enoise_hs_state_tests).
|
||||
|
||||
-include_lib("eunit/include/eunit.hrl").
|
||||
-record(key_pair, { puk, pik }).
|
||||
|
||||
noise_hs_test_() ->
|
||||
%% Test vectors from https://raw.githubusercontent.com/rweather/noise-c/master/tests/vector/noise-c-basic.txt
|
||||
{setup,
|
||||
fun() -> test_utils:noise_test_vectors() end,
|
||||
fun(_X) -> ok end,
|
||||
fun(Tests) ->
|
||||
[ {maps:get(name, T), fun() -> noise_hs_test(T) end}
|
||||
|| T <- test_utils:noise_test_filter(Tests) ]
|
||||
end
|
||||
}.
|
||||
|
||||
noise_hs_test(V = #{ name := Name }) ->
|
||||
Protocol = enoise_protocol:from_name(Name),
|
||||
|
||||
FixK = fun(undefined) -> undefined;
|
||||
(Bin) -> test_utils:hex_str_to_bin("0x" ++ binary_to_list(Bin)) end,
|
||||
|
||||
Init = #{ prologue => FixK(maps:get(init_prologue, V, <<>>))
|
||||
, e => FixK(maps:get(init_ephemeral, V, undefined))
|
||||
, s => FixK(maps:get(init_static, V, undefined))
|
||||
, rs => FixK(maps:get(init_remote_static, V, undefined)) },
|
||||
Resp = #{ prologue => FixK(maps:get(resp_prologue, V, <<>>))
|
||||
, e => FixK(maps:get(resp_ephemeral, V, undefined))
|
||||
, s => FixK(maps:get(resp_static, V, undefined))
|
||||
, rs => FixK(maps:get(resp_remote_static, V, undefined)) },
|
||||
Messages = maps:get(messages, V),
|
||||
HandshakeHash = maps:get(handshake_hash, V),
|
||||
|
||||
noise_test(Name, Protocol, Init, Resp, Messages, FixK(HandshakeHash)),
|
||||
|
||||
ok.
|
||||
|
||||
noise_test(_Name, Protocol, Init, Resp, Messages, HSHash) ->
|
||||
PubK = fun(undefined) -> undefined; (S) -> enacl:curve25519_scalarmult_base(S) end,
|
||||
HSInit = fun(P, R, #{ e := E, s := S, rs := RS, prologue := PL }) ->
|
||||
enoise_hs_state:init(P, R, PL, {#key_pair{ pik = S, puk = PubK(S) },
|
||||
#key_pair{ pik = E, puk = PubK(E) },
|
||||
RS, undefined})
|
||||
end,
|
||||
|
||||
InitHS = HSInit(Protocol, initiator, Init),
|
||||
RespHS = HSInit(Protocol, responder, Resp),
|
||||
|
||||
noise_test(Messages, InitHS, RespHS, HSHash),
|
||||
|
||||
ok.
|
||||
|
||||
noise_test([M = #{ payload := PL0, ciphertext := CT0 } | Msgs], SendHS, RecvHS, HSHash) ->
|
||||
PL = test_utils:hex_str_to_bin("0x" ++ binary_to_list(PL0)),
|
||||
CT = test_utils:hex_str_to_bin("0x" ++ binary_to_list(CT0)),
|
||||
case {enoise_hs_state:next_message(SendHS), enoise_hs_state:next_message(RecvHS)} of
|
||||
{out, in} ->
|
||||
{ok, SendHS1, Message} = enoise_hs_state:write_message(SendHS, PL),
|
||||
?assertEqual(CT, Message),
|
||||
{ok, RecvHS1, PL1} = enoise_hs_state:read_message(RecvHS, Message),
|
||||
?assertEqual(PL, PL1),
|
||||
noise_test(Msgs, RecvHS1, SendHS1, HSHash);
|
||||
{done, done} ->
|
||||
{ok, #{ rx := RX1, tx := TX1, hs_hash := HSHash1 }} = enoise_hs_state:finalize(SendHS),
|
||||
{ok, #{ rx := RX2, tx := TX2, hs_hash := HSHash2 }} = enoise_hs_state:finalize(RecvHS),
|
||||
?assertEqual(RX1, TX2), ?assertEqual(RX2, TX1),
|
||||
?assertEqual(HSHash, HSHash1), ?assertEqual(HSHash, HSHash2),
|
||||
noise_test([M | Msgs], TX1, RX1);
|
||||
{Out, In} -> ?assertMatch({out, in}, {Out, In})
|
||||
end.
|
||||
|
||||
noise_test([], _, _) -> ok;
|
||||
noise_test([#{ payload := PL0, ciphertext := CT0 } | Msgs], CA, CB) ->
|
||||
PL = test_utils:hex_str_to_bin("0x" ++ binary_to_list(PL0)),
|
||||
CT = test_utils:hex_str_to_bin("0x" ++ binary_to_list(CT0)),
|
||||
{ok, CA1, CT1} = enoise_cipher_state:encrypt_with_ad(CA, <<>>, PL),
|
||||
?assertEqual(CT, CT1),
|
||||
{ok, CA2, PL1} = enoise_cipher_state:decrypt_with_ad(CA, <<>>, CT1),
|
||||
?assertEqual(CA1, CA2),
|
||||
?assertEqual(PL, PL1),
|
||||
noise_test(Msgs, CB, CA1).
|
@ -5,120 +5,108 @@
|
||||
-module(enoise_tests).
|
||||
|
||||
-include_lib("eunit/include/eunit.hrl").
|
||||
-record(key_pair, { puk, pik }).
|
||||
|
||||
|
||||
noise_test_() ->
|
||||
%% Test vector from https://raw.githubusercontent.com/rweather/noise-c/master/tests/vector/noise-c-basic.txt
|
||||
noise_dh25519_test_() ->
|
||||
%% Test vectors from https://raw.githubusercontent.com/rweather/noise-c/master/tests/vector/noise-c-basic.txt
|
||||
{setup,
|
||||
fun() -> test_utils:noise_test_vectors() end,
|
||||
fun() -> setup_dh25519() end,
|
||||
fun(_X) -> ok end,
|
||||
fun(Tests) ->
|
||||
[ {maps:get(name, T), fun() -> noise_test(T) end} || T <- noise_test_filter(Tests) ]
|
||||
fun({Tests, SKP, CKP}) ->
|
||||
[ {T, fun() -> noise_test(T, SKP, CKP) end} || T <- Tests ]
|
||||
end
|
||||
}.
|
||||
|
||||
noise_test_filter(Tests0) ->
|
||||
Tests1 = [ T || T = #{ name := Name } <- Tests0, supported(Name) ],
|
||||
case length(Tests1) < length(Tests0) of
|
||||
true -> ?debugFmt("WARNING: ~p test vectors are unsupported", [length(Tests0) - length(Tests1)]);
|
||||
false -> ok
|
||||
end,
|
||||
Tests1.
|
||||
setup_dh25519() ->
|
||||
%% Generate a static key-pair for Client and Server
|
||||
SrvKeyPair = enoise_crypto:new_key_pair(dh25519),
|
||||
CliKeyPair = enoise_crypto:new_key_pair(dh25519),
|
||||
|
||||
supported(Name) ->
|
||||
try enoise_protocol:from_name(Name), true
|
||||
catch _:_ -> false end.
|
||||
#{ hs_pattern := Ps, hash := Hs, cipher := Cs } = enoise_protocol:supported(),
|
||||
Configurations = [ enoise_protocol:to_name(P, dh25519, C, H)
|
||||
|| P <- Ps, C <- Cs, H <- Hs ],
|
||||
%% Configurations = [ enoise_protocol:to_name(xk, dh25519, 'ChaChaPoly', blake2b) ],
|
||||
{Configurations, SrvKeyPair, CliKeyPair}.
|
||||
|
||||
noise_test(V = #{ name := Name }) ->
|
||||
%% ?debugFmt("~s", [Name]),
|
||||
Protocol = enoise_protocol:from_name(Name),
|
||||
noise_test(Conf, SKP, CKP) ->
|
||||
Protocol = enoise_protocol:from_name(Conf),
|
||||
Port = 4556,
|
||||
|
||||
FixK = fun(undefined) -> undefined;
|
||||
(Bin) -> test_utils:hex_str_to_bin("0x" ++ binary_to_list(Bin)) end,
|
||||
EchoSrv = echo_srv_start(Port, Protocol, SKP, enoise_crypto:pub_key(CKP)),
|
||||
|
||||
Init = #{ prologue => FixK(maps:get(init_prologue, V, <<>>))
|
||||
, e => FixK(maps:get(init_ephemeral, V, undefined))
|
||||
, s => FixK(maps:get(init_static, V, undefined))
|
||||
, rs => FixK(maps:get(init_remote_static, V, undefined)) },
|
||||
Resp = #{ prologue => FixK(maps:get(resp_prologue, V, <<>>))
|
||||
, e => FixK(maps:get(resp_ephemeral, V, undefined))
|
||||
, s => FixK(maps:get(resp_static, V, undefined))
|
||||
, rs => FixK(maps:get(resp_remote_static, V, undefined)) },
|
||||
Messages = maps:get(messages, V),
|
||||
HandshakeHash = maps:get(handshake_hash, V),
|
||||
{ok, TcpSock} = gen_tcp:connect("localhost", Port, [{active, false}, binary, {reuseaddr, true}], 100),
|
||||
|
||||
noise_test(Name, Protocol, Init, Resp, Messages, FixK(HandshakeHash)),
|
||||
Opts = [{noise, Protocol}, {s, CKP}] ++ [{rs, enoise_crypto:pub_key(SKP)} || need_rs(initiator, Conf) ],
|
||||
{ok, EConn} = enoise:connect(TcpSock, Opts),
|
||||
|
||||
ok = enoise:send(EConn, <<"Hello World!">>),
|
||||
{ok, <<"Hello World!">>} = enoise:recv(EConn, 12, 100),
|
||||
|
||||
ok = enoise:send(EConn, <<"Goodbye!">>),
|
||||
timer:sleep(10),
|
||||
{ok, <<"Goodbye!">>} = enoise:recv(EConn, 0, 100),
|
||||
|
||||
enoise:close(EConn),
|
||||
echo_srv_stop(EchoSrv),
|
||||
ok.
|
||||
|
||||
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.
|
||||
|
||||
noise_test(_Name, Protocol, Init, Resp, Messages, HSHash) ->
|
||||
PubK = fun(undefined) -> undefined; (S) -> enacl:curve25519_scalarmult_base(S) end,
|
||||
HSInit = fun(P, R, #{ e := E, s := S, rs := RS, prologue := PL }) ->
|
||||
enoise_hs_state:init(P, R, PL, {#key_pair{ pik = S, puk = PubK(S) },
|
||||
#key_pair{ pik = E, puk = PubK(E) },
|
||||
RS, undefined})
|
||||
end,
|
||||
echo_srv_stop(Pid) ->
|
||||
erlang:exit(Pid, kill).
|
||||
|
||||
InitHS = HSInit(Protocol, initiator, Init),
|
||||
RespHS = HSInit(Protocol, responder, Resp),
|
||||
|
||||
noise_test(Messages, InitHS, RespHS, HSHash),
|
||||
|
||||
ok.
|
||||
|
||||
noise_test([M = #{ payload := PL0, ciphertext := CT0 } | Msgs], SendHS, RecvHS, HSHash) ->
|
||||
PL = test_utils:hex_str_to_bin("0x" ++ binary_to_list(PL0)),
|
||||
CT = test_utils:hex_str_to_bin("0x" ++ binary_to_list(CT0)),
|
||||
case {enoise_hs_state:next_message(SendHS), enoise_hs_state:next_message(RecvHS)} of
|
||||
{out, in} ->
|
||||
{ok, SendHS1, Message} = enoise_hs_state:write_message(SendHS, PL),
|
||||
?assertEqual(CT, Message),
|
||||
{ok, RecvHS1, PL1} = enoise_hs_state:read_message(RecvHS, Message),
|
||||
?assertEqual(PL, PL1),
|
||||
noise_test(Msgs, RecvHS1, SendHS1, HSHash);
|
||||
{done, done} ->
|
||||
{ok, #{ rx := RX1, tx := TX1, hs_hash := HSHash1 }} = enoise_hs_state:finalize(SendHS),
|
||||
{ok, #{ rx := RX2, tx := TX2, hs_hash := HSHash2 }} = enoise_hs_state:finalize(RecvHS),
|
||||
?assertEqual(RX1, TX2), ?assertEqual(RX2, TX1),
|
||||
?assertEqual(HSHash, HSHash1), ?assertEqual(HSHash, HSHash2),
|
||||
noise_test([M | Msgs], TX1, RX1);
|
||||
{Out, In} -> ?assertMatch({out, in}, {Out, In})
|
||||
end.
|
||||
|
||||
noise_test([], _, _) -> ok;
|
||||
noise_test([#{ payload := PL0, ciphertext := CT0 } | Msgs], CA, CB) ->
|
||||
PL = test_utils:hex_str_to_bin("0x" ++ binary_to_list(PL0)),
|
||||
CT = test_utils:hex_str_to_bin("0x" ++ binary_to_list(CT0)),
|
||||
{ok, CA1, CT1} = enoise_cipher_state:encrypt_with_ad(CA, <<>>, PL),
|
||||
?assertEqual(CT, CT1),
|
||||
{ok, CA2, PL1} = enoise_cipher_state:decrypt_with_ad(CA, <<>>, CT1),
|
||||
?assertEqual(CA1, CA2),
|
||||
?assertEqual(PL, PL1),
|
||||
noise_test(Msgs, CB, CA1).
|
||||
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)
|
||||
client_test() ->
|
||||
TestProtocol = enoise_protocol:from_name("Noise_XK_25519_ChaChaPoly_BLAKE2b"),
|
||||
ClientPrivKey = <<64,168,119,119,151,194,94,141,86,245,144,220,78,53,243,231,168,216,66,199,49,148,202,117,98,40,61,109,170,37,133,122>>,
|
||||
ClientPubKey = <<115,39,86,77,44,85,192,176,202,11,4,6,194,144,127,123, 34,67,62,180,190,232,251,5,216,168,192,190,134,65,13,64>>,
|
||||
ServerPubKey = <<112,91,141,253,183,66,217,102,211,40,13,249,238,51,77,114,163,159,32,1,162,219,76,106,89,164,34,71,149,2,103,59>>,
|
||||
%% client_test() ->
|
||||
%% TestProtocol = enoise_protocol:from_name("Noise_XK_25519_ChaChaPoly_BLAKE2b"),
|
||||
%% ClientPrivKey = <<64,168,119,119,151,194,94,141,86,245,144,220,78,53,243,231,168,216,66,199,49,148,202,117,98,40,61,109,170,37,133,122>>,
|
||||
%% ClientPubKey = <<115,39,86,77,44,85,192,176,202,11,4,6,194,144,127,123, 34,67,62,180,190,232,251,5,216,168,192,190,134,65,13,64>>,
|
||||
%% ServerPubKey = <<112,91,141,253,183,66,217,102,211,40,13,249,238,51,77,114,163,159,32,1,162,219,76,106,89,164,34,71,149,2,103,59>>,
|
||||
|
||||
{ok, TcpSock} = gen_tcp:connect("localhost", 7890, [{active, false}, binary, {reuseaddr, true}], 1000),
|
||||
gen_tcp:send(TcpSock, <<0,8,0,0,3>>), %% "Noise_XK_25519_ChaChaPoly_Blake2b"
|
||||
%% {ok, TcpSock} = gen_tcp:connect("localhost", 7890, [{active, false}, binary, {reuseaddr, true}], 1000),
|
||||
%% gen_tcp:send(TcpSock, <<0,8,0,0,3>>), %% "Noise_XK_25519_ChaChaPoly_Blake2b"
|
||||
|
||||
Opts = [ {noise, TestProtocol}
|
||||
, {s, #key_pair{ pik = ClientPrivKey, puk = ClientPubKey }}
|
||||
, {rs, ServerPubKey}
|
||||
, {prologue, <<0,8,0,0,3>>}],
|
||||
%% Opts = [ {noise, TestProtocol}
|
||||
%% , {s, #key_pair{ pik = ClientPrivKey, puk = ClientPubKey }}
|
||||
%% , {rs, ServerPubKey}
|
||||
%% , {prologue, <<0,8,0,0,3>>}],
|
||||
|
||||
{ok, EConn} = enoise:connect(TcpSock, Opts),
|
||||
ok = enoise:send(EConn, <<"ok\n">>),
|
||||
%% receive
|
||||
%% {noise, EConn, <<"ok\n">>} -> ok
|
||||
%% after 1000 -> error(timeout) end,
|
||||
{ok, <<"ok\n">>} = enoise:recv(EConn, 3, 1000),
|
||||
enoise:close(EConn).
|
||||
%% {ok, EConn} = enoise:connect(TcpSock, Opts),
|
||||
%% ok = enoise:send(EConn, <<"ok\n">>),
|
||||
%% %% receive
|
||||
%% %% {noise, EConn, <<"ok\n">>} -> ok
|
||||
%% %% after 1000 -> error(timeout) end,
|
||||
%% {ok, <<"ok\n">>} = enoise:recv(EConn, 3, 1000),
|
||||
%% enoise:close(EConn).
|
||||
|
||||
|
||||
%% Expects a call-in from a local echo-client (noise-c)
|
||||
|
@ -112,3 +112,15 @@ parse_test_vectors(File) ->
|
||||
#{ vectors := Vectors } = jsx:decode(Bin, [{labels, atom}, return_maps]),
|
||||
Vectors.
|
||||
|
||||
%% Only test supported configurations
|
||||
noise_test_filter(Tests0) ->
|
||||
Tests1 = [ T || T = #{ name := Name } <- Tests0, supported(Name) ],
|
||||
case length(Tests1) < length(Tests0) of
|
||||
true -> ?debugFmt("WARNING: ~p test vectors are unsupported", [length(Tests0) - length(Tests1)]);
|
||||
false -> ok
|
||||
end,
|
||||
Tests1.
|
||||
|
||||
supported(Name) ->
|
||||
try enoise_protocol:from_name(Name), true
|
||||
catch _:_ -> false end.
|
||||
|
Loading…
x
Reference in New Issue
Block a user