tests: add encoder and decoder for unencrypted mtproto messages

req_pq and resPQ messages
parent d9d812e9
...@@ -52,16 +52,16 @@ try_decode_packet_len(Len, LenStripped, St) -> ...@@ -52,16 +52,16 @@ try_decode_packet_len(Len, LenStripped, St) ->
{incomplete, St} {incomplete, St}
end. end.
-spec encode_packet(binary(), codec()) -> {iodata(), codec()}. -spec encode_packet(iodata(), codec()) -> {iodata(), codec()}.
encode_packet(Bin, St) -> encode_packet(Data, St) ->
Size = byte_size(Bin), Size = iolist_size(Data),
Len = Size div 4, Len = Size div 4,
Packet = Packet =
case Len < 127 of case Len < 127 of
true -> true ->
[Len | Bin]; [Len | Data];
false -> false ->
[<<127, Len:24/unsigned-little-integer>> | Bin] [<<127, Len:24/unsigned-little-integer>> | Data]
end, end,
{Packet, St}. {Packet, St}.
......
...@@ -6,7 +6,17 @@ ...@@ -6,7 +6,17 @@
send/2, send/2,
recv_packet/2, recv_packet/2,
recv_all/2, recv_all/2,
close/1]). close/1,
ping_session/6]).
-export([unencrypted_cli_packet/1,
unencrypted_cli_packet/3,
parse_unencrypted_srv_packet/1]).
-export([req_pq/0,
res_pq_matches/2,
ping/0,
pong_matches/2]).
-export_type([client/0]). -export_type([client/0]).
-record(client, -record(client,
...@@ -128,3 +138,96 @@ tcp_recv_all_inner(Sock, Acc) -> ...@@ -128,3 +138,96 @@ tcp_recv_all_inner(Sock, Acc) ->
close(#client{sock = Sock}) -> close(#client{sock = Sock}) ->
ok = gen_tcp:close(Sock). ok = gen_tcp:close(Sock).
ping_session(Host, Port, Secret, DcId, Protocol, Timeout) ->
Cli0 = connect(Host, Port, Secret, DcId, Protocol),
ReqPQ = req_pq(),
Cli1 = send(unencrypted_cli_packet(ReqPQ), Cli0),
{ok, Packet, Cli2} = recv_packet(Cli1, Timeout),
ok = close(Cli2),
{_MsgId, response, ResPQ} = parse_unencrypted_srv_packet(Packet),
{res_pq_matches(ReqPQ, ResPQ),
ReqPQ, ResPQ}.
%%
%% Messages
%%
%% @doc encodes payload as unencrypted client message
%% https://core.telegram.org/mtproto/description#unencrypted-message
unencrypted_cli_packet(Payload) ->
Now = erlang:system_time(microsecond),
%% Is 128 enough?
PadSize = rand:uniform(128 div 4) * 4, % should be alined to 4b
Padding = crypto:strong_rand_bytes(PadSize),
unencrypted_cli_packet(Payload, Now, Padding).
unencrypted_cli_packet(Payload, Now, Pad) ->
%% Client message identifiers are divisible by 4.
Micro = 1000000,
NowSec = Now div Micro,
MicroFraction = Now rem Micro,
MicroDiv4 = MicroFraction - (MicroFraction rem 4),
%% MsgId = NowSec * (2 bsl 31) + MicroDiv4,
[<<0:64,
MicroDiv4:32/unsigned-little,
NowSec:32/unsigned-little,
(byte_size(Payload) + byte_size(Pad)):32/unsigned-little>>,
Payload | Pad].
%% @doc extracts payload from unencrypted server message
parse_unencrypted_srv_packet(<<0:64, MsgId:64/unsigned-little,
Size:32/unsigned-little,
Payload:Size/binary>>) ->
%% Server message identifiers modulo 4 yield 1 if the message is a response to a
%% client message, and 3 otherwise.
Kind =
case MsgId rem 4 of
1 -> response;
3 -> event
end,
{MsgId, Kind, Payload}.
%% https://core.telegram.org/mtproto/serialize#base-types
-define(int, 32/signed-little).
-define(long, 64/signed-little).
-define(REQ_PQ, 16#60469778:?int).
-define(RES_PQ, 16#05162463:?int).
%% @doc creates req_pq packet
req_pq() ->
%% req_pq#60469778 nonce:int128 = ResPQ;
Nonce = <<(crypto:strong_rand_bytes(12)):12/binary,
(erlang:unique_integer()):32/little>>,
<<?REQ_PQ, Nonce:16/binary>>.
%% @doc returns `true' if ResPQ nonce matches the nonce for ReqPQ
%% @param ReqPQ: req_pq packet generated by req_pq/0
%% @param ResPQ: resPQ packet received from server
res_pq_matches(<<?REQ_PQ, Nonce:16/binary>>, <<?RES_PQ, Nonce:16/binary, _/binary>>) ->
%% resPQ#05162463 nonce:int128 server_nonce:int128 pq:bytes \
%% server_public_key_fingerprints:Vector<long> = ResPQ;
true;
res_pq_matches(_, _) ->
false.
-define(PING, 16#7abe77ec:?int).
-define(PONG, 16#347773c5:?int).
%% @doc constructs 'ping' message
ping() ->
%% ping#7abe77ec ping_id:long = Pong;
PingId = erlang:unique_integer(),
<<?PING, PingId:?long>>.
pong_matches(<<?PING, PingId:?long>>, <<?PONG, _MsgId1:?long, PingId:?long>>) ->
%% pong#347773c5 msg_id:long ping_id:long = Pong;
true;
pong_matches(_, _) ->
false.
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment