用户协议 - Telegram-Telegram

用户协议

协议:详细说明

用户协议 从 4.6 版开始,主要的 Telegram 客户端都在使用MTProto 2.0。MTProto v.1.0 已弃用,目前正在逐步淘汰。

本文介绍了 MTProto 协议 2.0 版(云聊天、服务器-客户端加密)的基础层。与 1.0 版(此处描述以供参考)的主要区别如下:

使用 SHA-256 代替 SHA-1;
填充字节参与msg_key的计算;
msg_key不仅取决于要加密的消息,还取决于auth_key的一部分;
使用 12..1024 填充字节代替 v.1.0 中的 0..15 填充字节。
另请参阅:MTProto 2.0:秘密聊天,端到端加密

协议说明

用户协议

在使用传输协议通过网络传输消息之前,它会以某种方式进行加密,并在消息顶部添加一个外部标头,该标头由 64 位密钥标识符auth_key_id (唯一标识服务器和用户的授权密钥)和 128 位消息密钥msg_key。

授权密钥auth_key结合消息密钥msg_key定义了一个实际的 256 位密钥aes_key和一个 256 位初始化向量aes_iv,用于在无限乱码扩展 (IGE) 模式下使用 AES-256 加密对消息进行加密。请注意,要加密的消息的初始部分包含可变数据(会话、消息 ID、序列号、服务器 salt),这些数据显然会影响消息密钥(以及 AES 密钥和 iv)。在MTProto 2.0中,消息密钥定义为消息正文(包括会话、消息 ID、填充等)的 SHA-256 的 128 个中间位,前面加上从授权密钥中获取的 32 个字节。在旧的MTProto 1.0,消息密钥被计算为消息正文的 SHA-1 的低 128 位,不包括填充字节。

多部分消息被加密为单个消息。

MTProto 服务器-客户端加密,云聊天
对此设置有疑问?— 查看高级常见问题解答!

注1

要在 MTProto 中加密的每条明文消息始终包含以下要在解密时检查的数据,以使系统对组件的已知问题具有鲁棒性:

服务器盐(64 位)
会话 ID
消息序列号
消息长度
时间

笔记2

Telegram 的端到端加密秘密聊天在上述基础上使用了额外的加密层。有关详细信息,请参阅秘密聊天、端到端加密。

术语

授权密钥 (auth_key)
客户端设备和服务器共享的 2048 位密钥,在用户注册时通过交换 Diffie-Hellman 密钥直接在客户端设备上创建,并且从未通过网络传输。每个授权密钥都是用户特定的。没有什么可以阻止用户拥有多个密钥(对应于不同设备上的“永久会话”),如果设备丢失,其中一些可能会被永久锁定。另请参阅创建授权密钥。

Telegram 协议

服务器密钥

服务器使用 2048 位 RSA 密钥对自己的消息进行数字签名,同时进行注册并生成授权密钥。该应用程序有一个内置的公共服务器密钥,可用于验证签名但不能用于签署消息。私有服务器密钥存储在服务器上并且很少更改。

密钥标识符 (auth_key_id)
授权密钥的 SHA1 哈希的低 64 位用于指示使用哪个特定密钥来加密消息。密钥必须由其 SHA1 的低 64 位唯一定义,并且在发生冲突时,将重新生成授权密钥。零密钥标识符意味着不使用加密,这对于在注册期间用于在 Diffie-Hellman 交换中生成授权密钥的有限消息类型集是允许的。对于 MTProto 2.0,这里仍然使用 SHA1,因为 auth_key_id 应该标识独立于协议版本使用的授权密钥。

会议

客户端生成的(随机)64 位数字,用于区分各个会话(例如,在应用程序的不同实例之间,使用相同的授权密钥创建)。会话连同密钥标识符对应于一个应用程序实例。服务器可以维护会话状态。在任何情况下,都不能将用于一个会话的消息发送到另一个会话。服务器可能会单方面忘记任何客户端会话;客户应该能够处理这个问题。

服务器盐

应服务器的请求,每 30 分钟(每个会话分别)更改一个(随机)64 位数字。所有后续消息都必须包含新盐(尽管在 1800 秒内仍会接受带有旧盐的消息)。需要防止重放攻击和与将客户端时钟调整到遥远未来的某个时刻相关的某些技巧。

消息标识符 (msg_id)

一个(与时间相关的)64 位数字,用于唯一标识会话中的消息。客户端消息标识符可被 4 整除,如果消息是对客户端消息的响应,则服务器消息标识符模 4 产生 1,否则产生 3。客户端消息标识符必须单调增加(在单个会话中),与服务器消息标识符相同,并且必须近似等于 unixtime*2^32。这样,消息标识符指向创建消息的大致时间。消息在创建后超过 300 秒或在创建前 30 秒被拒绝(这是防止重放攻击所必需的)。在这种情况下,必须使用不同的标识符重新发送(或放置在具有更高标识符的容器中)。

重要提示:为了应对重放攻击,客户端传递的msg_id的低 32 位不能为空,并且必须显示创建消息时时间点的小数部分。

内容相关消息

需要明确确认的消息。这些包括所有的用户和许多服务消息,几乎所有的,除了容器和确认。

消息序列号 (msg_seqno)
一个 32 位数字,等于发送方在此消息之前创建的“内容相关”消息(需要确认的消息,尤其是非容器的消息)数量的两倍,如果当前消息是后续消息,则随后加一内容相关的消息。容器总是在其全部内容之后生成;因此,它的序号大于或等于其中包含的消息的序号。

消息密钥 (msg_key)

在MTProto 2.0中,要加密的消息的 SHA-256 散列的中间 128 位(包括 MTProto 2.0 的内部标头和填充字节),前面是授权密钥的 32 字节片段。

在MTProto 1.0中,消息密钥的定义不同,作为要加密的消息的 SHA-1 哈希的低 128 位,填充字节被排除在哈希计算之外。此计算不涉及授权密钥。

内部(加密)标头

在将消息或容器全部加密之前添加在消息或容器之前的标头(16 个字节)。由服务器盐(64 位)和会话(64 位)组成。

外部(加密)标头

在加密消息或容器之前添加的标头(24 字节)。由密钥标识符auth_key_id(64 位)和消息密钥msg_key(128 位)组成。

有效载荷

外部标头 + 加密消息或容器。

定义 AES 密钥和初始化向量
2048 位授权密钥 (auth_key) 和 128 位消息密钥 (msg_key) 用于计算 256 位 AES 密钥 (aes_key) 和 256 位初始化向量 (aes_iv),随后用于加密部分在无限乱码扩展 (IGE) 模式下使用 AES-256 对要加密的消息(即除了稍后添加的外部标头之外的所有内容)进行加密。

对于 MTProto 2.0,从 auth_key 和 msg_key 计算 aes_key 和 aes_iv 的算法如下。

算法

msg_key_large = SHA256 (substr (auth_key, 88+x, 32) + plaintext + random_padding);
msg_key = substr (msg_key_large, 8, 16);
sha256_a = SHA256 (msg_key + substr (auth_key, x, 36));
sha256_b = SHA256 (substr (auth_key, 40+x, 36) + msg_key);
aes_key = substr (sha256_a, 0, 8) + substr (sha256_b, 8, 16) + substr (sha256_a, 24, 8);
aes_iv = substr (sha256_b, 0, 8) + substr (sha256_a, 8, 16) + substr (sha256_b, 24, 8);
其中 x = 0 表示从客户端到服务器的消息,x = 8 表示从服务器到客户端的消息。

对于过时的 MTProto 1.0,msg_key、aes_key 和 aes_iv 的计算方式不同。

计算方式

auth_key 的低 1024 位不参与计算。它们可以(与剩余位一起或单独)在客户端设备上用于加密从服务器接收到的数据的本地副本。auth_key的低512位不存储在服务器上;因此,如果客户端设备使用它们来加密本地数据并且用户丢失了密钥或密码,则本地数据的数据解密是不可能的(即使可以从服务器获得数据)。

在 MTProto 1.0 中,当使用 AES 加密长度不能被 16 字节整除的数据块时,数据会在加密之前用 0 到 15 个随机填充字节random_padding填充到可被 16 字节整除的长度。在 MTProto 2.0 中,计算msg_key. 请注意,MTProto 2.0 需要 12 到 1024 字节的填充,但仍受制于生成的消息长度可被 16 字节整除的条件。

使用 MTProto 2.0 而不是 MTProto 1.0

客户端可以在同一 TCP 连接中仅使用 MTProto 2.0 或仅使用 MTProto 1.0。服务器检测从客户端接收到的第一条消息使用的协议,然后对其消息使用相同的加密,并期望客户端今后使用相同的加密。我们推荐使用 MTProto 2.0;MTProto 1.0 已弃用,仅支持向后兼容。

重要检查

当接收到加密消息时,必须检查msg_key实际上等于解密数据的 SHA-256 的中间 128 位,并在其前面加上 32 字节的 auth_key 片段,并且msg_id 具有偶校验从客户端到服务器的消息,以及从服务器到客户端的消息的奇偶校验。

此外,必须存储从对方接收到的最后 N 条消息的标识符(msg_id),如果消息的 msg_id 低于或等于存储的任何值,则该消息将被忽略。否则,将新消息 msg_id 添加到集合中,如果存储的 msg_id 值的数量大于 N,则忘记最旧的(即最低的)。

最重要的是,未来超过 30 秒或过去超过 300 秒的 msg_id 值将被忽略。这对于服务器来说尤其重要。客户端也会发现这很有用(以防止重放攻击),但前提是它确定了它的时间(例如,如果它的时间已经与服务器的时间同步)。

尽管如此,某些包含客户端发送到服务器的数据(例如,最近客户端查询的 msg_id)的客户端到服务器服务消息可能会在客户端上进行处理,即使时间看起来“不正确”。对于更改 server_salt 的消息和无效客户端时间的通知尤其如此。请参阅移动协议:服务消息。

在客户端设备上存储授权密钥

可能会建议关心安全的用户以与 ssh 中大致相同的方式对授权密钥进行密码保护。这可以通过在密钥前面添加密钥的加密哈希函数(例如 SHA-256)的值来完成,然后使用 CBC 模式下的 AES 和等于用户(文本)的密钥对整个字符串进行加密。 ) 密码。当用户输入密码时,存储的受保护密码通过检查 SHA-256 值进行解密和验证。从用户的角度来看,这实际上与使用应用程序或网站密码相同。

未加密的消息

特殊的纯文本消息可用于创建授权密钥以及执行时间同步。它们以 auth_key_id = 0(64 位)开头,这意味着没有 auth_key。紧随其后的是序列化格式的消息正文,没有内部或外部标头。在消息正文之前添加消息标识符(64 位)和以字节为单位的正文长度(32 字节)。

只有非常有限数量的特殊类型的消息可以作为纯文本传输。

加密消息

auth_key_id
int64 msg_key
int128 加密数据
字节
加密消息:encrypted_data
包含以下数据的密文:

int64 session_id
int64 message_id
int64 seq_no
int32 message_data_length
int32 message_data
字节 填充12..1024
字节

未加密的消息

auth_key_id =0
int64 message_id
int64 message_data_length
int32 message_data字节
MTProto 2.0 使用 12..1024 填充字节,而不是 MTProto 1.0 中使用的 0..15

创建授权密钥

在注册之前的应用程序安装过程中,通常为每个用户创建一次授权密钥。实际上,注册本身是在创建授权密钥之后发生的。但是,在后台生成授权密钥时,可能会提示用户填写注册表单。用户击键之间的间隔可用作生成授权密钥所需的高质量随机数的熵源。

请参阅创建授权密钥。

在创建授权密钥期间,电报客户端获取其服务器盐(将在不久的将来与新密钥一起用于所有通信)。然后客户端使用新生成的密钥创建一个加密会话,除非客户端创建一个新会话,否则在该会话中进行后续通信(包括传输用户的注册信息和电话号码验证)。客户端可以通过选择一个新的随机 session_id 随时自由地创建新的或附加的会话。