这次做这道椭圆曲线的密码的原因是之前见过这个有趣的方程问题。这题求方程:
约束条件:
x,y,z
都是正整数,并且六组解都互素,即给了约束条件gcd(x,y,z)=1
,也就是不能由一组解简单构造多解。- 不计
x,y,z
的顺序,即不能简单得由一组解排列组合。
上面这个方程要爆破到第一组最小的解是不可能的。毕竟这个方程看起来简单(整数解确实简单),但是它的最小正整数解有足足150位!最小解如下:
1 | x=20260869859883222379931520298326390700152988332214525711323500132179943287700005601210288797153868533207131302477269470450828233936557 |
如果要爆算的话,假设最优的只要穷举两个数,那么至少数量级为10^300。现在顶尖的超算也不超过10^20每秒,那么穷举时间大概是10^280s,这个数字应该比宇宙的年龄都大多了。
后面第六组解到了4500位(应该是最小的前六组解了),这个看似简单的方程的解就是如此离谱。
这个题我之前看过zml大佬的博客,有点印象,就把博客翻出来看了一下。有兴趣看详细数学原理的移步大佬的博客以及一篇14年的论文An unusual cubic representation problem(可见这个问题早些年求解出来是可以发数论方向的论文的),我后面写的东西偏脚本一些。
(呜呜,然而因为第一次碰到nc
交互因为输入过长被截断的情况,让我一度怀疑题目错了,错过了拿flag的时间,后面拿socket交互在服务器没关的时候还是拿了flag,还是比赛经验太少了,雾)
服务器源码
1 | #!/usr/bin/env python3 |
解题思路
方程化简,转化为二维空间求解(椭圆曲线)。
仅仅化成这样还是不够的,需要化成WeierstrassForm
模式:
可以将任何场上的椭圆曲线转换成下面的形式:
具体转换原理是可以将变量作一个线性变换(可逆的3*3矩阵),转换各变量的最高次数,然后将其中一个变量先固定为1,转换成上述的有理数域的椭圆曲线形式。每个原始方程的整数解(互素),一对一对应于椭圆曲线上的有理点,因此找到整数解就等于在曲线上找到有理点。具体怎么做我们不用考虑细节(论文内有推导),好消息是开源软件sagemath
集成了上述转换,总之转换成WeierstrassForm
方程后简化了我们计算。更一般的sagemath
化成下面的形式(f,g是有理系数)
然后有理数域上面的圆锥曲线求解就可以参考ECC了,取一组特解点P(X,Y
)不断做切线,不断得到得到nP
,注意这些解都是有理数域上面的,从而我们穷举n,可以得到许多组(X,Y,Z)
,一直到得到6组这样的正整数解即可,如下图(图源zml博客
)。
每一组都互素。为了减少穷举量或者最后结果的长度,可以先用python
穷举两组无关解,之后再代入求解即可。
1 | <<is_valid>> |
解题脚本(python,sagemath,magma
)
上面基于sagemath
的代码大都要自己先算一些参数处来(具体的脚本我还没有写,第一次接触sagemath
,还在摸索sagemath
的一些函数和语法,不过sagemath
还是很好用的,现在忙着保研,后面没事了慢慢摸索,想写一个基于任意正整数N的解的脚本)。
下面是在网上看到的基于magma的一个脚本(N=4)。基本逻辑思路注释非常明白了。当时比赛拿的这个脚本改的代码。基本上可以通用N为任意整数的情况的。
1 | // First we define our environment for our "problem" |
比赛用的的脚本,在线运行magma的网站Magma calculator,但是有个问题是在线环境它输出大于32 MB的时候会截断,并且数字会用/分割,后面再写python脚本把解都存到文件内。通过调输出的整数解的个数刚好可以拿到6组整数解。
1 | R<x,y,z> := RationalFunctionField(Rationals(),3); |
然后就是我算出结果没拿到flag窒息操作了,注意nc
在接受输入过长时会发生截断,因此必须用socket发送数据,可以直接用socket或者pwn
包发送。
最后拿到flag如下:
(sagemath
脚本咕咕咕中,这里放一个比赛后看到的大佬的writeup: Lazzaro)