传出消息的序列化和加密
一个DecryptedMessage类型的 TL 对象被创建并包含纯文本的消息。为了向后兼容,必须将对象包装在构造函数decryptedMessageLayer中,并指示支持的层(从46 开始)。
使用通用 TL 规则将生成的构造序列化为字节数组。结果数组前面有 4 个字节,其中包含不计算这 4 个字节的数组长度。
字节数组用 12 到 1024 个随机填充字节填充,使其长度可被 16 个字节整除。(在较旧的 MTProto 1.0 加密中,仅使用了 0 到 15 个填充字节。)
消息密钥msg_key计算为上一步中获得的数据的 SHA256 的中间 128 位,前面加上来自共享密钥key的 32 个字节。(对于较旧的 MTProto 1.0 加密,msg_key的计算方式不同,因为前面步骤中获得的数据的 SHA1 的 128 个低位,不包括填充字节。)霍尔果斯TG
对于 MTProto 2.0,AES 密钥aes_key和初始化向量aes_iv计算如下(key是在Key Generation期间获得的共享密钥),如下所示:
msg_key_large = SHA256 (substr (key, 88+x, 32) + plaintext + random_padding);
msg_key = substr (msg_key_large, 8, 16);
sha256_a = SHA256 (msg_key + substr (key, x, 36));
sha256_b = SHA256 (substr (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);
对于 MTProto 2.0,x=0表示来自秘密聊天发起者的消息,x=8表示相反方向的消息。
对于过时的 MTProto 1.0,msg_key、aes_key 和 aes_iv 的计算方式不同(请参阅本文档以供参考)。
数据使用 256 位密钥aes_key和 256 位初始化向量aes-iv进行加密,使用具有无限乱码扩展 (IGE) 的 AES-256 加密。加密密钥指纹key_fingerprint和消息密钥msg_key被添加到结果字节数组的顶部。
加密数据嵌入到messages.sendEncrypted API 调用中,并传递给 Telegram 服务器以传递给秘密聊天的另一方。
从 MTProto 1.0 升级到 MTProto 2.0
一旦秘密聊天中的双方至少使用第 73 层,他们就应该只对所有传出消息使用 MTProto 2.0。如果在创建秘密聊天期间尚未协商足够高的起始层,则某些第一次收到的消息可能会使用 MTProto 1.0。在收到使用 MTProto 2.0 加密的第一条消息(或第 73 层或更高层的第一条消息)后,所有具有更高序列号的消息也必须使用 MTProto 2.0 加密。
只要当前层低于 73,每一方都应该尝试使用 MTProto 1.0 解密收到的消息,如果不成功(msg_key 不匹配),请尝试 MTProto 2.0。一旦第一个 MTProto 2.0 加密的消息到达(或层升级到 73),就无需尝试对任何进一步的消息进行 MTProto 1.0 解密(除非客户端仍在等待某些间隙被关闭)。
解密传入消息
上述步骤以相反的顺序执行。收到加密消息时,您必须检查 msg_key实际上是否等于解密消息的 SHA256 散列的中间 128 位,前面加上从 shared key中获取的 32 个字节。如果消息层大于客户端支持的层,则必须通知用户客户端版本已过期并提示更新。
序号
有必要按原始顺序解释所有消息,以防止可能的操纵。秘密聊天支持一种独立于服务器处理 seq_no 计数器的特殊机制。
本文进一步描述了如何正确处理这些计数器:秘密聊天中的序列号。
请注意,您的客户端必须支持秘密聊天中的序列号才能与官方 Telegram 客户端兼容。
发送加密文件
发送到秘密聊天的所有文件都使用一次性密钥进行加密,这些密钥与聊天的共享密钥没有任何关系。在发送加密文件之前,假设加密文件的地址将使用messages.sendEncryptedFile方法的文件参数附加到加密消息的外部,并且直接解密的密钥将在正文中发送message(构造函数中的key参数 decryptedMessageMediaPhoto、decryptedMessageMediaVideo和decryptedMessageMediaFile。telegram
在将文件发送到秘密聊天之前,会计算 2 个随机 256 位数字,这些数字将用作用于加密文件的 AES 密钥和初始化向量。以类似方式使用具有无限乱码扩展 (IGE) 的 AES-256 加密。
密钥指纹计算如下:
摘要 = md5(key + iv)
指纹 = substr(digest, 0, 4) XOR substr(digest, 4, 4)
文件的加密内容存储在服务器上的方式与云聊天中文件的存储方式非常相似:使用对upload.saveFilePart的调用逐个进行。随后对messages.sendEncryptedFile的调用将为存储的文件分配一个标识符,并将地址与消息一起发送。收件人将收到 encryptedMessage的更新,文件参数将包含文件信息。
可以使用构造函数inputEncryptedFile将传入和传出的加密文件转发到其他秘密聊天,以避免将相同的内容两次保存在服务器上。
使用更新框
秘密聊天与特定设备(或者更确切地说与授权密钥)相关联,而不是用户。使用pts来描述客户端状态的常规消息框是不适合的,因为它是为长期消息存储和来自不同设备的消息访问而设计的。霍尔果斯霍尔果斯TG安卓版下载
为了解决这个问题,引入了一个额外的临时消息队列。当发送有关来自秘密聊天的消息的更新时,会发送一个新的qts值,这有助于在连接长时间中断或更新丢失的情况下重建差异。
随着事件数量的增加,qts的值随着每个新事件增加 1。初始值可能不(也不会)等于 0。
通过调用messages.receivedQueue方法或通过调用updates.getDifference(传递的qts的值,而不是最终状态)隐式确认客户端已接收和存储来自临时队列的事件这一事实。所有由客户端确认为已送达的消息,以及任何超过 7 天的消息,都可能(并将)从服务器中删除。
取消授权后,相应设备的事件队列将被强制清空,qts的值将变得无关紧要。