自从IETF的CFRG决定推荐OPAQUE作为下一代密码认证密钥交换(OPAQUE)作为下一代密码认证密钥交换以来,密码学社区中关于提交认证加密(学术上更倾向于称为随机密钥健壮性)的议论很多,因为OPAQUE需要RKR安全的AE方案。
随机密钥稳健性是某些对称加密模式具有的特性,其中如果两个接收者使用不同的密钥,则将有效的(密文、标签)对解密为两个不同的明文是不可行的。
在CFRG的讨论之后,立即发现AES-GCM不符合这一要求。
目前还不清楚的是,AES-GCM-SIV也有不足之处。但不要相信我的话,苏菲·施密格在链接的帖子中算出了数学,我强烈推荐阅读这篇文章。
这并不是说AES-GCM或AES-GCM-SIV注定要失败,或者应该被弃用。在使用这两种算法的大多数地方,您甚至可能都不关心随机密钥的健壮性!但是,如果你在做一些RKR真正重要的事情,你可能会非常在意。这肯定违反了最不令人惊讶的原则。
ChaCha20-Poly1305在这里也不能拯救您,因为这是基于密码散列函数(例如HMAC)的消息身份验证码提供的属性,而是多项式MAC(GMAC、Poly1305等)。不要这样做。
那么,如果每个标准化的、得到广泛支持的AAD构建都不能提供RKR安全性,那么软件工程师该怎么办呢?使用他们自己的密码?!
如果您始终使用定义良好的标准威胁模型(即可通过TLS 1.3访问的客户端-服务器应用程序,可能在服务器端存储哈希密码),那么滚动您自己的加密不仅是危险的,而且完全没有必要。
需要随机密钥稳健性的系统不属于AEAD密码构造的标准威胁模型。
然而,RKR远不是应用程序开发人员发现自己没有明确解决方案的唯一场景。另一个经常出现的例子是:
我需要加密关系数据库中的数据,但仍以某种方式在SELECT查询中使用它。
-太多该死的开发人员没有听说过CipherSweet。
您应该做的第一件事是清楚地记录您的需求以及您的系统必须防范的攻击。您的模型中任何未定义的攻击向量都应被假定为您的设计中的漏洞。(这很快就会进入未知的未知领域。)。
然后,您应该让密码学家审查您的设计,然后让密码学工程师为您构建它。
与其对理智合理的建议避而不谈,不如让我们实实在在地走一遍这个过程。在这篇文章的最后,我将分享我为这篇博客文章炮制的一个玩具例子。
为什么AES-GCM等不提供随机密钥健壮性?因为它们是用通用散列函数而不是加密散列函数构建的。
密码散列函数具有不同的属性(即抗前像和抗冲突),这使得在两个不同密钥下计算两个不同的认证标签非常困难。以这种方式攻击HMAC-SHA-256的代价与暴力强行使用128位AES密钥的代价差不多。(祝你好运!)。
然而,加密散列函数比多项式MAC慢得多,在类似HMAC的结构中使用它们几乎会使速度翻一番。
您可能会忍不住只将它们的键和消息散列在一起以节省CPU周期,但这对于几乎每个人都使用的散列函数来说实际上是不安全的(由于长度扩展攻击)。
从逻辑上说,如果我们有一个基于散列函数的AEAD密码构造,我们可以具有RKR安全性。
在AES-GCM和ChaCha20-Poly1305出现之前,到处都在使用基于AES-CBC和HMAC的特别结构。(在那个时代,每个人都使用HMAC-SHA1,但不要这么做。)。
但是,我们不希望在现代系统中重新引入临时CBC+HMAC的许多问题:
如果您忘记在HMAC标记中包括初始化向量,就可以让攻击者自由控制解密明文的前16个字节,而不必破坏MAC。
CBC+HMAC通常在应用层代码中实现,但这种结构的安全性在很大程度上取决于恒定时间函数的可用性和利用率。
CBC+HMAC没有标准格式,也没有输入MAC的操作顺序。
CBC+HMAC仅为AE模式,没有额外认证数据的空间。如果您试图天真地将额外数据添加到HMAC中,那么现在您必须担心规范化攻击!
CBC模式需要填充(通常是PKCS#7填充),而基于CTR的密码模式则不需要。
这是密码学家在过去十年(或更长时间)推动开发人员转向EAD模式的一长串原因之一。
到目前为止,应该清楚的是,如果我们具有加密然后MAC结构,其中MAC基于密码散列函数(例如SHA-256),则我们可能能够获得RKR安全性。
我们仍然需要定义将密钥(K)分成两个不同密钥(K1、K2)的某种方式。毕竟,您永远不会想要将一个密钥用于多个目的。
您的加密密钥和身份验证密钥应该不同,但它们也应该派生自相同的输入密钥!这主要是为了保护实现者免受拥有独立密钥和意外创建伪造漏洞的影响。
就用SHA-512(K),然后把它切成两半。一半用于加密,另一半用于身份验证。
使用HMAC-SHA256(k,c1)和HMAC-SHA256(k,c2),其中c1和c2是不同的(但任意的)常量。
使用HKDF。这适用于任何安全的加密散列函数,并且是专门为这样的情况设计的。HKDF还支持盐化,这可以用来随机化我们的派生密钥。
我们真的可以选择这三个中的任何一个,而且很安全,但我建议不要选择第一个选项。HKDF在幕后使用HMAC,因此后一种选择都可以。
BLAKE3宣传的6.8 GiB/s甚至可以比Poly1305或GHash更快(BLAKE3的速度真的能在长消息中大放异彩,因为它通过Merkle树实现了极高的并行性)。
我不是AES的粉丝,这已经不是什么秘密了。困扰我的不是AES的数学特性,而是128位的块大小和软件实现必须在速度和安全之间做出选择的事实。
ChaCha的256位安全级别更容易调整:底层PRF状态是512位(这意味着大约256位安全级别),密钥始终是256位。
此外,如果您在同一代码库中构建ChaCha和BLAKE3,则可以重用一些组件(即压缩函数G)。如果您试图发布少量代码(例如嵌入式系统),这是非常理想的。
标准的ead模式最大的问题之一是当你重复使用一个nonce时,它们会辉煌地爆炸。有两种方法可以走出这种危险:
使用大型随机数(例如,XChaCha20使用192位随机数)并随机生成它们,因此随机数重用的概率可以忽略不计。
由于我们已经计划使用散列函数来派生子密钥(一个用于加密,一个用于身份验证),因此接受比流密码要求更长的随机数也是有意义的。多余的字节可以传递给我们的KDF,而不会有重大风险或额外开销。
由于IETF的ChaCha20变体要求96位随机数,因此将我们的构造设计为支持256位随机数意味着我们可以将随机数的前160位传递给KDF,将后96位传递给流密码。您可以预期在2^80次加密之后会发生一次KDF冲突,但几乎可以肯定的是,使用不同的随机数会发生冲突。
我们希望确保攻击者不可能将两个不同的(密文,AAD)对输入到我们的构造中,而这两个对会产生相同的标记。
简单地将这两个值连接起来就有可能会有人删掉一大块密文并将其存储在AAD中。
最简单的解决方案是预先添加或附加组件的长度(作为64位无符号整数的小端八位字节字符串表示)。
在前置和附加之间的选择不会对安全性产生太大影响,但是附加长度对于流接口来说更友好。
毕竟,在流式加密/解密接口中,在完成所有数据的加密和身份验证之前,您可能不知道这两个组件的长度。
现在我们已经大致浏览了所需设计属性的粗略列表,让我们回顾一下:
我们希望接受256位随机数(160位用于KDF,96位用于流密码)。
我们希望确保我们的ChaCha20和BLAKE3-MAC密钥使用一些域分离常量从相同的输入密钥派生。
听起来挺多的。但是,就代码大小而言,它会产生什么结果呢?令人惊讶的是非常少!
我的意思是,如果你的密码学建议和实现是从一个毛茸茸的博客作者那里得到的,你的用户真的会感到安全吗?
这只是我放在一起的一个玩具示例,目的是为了说明如何提出新的密码设计。这只是第一步。
最终,您不应该使用它,原因很简单:我的设计和实现都没有经过同行评审。
也许我会稍微改进一下,然后把它踢给CFRG,让他们考虑有朝一日加入不透明的名单。这可能会是一个很好的设计。它可能容易受到某种微妙的攻击,我现在甚至无法想象。
除非专家另有说明,否则它是危险物质,你只能出于教育目的去玩它。
我写了很多关于密码学的文章,而且要写的主题总是比我有时间和精力去涵盖的多,所以这里有几个很酷的博客/等等,可以在我吃力地读完粗略的草稿地狱时查看一下。
防弹TLS时事通讯-由自由撰稿人、IT安全专家和AES-GCM开发者Hanno Böck主持的时事通讯。
关键材料-索菲·施密格(Google的密码学家)和Sarai Rosenberg(Pager Duty的安全工程师)的新博客。
我脑海中的小人-Scott Contini的博客,他是一位安全专家,经常在/r/crypto上发布有用的评论。
标题艺术由Khia创作。背景中的图来自本文关于通过提交身份验证加密进行消息加盖印记的内容。