调整协议,只加密头部分
This commit is contained in:
parent
94dc39805e
commit
22db3e1f46
@ -13,8 +13,9 @@ Run
|
|||||||
|
|
||||||
$ rebar3 shell
|
$ rebar3 shell
|
||||||
|
|
||||||
The UDP listener is configured in `config/sys.config` and listens on port `16380`
|
The UDP listener is configured in `config/sys.config`; the checked-in config
|
||||||
by default. It accepts RelayKit UDP relay frames.
|
listens on port `1443`, while the code fallback default is `16380`. It accepts
|
||||||
|
RelayKit UDP relay frames.
|
||||||
|
|
||||||
Protocol
|
Protocol
|
||||||
--------
|
--------
|
||||||
@ -29,7 +30,8 @@ The decrypted plaintext is one `RKP1` frame:
|
|||||||
magic(4) = "RKP1", version(1) = 1, type(1), reserved(2),
|
magic(4) = "RKP1", version(1) = 1, type(1), reserved(2),
|
||||||
stream_id(8), payload_len(4), payload(payload_len)
|
stream_id(8), payload_len(4), payload(payload_len)
|
||||||
|
|
||||||
Frame types are `open = 1`, `data = 2`, `close = 3`, and `error = 4`.
|
Frame types are `open = 1`, `open_ack = 2`, `data = 3`, `close = 4`, and
|
||||||
|
`error = 5`.
|
||||||
`open` payload is binary encoded as:
|
`open` payload is binary encoded as:
|
||||||
|
|
||||||
host_len(2), host(host_len), port(2),
|
host_len(2), host(host_len), port(2),
|
||||||
|
|||||||
@ -1,62 +1,38 @@
|
|||||||
%%%-------------------------------------------------------------------
|
%%%-------------------------------------------------------------------
|
||||||
%% @doc ChaCha20-Poly1305 helpers compatible with CryptoKit ChaChaPoly.
|
%% @doc ChaCha20-Poly1305 helpers compatible with CryptoKit ChaChaPoly.
|
||||||
|
%% 1. 只解决需要加解密数据的逻辑处理
|
||||||
%% @end
|
%% @end
|
||||||
%%%-------------------------------------------------------------------
|
%%%-------------------------------------------------------------------
|
||||||
|
|
||||||
-module(chacha20_cipher).
|
-module(chacha20_cipher).
|
||||||
|
|
||||||
-export([encrypt/2, decrypt/2]).
|
-export([encrypt/2, decrypt/4]).
|
||||||
|
|
||||||
-define(KEY_BYTES, 32).
|
-define(KEY_BYTES, 32).
|
||||||
-define(NONCE_BYTES, 12).
|
-define(NONCE_BYTES, 12).
|
||||||
-define(TAG_BYTES, 16).
|
-define(TAG_BYTES, 16).
|
||||||
-define(AAD, <<>>).
|
-define(AAD, <<>>).
|
||||||
|
|
||||||
encrypt(PlainText, Key) when is_binary(PlainText) ->
|
encrypt(PlainText, Key) when is_binary(PlainText), byte_size(Key) =:= ?KEY_BYTES ->
|
||||||
case valid_key(Key) of
|
Nonce = crypto:strong_rand_bytes(?NONCE_BYTES),
|
||||||
true ->
|
try crypto:crypto_one_time_aead(chacha20_poly1305, Key, Nonce, PlainText, ?AAD, true) of
|
||||||
Nonce = crypto:strong_rand_bytes(?NONCE_BYTES),
|
{CipherText, Tag} ->
|
||||||
try crypto:crypto_one_time_aead(
|
{ok, <<Nonce/binary, CipherText/binary, Tag/binary>>}
|
||||||
chacha20_poly1305, Key, Nonce, PlainText, ?AAD, true
|
catch
|
||||||
) of
|
_Class:_Reason ->
|
||||||
{CipherText, Tag} ->
|
{error, crypto_failed}
|
||||||
{ok, <<Nonce/binary, CipherText/binary, Tag/binary>>}
|
|
||||||
catch
|
|
||||||
_Class:_Reason ->
|
|
||||||
{error, crypto_failed}
|
|
||||||
end;
|
|
||||||
false ->
|
|
||||||
{error, invalid_key}
|
|
||||||
end;
|
end;
|
||||||
|
|
||||||
encrypt(_PlainText, _Key) ->
|
encrypt(_PlainText, _Key) ->
|
||||||
{error, invalid_plaintext}.
|
{error, invalid_plaintext}.
|
||||||
|
|
||||||
decrypt(Combined, Key) when is_binary(Combined), byte_size(Combined) >= ?NONCE_BYTES + ?TAG_BYTES ->
|
decrypt(Nonce, CipherText, Tag, Key) when is_binary(Nonce), is_binary(CipherText), is_binary(Tag), is_binary(Key) ->
|
||||||
case valid_key(Key) of
|
try crypto:crypto_one_time_aead(chacha20_poly1305, Key, Nonce, CipherText, ?AAD, Tag, false) of
|
||||||
true ->
|
error ->
|
||||||
<<Nonce:?NONCE_BYTES/binary, Rest/binary>> = Combined,
|
{error, authentication_failed};
|
||||||
CipherTextBytes = byte_size(Rest) - ?TAG_BYTES,
|
PlainText ->
|
||||||
<<CipherText:CipherTextBytes/binary, Tag:?TAG_BYTES/binary>> = Rest,
|
{ok, PlainText}
|
||||||
try crypto:crypto_one_time_aead(
|
catch
|
||||||
chacha20_poly1305, Key, Nonce, CipherText, ?AAD, Tag, false
|
_Class:_Reason ->
|
||||||
) of
|
{error, crypto_failed}
|
||||||
error ->
|
end.
|
||||||
{error, authentication_failed};
|
|
||||||
PlainText ->
|
|
||||||
{ok, PlainText}
|
|
||||||
catch
|
|
||||||
_Class:_Reason ->
|
|
||||||
{error, crypto_failed}
|
|
||||||
end;
|
|
||||||
false ->
|
|
||||||
{error, invalid_key}
|
|
||||||
end;
|
|
||||||
decrypt(Combined, _Key) when is_binary(Combined) ->
|
|
||||||
{error, ciphertext_too_short};
|
|
||||||
decrypt(_Combined, _Key) ->
|
|
||||||
{error, invalid_ciphertext}.
|
|
||||||
|
|
||||||
valid_key(Key) when is_binary(Key), byte_size(Key) =:= ?KEY_BYTES ->
|
|
||||||
true;
|
|
||||||
valid_key(_Key) ->
|
|
||||||
false.
|
|
||||||
@ -13,17 +13,6 @@
|
|||||||
|
|
||||||
-define(TCP_OPTIONS, [binary, {packet, raw}, {active, once}, {nodelay, true}]).
|
-define(TCP_OPTIONS, [binary, {packet, raw}, {active, once}, {nodelay, true}]).
|
||||||
|
|
||||||
-define(CHACHA20_KEY, <<
|
|
||||||
16#9F, 16#4A, 16#6C, 16#0D,
|
|
||||||
16#8B, 16#21, 16#7E, 16#3F,
|
|
||||||
16#42, 16#D9, 16#AA, 16#78,
|
|
||||||
16#13, 16#C5, 16#E2, 16#B6,
|
|
||||||
16#F0, 16#A9, 16#27, 16#BC,
|
|
||||||
16#6D, 16#31, 16#E8, 16#4C,
|
|
||||||
16#55, 16#FA, 16#10, 16#2A,
|
|
||||||
16#7E, 16#9D, 16#3C, 16#BB
|
|
||||||
>>).
|
|
||||||
|
|
||||||
-record(stream, {
|
-record(stream, {
|
||||||
id :: non_neg_integer(),
|
id :: non_neg_integer(),
|
||||||
socket :: inet:socket(),
|
socket :: inet:socket(),
|
||||||
@ -38,7 +27,6 @@
|
|||||||
peer :: {inet:ip_address(), inet:port_number()},
|
peer :: {inet:ip_address(), inet:port_number()},
|
||||||
idle_timeout :: timeout(),
|
idle_timeout :: timeout(),
|
||||||
connect_timeout :: timeout(),
|
connect_timeout :: timeout(),
|
||||||
cipher_key :: binary(),
|
|
||||||
users = #{} :: #{binary() => binary()},
|
users = #{} :: #{binary() => binary()},
|
||||||
streams = #{} :: #{non_neg_integer() => stream()},
|
streams = #{} :: #{non_neg_integer() => stream()},
|
||||||
sockets = #{} :: #{inet:socket() => non_neg_integer()}
|
sockets = #{} :: #{inet:socket() => non_neg_integer()}
|
||||||
@ -57,7 +45,6 @@ init([{udp, Server, _Sock}, Peer, IdleTimeout, ConnectTimeout, Users]) ->
|
|||||||
peer = Peer,
|
peer = Peer,
|
||||||
idle_timeout = IdleTimeout,
|
idle_timeout = IdleTimeout,
|
||||||
connect_timeout = ConnectTimeout,
|
connect_timeout = ConnectTimeout,
|
||||||
cipher_key = ?CHACHA20_KEY,
|
|
||||||
users = normalize_users(Users)
|
users = normalize_users(Users)
|
||||||
}, IdleTimeout}.
|
}, IdleTimeout}.
|
||||||
|
|
||||||
@ -90,16 +77,7 @@ terminate(_Reason, #state{streams = Streams}) ->
|
|||||||
code_change(_OldVsn, State, _Extra) ->
|
code_change(_OldVsn, State, _Extra) ->
|
||||||
{ok, State}.
|
{ok, State}.
|
||||||
|
|
||||||
handle_encrypted_datagram(Packet, State = #state{idle_timeout = IdleTimeout, cipher_key = Key}) ->
|
handle_encrypted_datagram(Packet, State = #state{idle_timeout = IdleTimeout}) ->
|
||||||
case chacha20_cipher:decrypt(Packet, Key) of
|
|
||||||
{ok, PlainPacket} ->
|
|
||||||
handle_datagram(PlainPacket, State);
|
|
||||||
{error, Reason} ->
|
|
||||||
logger:error("UDP relay ignored undecryptable datagram: ~p", [Reason]),
|
|
||||||
{noreply, State, IdleTimeout}
|
|
||||||
end.
|
|
||||||
|
|
||||||
handle_datagram(Packet, State = #state{idle_timeout = IdleTimeout}) ->
|
|
||||||
case relay_server_udp_protocol:decode(Packet) of
|
case relay_server_udp_protocol:decode(Packet) of
|
||||||
{ok, connection_close} ->
|
{ok, connection_close} ->
|
||||||
{stop, normal, State};
|
{stop, normal, State};
|
||||||
@ -246,21 +224,19 @@ remove_stream(StreamId, State = #state{streams = Streams, sockets = Sockets}) ->
|
|||||||
close_remote(#stream{socket = Socket}) ->
|
close_remote(#stream{socket = Socket}) ->
|
||||||
gen_tcp:close(Socket).
|
gen_tcp:close(Socket).
|
||||||
|
|
||||||
send_frame(_Type, _StreamId, <<>>, _State) ->
|
send_frame(Type, StreamId, <<Part:1200/binary, Rest/binary>>, State)
|
||||||
ok;
|
when byte_size(Rest) > 0 ->
|
||||||
send_frame(Type, StreamId, <<Part:1200/binary, Rest/binary>>, State) ->
|
|
||||||
case send_frame0(Type, StreamId, Part, State) of
|
case send_frame0(Type, StreamId, Part, State) of
|
||||||
ok ->
|
ok ->
|
||||||
send_frame(Type, StreamId, Rest, State);
|
send_frame(Type, StreamId, Rest, State);
|
||||||
Error ->
|
Error ->
|
||||||
Error
|
Error
|
||||||
end;
|
end;
|
||||||
send_frame(Type, StreamId, Rest, State) ->
|
send_frame(Type, StreamId, Payload, State) ->
|
||||||
send_frame0(Type, StreamId, Rest, State).
|
send_frame0(Type, StreamId, Payload, State).
|
||||||
|
|
||||||
send_frame0(Type, StreamId, Part, #state{server = Server, peer = Peer, cipher_key = Key}) ->
|
send_frame0(Type, StreamId, Part, #state{server = Server, peer = Peer}) ->
|
||||||
PlainPacket = relay_server_udp_protocol:encode(Type, StreamId, Part),
|
case relay_server_udp_protocol:encode(Type, StreamId, Part) of
|
||||||
case chacha20_cipher:encrypt(PlainPacket, Key) of
|
|
||||||
{ok, Packet} ->
|
{ok, Packet} ->
|
||||||
logger:error("UDP relay encrypt packet size: ~p", [byte_size(Packet)]),
|
logger:error("UDP relay encrypt packet size: ~p", [byte_size(Packet)]),
|
||||||
Server ! {datagram, Peer, Packet},
|
Server ! {datagram, Peer, Packet},
|
||||||
|
|||||||
@ -14,28 +14,52 @@
|
|||||||
|
|
||||||
-define(CONNECTION_CLOSE, 16#FF).
|
-define(CONNECTION_CLOSE, 16#FF).
|
||||||
|
|
||||||
|
-define(CHACHA20_KEY, <<
|
||||||
|
16#9F, 16#4A, 16#6C, 16#0D,
|
||||||
|
16#8B, 16#21, 16#7E, 16#3F,
|
||||||
|
16#42, 16#D9, 16#AA, 16#78,
|
||||||
|
16#13, 16#C5, 16#E2, 16#B6,
|
||||||
|
16#F0, 16#A9, 16#27, 16#BC,
|
||||||
|
16#6D, 16#31, 16#E8, 16#4C,
|
||||||
|
16#55, 16#FA, 16#10, 16#2A,
|
||||||
|
16#7E, 16#9D, 16#3C, 16#BB
|
||||||
|
>>).
|
||||||
|
|
||||||
|
decode(<<Nonce:12/binary, CipherHead:20/binary, Tag:16/binary, Body/binary>>) ->
|
||||||
|
case chacha20_cipher:decrypt(Nonce, CipherHead, Tag, ?CHACHA20_KEY) of
|
||||||
|
{ok, PlainText} ->
|
||||||
|
decode0(PlainText, Body);
|
||||||
|
Error ->
|
||||||
|
Error
|
||||||
|
end.
|
||||||
%% 全局控制指令也要加密处理
|
%% 全局控制指令也要加密处理
|
||||||
decode(<<"RKP1", ?VERSION:8, ?CONNECTION_CLOSE:8, 0:16, 0:64, 0:32, _Rest/binary>>) ->
|
decode0(<<"RKP1", ?VERSION:8, ?CONNECTION_CLOSE:8, 0:16, 0:64, 0:32>>, _Body) ->
|
||||||
{ok, connection_close};
|
{ok, connection_close};
|
||||||
decode(<<"RKP1", ?VERSION:8, TypeNo:8, 0:16, StreamId:64, PayloadLen:32, Rest/binary>>) ->
|
decode0(<<"RKP1", ?VERSION:8, TypeNo:8, 0:16, StreamId:64, PayloadLen:32>>, Body) when is_binary(Body) ->
|
||||||
case {type(TypeNo), byte_size(Rest) >= PayloadLen} of
|
case {type(TypeNo), byte_size(Body) >= PayloadLen} of
|
||||||
{undefined, _} ->
|
{undefined, _} ->
|
||||||
{error, unknown_frame_type};
|
{error, unknown_frame_type};
|
||||||
{_, false} ->
|
{_, false} ->
|
||||||
{error, truncated_frame};
|
{error, truncated_frame};
|
||||||
{Type, true} ->
|
{Type, true} ->
|
||||||
<<Payload:PayloadLen/binary, _Trailing/binary>> = Rest,
|
<<Payload:PayloadLen/binary, _Trailing/binary>> = Body,
|
||||||
{ok, {Type, StreamId, Payload}}
|
{ok, {Type, StreamId, Payload}}
|
||||||
end;
|
end;
|
||||||
decode(Packet) when byte_size(Packet) < ?HEADER_LENGTH ->
|
decode0(Header, _Body) when byte_size(Header) < ?HEADER_LENGTH ->
|
||||||
{error, frame_too_short};
|
{error, frame_too_short};
|
||||||
decode(_Packet) ->
|
decode0(_Header, _Body) ->
|
||||||
{error, bad_frame_header}.
|
{error, bad_frame_header}.
|
||||||
|
|
||||||
encode(Type, StreamId, Payload) when is_binary(Payload) ->
|
encode(Type, StreamId, Payload) when is_atom(Type), is_integer(StreamId), is_binary(Payload) ->
|
||||||
TypeNo = type_no(Type),
|
TypeNo = type_no(Type),
|
||||||
PayloadLen = byte_size(Payload),
|
PayloadLen = byte_size(Payload),
|
||||||
<<"RKP1", ?VERSION:8, TypeNo:8, 0:16, StreamId:64, PayloadLen:32, Payload/binary>>.
|
Head = <<"RKP1", ?VERSION:8, TypeNo:8, 0:16, StreamId:64, PayloadLen:32>>,
|
||||||
|
case chacha20_cipher:encrypt(Head, ?CHACHA20_KEY) of
|
||||||
|
{ok, EncryptHead} ->
|
||||||
|
<<EncryptHead/binary, Payload/binary>>;
|
||||||
|
Error ->
|
||||||
|
Error
|
||||||
|
end.
|
||||||
|
|
||||||
decode_open_request(<<HostLen:16, Host:HostLen/binary, Port:16,
|
decode_open_request(<<HostLen:16, Host:HostLen/binary, Port:16,
|
||||||
UsernameLen:16, Username:UsernameLen/binary,
|
UsernameLen:16, Username:UsernameLen/binary,
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user