EC, Elliptic Curve
ECC, Elliptic Curve Cryptography.
特点:密钥长度比RSA短。
比如,3072 bits RSA的强度等同于256 bits ECC.
基于椭圆曲线上乘法运算的逆运算的困难度。
ECC其实有三种用途 :
- 公钥密码ECC
- 数字签名ECDSA
- 密钥交换ECDH
公钥密码的签名和密钥交换都是基于加密实现的。
运算
EC上的运算都是以EC上的点为对象的。
这些运算相当于包含EC上所有点以及无限远点的集合上的阿贝尔群(交换群)。
椭圆曲线本身是一条光滑的曲线,即实数域R上的椭圆曲线。但ECC使用的椭圆曲线则是在有限域Fp上。有限域Fp有两种:
- 特征数为质数p的素域GF§,在整数集合{0, 1, , , p-1}中定义的加减乘除运算。
- 特征数为2m的扩张域GF(2m),已过时(FIPS 186-5)。
所以,椭圆曲线密码中的曲线并不是线,而是离散的点。
这里我们使用素域。
P是大于3的素数,且
4
a
3
+
27
b
2
≠
0
m
o
d
P
4a^3+27b^2\ne 0 \mod P
4a3+27b2=0modP
这个条件保证曲线不包含奇点(singularities)。
奇点是高中数学导数的内容,是曲线上瞬间改变方向的一个点。
GF§上的EC:
y
2
=
x
3
+
a
x
+
b
m
o
d
P
a
,
b
∈
G
F
(
P
)
y^2=x^3+ax+b \mod P \\ a,b\in GF(P)
y2=x3+ax+bmodPa,b∈GF(P)
这个方程又称为椭圆曲线的维尔斯特拉斯标准形式(Weierstrass normal form)。
EC是始终关于x轴对称的。
P,Q互逆:
P ( x , y ) + Q ( x , − y ) = 0 P(x,y)+Q(x,-y)=0 P(x,y)+Q(x,−y)=0
无穷远点的逆即自己。
P ( x 1 , y 1 ) + Q ( x 2 , y 2 ) = R ( x 3 , y 3 ) { λ = y 2 − y 1 x 2 − x 1 x 3 = λ 2 − x 1 − x 2 y 3 = λ ( x 1 − x 3 ) − y 1 P(x_1,y_1)+Q(x_2,y_2)=R(x_3,y_3) \begin{cases} \lambda=\frac{y_2-y_1}{x_2-x_1}\\ x_3=\lambda^2-x_1-x_2\\ y_3=\lambda(x_1-x_3)-y_1 \end{cases} P(x1,y1)+Q(x2,y2)=R(x3,y3)⎩ ⎨ ⎧λ=x2−x1y2−y1x3=λ2−x1−x2y3=λ(x1−x3)−y1
P=Q时
{ λ = 3 x 1 2 + a 2 y 1 x 3 = λ 2 − 2 x 1 y 3 = λ ( x 1 − x 3 ) − y 1 \begin{cases} \lambda=\frac{3x_1^2+a}{2y_1} x_3=\lambda^2-2x_1 y_3=\lambda(x_1-x_3)-y_1 \end{cases} {λ=2y13x12+ax3=λ2−2x1y3=λ(x1−x3)−y1
负数
A为EC上的一个点,-A就是A的纵坐标取反,即关于x轴的对称点。
加法
A+B
A和B两个点构成一条直线,会经过EC上的另一个点C(c1, c2),则A+B = (c1, -c2)。‘
A+(-A),直线与y轴平行,得不到点C,则将其记为单位元、无穷点、0元素,后续记为0:
◯
(
∞
,
∞
)
◯
(
∞
,
∞
)
+
◯
(
∞
,
∞
)
=
0
+
0
=
0
P
(
x
,
y
)
+
◯
(
∞
,
∞
)
=
P
(
x
,
y
)
\bigcirc(\infty,\infty) \\ \bigcirc(\infty,\infty)+\bigcirc(\infty,\infty)=0+0=0 \\ P(x,y)+\bigcirc(\infty,\infty)=P(x,y) \\
◯(∞,∞)◯(∞,∞)+◯(∞,∞)=0+0=0P(x,y)+◯(∞,∞)=P(x,y)
所以注意,点O不是原点。
倍数
2A
其实就是加法中,B=A。这时直线就取EC的切线,也能得到另一个点C,纵坐标取反就是2A。
而3A,则是2A+A.
逆运算
已知条件如下,求x:
- 椭圆曲线EC
- EC上的生成元点G
- G的x倍点xG(正运算)
这就是椭圆曲线上的离散对数问题。
ECC原理
收发双方已知:
- EC
- base point G
n:生成元G的阶
- nG == 0
- 私钥为随机数k,取值范围{1, 2, , , n-1}
- 公钥Q = kG
( n G = ◯ ) 私钥 : k ∈ { 1 , 2 , . . . , n − 1 } 公钥 : Q = k G M ∈ { 0 , 1 , . . . , n − 1 } (nG=\bigcirc) \\ 私钥: k\in\{1,2,...,n-1\} \\ 公钥: Q=kG \\ M\in\{0,1,...,n-1\} (nG=◯)私钥:k∈{1,2,...,n−1}公钥:Q=kGM∈{0,1,...,n−1}
加密
- 生成随机数r, 取值范围{1, 2, , , n-1}
- 点X1(x1, y1) = rG
- 点X2(x2, y2) = rQ,若横坐标x2 == 0,则重新生成r;
- 明文M作为横坐标m1映射至曲线上一点(m1, m2)
- 公钥加密c = m1 * x2 mod n
- 密文 {X1, xc}
解密
- 还原X2 = d*X1 = (x2, y2)
- 求x2的逆 1/x2 mod n
- m1 = c * 1/x2 mod n
整个过程中,主要是使用点的x坐标。
实现
https://github.com/C0deStarr/CryptoImp/tree/main/pubkey/ecc
- ecc.h
- ecc.c
ECDH原理
Elliptic Curve Diffie-Hellman
相比ECC的公钥加密,私钥解密,ECDH是将加密时的随机数r,换成发送方的私钥。
解密时,接收方把X1换成发送方的公钥。
比如,Alice的私钥为a,Bob的私钥为b,则共享密钥为abG。
加密:C = M + abG
解密:M = C - abG
攻击者已知:
- C
- aG
- bG
但是,攻击者不能根据aG和bG还原出双方私钥a和b,无法计算出abG来解密。
Miracl示例
本人基于ECC加解密修改了一下,把m1*x2改成M+X2,并不是所有消息都能解密成功,bug未解决。但下面这个demo没问题。
EC:
y 2 = x 3 − 2 x − 3 ( m o d 7 ) y^2=x^3-2x-3(\mod 7) y2=x3−2x−3(mod7)
生成元G=(3,2),阶n=10。
明文M(0, 2)
随机数r = 3
接收方
- 私钥k=3
- 公钥kG = (4, 2)
#include "ecc.h"
#include<stdio.h>
#include "miracl.h"
void print_point(epoint* p)
{
big bx = mirvar(0);
big by = mirvar(0);
char x = 0, y = 0;
epoint_get(p, bx, by);
big_to_bytes(1, bx, &x, TRUE);
big_to_bytes(1, by, &y, TRUE);
printf("(%d, %d)\n", x, y);
}
void test_ecc()
{
miracl* mip = mirsys(10, 10);
int i;
int N = 10;
int nP = N + 1;
int nRet = 0;
// y^2 = x^3 -2x - 3 mod 7
big bigA = mirvar(-2);
big bigB = mirvar(-3);
big bigP = mirvar(7);
// G(3, 2)
big gx = mirvar(3);
big gy = mirvar(2);
epoint* epointG = epoint_init();
epoint* epointAdd = epoint_init();
// public key
epoint* epointPubkey = epoint_init();
// private key
big bigK = mirvar(3);
// random
big bigR = mirvar(3);
// message
epoint* epointM = epoint_init();
// cipher (X1, C)
epoint* epointC = epoint_init();
epoint* epointX1 = epoint_init();
// r * public key
epoint* epointX2 = epoint_init();
// dec
epoint* epointDec = epoint_init();
big bx = mirvar(0);
big by = mirvar(0);
big bn = mirvar(1);
//init ecurve
ecurve_init(bigA, bigB, bigP, MR_PROJECTIVE);
//init generator
if (epoint_set(gx, gy, 0, epointG))
{
// epointG is on the active EC
for (i = 0; i < nP; i++)
{
ecurve_mult(bn, epointG, epointAdd);
printf("%2dG: ", i+1);
print_point(epointAdd);
incr(bn, 1, bn);
}
}
// public key
ecurve_mult(bigK, epointG, epointPubkey);
// encrypt
printf("======encrypt========\n");
convert(0, bx);
convert(2, by);
epoint_set(bx, by, 0, epointM);
printf("msg:\n");
print_point(epointM);
epoint_copy(epointM, epointC);
ecurve_mult(bigR, epointG, epointX1);
printf("X1:\n");
print_point(epointX1);
ecurve_mult(bigR, epointPubkey, epointX2);
printf("X2:\n");
print_point(epointX2);
ecurve_add(epointX2, epointC);
printf("cipher: (X2 + M, X1)\n");
print_point(epointC);
print_point(epointX1);
// decrypt
printf("======decrypt========\n");
ecurve_mult(bigK, epointX1, epointX2);
printf("kX1 == X2\n");
print_point(epointX2);
epoint_copy(epointC, epointDec);
ecurve_sub(epointX2, epointDec);
printf("decrypt: C - kX1\n");
print_point(epointDec);
/*
1G: (3, 2)
2G: (2, 6)
3G: (4, 2)
4G: (0, 5)
5G: (5, 0)
6G: (0, 2)
7G: (4, 5)
8G: (2, 1)
9G: (3, 5)
10G: (0, 0)
11G: (3, 2)
======encrypt========
msg:
(0, 2)
X1:
(4, 2)
X2:
(3, 5)
cipher: (X2 + M, X1)
(5, 0)
(4, 2)
======decrypt========
kX1 == X2
(3, 5)
decrypt: C - kX1
(0, 2)
*/
mirexit();
getchar();
return 0;
}
参考资料
《图解密码技术》第3版
ECC — PyCryptodome 3.17.0 documentation
Elliptic Curve Cryptography: a gentle introduction - Andrea Corbellini
FIPS 186-5, Digital Signature Standard (DSS) | CSRC (nist.gov)