ECC椭圆曲线加密算法:有限域和离散对数

ECC椭圆曲线加密算法:有限域和离散对数

Hi all,我来翻译第二篇啦。若大家发现那些翻译的不够准确还望指出,不胜感激。首先放上原文链接:

http://andrea.corbellini.name/2015/05/23/elliptic-curve-cryptography-finite-fields-and-discrete-logarithms/andrea.corbellini.name

在上一篇文章里,我们已经展示了在实数域上的椭圆曲线在“群”上是如何使用的。尤其是,我们还针对“加”(point addition) 定义了一个规则:对于在一条线上的三个点,他们的和是0(P+Q+R=0). 我们也推出了一个几何方法和代数方法去计算这些点的加法。

然后呢,我们介绍了标量积(scalar multiplication) (nP=P+P+...+P),我们还发现了一个“简单的”算法去计算这些标量积。double and add

整数域模p

首先,有限域是一个带有有限元素的集合。比如,有一个有限域是整数模p的集合(integers mod p,p是素数),可表示为 \mathbb {Z}/p,GF(p) \mathbb {F}_{p} ,我们一般用后者。

在这个有限域中,我们有两个二元操作:加(+)和乘( * )。这两种操作都是封闭的,满足结合律和交换律,[这块的封闭应当这样理解:在有限域中,两个数的加和乘的结果仍然在这个有限域中。--译者注] 且含有一个独一无二的单位元,对于所有的有限域里的元素,都有一个独一无二的相反数。最后,乘法还满足分配律: x*(y+z) = x*y + x*z

整数模p的集合包含所有从0到 p-1 的整数。加法和乘法都按模数运算规则去计算。这里有一些 \mathbb{F}_{23} 的例子:

  • 加: (18+9)mod\, 23=4
  • 减: (7-14)\,mod\,23=16
  • 乘: 4*7\,mod\,23=16
  • 加法逆元: -5\,mod \,23=18 的确: (5+(-5))\,mod\,23\,=(5+18)\,mod\,23\,=\,0
  • 乘法逆元: 9^{-1}\,mod\,23\,=\,18 的确: 9*9^{-1}\,mod\,23\,=\,9*18\,mod\,23\,=\,1

如果这些式子你觉得不太能理解,那么你可能需要一些关于模运算的入门指导,请参考:Khan Academy.

就如我前面提到的,整数模p是一个域,因此上面列的所有属性都是满足的。请注意p是素数这个条件很重要!比如 整数模4的集合就不是域:2没有乘法逆元( 2*x\,mod\,4\,=\,1 无解)。

模p的除法

我们即将定义在 \mathbb {F}_{p} 上的椭圆曲线,但是在这之前我们需要弄清在 \mathbb {F}_{p}x/y 代表什么?可以简单表示为: x/y\,=\,x*y^{-1}x/y 等于x 乘上 y的逆元。这个解释给了我们基本的做除法的方法:1.求逆元,2.做乘法。

用欧几里得拓展算法来计算乘法逆元非常的“简单易做”,它的时间复杂度是 O(log\,p) (或者是 O(k) 如果考虑bit长度的话) 在最坏的情况下。 这里不会给出欧几里得拓展算法的细节,但是放上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} 上定义椭圆曲线,它的形式是点集,在上一篇文章中我们是这样写的:

\{ (x,y)∈\mathbb {R}_{2}\,|\,y^{2}=x^{3}+ax+b,\,4a^{3}+27b^{2}≠0\} ∪ \{0\}

现在,可以写成:

\{ (x,y)∈(\mathbb{F}_{p})^{2}\,|\,y^{2}\equiv x^{3}+ax+b(mod\,p),\,4a^{3}+27b^{2}\not\equiv0(mod\,p)\} ∪ \{0\}

这里的0仍然是无限远的点,a和b是 \mathbb{F}_p 上的两个整数。

图1

图1:曲线 y^{2}\equiv x^{3}-7x+10(mod\,p)p=19,97,127,487 . 请注意,对于每一个 x ,至多有两个对称的点满足: y=p/2 .

图2

图2:曲线 y^{2}\equiv x^{3}(mod\,29) 是一个奇点,并且在 (0,0) 处有三个点,这是一个无效的椭圆曲线。

图1是连续的椭圆曲线在xy轴平面上表现为不相交的点集。\mathbb{F}_{p} 上的椭圆曲线仍然可以形成阿贝尔群。(elliptic curves in \mathbb{F}_{p} still form an abelian group.

点加

显然,我们需要改变一点点关于加法的定义,为了使它能更好的工作在 \mathbb{F}_{p} 。 在实数域上,我们约定俗成三个对齐的点的和是0( P + Q + R = 0 )。在实数域上这个定义没有问题,就是共线。但是在 \mathbb{F}_{p} 上,我们如何定义三个点的对齐?

我们可以说,如果一条直线连接了三个点,这三个点就是对齐的。当然, \mathbb{F}_{p} 上的直线不同于 \mathbb{R} 上的直线。也就是说, \mathbb{F}_{p} 上的直线就是点集( x,y ),这个点集满足 ax+by+c≡0(mod\, p) (这是带有" (mod\, p) "的标准的线性方程式)

请见注释1(方程式在这里长的不标准...)

注释1: 上图的所有点都在y^2 \equiv x^3 - x + 3 \pmod{127}P=(16,20) \, and  \,Q=(41,120). 请注意连接某些点的一次方程 y \equiv 4x + 83 \pmod{127} 在图中不停的“重复(因为有mod 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 (相反数的定义)

代数和

点加的计算和上篇文章中基本差不多,除了要在每个等式后加上“ mod\, p ”。因此,鉴于 P = (x_P, y_P), Q = (x_Q, y_Q)R = (x_R, y_R) ,我们可以计算 P + Q = -R ,如下:

\begin{array}{rcl} 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{array}

如果 P \ne Q ,假设斜率是: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给出的证明浅显易懂。如果你对“为什么这个方程式几乎适用于所有环境”很感兴趣,read it!

言归正传,由于在几何方法上有一些问题,所以我们不会定义一个几何方法。比如,在第一篇稿子中,我们说 要计算 P+P 我们需要在曲线 P 上正切,但是如果不是一条线的话(without continuity, 即没有连续性),“正切”没有任何意义。确实我们可以权衡利弊后做一些变通,但是这种纯几何方法太复杂而且不实用。

椭圆曲线的阶

我们之前说到每个在有限域上的椭圆曲线都由有限个点组成。那么我们不禁要问:到底是多少个点?

首先,我们要定义一下 在一个群有多少个点就叫做这个群的“阶”(order)【在此放上wiki关于order的解释】。

群举从 xp-1 所有可能的值去数有多少个点不太可行,因为它的时间复杂度是 O(p) ,当 p 很大的时候,这算下来就很慢很慢。

还好,有一个更快的算法来计算阶:Schoof算法。在此不展细节,我们只需要知道他的复杂度是多项式时间(大名鼎鼎的Polynomial-Time.在此奉上wiki)

数乘和循环子群

在实数域乘法的定义是: n P = \underbrace{P + P + \cdots + P}_{n\ \text{times}}

我们可以用倍加算法(请见上一篇)去做乘法,时间复杂度是 O(\log n) (或者 O(k) ,这里的 kn 的二进制倍数)。

\mathbb{F}_p 上的椭圆曲线的乘法有个很有意思的属性。取一个曲线: y^2 \equiv x^3 + 2x + 3 \pmod{97} 和点 P = (3, 6) ,现在来计算P的所有倍数:

p=(3,6)的所有倍乘的取值只有5个点(0, P, 2P, 3P, 4P )然后不停的循环重复。我们能很容易的发现椭圆曲线上的数乘和模运算的加法非常相似。
  • 0P = 0
  • 1P = (3, 6)
  • 2P = (80, 10)
  • 3P = (80, 87)
  • 4P = (3, 91)
  • P = 0
  • 6P = (3, 6)
  • 7P = (80, 10)
  • ...

到此,我们发现了两个事情:第一,P的倍乘只有5个取值,永远不会出现第6个。第二,他们是循环重复的。我们可以写成这样: ( k 取任意整数)

  • 5kP = 0
  • (5k + 1)P = P
  • (5k + 2)P = 2P
  • (5k + 3)P = 3P
  • (5k + 4)P = 4P

所以呢,这五个式子可以被“压缩”成一个(模运算): kP = (k \bmod{5})P

不仅如此,我们可以立即验证:P的加法是个闭环。(These five points are closed under addition. )这意味着:不论我加的是0, P, 2P, 3P 还是 4P, 结果永远都是这五个点中的一个。Again, 其他点永远不会出现在这根椭圆曲线的结果里。

这个规则同样适用于所有的点,不仅仅是对 P = (3, 6) 。事实上,对于任意的 P :

nP + mP = \underbrace{P + \cdots + P}_{n\ \text{times}} + \underbrace{P + \cdots + P}_{m\ \text{times}} = (n + m)P

这意味着:如果我们将n倍的P进行相加,我们获得的仍然是P的倍数(If we add two multiples of P, we obtain a multiple of P)【由于这个定理太重要了,我把英文也放上来】。(比如,nP的相加是个闭环。)这足够来证明:nP的集合是椭圆曲线形成的群里的一个具有循环性质的子群(the set of the multiples of P is a cyclic subgroup of the group formed by the elliptic curve.)。这里的点 P 叫做循环子群的 生成器 或者 基点。

子群的阶

我们可以扪心自问下,由P生成子群的阶到底是什么?(或者,P的阶是什么?)为了回答这个问题,我们不能使用Schoolf的算法,因为这个算法只能在整个的椭圆曲线上生效,在子群上无效。在解决这个问题之前,我们需要打点地基:

  • 到现在为止,我们已经定义了:阶,就是一个群的点的数量。这个定义仍然是有效的,但是在循环的子群里我们可以下一个新的,与前面的定义相等的定义: P 的阶是最小的正整数 nn 满足的条件是 nP=0 。事实上,我们回顾前面的例子, 5P=0
  • P 的阶和椭圆曲线是有联系的,拉格朗日定理告诉我们,子群的阶是父群的阶的因子。换句话说,如果一个椭圆曲线包含 N 个点,它的一个子群包含 n 个点,那么 nN 的因子。

以上两条规则结合起来给我们指了一条明路,如何根据基点 P 找到子群的阶:

  1. 使用Schoof的算法去计算椭圆曲线的阶 N
  2. 找到 N 所有的因子。
  3. 对于 N 的每一个因子 n ,计算 nP
  4. 找到最小的且满足 nP=0n , n 就是子群的阶。

举个栗子,在 \mathbb{F}_{37} 上的曲线 y^2 = x^3 - x + 3 的阶是 N=42 。它的子群的阶可能是 n=1, 2, 3, 6, 7, 14, 21\, or\, 42 。如果我们代入曲线上的点 P=(2,3) 我们可以发现 P \ne 0, 2P \ne 0, ..., 7P = 0 。因此, P 的阶是7。

请注意,很重要的一点是一定一定要是最小的因子,而不是随机的一个因子。如果我们随机处理一下,我们可能取 n=14 ,但它不是子群的阶,仅仅是一个倍数。

另一个例子:定义在 \mathbb{F}_{29} 椭圆曲线上的方程式 y^2 = x^3 - x + 1 的阶是 N = 37 ,这是一个素数,所以它的子群的阶可能只含有1和37. 很容易我们就可以猜出来,当 n = 1 的时候,子群仅包含一个点,就是0(参考上篇文章的point at infinity)。当 n=N 时,子群包含椭圆曲线的所有的点。

找基点

在ECC算法中,我们想找到一个阶数比较大的子群。所以通常呢,我们会选择一条椭圆曲线,然后去计算它的阶( N ), 选择一个以较大的因子作为子群的阶( n ),最终,依此找到一个合适的基点。也就是说,我们不会选择一个基点然后去计算它的阶,我们会反着来操作一波。

首先,我们要介绍一个术语。拉格朗日定理说, h = N / n 里的 h 永远是一个整数(因为 nN 的因子)。这里的 h 有个名字:辅因子(cofactor of the subgroup)。

现在,思考一下对于椭圆曲线中的每一个点,我们有 NP = 0 ,且 N 是任意一个 n 的倍数。借助辅因子的概念,我们可以写成: n(hP) = 0

假设 n 是素数,这个方程式告诉我们:点 G = hP 生成了一个阶为 n 的子群(除了当 G = hP = 0 ,在这个例子中子群的阶是1)。

现在我们总结一下算法:

  1. 计算椭圆曲线的阶 N
  2. 选择一个阶为 n 的子群。n必须是素数且必须是 N 的因子。【至于为什么一定是素数,请见下一篇文章】
  3. 计算辅因子 h = N / n
  4. 在曲线上选择一个随机的点 P
  5. 计算 G = hP
  6. 如果 G 是0,那么回到步骤4。否则我们就已经找到了阶为 n 和辅因子是 h 的子群的生成器/基点。

请注意,上面这个算法仅仅适用于 n 是素数的情况下。如果 n 不是素数,那么 G 的阶可以是 n 的任何一个因子。

离散对数

当有一条连续的椭圆曲线,我们现在要讨论的问题是:如果我们已知 P和Q ,要想得到 G = hP ,我们应该怎么去计算这个 k ?

这个问题,就是椭圆曲线中大名鼎鼎的离散对数问题,它被认为是个很难很难的问题!【插一句,这也是ECC的核心的核心,也是为什么ECC安全的原因】。到目前为止,没有找到一个能在多项式时间内解出来的算法。因此,也没有数学证明。

这个难题同样也是其他涉及离散对数问题的加密算法的难题,比如DSA算法,D-H密钥交换算法,ElGamal算法。不同点在于,上述算法使用了模幂算法而不是数乘。模幂算法的离散对数问题可以简述为:当我们知道 a和bb = a^k \bmod{p} ,那么如何求 k ?

这两个问题中,值都是“离散”的,因为他们都取自于有限的集合(循环的子群)。而且都是“对数”,就是普通意义上的对数运算。

ECC有趣的地方在于,到今天为止,它的离散问题看上去比其他密码学中的离散问题难多了。这就说明我们可以用更少的位数的整数 k 做到和其他加密算法一样安全级别的加密效果。

其他

下一篇会介绍:键值对的生成,ECDH和ECDSA算法。

【我已经尽量的还原文章了,其中加了一点点个人见解和wiki的简介。阅读愉快:)--xiaopei】

编辑于 03-10