Merge pull request #6 from aeternity/fix_bad_data_handshake
Fix bad data handshake
This commit is contained in:
commit
29420d6d63
@ -80,8 +80,10 @@ 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) ->
|
||||||
{HS1, RestBuf1} = read_message(HS#noise_hs{ msgs = Msgs }, Msg, Message),
|
case read_message(HS#noise_hs{ msgs = Msgs }, Msg, Message) of
|
||||||
decrypt_and_hash(HS1, RestBuf1).
|
{ok, HS1, RestBuf1} -> decrypt_and_hash(HS1, RestBuf1);
|
||||||
|
Err = {error, _} -> Err
|
||||||
|
end.
|
||||||
|
|
||||||
remote_keys(#noise_hs{ rs = RS }) ->
|
remote_keys(#noise_hs{ rs = RS }) ->
|
||||||
RS.
|
RS.
|
||||||
@ -93,10 +95,12 @@ 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) ->
|
||||||
{HS, Data};
|
{ok, HS, Data};
|
||||||
read_message(HS, [Token | Tokens], Data0) ->
|
read_message(HS, [Token | Tokens], Data0) ->
|
||||||
{HS1, Data1} = read_token(HS, Token, Data0),
|
case read_token(HS, Token, Data0) of
|
||||||
read_message(HS1, Tokens, Data1).
|
{ok, HS1, 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),
|
||||||
@ -115,21 +119,33 @@ 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),
|
||||||
<<REPub:DHLen/binary, Data1/binary>> = Data0,
|
case Data0 of
|
||||||
|
<<REPub:DHLen/binary, Data1/binary>> ->
|
||||||
RE = enoise_keypair:new(DH, REPub),
|
RE = enoise_keypair:new(DH, REPub),
|
||||||
{mix_hash(HS#noise_hs{ re = RE }, REPub), Data1};
|
{ok, 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,
|
||||||
<<Temp:DHLen/binary, Data1/binary>> = Data0,
|
case Data0 of
|
||||||
{ok, HS1, RSPub} = decrypt_and_hash(HS, Temp),
|
<<Temp:DHLen/binary, Data1/binary>> ->
|
||||||
|
case decrypt_and_hash(HS, Temp) of
|
||||||
|
{ok, HS1, RSPub} ->
|
||||||
RS = enoise_keypair:new(DH, RSPub),
|
RS = enoise_keypair:new(DH, RSPub),
|
||||||
{HS1#noise_hs{ rs = RS }, Data1};
|
{ok, 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),
|
||||||
{mix_key(HS, dh(HS, K1, K2)), Data}.
|
{ok, 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};
|
||||||
|
27
test/enoise_bad_data_tests.erl
Normal file
27
test/enoise_bad_data_tests.erl
Normal file
@ -0,0 +1,27 @@
|
|||||||
|
%%%-------------------------------------------------------------------
|
||||||
|
%%% @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).
|
||||||
|
|
@ -69,8 +69,6 @@ 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,
|
||||||
@ -85,8 +83,8 @@ noise_monitor_test_() ->
|
|||||||
{setup,
|
{setup,
|
||||||
fun() -> setup_dh25519() end,
|
fun() -> setup_dh25519() end,
|
||||||
fun(_X) -> ok end,
|
fun(_X) -> ok end,
|
||||||
fun({[T|Tests] = _Tests, SKP, CKP}) ->
|
fun({Tests, SKP, CKP}) ->
|
||||||
[ {T, fun() -> noise_monitor_test(T, SKP, CKP) end} ]
|
[ {T, fun() -> noise_monitor_test(T, SKP, CKP) end} || T <- Tests ]
|
||||||
end
|
end
|
||||||
}.
|
}.
|
||||||
|
|
||||||
@ -104,7 +102,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),
|
||||||
echo_srv_stop(EchoSrv),
|
enoise_utils:echo_srv_stop(EchoSrv),
|
||||||
ok.
|
ok.
|
||||||
|
|
||||||
noise_test_run(Conf, SKP, CKP) ->
|
noise_test_run(Conf, SKP, CKP) ->
|
||||||
@ -151,11 +149,12 @@ noise_test_run_(Conf, SKP, CKP) ->
|
|||||||
Protocol = enoise_protocol:from_name(Conf),
|
Protocol = enoise_protocol:from_name(Conf),
|
||||||
Port = 4556,
|
Port = 4556,
|
||||||
|
|
||||||
EchoSrv = echo_srv_start(Port, Protocol, SKP, CKP),
|
SrvOpts = [{echos, 2}, {cpub, 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, SKP} || need_rs(initiator, Conf) ],
|
Opts = [{noise, Protocol}, {s, CKP}] ++ [{rs, 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!">>),
|
||||||
@ -176,7 +175,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 ->
|
||||||
@ -185,42 +184,6 @@ 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"),
|
||||||
|
81
test/enoise_utils.erl
Normal file
81
test/enoise_utils.erl
Normal file
@ -0,0 +1,81 @@
|
|||||||
|
%%%-------------------------------------------------------------------
|
||||||
|
%%% @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 -> gen_tcp:close(TcpSock), {error, {R, erlang:get_stacktrace()}} 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).
|
||||||
|
|
Loading…
x
Reference in New Issue
Block a user