微信安全通信协议mmtls分析

本文为现代密码学课程作业。

背景

mmtls协议是TLS1.3版本的简化版或者改良版。由于TLS1.3长期处于草案状态,微信团队需要进行安全通信协议设计又无法直接使用TLS1.3协议,因此微信决定采用TLS1.3草案中的一些标准,设计了自己的mmtls协议。mmtls在微信通信层次中属于介于应用层和网络连接层之间的层次,类似于TLS在HTTP与TCP之间的位置。

微信通信过程中使用TCP进行报文传输,微信通信分为长连接和短连接两种。其中短连接使用HTTP协议加上mmtls,长连接使用私有协议加上mmtls。从内部来说,mmtls有三个子协议:Record协议、Handshake协议和Alert协议。其中Alert协议和Handshake协议作为Record协议的上层。

认证密钥协商:Handshake协议

TLS1.3中提出了2中1-RTT密钥协商方式(1-RTT ECDHE和1-RTT PSK)和4种0-RTT密钥协商方式(0-RTT ECDH、0-RTT PSK、0-RTT ECHD-ECDHE、0-RTT PSK-ECDHE),mmtls只选择了1-RTT ECDHE、1-RTT PSK和0-PSK 三种方式。其中长连接使用1-RTT ECDHE和1-RTT PSK,短连接使用0-PSK。

1-RTT ECDHE密钥协商

普通的D-H密钥协商存在被中间人攻击的可能,需要对协商数据进行身份认证,即“带认证的密钥协商”。认证有两种方式,基于MAC的对称方式和基于数字签名的非对称方式。mmtls采用数字签名的方式,使用的算法叫做ECDSA。通信双方进行密钥协商的时候,分别运行签名算法对自己的公钥进行签名,收到对方的信息后首先要验证签名,签名正确再进行密钥协商。mmtls协议中只对服务器端进行认证,不对客户端进行认证。

ECDSA的过程为,服务器收到客户端的cli_pub_key之后,随机一对ECDH密钥svr_pub_keysvr_pro_key,用签名密钥sign_keysvr_pub_key签名得到签名值,将签名值和svr_pub_key一同发给客户端。客户端收到之后用verify_key进行验证,验证成功之后再继续协商密钥。其中verify_key直接内置在微信客户端内。

1-RTT PSK密钥协商

PSK是ECDH握手中服务器下发的,包含作对称加密密钥的key以及用ticket_key对key加密后的密文ticket。PSK在安全信道中传输,只有服务器知道ticket_key。PSK协商过程中,客户端将ticket发给服务器,服务器将ticket解密后得到key,再用key计算协商的数据的HMAC进行认证。PSK使用的是对称密钥。

0-RTT PSK密钥协商

在1-RTT PSK握手之前,客户端已经得到了一个对称加密密钥,可以直接用这个密钥加密数据,和ticket一起发送给服务器,节约一个轮次。

对称加密传输:Record协议

经过握手之后,客户端和服务器端协商出了对称加密密钥pre_master_key。我们需要加密也需要认证,在TLS1.3中只能使用AEAD类(将Encrypt和MAC集成在算法内部)的算法进行加密。mmtls选择了AES-GCM作为认证加密算法。AES-GCM中,AES为加密算法,GCM指采用Counter模式,并带有GMAC消息认证码。

对称加密需要6个加密参数,分别是:

1
2
3
4
5
6
Client Write MAC Key (用于 Client 算消息认证码,以及 Server 验证消息认证码)
Server Write MAC Key (用于 Server 算消息认证码,以及 Client 验证消息认证码)
Client Write Encryption Key(用做 Client 做加密,以及 Server 解密)
Server Write Encryption Key(用做 Server 做加密,以及 Client 解密)
Client Write IV (Client 加密时使用的初始化向量)
Server Write IV (Server 加密时使用的初始化向量)

pre_master_key只有48字节,不能满足上述6个参数的需要,需要对密钥进行扩展。mmtls使用的是HKDF进行密钥扩展。在0-RTT下的pre_master_key可能会有两个,Static SecretEphemeral Secret。HKDF定义了两个函数进行扩展,HKDF-Extract保证密钥的熵均匀、伪随机性足够,HKDF-Expand对密钥的长度进行扩展。在mmtls中,pre_master_key进过扩展,得到长度为2*enc_key_length 2*iv_length的一段buffer,称为key_block。剩下的4个参数就用key_block中截取的一部分代表。

1
2
3
4
client_write_key = key_block[0...enc_key_length-1]
client_write_key = key_block[enc_key_length...2*enc_key_length-1]
client_write_IV = key_block[2*enc_key_length...2*enc_key_length iv_length-1]
server_write_IV = key_block[2*enc_key_length iv_length...2*enc_key_length 2*iv_length-1]

总结

mmtls参考了TLS1.3草案,结合微信自身背景,在密钥协商过程中使用ECDH,签名验证中使用ECDSA,认证加密中使用AES-GCM,密钥扩展中使用HKDF,摘要算法使用SHA256。