Day_and_Night
题目源码
from Crypto.Util.number import *
def pad(msg): # PKCS#1 v1.5
print(f'{2048 // 8 - len(msg) = }') # 189个字节附近, 也就是约等于 189 * 8 = 1512 bit 附近
return msg + b'\x00' * (2048 // 8 - len(msg)) # 2048 = 256 * 8
def day():
print("# == Day Time == #")
A = [getRandomRange(1 << 30, 1 << 40) for _ in '01234'] # A = [1, 2, 3, 4, 5]
# from notebook import pubkeys # pubkeys = [(e, n), ...]
pubkeys = [
[13, 16966923273393034574368601955241184391111444785998466710722972275633988040568029224272430397635357872449163107278928804572765398970752644900568552514020294813546024733050135082024292481598322682990045855877556853392263676715734911232910368712575738786450510156105046776600566496799942574786503183241332740784997363514285857446938222302824964396227964711868412832852194845619628366554904150230665043909281603861298992421759553638135733037244888585600075655017766713763334284974682754159767494437867386464788270392548503256135412609157993954349845981674000072937143857040970855382655304911806719316346552101453584266121],
[13, 13636547774903694433910615288816366243965221147245493790443257513469643051508482519945160993497983268746607032666514024577996510214003878851754244812785830861056511467944880656887067183093022088006603023892079757597903932697728395729605528467171403935076011715369931494813762896302405865063013876978950952553183445737415893862580946529557997177862386840671940665891984463553509323759394863514802662045074793125218445076375294720405008157895103559623204523371691063630903658360461001806917018298844432738514873530829540084732665560723768831816785580126848217585494349751859982707295270194451027637908966488433591336079],
[13, 13422209706853784863425138450444393536114566510977689013118842078064352816867606066548879016746689784867031499069217146653949323203507755857385123653072875573743110808787259755688613540214185294636104765964080842615717180413731820679997453377899736419007193552970900617704671115646941207925636014254352661937769415565529244046272257822921740649781322893596978112103176487718960676212380426151079825140922318909470514158027009961239217428522970405297292867815726509360369236095377704967444339421311749405316999191339677904499413281713365474466679134722393111495793887387332928227905039036942450742103526490509249576159],
[13, 21746430662648628292439055708644436184530740821098010749514133474681581832957631729292681550968108122182841907644832479495695011213810971334153360397292385819609198743433956578966857749821825621262178208409227379634952172419748956184624505995106932133301461499473823229623656490844764958301835433668618062743327682117611417235819168865744757242990985069648255779083246816509593018812991050741917153961245784296647661659339041115118272068392627299499703962645966495325055378652176922700072734325787550811454984878236039742147547963385471101805136506286953288566035101103034341742923072819774753918471775281464739904101],
[13, 16012920802348742413202394765537959589119609441428117329692661212660781149297315973073063954731746455220386673231725950116051390914388279415796378305381377822621647264088603927560119754278978906758212463937071494263575443156089509654950881813861479449401068535285885349738367249035960528895049222211387021733152221289202030407416118814341241382479971018611239102343929419190892133819275458474489856666791981711793637127167775551296403296397810821548297674986394286293380472089135564385129140069501205054853729781710604489652713172579677128036322403868618295553335257593973027194532512596143733434234439677513237984107]
]
# pubkeys = [(e, n), (e, n), (e, n), (e, n), (e, n)] # pubkeys = [(e, n), ...]
hint = str(A)[1:-1].replace(', ', '||').encode() # 1, 2, 3, 4, 5 -> 1||2||3||4||5
print(f"hint_bytes = {hint}")
print(f'{pad(hint) = }')
hint = bytes_to_long(pad(hint))
# DEBUG
def unpad(msg):
while msg[-1] == 0: # 如果最后一位是0
msg = msg[:-1] # 去掉最后一位
return msg # 返回明文
print(f'{unpad(long_to_bytes(hint)) = }') # b'1||2||3||4||5'
print(f"hint_long = {hint}")
package = []
for pubkey in pubkeys:
e, n = pubkey
# print(f"{e = }")
# print(f"{n = }")
print(f'{n.bit_length() = }')
package.append(pow(hint, e, n))
print(f"{package = }")
return A
def night(A):
print("# == Night Time == #")
# from secrets import secret
secret = b'123'
p, q = [getPrime(1024) for _ in '01']
print(f"{p = }")
print(f"{q = }")
e, n = 0x10001, p * q
message = bytes_to_long(pad(secret))
ciphertext = pow(message, e, n)
print(f"{e = }")
print(f"{n = }")
print(f"{ciphertext = }")
core = getRandomRange(1 << 70, 1 << 80)
print(f"{core = }")
print(f"{core.bit_length() = }")
box = (A[4] * p ** 4 + A[3] * p ** 3 + A[2] * p ** 2 + A[1] * p ** 1 + core) % n
shell = (A[4] * core ** 4 + A[3] * core ** 3 + A[2] * core ** 2 + A[1] * core ** 1 + A[0]) % n
print(f"{shell = }")
A = day()
night(A)
思路
求 day()
的返回值 A[]
, 需要注意的是加密代码中的 pad()
, 这里使用单纯的 \00
字节填充, 所以可以看成是 1byte 也就是 8bit 的 0
, 尝试后发现 padding
的字节长度在 189位 (调试方式: 可以写完解密 A[]
代码的雏形后尝试在189(数据来源:在加密代码的 pad()
内返回前加入 print(f'{2048 // 8 - len(msg) = }')
后运行几次加密代码, 观察输出来测试附近的数值然后微调(+- 10), 在无自构造的 unpad()
介入下无 \00
数据即代表调试的数据大概率没问题)
因为 pad()
的存在, m 将远大于 n, 这时利用CRT后爆破 k 显得太过无力(时间复杂度太坏), 于是我们可以考虑对带有规律 padding
的加密前的信息进行优化
同时 pad()
假设填充了 189bytes 的 \00
, 也就是 8*189
bit的 0, 等同如下移位运算:
如果觉得转换有点迷糊可能是没有学过位运算, 可以看如下这个例子
sage: m = 3
sage: m * (2^10)
3072
sage: m << 10
3072
于是我们可以对CRT进行改造,但毕竟已经调用函数了,改造CRT就显得太麻烦了,我们可以对 c[]
预处理来达到同样的效果, 预处理代码如下:
# 对 c 调整
print("# 对 c 调整 #")
# 因为 m 进行了 padding, 而且 padding 很有规律, 只是往后补 0, 且估算出补 '0' 的长度大概在 189 字节左右
# m << 189 * 8, 也就是 m * (2 ** (189 * 8)) 后面这部分记作"填充"
填充 = pow(pow(2, 189 * 8), e1)
for i in range(1, len(c_values) + 1):
temp = f'c{i} = c{i} * inverse_mod(填充, n{i})'
exec(temp)
# 更新 c_values
c_values = [c1, c2, c3, c4, c5]
print(f"{c_values = }")
其代码的数学表达摘要为:
\[\begin{aligned} m << (189 * 8) \equiv m * (2 ^ {189 * 8}) \\ 把这里的 {2^{189 * 8}} 看成一个整体 a \\ (a * m) ^ e \equiv c_1\pmod{n_1} \\ a ^ e * m ^ e \equiv c_1\pmod{n_1} \\ m ^ e \equiv c_1 * a ^ {-e} \pmod{n_1} \end{aligned} \]# == Day Time == #
pubkeys = [
[13, 16966923273393034574368601955241184391111444785998466710722972275633988040568029224272430397635357872449163107278928804572765398970752644900568552514020294813546024733050135082024292481598322682990045855877556853392263676715734911232910368712575738786450510156105046776600566496799942574786503183241332740784997363514285857446938222302824964396227964711868412832852194845619628366554904150230665043909281603861298992421759553638135733037244888585600075655017766713763334284974682754159767494437867386464788270392548503256135412609157993954349845981674000072937143857040970855382655304911806719316346552101453584266121],
[13, 13636547774903694433910615288816366243965221147245493790443257513469643051508482519945160993497983268746607032666514024577996510214003878851754244812785830861056511467944880656887067183093022088006603023892079757597903932697728395729605528467171403935076011715369931494813762896302405865063013876978950952553183445737415893862580946529557997177862386840671940665891984463553509323759394863514802662045074793125218445076375294720405008157895103559623204523371691063630903658360461001806917018298844432738514873530829540084732665560723768831816785580126848217585494349751859982707295270194451027637908966488433591336079],
[13, 13422209706853784863425138450444393536114566510977689013118842078064352816867606066548879016746689784867031499069217146653949323203507755857385123653072875573743110808787259755688613540214185294636104765964080842615717180413731820679997453377899736419007193552970900617704671115646941207925636014254352661937769415565529244046272257822921740649781322893596978112103176487718960676212380426151079825140922318909470514158027009961239217428522970405297292867815726509360369236095377704967444339421311749405316999191339677904499413281713365474466679134722393111495793887387332928227905039036942450742103526490509249576159],
[13, 21746430662648628292439055708644436184530740821098010749514133474681581832957631729292681550968108122182841907644832479495695011213810971334153360397292385819609198743433956578966857749821825621262178208409227379634952172419748956184624505995106932133301461499473823229623656490844764958301835433668618062743327682117611417235819168865744757242990985069648255779083246816509593018812991050741917153961245784296647661659339041115118272068392627299499703962645966495325055378652176922700072734325787550811454984878236039742147547963385471101805136506286953288566035101103034341742923072819774753918471775281464739904101],
[13, 16012920802348742413202394765537959589119609441428117329692661212660781149297315973073063954731746455220386673231725950116051390914388279415796378305381377822621647264088603927560119754278978906758212463937071494263575443156089509654950881813861479449401068535285885349738367249035960528895049222211387021733152221289202030407416118814341241382479971018611239102343929419190892133819275458474489856666791981711793637127167775551296403296397810821548297674986394286293380472089135564385129140069501205054853729781710604489652713172579677128036322403868618295553335257593973027194532512596143733434234439677513237984107]
]
package = [11348275592172789430802632398715397831929456592129727148350341695822097922745546634103895397114240780119492097879709874341733998189314701220819602086989170395665694216144611451695210776788208725517321710496401109614989729783280209806513298851774400705822274570042128297336679615535569295725037051009221966882907606741910671983811835353310553679554656322510247012465612842055583530354825164440556725066351429241835394579392892121937744444324723509039593320520073762479405487678614235658010109343868772211070874500834820005557924944766175445227592086997786601600141172729543326022039897279215360210475222205314199752358, 11817047514083750028443306573161291960303002458276716688201587284992675854178250185695613342603396451495053496021018943618684096193774851513377343785523373066637422156529843356640438514917147007662989366432951340073247573347091422478343675687872524102452812391963856179412214914106817415620613886827405526709334122157218264253412138947978859771457788135381194396144386611627935519990330309154612803264373276472523863776757124489507206744895878709136339189551422814752920383697719212261820943113370067423118794871570596583232694067119303936516491830190746798281923693136901268220982292626728815051587839682733501582997, 3131427695639074007646625687827523632044624200167725001731246731463369200890045250738898155425873822581405723445926216099453276470655307513244478613248253397941362848890729091875615893643288181126353294271063912991208029628896380928878098320509891044728414542705582996774991148116051162157848421643082210867849206627757941596203046799655123006406284498009424107637970927771132706399889289085811447333980744767367889464364519571965014435281718495338752896521777070285880327861778156398424643033288480056322928449210425585380975486759287881877636187364299666207762828705236236071045450767253946524067440138527952996304, 3345060634868343217881273515503289974894457362066685037297545772310131198850220025672843957153107490874807599311288876818421468275658855183857485067696603798636556327070411528170381402923911922437076616994460720931230980991440337397730061039967493443152767007916713160518618697678791358770766857737217910848181657393760417031865203943283074078341988117567851667858026108335625589177179258992930648353570238327435125409871028669479754623782967450462280802423233657422873275069133939005033872391235288329468886849808534906305377008408076775145268714146937205940659708210819711903273898505450411746796296074348301840443, 689229715602713064347690095007369004049337459458047359544160409565437783924368962797071453687950582019132146435134269723546824657132278724115055713024437048832952574546581164678912794341156478859615959289720931333010836265396177238403118182219901102453660596556053537987705012972371809213618745302576874122654094213771963420178465724399112964705426548207838941065298709968044913923423922781681311806180694571535910059881703228238538782302346351472443730399075812346657046097375387076507982673848430573754749554553794234945423901111974280859817600907619255722739374513108613738135872066592727564964404002462119084702]
# == Night Time == #
# e = 65537
# n = 19628814650078845889624476490795800811418703155298421183649751922179289373872110164946485468758965499898659860381452920316689731813466611171330082419646032975535828938381971760997472673267667258345915647828047467420412766203712358122436358120743184983881044132767579608354023899206025747277296489890315455139161247982158229333619416375842911027431739437723838483131248360727904613423834627213036877579164949084954352768932164210879329537266501942223360207803513487189832308290555832923018589686990981697562750385892888271329399531648035355662276186237232094775404084845357101354614900716614704579902047366473325519851
# ciphertext = 18231770979476945075115454933498285687471248736331968136534439886998853983159878091479109027681230666670001308495054141682281492473521819495259722942633525843224223138121210743435008344491182811096997837681349401919300801071760448609909297020846949507707329708251861866061443743942558286917774317607571379516986051564317860557772153943821431899226380831989795907857566336085097807330399388077758869632230636727348278392968946952484442630549599469645903047917899063746000458656292320802815605327181346234519795519772619774175495201258591571744214020509439053957080210165266709953654561600989486386199867341092142358249
# shell = 7450311459083436799013470526820816670296314417175130490433500468646960366746415074085735399822973890608046359739147553438015771999419943640758794686461655503899174274852006852675273759918109697488252918038788275865249305840107266839320745067341253227218580768443831238305064450748932813304412926315551102620477056031794884272260575749720364002093756539399717557808977458033683588388899534871439955606629530406662400672087729944642496842353084711242911328651341343429685261174338868058208644730980459845089948243884209259777439653298562832822410224626781415627048867272937969823329086437858777995321438386601575065665
# day time: 多组同 e, m 不同 n , CRT
# 提取 e, n
e_variables = [] # 装变量名
n_variables = []
# variables = ['e1', 'e2', 'e3', 'e4', 'e5', 'n1', 'n2', 'n3', 'n4', 'n5']
for i, sublist in enumerate(pubkeys, start=1):
e, n = sublist # e, n = (e, n)
e_variables.append(f"e{i} = {e}") # 压入变量名
n_variables.append(f"n{i} = {n}")
for e_var, n_var in zip(e_variables, n_variables):
exec(e_var)
exec(n_var)
print(e_var)
print(n_var)
# 对每个 n 进行 GCD, 没问题, 直接使用相同e(一般较小),e组不同模数N加密相同明文m(m小于所有N),低加密指数广播攻击(中国剩余定理CRT)
# 两两 GCD
# for i in range(1, 6):
# for j in range(i+1, 6):
# print(f"i = {i}, j = {j}")
# print(gcd(eval(f"n{i}"), eval(f"n{j}")))
# 将 m**e 视作物数, N_i 数之余c_i, 根据中国剩余定理,求得 m ** e, 然后开 e 次方即可
import sympy
N_values = [n1, n2, n3, n4, n5]
e_values = [e1, e2, e3, e4, e5]
# 从package中提取 c
c_values = []
for i, c in enumerate(package, start=1): # enumerate 枚举
c_values.append(f"c{i} = {c}")
exec(c_values[-1])
for c in c_values:
print(c)
c_values = [c1, c2, c3, c4, c5]
# c_values = [int(c) for c in c_values]
# 对 c 调整
print("# 对 c 调整 #")
# 因为 m 进行了 padding, 而且 padding 很有规律, 只是往后补 0, 且估算出补 '0' 的长度大概在 189 字节左右
# m << 189 * 8, 也就是 m * (2 ** (189 * 8)) 后面这部分记作"填充"
填充 = pow(pow(2, 189 * 8), e1)
for i in range(1, len(c_values) + 1):
temp = f'c{i} = c{i} * inverse_mod(填充, n{i})'
exec(temp)
# 更新 c_values
c_values = [c1, c2, c3, c4, c5]
print(f"{c_values = }")
# CRT
print("# CRT #")
#sage | Lazzaro's code
def chinese_remainder(modulus, remainders):
Sum = 0
prod = reduce(lambda a, b: a*b, modulus)
for m_i, r_i in zip(modulus, remainders):
p = prod // m_i
Sum += r_i * (inverse_mod(p,m_i)*p)
return Sum % prod
# plaintext = chinese_remainder([3,5,7],[2,3,2]) #23
# plaintext = chinese_remainder(N_values, c_values)
from sympy.ntheory.modular import crt
plaintext = chinese_remainder(N_values, c_values) # return (x, prod(N_values))
print(f"{plaintext = }")
def unpad(msg):
while msg[-1] == 0: # 如果最后一位是0
msg = msg[:-1] # 去掉最后一位
return msg # 返回明文
N_product = sympy.prod(N_values)
print(f"{N_product = }")
print(f'{N_product.bit_length() = }') # 2048 * 5
import gmpy2
# m > N_product 时,m = m % N_product = m - k * N_product (k 为正整数) 于是枚举 k
k = 0
while True:
# print(f"k = {k}")
plaintext2 = plaintext + k * N_product # 这里的 N_product 是所有 N 的乘积
# print(f"plaintext = {plaintext}")
if k % 100000 == 0 and k != 0:
print(f"k = {k}")
if gmpy2.iroot(plaintext2, e1)[1] == True:
print(f"final_k = {k}")
print(f"m = {gmpy2.iroot(plaintext2, e1)[0]}")
print(f"明文 m 也就是 A[] = {long_to_bytes(gmpy2.iroot(plaintext2, e1)[0])}")
break
k += 1
求出 A[]
后来到Night(Part2), z3解有限域下高次幂不容易,利用copper来求 box
缩减下未知元个数, 利用整除关系将
由 \(\pmod{n}\) 转换到 \(\pmod{p}\)
\[\begin{aligned} a & \equiv b \pmod{n} \\ a &= b + k \times n \\ a &= b + k \times p \times q \\ a &= b + k_1 \times p \\ a & \equiv b \pmod{p} \end{aligned} \]由
box = (A[4] * p ** 4 + A[3] * p ** 3 + A[2] * p ** 2 + A[1] * p ** 1 + core) % n
shell = (A[4] * core ** 4 + A[3] * core ** 3 + A[2] * core ** 2 + A[1] * core ** 1 + A[0]) % n
中的第一行(box 函数) 由 \(\pmod{n}\) 转换到 \(\pmod{p}\) 有限域下, 目的是消去 box 函数中的 p
box = core % p
之后 copper 出 core (core 小于 \(n^{(beta)^2 /4}\) (此多项式为四次幂))
再将 core 带入下式
shell = (A[4] * core ** 4 + A[3] * core ** 3 + A[2] * core ** 2 + A[1] * core ** 1 + A[0]) % p
对其与 n 进行 GCD 求出 p (shell 函数 < n 且与 n 有公因子 p)
之后再求出 q, 算私钥 d 后算 m 即可
求 Night 的代码如下
from Crypto.Util.number import *
def unpad(msg):
while msg[-1] == 0: # 如果最后一位是0
msg = msg[:-1] # 去掉最后一位
return msg # 返回明文
e = 65537
n = 19628814650078845889624476490795800811418703155298421183649751922179289373872110164946485468758965499898659860381452920316689731813466611171330082419646032975535828938381971760997472673267667258345915647828047467420412766203712358122436358120743184983881044132767579608354023899206025747277296489890315455139161247982158229333619416375842911027431739437723838483131248360727904613423834627213036877579164949084954352768932164210879329537266501942223360207803513487189832308290555832923018589686990981697562750385892888271329399531648035355662276186237232094775404084845357101354614900716614704579902047366473325519851
c = 18231770979476945075115454933498285687471248736331968136534439886998853983159878091479109027681230666670001308495054141682281492473521819495259722942633525843224223138121210743435008344491182811096997837681349401919300801071760448609909297020846949507707329708251861866061443743942558286917774317607571379516986051564317860557772153943821431899226380831989795907857566336085097807330399388077758869632230636727348278392968946952484442630549599469645903047917899063746000458656292320802815605327181346234519795519772619774175495201258591571744214020509439053957080210165266709953654561600989486386199867341092142358249
shell = 7450311459083436799013470526820816670296314417175130490433500468646960366746415074085735399822973890608046359739147553438015771999419943640758794686461655503899174274852006852675273759918109697488252918038788275865249305840107266839320745067341253227218580768443831238305064450748932813304412926315551102620477056031794884272260575749720364002093756539399717557808977458033683588388899534871439955606629530406662400672087729944642496842353084711242911328651341343429685261174338868058208644730980459845089948243884209259777439653298562832822410224626781415627048867272937969823329086437858777995321438386601575065665
A = [646919678902, 598613162045, 90472013741, 917100488664, 838250498519] # 已知的A数组
PR.<core> = PolynomialRing(Zmod(n))
f = (A[4] * core ** 4 + A[3] * core ** 3 + A[2] * core ** 2 + A[1] * core ** 1 + A[0]) - shell
f = f.monic() # 使首项系数为1
roots = f.small_roots(X = 2^80,beta=0.4,epsilon = 0.03) # 求解f的根,限制根的大小在2^80以内, beta和epsilon是调整参数, epsilon越小, beta越大, 越能找到更小的根
print(f"{roots = }")
core = roots[0]
print(f"{core = }")
# core = 72398025613557274548283
p = GCD(shell - (A[4] * core ** 4 + A[3] * core ** 3 + A[2] * core ** 2 + A[1] * core ** 1 + A[0]), n)
q = n//p
phi = (p-1)*(q-1)
d = inverse(e,phi)
m = int(pow(c,d,n))
print(unpad(long_to_bytes(m)))