NepCTF 2022
signin
p,q极其近似,对n开根号取前后素数求出p,q
1 |
|
中学数学
已知$q=next_prime(p+(p>>500))$,则
$$
q=\lfloor (1+\frac{1}{2^{500}})p\rfloor+r
$$
其中r是满足q为素数的最小整数,
$$
n=pq=\lfloor (1+\dfrac{1}{2^{500}})p^2\rfloor+rp
$$
于是
$$
⌊(1+\dfrac{1}{2^{500}})n⌋=⌊(1+\dfrac{1}{2^{500}})p⌋^2+⌊(1+\dfrac{1}{2^{500}})rp⌋>⌊(1+\dfrac{1}{2^{500}})p⌋^2
$$
又有
$$
⌊(1+\dfrac{1}{2^{500}})n⌋=⌊(1+\dfrac{1}{2^{500}})p⌋^2+⌊(1+\dfrac{1}{2^{500}})rp⌋<⌊(1+\dfrac{1}{2^{500}})p⌋^2+2⌊(1+\dfrac{1}{2^{500}})rp⌋+r^2 = q^2
$$
所以
$$
⌊(1+\dfrac{1}{2^{500}})p⌋<\sqrt{\lfloor(1+\frac{1}{2^{500}})n\rfloor}<\lfloor(1+\frac{1}{2^{500}})p\rfloor+r
$$
即
$$
q−r<\sqrt{⌊(1+\dfrac{1}{2^{500}})n⌋}
$$
所以
$$
\lfloor(1+\frac{1}{2^{500}})p\rfloor<\sqrt{\lfloor(1+\frac{1}{2^{500}})n\rfloor}<\lfloor(1+\frac{1}{2^{500}})p\rfloor+r
$$
得到了无需爆破的形式。
但注意到next_prime算法的本质逻辑即是通过不断枚举奇数判断是否为素数,而这里n的分解保证了其必为素数,再判断素数的话属于是多此一举(浪费计算资源),所以我们可以根据素数分布公式或next_prime的方案向下枚举,得到唯一分解即可。
1 |
|
bd_key
比赛时思路:
分析代码发现,do_schnorr_identification()
能影响(或有作用)的是challenge()
,分析getRandomNBytes()
,发现给出的c = __Dual_EC_DRBG(0)
。
在忽略s_i
等其他变量的,通过getRandomNBytes()
分析key
和c
的获取:
1 |
|
虽然看上去可以通过c
求key
,但是在执行上面两个函数时会影响s_i
,进而影响到key
。
于是乎大致思路就是通过c
来求出r_i
之前的s_i
,然后走一遍流程就可以得到key
。
分析__Dual_EC_DRBG()
,得出以下公式
1 |
|
我们知道P,Q,c
,利用P,Q和生成的点都在椭圆曲线上,兴许可以联立方程求出s_i
?
利用椭圆曲线方程,已知x
求y
,求出生成点,然后利用ECDLP求解s_i
(好像跑不出来)
注意类名,放网上一搜就有相关内容!!!
Dual_EC_DBRG
问题,有兴趣的可以搜一搜其背后的故事,真有点魔幻。
整合一下上面的分析,生成c
的过程有方程如下:
1 |
|
我们知道d
,这就相当于给了个后门。
假设横坐标为c
的点为R = (c,y)
,根据上面的(1)(2)(3)公式以及$P=d*Q$,有
$$
d*R = d*(s_i*Q) = s_i*(d*Q) = s_i*P
$$
于是可以直接得到(3)的$s_i$ ,再走一遍流程就可以得到key
。
1 |
|
P or S
分析enc()
函数:
1 |
|
细心点可以发现 $q$ 的生成可以看成 $GF(2)$ 上的矩阵运算(行乘列,很像吧),假设 $Pbox$ 的矩阵为 $P$ (32×32),传入的明文的矩阵为 $M$ (32×1),则中间生成矩阵为 $T=P*M$ ,$t$ 这一步就是额外加一个矩阵 $K_i$(32×1),于是6次循环有如下公式
$$
c = P*(P*(P*(P*(P*(P*M+K_1)+K_2)+K_3)+K_4)+K_5)+K_6 = P^6*M+\sum_{i=0}^{n=6}P^{6-i}K_i
$$
通过已知一组明密文可以计算出对应的$\sum_{i=0}^{n=6}P^{6-i}K_i$ 。
1 |
|
参考: