在前一篇文章中,我们介绍了 Diffie-Hellman 密钥交换协议,它的安全性根植于有限域上离散对数问题的困难性。然而,随着计算能力的增长和攻击算法的进步,基于有限域乘法群的方案在密钥长度方面承受着越来越大的压力。椭圆曲线密码学(Elliptic Curve Cryptography,ECC)的出现,为公钥密码体制提供了一条更优雅的路径:在更短的密钥长度下获得同等甚至更高的安全强度。本文将从实数域上的几何图像出发,逐步将读者带入有限域上的离散点群世界,最终深入到曲线选择、标量乘法优化与现代安全曲线标准的核心议题。
一、为什么需要椭圆曲线
自 1977 年 RSA 体制问世以来,公钥密码学的安全性长期依赖于大整数分解问题(Integer Factorization Problem)和有限域上的离散对数问题(Discrete Logarithm Problem,DLP)。这两个问题的困难性虽然经受住了数十年的密码分析考验,但它们面临一个共同的困境——随着攻击算法(尤其是数域筛法,Number Field Sieve)的改进,维持同等安全强度所需的密钥长度正在加速增长。
具体而言,RSA 在 2000 年前后推荐使用 1024 位密钥,而到了 2020 年代,业界普遍要求至少 2048 位,若要保障至 2030 年之后的安全性,则需要 3072 位乃至 4096 位的密钥。密钥长度的增长不仅意味着存储开销的增加,更意味着模幂运算(modular exponentiation)的计算量急剧攀升——RSA 加密与签名的时间复杂度大致与密钥位数的三次方成正比。
1985 年,Neal Koblitz 和 Victor Miller 各自独立提出了基于椭圆曲线的密码体制。他们观察到,椭圆曲线上的离散对数问题(Elliptic Curve Discrete Logarithm Problem,ECDLP)不存在类似数域筛法的亚指数时间算法,目前已知的最优攻击仍停留在完全指数时间级别。这意味着,同样的安全强度只需要更短的密钥。下面是一组广泛引用的等价安全强度对照:
| 对称密钥长度 | RSA 密钥长度 | ECC 密钥长度 |
|---|---|---|
| 80 位 | 1024 位 | 160 位 |
| 112 位 | 2048 位 | 224 位 |
| 128 位 | 3072 位 | 256 位 |
| 192 位 | 7680 位 | 384 位 |
| 256 位 | 15360 位 | 512 位 |
从表中可以清楚地看到,ECC 的密钥长度大约是对称密钥长度的两倍,而 RSA 的密钥长度增长速度远快于此。当安全强度要求达到 256 位时,RSA 需要超过 15000 位的密钥,这在实际部署中几乎不可接受。而 ECC 只需 512 位的密钥,无论是在存储、传输还是计算开销上都具有压倒性的优势。
这种效率优势使得 ECC 在资源受限的场景中尤其受青睐——嵌入式设备、智能卡、物联网(IoT)终端以及移动通信协议(如 TLS 1.3)中,ECC 已成为事实上的标准选择。
二、实数域上的椭圆曲线
要理解椭圆曲线密码学的代数结构,最自然的起点是在实数域 ℝ 上观察这些曲线的几何形态。
一条椭圆曲线(elliptic curve)在 Weierstrass 标准形式(Weierstrass normal form)下可以表示为:
\[y^2 = x^3 + ax + b\]
其中 \(a, b \in \mathbb{R}\),并且判别式 \(\Delta = -16(4a^3 + 27b^2) \neq 0\),以保证曲线没有奇异点(singular point),即没有尖点或自交点。
这里有一个常见的误解需要澄清:椭圆曲线与几何学中的椭圆(ellipse)没有直接关系。“椭圆曲线”这一名称源于计算椭圆弧长时出现的椭圆积分(elliptic integral),是一个历史遗留的术语。
在实数平面上,Weierstrass 曲线关于 x 轴对称。根据参数 \(a\) 和 \(b\) 的取值不同,曲线可能呈现为一个连通分支,也可能呈现为一个主分支加上一个独立的卵形分支。
弦切法则(chord-and-tangent rule) 定义了椭圆曲线上的点加法运算。给定曲线上两个不同的点 \(P\) 和 \(Q\),过这两点作一条直线(弦,chord),该直线与曲线的第三个交点为 \(R'\),然后将 \(R'\) 关于 x 轴取对称点,所得到的点 \(R\) 就定义为 \(P + Q\)。
当 \(P = Q\) 时,弦退化为曲线在 \(P\) 点的切线(tangent line),用同样的方式得到 \(2P\),即点的倍乘(point doubling)。
为了使这个加法运算构成一个完整的群(group),我们还需要引入一个特殊的元素——无穷远点(point at infinity),记为 \(\mathcal{O}\)。直观地说,\(\mathcal{O}\) 是所有垂直直线在”无穷远处”的交点。它扮演群的单位元(identity element)的角色:对于曲线上任意一点 \(P\),都有 \(P + \mathcal{O} = P\)。
同时,每个点 \(P = (x, y)\) 的逆元(inverse)就是它关于 x 轴的对称点 \(-P = (x, -y)\)。可以验证 \(P + (-P) = \mathcal{O}\),因为过 \(P\) 和 \(-P\) 的直线是垂直线,与曲线”交于”无穷远点。
这样,椭圆曲线上的点集(连同无穷远点)在上述加法运算下构成一个阿贝尔群(Abelian group),即满足封闭性、结合律、存在单位元、存在逆元以及交换律。这个群结构是整个椭圆曲线密码学的代数基础。
三、有限域上的椭圆曲线
实数域上的几何直觉虽然优美,但密码学需要的是精确的离散运算——我们不能依赖浮点数的近似计算来执行加密协议。因此,密码学中的椭圆曲线定义在有限域(finite field)上。
最常用的有限域是素数域 \(\mathrm{GF}(p)\)(又写作 \(\mathbb{F}_p\)),其中 \(p\) 是一个大素数。曲线方程仍然是 \(y^2 \equiv x^3 + ax + b \pmod{p}\),但所有运算都在模 \(p\) 的意义下进行。曲线上的”点”不再是平面上的连续轨迹,而是满足方程的一组离散的坐标对 \((x, y)\),其中 \(x, y \in \{0, 1, \ldots, p-1\}\)。
实数域上的弦切法则在有限域上翻译为纯代数公式。给定曲线 \(E: y^2 = x^3 + ax + b\) 上的两个点 \(P = (x_1, y_1)\) 和 \(Q = (x_2, y_2)\),它们的和 \(R = P + Q = (x_3, y_3)\) 按如下公式计算:
点加法(\(P \neq Q\)):
\[\lambda = \frac{y_2 - y_1}{x_2 - x_1} \pmod{p}\]
\[x_3 = \lambda^2 - x_1 - x_2 \pmod{p}\]
\[y_3 = \lambda(x_1 - x_3) - y_1 \pmod{p}\]
点倍乘(\(P = Q\)):
\[\lambda = \frac{3x_1^2 + a}{2y_1} \pmod{p}\]
\[x_3 = \lambda^2 - 2x_1 \pmod{p}\]
\[y_3 = \lambda(x_1 - x_3) - y_1 \pmod{p}\]
这里的除法是模逆运算(modular inverse),可以通过扩展欧几里得算法(Extended Euclidean Algorithm)高效计算。
一个自然的问题是:有限域上的椭圆曲线到底包含多少个点?Hasse 定理(Hasse’s theorem)给出了精确的界:
\[|\\#E(\mathbb{F}_p) - (p + 1)| \leq 2\sqrt{p}\]
也就是说,曲线上的点数 \(\\#E(\mathbb{F}_p)\)(包括无穷远点 \(\mathcal{O}\))大约等于 \(p + 1\),偏差不超过 \(2\sqrt{p}\)。这个结果告诉我们,曲线的阶(group order)与素数 \(p\) 处于同一数量级。
在密码学应用中,我们通常希望曲线的阶含有一个大素数因子 \(n\),这样可以选取一个 \(n\) 阶子群来工作。如果曲线的阶本身就是素数,那就更理想——整个群都可以直接使用,不必担心小子群攻击(small subgroup attack)。曲线阶的精确计算可以使用 Schoof 算法或其改进版本 SEA(Schoof-Elkies-Atkin)算法,这些算法可以在多项式时间内求解。
四、ECDLP
椭圆曲线密码学的安全性建立在椭圆曲线离散对数问题(Elliptic Curve Discrete Logarithm Problem,ECDLP)之上。该问题可以简洁地表述为:
给定椭圆曲线上的两个点 \(P\) 和 \(Q = kP\)(即 \(P\) 与自身相加 \(k\) 次的结果),求整数 \(k\)。
直觉上,标量乘法(scalar multiplication)\(kP\) 的计算——利用后文将介绍的倍加法(double-and-add)——非常高效,只需 \(O(\log k)\) 次群运算。但反过来,从 \(P\) 和 \(Q\) 推出 \(k\) 却被认为是极其困难的。
那么,ECDLP 为什么比有限域上的普通 DLP 更难?关键在于攻击算法的差异。对于 \(\mathbb{F}_p^*\) 上的 DLP,存在亚指数时间的攻击算法——指标演算法(index calculus)利用了有限域的代数结构(光滑数的存在),能够在 \(L_p[1/3, c]\)(亚指数复杂度)时间内求解。然而,目前没有人成功地将指标演算法推广到一般的椭圆曲线群上。
当前针对 ECDLP 的最优通用攻击是 Pollard ρ 算法(Pollard’s rho algorithm)及其并行化版本,时间复杂度为 \(O(\sqrt{n})\),其中 \(n\) 是群的阶。这是一个完全指数时间的算法——如果 \(n\) 约为 \(2^{256}\),攻击者需要大约 \(2^{128}\) 次运算,这恰好对应 128 位的安全强度。
当然,存在一些特殊的曲线类别容易受到特定攻击:
- 超奇异曲线(supersingular curve):可以通过 MOV 攻击(Menezes-Okamoto-Vanstone attack)将 ECDLP 嵌入(embedding)到有限域的乘法群中,从而利用指标演算法。因此,密码学应用中必须避免使用超奇异曲线,除非是在配对密码学(pairing-based cryptography)中有意利用这一性质。
- 异常曲线(anomalous curve):曲线阶 \(\\#E(\mathbb{F}_p) = p\) 的情况,Smart 攻击可以在线性时间内求解 ECDLP。
- 嵌入度过低的曲线:嵌入度(embedding degree)较小时,MOV 攻击同样有效。
排除这些特殊情况后,在精心选择的曲线上,ECDLP 是目前已知最难的离散对数问题之一,这正是 ECC 能够以短密钥实现高安全性的根本原因。
五、曲线选择
椭圆曲线密码学的实际部署离不开具体曲线参数的选择。不同的应用场景催生了不同的标准曲线,它们各有优劣。
NIST P-256(secp256r1) 是美国国家标准与技术研究院(NIST)于 2000 年推荐的标准曲线之一,定义在一个 256 位素数域上。P-256 被广泛部署在 TLS、X.509 证书以及众多政府和企业级系统中。然而,它的参数来源曾引发争议——曲线参数中的种子值未提供充分的生成过程解释,部分密码学家担心这些参数可能经过刻意挑选,以便某些实体能够利用尚未公开的弱点。这种担忧在 2013 年 Snowden 事件后进一步加剧。
secp256k1 是另一条 256 位曲线,其参数采用了 Koblitz 曲线的特殊形式 \(y^2 = x^3 + 7\)(\(a = 0, b = 7\)),由于参数极为简洁,被认为具有更高的参数透明度。secp256k1 因比特币(Bitcoin)的采用而广为人知,也被以太坊(Ethereum)等区块链系统使用。
Curve25519 由 Daniel J. Bernstein 于 2006 年提出,是一条定义在素数 \(p = 2^{255} - 19\) 上的 Montgomery 曲线。它的设计目标是高性能、抗侧信道攻击(side-channel resistance)和参数刚性(rigidity)——每一个参数的选取都有清晰、可审计的理由,不存在隐藏的后门空间。Curve25519 的密钥交换协议 X25519 已成为 TLS 1.3 的默认选项之一。
Ed25519 是 Curve25519 的 twisted Edwards 形式,由 Bernstein 等人提出,专门用于数字签名方案 EdDSA(Edwards-curve Digital Signature Algorithm)。它继承了 Curve25519 的所有安全优势,同时利用 Edwards 曲线的统一加法公式(unified addition formula)获得了更高的实现效率和更简洁的代码结构。
BN256 和 BLS12-381 是配对友好曲线(pairing-friendly curve),它们支持双线性配对(bilinear pairing)操作,使得零知识证明(zero-knowledge proof)、基于身份的加密(Identity-Based Encryption,IBE)以及聚合签名(aggregate signature)等高级密码协议成为可能。BLS12-381 由 Zcash 团队提出,目前在以太坊 2.0 的共识层和多个零知识证明系统中被广泛使用。
在选择曲线时,参数刚性(rigidity)是一个核心关注点。所谓参数刚性,是指曲线的每一个参数都有明确的、不可替代的选取理由——例如,Curve25519 选取素数 \(2^{255} - 19\) 是因为它是小于 \(2^{255}\) 的最大梅森素数(Mersenne-like prime),这个选择没有自由度,不可能在其中隐藏后门。而 NIST P-256 的种子值来源不够透明,无法排除精心构造的可能性。
笔者认为,NIST P-256 与 Curve25519 之间的选择,折射出密码学工程中一种更深层的张力:机构信任(institutional trust)与可验证安全(verifiable security)之间的博弈。P-256 的安全性论证依赖于「你信任 NIST 不会在参数中植入后门」这一前提——这本质上是一种社会性的信任。而 Curve25519 的安全性论证是自证的:每一个参数的选取都附带了完整的推导过程,任何人都可以独立验证这些参数确实是在给定约束下的唯一选择或最优选择。2013 年 Snowden 事件之后,密码学社区逐渐形成了一个共识:在数学安全性等价的情况下,应当优先选择参数来源可验证的方案。这不仅是技术判断,更是一种工程哲学——密码系统的安全性不应依赖于对任何单一机构的信任。这一原则如今已深刻影响了后续所有新曲线标准的设计过程:从 Ed448 到 BLS12-381,参数的可审计性已成为不可或缺的基本要求。
六、Montgomery 曲线
Montgomery 曲线由 Peter Montgomery 于 1987 年提出,其标准形式为:
\[By^2 = x^3 + Ax^2 + x\]
其中 \(B(A^2 - 4) \neq 0\)。
Montgomery 曲线最显著的特征是支持高效的 Montgomery 梯形算法(Montgomery ladder),这是一种仅使用 x 坐标进行标量乘法的方法。在 Montgomery 梯形中,每一步迭代同时维护两个点 \((kP, (k+1)P)\),根据标量的当前比特位是 0 还是 1,执行不同的操作组合——但关键在于,无论比特位是什么,每一步都执行相同数量的加法和倍乘运算。这使得算法具有天然的常量时间(constant-time)特性,能有效抵抗简单功耗分析(Simple Power Analysis,SPA)和计时攻击(timing attack)等侧信道威胁。
Curve25519 是 Montgomery 曲线的典范实例,其方程为:
\[y^2 = x^3 + 486662x^2 + x\]
定义在素数域 \(\mathbb{F}_p\)(\(p = 2^{255} - 19\))上。参数 \(A = 486662\) 的选取是该域上满足安全要求的最小正整数值,这再次体现了参数刚性的设计哲学。
Montgomery 形式的另一个优势是它与 Edwards 形式之间存在双有理等价(birational equivalence)——同一条曲线可以在两种形式之间自由转换,分别利用各自的计算优势。例如,X25519 密钥交换使用 Montgomery 形式来获得 Montgomery 梯形的效率,而 Ed25519 签名则使用 twisted Edwards 形式来获得统一加法公式的便利。
七、Edwards 与 twisted Edwards 曲线
Harold Edwards 在 2007 年提出了一种新的椭圆曲线形式:
\[x^2 + y^2 = 1 + dx^2y^2\]
其中 \(d \neq 0, 1\)。这种形式的革命性意义在于它的加法公式具有统一性(completeness):
\[(x_1, y_1) + (x_2, y_2) = \left(\frac{x_1 y_2 + x_2 y_1}{1 + d x_1 x_2 y_1 y_2}, \frac{y_1 y_2 - x_1 x_2}{1 - d x_1 x_2 y_1 y_2}\right)\]
这个公式的一个惊人特性是:对于非正方(non-square)的 \(d\),它对曲线上的任意两个点(包括 \(P = Q\) 的情况和加上逆元的情况)都成立,不需要任何特殊情况的分支判断。在 Weierstrass 形式下,点加法和点倍乘需要不同的公式,而当两个输入点相同、互逆或其中一个是无穷远点时,还需要额外的条件分支。这些分支不仅增加了代码复杂度,更重要的是引入了侧信道泄漏风险——攻击者可以通过观察程序的执行路径差异来推断秘密标量的比特信息。
twisted Edwards 曲线(twisted Edwards curve)是 Edwards 曲线的推广形式:
\[ax^2 + y^2 = 1 + dx^2y^2\]
其中 \(a \neq d\),\(a, d \neq 0\)。当 \(a = 1\) 时退化为标准 Edwards 曲线。twisted Edwards 形式的加法公式同样具有统一性,并且可以在保持完备性的同时提供更灵活的参数选择。
Ed25519 正是一条 twisted Edwards 曲线,参数为 \(a = -1\),定义在与 Curve25519 相同的素数域上。它的单位元(中性元)是点 \((0, 1)\),而不是抽象的无穷远点,这使得实现更加直观。
Edwards 曲线形式的核心优势可以总结如下:
- 统一加法公式:消除了条件分支,天然抵抗侧信道攻击。
- 高效运算:在扩展坐标(extended coordinates)下,点加法仅需 8 次域乘法(field multiplication),优于 Weierstrass 形式。
- 代码简洁:单一公式覆盖所有情况,显著减少了实现错误的可能性。
- 与 Montgomery 形式的互通:通过双有理映射,可以在两种形式间灵活切换。
一个经常被忽视的观点是,Edwards 曲线的出现标志着椭圆曲线密码学从「数学优先」到「实现优先」的哲学转变。在 Weierstrass 传统下,曲线形式的选择以数学通用性为导向——Weierstrass 方程可以表示任何椭圆曲线,因此被视为「最自然」的选择。但这种数学上的通用性,恰恰导致了工程上的脆弱性:加法公式中的条件分支、奇异点的特殊处理、不同情况下需要不同公式——每一个「数学上的例外情况」都是侧信道攻击者可以利用的泄漏点。Edwards 形式反转了这一优先级:它牺牲了数学上的通用性(不是所有椭圆曲线都能写成 Edwards 形式),换取了实现层面的绝对简洁与安全。笔者认为,这种「为实现而设计数学」的理念,是过去二十年密码工程领域最重要的范式转移之一。它提醒我们:一个密码方案的安全性不仅取决于数学问题的困难性,也取决于数学结构是否「对实现者友好」——因为在真实世界中,大多数密码系统不是被数学攻破的,而是被实现漏洞攻破的。
曲线形式综合对比
在介绍了三种主要的曲线形式之后,下表从运算效率、实现安全性和典型应用三个维度做一个系统对比:
| 维度 | Weierstrass 形式 | Montgomery 形式 | Edwards / Twisted Edwards 形式 |
|---|---|---|---|
| 标准方程 | y² = x³ + ax + b | By² = x³ + Ax² + x | ax² + y² = 1 + dx²y² |
| 加法公式 | 点加 / 点倍乘需分别处理,存在多种例外情况 | 仅用 x 坐标的差分加法,配合 Montgomery ladder | 统一公式(complete),无需分支判断 |
| 点加法域乘次数 | 约 12M(混合坐标) | 约 5M + 4S(仅 x 坐标) | 约 8M(扩展坐标) |
| 侧信道抗性 | 较弱,需额外工程措施 | 强,ladder 天然常量时间 | 强,complete 公式消除分支 |
| 密钥交换 | ECDH(secp256r1 等) | X25519、X448 | 可用但通常转换到 Montgomery 形式 |
| 数字签名 | ECDSA(P-256、secp256k1) | 不直接使用 | EdDSA(Ed25519、Ed448) |
| 零知识证明 | 部分 SNARK 系统使用 | 较少直接使用 | Jubjub、Bandersnatch 等 ZKP 友好曲线 |
| 配对运算 | BN256、BLS12-381 等配对友好曲线 | 不支持 | 不直接支持,但可用于配对友好曲线的辅助运算 |
| 代表曲线 | NIST P-256、secp256k1、BLS12-381 | Curve25519、Curve448 | Ed25519、Ed448、Jubjub |
从工程实践来看,现代密码协议越来越倾向于「Montgomery 做交换,Edwards 做签名」的组合模式——X25519 和 Ed25519 正是这种分工的典范。两者在数学上是同一条曲线的不同表示形式,共享相同的安全性保证,却各自在自己擅长的场景中发挥最优性能。而在零知识证明领域,Edwards 形式的曲线(如 Zcash 使用的 Jubjub 和以太坊研究的 Bandersnatch)因其算术电路友好性而成为首选——complete 加法公式意味着在电路中无需处理分支逻辑,显著降低了约束(constraint)数量。
八、标量乘法优化
标量乘法(scalar multiplication)——计算 \(kP\)(即点 \(P\) 自加 \(k\) 次)——是椭圆曲线密码方案中最核心的运算。密钥生成、签名、验签和密钥交换都归结为一次或几次标量乘法。因此,标量乘法的效率直接决定了 ECC 系统的实际性能。
倍加法(double-and-add) 是最基本的标量乘法算法,类似于整数幂运算中的快速幂算法。将标量 \(k\) 表示为二进制形式 \(k = (k_{n-1}, k_{n-2}, \ldots, k_1, k_0)_2\),从最高位开始扫描:
Q = O (无穷远点)
for i from n-1 down to 0:
Q = 2Q (点倍乘)
if k_i == 1:
Q = Q + P (点加法)
return Q
该算法的运算次数与 \(k\) 的比特长度成正比:大约需要 \(n\) 次倍乘和平均 \(n/2\) 次加法(取决于 \(k\) 中 1 的个数)。
非相邻形式(Non-Adjacent Form,NAF) 利用了椭圆曲线群中减法与加法代价相同的特性。NAF 将标量表示为 \(\{-1, 0, 1\}\) 三进制形式,且保证不会出现连续两个非零数字。NAF 表示中非零数字的平均密度约为 \(1/3\),相比二进制表示的 \(1/2\) 减少了约三分之一的点加法次数。
窗口 NAF(wNAF,windowed NAF) 进一步扩展了这一思想。使用宽度为 \(w\) 的窗口,标量被表示为 \(\{-(2^{w-1}-1), \ldots, -1, 0, 1, \ldots, 2^{w-1}-1\}\) 中的数字序列,非零数字的平均密度降至约 \(1/(w+1)\)。代价是需要预计算并存储 \(2^{w-2}\) 个点。这在签名方案中尤其有价值——当基点(base point)\(G\) 是固定的时候,预计算表可以一次生成、反复使用。
GLV 分解(Gallant-Lambert-Vanstone decomposition) 适用于具有高效自同态映射(endomorphism)的曲线,例如 secp256k1。该曲线的 \(j\) 不变量(\(j\)-invariant)为零,存在一个廉价的自同态 \(\phi: (x, y) \mapsto (\beta x, y)\),其中 \(\beta\) 是 \(\mathbb{F}_p\) 中的一个特定元素。利用这个自同态,可以将一次 256 位的标量乘法分解为两次 128 位的标量乘法,然后通过 Shamir’s trick(联合稀疏形式,joint sparse form)并行执行,总体效率提升约 40% 至 50%。
综合来看,一次优化良好的标量乘法在 256 位曲线上通常需要约 300 至 400 次域乘法,这在现代处理器上仅需微秒级别的时间。
以下是一段 Python 代码,演示了在小素数域上实现椭圆曲线点加法和标量乘法的基本过程:
def modinv(a, p):
"""扩展欧几里得算法求模逆"""
if a == 0:
raise ValueError("零没有模逆")
g, x, _ = extended_gcd(a % p, p)
if g != 1:
raise ValueError("模逆不存在")
return x % p
def extended_gcd(a, b):
if a == 0:
return b, 0, 1
g, x, y = extended_gcd(b % a, a)
return g, y - (b // a) * x, x
class EllipticCurve:
"""Weierstrass 曲线 y^2 = x^3 + ax + b (mod p)"""
def __init__(self, a, b, p):
self.a = a
self.b = b
self.p = p
# 检查判别式非零
assert (4 * a**3 + 27 * b**2) % p != 0
def is_on_curve(self, point):
if point is None: # 无穷远点
return True
x, y = point
return (y * y - x * x * x - self.a * x - self.b) % self.p == 0
def point_add(self, P, Q):
"""点加法:返回 P + Q"""
if P is None:
return Q
if Q is None:
return P
x1, y1 = P
x2, y2 = Q
if x1 == x2:
if (y1 + y2) % self.p == 0:
return None # P + (-P) = O
# 点倍乘
lam = (3 * x1 * x1 + self.a) * modinv(2 * y1, self.p) % self.p
else:
# 点加法
lam = (y2 - y1) * modinv(x2 - x1, self.p) % self.p
x3 = (lam * lam - x1 - x2) % self.p
y3 = (lam * (x1 - x3) - y1) % self.p
return (x3, y3)
def scalar_mult(self, k, P):
"""标量乘法:返回 kP(倍加法)"""
result = None # 无穷远点(单位元)
addend = P
while k > 0:
if k & 1:
result = self.point_add(result, addend)
addend = self.point_add(addend, addend)
k >>= 1
return result
# 示例:在小素数域 GF(97) 上的曲线 y^2 = x^3 + 2x + 3
curve = EllipticCurve(a=2, b=3, p=97)
# 选取一个基点
G = (3, 6)
assert curve.is_on_curve(G)
# 计算标量乘法
for k in range(1, 6):
Q = curve.scalar_mult(k, G)
print(f"{k}G = {Q}", " 在曲线上" if curve.is_on_curve(Q) else " 不在曲线上")
# 验证结合律:(a+b)G = aG + bG
a, b = 7, 11
abG = curve.scalar_mult(a + b, G)
aG_plus_bG = curve.point_add(curve.scalar_mult(a, G), curve.scalar_mult(b, G))
print(f"\n(a+b)G = {abG}")
print(f"aG + bG = {aG_plus_bG}")
print(f"结合律验证: {abG == aG_plus_bG}")这段代码清晰地展示了有限域上椭圆曲线运算的核心逻辑。在实际密码库中,这些运算会使用射影坐标(projective coordinates)或雅可比坐标(Jacobian coordinates)来避免昂贵的模逆运算,并结合前述的窗口方法和预计算表来进一步加速。
九、安全曲线标准
选择一条”好”的椭圆曲线不仅仅是确保 ECDLP 足够难——还需要考虑一系列工程和安全方面的细节。Daniel J. Bernstein 和 Tanja Lange 发起的 SafeCurves 项目(safecurves.cr.yp.to)系统地整理了安全曲线应当满足的标准,为密码学界提供了一份全面的评估框架。
SafeCurves 核心标准 包括以下几个维度:
参数刚性(rigidity):曲线参数的选取过程是否完全确定、不存在隐藏的自由度。理想的做法是采用”nothing-up-my-sleeve”参数——即那些显然不是精心构造的值,例如自然常数的某种哈希、最小满足条件的整数等。Curve25519 在这方面表现优异:素数 \(2^{255} - 19\) 是特定形式下的最自然选择,系数 \(A = 486662\) 是满足安全约束的最小值。相比之下,NIST P-256 的种子来源不够透明,在 SafeCurves 的刚性评估中未能通过。
扭曲安全性(twist security):椭圆曲线 \(E\) 的二次扭曲(quadratic twist)\(E'\) 的群阶也应包含大素数因子。这一点之所以重要,是因为在 X25519 等仅使用 x 坐标的协议中,接收到的 x 坐标可能对应原曲线上的点,也可能对应扭曲曲线上的点。如果扭曲曲线的群阶含有小因子,攻击者可以通过小子群攻击泄漏私钥的部分信息。Curve25519 的扭曲曲线的阶同样包含大素数因子,因此具备扭曲安全性。
完备性(completeness):曲线的加法公式是否对所有合法输入都有效,不存在例外情况。如前所述,Edwards 曲线和 twisted Edwards 曲线天然具备这一性质,而 Weierstrass 形式的加法公式则不然。完备性不仅简化了实现,更消除了由条件分支引发的侧信道泄漏。
从工程安全的角度来看,完备加法公式(complete addition formula)的重要性怎么强调都不为过——笔者甚至认为它是决定一条曲线「工程安全性」的最关键单一因素。历史上,几乎所有针对 ECC 实现的实际侧信道攻击,都利用了加法公式中的条件分支:通过功耗分析(power analysis)、电磁辐射分析(electromagnetic analysis)或缓存计时攻击(cache-timing attack),攻击者可以精确识别标量乘法执行过程中哪些步骤执行了点加法、哪些步骤执行了点倍乘、哪些步骤触发了例外处理。每一个分支差异都可能泄漏标量(即私钥)的一个比特。完备加法公式从根本上消除了这一攻击面:无论输入如何,执行路径完全相同,攻击者无法从侧信道观测中提取任何信息。这就是为什么在 2020 年代的新项目中,如果没有强制性的合规约束,几乎没有理由不选择 Edwards 形式的曲线。
梯形支持(ladder-ability):曲线是否支持高效的 Montgomery 梯形算法或类似的常量时间标量乘法。Montgomery 曲线天然支持这一特性,而通过双有理映射与 Montgomery 曲线等价的 Edwards 曲线也间接具备梯形支持。
其他标准 还包括:曲线的辅因子(cofactor)应当尽可能小(理想情况下为 1、4 或 8);基域素数应当具有便于快速模归约的特殊形式(如 \(2^{255} - 19\) 或 \(2^{448} - 2^{224} - 1\));曲线的嵌入度应当足够大以抵抗 MOV 攻击;群的判别式(CM discriminant)应当足够大以排除某些代数结构上的弱点。
下面从 SafeCurves 标准的视角对几条主流曲线做一个简要对比:
| 曲线 | 域位数 | 形式 | 辅因子 | 刚性 | 扭曲安全 | 完备加法 |
|---|---|---|---|---|---|---|
| NIST P-256 | 256 | Weierstrass | 1 | 否 | 否 | 否 |
| secp256k1 | 256 | Weierstrass | 1 | 是 | 否 | 否 |
| Curve25519 | 255 | Montgomery | 8 | 是 | 是 | — |
| Ed25519 | 255 | tw. Edwards | 8 | 是 | 是 | 是 |
| Ed448 | 448 | Edwards | 4 | 是 | 是 | 是 |
从表中可以看出,Curve25519 和 Ed25519 在 SafeCurves 的所有核心标准上都表现优异,这也解释了为什么它们在近年来获得了越来越广泛的采用。TLS 1.3 将 X25519 列为必须支持的密钥交换机制,Signal 协议和 WireGuard VPN 也都以 Curve25519 为核心。相比之下,NIST 曲线虽然仍然被广泛部署(尤其是在受政府合规要求约束的系统中),但在安全社区中的信任度正在下降。
值得一提的是,SafeCurves 标准并非”通过与否”的二元判定,而是一个连续的安全性频谱。即使一条曲线在某个维度上不满足 SafeCurves 的最高要求,也不意味着它一定不安全——只是意味着它的安全论证不如满足所有标准的曲线那样充分和透明。密码工程师在选择曲线时,应当综合考虑安全性、性能、生态支持和合规要求等多方面因素。
小结
椭圆曲线密码学将优美的数学结构转化为了高效的密码工具。从实数域上的弦切法则到有限域上的离散点群,从 Weierstrass 形式到 Montgomery 和 Edwards 形式,从朴素的倍加法到精巧的窗口化与 GLV 分解,每一步优化都植根于对底层数学结构的深刻理解。ECDLP 的困难性为我们提供了坚实的安全基础,而 SafeCurves 等项目则为曲线选择提供了系统化的评估标准。在下一篇文章中,我们将在椭圆曲线群的基础上构建具体的数字签名方案——ECDSA 与 EdDSA,将本文的群运算付诸实际的密码协议应用。
密码学百科系列 · 第 17 篇
← 上一篇:Diffie-Hellman | 系列目录 | 下一篇:数字签名 →