使用Wycheproof发现elliptic加密库中的密码学漏洞

本文详细介绍了在广泛使用的JavaScript椭圆曲线密码库elliptic中发现的两个安全漏洞,包括EdDSA签名可塑性和ECDSA签名验证错误,这些漏洞每周影响超过1000万次下载和近3000个项目。

使用Wycheproof发现elliptic加密库中的密码学漏洞

Trail of Bits公开披露了elliptic(一个广泛使用的JavaScript椭圆曲线密码库)中的两个漏洞。该库每周下载量超过1000万次,被近3000个项目使用。这些漏洞由于缺少模约减和长度检查,可能分别允许攻击者伪造签名或阻止有效签名验证。

其中一个漏洞在2024年10月结束的90天披露窗口后仍未修复。截至本文发布时,该漏洞仍未解决。

我使用Wycheproof(一个包含测试向量的集合,旨在针对已知漏洞测试各种加密算法)发现了这些漏洞。在这篇博客文章中,我将描述如何使用Wycheproof测试elliptic库,发现的漏洞如何工作,以及它们如何实现签名伪造或阻止签名验证。

方法论

在Trail of Bits实习期间,我为《测试手册》的新密码测试章节撰写了关于使用Wycheproof的详细指南。我决定使用elliptic库作为该指南的真实案例研究,这使我能够发现相关漏洞。

我按照指南中的描述为elliptic包编写了Wycheproof测试工具。然后,我分析了Wycheproof提供的各种失败测试用例所覆盖的源代码,将它们分类为误报或真实发现。在理解这些测试用例失败的原因后,我为每个错误编写了概念验证代码。确认它们是真实发现后,我开始了协调披露过程。

发现

我总共识别了五个漏洞,导致了五个CVE。其中三个漏洞是次要的解析问题。我通过针对存储库的公开拉取请求披露了这些问题,并随后请求了CVE ID以跟踪它们。

其中两个问题更为严重。我使用GitHub咨询功能私下披露了它们。以下是这些漏洞的一些详细信息。

CVE-2024-48949:EdDSA签名可塑性

此问题源于缺少越界检查,这在NIST FIPS 186-5第7.8.2节"HashEdDSA签名验证"中有规定:

将签名的前半部分解码为点R,将签名的后半部分解码为整数s。验证整数s在0 ≤ s < n的范围内。

在elliptic库中,从未执行检查s是否在0 ≤ s < n范围内以验证它不超过生成点阶n。此漏洞允许攻击者伪造新的有效签名sig’,但仅适用于已知的签名和消息对(msg, sig)。

1
2
3
Signature=(𝑚𝑠𝑔,𝑠𝑖𝑔)
𝑠𝑖𝑔=(𝑅||𝑠)
𝑠′ mod 𝑛==𝑠

需要实施以下检查以防止此伪造攻击:

1
2
3
if (sig.S().gte(sig.eddsa.curve.n)) {
    return false;
}

伪造的签名可能会破坏协议的共识。一些协议会正确拒绝伪造的签名消息对为无效,而elliptic库的用户会接受它们。

CVE-2024-48948:带前导零的哈希上的ECDSA签名验证错误

第二个问题涉及ECDSA实现:有效签名可能无法通过验证检查。

以下是失败的Wycheproof测试用例:

1
2
[testvectors_v1/ecdsa_secp192r1_sha256_test.json][tc296] special case hash
[testvectors_v1/ecdsa_secp224r1_sha256_test.json][tc296] special case hash

两个测试用例都失败,因为特定制作的哈希包含四个前导零字节,由哈希十六进制字符串343236343739373234使用SHA-256产生:

1
00000000690ed426ccf17803ebe2bd0884bcd58a1bb5e7477ead3645f356e7a9

我们将使用secp192r1曲线测试用例来说明签名验证失败的原因。

负责验证椭圆曲线签名的函数位于lib/elliptic/ec/index.js

1
2
3
4
EC.prototype.verify = function verify(msg, signature, key, enc) {
  msg = this._truncateToN(new BN(msg, 16));
  ...
}

在传递给验证函数调用之前,消息必须被哈希,这发生在elliptic库外部。根据FIPS 186-5第6.4.2节"ECDSA签名验证算法",消息的哈希必须根据椭圆曲线基点的阶n进行调整:

如果log2(n) ≥ hashlen,设置E = H。否则,设置E等于H的最左log2(n)位。

为了实现这一点,调用了_truncateToN函数,该函数执行必要的调整。在调用此函数之前,哈希后的消息msg使用new BN(msg, 16)从十六进制字符串或数组转换为数字对象。

1
2
3
4
5
6
EC.prototype._truncateToN = function _truncateToN(msg, truncOnly) {
  var delta = msg.byteLength() * 8 - this.n.bitLength();
  if (delta > 0)
    msg = msg.ushrn(delta);
  ...
};

delta变量计算哈希大小与曲线当前生成器阶n之间的差异。如果msg占用的位数比n多,则按差异移位。对于此特定测试用例,我们使用secp192r1(使用192位)和SHA-256(使用256位)。哈希应向右移位64位以保留最左192位。

elliptic库中的问题出现是因为new BN(msg, 16)转换移除了前导零,导致较小的哈希占用更少的字节。

1
690ed426ccf17803ebe2bd0884bcd58a1bb5e7477ead3645f356e7a9

在delta计算期间,msg.byteLength()然后返回28字节而不是32。

1
2
3
4
EC.prototype._truncateToN = function _truncateToN(msg, truncOnly) {
  var delta = msg.byteLength() * 8 - this.n.bitLength();
  ...
};

此错误计算导致不正确的delta为32 = (288 - 192)而不是64 = (328 - 192)。因此,哈希后的消息未正确移位,导致验证失败。如果消息哈希包含足够的前导零,此问题会导致有效签名被拒绝,概率为2^-32。

要修复此问题,应向验证函数添加一个额外参数以允许解析哈希大小:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
EC.prototype.verify = function verify(msg, signature, key, enc, msgSize) {
  msg = this._truncateToN(new BN(msg, 16), undefined, msgSize);
  ...
}

EC.prototype._truncateToN = function _truncateToN(msg, truncOnly, msgSize) {
  var size = (typeof msgSize === 'undefined') ? (msg.byteLength() * 8) : msgSize;
  var delta = size - this.n.bitLength();
  ...
};

持续测试的重要性

这些漏洞作为一个例子,说明了为什么持续测试对于确保广泛使用的密码工具的安全性和正确性至关重要。特别是,Wycheproof和其他积极维护的密码测试向量集是确保高质量密码库的优秀工具。我们建议在CI/CD管道中包含这些测试向量(以及任何其他相关的测试向量),以便在每次代码更改时重新运行它们。这将确保您的库现在和将来都能抵御这些特定的密码问题。

协调披露时间线

对于披露过程,我们使用GitHub的集成安全咨询功能私下披露漏洞,并使用报告模板作为报告结构的模板。

2024年7月9日:我们在对elliptic库运行Wycheproof期间发现了失败的测试向量。

2024年7月10日:我们确认ECDSA和EdDSA模块都有问题,并编写了概念验证脚本和修复方案。

对于CVE-2024-48949

2024年7月16日:我们使用GitHub安全咨询功能向elliptic库维护者披露了EdDSA签名可塑性问题,并创建了一个包含我们建议修复的私有拉取请求。

2024年7月16日:elliptic库维护者确认了EdDSA问题的存在,合并了我们建议的修复,并创建了一个新版本,未公开披露该问题。

2024年10月10日:我们向MITRE请求了CVE ID。

2024年10月15日:由于自我们私下披露以来已过去90天,此漏洞变为公开。

对于CVE-2024-48948

2024年7月17日:我们使用GitHub安全咨询功能向elliptic库维护者披露了ECDSA签名验证问题,并创建了一个包含我们建议修复的私有拉取请求。

2024年7月23日:我们联系以向ECDSA GitHub咨询添加额外的协作者,但未收到回复。

2024年8月5日:我们联系以确认ECDSA问题,并再次请求向GitHub咨询添加额外的协作者。我们未收到回复。

2024年8月14日:我们再次联系以确认ECDSA问题,并再次请求向GitHub咨询添加额外的协作者。我们未收到回复。

2024年10月10日:我们向MITRE请求了CVE ID。

2024年10月13日:Wycheproof测试开发人员Daniel Bleichenbacher独立发现并披露了问题#321,这与本发现相关。

2024年10月15日:由于自我们私下披露以来已过去90天,此漏洞变为公开。

comments powered by Disqus
使用 Hugo 构建
主题 StackJimmy 设计