libsodium 密码学库首曝安全漏洞:CVE-2025-69277 技术深度分析
漏洞概述
libsodium,这个致力于简化密码学应用、拥有13年辉煌历史且此前从未记录过CVE漏洞的知名密码学库,在2025年底曝出了其首个安全漏洞,编号为 CVE-2025-69277。该漏洞影响库中的低阶函数 crypto_core_ed25519_is_valid_point()。
该函数本应严格验证一个给定的椭圆曲线点是否属于爱德华兹曲线Ed25519上的主密码学子群(prime-order subgroup)。然而,由于验证逻辑不完整,在某些特定条件下,该函数会错误地将一些不属于主群的“非法”点判定为有效。
根据MITRE的评估,该漏洞的CVSS 3.1评分为 4.5(中危),攻击向量本地化,攻击复杂度较高。其根本原因被归类为 CWE-184:不完整的禁止输入列表。
受影响范围与状态
- 受影响的版本:所有在 2025年12月30日之前发布 的 libsodium 版本均包含此缺陷。具体包括官方tarball、Visual Studio和MingW二进制文件、NuGet包、swift-sodium框架、rustlibsodium-sys-stable绑定以及libsodium.js。
- 受影响的衍生库:此漏洞通过依赖关系影响了多个生态中的包,包括:
- PyPI:
PyNaCl(< 1.6.2),hdwallet(< 3.6.1) - Composer:
paragonie/sodium_compat(2.x系列 < 2.5.0; 1.x系列 < 1.24.0)
- PyPI:
- 修复状态:漏洞在发现后已被立即修复。所有在2025年12月30日之后发布的稳定包均已包含修复补丁。主流Linux发行版(如Debian)也已发布安全更新。
技术背景:Ed25519与子群
要理解此漏洞,需要一些椭圆曲线密码学的背景知识。Edwards25519(简称Ed25519)曲线被广泛用于数字签名(如Ed25519签名方案)。
这条曲线上的点构成了多个具有不同“阶”(order,可理解为点的数量)的循环子群:
- 阶为L的主子群:这是密码学操作(如签名和密钥交换)实际发生的安全区域。
L是一个约2^252的大素数。 - 小阶子群:例如阶为1、2、4、8的子群。
- 混合阶子群:例如阶为2L、4L、8L的子群。
安全的协议必须确保所有运算都在主子群中进行。攻击者如果能够将一个小阶点或混合阶点注入运算,可能破坏协议的安全假设,导致潜在的私钥泄露或签名伪造(尽管对于Ed25519签名本身,由于设计,此类攻击通常难以直接利用)。
函数 crypto_core_ed25519_is_valid_point() 的设计目的正是为了过滤掉那些不在主子群中的点。
漏洞技术细节
1. 正确的验证原理
验证一个点 P 是否在阶为 L 的主子群上的标准数学方法是:计算 [L] * P(即点 P 与标量 L 的标量乘法)。如果 P 的阶确实是 L,那么 [L] * P 的结果应该是无穷远点(或称恒等点、零元)。
在libsodium的内部实现中,椭圆曲线点使用扩展坐标 (X, Y, Z) 表示。无穷远点在该坐标系下的表示为:X坐标为0,且Y坐标等于Z坐标(即 X = 0 且 Y = Z)。Z 可能因之前的运算而异,不一定为1。
2. 有缺陷的代码
漏洞出现前的旧验证代码仅检查了乘法结果点的 X 坐标是否为0:
|
|
它遗漏了检查 Y == Z 这一关键条件。这意味着,如果一个点经过 [L] 乘法后,其 X 坐标恰好为0,但 Y 坐标不等于 Z 坐标,它仍然会被错误地接受。
3. 如何构造一个能“骗过”旧检查的非法点?
从任何一個主子群上的点 Q(例如通过 crypto_core_ed25519_random() 生成)出发,将其与一个阶为2的点 (0, -1) 相加(或等效地,同时取反其X和Y坐标)。得到的点 Q' = Q + (0, -1) 不属于主子群,但它经过 [L] 乘法后,其 X 坐标恰好为0,而 Y 坐标不等于 Z 坐标。这个点 Q' 就能通过有缺陷的验证。
修复方案
修复非常简单直接,就是补上缺失的检查:
|
|
现在,该函数会严格验证 X = 0 且 Y = Z 两个条件,确保结果确实是无穷远点。
实际影响与建议
谁受影响?
绝大多数libsodium用户不受影响。
该漏洞仅影响直接调用低阶函数 crypto_core_ed25519_is_valid_point() 并用于验证来自不可信来源的点的应用程序。libsodium的所有高级API(如 crypto_sign_* 系列签名函数)均不受影响,因为它们根本不使用这个有问题的函数。
受影响的应用场景主要是那些使用libsodium作为算法工具箱、自行构建定制化密码学协议的开发者。
给开发者的建议
- 立即升级:将libsodium升级至2025年12月30日之后发布的任何版本。
- 优先使用Ristretto255:如果您的自定义协议需要在曲线上进行群运算,强烈建议使用libsodium自2019年起支持的 Ristretto255 群。Ristretto255抽象掉了复杂的子群问题,只要一个点能成功解码,它就保证在安全的素数阶群上,无需额外验证。
- 临时缓解措施:如果无法立即升级,可以在应用层实现以下替代验证函数:
1 2 3 4 5 6 7 8 9 10 11 12 13int is_on_main_subgroup(const unsigned char p[crypto_core_ed25519_BYTES]) { /* l - 1 (群阶减1) */ static const unsigned char L_1[crypto_core_ed25519_SCALARBYTES] = { ... }; /* 恒等点编码: (x=0, y=1) */ static const unsigned char ID[crypto_core_ed25519_BYTES] = { ... }; unsigned char t[crypto_core_ed25519_BYTES]; unsigned char r[crypto_core_ed25519_BYTES]; if (crypto_scalarmult_ed25519_noclamp(t, L_1, p) != 0 || crypto_core_ed25519_add(r, t, p) != 0) { return 0; } return sodium_memcmp(r, ID, sizeof ID) == 0; }
关于维护与可持续性的思考
此漏洞的披露也引发了社区对关键开源基础设施可持续性的讨论。libsodium主要由创始人Frank Denis一人维护,是一项无偿且耗时的工作。他在公告中呼吁,如果项目对您有用,请考虑通过 Open Collective 进行赞助,以帮助他投入更多时间维护项目,确保其长期健康发展。
许多企业虽认可其价值,但现有的公司捐赠流程往往难以适配这类非标准化的个人赞助模式,这构成了开源可持续发展中的一个普遍挑战。