Skip to content

Aead高级加密方式

zk-123 edited this page Nov 5, 2019 · 2 revisions

aead加密方式

aead是一种带有关联数据的身份验证加密方式。aead加密方式同时提供机密性、完整性、真实性。它们在现代硬件上具有出色的性能和功效。用户应尽可能使用aead加密方式。

aead加密方式与其对称加密方式主要不同的地方就是:它每一段密文必定有对应的校验码,通过核对校验码来判断密文是否完整。

建议使用以下aead加密方式。合规的shadowsocks实现必须支持aead_chacha20_poly1305加密方式。具有硬件aes加速功能的设备的实现还应支持:aead_aes_128_gcmaead_aes_128_gcm, aead_aes_256_gcm等加密方式。

名称 别名 密钥长度(主密钥) 盐长度 随机数长度 校验码长度
aead_chacha20_poly1305 chacha20-ietf-poly1305 32 32 12 16
aead_aes_256_gcm aes-256-gcm 32 32 12 16
aead_aes_192_gcm aes-192-gcm 24 24 12 16
aead_aes_128_gcm aes-128-gcm 16 16 12 16

主密钥派生

主密钥可以通过密码获得,shadowsocks根据密码获取密钥的算法采用的是SSL的 EVP_BytesToKey算法。经查询后得知,其获取密钥算法的基本思想如下:

首先通过MD5对密码进行摘要,得出16位长度的一次密钥。如果shadowsocks需要的密钥长度不超过16位,则从一次密钥截取0-length长度作为结果;如果shadowsocks需要的密钥长度大于16位,那么就用MD5再次对一次密钥 + 密码再次摘要,又得出16位密钥,我们把一次密钥 + 本次密钥 共32位长度作为二次密钥,如果需要的密钥长度在32位范围内,那么就采取之前的方式,在二次密钥上截取就好了。如果超过32位,再次将二次密钥 + 密码 再进行摘要。

子密钥派生

子密钥通过HKDF_SHA函数派生出来,它(这个函数)接收:主密钥,盐,信息串这三项作为输入;即使主密钥较弱的情况下,也能生成高强度的子密钥。

HKDF_SHA1(key, salt, info) => subkey
  • 入参key字符串,就是主密钥。
  • 入参info字符串, 固定值为:ss-subkey
  • 入参salt字符串, 随机生成,在预共享朱密钥的整个生命周期中,Salt必须唯一。(我理解的是在一个链接中子密钥, salt唯一)
  • 出参subkey字符串,就是子密钥(长度应该是固定值32)。

认证加密/解密

AE_encrypt是一个加密函数,该函数入参:子密钥,随机数,明文消息;该函数出参:密文,校验码。

AE_encrypt(key, nonce, message) => (ciphertext, tag)
  • 入参key,就是上述生成的子密钥,每次会话唯一。
  • 入参nonce,随机数,每会话的随机数必须唯一。
  • 入参message,明文。
  • 出参ciphertext,密文。
  • 出参tag, 校验码。

AE_decrypt是一个解密函数,该函数入参:子密钥,随机数,密文,校验码;该函数出参:明文。

AE_decrypt(key, nonce, ciphertext, tag) => message
  • 入参key, 子密钥,每次会话唯一。
  • 入参nonce, 随机数,每会话的随机数必须唯一。
  • 入参ciphertext, 密文。
  • 入参tag,校验码。
  • 出参message, 明文。

TCP协议

aead加密的TCP流以随机生成盐开始,以派生出每个回话的子密钥,然后是任一数量的加密块,每个加密块具有以下结构:

[encrypted payload length][length tag][encrypted payload][payload tag]
  • [encrypted payload length]: 有效负载长度密文。(注意:该长度解密后的值是小于0x3FFF的无符号整数)
  • [length tag]: 有效负载长度密文的校验码。
  • [encrypted payload]: 有效负载密文。
  • [payload tag]: 有效负载密文校验码。

其他应注意:

  • 加密和解密nonce不同,子密钥不同。
  • nonce是一个从0开始,每次加密或解密就会递增的,无符号的小端整数(小端整数就是只有第一个字节递增)。
  • 每个加密块都会加密/解密两次,nonce也要递增两次。

UDP协议

aead加密方式的每个UDP包应该有以下结构:

[salt][encrypted payload][tag]

开始的salt用于生成子密钥,每个UDPsalt应随机生成以确保其唯一性,每个UDP数据包都使用全0字节作为nonce以加密/解密。

Translated from http://shadowsocks.org/en/spec/AEAD-Ciphers.html

Clone this wiki locally