DGZ's Blog.

hgame2020 RSA?

Word count: 1.5kReading time: 6 min
2020/03/06 18 Share

task.py

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
from Crypto.Util.number import *
import gmpy2
from flag import flag

p = getPrime(256)
q = getPrime(256)
r = getPrime(512)
e = 2
m = bytes_to_long(flag)

n = p * q + r
c = pow(m, e, n)

with open('task', 'w') as f:
f.write("c = {}\n".format(str(c)))
f.write("e = {}\n".format(str(e)))
f.write("q = {}\n".format(str(q)))
f.write("n = {}\n".format(str(n)))

虽然有pow但不是RSA,因为n是素数
我觉得判断的原因就是p * q为非素数,再加上一个素数,就得到素数
后面知道是利用Tonelli–Shanks算法求解二次剩余问题。

c=m2mod n m2c(mod n)

已知n、c求m
参考:https://en.wikipedia.org/wiki/Tonelli%E2%80%93Shanks_algorithm
https://blog.csdn.net/wmdcstdio/article/details/49862189?locationNum=15

Tonelli–Shanks算法

Tonellii - Shanks算法(Shanks称为RESSOL算法)在模运算中用于求解r2n(mod p)的一个全等,其中p是质数:即求n模p的平方根。
Tonelli–Shanks不可用于复合模:求平方根模的复合数是一个与大整数分解等价的计算问题。

核心

给予一个非零n和一个奇素数p,欧拉判别法告诉我们n有一个平方根(例如,n是一个二次剩余)那么有:

np121(mod p)

相反,如果一个数z没有平方根(不满足二次剩余),则有:

zp121(mod p)

我们不难发现z,因为在1和p-1之间的接近一半的的整数有这种性质,所以我们假定我们有这样的非二次剩余。
通过(通常情况下)重复除以2,我们可以把p-1写作Q2S,而且此处Q为奇数,注意到如果我们尝试

RnQ+12(mod p)

接着R2nQ+1(mod p)=(n)(nQ)(mod p)
如果tnQ1(mod p)
那么R是n的一个平方根。否则对于M=S,我们有R和t满足

R2nt(mod p);和
t是一个1的2M1根(因为)
关键就是这里,如果满足此时t=1,那么R就是需要求解的r

如果我给一个R和t对于特定的M满足先前提到的(R不是n的平方根),我们能简单地计算出另外的R和t,且对于M-1满足先前提到的关系,这样我们能重复这一过程知道t变成1的2的20次方根,例如,t=1,在这时R是一个n的一个平方根。

我们可以检查t是否是1的一个2M2根通过开{M-1}次方,和检查是否等于1。如果是,那我们不需要做任何事,相同的R和t就可以了。但如果不是,t2M2一定要是-1(因为将它开方得到1,1模p只能有两个平方根1和-1)

为了找到一对新的R和t,我们能把R乘一个待定的因数b。接下来t一定要被因数b2为了保证R2nt(mod p), 所以我们需要去找到一个因数b2,以达成tb2是一个1的2M2根,或者相同地,b2是一个-1的2M2根。

关键的就是利用z,这个已知的非二次剩余数,欧拉判别法应用如上所示,zQ是-1的2S1根,通过重复对zQ开方,我们可以得到一个12i次方根的序列。我们可以选择一个合适的去作为b,通过一点点变量维护和琐碎的情况压缩(翻不动),算法就有了。

例子

求解这样的全等式:r25(mod 41)

n=5 p=41

41是一个所需要的素数,并且411(mod 4) 5是一个通过欧拉判别法得知的二次剩余数:54112=520=1(正如之前,在(Z/41Z)× (41模整数的乘法群)被暗中mod 41)

类似的等于关系下面会多次出现,因为其实还隐藏了mod41

1.p1=40=523,所以Q 5 , S 3
2.找到z

  • 24112=1,所以2是一个二次剩余数
  • 34112=40=1,所以3是一个二次非剩余数,令z 3

3.令

  • M S=3
  • c zQ=35=38
  • t nQ=55=9
  • R nQ+12=55+12=2

4.循环

  • 第一次迭代:
  • t 1,所以我们没完成
  • t21=40,t22=1i2
  • bc2Mi1=382321=38
  • Mi=2
  • cb2=382=9
  • ttb2=99=40
  • RRb=238=35

/n

  • 二次迭代:
  • t1, 所以我们还未完成
  • t21=1所以 i1
  • bc2Mi1=92211=9
  • Mi=1
  • cb2=92=40
  • ttb2=4040=1
  • RRb=359=28

/n

三次迭代

  • t=1,我们完成了,返回值r=R=28

的确,2825(mod 41)(28)21325(mod 41),所以这个算法最终产生了两个同余解

脚本

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
from Crypto.Util.number import *

def legendre(a, p):
return pow(a, (p - 1) // 2, p)

def tonelli(n, p):
assert legendre(n, p) == 1, "not a square (mod p)"
q = p - 1
s = 0
while q % 2 == 0:
q //= 2
s += 1
if s == 1:
return pow(n, (p + 1) // 4, p)
for z in range(2, p):
if p - 1 == legendre(z, p):
break
c = pow(z, q, p)
r = pow(n, (q + 1) // 2, p)
t = pow(n, q, p)
m = s
t2 = 0
while (t - 1) % p != 0:
t2 = (t * t) % p
for i in range(1, m):
if (t2 - 1) % p == 0:
break
t2 = (t2 * t2) % p
b = pow(c, 1 << (m - i - 1), p)
r = (r * b) % p
c = (b * b) % p
t = (t * c) % p
m = i
return r

n = 2166906408965390116437761185603075699086315246646982132520792868301615462732707695943058783211233589602162482847874299962577512765886861573898075548033678
p = 7236834786093916009325417235344284284391050287273422058348241360770131423240807259717864686885012301293921328397391157761688776563780348734483657156421779

res = tonelli(n, p)
print(long_to_bytes(res))
#b'hgame{eaa5262c-4631-46ef-a97b-53277ab7e1d8}'
CATALOG
  1. 1. Tonelli–Shanks算法
  2. 2. 脚本