经过前面几篇文章的介绍,相信对公钥密码学有所了解的各位应该已经听说过ECC,ECDH和ECDSA,ECC是椭圆密码学的简称,后面两个是基于椭圆密码学的具体算法。
目前我们可以在当前web和IT世界当中的三大主要技术TSL ,PGP ,SSH 当中都可以找到椭圆曲线密码系统,更不用说Bitcoin 以及其它加密货币了。
在ECC开始流行之前,几乎所有的公钥加密算法都是基于RSA,DSA以及DH,底层的加密系统对应的数学难题是模运算。RSA系列算法依然很重要,常常和ECC一同使用。考虑到RSA系列算法背后的原理比较简单,也很容易解释清楚,而ECC却依然蒙着神秘的面纱,本文将提供一个关于ECC的综述,并解释其安全背后的机理,也一并给出一些具体的例子。文章主要包含以下几个方面的内容:
基于群论在实数域定义的椭圆曲线
基于有限域的椭圆曲线和离散对数问题
密钥对的生成和两个ECC算法:ECDH和ECDSA
ECC破解算法,与RSA算法安全性比较
在开始之前,你需要对集合论、几何学以及模运算有所了解,并且对对称加密和非对称加密比较熟悉,关于秘密学当中对“easy”问题和“hard”问题的定位也要有清晰的认识。
椭圆曲线(Elliptic Curves)
首先,什么是椭圆曲线,Wolfram MathWorld给出了完整的定义 。而ECC算法所采用的椭圆曲线是一类可用Weierstrass公式加以描述的椭圆曲线E E E ,即:
E = { ( x , y ) ∣ y 2 + A 1 y = x 3 + A 2 x 2 + A 3 x + A 4 } E=\{(x,y)|y^2+A_1 y=x^3+A_2 x^2 +A_3x + A_4\}
E = { ( x , y ) ∣ y 2 + A 1 y = x 3 + A 2 x 2 + A 3 x + A 4 }
其中参数A 1 A_1 A 1 至 A 2 A_2 A 2 的不同取值决定椭圆曲线在坐标系中的形状。实际常用的形式是一类称为Weierstrass Normal Form(WNF)的的简化形式,即:
E = { ( x , y ) ∣ y 2 = x 3 + a x + b } E=\{(x,y)|y^2=x^3+ax + b\}
E = { ( x , y ) ∣ y 2 = x 3 + a x + b }
由于ECC算法运算过程中需要使用WMF曲线的切线,因此为了使WNF曲线没有奇异点,即处处光滑可导,需要满足其判别式不为零,即:
Δ = 4 a 3 + 27 b 2 ≠ 0 \Delta=4a^3+27b^2\neq 0
Δ = 4 a 3 + 2 7 b 2 = 0
同时,定义射影平面上的无穷远点,记为0.无穷远点是所有曲线在无穷远处的交点,也被称为理想点。因此,下面的论述当中所采用的完整的WNF椭圆曲线公式为:
E = { ( x , y ) ∈ R 2 ∣ y 2 = x 3 + a x + b , Δ = 4 a 3 + 27 b 2 ≠ 0 } ∪ { 0 } E=\{(x,y) \in \mathbb{R}^2 | y^2=x^3+ax+b,\Delta=4a^3+27b^2\neq0\}\cup\{0\}
E = { ( x , y ) ∈ R 2 ∣ y 2 = x 3 + a x + b , Δ = 4 a 3 + 2 7 b 2 = 0 } ∪ { 0 }
当令b = 1 b=1 b = 1 , a a a 从2递减1到-3的椭圆曲线如下所示:
下面我们再来看两条存在奇异点的曲线,如下图所示:
左边这条曲线的方程为y 2 = x 3 y^2=x^3 y 2 = x 3 ,右边的这条曲线方程为y 2 = x 3 − 3 x + 2 y^2=x^3-3x+2 y 2 = x 3 − 3 x + 2 ,两者都不是合格可用的椭圆曲线。另外,不难发现,所有的椭圆曲线都是关于x x x 轴对称的。
群论(Groups)
从数学的角度,一个群是由一种集合以及定义在该群上的一个二元运算所组成,且符合“群公理”。具体完整的定义如下:假设G \mathbb{G} G 是一个非空集合,+ + + 是它的一个二元运算,如果满足以下条件,则G \mathbb{G} G 和 + + + 构成一个群。
封闭性(closure):若a a a 和 b b b 是集合G \mathbb{G} G 的成员,则存在唯一确定的c ∈ G c \in \mathbb{G} c ∈ G ,使得c = a + b c=a+b c = a + b
结合律(associativity):即对G \mathbb{G} G 中任意元素a , b , c a,b,c a , b , c ,都有( a + b ) + c = a + ( b + c ) (a+b)+c=a+(b+c) ( a + b ) + c = a + ( b + c )
单位元(identify element):存在G \mathbb{G} G 中的一个元素e e e ,对任意所有的G \mathbb{G} G 中的元素a a a ,总有等式e + a = a + e = a e+a=a+e=a e + a = a + e = a 。则将e e e 称为单位元,也称幺元
逆元(inverse):对于G \mathbb{G} G 任意一个a a a ,存在G \mathbb{G} G 中的一个元素b b b ,使得总有a + b = b + a = e a+b=b+a=e a + b = b + a = e (e e e 为单位元),则称a a a 与 b b b 互为逆元素,简称逆元。b b b 记作a − 1 a^{-1} a − 1
群运算的次序很重要,把元素a a a 与元素b b b 结合,所得到的结果不一定与把元素b b b 与元素a a a 结合相同;亦即,a + b = b + a a+b=b+a a + b = b + a (交换律)不一定恒成立。满足交换律的群称为交换群(阿贝尔群,以尼尔斯·阿贝尔命名),不满足交换律的群称为非交换群(非阿贝尔群)。
如果我们将加法作为集合的二元运算,那么加上整数集合Z \mathbb{Z} Z 将得到一个群,且是一个阿贝尔群。而自然数集合N \mathbb{N} N 因为不满足逆元,则无法与加法构成一个群。
群的奇妙之处在于如果你能够上述四个性质,那么你可以获得一些其它有趣的性质。
比如,单位元是独一无二的,逆元也是独一无二的。对于任何一个a a a ,有且仅有一个b b b 满足a + b = 0 a+b=0 a + b = 0 。
椭圆曲线的群法则(The group law for elliptic cirves)
我们可以基于特定的椭圆曲线定义一个群,作如下规定:
群的元素是椭圆曲线上面的点的集合
单位元是在无穷远处的点,记为0
一个元素点P P P 的逆元是其关于x x x 轴对称的点
二元远算操作规则定义如下:在曲线上面找到对齐的在一条直线上面的三个非零的点:P , Q , R P, Q, R P , Q , R ,三者之和为0,即P + Q + R = 0 P+Q+R=0 P + Q + R = 0
对于最后一点,我们的要求仅仅是一条直线上面对齐的三个点,而对齐本身与顺序是无关的,因此,如果P , Q , R P, Q, R P , Q , R 是对齐的,则有P + ( Q + R ) = Q + ( P + R ) = R + ( P + Q ) = . . . = 0 P+(Q+R)=Q+(P+R)=R+(P+Q)=...=0 P + ( Q + R ) = Q + ( P + R ) = R + ( P + Q ) = . . . = 0 ,如此这般,我们也顺带证明了我们所定义的二元操作符是能够同时满足结合律和交换律的,我们在椭圆曲线上面定义的群还是一个阿贝尔群。
几何加法(Geometric addition)
庆幸我们是在一个阿贝尔群当中,我们可以将P + Q + R = 0 P+Q+R=0 P + Q + R = 0 写成P + Q = − R P+Q=-R P + Q = − R 。这个形式的方程,可以让我们得到一种计算两个点P P P 和 Q Q Q 相加之和的几何方法:如果我们画一条直线通过P P P 和 Q Q Q ,那么直线会与曲线相交与第三个点R R R 。那么该点的逆元− R -R − R 即为P + Q P+Q P + Q 的结果。
几何方法一目了然,但是还需要回答一些边界条件:
P = 0 o r Q = 0 P=0 \, or\, Q=0 P = 0 o r Q = 0 :考虑到无穷远点本身并不在这个平面上面,线是画不出来的,但是我们已经定义了0为单位元,对于任意P P P 和 Q Q Q ,都有P + 0 = P 和 0 + Q = Q P+0=P和0+Q=Q P + 0 = P 和 0 + Q = Q
P = − Q P=-Q P = − Q :经过这两个点的直线是一条垂直线,与椭圆曲线不会有第三个交点。但是P P P 是 Q Q Q 的逆元,根据逆元的定义有P + Q = P + ( − P ) = 0 P+Q=P+(-P)=0 P + Q = P + ( − P ) = 0
P = Q P=Q P = Q :会有无数条线经过这个点,情况会有一些复杂。令Q ′ ≠ P Q'\neq P Q ′ = P ,如果我们让Q ′ Q' Q ′ 无限得接近P P P ,则经过它们的直线会变成椭圆曲线的切线,此时有P + P = − R P+P=-R P + P = − R ,其中R R R 是切线与椭圆曲线的交点。如下图所示:
P ≠ Q P \neq Q P = Q ,且找不到第三点R R R :这个情况与上面的情况非常类似,此时过P , Q P,Q P , Q 的线就是椭圆曲线的切线。假设P P P 就是切点,在前面的一个情况当中,P + P = − Q P+P=-Q P + P = − Q ,这个方程现在变成P + Q = − P P+Q=-P P + Q = − P 。反过来,如果Q Q Q 是这个切点,则有P + Q = − Q P+Q=-Q P + Q = − Q 。如下图所示:
现在几何加法已经能够覆盖所有的情况了,你只需要尺规就可以在椭圆曲线上面进行各种加法计算了。如果有兴趣也可以看看本文主要参考资料作者提供的图形化工具 ,这个工具提供了在实数域和离散有限域上面的加法和乘法实现,可以直观的看出这两者的区别,便于对文章的理解。
代数加法(Algebraic addition)
如果要使用计算机进行具体的加法远算,那么要从几何方法切换到代数方法。将以上所描述的规则转化为方程组是直截了当的办法,但是因为涉及到求解三次方程,会非常麻烦,这里仅仅给出最终结果,具体的求解和推导过程可以查阅相关资料,比如wiki百科等。
首先,对于简单的边界条件先排除掉,我们已经知道P + ( − P ) = 0 P+(-P)=0 P + ( − P ) = 0 ,以及P + 0 = 0 + P = P P+0=0+P=P P + 0 = 0 + P = P 。因此,我们只需要考虑两个非零且不对称的点P = ( x P , y P ) , Q = ( x Q , y Q ) P=(x_P,y_P),Q=(x_Q,y_Q) P = ( x P , y P ) , Q = ( x Q , y Q ) 。
如果P P P 和 Q Q Q 不相等,则通过这两个点的直线的斜率为:
m = y P − y Q x P − x Q m=\frac{y_P-y_Q}{x_P-x_Q}
m = x P − x Q y P − y Q
则直线与椭圆曲线的第三个交点R ( x R , y R ) R(x_R,y_R) R ( x R , y R ) 的坐标如下:
或者,等价于:
y R = y Q + m ( x R − x Q ) y_R=y_Q+m(x_R-x_Q)
y R = y Q + m ( x R − x Q )
因此有:( x P , y P ) + ( x Q , y Q ) = ( x R , − y R ) (x_P,y_P)+(x_Q,y_Q)=(x_R,-y_R) ( x P , y P ) + ( x Q , y Q ) = ( x R , − y R ) ,需要注意y R y_R y R 的负号。
为了验证结果的正确性,需要验证以下两点:
R R R 是否在这条曲线上面
P , Q , R P,Q,R P , Q , R 是否共线
验证三点是否共线相对简单,而验证一个点是否在椭圆曲线上面则相对复杂一些,需要求解三次方程。这里我们还是借助前面提到的图形化工具 ,在曲线y 2 = x 3 − 7 x + 10 y^2=x^3-7x+10 y 2 = x 3 − 7 x + 1 0 上面取两点P = ( 1 , 2 ) , Q = ( 3 , 4 ) P=(1,2),Q=(3,4) P = ( 1 , 2 ) , Q = ( 3 , 4 ) ,得到P + Q = − R = ( − 3 , 2 ) P+Q=-R=(-3,2) P + Q = − R = ( − 3 , 2 ) ,我们由此验证下前面的代数表达式是否正确:
m = y P − y Q x P − x Q = 2 − 4 1 − 3 = 1 \begin{aligned}
m &= \frac{y_P-y_Q}{x_P-x_Q} \\
&= \frac{2-4}{1-3} \\
&= 1
\end{aligned}
m = x P − x Q y P − y Q = 1 − 3 2 − 4 = 1
x R = m 2 − x P − x Q = 1 2 − 1 − 3 = − 3 \begin{aligned}
x_R &= m^2-x_P-x_Q \\
&= 1^2-1-3 \\
&= -3
\end{aligned}
x R = m 2 − x P − x Q = 1 2 − 1 − 3 = − 3
y R = y P + m ( x R − x P ) = 2 + 1 × ( − 3 − 1 ) = − 2 = y Q + m ( x R − x Q ) = 4 + 1 × ( − 3 − 3 ) = − 2 \begin{aligned}
y_R &= y_P+m(x_R-x_P) \\
&= 2+1 \times(-3-1) \\
&= -2 \\
&= y_Q+m(x_R-x_Q) \\
&= 4+1 \times(-3-3) \\
&= -2
\end{aligned}
y R = y P + m ( x R − x P ) = 2 + 1 × ( − 3 − 1 ) = − 2 = y Q + m ( x R − x Q ) = 4 + 1 × ( − 3 − 3 ) = − 2
另外,当P P P 或者Q Q Q 其中之一是切点的时候,上述方程依然正确,具体读者可以自行验证。
而当P = Q P=Q P = Q 时,情况会稍微复杂一些:因为x P = x Q x_P=x_Q x P = x Q ,所以关于斜率我们需要采用另外一个不同的方程:
m = 3 x P 2 + a 2 y P m=\frac{3x^2_P+a}{2y_P}
m = 2 y P 3 x P 2 + a
不难发现,正如我们期望的那样,上述m m m 的表达式就是下式的一阶导数:
y P = ± x P 3 + a x P + b y_P=\pm \sqrt{x^3_P+ax_P+b}
y P = ± x P 3 + a x P + b
为了证明这一结果的有效性,只需检查R R R 在这条椭圆曲线上,以及经过R R R 和 P P P 的直线与椭圆曲线只有两个交点。当然,具体证明还是交给相关专业的数学教材,这里只举一个具体的例子:P = Q = ( 1 , 2 ) P=Q=(1,2) P = Q = ( 1 , 2 ) 。
m = 3 x P 2 + a 2 y P = 3 × 1 2 − 7 2 × 2 = − 1 \begin{aligned}
m &= \frac{3x^2_P+a}{2y_P} \\
&= \frac{3\times 1^2-7}{2\times 2} \\
&= -1
\end{aligned}
m = 2 y P 3 x P 2 + a = 2 × 2 3 × 1 2 − 7 = − 1
x R = m 2 − x P − x Q = ( − 1 ) 2 − 1 − 1 = − 1 \begin{aligned}
x_R &= m^2-x_P-x_Q \\
&= (-1)^2-1-1 \\
&= -1
\end{aligned}
x R = m 2 − x P − x Q = ( − 1 ) 2 − 1 − 1 = − 1
y R = y P + m ( x R − x P ) = 2 + ( − 1 ) × ( − 1 − 1 ) = 4 \begin{aligned}
y_R &= y_P+m(x_R-x_P) \\
&= 2+(-1) \times(-1-1) \\
&= 4
\end{aligned}
y R = y P + m ( x R − x P ) = 2 + ( − 1 ) × ( − 1 − 1 ) = 4
这里我们有P + P = − R = ( − 1 , − 4 ) P+P=-R=(-1,-4) P + P = − R = ( − 1 , − 4 ) ,结果正确。
标量乘法(Scalar multiplication)
除了加法,我们可以定义另外一个操作:标量乘法,具体如下:
n P = P + P + . . . + P ⏟ n t i m e s nP=\underbrace{P+P+...+P}_{n\,times}
n P = n t i m e s P + P + . . . + P
其中n n n 是一个自然数。这里我们依然可以借助前面提到的图形化工具 对标量乘法进行演示。
从上面的形式可以看出,计算标量乘法n P nP n P 需要进行n n n 次的加法。如果n n n 有 k k k 个二进制位的话,那么我们的算法复杂度将是O ( 2 k ) O(2^k) O ( 2 k ) ,不过,对于乘法的计算有现成的更为优化的多项式级别复杂度的算法。
其中一个比较著名的是**加倍累加(double and add )**算法。我们用一个具体的例子来解释其原理。不妨取n = 151 n=151 n = 1 5 1 ,其二进制表示形式为:10010111 2 {10010111}_2 1 0 0 1 0 1 1 1 2 。而将二进制转化为十进制的过程可以用2的不同次方的累加表示如下:
从这个角度,我们可以改写上述变量乘法公式:
151 × P = 2 7 × P + 2 4 × P + 2 2 × P + 2 1 × P + 2 0 × P 151\times P=2^7\times P+2^4\times P+2^2\times P + 2^1\times P+2^0\times P
1 5 1 × P = 2 7 × P + 2 4 × P + 2 2 × P + 2 1 × P + 2 0 × P
这样,标量乘法可以表示为多项式乘法类似的算法,具体步骤如下:
得到P P P
翻倍P P P ,得到2 P 2P 2 P
将2 P 2P 2 P 加到P P P (得到2 1 × P + 2 0 × P 2^1\times P+2^0\times P 2 1 × P + 2 0 × P 的结果)
翻倍2 P 2P 2 P ,得到2 2 × P 2^2\times P 2 2 × P
将翻倍的2 2 × P 2^2\times P 2 2 × P 加到上一次的结果当中得到2 2 × P + 2 1 × P + 2 0 × P 2^2\times P+2^1\times P+2^0\times P 2 2 × P + 2 1 × P + 2 0 × P
翻倍2 2 × P 2^2\times P 2 2 × P ,得到2 3 × P 2^3\times P 2 3 × P
不对2 3 × P 2^3\times P 2 3 × P 进行任何操作
翻倍2 3 × P 2^3\times P 2 3 × P ,得到2 4 × P 2^4\times P 2 4 × P
将翻倍的2 4 × P 2^4\times P 2 4 × P 加到上一次的结果当中得到2 4 × P + 2 2 × P + 2 1 × P + 2 0 × P 2^4\times P+2^2\times P+2^1\times P+2^0\times P 2 4 × P + 2 2 × P + 2 1 × P + 2 0 × P
…
最后,我们仅需要7次的翻倍计算和4次的加法计算就可以得到151 × P 151\times P 1 5 1 × P 的结果。
如果这个过程不够清晰,这里有一段实现了这个算法的python脚本:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 def bits (n ): """ Generates the binary digits of n, starting from the least significant bit. bits(151) -> 1, 1, 1, 0, 1, 0, 0, 1 """ while n: yield n & 1 n >>= 1 def double_and_add (n, x ): """ Returns the result of n * x, computed using the double and add algorithm. """ result = 0 addend = x for bit in bits(n): if bit == 1 : result += addend addend *= 2 return result
这里我们分析下这个算法的时间复杂度。如果翻倍和加法都是O ( 1 ) O(1) O ( 1 ) 的话,那么这个算法就是O ( log n ) O(\log n) O ( log n ) ,比起之前的O ( n ) O(n) O ( n ) ,算法时间复杂度大大简化。
对数(Logarithm)
给定n n n 和 P P P ,我们有一个至少是多项式时间复杂度的算法来计算Q = n P Q=nP Q = n P 。但是,如果反过来呢?比如已知Q Q Q 和 P P P 来计算n n n 呢?通常将这种问题叫作对数问题(logarithm problem)。之所以用对数来代替除法是为了与其它的加密系统保持一致。
对于对数问题我们不知道是否有可以称之为“简单”的算法,但是通过一些乘法实验 还是很容易看出一些规律的。比如,以椭圆曲线y 2 = x 3 − 3 x + 1 y^2=x^3-3x+1 y 2 = x 3 − 3 x + 1 和点P = ( 0 , 1 ) P=(0,1) P = ( 0 , 1 ) 为例。我们可以立刻确认,如果n n n 是奇数,n P nP n P 将会在曲线的左半部分;如果n n n 是偶数,n P nP n P 将会在曲线的右半部分。如果我们做更多的实验,我们可以发现更多的规律,最终可以指引我们写出一个基于特定曲线的求解对数问题的特定算法。
当然,对数问题也有一些衍生:离散对数问题。这个将在下一篇文章当中进行讨论,如果我们减小椭圆曲线的域,变量乘法依然“简单”,但是离散对数却变成一个“难题”,这个双重特性是椭圆曲线密码学的基石。
参考资料