前置知识
MAC
在密码学中,Message Authentication Code(消息认证码,有时也被称为tag)是用来认证消息的比较短的信息。换言之,MAC用来保证消息的数据完整性和消息的数据源认证。
MAC由消息本身和一个密钥经过一系列计算产生,用于生成MAC的算法,称为MAC算法。MAC算法应能满足如下几个条件:
- 在仅有消息本身没有密钥的情况下,无法得到该消息的MAC
- 同一个消息在使用不同密钥的情况下,生成的MAC应当无关联
- 在已有一系列消息以及其MAC时,给定一个新的消息,无法得到该消息的MAC。
下图摘自维基百科,可以很好的描述MAC的使用原理:
HMAC
HMAC (有时扩展为 英语:keyed-hash message authentication code, 密钥散列消息认证码, 或 英语:hash-based message authentication code,散列消息认证码),是一种通过特别计算方式之后产生的消息认证码(MAC),使用密码散列函数,同时结合一个加密密钥。它可以用来保证资料的完整性,同时可以用来作某个消息的身份验证。
HMAC是MAC算法中的一种,其基于加密HASH算法实现。任何加密HASH, 比如MD5、SHA256等,都可以用来实现HMAC算法,其相应的算法称为HMAC-MD5、HMAC-SHA256等。
function hmac (key, message)
if (length(key) > blocksize) then
key = hash(key) // keys longer than blocksize are shortened
end if
if (length(key) < blocksize) then
key = key + [0x00 * (blocksize - length(key))] // keys shorter than blocksize are zero-padded (where ∥ is concatenation)
end if
o_key_pad = [0x5c * blocksize] ⊕ key // Where blocksize is that of the underlying hash function
i_key_pad = [0x36 * blocksize] ⊕ key // Where ⊕ is exclusive or (XOR)
return hash(o_key_pad + hash(i_key_pad + message))
end function
AEAD
认证加密(英语:Authenticated encryption,AE)和带有关联数据的认证加密(authenticated encryption with associated data,AEAD,AE的变种)是一种能够同时保证数据的保密性、 完整性和真实性的一种加密模式。这些属性都是在一个易于使用的编程接口下提供的。
具体的实现方法
- Encrypt-then-MAC (EtM)
EtM方法
首先对明文进行加密,然后根据得到的密文生成消息认证码(MAC)。密文和它的MAC一起发送。例如IPsec。EtM是ISO/IEC 19772:2009规定的六种认证加密方法中的一种。[5]这是唯一可以达到认证加密安全性最高定义的方法,但这只有在使用的MAC“强不可伪造”时才能实现。[8]2014年11月,EtM的传输层安全性协议(TLS)和资料包传输层安全(DTLS)扩展已经作为RFC 7366发布。各种EtM密码包也存在于SSHv2中(例如[email protected])。
- Encrypt-and-MAC (E&M)
基于明文生成MAC,并且明文在没有MAC的情况下被加密。明文的MAC和密文一起发送。用于例如SSH。E&M方法本身并未被证明是“强不可伪造”的。
- MAC-then-Encrypt (MtE)
基于明文生成MAC,然后将明文和MAC一起加密以基于两者生成密文。密文(包含加密的MAC)被发送。MtE方法本身并未被证明是“强不可伪造”的。用于例如SSL/TLS。[8]尽管有理论上的安全性,但对SSL/TLS进行更深入的分析将保护模型化为MAC-then-pad-then-encrypt,即明文先填充到加密函数的块大小。填充错误通常会导致接收方发现可检测到的错误,从而导致密文填塞攻击(Padding oracle attack),如Lucky Thirteen attack。
Vmess协议的Client(基于v2ray-core)
ClientSession.isAEAD
这里首先有一个是否采用AEAD的属性
在outbound中可以看到
isAEAD默认是false,只有在没有明确关闭AEAD模式,同时在client配置文件中没有指定了AlterID属性时才会启用
ClientSession.idHash
这个属性protocol.idHash其实是一个hmac函数,通过这个函数可以计算mac值。前面前置知识写了mac算法有很多种,hmac便是一种。这个属性一般都是传默认值,protocol.DefaultIDHash
ClientSession.requestBodyKey和ClientSession.requestBodyIV和ClientSession.responseHeader
这个比较关键,是request加密的关键参数。
可以看到,首先生成了33位随机的byte,然后把前16位作为BodyKey,后16位作为BodyIV。同时最后一位留作校验responseHeader。
如果client没有启用AEAD模式,则返回的BodyKey应该为requestBodyKey的md5,返回的BodyIV也为requestBodyIV的md5。
如果client启用了AEAD模式,则返回的BodyKey为requestBodyKey进行sha256后得到的前16位,返回的BodyIV同理。
protocol.MemoryUser结构体
protocol.Account结构体
Account结构体是MemoryUser的核心属性
Account结构体中对应config.json中的设置
我们也可以通过
这种方式动态生成protocol.Account
客户端请求认证信息部分
查看outbound中客户端发请求的部分,
其中的reuqest头部分的Version为硬编码的1,User。
再回到client的Encode函数中,可以看到从拿到的header中拿出了用户信息,同时判断client是否被设置成AEAD模式,如果没设置AEAD模式那就好说,拿到一个以用户的ValidID作为key的HMAC生成器。此处的ValidID是怎么来的呢,我们跟进看
当MemoryAccount中的中的AlterID个数为0时返回MemoryAccound中的ID,其实也就是config.json中指定的id(如果配置json中没指定,那就会随机从protocol包中的server_spec进行随机抽取)。
也就是说根据配置文件中的id作为hmac算法的key拿到一个hmac生成器,同时把当前的时间戳放进去,算出来一个MAC,将其写入请求流
也就是官方文档中的客户端请求中的16字节认证信息。