4.1、Pohlig-Hellman算法攻击
我们通过前面对ECC的介绍可以得知,ECC是在一个有限域下的计算,椭圆曲线上的点组成的集合构成阿贝尔群。
Pohlig-Hellman算法主要是去破解这个群的阶为光滑数的离散对数。所以我们判断可不可以使用Pohlig-Hellman算法,
得先看椭圆曲线所在群的阶是不是光滑数(Smooth Integer)。
4.1.1、光滑数(Smooth Integer)
如果一个数的所有质因数都小于等于B,那么我们称这个数为B-光滑数(B-Smooth Integer)。
例如:
2
×
3
2
×
5
3
×
7
=
15750
因为
15750
的所有质因数为
{
3
,
5
,
7
}
,
其中
7
为质因数最大的值,所以该
15750
是一个
7
−
光滑数
需要注意的是,
一个数如果被称之为
B
−
光滑数,那么这个
B
不一定是这个数的质因数
例如上面的式子,他也可以叫做
8
−
光滑数、
13
−
光滑数等。
例如:2 \times 3^2 \times 5^3 \times 7 = 15750 \\ 因为15750的所有质因数为\{3,5,7\},\\其中7为质因数最大的值,所以该15750是一个7-光滑数\\ 需要注意的是,\\一个数如果被称之为B-光滑数,那么这个B不一定是这个数的质因数\\ 例如上面的式子,他也可以叫做8-光滑数、13-光滑数等。
例如:2×32×53×7=15750因为15750的所有质因数为{3,5,7},其中7为质因数最大的值,所以该15750是一个7−光滑数需要注意的是,一个数如果被称之为B−光滑数,那么这个B不一定是这个数的质因数例如上面的式子,他也可以叫做8−光滑数、13−光滑数等。
4.1.2、Pohlig-Hellman算法思想
设乘法群 Z p ∗ , 其阶为 n 。 假设 p 为大质数,所以 n = ϕ n = p − 1 设其质因数分解为: n = q 1 e 1 q 2 e 2 q 3 e 3 . . . . q k e k 现在有如下等式: h = g x ( m o d p ) 两边同时求 n q i e i 次幂可以得到: h n q i e i = ( g x ) n q i e i ( m o d p ) 右边式子的 x 可以提出来得到: h n q i e i = ( g n q i e i ) x ( m o d p ) 假设 h i = h n q i e i , g i = ( g n q i e i ) ,可以得到: h i = g i x ( m o d p ) 因为 g i 的阶为 q i e i , 所以上面的等式就是在一个 q i e i 阶的子群上求离散对数 , 这样这个离散对数的范围就变小了, 可以尝试求解这个离散对数, 满足等式的解都模 q i e i 同余 设其中一个解 a i , 那么解可以表示为 : x = a i ( m o d q i e i ) 所以对于每一个 i ( 1 ≤ i ≤ k ) , 都做上诉操作的话,那么就会得到如下的方程组: { x = a 1 ( m o d q 1 e 1 ) x = a 2 ( m o d q 2 e 2 ) . . . x = a k ( m o d q k e k ) 通过剩余定理可以求出方程组的解, 方程组每个解都与 q i e i 的乘积同余,也就是模 n , 因为 n = q 1 e 1 q 2 e 2 q 3 e 3 . . . . q k e k 。 那么其通解为 : x ≡ a ( m o d n ) 这个解也就是 h ≡ g x ( m o d p ) 的解。 设乘法群\mathbb Z_{p}^{*},其阶为n。\\ 假设p为大质数,所以n = \phi{n} = p-1\\ 设其质因数分解为: n = q_1^{e_1} q_2^{e_2} q_3^{e_3} ....q_k^{e_k} \\ 现在有如下等式:\\ h = g^ x (mod p)\\ 两边同时求\frac{n}{q_i^{e_i}}次幂可以得到:\\ h^{\frac{n}{q_i^{e_i}}} = ({g^x})^{\frac{n}{q_i^{e_i}}} (mod p)\\ 右边式子的x可以提出来得到:\\ h^{\frac{n}{q_i^{e_i}}} = (g^{\frac{n}{q_i^{e_i}}})^x (mod p)\\ 假设h_i = h^{\frac{n}{q_i^{e_i}}},g_i = (g^{\frac{n}{q_i^{e_i}}}),可以得到:\\ h_i = g_i^x (mod p)\\ 因为g_i的阶为q_i^{e_i},所以上面的等式就是在一个q_i^{e_i}阶的子群上求离散对数,\\这样这个离散对数的范围就变小了,\\可以尝试求解这个离散对数,\\满足等式的解都模q_i^{e_i} 同余\\ 设其中一个解a_i ,那么解可以表示为:\\ x = a_i (mod q_i^{e_i})\\ 所以对于每一个i(1\leq i \leq k),都做上诉操作的话,那么就会得到如下的方程组:\\ \begin{cases} x = a_1 (mod q_1^{e_1})\\ x = a_2 (mod q_2^{e_2})\\ \ \ \ \ \ \ \ \ \ \ \ .\\ \ \ \ \ \ \ \ \ \ \ \ .\\ \ \ \ \ \ \ \ \ \ \ \ .\\ x = a_k (mod q_k^{e_k})\\ \end{cases} \\ 通过剩余定理可以求出方程组的解,\\方程组每个解都与q_i^{e_i}的乘积同余,也就是模n,\\因为n = q_1^{e_1} q_2^{e_2} q_3^{e_3} ....q_k^{e_k} 。\\那么其通解为:\\ x \equiv a (mod n)\\ 这个解也就是h \equiv g^x (mod p)的解。 设乘法群Zp∗,其阶为n。假设p为大质数,所以n=ϕn=p−1设其质因数分解为:n=q1e1q2e2q3e3....qkek现在有如下等式:h=gx(modp)两边同时求qiein次幂可以得到:hqiein=(gx)qiein(modp)右边式子的x可以提出来得到:hqiein=(gqiein)x(modp)假设hi=hqiein,gi=(gqiein),可以得到:hi=gix(modp)因为gi的阶为qiei,所以上面的等式就是在一个qiei阶的子群上求离散对数,这样这个离散对数的范围就变小了,可以尝试求解这个离散对数,满足等式的解都模qiei同余设其中一个解ai,那么解可以表示为:x=ai(modqiei)所以对于每一个i(1≤i≤k),都做上诉操作的话,那么就会得到如下的方程组:⎩ ⎨ ⎧x=a1(modq1e1)x=a2(modq2e2) . . .x=ak(modqkek)通过剩余定理可以求出方程组的解,方程组每个解都与qiei的乘积同余,也就是模n,因为n=q1e1q2e2q3e3....qkek。那么其通解为:x≡a(modn)这个解也就是h≡gx(modp)的解。
4.1.3、sage脚本
### 不知道对不对,不能用暂时写在这里,后面遇到问题再修改
def Pohlig-Hellman(p,a,b,P,G):
E = EllipticCurve(GF(p),[a,b])
n = E.order()
factors = factor(n)
print(factors)
result = []
factors = [4 ,3 , 1170811] #
for f1 in factors:
t = n //f1
res = discrete_log(t*P,t*G,operation='+')
result += [res]
print(result)
k = crt(result,factors)
return k
4.1.4、例题:
#Alice do this
p=14050339
a=1
b=3243167
E = EllipticCurve(GF(p),[a,b])
G=E(7112688,7410262)
k=random.randrange(1,G.order())
K=k*G
print(K)
#(6562993 : 2753874 : 1)
######################################
######################################
#Bob do this
import random
flag="0xGame{xxxxxxxx}"
table='abcdefghijklmnopqrstuvwxyz'
m=flag[7:-1]
m1=m[:4]
m2=m[4:]
m_1=''
m_2=''
for i in m1:
s=str(table.index(i)+1)
if len(s)<2:
s='0'+s
m_1+=s
for i in m2:
s=str(table.index(i)+1)
if len(s)<2:
s='0'+s
m_2+=s
x=int(m_1)
y=int(m_2)
P=E(x,y)
r=random.randrange(1,G.order())
C1=P+r*K
C2=r*G
print(C1)
#(3095063 : 1465594 : 1)
print(C2)
#(6437074 : 4385056 : 1)
######################################
######################################
#Eva wants to know the flag.
#Can you help Eva?
4.1.5、exp.sage
p=14050339
a=1
b=3243167
E = EllipticCurve(GF(p),[a,b])
n = E.order()
G=E(7112688,7410262)
K = E(6562993 , 2753874) # K = k*G
C1 = E(3095063 , 1465594 ) # C1 = M + k*K
C2 = E(6437074 ,4385056 ) # C2 = k*G
factors = factor(n)
print(factors)
result = []
factors = [4 ,3 , 1170811]
for f1 in factors:
t = n //f1
res = discrete_log(t*K,t*G,operation='+')
result += [res]
print(result)
k = crt(result,factors)
M = C1-k*C2
print(M)
table='abcdefghijklmnopqrstuvwxyz'
a=(12050118 ,14050303)
def flag(a):
flag=""
x=a[0]
y=a[1]
m=str(x)+str(y)
for i in range(0,16,2):
flag+=str(table[int(m[i:i+2])-1])
return flag
print("0xGame{"+flag(a)+"}")
4.2、E(Fp)=p,曲线的阶等于模数p,(Smart’s Attack)
Fp指的是有限域,E表示曲线,曲线E的阶等于有限域Fp的模数p,这种曲线被称为异常曲线(anomalous curves)。
因为E的阶就是椭圆曲线上所有点的个数,也就是说,椭圆曲线上所有点的个数等于p。
4.2.1、Smart’s Attack原理
- 异常子群
异常子群是椭圆曲线上哪些整个子群的阶,是有限域的模数p的整倍数的点的集合。
因为异常曲线的阶就等于p,所以异常曲线本身就是一个异常子群。
- Smart’s 攻击
异常曲线可以被特定的攻击方式所破解,其中一个著名的是所谓的Smart’s 攻击。
Smart’s 攻击利用了椭圆曲线上的点可以通过一个同态映射(有限域同态映射)映射到有限域的模数p的加法群上去。
在加法群上解ECDLP简单的多。
4.2.2、sage脚本
p =
A =
B =
E = EllipticCurve(GF(p),[A,B])
P = E(,)
Q = E(,)
def SmartAttack(P,Q,p):
E = P.curve()
Eqp = EllipticCurve(Qp(p, 2), [ ZZ(t) + randint(0,p)*p for t in E.a_invariants() ])
P_Qps = Eqp.lift_x(ZZ(P.xy()[0]), all=True)
for P_Qp in P_Qps:
if GF(p)(P_Qp.xy()[1]) == P.xy()[1]:
break
Q_Qps = Eqp.lift_x(ZZ(Q.xy()[0]), all=True)
for Q_Qp in Q_Qps:
if GF(p)(Q_Qp.xy()[1]) == Q.xy()[1]:
break
p_times_P = p*P_Qp
p_times_Q = p*Q_Qp
x_P,y_P = p_times_P.xy()
x_Q,y_Q = p_times_Q.xy()
phi_P = -(x_P/y_P)
phi_Q = -(x_Q/y_Q)
k = phi_Q/phi_P
return ZZ(k)
r = SmartAttack(P, Q, p)
print(r)
/**
脚本来自Lazzaro 佬:
* Lazzaro @ https://lazzzaro.github.io
*/
4.2.3、例题:
# Sage
from secret import flag
from Crypto.Util.number import *
assert(flag[:5] == b"flag{" and flag[-1:] == b"}")
flag = flag[5:-1]
d1 = bytes_to_long(flag[:len(flag)//2])
d2 = bytes_to_long(flag[len(flag)//2:])
N1 = 27544759469094453505371358768052861416297003882211878831861112512567899543941
A1 = 4208715803791813173086894172778966025419787767340027559010619240548499823390
B1 = 11846440123913040489420209031751160809904311707943252241515965930654415480691
P1x = 479750084250968709343887919962436485997147832319843477221083468203689368148
P1y = 15452861783577624143044213767588871736433639621547613407582902947429567101675
P1 = (P1x,P1y)
E1 = EllipticCurve(Zmod(N1), [0, 0, 0, A1, B1])
P1 = E1(P1)
Q1 = d1 * P1
N2 = 6471339743593595797696002766822660599108196938080465998531085409467
A2 = 3199218821393204771660095172457569312269694438403110131957204042314
B2 = 762889472027318213897694878260359911054972690369935049954326689904
P2x = 2557373437970770011124755960432555084678930336188254243278984381842
P2y = 4442763096366920105760404533052204677305995021662082361185473321644
P2 = (P2x,P2y)
E2 = EllipticCurve(Zmod(N2), [0, 0, 0, A2, B2])
P2 = E2(P2)
Q2 = d2 * P2
print(Q1)
print(Q2)
'''
(14736970297054248276364510675718632926198693034158620007675880103924809577805 : 3447209262654420855289144268810543114387612255490962015335062266658385100211 : 1)
(4834036103940457959470026215023033401071737087504569417466448644066 : 5511016821581393405975510064568222454318072088628361854656950557373 : 1)
'''
4.2.4、exp.sage
## flag1
N1 = 27544759469094453505371358768052861416297003882211878831861112512567899543941
p1 = 92636417177965240871815246762704348071
p2 = 297342668339361548416629796745639177971
a = 4208715803791813173086894172778966025419787767340027559010619240548499823390
b = 11846440123913040489420209031751160809904311707943252241515965930654415480691
P1x = 479750084250968709343887919962436485997147832319843477221083468203689368148
P1y = 15452861783577624143044213767588871736433639621547613407582902947429567101675
P2x = 14736970297054248276364510675718632926198693034158620007675880103924809577805
P2y = 3447209262654420855289144268810543114387612255490962015335062266658385100211
# P1 = (P1x,P1y)
# P2 = (P2x,P2y)
E1 = EllipticCurve(Zmod(p1), [0, 0, 0, a, b])
P1 = E1([P1x,P1y])
Q1 = E1([P2x,P2y])
n1 = E1.order()
print(n1)
print(factor(n1)) ## 会发现可以分解,那么就可以使用Pohlig-Hellman算法攻击。
d1p = P1.discrete_log(Q1)
print("d1p:",d1p)
E2 = EllipticCurve(Zmod(p2), [0, 0, 0, a, b])
P2 = E2([P1x,P1y])
Q2 = E2([P2x,P2y])
def SmartAttack(P,Q,p):
E = P.curve()
Eqp = EllipticCurve(Qp(p, 2), [ ZZ(t) + randint(0,p)*p for t in E.a_invariants() ])
P_Qps = Eqp.lift_x(ZZ(P.xy()[0]), all=True)
for P_Qp in P_Qps:
if GF(p)(P_Qp.xy()[1]) == P.xy()[1]:
break
Q_Qps = Eqp.lift_x(ZZ(Q.xy()[0]), all=True)
for Q_Qp in Q_Qps:
if GF(p)(Q_Qp.xy()[1]) == Q.xy()[1]:
break
p_times_P = p*P_Qp
p_times_Q = p*Q_Qp
x_P,y_P = p_times_P.xy()
x_Q,y_Q = p_times_Q.xy()
phi_P = -(x_P/y_P)
phi_Q = -(x_Q/y_Q)
k = phi_Q/phi_P
return ZZ(k)
n2 = E2.order()
print(n2)
print(factor(n2)) ## 会发现分解不出来 但是和p2相同,可以使用smart's attack
d2p = SmartAttack(P2,Q2,p2)
print("d2p:",d2p)
crt([d2p,d1p],[P2.order(),P1.order()])
from Crypto.Util.number import *
long_to_bytes(8903278410456399320332481117135260654778214)
## flag2
N2 = 6471339743593595797696002766822660599108196938080465998531085409467
a = 3199218821393204771660095172457569312269694438403110131957204042314
b = 762889472027318213897694878260359911054972690369935049954326689904
P1x = 2557373437970770011124755960432555084678930336188254243278984381842
P1y = 4442763096366920105760404533052204677305995021662082361185473321644
p1 = 92636417177965240871815246762704348071
p2 = 69857405335111415530599248077
P2x = 4834036103940457959470026215023033401071737087504569417466448644066
P2y = 5511016821581393405975510064568222454318072088628361854656950557373
# P1 = (P1x,P1y)
# P2 = (P2x,P2y)
E1 = EllipticCurve(Zmod(p1), [0, 0, 0, a, b])
P1 = E1([P1x,P1y])
Q1 = E1([P2x,P2y])
n1 = E1.order()
print(n1)
print(factor(n1)) ## 会发现可以分解,那么就可以使用Pohlig-Hellman算法攻击。
d1p = P1.discrete_log(Q1)
print("d1p:",d1p)
E2 = EllipticCurve(Zmod(p2), [0, 0, 0, a, b])
P2 = E2([P1x,P1y])
Q2 = E2([P2x,P2y])
n2 = E2.order()
print(n2)
print(factor(n2)) ## 会发现可以分解,那么就可以使用Pohlig-Hellman算法攻击。
d2p = P2.discrete_log(Q2)
print("d2p:",d2p)
crt([d2p,d1p],[P2.order(),P1.order()])
# from Crypto.Util.number import *
# long_to_bytes(3939240554531229245919495024969655604621873)
4.3、超奇异椭圆曲线 (MOV攻击)
4.3.1、超奇异椭圆曲线
超奇异椭圆曲线是椭圆曲线密码学领域中一类具有特殊性质的椭圆曲线。
- 有限域上椭圆曲线的阶
有限域上椭圆曲线群中的元素个数为有限域上椭圆曲线的阶, 一般用 E ( F q ) 表示 有限域上椭圆曲线群中的元素个数为有限域上椭圆曲线的阶,\\ 一般用E(\mathbb F_q)表示 有限域上椭圆曲线群中的元素个数为有限域上椭圆曲线的阶,一般用E(Fq)表示
-
有限域上椭圆曲线的迹
有限域上椭圆曲线的迹为满足 E ( F q ) = q + 1 − t 其中 t 为椭圆曲线的迹 有限域上椭圆曲线的迹为满足E(\mathbb F_q) = q+1-t \\ 其中t为椭圆曲线的迹 有限域上椭圆曲线的迹为满足E(Fq)=q+1−t其中t为椭圆曲线的迹 -
超奇异椭圆曲线
对于有限域 F q 上的椭圆曲线 E , 如果 F q 的特性 p 整除 t , 那么称这个椭圆曲线为超奇异椭圆曲线。 假设这个域为素数域也就是说 q 为素数,那么他的特性 p 就是 q 。 假设 E ( F q ) 为 n ,那么 t = q + 1 − n 。 也就是说: t = 0 ( m o d p ) q + 1 − n = 0 ( m o d q ) 满足这个式子,他是超奇异椭圆曲线。 对于有限域\mathbb F_q上的椭圆曲线E,\\如果\mathbb F_q的特性p整除t,\\那么称这个椭圆曲线为超奇异椭圆曲线。\\ 假设这个域为素数域也就是说q为素数,那么他的特性p就是q。\\ 假设E(\mathbb F_q) 为 n,那么t=q+1-n。\\ 也就是说:\\ t = 0 \ \ (mod p) \\ q+1-n=0\ \ (mod q)\\ 满足这个式子,他是超奇异椭圆曲线。 对于有限域Fq上的椭圆曲线E,如果Fq的特性p整除t,那么称这个椭圆曲线为超奇异椭圆曲线。假设这个域为素数域也就是说q为素数,那么他的特性p就是q。假设E(Fq)为n,那么t=q+1−n。也就是说:t=0 (modp)q+1−n=0 (modq)满足这个式子,他是超奇异椭圆曲线。
4.3.2、MOV攻击(sage脚本)
满足超奇异椭圆曲线的条件的话那么直接调用脚本吧。
### MOV攻击脚本 取之https://zhuanlan.zhihu.com/p/421541257
# 初始化椭圆曲线的参数A2, B2, P2x, P2y, Q2x, Q2y以及质数p2
A2 = #需要填写
B2 = #需要填写
P2x = #需要填写
P2y = #需要填写
Q2x = #需要填写
Q2y = #需要填写
p2 = #需要填写
k = 2 # 嵌入度,需要填写 嵌入度k是满足k>=2,且阶n整除p^k−1的最小k。
# 如果n= q+1 的情况下,当k等于2 ,那么p^2 - 1 =(p-1)*(p+1),所以n一定整除p^k-1,所以k一般为2。
_P2 = (P2x,P2y) # 定义点P2的坐标
_Q2 = (Q2x,Q2y) # 定义点Q2的坐标
# Pohlig Hellman算法的注释示例
# d2p = P2p.discrete_log(Q2p) # 离散对数的示例值
# d2p = 64863796476861801236088764479
# 定义有限域F1
F1 = GF(p2)
# 在有限域F1上创建椭圆曲线E1
E1 = EllipticCurve(F1, [0, 0, 0, A2, B2])
# 定义扩展有限域F2
F2 = GF(p2^k)
# 创建从F1到F2的同态映射phi
phi = Hom(F1, F2)(F1.gen().minpoly().roots(F2)[0][0])
# 在扩展有限域F2上创建椭圆曲线E2
E2 = EllipticCurve(F2 ,[0, 0, 0, A2, B2])
# 计算椭圆曲线E1的阶
n = E1.order()
# 定义E1上的点P1和R1
P1 = E1(_P2)
R1 = E1(_Q2)
# 将点P1和R1映射到扩展椭圆曲线E2上的点P2和R2
P2 = E2(phi(P1.xy()[0]), phi(P1.xy()[1]))
R2 = E2(phi(R1.xy()[0]), phi(R1.xy()[1]))
# 计算系数
cn1 = p2 + 1
coeff = ZZ(cn1 / n)
# 在椭圆曲线E2上生成一个随机点Q
Q = coeff * E2.random_point()
# 计算点P2和Q的Weil配对alpha
alpha = P2.weil_pairing(Q, n)
# 计算点R2和Q的Weil配对beta
beta = R2.weil_pairing(Q, n)
# 计算离散对数d
d = beta.log(alpha)
# 打印离散对数d
print(d)
4.3.3、例题:
from Crypto.Util.number import *
a = 3199218821393204771660095172457569312269694438403110131957204042314
b = 762889472027318213897694878260359911054972690369935049954326689904
q = 69857405335111415530599248077
flag = b"flag{test}"
m = bytes_to_long(flag)
E = EllipticCurve(GF(q),[a,b])
n = E.order()
assert ((q+1-n) % q == 0)
G = E.gen(0)
P = m*G
print(G)
print(P)
#(18683499524655738422693277245 : 604168370362104182728658798 : 1)
#(40441330983159326501963716627 : 48676457940455286659229939755 : 1)
4.3.4、exp.sage
A2 = 3199218821393204771660095172457569312269694438403110131957204042314
B2 = 762889472027318213897694878260359911054972690369935049954326689904
P2x = 69536212148091099878921365078
P2y = 41800554089087360052589801616
Q2x = 1855081608022138031155777962
Q2y = 41825786773805342875083540156
p2 = 69857405335111415530599248077
_P2 = (P2x,P2y)
_Q2 = (Q2x,Q2y)
# Pohlig Hellman
# d2p = P2p.discrete_log(Q2p) # 64863796476861801236088764479
# d2p = 64863796476861801236088764479
k = 2
F1 = GF(p2)
E1 = EllipticCurve(F1, [0, 0, 0, A2, B2])
F2 = GF(p2^k)
phi = Hom(F1, F2)(F1.gen().minpoly().roots(F2)[0][0])
E2 = EllipticCurve(F2 ,[0, 0, 0, A2, B2])
n = E1.order()
P1 = E1(_P2)
R1 = E1(_Q2)
P2 = E2(phi(P1.xy()[0]), phi(P1.xy()[1]))
R2 = E2(phi(R1.xy()[0]), phi(R1.xy()[1]))
cn1 = p2 + 1
coeff = ZZ(cn1 / n)
Q = coeff * E2.random_point()
alpha = P2.weil_pairing(Q, n)
beta = R2.weil_pairing(Q, n)
d = beta.log(alpha)
print(d)
from Crypto.Util.number import *
print(long_to_bytes(d))