mirror of
https://github.com/seriyps/mtproto_proxy.git
synced 2026-08-27 04:15:50 +00:00
Add property-based tests for codecs
This commit is contained in:
parent
2f015e2f3f
commit
229ca2b6b8
9 changed files with 441 additions and 41 deletions
56
test/mtp_prop_gen.erl
Normal file
56
test/mtp_prop_gen.erl
Normal file
|
|
@ -0,0 +1,56 @@
|
|||
%% Common data generators for property-based tests
|
||||
|
||||
-module(mtp_prop_gen).
|
||||
-include_lib("proper/include/proper.hrl").
|
||||
|
||||
-export([stream_4b/0,
|
||||
packet_4b/0,
|
||||
stream_16b/0,
|
||||
packet_16b/0,
|
||||
key/0,
|
||||
iv/0,
|
||||
secret/0,
|
||||
dc_id/0,
|
||||
codec/0
|
||||
]).
|
||||
|
||||
|
||||
%% 4-byte aligned packet: `binary()`
|
||||
packet_4b() ->
|
||||
?LET(IoList, proper_types:non_empty(proper_types:list(proper_types:binary(4))),
|
||||
iolist_to_binary(IoList)).
|
||||
|
||||
%% List of 4-byte aligned packets: `[binary()]`
|
||||
stream_4b() ->
|
||||
proper_types:list(packet_4b()).
|
||||
|
||||
%% 16-byte aligned packet: `binary()`
|
||||
packet_16b() ->
|
||||
?LET(IoList, proper_types:non_empty(proper_types:list(proper_types:binary(16))),
|
||||
iolist_to_binary(IoList)).
|
||||
|
||||
%% List of 16-byte aligned packets: `[binary()]`
|
||||
stream_16b() ->
|
||||
proper_types:list(packet_16b()).
|
||||
|
||||
%% 32-byte encryption key: `binary()`
|
||||
key() ->
|
||||
proper_types:binary(32).
|
||||
|
||||
%% 16-byte encryption initialization vector: `binary()`
|
||||
iv() ->
|
||||
proper_types:binary(16).
|
||||
|
||||
%% 16-byte secret: `binary()`
|
||||
secret() ->
|
||||
proper_types:binary(16).
|
||||
|
||||
%% Datacenter ID: `[-9..9]`
|
||||
dc_id() ->
|
||||
proper_types:integer(-9, 9).
|
||||
|
||||
codec() ->
|
||||
Protocols = [mtp_abridged, mtp_intermediate, mtp_secure],
|
||||
proper_types:oneof(
|
||||
[proper_types:exactly(P)
|
||||
|| P <- Protocols]).
|
||||
43
test/prop_mtp_abridged.erl
Normal file
43
test/prop_mtp_abridged.erl
Normal file
|
|
@ -0,0 +1,43 @@
|
|||
-module(prop_mtp_abridged).
|
||||
-include_lib("proper/include/proper.hrl").
|
||||
|
||||
-export([prop_codec/1, prop_stream/1]).
|
||||
|
||||
prop_codec(doc) ->
|
||||
"Tests that any 4-byte aligned binary can be encoded and decoded back".
|
||||
|
||||
prop_codec() ->
|
||||
?FORALL(Bin, mtp_prop_gen:packet_4b(), codec(Bin)).
|
||||
|
||||
codec(Bin) ->
|
||||
Codec = mtp_abridged:new(),
|
||||
{Data, Codec1} = mtp_abridged:encode_packet(Bin, Codec),
|
||||
{ok, Decoded, _} = mtp_abridged:try_decode_packet(iolist_to_binary(Data), Codec1),
|
||||
Decoded == Bin.
|
||||
|
||||
|
||||
prop_stream(doc) ->
|
||||
"Tests that any number of packets can be encoded, concatenated and decoded".
|
||||
|
||||
prop_stream() ->
|
||||
?FORALL(Stream, mtp_prop_gen:stream_4b(), stream_codec(Stream)).
|
||||
|
||||
stream_codec(Stream) ->
|
||||
Codec = mtp_abridged:new(),
|
||||
{BinStream, Codec1} =
|
||||
lists:foldl(
|
||||
fun(Bin, {Acc, Codec1}) ->
|
||||
{Data, Codec2} = mtp_abridged:encode_packet(Bin, Codec1),
|
||||
{<<Acc/binary, (iolist_to_binary(Data))/binary>>,
|
||||
Codec2}
|
||||
end, {<<>>, Codec}, Stream),
|
||||
DecodedStream = decode_stream(BinStream, Codec1, []),
|
||||
Stream == DecodedStream.
|
||||
|
||||
decode_stream(BinStream, Codec, Acc) ->
|
||||
case mtp_abridged:try_decode_packet(BinStream, Codec) of
|
||||
{incomplete, _} ->
|
||||
lists:reverse(Acc);
|
||||
{ok, DecPacket, Codec1} ->
|
||||
decode_stream(<<>>, Codec1, [DecPacket | Acc])
|
||||
end.
|
||||
32
test/prop_mtp_aes_cbc.erl
Normal file
32
test/prop_mtp_aes_cbc.erl
Normal file
|
|
@ -0,0 +1,32 @@
|
|||
-module(prop_mtp_aes_cbc).
|
||||
-include_lib("proper/include/proper.hrl").
|
||||
|
||||
-export([prop_stream/1]).
|
||||
|
||||
prop_stream(doc) ->
|
||||
"Tests that any number of packets can be encoded, concatenated and decoded"
|
||||
" as a stream using the same key for encoding and decoding".
|
||||
|
||||
prop_stream() ->
|
||||
?FORALL({Key, Iv, Stream}, arg_set(), stream_codec(Key, Iv, Stream)).
|
||||
|
||||
|
||||
arg_set() ->
|
||||
proper_types:tuple(
|
||||
[mtp_prop_gen:key(),
|
||||
mtp_prop_gen:iv(),
|
||||
mtp_prop_gen:stream_16b()
|
||||
]).
|
||||
|
||||
stream_codec(Key, Iv, Stream) ->
|
||||
Codec = mtp_aes_cbc:new(Key, Iv, Key, Iv, 16),
|
||||
{BinStream, Codec2} =
|
||||
lists:foldl(
|
||||
fun(Bin, {Acc, Codec1}) ->
|
||||
{Data, Codec2} = mtp_aes_cbc:encrypt(Bin, Codec1),
|
||||
{<<Acc/binary, (iolist_to_binary(Data))/binary>>,
|
||||
Codec2}
|
||||
end, {<<>>, Codec}, Stream),
|
||||
{Decrypted, _Codec3} = mtp_aes_cbc:decrypt(BinStream, Codec2),
|
||||
%% io:format("Dec: ~p~nOrig: ~p~nCodec: ~p~n", [Decrypted, Stream, _Codec3]),
|
||||
Decrypted == iolist_to_binary(Stream).
|
||||
44
test/prop_mtp_full.erl
Normal file
44
test/prop_mtp_full.erl
Normal file
|
|
@ -0,0 +1,44 @@
|
|||
-module(prop_mtp_full).
|
||||
-include_lib("proper/include/proper.hrl").
|
||||
|
||||
-export([prop_codec/1, prop_stream/1]).
|
||||
|
||||
|
||||
prop_codec(doc) ->
|
||||
"Tests that any 4-byte aligned binary can be encoded and decoded back".
|
||||
|
||||
prop_codec() ->
|
||||
?FORALL(Bin, mtp_prop_gen:packet_4b(), codec(Bin)).
|
||||
|
||||
codec(Bin) ->
|
||||
Codec = mtp_full:new(),
|
||||
{Data, Codec1} = mtp_full:encode_packet(Bin, Codec),
|
||||
{ok, Decoded, _} = mtp_full:try_decode_packet(iolist_to_binary(Data), Codec1),
|
||||
Decoded == Bin.
|
||||
|
||||
|
||||
prop_stream(doc) ->
|
||||
"Tests that any number of packets can be encoded, concatenated and decoded".
|
||||
|
||||
prop_stream() ->
|
||||
?FORALL(Stream, mtp_prop_gen:stream_4b(), stream_codec(Stream)).
|
||||
|
||||
stream_codec(Stream) ->
|
||||
Codec = mtp_full:new(),
|
||||
{BinStream, Codec1} =
|
||||
lists:foldl(
|
||||
fun(Bin, {Acc, Codec1}) ->
|
||||
{Data, Codec2} = mtp_full:encode_packet(Bin, Codec1),
|
||||
{<<Acc/binary, (iolist_to_binary(Data))/binary>>,
|
||||
Codec2}
|
||||
end, {<<>>, Codec}, Stream),
|
||||
DecodedStream = decode_stream(BinStream, Codec1, []),
|
||||
Stream == DecodedStream.
|
||||
|
||||
decode_stream(BinStream, Codec, Acc) ->
|
||||
case mtp_full:try_decode_packet(BinStream, Codec) of
|
||||
{incomplete, _} ->
|
||||
lists:reverse(Acc);
|
||||
{ok, DecPacket, Codec1} ->
|
||||
decode_stream(<<>>, Codec1, [DecPacket | Acc])
|
||||
end.
|
||||
52
test/prop_mtp_intermediate.erl
Normal file
52
test/prop_mtp_intermediate.erl
Normal file
|
|
@ -0,0 +1,52 @@
|
|||
-module(prop_mtp_intermediate).
|
||||
-include_lib("proper/include/proper.hrl").
|
||||
|
||||
-export([prop_codec/1, prop_stream/1, prop_stream_padding/1]).
|
||||
|
||||
|
||||
prop_codec(doc) ->
|
||||
"Tests that any 4-byte aligned binary can be encoded and decoded back".
|
||||
|
||||
prop_codec() ->
|
||||
?FORALL(Bin, mtp_prop_gen:packet_4b(), codec(Bin)).
|
||||
|
||||
codec(Bin) ->
|
||||
Codec = mtp_intermediate:new(),
|
||||
{Data, Codec1} = mtp_intermediate:encode_packet(Bin, Codec),
|
||||
{ok, Decoded, _} = mtp_intermediate:try_decode_packet(iolist_to_binary(Data), Codec1),
|
||||
Decoded == Bin.
|
||||
|
||||
|
||||
prop_stream(doc) ->
|
||||
"Tests that any number of packets can be encoded, concatenated and decoded".
|
||||
|
||||
prop_stream() ->
|
||||
?FORALL(Stream, mtp_prop_gen:stream_4b(), stream_codec(Stream, false)).
|
||||
|
||||
stream_codec(Stream, Padding) ->
|
||||
Codec = mtp_intermediate:new(#{padding => Padding}),
|
||||
{BinStream, Codec1} =
|
||||
lists:foldl(
|
||||
fun(Bin, {Acc, Codec1}) ->
|
||||
{Data, Codec2} = mtp_intermediate:encode_packet(Bin, Codec1),
|
||||
{<<Acc/binary, (iolist_to_binary(Data))/binary>>,
|
||||
Codec2}
|
||||
end, {<<>>, Codec}, Stream),
|
||||
DecodedStream = decode_stream(BinStream, Codec1, []),
|
||||
Stream == DecodedStream.
|
||||
|
||||
decode_stream(BinStream, Codec, Acc) ->
|
||||
case mtp_intermediate:try_decode_packet(BinStream, Codec) of
|
||||
{incomplete, _} ->
|
||||
lists:reverse(Acc);
|
||||
{ok, DecPacket, Codec1} ->
|
||||
decode_stream(<<>>, Codec1, [DecPacket | Acc])
|
||||
end.
|
||||
|
||||
|
||||
prop_stream_padding(doc) ->
|
||||
"Tests that any number of packets can be encoded, concatenated and decoded"
|
||||
" using encoder with random padding enabled".
|
||||
|
||||
prop_stream_padding() ->
|
||||
?FORALL(Stream, mtp_prop_gen:stream_4b(), stream_codec(Stream, true)).
|
||||
104
test/prop_mtp_obfuscated.erl
Normal file
104
test/prop_mtp_obfuscated.erl
Normal file
|
|
@ -0,0 +1,104 @@
|
|||
-module(prop_mtp_obfuscated).
|
||||
-include_lib("proper/include/proper.hrl").
|
||||
|
||||
-export([prop_stream/1,
|
||||
prop_client_server_handshake/1,
|
||||
prop_client_server_stream/1]).
|
||||
|
||||
prop_stream(doc) ->
|
||||
"Tests that any number of packets can be encrypted with mtp_obfuscatedcoded,"
|
||||
" concatenated and decoded as a stream using the same key for encoding and decoding".
|
||||
|
||||
prop_stream() ->
|
||||
?FORALL({Key, Iv, Stream}, stream_arg_set(), stream_codec(Key, Iv, Stream)).
|
||||
|
||||
|
||||
stream_arg_set() ->
|
||||
proper_types:tuple(
|
||||
[mtp_prop_gen:key(),
|
||||
mtp_prop_gen:iv(),
|
||||
mtp_prop_gen:stream_4b()
|
||||
]).
|
||||
|
||||
stream_codec(Key, Iv, Stream) ->
|
||||
Codec = mtp_obfuscated:new(Key, Iv, Key, Iv),
|
||||
{BinStream, Codec2} =
|
||||
lists:foldl(
|
||||
fun(Bin, {Acc, Codec1}) ->
|
||||
{Data, Codec2} = mtp_obfuscated:encrypt(Bin, Codec1),
|
||||
{<<Acc/binary, (iolist_to_binary(Data))/binary>>,
|
||||
Codec2}
|
||||
end, {<<>>, Codec}, Stream),
|
||||
{Decrypted, _Codec3} = mtp_obfuscated:decrypt(BinStream, Codec2),
|
||||
%% io:format("Dec: ~p~nOrig: ~p~nCodec: ~p~n", [Decrypted, Stream, _Codec3]),
|
||||
Decrypted == iolist_to_binary(Stream).
|
||||
|
||||
|
||||
prop_client_server_handshake(doc) ->
|
||||
"Tests that for any secret, protocol and dc_id, it's possible to perform handshake".
|
||||
|
||||
prop_client_server_handshake() ->
|
||||
?FORALL({Secret, DcId, Protocol}, cs_hs_arg_set(),
|
||||
cs_hs_exchange(Secret, DcId, Protocol)).
|
||||
|
||||
cs_hs_arg_set() ->
|
||||
proper_types:tuple(
|
||||
[mtp_prop_gen:secret(),
|
||||
mtp_prop_gen:dc_id(),
|
||||
mtp_prop_gen:codec()]).
|
||||
|
||||
cs_hs_exchange(Secret, DcId, Protocol) ->
|
||||
%% io:format("Secret: ~p; DcId: ~p, Protocol: ~p~n",
|
||||
%% [Secret, DcId, Protocol]),
|
||||
{Packet, _, _, _CliCodec} = mtp_obfuscated:client_create(Secret, Protocol, DcId),
|
||||
case mtp_obfuscated:from_header(Packet, Secret, [Protocol]) of
|
||||
{ok, DcId, Protocol, _SrvCodec} ->
|
||||
true;
|
||||
_ ->
|
||||
false
|
||||
end.
|
||||
|
||||
prop_client_server_stream(doc) ->
|
||||
"Tests that for any secret, protocol and dc_id, it's possible to perform"
|
||||
" handshake/key exchange and then do bi-directional encode/decode stream of data".
|
||||
|
||||
prop_client_server_stream() ->
|
||||
?FORALL({Secret, DcId, Protocol, Stream}, cs_stream_arg_set(),
|
||||
cs_stream_exchange(Secret, DcId, Protocol, Stream)).
|
||||
|
||||
cs_stream_arg_set() ->
|
||||
proper_types:tuple(
|
||||
[mtp_prop_gen:secret(),
|
||||
mtp_prop_gen:dc_id(),
|
||||
mtp_prop_gen:codec(),
|
||||
mtp_prop_gen:stream_4b()]).
|
||||
|
||||
cs_stream_exchange(Secret, DcId, Protocol, Stream) ->
|
||||
%% io:format("Secret: ~p; DcId: ~p, Protocol: ~p~n",
|
||||
%% [Secret, DcId, Protocol]),
|
||||
{Header, _, _, CliCodec} = mtp_obfuscated:client_create(Secret, Protocol, DcId),
|
||||
{ok, DcId, Protocol, SrvCodec} = mtp_obfuscated:from_header(Header, Secret, [Protocol]),
|
||||
|
||||
%% Client to server
|
||||
{CliCodec1,
|
||||
SrvCodec1,
|
||||
Cli2SrvTransmitted} = transmit_stream(CliCodec, SrvCodec, Stream),
|
||||
{_CliCodec2,
|
||||
_SrvCodec2,
|
||||
Srv2CliTransmitted} = transmit_stream(SrvCodec1, CliCodec1, Stream),
|
||||
BinStream = iolist_to_binary(Stream),
|
||||
(Cli2SrvTransmitted == BinStream)
|
||||
andalso (Srv2CliTransmitted == BinStream).
|
||||
|
||||
transmit_stream(EncCodec, DecCodec, Stream) ->
|
||||
{EncStream, EncCodec3} =
|
||||
lists:foldl(
|
||||
fun(Packet, {Acc, CliCodec1}) ->
|
||||
{Data, CliCodec2} = mtp_obfuscated:encrypt(Packet, CliCodec1),
|
||||
{<<Acc/binary, (iolist_to_binary(Data))/binary>>,
|
||||
CliCodec2}
|
||||
end, {<<>>, EncCodec}, Stream),
|
||||
{Decrypted, DecCodec2} = mtp_obfuscated:decrypt(EncStream, DecCodec),
|
||||
{EncCodec3,
|
||||
DecCodec2,
|
||||
Decrypted}.
|
||||
Loading…
Add table
Add a link
Reference in a new issue