密码学体系中,对称加密的实现,其实有两大类的算法。以AES、DES为代表的块加密(也叫分组加密)算法,和以ChaCha20、RC4为代表的流加密算法。在本文中,我们来简单探讨一下对这些问题的一些看法和认知。由于篇幅的限制,不会过于深入技术细节,而是将会重点从应用的角度,阐述流加密算法的原理、实现、特点和应用等方面的内容。从而帮助读者理解相关的重要概念和实现过程,并且可以在其基础上,有能力在自己的项目中开展应用并且获益。
原理
先从原理说起。从原理而言,流加密算法和块加密是完全不同的。一般块加密算法我们都比较熟悉,AES算法基本上已经一统江湖。甚至主流的CPU产品,已经内置了相关的指令集,来加速其执行。它的基本原理是,先将信息编码成为固定长度的信息块,然后以块为单位,进行加解密变换操作,然后使用某些算法规则,将变换后的块连接起来,就是最后的加密结果。 而流加密,这里的流,其实不是原文的编码信息流,而是按照算法,对密钥进行编码产生的密钥的编码流。真正的加密,就是使用密钥流对原文的编码流进行对位的异或操作,就得到密文;解密就是计算相同的密钥流,再做一遍异或操作,就得到原文了。
可以看到,不管是原理还是执行过程,相对而言,流加密更加简单。事实也是,在只使用软件处理的情况下,一般而言相同密钥长度流加密算法的性能要优于块加密算法。
这就可以解释,为什么我们已经有了优秀的AES算法,还需要流加密算法了。笔者总结,这个问题有以下要点:
- 性能: 纯软件方面,流加密算法可以提供比块加密更好的性能和资源占用表现
- 场景: 在当今应用更广泛的移动互联网场景之下,通常使用Arm架构,AES并没有硬件指令集的支持,其性能优势更为明显,也让其能够更好的适应通信、物联网等更广泛的应用场景
- 灵活: 流加密算法,不需要对信息进行分块,也不需要进行补位操作,这样就特别适合于通信信息的加密,能够更好的适应信息传输动态、随机的业务特点
- AES: AES已经是一个黄金标准,但行业需要挑战者和新技术
实现
流加密算法本身这个技术由来已久,其代表算法是RC4,基本上是和DES相同时代的技术,现在实际的运行环境中,已经被新的chacha20相关算法所取代。
比如,我们查看TLS1.3和OpenSSL的建议加密套件列表,能看到它建议使用的算法是:
- TLS_AES_256_GCM_SHA384
- TLS_CHACHA20_POLY1305_SHA256
- TLS_AES_128_GCM_SHA256
- TLS_AES_128_CCM_8_SHA256
- TLS_AES_128_CCM_SHA256
其中第二个,就是流加密套件Chacha20-Poly1305(后面简称为chacha,后面的SHA256是配套的Hash方案)。这个套件其实是由两个部分组成并组合使用,ChaCha20是一个流加密算法实现;Poly1305是一个MAC算法;两者构成了加解密和信息验证同时进行的更安全的体系,可以同时保证信息的机密性和完整性。这种方式被称为AEAD(Authenticated Encryption with Associated Data,带有关联数据的认证加密),密码学应用的最佳实践建议在对称加密的场景中优先使用这个模式(AES-GCM也是AEAD)。
但和AES的出身显赫(参加NIST组织的对称加密算法评审过程)不同,chacha的发展好像略显低调。Chacha20和Poly1305分别在2005和2008年设计出来,到了2013年,它们被合并并选为IETF的草案标准,并在2015年成为TLS1.2的标准组成部分。chacha得到重视和大力发展得益于google选择作为其移动互联网应用的标准算法,因为在手机应用上通常无法使用AES指令集加速,chacha的性能成为关键因素。此外,虽然没有经过像AES那样严格的行业评审,但chacha开始被google选择, 随后加入行业标准,并且广泛而大量的应用,本身就是一种安全背书,而且迄今也没有发现有什么明显的弱点和漏洞,这可能也是一个安全算法在安全性方面评估的另一种方式和角度(实用为王)。
ChaCha20-Poly1305算法由RFC8439定义,它使用256位的密钥和96位的随机初始向量,并使用计数器模式,衍生计算生成密钥流,可以对不定长的明文进行加密;随后,它使用一个改进的Poly1305实现,基于密钥、密文和附加信息生成一个验证Tag,长度为128位,作为密文的扩展。这个大概的过程如下图所示:
其实ChaCha20的实现是比较简单清晰的,但由于Poly1305的算法比较复杂,笔者会有机会的话另行撰文详细讨论。这里我们主要关注原理、特点和应用。
应用
使用chacha是非常简单方便的。 在nodejs crypto模块中,内置(应该是通过OpenSSL)了chacha的算法,而且使用方式,和AES-256-GCM也是一样的,基本上就换个名字就可以了。
下面简单展示了一下在nodejs环境中chacha的应用:
这段代码的要点如下:
- 算法名称为: chacha20-poly1305
- iv长度为12,tag长度为16
- 密钥通过密码、盐和衍生函数生成,注意在两端参数的一致性
- 可以自定义附加数据,附加在加密结果最后,并使用最后一个字节表示附加信息的长度
- 加密结果、IV、tag和附加数据,都统一连接起来进行base64编码
- 解密结果是一个数组,分别是原文(解码后的)和附加数据(可以使用字符串)
在浏览器环境,也有纯JS的实现,由于算法简单,结构清晰,chacha具备很好的兼容性、可扩展和可移植性。
Chacha20核心算法
笔者觉得这一部分还是有必要简单交代一下。因为它是chacha2算法的核心和精华,值得学习和分享。
chacha的核心计算,也是基于块数据进行操作的。每个块的计算分为三个部分。
第一部分是块输入,初始输入16个"字"(每个字4字节),前四个是常数,中间8个是密钥,然后是一个计数器,然后是3个随机数IV(如下);
| 0x61707865 | 0x3320646e | 0x79622d32 | 0x6b206574 |
| KEY | KEY | KEY | KEY |
| KEY | KEY | KEY | KEY |
| COUNTER | NONCE | NONCE | NONCE |
第二部分主要是定义一个QR操作,它可以对四个变量间进行循环变换,这个计算结构也称为ARX(Addtion相加,ROTL偏置,X异或,都是高效的CPU操作);
// ROTL in 32
ROTL = (a,b) => (a << b) | (a >> (32 - b));
// QR操作
QR = (a,b,c,d)=> {
b ^= ROTL(a + d, 7),
c ^= ROTL(b + a, 9),
d ^= ROTL(c + b, 13),
a ^= ROTL(d + c, 18)
}
第三个部分,对一个4x4的数据块进行20轮计算,在奇数轮,对列进行QR变换,在偶数轮,对对角线和偏移对角线进行QR变换。最后将结果和初始数据进行对位相加作为本次块运算的结果。
// 输入初始化
for (i = 0; i < 16; ++i) x[i] = in[i];
// 10 loops × 2 rounds/loop = 20 rounds
for (i = 0; i < ROUNDS; i += 2) {
// Odd round
QR(x[0], x[4], x[ 8], x[12]); // column 0
QR(x[1], x[5], x[ 9], x[13]); // column 1
QR(x[2], x[6], x[10], x[14]); // column 2
QR(x[3], x[7], x[11], x[15]); // column 3
// Even round
QR(x[0], x[5], x[10], x[15]); // diagonal 1 (main diagonal)
QR(x[1], x[6], x[11], x[12]); // diagonal 2
QR(x[2], x[7], x[ 8], x[13]); // diagonal 3
QR(x[3], x[4], x[ 9], x[14]); // diagonal 4
}
for (i = 0; i < 16; ++i) out[i] = x[i] + in[i];
性能
有一个表,可以很容易让我们理解chacha和AES之间的性能差异:
简单而言,纯软件性能,双方会有3倍左右的差距。要发挥AES的性能,必须要指令集和硬件的支持。而chacha对环境和硬件的要求就宽松许多,这就是为什么google喜欢chacha的原因。
Poly1305的实现比较复杂,对于应用和理解的关系不大,就不在这里展示了。
安全
网络上,有一篇分析报告,分析了chacha的安全性和相关的问题。
www.cryptrec.go.jp/exreport/cr…
此文针对ChaCha的密码进行了已知的分析和攻击,简单的结论如下:
| 算法 | 攻击方式 | 结论 |
|---|---|---|
| ChaCha | 差分-旋转密码分析 | 未找到攻击 |
| ChaCha | 差分-回旋攻击 | 未找到攻击 |
| ChaCha | 线性密码分析 | 未找到攻击 |
| ChaCha | 区分攻击 | 未找到攻击 |
| ChaCha | 时间-内存-数据折衷 | 在实际中受保护 |
| ChaCha | 代数攻击 | 未找到攻击 |
| ChaCha | 初始化过程攻击 | 未找到攻击 |
| ChaCha | 侧信道-单电源分析 | 在实际中受保护 |
| ChaCha | 侧信道-差分电源分析 | 在实际中受保护 |
| ChaCha | 侧信道-缓存定时攻击 | 未找到攻击 |
| ChaCha | 侧信道-故障注入分析 | 在实际中受保护 |
| Poly1305 | N/A | 可证明安全 |
| ChaCha20-Poly1305 | N/A | 可证明安全 |
最后的结论是:
在ChaCha20-Poly1305 AEAD中几乎找不到任何弱点
名字和由来
chacha算法,应该是由另一个流加密算法salsa发展而来。这些名字都取得非常生活化。关于这些名字的由来,笔者没有看到权威的说法,可能就是约定俗成而已。笔者猜想,salsa是一种调料酱,可以勉强跟流加密技术联系起来。而chacha最为人所知,是一种舞蹈-恰恰,好像也非常符合这个算法快速灵动的调性。 20就非常简单了,这个操作需要计算10轮,每轮两种变换操作,总共20。
Poly的意思也比较费解,但经过研究,这样的说法应该是最合理的。Poly是polinomial(多项式),这个多项式由认证码计算得到,而1305是指计算过程中,需要对一个质数求余,这个质数是:
2^130-5
附-NIST的算法黑名单
我们大多都听说或者了解NIST建议的密码学套件和算法,但很少了解到,在历史和技术发展过程中,其实也是有黑名单的。很大程度上并不是说这些技术有安全缺陷,而是随着技术的发展,不再被建议使用了。
有一个RFC 7540就是这样一个文件,包含了一个黑名单,我们来简单了解一下里面的内容:
TLS_NULL_WITH_NULL_NULL
TLS_RSA_WITH_NULL_MD5
TLS_RSA_WITH_NULL_SHA
TLS_RSA_EXPORT_WITH_RC4_40_MD5
TLS_RSA_WITH_RC4_128_MD5
TLS_RSA_WITH_RC4_128_SHA
....
简单而言,就是: MD5不要用,RC4不要用,DES不要用...