Crypto
1 | from Crypto.Util.number import * |
输出output
1 | 55584485421406970301254549526604327461056610434113867867423369210521481715933 |
代码比较简单易懂,这里有篇介绍LCG线性同余生成器的博文,LCG属于流密码的范畴。
https://zeroyu.xyz/2018/11/02/Cracking-LCG/
不同的是,题目中给到的输出状态不是连续的输出,无法直接套用。
假设初始state,也就是flag记为S0,于是有:
1 | S1 ≡ (S0*a + b) % m |
其中,S1,S3和S5是已知的,于是有:
1 | S5-S3 ≡ (S3-S1)*a^2 % m |
使用模逆运算计算a^2的值,注意模逆运算实际上使用算法是扩展欧几里得算法,关于欧几里得算法和扩展欧几里得算法可以参考下面的链接加深理解:
https://www.bilibili.com/video/av24248451?from=search&seid=8600306360895299893
1 | #encoding=utf-8 |
求得:
1 | a^2 ≡ |
直接开方求a的值是不好使的。搜索模p平方根找到了计算模平方根的算法:Tonelli–Shanks算法,从这里找到了这个算法的python实现:https://rosettacode.org/wiki/Tonelli-Shanks_algorithm#Python
但是直接带入
29895665113034070831270870121097218673769521820755738885706707818988524697486和55584485421406970301254549526604327461056610434113867867423369210521481715933
求解是报错的
看一下算法约束条件可以发现要求模数p是奇质数
调用sympy库查看,发现这里的p不是质数
使用yafu分解p得到质因子P和Q:
1 | P = 225198214939471987016766287890315801997 |
既然p不是质数,那么上面的算法就不适用,根据Rabin加密系统(维基百科搜Rabin cryptosystem)的加解密算法,可以进行求解。该算法要求质因子P和Q满足模4余3的条件。
但是这里的P和Q并不是模4余3的,尽管如此同样可以解,参考:https://xz.aliyun.com/t/5113
a^2 ≡
29895665113034070831270870121097218673769521820755738885706707818988524697486 % m
分解为两个同余式:
a^2 ≡
29895665113034070831270870121097218673769521820755738885706707818988524697486 % 225198214939471987016766287890315801997
a^2 ≡
29895665113034070831270870121097218673769521820755738885706707818988524697486 % 246824715890162716916904917666448620689
这里的两个同余式里的模数都是质数,所以是可解的
二次同余方程解法的通用算法可以参考Cipolla’s algorithm,和上面的Tonelli-Shanks algorithm都是解决x^2≡n mod p问题的,但是对于不同的约束条件求解效率不同。
通过Cipolla’s algorithm算法求出解之后,再使用中国剩余定理求解a的值,得到a的值之后,再根据LCG的特点:
1 | S3 ≡ ((S1*a+b)*a + b) % m = (S1*a^2 + (a+1)*b) % m |
即
1 | (a+1)*b ≡ (S3-S1*a^2) % m |
可求得b的值
然后再通过
1 | S1 ≡ (S0*a + b) % m => a*S0 ≡ (S1-b) % m |
求得S0,也就是flag。
于是最终的解题脚本:
1 | from random import randint |