维纳攻击

一、攻击条件

设$$N=pq,q<p<2q,$$若:

$$d < \frac {1}{3}N^{\frac {1}{4}}$$

给出$$N,e(ed \equiv 1 \pmod {\lambda (N)},$$则攻击者可以恢复d

针对维纳攻击的对策:

  1. 选择较大公钥:$$将e替换为e’=e+k \lambda(N),当e’足够大即e’>N^{\frac {3}{2}}$$
  2. 中国剩余定理:$$选择d使得\quad dp \equiv d \pmod {p-1},dq \equiv d \pmod {q-1},但d本身不小,那么可以:$$

$$计算M_p \equiv C^dp \pmod p,Mq \equiv c^dq \pmod q$$

$$利用中国剩余定理:M \equiv M_p \pmod p,M\equiv M_q \pmod q,其中0 \leq M <N$$

$$由于d模 \lambda (N)可能很大,故维纳攻击并不适用$$

二、攻击原理

How to attack

$$记G=gcd(p-1,q-1),\lambda(N)=lcm(p-1,q-1),即G \cdot \lambda(N)=\phi(N)$$

$$由ed \equiv 1 \pmod {\lambda (N)},设ed=K \cdot \lambda (N)+1 $$

$$则ed=\frac {K}{G}(p-1)(q-1)+1$$

$$记k=\frac {K}{gcd(K,G)},g=\frac {G}{gcd(K,G)},代入的:$$

$$ed=\frac {k}{g} (p-1)(q-1)+1$$

两边同时除以dpg得:

$$\frac {e}{pq}=\frac {k}{dg}(1-\frac {p+q-1- \frac {g}{k}}{pq})$$

$$故 \frac {e}{pq}略小于 \frac {k}{dg}$$

Proof

$$已知ed-k \lambda (N)=1,那么:$$

$$\lvert \frac {e}{\lambda (N)}-\frac {k}{d} \rvert =\frac {1}{d \lambda (N)}$$

$$两边同时乘以 \frac {1}{G}得:$$

$$\lvert \frac {e}{\phi (N)}-\frac {k}{Gd} \rvert =\frac {1}{d \phi (N)}$$

$$也就是说 \frac {k}{Gd} 和 \frac {e}{\phi(N)}是很接近的,\phi (N)也可以用N来近似$$

$$又$$

$$\lvert p+q-1 \rvert <3 \sqrt {N} \ \lvert N-\phi(N) \rvert<3 \sqrt {N}$$

$$那么用N代替\phi(N)得到:$$

$$\lvert \frac {e}{N}-\frac {k}{Gd} \rvert=\lvert \frac{edG-kN}{NGd} \rvert =\lvert \frac{edG-k\phi(n)-kN+k\phi (N)}{NGd} \rvert \ \qquad \qquad \qquad \qquad \qquad \qquad=\lvert \frac {1-k(N-\phi(N))}{NGd} \rvert \ \qquad \qquad \qquad \qquad \qquad \qquad< \lvert \frac {-k(N-\phi(N))}{NGd} \rvert\ \qquad \qquad \qquad \qquad \qquad \qquad<\frac {3k \sqrt N}{NGd} \ \qquad \qquad \qquad \qquad \qquad \qquad \leq \frac {3k}{d \sqrt N}$$

$$由e<\lambda(N),k \lambda(N)d:$$

$$k\frac{1}{2d^2}$$

$$那么:$$

$$\lvert \frac{e}{N}-\frac{k}{Gd} \rvert<\frac {3k}{d\sqrt {N}}<\frac {1}{2d^2}$$

$$如果 \lvert x-\frac{a}{b} \rvert <\frac{1}{2b^2},那么x收敛于\frac {a}{b},即\frac {e}{N}收敛于\frac {k}{Gd},因此算法可以找到\frac {k}{Gd}$$

三、工具

https://github.com/orisano/owiener

1
2
3
4
5
6
7
8
9
10
11
12
import owiener

e = 30749686305802061816334591167284030734478031427751495527922388099381921172620569310945418007467306454160014597828390709770861577479329793948103408489494025272834473555854835044153374978554414416305012267643957838998648651100705446875979573675767605387333733876537528353237076626094553367977134079292593746416875606876735717905892280664538346000950343671655257046364067221469807138232820446015769882472160551840052921930357988334306659120253114790638496480092361951536576427295789429197483597859657977832368912534761100269065509351345050758943674651053419982561094432258103614830448382949765459939698951824447818497599
n = 109966163992903243770643456296093759130737510333736483352345488643432614201030629970207047930115652268531222079508230987041869779760776072105738457123387124961036111210544028669181361694095594938869077306417325203381820822917059651429857093388618818437282624857927551285811542685269229705594166370426152128895901914709902037365652575730201897361139518816164746228733410283595236405985958414491372301878718635708605256444921222945267625853091126691358833453283744166617463257821375566155675868452032401961727814314481343467702299949407935602389342183536222842556906657001984320973035314726867840698884052182976760066141
d = owiener.attack(e, n)

if d is None:
print("Failed")
else:
print("Hacked d={}".format(d))

# Hacked d=4221909016509078129201801236879446760697885220928506696150646938237440992746683409881141451831939190609743447676525325543963362353923989076199470515758399
1
2
3
4
5
6
7
8
9
10
11
12
13
14
from Crypto.Util.number import *

n = ?
e = ?
c = ?

Ge = Matrix(ZZ, 2, 2, [e, isqrt(n), n, 0])
L = Ge.LLL()
for row in L:
if row[1]%isqrt(n) == 0:
d = row[1]//isqrt(n)
m = pow(c, d, n)
print(long_to_bytes(m))
break

扩展维纳攻击

guo

$$对于两个e的情况,考虑:$$

$$e_1d_1g-k_1(p-1)(q-1)=g \ e_2d_2g-k_2(p-1)(q-1)=g$$

$$进而推出:$$

$$k_2d_1e_1-k_1d_2e_2=k_2-k_1$$

$$两边同时除以k_2d_1e_2:$$

$$\frac {e_1}{e_2}-\frac{k_1d_2}{k_2d_1}=\frac {k_2-k_1}{k_2d_1e_2}$$

$$设d_i

$$\frac {k_1d_2}{k_2d_1}是\frac {e_1}{e_2}的连分数近似,当k_2和d_1最多为N^\alpha而且g很小,有:$$

$$\alpha <\frac {1}{3}-\epsilon(\epsilon >0)$$

$$然而得到了\frac {k_1d_2}{k_2d_1}还是无法分解$$

一、攻击原理

$$记d_ige_i-k_iN=g+k_is为维纳等式,记W_i,k_id_je_j-k_jd_ie_i=k_i-k_j为郭等式G_{i,j}$$

待补充

二、两个小解密指数

构造

$$选取关系W_1,G_{1,2}.W_1W_2,有:$$

$$d_1 g e_1- k_1 N = g+k_1s$$

$$k_1d_2e_2-k_2d_1e_1=k_1-k_2$$

$$d_1d_2g^2e_1e_2-d_1gk_2e_1N-d_2gk_1e_2N+k_1k_2N^2=(g+k_1s)(g+k_2s)$$

$$将第一个式子乘上k_2,那么便可构造格:$$

$$\begin {pmatrix} k_1k_2&d_1gk_2&d_2gk_1&d_1d_2g^2 \end {pmatrix} \begin {pmatrix} 1&-N&0&N^2 \ &e_1&-e_1&-e_1N \ &&e_2&-e_2N \ &&&e_1e_2 \end {pmatrix} = $$

$$\begin {pmatrix} k_1k_2 &k_2(g+k_1s)&g(k_1-k_2)&(g+k_1s)(g+k)2s) \end {pmatrix}$$

$$等式右边向量的各方向上大小为N^{2\alpha_2},N^{\frac {1}{2+2\alpha_2}},N^{\alpha_2},N^{1+2\alpha_2},为了使大小相等,我们可以考虑构造矩阵:$$

$$D=\begin {pmatrix} N&&&\ &N^{\frac{1}{2}}&&\ &&N^{1+\alpha_2}&\ &&&1 \end {pmatrix}$$

$$最终构造的矩阵为:$$

$$L_2=\begin {pmatrix} 1&-N&0&N^2 \ &e_1&-e_1&-e_1N \ &&e_2&-e_2N \ &&&e_1e_2 \end {pmatrix}*D$$

$$检查一下向量\mathbf b有$$

$$| \mathbf bL_2|<2N^{1+2\alpha_2}$$

$$那么便可以使用格基规约算法LLL得到\mathbf b,进而求解\frac {\mathbf b[1]}{\mathbf b[0]}即\frac{d_1g}{k_1},那么:$$

$$\phi (N)=\frac {edg}{k}-\frac{g}{k}$$

$$假设这些格中最短向量长度为\Delta^{\frac{1}{4}-\epsilon} (\Delta =det(L_2)=N^{\frac{13}{2+\alpha_2}})$$

$$通常情况下,随机格中最短向量的长度大约为闵可夫斯基界:2\Delta^{\frac{1}{n}}$$

$$所以当\mathbf b是最短向量时:$$

$$N^{1+2\alpha_2}<\frac{1}{c_2}(N^{\frac{13}{2+\alpha_2}})^{\frac{1}{4}}$$

$$其中c_2是一个小常数(忽略),对于N的指数:$$

$$1+2\alpha_2<\frac {13}{4(2+\alpha_2)} \Rightarrow \alpha_2< \frac{5}{14}$$

$$因此,当\alpha_2<\frac{5}{14}-\epsilon’时目标向量比闵可夫斯基界短$$

模版

1
2
3
4
5
6
7
8
9
10
11
12
13
from Crypto.Util.number import *

e1 = ?
e2 = ?
N = ?
a = 5/14
D = diagonal_matrix(ZZ, [N, int(N^(1/2)), int(N^(1+a)), 1])
M = matrix(ZZ, [[1, -N, 0, N^2], [0, e1, -e1, -e1*N], [0, 0, e2, -e2*N], [0, 0, 0, e1*e2]])*D
L = M.LLL()
t = vector(ZZ, L[0])
x = t * M^(-1)
phi = int(x[1]/x[0]*e1)
d = inverse(e,phi)

三、三个小解密指数

构造

之后补充

模版

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
e1 = ?
e2 = ?
e3 = ?
n = ?
a = 2/5
D = diagonal_matrix(ZZ,[int(n^1.5), n, int(n^(a+1.5)), int(n^0.5), int(n^(a+1.5)), int(n^(a+1)), int(n^(a+1)), 1])
L = Matrix(ZZ,[[1, -n, 0, n^2, 0, 0, 0, -n^3],
[0, e1, -e1, -n*e1, -e1, 0, n*e1, n^2*e1],
[0, 0, e2, -n*e2, 0, n*e2, 0, n^2*e2],
[0, 0, 0, e1*e2, 0, -e1*e2, -e1*e2, -n*e1*e2],
[0, 0, 0, 0, e3, -n*e3, -n*e3, n^2*e3],
[0, 0, 0, 0, 0, e1*e3, 0, -n*e1*e3],
[0, 0, 0, 0, 0, 0, e2*e3, -n*e2*e3],
[0, 0, 0, 0, 0, 0, 0, e1*e2*e3]]) * D
Ge = L.LLL()[0]
x = vector(ZZ, Ge) / L
phi = int(x[1]/x[0]*e1)
d = inverse(e,phi)

四、四个小解密指数

构造

之后补充

模版

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
e1 = ?
e2 = ?
e3 = ?
n = ?
a = 2/5
D = diagonal_matrix(ZZ,[int(n^1.5), n, int(n^(a+1.5)), int(n^0.5), int(n^(a+1.5)), int(n^(a+1)), int(n^(a+1)), 1])
L = Matrix(ZZ,[[1, -n, 0, n^2, 0, 0, 0, -n^3],
[0, e1, -e1, -n*e1, -e1, 0, n*e1, n^2*e1],
[0, 0, e2, -n*e2, 0, n*e2, 0, n^2*e2],
[0, 0, 0, e1*e2, 0, -e1*e2, -e1*e2, -n*e1*e2],
[0, 0, 0, 0, e3, -n*e3, -n*e3, n^2*e3],
[0, 0, 0, 0, 0, e1*e3, 0, -n*e1*e3],
[0, 0, 0, 0, 0, 0, e2*e3, -n*e2*e3],
[0, 0, 0, 0, 0, 0, 0, e1*e2*e3]]) * D
Ge = L.LLL()[0]
x = vector(ZZ, Ge) / L
phi = int(x[1]/x[0]*e1)
d = inverse(e,phi)

五、一般情况

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
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
from Crypto.Util.number import *
isdigit = lambda x: ord('0') <= ord(x) <= ord('9')

def my_permutations(g, n):
sub = []
res = []
def dfs(s, prev):
if len(s) == n:
res.append(s[::])
for i in g:
if i in s or i < prev:
continue
s.append(i)
dfs(s, max(prev, i))
s.remove(i)
dfs(sub, 0)
return res

class X3NNY(object):
def __init__(self, exp1, exp2):
self.exp1 = exp1
self.exp2 = exp2

def __mul__(self, b):
return X3NNY(self.exp1 * b.exp1, self.exp2 * b.exp2)

def __repr__(self):
return '%s = %s' % (self.exp1.expand().collect_common_factors(), self.exp2)

class X_Complex(object):
def __init__(self, exp):
i = 0
s = '%s' % exp
while i < len(s):
if isdigit(s[i]):
num = 0
while i < len(s) and isdigit(s[i]):
num = num*10 + int(s[i])
i += 1
if i >= len(s):
self.b = num
elif s[i] == '*':
self.a = num
i += 2
elif s[i] == '/':
i += 1
r = 0
while i < len(s) and isdigit(s[i]):
r = r*10 + int(s[i])
i += 1
self.b = num/r
else:
i += 1
if not hasattr(self, 'a'):
self.a = 1
if not hasattr(self, 'b'):
self.b = 0

def WW(e, d, k, g, N, s):
return X3NNY(e*d*g-k*N, g+k*s)
def GG(e1, e2, d1, d2, k1, k2):
return X3NNY(e1*d1*k2- e2*d2*k1, k2 - k1)

def W(i):
e = eval("e%d" % i)
d = eval("d%d" % i)
k = eval("k%d" % i)
return WW(e, d, k, g, N, s)

def G(i, j):
e1 = eval("e%d" % i)
d1 = eval("d%d" % i)
k1 = eval("k%d" % i)

e2 = eval("e%d" % j)
d2 = eval("d%d" % j)
k2 = eval("k%d" % j)

return GG(e1, e2, d1, d2, k1, k2)

def R(e, sn): # min u max v
ret = X3NNY(1, 1)
n = max(e)
nn = len(e)
l = set(i for i in range(1, n+1))
debug = ''
u, v = 0, 0
for i in e:
if i == 1:
ret *= W(1)
debug += 'W(%d)' % i
nn -= 1
l.remove(1)
u += 1
elif i > min(l) and len(l) >= 2*nn:
ret *= G(min(l), i)
nn -= 1
debug += 'G(%d, %d)' % (min(l), i)
l.remove(min(l))
l.remove(i)
v += 1
else:
ret *= W(i)
l.remove(i)
debug += 'W(%d)' % i
nn -= 1
u += 1
# print(debug, end = ' ')
return ret, u/2 + (sn - v) * a

def H(n):
if n == 0:
return [0]
if n == 2:
return [(), (1,), (2,), (1, 2)]
ret = []
for i in range(3, n+1):
ret.append((i,))
for j in range(1, i):
for k in my_permutations(range(1, i), j):
ret.append(tuple(k + [i]))
return H(2) + ret

def CC(exp, n):
cols = [0 for i in range(1<<n)]

# split exp
texps = ('%s' % exp.exp1.expand()).strip().split(' - ')
ops = []
exps = []
for i in range(len(texps)):
if texps[i].find(' + ') != -1:
tmp = texps[i].split(' + ')
ops.append(0)
exps.append(tmp[0])
for i in range(1, len(tmp)):
ops.append(1)
exps.append(tmp[i])
else:
ops.append(0)
exps.append(texps[i])
if exps[0][0] == '-':
for i in range(len(exps)):
ops[i] = 1-ops[i]
exps[0] = exps[0][1:]
else:
ops[0] = 1
# find e and N
l = []
for i in range(len(exps)):
tmp = 1 if ops[i] else -1
en = []
j = 0
while j < len(exps[i]):
if exps[i][j] == 'e':
num = 0
j += 1
while isdigit(exps[i][j]):
num = num*10 + int(exps[i][j])
j += 1
tmp *= eval('e%d' % num)
en.append(num)
elif exps[i][j] == 'N':
j += 1
num = 0
if exps[i][j] == '^':
j += 1
while isdigit(exps[i][j]):
num = num*10 + int(exps[i][j])
j += 1
if num == 0:
num = 1
tmp *= eval('N**%d' % num)
else:
j += 1
if tmp == 1 or tmp == -1:
l.append((0, ()))
else:
l.append((tmp, tuple(sorted(en))))

# construct h
mp = H(n)
for val, en in l:
cols[mp.index(en)] = val
# print(cols)
return cols

def EWA(n, elist, NN, alpha):
mp = H(n)
var('a')
S = [X_Complex(n*a)]
cols = [[1 if i == 0 else 0 for i in range(2^n)]]
for i in mp[1:]:
eL, s = R(i, n)
cols.append(CC(eL, n))
S.append(X_Complex(s))

alphaA,alphaB = 0, 0
for i in S:
alphaA = max(i.a, alphaA)
alphaB = max(i.b, alphaB)
# print(alphaA, alphaB)
D = []
for i in range(len(S)):
# print((alphaA-S[i].a), (alphaB - S[i].b))
D.append(
int(NN^((alphaA-S[i].a)*alpha + (alphaB - S[i].b)))
)
kw = {'N': NN}
for i in range(len(elist)):
kw['e%d' % (i+1)] = elist[i]

B = Matrix(ZZ, Matrix(cols).T(**kw)) * diagonal_matrix(ZZ, D)
L = B.LLL(0.5)
v = Matrix(ZZ, L[0])
x = v * B**(-1)
phi = int(x[0,1]/x[0,0]*elist[0])
return phi

def attack(NN, elist, alpha):
phi = EWA(len(elist), elist, NN, alpha)
print(phi)
return phi

n = ? # n是d的比特位数
NN = ?
elist = [e1,e2,e3, ...,en]

alpha = n / int(NN).bit_length()
for i in range(1, len(elist)+1):
var("e%d" % i)
var("d%d" % i)
var("k%d" % i)
g, N, s = var('g'), var('N'), var('s')

for i in range(len(elist)):
elist[i] = Integer(elist[i])
phi = attack(NN, elist, alpha)
d = inverse(e, phi)

参考

https://ctf-wiki.org/crypto/asymmetric/rsa/d_attacks/rsa_extending_wiener/#_3

https://seandictionary.top/rsa/

感谢