# 椭圆曲线密码学:有限域和离散对数 >原文[地址](https://andrea.corbellini.name/2015/05/23/elliptic-curve-cryptography-finite-fields-and-discrete-logarithms/),DAOC DAO进行翻译整理 [在上一篇文章中](https://andrea.corbellini.name/2015/05/17/elliptic-curve-cryptography-a-gentle-introduction/),我们了解了如何使用实数上的椭圆曲线来定义群。具体来说,我们定义了一个点加法规则:给定三个对齐的点,它们的和为零($P + Q + R = 0$)我们推导出了计算点加法的几何方法和代数方法。 然后我们引入了标量乘法($nP = P + P + \cdots + P$),我们发现了一种计算标量乘法的“简单”算法:翻倍加和。 现在我们将把椭圆曲线限制在有限域内,而不是实数集,看看情况如何变化。 ## 模$p$整数域 首先,有限域是具有有限个元素的集合。有限域的一个例子是模$p$的整数集,其中$p$是素数,通常表示为$\mathbb{Z}/p$,$GF(p)$或者$\mathbb{F}_p$。我们将使用后一种符号。 在域中,我们有两个二元运算:加法 ($+$) 和乘法 ($·$)。两者都是封闭的、结合的和交换的。对于这两种运算,都存在一个唯一的恒等元素,并且对于每个元素都有一个唯一的逆元素。最后,乘法对加法是分配的: $$x \cdot (y + z) = x \cdot y + x \cdot z$$ 模$p$整数集的元素由$0$到$p-1$构成。加法和乘法的工作方式与[取模运算](http://en.wikipedia.org/wiki/Modular_arithmetic)(也称为“时钟运算”)相同。以下是一些运算示例$\mathbb{F}_{23}$: - 加法:$(18 + 9) \bmod{23} = 4$ - 减法:$(7 - 14) \bmod{23} = 16$ - 乘法:$4 \cdot 7 \bmod{23} = 5$ - 加法逆元:$-5 \bmod{23} = 18$ 即:$(5 + (-5)) \bmod{23} = (5 + 18) \bmod{23} = 0$ - 乘法逆元:$9^{-1} \bmod{23} = 18$ 即:$9 \cdot 9^{-1} \bmod{23} = 9 \cdot 18 \bmod{23} = 1$ 如果您对这些等式不熟悉,并且需要模运算的入门知识,请查看[可汗学院](https://www.khanacademy.org/computing/computer-science/cryptography/modarithmetic/a/what-is-modular-arithmetic)。 正如我们已经说过的,模$p$整数集是一个域,因此上面列出的所有属性都成立。请注意$p$是素数很重要!模4的整数集不是域:2没有乘法逆元(即方程$2 \cdot x \bmod{4} = 1$没有解)。 ## 模$p$除法 我们很快就会定义椭圆曲线$\mathbb{F}_p$,但在这样做之前,我们需要清楚的了解在$\mathbb{F}_p$中$x / y$意味着什么。简单地说,$x / y = x \cdot y^{-1}$,或者说,$x$除以$y$等于$x$乘上$y$的乘法逆元。这个事实并不令人惊讶,但却为我们提供了执行除法的基本方法:找到一个数的乘法逆元,然后执行一次乘法。 使用扩展的欧几里得算法可以“轻松”地计算乘法逆元,即$O(\log p)$(或者$O(k)$,如果我们考虑位长),在最坏的情况下。 我们不会介绍扩展欧几里得算法的细节,因为它与主题无关,但是这里有一个可行的 Python 实现: ``` def extended_euclidean_algorithm(a, b): """ Returns a three-tuple (gcd, x, y) such that a * x + b * y == gcd, where gcd is the greatest common divisor of a and b. This function implements the extended Euclidean algorithm and runs in O(log b) in the worst case. """ s, old_s = 0, 1 t, old_t = 1, 0 r, old_r = b, a while r != 0: quotient = old_r // r old_r, r = r, old_r - quotient * r old_s, s = s, old_s - quotient * s old_t, t = t, old_t - quotient * t return old_r, old_s, old_t def inverse_of(n, p): """ Returns the multiplicative inverse of n modulo p. This function returns an integer m such that (n * m) % p == 1. """ gcd, x, y = extended_euclidean_algorithm(n, p) assert (n * x + p * y) % p == gcd if gcd != 1: # Either n is 0, or p is not a prime number. raise ValueError( '{} has no multiplicative inverse ' 'modulo {}'.format(n, p)) else: return x % p ``` ## 椭圆曲线$\mathbb{F}_p$ 现在我们有了在$\mathbb{F}_p$上定义椭圆曲线的所有必要元素。在[之前文章](https://hackmd.io/@daoc-dao/Skf9sdbcJx)中定义的点集: $$ \begin{array}{rcl} \left\{(x, y) \in \mathbb{R}^2 \right. & \left. | \right. & \left. y^2 = x^3 + ax + b, \right. \\ & & \left. 4a^3 + 27b^2 \ne 0\right\}\ \cup\ \left\{0\right\} \end{array} $$ 现在变为: $$ \begin{array}{rcl} \left\{(x, y) \in (\mathbb{F}_p)^2 \right. & \left. | \right. & \left. y^2 \equiv x^3 + ax + b \pmod{p}, \right. \\ & & \left. 4a^3 + 27b^2 \not\equiv 0 \pmod{p}\right\}\ \cup\ \left\{0\right\} \end{array} $$ 其中$0$仍然表示无限远点,$a$和$b$表示$\mathbb{F}_p$上的两个整数。 ![image](https://hackmd.io/_uploads/rkT_K72qyg.png) >曲线$y^2 \equiv x^3 - 7x + 10 \pmod{p}$和$p = 19, 97, 127, 487$。注意,对于每一个$x$,最多有两个点,还要注意关于$y = p / 2$对称。 ![image](https://hackmd.io/_uploads/r1ty6Xhckg.png) >曲线$y^2 \equiv x^3 \pmod{29}$是奇异的,并且有三重点$(0,0)$,它不是有效的椭圆曲线。 以前连续的曲线现在变成了一组在$xy$平面不相交的点。但我们可以证明,即使我们约束了我们的域,椭圆曲线在$\mathbb{F}_p$仍然形成一个阿贝尔群。 ## 点加法 显然,我们需要稍微改变一下加法的定义,才能让它在$\mathbb{F}_p$发挥作用。对于实数,我们说三个对齐点的总和为零($P + Q + R = 0$)我们可以保留这个定义,但是在$\mathbb{F}_p$上三个点对齐是什么意思呢? 如果有一条线连接所有点,我们可以说这三个点是对齐的。但显然,$\mathbb{F}_p$上的线和$\mathbb{R}$上的线是不一样的。我们可以非正式的说,$\mathbb{F}_p$上的一条线是满足$ax + by + c \equiv 0 \pmod{p}$的点集$(x,y)$。(标准方程加上$(modp)运算$) ![image](https://hackmd.io/_uploads/SkHbTm2qke.png) >当$P = (16, 20)$和$Q = (41, 120)$时,在曲线$y^2 \equiv x^3 - x + 3 \pmod{127}$上的加法。注意,$y \equiv 4x + 83 \pmod{127}$连接各点的线在平面上“重复”。 假设我们在一个群中,点加法保留了我们已经知道的属性: - $Q + 0 = 0 + Q = Q$(来自单位元的定义) - 给定一个非零点$Q$,逆$-Q$是横坐标相同但纵坐标相反的点或者,如果你愿意,$-Q = (x_Q, -y_Q \bmod{p})$。例如,如果曲线在$\mathbb{F}_{29}$有点$Q = (2, 5)$,则逆元$-Q = (2, -5 \bmod{29}) = (2, 24)$ - 并且,$P + (-P) = 0$(来自逆元的定义) ## 代数和 计算点加法的公式与上一篇文章完全相同,只是我们需要在表达式的末尾添加$(\bmod(p))$。因此,给定$P = (x_P, y_P)$,$Q = (x_Q, y_Q)$和$R = (x_R, y_R)$,我们可以计算$P + Q = -R$如下: $$ \begin{align*} x_R & = (m^2 - x_P - x_Q) \bmod{p} \\ y_R & = [y_P + m(x_R - x_P)] \bmod{p} \\ & = [y_Q + m(x_R - x_Q)] \bmod{p} \end{align*} $$ 如果$P \ne Q$,斜率$m$采取以下形式: $$ m = (y_P - y_Q)(x_P - x_Q)^{-1} \bmod{p} $$ 如果$P = Q$,我们有: $$ m = (3 x_P^2 + a)(2 y_P)^{-1} \bmod{p} $$ 这些方程没有改变,者并非巧合:这些方程适用于所有领域,无论是有限域还是无限域(除了$\mathbb{F}_2$和$\mathbb{F}_3$,这是特殊情况)。现在我觉得我必须为这个事实提供一个理由。问题是:群律的证明通常涉及复杂的数学概念。然而,我发现[Stefan Friedl 的证明](https://arxiv.org/pdf/1710.00214)只使用了基本概念。如果你对这些方程在(几乎)每个领域都适用的原因感兴趣,请阅读它。 回到我们——我们不会定义几何方法:事实上,这样做存在一些问题。例如,在上一篇文章中,我们说要计算$P + P$我们需要取曲线的切线$P$。但是如果没有连续性,“切线”这个词就没有任何意义。我们可以解决这个问题和其他问题,但是纯几何方法太复杂了,根本不实用。 相反,您可以使用我编写的用于计算点加法的[交互式工具](https://andrea.corbellini.name/ecc/interactive/modk-add.html)。 ## 椭圆曲线群的阶 我们说过,有限域上的椭圆曲线有有限个点。我们需要回答的一个重要问题是:到底有多少个点? 首先,我们设群中点的个数称为该群的阶。 尝试所有可能的值$x$从 0 到$p-1$不是一种可行的计数方式,因为它需要$O(p)$步骤,如果$p$是一个大素数就很“难”。 幸运的是,有一种更快的算法可以计算阶数:[Schoof算法](https://en.wikipedia.org/wiki/Schoof%27s_algorithm)。我不会介绍该算法的细节——重要的是它在多项式时间内运行,这就是我们所需要的。 ## 标量乘法和循环子群 与实数一样,乘法可以定义为: $$ n P = \underbrace{P + P + \cdots + P}_{n\ \text{times}} $$ 并且,我们可以使用翻[倍加和算法](https://andrea.corbellini.name/2015/05/17/elliptic-curve-cryptography-a-gentle-introduction/#double-and-add)在$O(\log n)$步来执行乘法(或者$O(k)$,$k$为$n$位比特数)。我还编写了一个用于标量乘法的[交互式工具](https://andrea.corbellini.name/ecc/interactive/modk-mul.html)。 椭圆曲线$\mathbb{F}_p$上的点乘法有一个有趣的特性。以曲线$y^2 \equiv x^3 + 2x + 3 \pmod{97}$以及点$P=(3,6)$为例,现在计算所有的$P$: ![image](https://hackmd.io/_uploads/S1MDBN25kx.png) >$P=(3,6)$只有五个不同的点$(0, P, 2P, 3P, 4P)$,并且他们循环往复。很容易发现椭圆曲线上的标量乘法和模运算的加法之间的相似性。 - $0P = 0$ - $1P = (3, 6)$ - $2P = (80, 10)$ - $3P = (80, 87)$ - $4P = (3, 91)$ - $5P = 0$ - $6P = (3, 6)$ - $7P = (80, 10)$ - $8P = (80, 87)$ - $9P = (3, 91)$ - ... 在这里我们可以立即发现两件事:首先,$P$只有五个:椭圆曲线的其他点永远不会出现。其次,它们是循环重复的。我们可以写成: - $5kP = 0$ - $(5k + 1)P = P$ - $(5k + 2)P = 2P$ - $(5k + 3)P = 3P$ - $(5k + 4)P = 4P$ 对于每个整数$k$请注意,得益于模运算符,这五个方程可以“压缩”为一个方程: $$ kP = (k \bmod{5})P $$ 不仅如此,我们还可以立即验证这五个点在加法下是封闭的。这意味着:无论我添加什么$0, 2P, 3P或者4P$,结果总是这五个点之一。同样,椭圆曲线的其他点永远不会出现在结果中。 该结论适用于曲线上的每一点,而不仅仅是$P = (3, 6)$。事实上,如果我们采用通用的$P$: $$ nP + mP = \underbrace{P + \cdots + P}_{n\ \text{times}} + \underbrace{P + \cdots + P}_{m\ \text{times}} = (n + m)P $$ 这意味着:如果我们加上两倍$P$,我们得到了一倍$P$(即$P$的倍数在加法下是封闭的)。这足以证明$P$倍数的集合是椭圆曲线形成的群的循环子群。 “子群” 是另一个群的子集。“循环子群” 是元素循环重复的子群,就像我们在前面的例子中展示的那样。点$P$称为循环子群的生成元或基点。 循环子群是 ECC 和其他密码系统的基础。我们将在下一篇文章中了解原因。 ## 子群的阶 我们可以问由一个点$P$生成的子群的阶是多少(或者等价地问,$P$的阶是多少)。要回答这个问题,我们不能使用 Schoof 算法,因为该算法只适用于整个椭圆曲线,而不适用于子群。在解决这个问题之前,我们还需要一些信息: - 到目前为止,我们已经将阶定义为群中点的数量。这个定义仍然有效,但在循环子群中,我们可以给出一个新的等效定义:$P$的阶是最小的正整数使得$nP = 0$。事实上,如果你看看前面的例子,我们的子群包含五个点,我们有$5P = 0$。 - $P$的阶通过[拉格朗日定理](https://en.wikipedia.org/wiki/Lagrange%27s_theorem_(group_theory))可以将子群的阶与椭圆曲线的阶联系起来,该定理指出子群的阶是母群阶的除数。换句话说,如果椭圆曲线包含$N$点,并且它的一个子群包含$n$点,那么$n$是$N$的除数。 这两个信息结合起来,为我们提供了一种找出基点$P$子群的阶的方法: 1. 使用 Schoof 算法计算椭圆曲线的阶$N$ 2. 找出所有的因数$N$ 3. 对于每一个$N$的除数$n$,计算$nP$ 4. 使得$nP=0$的最小的$n$就是子群的阶 例如,域$\mathbb{F}_{37}$上的曲线$y^2 = x^3 - x + 3$的阶$N = 42$。它的子群可能的阶为$n=1,2,3,6,7,14,21或者42$。如果我们尝试$P=(2,3)$,我们可以看到$P \ne 0$,$2P \ne 0$,...,$7P = 0$,因此$P$的阶为$n=7$。 请注意,取最小除数很重要,而不是随机取除数。如果我们随机进行,我们可以取$n=14$,这不是子群的阶,而是它的倍数之一。 另一个例子:由以下方程定义的椭圆曲线$y^2 = x^3 - x + 1$在域$\mathbb{F}_{29}$有阶$N = 37$,是素数。其子群只能有阶$n=1$或者$n=37$。正如你很容易猜到的,当$n=1$,子群仅包含无穷远点,当$n = N$,子群包含椭圆曲线的所有点。 ## 寻找基点 对于我们的 ECC 算法,我们需要高阶子群。因此,一般来说,我们会选择一条椭圆曲线,计算其阶数($N$),选择一个高因子作为子群的阶($n$),最终找到一个合适的基点。也就是说:我们不会先选择一个基点,然后计算它的阶数,而是做相反的事情:先选择一个看起来足够好的阶数,然后再寻找一个合适的基点。我们怎么做呢? 首先,我们需要再引入一个术语。拉格朗日定理意味$h = N / n$始终是整数(因为$n$是$N$的除数)。数字$h$有一个名字:它是子群的余因子。 现在考虑对于椭圆曲线的每个点,我们都有$NP=0$。这是因为$N$是任意候选人的倍数$n$,利用余因子的定义,我们可以写出: $$ n(hP) = 0 $$ 现在假设$n$是一个素数(出于将在下一篇文章中解释的原因,我们更喜欢素数阶)。这个方程以这种形式写出,告诉我们点$G = hP$生成一个阶为n的子群(除非$G = hP = 0$,在这种情况下子群的阶为 1) 鉴于此,我们可以概述以下算法: 1. 计算椭圆曲线的阶$N$ 2. 选择阶为$n$的子群,为了使算法有效,这个数字必须是素数,并且必须是$N$ 3. 计算余因子$h = N / n$ 4. 选择一个曲线上的随机点$P$ 5. 计算$G = hP$ 6. 如果$G$为0,则返回步骤4,否则,我们找到了具有$n$阶子群的生成元,和余因子$h$ 请注意,此算法仅在以下情况下有效:$n$是素数。如果$n$不是素数,那么$G$可能是$n$。 ## 离散对数 正如我们在处理连续椭圆曲线时所做的那样,我们现在要讨论这个问题:如果我们知道$P$和$Q$,是什么$k$使得$Q=kP$? 这个问题被称为椭圆曲线的离散对数问题,人们认为这是一个“难题”,因为没有已知的多项式时间算法可以在传统计算机上运行。然而,没有数学证据支持这一观点。 这个问题也类似于其他密码系统(如数字签名算法 (DSA)、Diffie-Hellman 密钥交换 (DH) 和 ElGamal 算法)使用的离散对数问题——它们有相同的名称并非巧合。不同之处在于,对于这些算法,我们使用模幂而不是标量乘法。它们的离散对数问题可以表述如下:如果我们知道$a$和$b$,什么$k$使得$b = a^k \bmod{p}$? 这两个问题都是“离散的”,因为它们涉及有限集(更准确地说是循环子群)。它们也是“对数”,因为它们类似于普通对数。 ECC 之所以有趣,是因为到目前为止,与密码学中使用的其他类似问题相比,椭圆曲线的离散对数问题似乎“更难”。这意味着我们需要更少的位数来表示整数$k$为了达到与其他密码系统相同的安全级别,我们将在本系列的第四篇也是最后一篇文章中详细介绍。