Recently, posts have started appearing on HabrΓ© about how good Telegram is, how brilliant and experienced the Durov brothers are in building network systems, etc. At the same time, very few people really plunged into the technical device - at maximum, they use a fairly simple (and very different from MTProto) Bot API based on JSON, and usually they just take all the praises and public relations that revolve around the messenger. Almost a year and a half ago, my colleague at the Echelon NGO Vasily (unfortunately, his account on Habr erased along with a draft) began writing his own Telegram client from scratch on Perl, and the author of these lines later joined. Why at Perl, some will ask immediately? Because in other languages ββsuch projects already exist In fact, this is not the point, there could be any other language where there is no ready-made library , and accordingly, the author must go all the way from scratch . Moreover, cryptography is such a thing - trust, but verify. With a product aimed at security, you canβt just take and rely on the finished library from the manufacturer, blindly believing it (however, this is a topic more for the second part). At the moment, the library works quite well at the "middle" level (it allows you to make any API requests).
However, in this series of posts there will be not much cryptography and mathematics. But there will be many other technical details and architectural crutches (useful to those who will not write from scratch, but will use the library in any language). So, the main goal was to try to implement the client from scratch according to official documentation . That is, suppose that the source code of official clients is closed (again, in the second part, we will expand on the topic of how this really happens ), but, as in the old days, for example, there is a standard like RFC - is it possible to write a client using just one specification, "not peeping" at the source, at least official (Telegram Desktop, mobile), at least unofficial Telethon?
Table of contents:
- Documentation ... does it exist? True?..
- Where to start a young author?
- Binary serialization: TL (Type Language) and its scheme, and layers, and many other scary words
- MTProto
- API?
- To be continued!
Documentation ... does it exist? True?..
Fragments of notes for this article began to be collected last summer. All this time on the official site https://core.telegram.org the documentation was as of Layer 23, i.e. stuck somewhere in 2014 (remember, then there werenβt even any channels yet?). Of course, in theory, this should have allowed us to implement a client with functionality at that time in 2014. But even in this state, the documentation was, firstly, incomplete, and secondly, sometimes contradicted itself. A little more than a month ago, in September 2019, it was accidentally discovered that the site had a large update of the documentation, on the completely fresh Layer 105, with the note that now everything needs to be read again. Indeed, many articles were revised, but many remained unchanged. Therefore, reading the criticisms below regarding documentation, it should be borne in mind that some of these things are no longer relevant, but some are still quite. In the end, 5 years in the modern world is not just a lot, but a lot. Since then (especially if you do not take into account the ejected and re-resumed since then geochats), the number of API methods in the scheme has grown from a hundred to more than two hundred and fifty!
Where to start a young author?
, Telethon Python Madeline PHP, β api_id
api_hash
( API ), . , , , . , , β , , .
, , , Telegram , , .. , "" , , .. . , , .
, . https://core.telegram.org/ Getting Started , MTProto β OSI , .
, MTProto, , ( , layer violation) , ...
: TL (Type Language) , ,
, , Telegram β . , , .
, . , , JSON Schema, . : . , , . MTProto, , - , :
int ? = Int; long ? = Long; double ? = Double; string ? = String; vector#1cb5c415 {t:Type} # [ t ] = Vector t; rpc_error#2144ca19 error_code:int error_message:string = RpcError; rpc_answer_unknown#5e2ad36e = RpcDropAnswer; rpc_answer_dropped_running#cd78e586 = RpcDropAnswer; rpc_answer_dropped#a43ad8b7 msg_id:long seq_no:int bytes:int = RpcDropAnswer; msg_container#73f1f8dc messages:vector<%Message> = MessageContainer; ---functions--- set_client_DH_params#f5045f1f nonce:int128 server_nonce:int128 encrypted_data:bytes = Set_client_DH_params_answer; ping#7abe77ec ping_id:long = Pong; ping_delay_disconnect#f3427b8c ping_id:long disconnect_delay:int = Pong; invokeAfterMsg#cb9f372d msg_id:long query:!X = X; invokeAfterMsgs#3dc4b4f0 msg_ids:Vector<long> query:!X = X; account.updateProfile#78515775 flags:# first_name:flags.0?string last_name:flags.1?string about:flags.2?string = User; account.sendChangePhoneCode#8e57deb flags:# allow_flashcall:flags.0?true phone_number:string current_number:flags.0?Bool = auth.SentCode;
, , β , ( , ?), , β¦ . ++ ( , ). , , , , ( ), - , - , β , ( ) ? (, JSON- β ).
Binary Data Serialization 4 . , , , , , , , β¦ ! TL Language, , , , !
, , , , , , , - . :
- , , ,
- IT- β
- , , , TL,
LeoNerd #perl
IRC- FreeNode, Telegram Matrix ( ):
, - , , , .
, bare- (int, long ..) - β β . , , , .
TL , β¦
constructor = Type; myVec ids:Vector<long> = Type; fixed#abcdef34 id:int = Type2; fixedVec set:Vector<Type2> = FixedVec; constructorOne#crc32 field1:int = PolymorType; constructorTwo#2crc32 field_a:long field_b:Type3 field_c:int = PolymorType; constructorThree#deadcrc bit_flags_of_what_really_present:# optional_field4:bit_flags_of_what_really_present.1?Type = PolymorType; an_id#12abcd34 id:int = Type3; a_null#6789cdef = Type3;
, ( β ) #
CRC32 . , β . , β , , β . , , β .
---functions---
, , : RPC-, β ( , , ), " " β . , , β ---types---
, " ". , .. , , C++, TL - .
"" "", ? , , - β , β -, final
. , , - . β , ( , , ) β , .
? , 4 , 0xcrc32
β , field1
int
, .. 4 , PolymorType
. 0x2crc32
, , long
, 8 . , . , Type3
, , 0x12abcd34
, 4 int
, 0x6789cdef
, . β . 4 int
field_c
constructorTwo
PolymorType
.
, 0xdeadcrc
constructorThree
, . bit_flags_of_what_really_present
#
β , nat
, " ". , , unsigned int β , , , . , , , β on the wire, , ( ). , , , , Type
, 2 . ( ), ids
ids:Vector<long>
.
, generic' Java. . , . , Vector. 4 CRC32 Vector, , 4 β , .
, 4 , β bytes
string
4 β , ? TL , , , 4 , JSON ? , , , , ?..
, , , . -, CRC32 ( whitespace ..) β , , CRC32 , , . , , , ?..
-, CRC32, - , (). β , 232, . , CRC32 ( ) , ? , : CRC32 , 4 4 β . ( ), , .
, , CRC32? ( ) -, 239, -!
, , , Vector<int>
Vector<PolymorType>
CRC32. ? , ? , , Vector<int>
, 40000 . Vector<Type2>
, int
β 10000 0xabcdef34 4 int
, fixedVec
80000 40000 ?
β , , id, , β . Telegram .
...
Vector,
, , ( ) tuples . , , , . ? , , β lazy evaluation . : , .. β , β ( (cons)
Lisp). , , 4 (CRC32 TL) . , β .
, TL , . :
Serialization always uses the same constructor βvectorβ (const 0x1cb5c415 = crc32("vector t:Type # [ t ] = Vector tβ) that is not dependent on the specific value of the variable of type t.
The value of the optional parameter t is not involved in the serialization since it is derived from the result type (always known prior to deserialization).
: vector {t:Type} # [ t ] = Vector t
β , ! . , . , :
The Vector t polymorphic pseudotype is a βtypeβ whose value is a sequence of values of any type t, either boxed or bare.
β¦ . , ( ), , : , ( - ACM), . β β .
, . ,#
nat
, :
There are type expressions (type-expr) and numeric expressions (nat-expr). However, they are defined the same way.
type-expr ::= expr nat-expr ::= expr
, .. .
, (vector<int>
, vector<User>
) (#1cb5c415
), .. ,
users.getUsers#d91a548 id:Vector<InputUser> = Vector<User>;
, . , β , bare-, , - β ? - PHP, ?
β TL? , , protobuf, ? , .
TL
TL (), Telegram. . , Telegram. , ( ( ) ).
Templates are not used now. Instead, the same universal constructors (for example, vector {t:Type} [t] = Vector t) are used w
, , , .
#define ZHUKOV_BYTES_HACK #ifdef ZHUKOV_BYTES_HACK /* dirty hack for Zhukov request */
, :
static const char *reserved_words_polymorhic[] = { "alpha", "beta", "gamma", "delta", "epsilon", "zeta", "eta", "theta", NULL };
β , :
intHash {alpha:Type} vector<coupleInt<alpha>> = IntHash<alpha>;
, int β Type. C++ :
template <T> class IntHash { vector<pair<int,T>> _map; }
, alpha
β ! C++ T, alpha, betaβ¦ 8 , . , - :
-- TL -- ... , ,... ... , ! -- ? -- , ! -- , ? -- , , --
TL "". Telegram-.
:
Vasily, [09.10.18 17:07]
, , ,
, .jpg
.webp
, , , , - DSL, ?..
telegram-cli , TLO (cli) , β TL , TL?..
16.12 04:18 Vasily: -, - lex+yacc
16.12 04:18 Vasily:
16.12 04:18 Vasily:
16.12 04:19 Vasily: 3+<censored>
nametype = re.match(r'([a-zA-Z\.0-9_]+)(#[0-9a-f]+)?([^=]*)=\s*([a-zA-Z\.<>0-9_]+);', line); if (not nametype): if (not re.match(r'vector#1cb5c415 \{t:Type\} # \[ t \] = Vector t;', line)): print('Bad line found: ' + line);
1100+ , + , , , TL, , β¦ , , ?!
β¦ , CRC32? , Telegram Desktop , CRC32 !
Vasily, [18.12 22:49]
, TL
, ,
tdesktop, ,
, .
, telegram-cli β , Telegram Desktop β , ? ?.. Android- ( , ), , .
? , , , :
Vasily:flags.0? true
, true,
Vasily:flags.1? int
, ,
Vasily: , , !
Vasily: - , true β , -
Vasily: ,
, , Telethon? MTProto, β , %
" bare-", .. , :
Vasily, [22.06.18 18:38]
:
msg_container#73f1f8dc messages:vector message = MessageContainer;
:
msg_container#73f1f8dc messages:vector<%Message> = MessageContainer;
, -
bare
telethon
msg_container
, %. .
Vadim Goncharov, [22.06.18 19:22]
tdesktop?
Vasily, [22.06.18 19:23]
TL
// parsed manually
TL ,
%
,
,
TL,
" ", , "- , , ".
: " ,
args: /* empty */ { $$ = NULL; } | args arg { $$ = g_list_append( $1, $2 ); } ; arg: LC_ID ':' type-term { $$ = tl_arg_new( $1, $3 ); } | LC_ID ':' condition '?' type-term { $$ = tl_arg_new_cond( $1, $5, $3 ); free($3); } | UC_ID ':' type-term { $$ = tl_arg_new( $1, $3 ); } | type-term { $$ = tl_arg_new( "", $1 ); } | '[' LC_ID ']' { $$ = tl_arg_new_mult( "", tl_type_new( $2, TYPE_MOD_NONE ) ); } ;
- ,
struct tree *parse_args4 (void) { PARSE_INIT (type_args4); struct parse so = save_parse (); PARSE_TRY (parse_optional_arg_def); if (S) { tree_add_child (T, S); } else { load_parse (so); } if (LEX_CHAR ('!')) { PARSE_ADD (type_exclam); EXPECT ("!"); } PARSE_TRY_PES (parse_type_term); PARSE_OK; }
# Regex to match the whole line match = re.match(r''' ^ # We want to match from the beginning to the end ([\w.]+) # The .tl object can contain alpha_name or namespace.alpha_name (?: \# # After the name, comes the ID of the object ([0-9a-f]+) # The constructor ID is in hexadecimal form )? # If no constructor ID was given, CRC32 the 'tl' to determine it (?:\s # After that, we want to match its arguments (name:type) {? # For handling the start of the '{X:Type}' case \w+ # The argument name will always be an alpha-only name : # Then comes the separator between name:type [\w\d<>#.?!]+ # The type is slightly more complex, since it's alphanumeric and it can # also have Vector<type>, flags:# and flags.0?default, plus :!X as type }? # For handling the end of the '{X:Type}' case )* # Match 0 or more arguments \s # Leave a space between the arguments and the equal = \s # Leave another space between the equal and the result ([\w\d<>#.?]+) # The result can again be as complex as any argument type ;$ # Finally, the line should always end with ; ''', tl, re.IGNORECASE | re.VERBOSE)
:
---functions--- return FUNCTIONS; ---types--- return TYPES; [a-z][a-zA-Z0-9_]* yylval.string = strdup(yytext); return LC_ID; [A-Z][a-zA-Z0-9_]* yylval.string = strdup(yytext); return UC_ID; [0-9]+ yylval.number = atoi(yytext); return NUM; #[0-9a-fA-F]{1,8} yylval.number = strtol(yytext+1, NULL, 16); return ID_HASH; \n /* skip new line */ [ \t]+ /* skip spaces */ \/\/.*$ /* skip comments */ \/\*.*\*\/ /* skip comments */ . return (int)yytext[0];
.. β ".
, TL 100 ~300 ( print
' ), . , β .
β , ? , ( ), TL. , . , , , . ?
, constraints. :
The fileβs binary content is then split into parts. All parts must have the same size ( part_size ) and the following conditions must be met:
part_size % 1024 = 0
(divisible by 1KB)524288 % part_size = 0
(512KB must be evenly divisible by part_size)
The last part does not have to satisfy these conditions, provided its size is less than part_size.
Each part should have a sequence number, file_part, with a value ranging from 0 to 2,999.
After the file has been partitioned you need to choose a method for saving it on the server. Use upload.saveBigFilePart in case the full size of the file is more than 10 MB and upload.saveFilePart for smaller files.
[...] one of the following data input errors may be returned:
- FILE_PARTS_INVALID β Invalid number of parts. The value is not between
1..3000
- ? - TL? . , Turbo Pascal , . , enum
β , () . β , , . , β¦ , , , ?
TL . , , JSON Schema. 512 - , , , 1..3000
( ) , ?..
, . , TL β , TL , . TL . , ( , RPC -, ) β ?.. β .
, ? , , description ( JSON- ), , β ? :
-channelFull#76af5481 flags:# can_view_participants:flags.3?true can_set_username:flags.6?true can_set_stickers:flags.7?true hidden_prehistory:flags.10?true id:int about:string participants_count:flags.0?int admins_count:flags.1?int kicked_count:flags.2?int banned_count:flags.2?int read_inbox_max_id:int read_outbox_max_id:int unread_count:int chat_photo:Photo notify_settings:PeerNotifySettings exported_invite:ExportedChatInvite bot_info:Vector<BotInfo> migrated_from_chat_id:flags.4?int migrated_from_max_id:flags.4?int pinned_msg_id:flags.5?int stickerset:flags.8?StickerSet available_min_id:flags.9?int = ChatFull;
+channelFull#1c87a71a flags:# can_view_participants:flags.3?true can_set_username:flags.6?true can_set_stickers:flags.7?true hidden_prehistory:flags.10?true can_view_stats:flags.12?true id:int about:string participants_count:flags.0?int admins_count:flags.1?int kicked_count:flags.2?int banned_count:flags.2?int online_count:flags.13?int read_inbox_max_id:int read_outbox_max_id:int unread_count:int chat_photo:Photo notify_settings:PeerNotifySettings exported_invite:ExportedChatInvite bot_info:Vector<BotInfo> migrated_from_chat_id:flags.4?int migrated_from_max_id:flags.4?int pinned_msg_id:flags.5?int stickerset:flags.8?StickerSet available_min_id:flags.9?int = ChatFull;
-message#44f9b43d flags:# out:flags.1?true mentioned:flags.4?true media_unread:flags.5?true silent:flags.13?true post:flags.14?true id:int from_id:flags.8?int to_id:Peer fwd_from:flags.2?MessageFwdHeader via_bot_id:flags.11?int reply_to_msg_id:flags.3?int date:int message:string media:flags.9?MessageMedia reply_markup:flags.6?ReplyMarkup entities:flags.7?Vector<MessageEntity> views:flags.10?int edit_date:flags.15?int post_author:flags.16?string grouped_id:flags.17?long = Message;
+message#44f9b43d flags:# out:flags.1?true mentioned:flags.4?true media_unread:flags.5?true silent:flags.13?true post:flags.14?true from_scheduled:flags.18?true id:int from_id:flags.8?int to_id:Peer fwd_from:flags.2?MessageFwdHeader via_bot_id:flags.11?int reply_to_msg_id:flags.3?int date:int message:string media:flags.9?MessageMedia reply_markup:flags.6?ReplyMarkup entities:flags.7?Vector<MessageEntity> views:flags.10?int edit_date:flags.15?int post_author:flags.16?string grouped_id:flags.17?long = Message;
, GitHub, , . " 10 ", , , - β¦ , , .
, . ? , ? . , , . :
storage.fileUnknown#aa963b05 = storage.FileType; storage.filePartial#40bc6f52 = storage.FileType; storage.fileJpeg#7efe0e = storage.FileType; storage.fileGif#cae1aadf = storage.FileType; storage.filePng#a4f63c0 = storage.FileType; storage.filePdf#ae1e508d = storage.FileType; storage.fileMp3#528a0677 = storage.FileType; storage.fileMov#4b09ebbc = storage.FileType; storage.fileMp4#b3cea0e4 = storage.FileType; storage.fileWebp#1081464c = storage.FileType;
, 5 , 32 . . TL .
, . , MTProto ( ) Gzip, β , . , RpcResult, . ?.. , .
, β InputPeerUser
InputUser
. . ! . ? , , telegram-cli:
if (tgl_get_peer_type (E->id) != TGL_PEER_CHANNEL || (C && (C->flags & TGLCHF_MEGAGROUP))) { out_int (CODE_messages_get_history); out_peer_id (TLS, E->id); } else { out_int (CODE_channels_get_important_history); out_int (CODE_input_channel); out_int (tgl_get_peer_id (E->id)); out_long (E->id.access_hash); } out_int (E->max_id); out_int (E->offset); out_int (E->limit); out_int (0); out_int (0);
, , ! , ?.. , , , ? ? .
. (layers)
, , . , , , , , , . , β "", . , . , β , . 2. TL:
If a client supports Layer 2, then the following constructor must be used:
invokeWithLayer2#289dd1f6 {X:Type} query:!X = X;
In practice, this means that before every API call, an int with the value0x289dd1f6
must be added before the method number.
. ?
invokeWithLayer3#b7475268 query:!X = X;
? ,
invokeWithLayer4#dea0d430 query:!X = X;
? , , , β , -? 4 β . So,
invokeWithLayer5#417a57ae query:!X = X;
, . :
Update: Starting with Layer 9, helper methodsinvokeWithLayerN
can be used only together withinitConnection
! 9 , , , Internet- 80- β !
?..
invokeWithLayer10#39620c41 query:!X = X; ... invokeWithLayer18#1c900537 query:!X = X;
. 9 , , , , , , . .
?..
Vasily, [16.07.18 14:01]
:
. InvokeWithLayer. , .
.. ,
Vadim Goncharov, [16.07.18 14:02]
InvokeWithLayer ?
Vasily, [16.07.18 14:02]
Vadim Goncharov, [16.07.18 14:02]
, ,
, .. Updates
β , API-, . , , , Updates .
, , :
- , ,
- ?
- , ?
, , , ( , )? ! !
. 14 , Telegram - β¦ :
2019-08-15 09:28:35.880640 MSK warn main: ANON:87: unknown object type: 0x80d182d1 at TL/Object.pm line 213. 2019-08-15 09:28:35.751899 MSK warn main: ANON:87: unknown object type: 0xb5223b0f at TL/Object.pm line 213.
(, ). TL - β , , . ?
, β . . CRC32 β 73 , 82. β !
, ? , Telegram Desktop 1.2.17 (, Linux), Exception: MTP Unexpected type id #b5223b0f read in MTPMessageMedia...
, - , ...
-? : 91, 73. , , , , .
: , , , , , β , . .
? , . , , "", , , , . "", .
β¦ ?!.. , , . Android TL-, ( ) (). :
public static class TL_message_layer68 extends TL_message { public static int constructor = 0xc09be45f; //... // //... public static class TL_message_layer47 extends TL_message { public static int constructor = 0xc992e15c; public static Message TLdeserialize(AbstractSerializedData stream, int constructor, boolean exception) { Message result = null; switch (constructor) { case 0x1d86f70e: result = new TL_messageService_old2(); break; case 0xa7ab1991: result = new TL_message_old3(); break; case 0xc3060325: result = new TL_message_old4(); break; case 0x555555fa: result = new TL_message_secret(); break; case 0x555555f9: result = new TL_message_secret_layer72(); break; case 0x90dddc11: result = new TL_message_layer72(); break; case 0xc09be45f: result = new TL_message_layer68(); break; case 0xc992e15c: result = new TL_message_layer47(); break; case 0x5ba66c13: result = new TL_message_old7(); break; case 0xc06b9607: result = new TL_messageService_layer48(); break; case 0x83e5de54: result = new TL_messageEmpty(); break; case 0x2bebfa86: result = new TL_message_old6(); break; case 0x44f9b43d: result = new TL_message_layer104(); break; case 0x1c9b1027: result = new TL_message_layer104_2(); break; case 0xa367e716: result = new TL_messageForwarded_old2(); //custom break; case 0x5f46804: result = new TL_messageForwarded_old(); //custom break; case 0x567699b3: result = new TL_message_old2(); //custom break; case 0x9f8d60bb: result = new TL_messageService_old(); //custom break; case 0x22eb6aba: result = new TL_message_old(); //custom break; case 0x555555F8: result = new TL_message_secret_old(); //custom break; case 0x9789dac4: result = new TL_message_layer104_3(); break;
boolean fixCaption = !TextUtils.isEmpty(message) && (media instanceof TLRPC.TL_messageMediaPhoto_old || media instanceof TLRPC.TL_messageMediaPhoto_layer68 || media instanceof TLRPC.TL_messageMediaPhoto_layer74 || media instanceof TLRPC.TL_messageMediaDocument_old || media instanceof TLRPC.TL_messageMediaDocument_layer68 || media instanceof TLRPC.TL_messageMediaDocument_layer74) && message.startsWith("-1");
β¦ . , , , ?.. ! , , , , _old7
- β¦ ,
TL_message_layer104 TL_message_layer104_2 TL_message_layer104_3
, , ?! , , "", , , , ?.. ? , ?..
Telegram Desktop, , β , , - . , , , ? , , , .
? , -, .
, :
public static class TL_folders_deleteFolder extends TLObject { public static int constructor = 0x1c295881; public int folder_id; public TLObject deserializeResponse(AbstractSerializedData stream, int constructor, boolean exception) { return Updates.TLdeserialize(stream, constructor, exception); } public void serializeToStream(AbstractSerializedData stream) { stream.writeInt32(constructor); stream.writeInt32(folder_id); } } //manually created //RichText start public static abstract class RichText extends TLObject { public String url; public long webpage_id; public String email; public ArrayList<RichText> texts = new ArrayList<>(); public RichText parentRichText; public static RichText TLdeserialize(AbstractSerializedData stream, int constructor, boolean exception) { RichText result = null; switch (constructor) { case 0x1ccb966a: result = new TL_textPhone(); break; case 0xc7fb5e01: result = new TL_textSuperscript(); break;
"manually created" , ( maintenance?), . , β , (- GPL Linux), .
. , .
MTProto
, . . , Telegram β -, , (, API sticker pack β , ).
, "" (message) "" (session) β , Telegram-. , , , "" β , , , , . β¦ .
β . 5 :
- TCP
- Websocket
- Websocket over HTTPS
- HTTP
- HTTPS
Vasily, [15.06.18 15:04]
UDP ,
TCP
UDP TCP, sequence number crc
?
, TCP 4 :
- Abridged
- Intermediate
- Padded intermediate
- Full
, Padded intermediate MTProxy, - . ( ), ? , payload MTProto, :
- Abridged 1 4 , 0xef,
- Intermediate 4 ,
0xeeeeeeee
, Intermediate - Full , : , sequence number, , MTProto, , CRC32. , TCP. , , . , , TCP 16- , . , - 16 , β β SHA . CRC32 β .
Abridged, , Intermediate, "In case 4-byte data alignment is needed", - . , , Telegram , ? , ( -...). , Abridged, padding' 16 β 3 ?
, , , .
, .. Web MTProxy, , , , . MTProxy , 2018, , , ! , ( ) MTProxy , ( ), Go, Node.js .
, . 5- OSI, β MTProto session.
, , , Diffie-Hellman
β¦ β , Active sessions. .
. , plaintext β . "" ? Telegram ( 4 , ):
session β UI "current sessions", / OS.
β MTProto session, sequence number ( ) , TCP-. MTProto-, .
sessions authorization. , , UI- , authorization, , . :
- auth_key bounds it to account, SMS β authorization
- MTProto session,
session_id
.- , authorization
session_id
instance β- , MTProto sessions auth_key β DC.
- , DC β DC auth_key !
- , , authorization (UI-), API
auth.exportAuthorization
DCauth.importAuthorization
DC.- , MTProto sessions (
session_id
) DC, auth_key.- , Perfect Forward Secrecy. auth_key permanent key β per DC β
auth.bindTempAuthKey
temporary auth_key β , temp_auth_key per DC, MTProto sessions DC.
, salt ( future salts) auth_key .. shared MTProto sessions DC.
" TCP-"? , - β () TCP- , . HTTP, MTProto , , β , TCP-.
, . β ? β .
, auth_key
- Telegram. ...
Vasily, [19.06.18 20:05]
data_with_hash := SHA1(data) + data + (any random bytes); such that the length equal 255 bytes;
encrypted_data := RSA (data_with_hash, server_public_key); a 255-byte long number (big endian) is raised to the requisite power over the requisite modulus, and the result is stored as a 256-byte number.
- DH
DH
β proof of work , . DoS-. RSA- , , new_nonce
. , ?
Vasily, [20.06.18 00:26]
appid
DH
, , 4 .
-404, ?
: "-, DH", 404
? ? - ( ).
, -
32 .
, BE
Vadim Goncharov, [20.06.18 15:49]
- 404?
Vasily, [20.06.18 15:49]
!
Vadim Goncharov, [20.06.18 15:50]
, " "
Vasily, [20.06.18 15:50]
%)
error reporting
Vasily, [20.06.18 20:18]
, MD5.
The key fingerprint is computed as follows:
digest = md5(key + iv) fingerprint = substr(digest, 0, 4) XOR substr(digest, 4, 4)
SHA1 sha2
, , auth_key
2048 - . ? , 1024 β¦ . . TLS-, . , ! , , . .. "-", - ICQ, "-", SSH ( - gitlab/github). . " DC"? " "? , β , .
, "" . , ? ? :
Vasily, [21.06.18 17:53]
2 %)
,
Vasily, [21.06.18 18:02]
, ,
278 static const char *goodPrime = "c71caeb9c6b1c9048e6c522f70f13f73980d40238e3e21c14934d037563d930f48198a0aa7c14058229493d22530f4dbfa336f6e0ac925139543aed44cce7c3720fd51f69458705ac68cd4fe6b6b13abdc9746512969328454f18faf8c595f642477fe96bb2a941d5bcd1d4ac8cc49880708fa9b378e3c4f3a9060bee67cf9a4a4a695811051907e162753b56b0f6b410dba74d8a84b2a14b3144e0ef1284754fd17ed950d5965b4b9dd46582db1178d169c6bc465b0d6ff9ca3928fef5b9ae4e418fc15e83ebea0f87fa9ff5eed70050ded2849f47bf959d956850ce929851f0d8115f635b105ee2e4e15d04b2454bf6f4fadf034b10403119cd8e3b92fcc5b"; 279 if (!strcasecmp(prime, goodPrime)) {
, - , .
, . , .. , , AES.
The message key is defined as the 128 middle bits of the SHA256 of the message body (including session, message ID, etc.), including the padding bytes, prepended by 32 bytes taken from the authorization key.
Vasily, [22.06.18 14:08]
, ,
auth_key
. . β¦ . Feel free to study the open source code.
Note that MTProto 2.0 requires from 12 to 1024 bytes of padding, still subject to the condition that the resulting message length be divisible by 16 bytes.
?
, 404
, , MAC . AES , IGE. , , FAQβ¦ , , SHA- , β - silently ignore ( , ?).
, , . , Telegram Desktop. ( D877F783D5D3EF8C) , MTProto ( 1.0), .. , ( - auth_key
256 , msg_key
). , . , β . , , ?.. MAC , . MTProto , . Telegram Desktop user_data
β AES CTR.
Vasily, [21.06.18 01:27]
, , IGE: IGE was the first attempt at an "authenticating encryption mode," originally for Kerberos. It was a failed attempt (it does not provide integrity protection), and had to be removed. That was the beginning of a 20 year quest for an authenticating encryption mode that works, which recently culminated in modes like OCB and GCM.
:
The team behind Telegram, led by Nikolai Durov, consists of six ACM champions, half of them Ph.Ds in math. It took them about two years to roll out the current version of MTProto.
.
tls
, , . , , TL ? ? , , initConnection, ?
Vasily, [25.06.18 18:46]
Initializes connection and save information on the user's device and application.
app_id, device_model, system_version, app_version lang_code.
query
. Feel free to study the open source
invokeWithLayer , - ? , β , β , :
Vasily, [25.06.18 19:13]
, , invokewithlayer
initConnection , ? , , , , . ! ! , β , ...
Only a small portion of the API methods are available to unauthorized users:
- auth.sendCode
- auth.resendCode
- account.getPassword
- auth.checkPassword
- auth.checkPhone
- auth.signUp
- auth.signIn
- auth.importAuthorization
- help.getConfig
- help.getNearestDc
- help.getAppUpdate
- help.getCdnConfig
- langpack.getLangPack
- langpack.getStrings
- langpack.getDifference
- langpack.getLanguages
- langpack.getLanguage
, auth.sendCode
, , api_id api_hash, SMS . DC ( , ), DC. , IP- DC , help.getConfig
. - 5 , 2018 .
, . , IP-? , , MTProto? : " , ?". , - RSA-, .. . , , (, MTProto, , ).
, . . , , . ...
Vasily, [10.07.18 14:45]
https://core.telegram.org/method/help.getConfig
config#7dae33e0 [...] = Config; help.getConfig#c4f9186b = Config;
https://core.telegram.org/api/datacenter
config#232d5905 [...] = Config; help.getConfig#c4f9186b = Config;
,
tdesktop
, , , . . ? , , ? , β (, ).
β¦ , - API, .. , - MTProto? :
Vasily, [28.06.18 02:04]
, e2e
Mtproto ,
, , mtproto
? PFS, (, Telegram Desktop ). API auth.bindTempAuthKey
, .. . β , , initConnection
.., . , DC, auth_key_id
, "" β , β¦ , future salts, ?..
MTProto .
, msg_id, msg_seqno, ,
? "" , , API. , msg_key , . ( , , padding, ):
- salt β int64
- session_id β int64
- message_id β int64
- seq_no β int32
, β DC. ? , get_future_salts
, , , , "", () β . , , , new_session_created
β - , . .
. , MTProto ? , session_id
seq_no
. , TCP-, . , , , . β TCP- , seq_no
. β , , .
seq_no
? , . , :
Content-related Message
A message requiring an explicit acknowledgment. These include all the user and many service messages, virtually all with the exception of containers and acknowledgments.
Message Sequence Number (msg_seqno)
A 32-bit number equal to twice the number of βcontent-relatedβ messages (those requiring acknowledgment, and in particular those that are not containers) created by the sender prior to this message and subsequently incremented by one if the current message is a content-related message. A container is always generated after its entire contents; therefore, its sequence number is greater than or equal to the sequence numbers of the messages contained in it.
1, 2?.. , " ACK, ", β , , , seq_no
! ? - , , , . . TCP , - , , TCP seq_no
, seq_no
β . MTProto seq_no
, TCP, msg_id
!
msg_id
, ? , . 64- , "- ", β Unix timestamp, , 32 . .. ( ). , - , . , β session_id
β : Under no circumstances can a message meant for one session be sent into a different session. , , β , , id . , .
, msg_id
...
RPC: , , . .
, , , " RPC-", . content-related ! , ! . msg_id
. β :
rpc_result#f35c6d01 req_msg_id:long result:Object = RpcResult;
, . , API, , β , , , , ? , , no workers, : TCP- β -, message_id
. , .
?.. ? RPC- msg_id
! " !"? , ? ,
msgs_ack#62d6b459 msg_ids:Vector long = MsgsAck;
. ! RpcResult, . , MsgsAck β , " ". RpcResult. .
, ! . . . , , . .
.
rpc_error#2144ca19 error_code:int error_message:string = RpcError;
, -, β ! . , , , . , β HTTP- ( , , ), ___. , PHONE_NUMBER_OCCUPIED FILE_PART__MISSING. , . , FLOOD_WAIT_3600
, , PHONE_MIGRATE_5
, 5- DC. , ? , , .
, , , , . . -, , / β RpcError
RpcResult
. ? ?.. , , RpcError
RpcResult
, ?.. , , .. req_msg_id
?..
. , , :
rpc_drop_answer#58e4a740 req_msg_id:long = RpcDropAnswer;
, , , ( , ), (: Telegram Desktop ).
:
, TL, MTProto Telegram , , soft skills , . , , .
, .
bad_msg_notification#a7eff811 bad_msg_id:long bad_msg_seqno:int error_code:int = BadMsgNotification; bad_server_salt#edab447b bad_msg_id:long bad_msg_seqno:int error_code:int new_server_salt:long = BadMsgNotification;
, MTProto, " β β " β . :
- , . - , .
- ? 16, 17, 18, 19, 20, 32, 33, 34, 35, 48, 64β¦ , ?
:
The intention is that error_code values are grouped (error_code >> 4): for example, the codes 0x40 β 0x4f correspond to errors in container decomposition.
, -, , -, , ? ?.. , .
:
- Request for Message Status Information
If either party has not received information on the status of its outgoing messages for a while, it may explicitly request it from the other party:
msgs_state_req#da69fb52 msg_ids:Vector long = MsgsStateReq;
- Informational Message regarding Status of Messages
msgs_state_info#04deb57d req_msg_id:long info:string = MsgsStateInfo;
Here,info
is a string that contains exactly one byte of message status for each message from the incoming msg_ids list:
- 1 = nothing is known about the message (msg_id too low, the other party may have forgotten it)
- 2 = message not received (msg_id falls within the range of stored identifiers; however, the other party has certainly not received a message like that)
- 3 = message not received (msg_id too high; however, the other party has certainly not received it yet)
- 4 = message received (note that this response is also at the same time a receipt acknowledgment)
- +8 = message already acknowledged
- +16 = message not requiring acknowledgment
- +32 = RPC query contained in message being processed or processing already complete
- +64 = content-related response to message already generated
- +128 = other party knows for a fact that message is already received
This response does not require an acknowledgment. It is an acknowledgment of the relevant msgs_state_req, in and of itself.
Note that if it turns out suddenly that the other party does not have a message that looks like it has been sent to it, the message can simply be re-sent. Even if the other party should receive two copies of the message at the same time, the duplicate will be ignored. (If too much time has passed, and the original msg_id is not longer valid, the message is to be wrapped in msg_copy).
- Voluntary Communication of Status of Messages
Either party may voluntarily inform the other party of the status of the messages transmitted by the other party.
msgs_all_info#8cc0d131 msg_ids:Vector long info:string = MsgsAllInfo
- Extended Voluntary Communication of Status of One Message
β¦
msg_detailed_info#276d3ec6 msg_id:long answer_msg_id:long bytes:int status:int = MsgDetailedInfo;
msg_new_detailed_info#809db6df answer_msg_id:long bytes:int status:int = MsgDetailedInfo;
- Explicit Request to Re-Send Messages
msg_resend_req#7d861a08 msg_ids:Vector long = MsgResendReq;
The remote party immediately responds by re-sending the requested messages [...] - Explicit Request to Re-Send Answers
msg_resend_ans_req#8610baeb msg_ids:Vector long = MsgResendReq;
The remote party immediately responds by re-sending answers to the requested messages [...] - Message Copies
In some situations, an old message with a msg_id that is no longer valid needs to be re-sent. Then, it is wrapped in a copy container:
msg_copy#e06046b2 orig_message:Message = MessageCopy;
Once received, the message is processed as if the wrapper were not there. However, if it is known for certain that the message orig_message.msg_id was received, then the new message is not processed (while at the same time, it and orig_message.msg_id are acknowledged). The value of orig_message.msg_id must be lower than the containerβs msg_id.
, msgs_state_info
TL ( , enum, ). . - , ?.. , - , , β , . .
, , , , . , . , ! , -, . , , , TL β () , , , .. .
. .
, ( ), β TCP ( , , ), MTProto β . , , .
β . , - β . ? . , - , , Telegram Desktop 4 , ( , , ; , , MTProto ).
? , , -, . , , TCP β RTT "" ( ) . , β .
, , ? TCP TCP β .
, , , , API? , , , . ? msg_id
, , β - (, , , β , - ). , , :
- .
-
msg_id
β ; . - ) MsgsAck β , " ".
) , - , badmsg β " "
) , β , . -
RpcResult
β ( ) β , .
, . , , msg_id
. , - , .
. , , , β , ? (, ).
? " " " , !" ( , , rationale , ), / β , . ?
A server usually acknowledges the receipt of a message from a client (normally, an RPC query) using an RPC response. If a response is a long time coming, a server may first send a receipt acknowledgment, and somewhat later, the RPC response itself.
A client normally acknowledges the receipt of a message from a server (usually, an RPC response) by adding an acknowledgment to the next RPC query if it is not transmitted too late (if it is generated, say, 60-120 seconds following the receipt of a message from the server). However, if for a long period of time there is no reason to send messages to the server or if there is a large number of unacknowledged messages from the server (say, over 16), the client transmits a stand-alone acknowledgment.
β¦ : , , , .
:
Ping Messages (PING/PONG)
ping#7abe77ec ping_id:long = Pong;
A response is usually returned to the same connection:
pong#347773c5 msg_id:long ping_id:long = Pong;
These messages do not require acknowledgments. A pong is transmitted only in response to a ping while a ping can be initiated by either side.
Deferred Connection Closure + PING
ping_delay_disconnect#f3427b8c ping_id:long disconnect_delay:int = Pong;
Works like ping. In addition, after this is received, the server starts a timer which will close the current connection disconnect_delay seconds later unless it receives a new message of the same type which automatically resets all previous timers. If the client sends these pings once every 60 seconds, for example, it may set disconnect_delay equal to 75 seconds.
?! 60 , - , . 120 , , , . , β " , ", TCP_NODELAY, . , , β 200 . β , , 5 , "User is typing...". .
, . , TCP-. , 10 β , . 3 , β , ?..
. TCP-, , . , , . , SSH- , - , , β ( , ), . , , (, !), , , β .
/ IM , β -. "", . , Jabber ( 20 ) β , , , online ( ). , TCP_KEEPALIVE, , TCP, ( ), β , , , , (, ? Telegram Desktop Ubuntu 18.04 ).
, β , , .
Telegram? ! .. , , . β ping_delay_disconnect
, . , , . , , . , , , , ...
?
, Telegram/ ( ) .
, Telegram ? , , TCP-, , . β . , UDP-, ( β ). - , TCP , , ( ), " " β .
? , msg_id
, replay-, . , ( Updates, API ), :
- , TCP- , β , , , . id', " seq_no" β , TCP ( β seq ). , ?
- replay- , - nonce. , .
uint32
β , 16 , β ( ). -
msg_id
β , -, id , -, id , . ,seq_no
.
, β API , . , , .
API ?
-! , , , , ( , , PUSH, - ).
, Perl! ( , , bless β , β ):
2019.10.24 12:00:51 $1 = { 'cb' => 'TeleUpd::__ANON__', 'out' => bless( { 'filter' => bless( {}, 'Telegram::ChannelMessagesFilterEmpty' ), 'channel' => bless( { 'access_hash' => '-6698103710539760874', 'channel_id' => '1380524958' }, 'Telegram::InputPeerChannel' ), 'pts' => '158503', 'flags' => 0, 'limit' => 0 }, 'Telegram::Updates::GetChannelDifference' ), 'req_id' => '6751291954012037292' }; 2019.10.24 12:00:51 $1 = { 'in' => bless( { 'req_msg_id' => '6751291954012037292', 'result' => bless( { 'pts' => 158508, 'flags' => 3, 'final' => 1, 'new_messages' => [], 'users' => [], 'chats' => [ bless( { 'title' => '', 'username' => 'hoolinomics', 'flags' => 8288, 'id' => 1380524958, 'access_hash' => '-6698103710539760874', 'broadcast' => 1, 'version' => 0, 'photo' => bless( { 'photo_small' => bless( { 'volume_id' => 246933270, 'file_reference' => ' 'secret' => '1854156056801727328', 'local_id' => 228648, 'dc_id' => 2 }, 'Telegram::FileLocation' ), 'photo_big' => bless( { 'dc_id' => 2, 'local_id' => 228650, 'file_reference' => ' 'secret' => '1275570353387113110', 'volume_id' => 246933270 }, 'Telegram::FileLocation' ) }, 'Telegram::ChatPhoto' ), 'date' => 1531221081 }, 'Telegram::Channel' ) ], 'timeout' => 300, 'other_updates' => [ bless( { 'pts_count' => 0, 'message' => bless( { 'post' => 1, 'id' => 852, 'flags' => 50368, 'views' => 8013, 'entities' => [ bless( { 'length' => 20, 'offset' => 0 }, 'Telegram::MessageEntityBold' ), bless( { 'length' => 18, 'offset' => 480, 'url' => 'https://alexeymarkov.livejournal.com/[url_].html' }, 'Telegram::MessageEntityTextUrl' ) ], 'reply_markup' => bless( { 'rows' => [ bless( { 'buttons' => [ bless( { 'text' => '???? 165', 'data' => 'send_reaction_0' }, 'Telegram::KeyboardButtonCallback' ), bless( { 'data' => 'send_reaction_1', 'text' => '???? 9' }, 'Telegram::KeyboardButtonCallback' ) ] }, 'Telegram::KeyboardButtonRow' ) ] }, 'Telegram::ReplyInlineMarkup' ), 'message' => ' ! // [ ] .', 'to_id' => bless( { 'channel_id' => 1380524958 }, 'Telegram::PeerChannel' ), 'date' => 1571724559, 'edit_date' => 1571907562 }, 'Telegram::Message' ), 'pts' => 158508 }, 'Telegram::UpdateEditChannelMessage' ), bless( { 'pts' => 158508, 'message' => bless( { 'edit_date' => 1571907589, 'to_id' => bless( { 'channel_id' => 1380524958 }, 'Telegram::PeerChannel' ), 'date' => 1571807301, 'message' => ' Facebook ? ? -, . , , , . : Facebook . , , . . - . , , - . . , : Whatsapp, Instagram. , ! . . - - , . # #facebook # #', 'reply_markup' => bless( { 'rows' => [ bless( { 'buttons' => [ bless( { 'data' => 'send_reaction_0', 'text' => '???? 452' }, 'Telegram::KeyboardButtonCallback' ), bless( { 'text' => '???? 21', 'data' => 'send_reaction_1' }, 'Telegram::KeyboardButtonCallback' ) ] }, 'Telegram::KeyboardButtonRow' ) ] }, 'Telegram::ReplyInlineMarkup' ), 'entities' => [ bless( { 'length' => 199, 'offset' => 0 }, 'Telegram::MessageEntityBold' ), bless( { 'length' => 8, 'offset' => 919 }, 'Telegram::MessageEntityHashtag' ), bless( { 'offset' => 928, 'length' => 9 }, 'Telegram::MessageEntityHashtag' ), bless( { 'length' => 6, 'offset' => 938 }, 'Telegram::MessageEntityHashtag' ), bless( { 'length' => 11, 'offset' => 945 }, 'Telegram::MessageEntityHashtag' ) ], 'views' => 6964, 'flags' => 50368, 'id' => 854, 'post' => 1 }, 'Telegram::Message' ), 'pts_count' => 0 }, 'Telegram::UpdateEditChannelMessage' ), bless( { 'message' => bless( { 'reply_markup' => bless( { 'rows' => [ bless( { 'buttons' => [ bless( { 'data' => 'send_reaction_0', 'text' => '???? 213' }, 'Telegram::KeyboardButtonCallback' ), bless( { 'data' => 'send_reaction_1', 'text' => '???? 8' }, 'Telegram::KeyboardButtonCallback' ) ] }, 'Telegram::KeyboardButtonRow' ) ] }, 'Telegram::ReplyInlineMarkup' ), 'views' => 2940, 'entities' => [ bless( { 'length' => 609, 'offset' => 348 }, 'Telegram::MessageEntityItalic' ) ], 'flags' => 50368, 'post' => 1, 'id' => 857, 'edit_date' => 1571907636, 'date' => 1571902479, 'to_id' => bless( { 'channel_id' => 1380524958 }, 'Telegram::PeerChannel' ), 'message' => ' 1 . 10 (, 1-) : // [ ] , 1 , ... , .' }, 'Telegram::Message' ), 'pts_count' => 0, 'pts' => 158508 }, 'Telegram::UpdateEditChannelMessage' ), bless( { 'pts' => 158508, 'pts_count' => 0, 'message' => bless( { 'message' => ', , , 1? // [ ] # #it #', 'edit_date' => 1571907650, 'date' => 1571893707, 'to_id' => bless( { 'channel_id' => 1380524958 }, 'Telegram::PeerChannel' ), 'flags' => 50368, 'post' => 1, 'id' => 856, 'reply_markup' => bless( { 'rows' => [ bless( { 'buttons' => [ bless( { 'data' => 'send_reaction_0', 'text' => '???? 360' }, 'Telegram::KeyboardButtonCallback' ), bless( { 'data' => 'send_reaction_1', 'text' => '???? 32' }, 'Telegram::KeyboardButtonCallback' ) ] }, 'Telegram::KeyboardButtonRow' ) ] }, 'Telegram::ReplyInlineMarkup' ), 'views' => 4416, 'entities' => [ bless( { 'offset' => 0, 'length' => 64 }, 'Telegram::MessageEntityBold' ), bless( { 'offset' => 1551, 'length' => 5 }, 'Telegram::MessageEntityHashtag' ), bless( { 'length' => 3, 'offset' => 1557 }, 'Telegram::MessageEntityHashtag' ), bless( { 'offset' => 1561, 'length' => 10 }, 'Telegram::MessageEntityHashtag' ) ] }, 'Telegram::Message' ) }, 'Telegram::UpdateEditChannelMessage' ) ] }, 'Telegram::Updates::ChannelDifference' ) }, 'MTProto::RpcResult' ) }; 2019.10.24 12:00:51 $1 = { 'in' => bless( { 'update' => bless( { 'user_id' => 2507460, 'status' => bless( { 'was_online' => 1571907651 }, 'Telegram::UserStatusOffline' ) }, 'Telegram::UpdateUserStatus' ), 'date' => 1571907650 }, 'Telegram::UpdateShort' ) }; 2019.10.24 12:05:46 $1 = { 'in' => bless( { 'chats' => [], 'date' => 1571907946, 'seq' => 0, 'updates' => [ bless( { 'max_id' => 141719, 'channel_id' => 1295963795 }, 'Telegram::UpdateReadChannelInbox' ) ], 'users' => [] }, 'Telegram::Updates' ) }; 2019.10.24 13:01:23 $1 = { 'in' => bless( { 'server_salt' => '4914425622822907323', 'unique_id' => '5297282355827493819', 'first_msg_id' => '6751307555044380692' }, 'MTProto::NewSessionCreated' ) }; 2019.10.24 13:24:21 $1 = { 'in' => bless( { 'chats' => [ bless( { 'username' => 'freebsd_ru', 'version' => 0, 'flags' => 5440, 'title' => 'freebsd_ru', 'min' => 1, 'photo' => bless( { 'photo_small' => bless( { 'local_id' => 328733, 'volume_id' => 235140688, 'dc_id' => 2, 'file_reference' => ' 'secret' => '4426006807282303416' }, 'Telegram::FileLocation' ), 'photo_big' => bless( { 'dc_id' => 2, 'file_reference' => ' 'volume_id' => 235140688, 'local_id' => 328735, 'secret' => '71251192991540083' }, 'Telegram::FileLocation' ) }, 'Telegram::ChatPhoto' ), 'date' => 1461248502, 'id' => 1038300508, 'democracy' => 1, 'megagroup' => 1 }, 'Telegram::Channel' ) ], 'users' => [ bless( { 'last_name' => 'Panov', 'flags' => 1048646, 'min' => 1, 'id' => 82234609, 'status' => bless( {}, 'Telegram::UserStatusRecently' ), 'first_name' => 'Dima' }, 'Telegram::User' ) ], 'seq' => 0, 'date' => 1571912647, 'updates' => [ bless( { 'pts' => 137596, 'message' => bless( { 'flags' => 256, 'message' => ' ??', 'to_id' => bless( { 'channel_id' => 1038300508 }, 'Telegram::PeerChannel' ), 'id' => 119634, 'date' => 1571912647, 'from_id' => 82234609 }, 'Telegram::Message' ), 'pts_count' => 1 }, 'Telegram::UpdateNewChannelMessage' ) ] }, 'Telegram::Updates' ) };
, β , !
Oh, wai~~β¦ ? - β¦ , Web API JSON, ?..
β¦ , ?.. β , Web- ?.. JSON HTTPS ?! ? ?
, TL+MTProto, . , HTTP, "-", , - TLS ?
. , JSON, , . MsgPack , , , CBOR β , , RFC 7049. , , , :
- 25 + 256 β ,
- 26 β Perl c
- 27 β
, TL CBOR . CBOR - :
cborlen=1039673 tl_len=1095092
, : , , .
. RTT ( ) β MTProto, β , , etc. TLS? :
PFS TLS TLS session tickets (RFC 5077) . , ( session ticket). , session ticket, , . ticket (session ticket key), frontend-, SSL .[10]. , session ticket PFS , , (OpenSSL, nginx, Apache ; , ).
RTT , ClientHello ServerHello, Finished . , Web, , , - , Web- β . , , .
- ? .
To be continued!
, β , , , .. , , , .
/ . , :
- TL-
- dialogs roster
- vs
- photo image
! Stay tuned!