2023香山杯-密码部分WP

文章描述了解决一个基于RSA的加密问题,涉及生成大素数、计算公钥和私钥,以及在有限域中寻找高次方根以恢复原始消息。解题关键在于利用欧拉函数和中国剩余定理来找到私钥d。

list

题目附件

import os
import gmpy2
from Crypto.Util.number import *
import random
from secrets import flag
def pad(s,l):
    return s + os.urandom(l - len(s))
def gen():
    g = getPrime(8)
    while True:
        p = g * random.getrandbits(138) + 1
        if isPrime(p):
            break
    while True:
        q = g * random.getrandbits(138) + 1
        if isPrime(q):
            break
    N = p ** 5 * q
    phi = p ** 4 * (p - 1) * (q - 1)
    d = random.getrandbits(256)
    e = inverse(d, phi)
    E = e * g
    hint = gmpy2.gcd(E, phi)
    return N, E, hint

flag = pad(flag,64)
m = bytes_to_long(flag)
n,e,hint = gen()
c = pow(m,e,n)
print(f'hint = {hint}')
print(f'n = {n}')
print(f'e = {e}')
print(f'c = {c}')
# hint = 251
# n = 108960799213330048807537253155955524262938083957673388027650083719597357215238547761557943499634403020900601643719960988288543702833581456488410418793239589934165142850195998163833962875355916819854378922306890883033496525502067124670576471251882548376530637034077
# e = 3359917755894163258174451768521610910491402727660720673898848239095553816126131162471035843306464197912997253011899806560624938869918893182751614520610693643690087988363775343761651198776860913310798127832036941524620284804884136983215497742441302140070096928109039
# c = 72201537621260682675988549650349973570539366370497258107694937619698999052787116039080427209958662949131892284799148484018421298241124372816425123784602508705232247879799611203283114123802597553853842227351228626180079209388772101105198454904371772564490263034162

解题思路

首先要恢复 p,q.

恢复p、q后就是在有限域内开高次方的问题。

恢复p、q参考论文:https://eprint.iacr.org/2015/399.pdf

e∗d=1 mod phi,gcd(e,phi)=1⇒e*d=1\textbf{ }mod\textbf{ }phi,gcd(e,phi)=1\Rightarrowed=1 mod phi,gcd(e,phi)=1

e∗x−1=k∗phie*x-1=k*phiex1=kphi

因为n=pr∗q,所以gcd(e∗x−1,n)=gcd(pr−1∗(p−1)∗(q−1),pr∗q)=pr−1因为n=p^{r}*q,所以gcd(e*x-1,n)=gcd(p^{r-1}*(p-1)*(q-1),p^{r}*q)=p^{r-1}n=prq,gcd(ex1,n)=gcd(pr1(p1)(q1),prq)=pr1

在多项式时间内求解上面方程(copper)求出d.

small_roots()r−1r-1r1次方就能恢复 p

exp

import gmpy2
n = 108960799213330048807537253155955524262938083957673388027650083719597357215238547761557943499634403020900601643719960988288543702833581456488410418793239589934165142850195998163833962875355916819854378922306890883033496525502067124670576471251882548376530637034077
e = 3359917755894163258174451768521610910491402727660720673898848239095553816126131162471035843306464197912997253011899806560624938869918893182751614520610693643690087988363775343761651198776860913310798127832036941524620284804884136983215497742441302140070096928109039
R.<d> = PolynomialRing(Zmod(n))
f = e*d - 251
res = f.monic().small_roots(X = 2^256,beta = 0.44)  # d = random.getrandbits(256)
# print(res)
p_4 = gmpy2.gcd(int(f(res[0])),n)
p = gmpy2.iroot(p_4,4)[0] # True
q = n//p**5

print(f"p,q = {p},{q}")

# sage
# 开251次方
from Crypto.Util.number import *
import itertools

hint = 251
n = 108960799213330048807537253155955524262938083957673388027650083719597357215238547761557943499634403020900601643719960988288543702833581456488410418793239589934165142850195998163833962875355916819854378922306890883033496525502067124670576471251882548376530637034077
e = 3359917755894163258174451768521610910491402727660720673898848239095553816126131162471035843306464197912997253011899806560624938869918893182751614520610693643690087988363775343761651198776860913310798127832036941524620284804884136983215497742441302140070096928109039
c = 72201537621260682675988549650349973570539366370497258107694937619698999052787116039080427209958662949131892284799148484018421298241124372816425123784602508705232247879799611203283114123802597553853842227351228626180079209388772101105198454904371772564490263034162

p,q=69367143733862710652791985332025152581988181 ,67842402383801764742069883032864699996366777

p_list = [p,q]
n_list = [p**5,q]
print(n_list)
# print(reduce((lambda x, y: x * y), n_list) - n)   # 0
# print(euler_phi(p_list[0])) # 直接求欧拉函数

res=[]

for pi in n_list:
    d = inverse(int(e//251),euler_phi(pi))     # 对n_listt 每一个 pi 求欧拉函数
    m = pow(c,d,pi)
    temp = (Zmod(pi)(m).nth_root(251, all=True))
    #print('temp =',temp) # 列表 251
    if temp is not None:
            res.append(temp)
    else:
            print("None")
   
for vc in itertools.product(*res):
    _c = [int(x) for x in vc]
    m = long_to_bytes(int(crt(_c, n_list)))
    if b"flag" in m:
        print(m)
# b'flag{4b68c7eece6be865f6da2a4323edd491}\x9d\xcf\xdc\xcb\xb8\xbdd\xec\xadh\xa6C\x99\xa0)7\xfb\x02\xba\x90q8\x10+\x7f}'

补充

这个有限域开高次方的脚本只适用于n=pr∗qn=p^{r}*qn=prq的题型

nth_root()通常用于计算数字的n次方根

Zmod(pi)(m).nth_root(251, all=True)相当于求解元素模pi且余数是m的251次方根,即r251=m mod pir^{251}=m \textbf{ }mod\textbf{ }pir251=m mod pi

all=True表明会返回所有的251次方根(可能是一个列表)

for vc in itertools.product(*res):
    _c = [int(x) for x in vc]

可以知道res的元素是列表,*res是将res

中的每一个列表作为单独的参数传给product,然后计算这些列表的笛卡尔积

看个例子

res=[[6,2,7,8],[0,4]]
for vc in itertools.product(*res):
    _c = [int(x) for x in vc]
    print(_c)

打印出[6,2,7,8]和[0,4]这两个列表的笛卡尔积

[6, 0]
[6, 4]
[2, 0]
[2, 4]
[7, 0]
[7, 4]
[8, 0]
[8, 4]

crt(_c, n_list)将会返回一个整数,这个整数模每一个n_list中的模数会得到_c对应的数

例如

a = [2,3,1]
n = [3,4,5]
x = crt(a,n)
相当于x要满足x = 2 mod 3
           x = 3 mod 4
           x = 1 mod 5
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值