SUSCTF 2022 Crypto复现

InverseProblem

P r o b l e m Problem Problem

import numpy as np
from secret import flag

def gravity(n,d=0.25):
    A=np.zeros([n,n])
    for i in range(n):
        for j in range(n):
            A[i,j]=d/n*(d**2+((i-j)/n)**2)**(-1.5)
    return A

n=len(flag)
A=gravity(n)
x=np.array(list(flag))
b=A@x
np.savetxt('b.txt',b)

A n a l y s i s Analysis Analysis

简答来说,题目中gravity()函数生成了一个实数矩阵 A \pmb{A} AA,并且大小为len(flag) x len(flag);由flag以单个字符构成的1 x len(flag)大小的矩阵 X \pmb{X} XX;由矩阵乘法 B = A X \pmb{B}=\pmb{A}\pmb{X} BB=AAXX,生成结果矩阵 b \pmb{b} bb;已知量有实数矩阵 A \pmb{A} AA,以及结果矩阵 B \pmb{B} BB

我们已知矩阵乘法中的一个乘数矩阵以及结果矩阵,那么可以计算 X = A B \pmb{X}=\pmb{A} \pmb{B} XX=AABB,得到flag矩阵 X \pmb{X} XX;但是这里我们不能直接这样左乘结果矩阵得 B \pmb{B} BB,因为 A \pmb{A} AA是实数矩阵,并且几乎是矩阵中的元素几乎是无限小数;那么就涉及到一个问题,浮点数的精度达到了17位(Python默认),到哪一位小数才是实际对矩阵乘法的结果造成了影响的呢?

这里我们不得而知,涉及到85*85个数的相乘问题(len(flag) = 85)过于复杂;但是我们可以先用数学符号表达这种有不确定影响的情况(将不确定部分用向量 s s s表示),这实际上就是LWE问题的思路
B + s = A X \pmb{B} + s=\pmb{A}\pmb{X} BB+s=AAXX
LWE问题定义

随机选取一个矩阵 A ∈ Z q m × n \pmb{A}\in\mathbb{Z}^{m\times n}_q AAZqm×n,一个随机向量 s ∈ Z q n s\in \mathbb{Z}^n_q sZqn,和一个随机的噪音 e ∈ ϵ m e\in \epsilon^m eϵm(也叫做误差向量)

系统输出 g A ( s , e ) = A s + e ( m o d q ) g_{\pmb{A}}(s,e)=\pmb{A}s+e\pmod q gAA(s,e)=AAs+e(modq)

问题在于,给定矩阵 A \pmb{A} AA,以及输出 g A ( s , e ) g_{\pmb{A}}(s,e) gAA(s,e);还原 s s s

对比发现我们这里的问题中设置的向量 s s s实际上就相当于误差向量 e e e

对于LWE问题我们可以将其转换为SVP问题求解,通过构造基向量来将目标向量 s s s作为其格的最短向量

可以构造矩阵 ( A 0 − B 1 ) \begin{pmatrix}\pmb{A} & \qquad 0\\ -\pmb{B} & \qquad 1 \end{pmatrix} (AABB01)作为基向量(有多种构造方式以及解决方法)
( X 1 × 85 1 ) ( A 85 × 85 0 85 × 1 − B 1 × 85 1 ) = ( s 1 × 85 1 ) \begin{pmatrix} \pmb{X}_{1\times 85}\qquad 1\end{pmatrix} \begin{pmatrix}\pmb{A}_{85\times 85} & \qquad 0_{85\times 1}\\ -\pmb{B}_{1\times 85} & \qquad 1 \end{pmatrix} = \begin{pmatrix}s_{1\times 85} \qquad 1 \end{pmatrix} (XX1×851)(AA85×85BB1×85085×11)=(s1×851)
( A 0 − B 1 ) \begin{pmatrix}\pmb{A} & \qquad 0\\ -\pmb{B} & \qquad 1 \end{pmatrix} (AABB01)进行LLL算法得到最短向量$ \begin{pmatrix}s_{1\times 85} \qquad 1 \end{pmatrix}$;那么对于上面这个矩阵乘法,我们可以
( X 1 × 85 1 ) = ( s 1 × 85 1 ) ( A 85 × 85 0 85 × 1 − B 1 × 85 1 ) − 1 \begin{pmatrix} \pmb{X}_{1\times 85}\qquad 1\end{pmatrix} = \begin{pmatrix}s_{1\times 85} \qquad 1 \end{pmatrix} \begin{pmatrix}\pmb{A}_{85\times 85} & \qquad 0_{85\times 1}\\ -\pmb{B}_{1\times 85} & \qquad 1 \end{pmatrix}^{-1} (XX1×851)=(s1×851)(AA85×85BB1×85085×11)1
得到flag矩阵 X \pmb{X} XX;而关于为什么这样构造的基向量就可以使目标向量是其格的最短向量,应该是构造类似形式的矩阵(基向量)就可以满足条件(论文出处是这样构造的类似形式的矩阵)

S o l u t i o n Solution Solution

# type:ignore
import numpy as np

def gravity(n,d=0.25):
    A=np.zeros([n,n])
    for i in range(n):
        for j in range(n):
            A[i,j]=d/n*(d**2+((i-j)/n)**2)**(-1.5)
    return A

enc = []
with open("C:\\Users\\Menglin\\Desktop\\b.txt","rb") as f:
    for line in f.readlines():
        enc.append(float(line.strip().decode()))

b = enc
n = 85
multiple = 10 ^ 20
A = gravity(n)

A = [[int(j * multiple) for j in i] for i in A]
b = [int(i * (-1) * multiple) for i in b]
M = [A[i] + [0] for i in range(n)]
M.append(b + [1])
M = Matrix(ZZ, n + 1, n + 1, M)
ans = M.LLL()[0]
# print(ans)
flag = M.solve_left(ans)
# print(bytes(flag))
print(bytes(flag[:-1]).decode())

SpecialCurve3

P r o b l e m Problem Problem

from Crypto.Util.number import *
from secret import flag,getMyPrime
import hashlib
import random

class SpecialCurve:
    def __init__(self,p,a,b):
        self.p=p
        self.a=a
        self.b=b

    def __str__(self):
        return f'SpecialCurve({self.p},{self.a},{self.b})'

    def add(self,P1,P2):
        x1,y1=P1
        x2,y2=P2
        if x1==0:
            return P2
        elif x2==0:
            return P1
        elif x1==x2 and (y1+y2)%self.p==0:
            return (0,0)
        if P1==P2:
            t=(2*self.a*x1-self.b)*inverse(2*y1,self.p)%self.p
        else:
            t=(y2-y1)*inverse(x2-x1,self.p)%self.p
        x3=self.b*inverse(self.a-t**2,self.p)%self.p
        y3=x3*t%self.p
        return (x3,y3)

    def mul(self,P,k):
        assert k>=0
        Q=(0,0)
        while k>0:
            if k%2:
                k-=1
                Q=self.add(P,Q)
            else:
                k//=2
                P=self.add(P,P)
        return Q

def problem(size,k):
    p=getMyPrime(size)
    x=random.randint(1,p-1)
    y=random.randint(1,p-1)
    e=random.randint(1,p-1)
    a=k*random.randint(1,p-1)**2%p
    b=(a*x**2-y**2)*inverse(x,p)%p
    curve=SpecialCurve(p,a,b)
    G=(x,y)
    Q=curve.mul(G,e)
    print(f'curve={curve}')
    print(f'G={G}')
    print(f'Q={Q}')
    return e

e1=problem(128,1)
e2=problem(256,0)
e3=problem(512,-1)
enc=bytes_to_long(hashlib.sha512(b'%d-%d-%d'%(e1,e2,e3)).digest())^bytes_to_long(flag.encode())
print(f'enc={enc}')
'''
curve=SpecialCurve(233083587295210134948821000868826832947,73126617271517175643081276880688551524,88798574825442191055315385745016140538)
G=(183831340067417420551177442269962013567, 99817328357051895244693615825466756115)
Q=(166671516040968894138381957537903638362, 111895361471674668502480740000666908829)
curve=SpecialCurve(191068609532021291665270648892101370598912795286064024735411416824693692132923,0,58972296113624136043935650439499285317465012097982529049067402580914449774185)
G=(91006613905368145804676933482275735904909223655198185414549961004950981863863, 96989919722797171541882834089135074413922451043302800296198062675754293402989)
Q=(13504049588679281286169164714588439287464466303764421302084687307396426249546, 110661224324697604640962229701359894201176516005657224773855350780007949687952)
curve=SpecialCurve(52373730653143623993722188411805072409768054271090317191163373082830382186155222057388907031638565243831629283127812681929449631957644692314271061305360051,28655236915186704327844312279364325861102737672471191366040478446302230316126579253163690638394777612892597409996413924040027276002261574013341150279408716,42416029226399083779760024372262489355327595236815424404537477696856946194575702884812426801334149232783155054432357826688204061261064100317825443760789993)
G=(15928930551986151950313548861530582114536854007449249930339281771205424453985946290830967245733880747219865184207937142979512907006835750179101295088805979, 29726385672383966862722624018664799344530038744596171136235079529609085682764414035677068447708040589338778102975312549905710028842378574272316925268724240)
Q=(38121552296651560305666865284721153617113944344833289618523344614838728589487183141203437711082603199613749216407692351802119887009907921660398772094998382, 26933444836972639216676645467487306576059428042654421228626400416790420281717654664520663525738892984862698457685902674487454159311739553538883303065780163)
enc=4161358072766336252252471282975567407131586510079023869994510082082055094259455767245295677764252219353961906640516887754903722158044643700643524839069337
'''

A n a l y s i s Analysis Analysis

构造了三条曲线,而曲线方程是
y 2 ≡ a x 2 − b x ( m o d p ) y^2 \equiv ax^2 -bx \pmod p y2ax2bx(modp)
可以看作圆锥曲线(而不是椭圆曲线)

怎么看出来的呢?看类SpecialCurve的点加法运算关键代码

        if P1==P2:
            t=(2*self.a*x1-self.b)*inverse(2*y1,self.p)%self.p

而对于整个类里面定义的计算是类似有限域椭圆曲线计算关系的,(关于斜率 t t t的单参数曲线,其加法原理是过原点作两点割线(两点相同为切线)的平行线与曲线相交得到新的一点,满足 t 3 = t 1 t 2 + a t 1 + t 2 m o d    p t_3=\frac{t_1 t_2 +a}{t_1+t_2} \mod p t3=t1+t2t1t2+amodp

image-20220913132711875

所以对照计算关系可知,在这里P1==P2条件下的两点相加,应该是求切线;而t就是切线方程的斜率,斜率的计算可以看作是对y求导,那么反过来对导函数积分
2 y ⋅ y ′ ≡ 2 a x − b ( m o d p ) ⇒ y 2 ≡ a x 2 − b x ( m o d p ) 2y\cdot y'\equiv 2ax-b \pmod p \\ \Rightarrow y^2 \equiv ax^2 -bx\pmod p 2yy2axb(modp)y2ax2bx(modp)
题目在这种圆锥曲线加密的情况下,设置了不同的方程参数,分别是 a ≡ r 2 ( m o d p ) a\equiv r^2\pmod p ar2(modp),也就是说 a a a p p p的二次剩余; a = 0 a=0 a=0,圆锥曲线方程变为 y 2 ≡ − b x ( m o d p ) y^2\equiv -bx \pmod p y2bx(modp) a ≡ − r 2 ( m o d p ) a\equiv -r^2\pmod p ar2(modp),非二次剩余

那么可以根据计算斜率的公式 t 3 = t 1 t 2 + a t 1 + t 2 m o d    p t_3=\frac{t_1 t_2 +a}{t_1+t_2} \mod p t3=t1+t2t1t2+amodp;将已知初始点 G G G以及终点 Q Q Q的问题,进一步细化为为已知这两点所在切线的斜率,求 G ⋅ e = Q G\cdot e=Q Ge=Q中的 e e e

根据论文 SUSCTF2022_official_wp/圆锥曲线公钥密码算法的参数选择_徐旭东.pdf at main · susers/SUSCTF2022_official_wp (github.com) 所写的将圆锥曲线群变为普通乘法群的方法,我们可以将ECDLP问题变为DLP问题,进而使用求解DLP问题的方法快速求解第一种情况;第二种情况中,对照 t 3 ≡ t 1 t 2 + a t 1 + t 2 m o d    p t_3\equiv \frac{t_1 t_2 +a}{t_1+t_2} \mod p t3t1+t2t1t2+amodp,发现斜率可以写作 1 t 3 ≡ 1 t 1 + 1 t 2 ( m o d p ) \frac{1}{t_3} \equiv \frac{1}{t_1} +\frac{1}{t_2} \pmod p t31t11+t21(modp),相当于加法群,也就是说可以将 G ⋅ e = Q G\cdot e=Q Ge=Q表示为 1 t Q ≡ 1 t G ⋅ e ( m o d p ) \frac{1}{t_Q} \equiv \frac{1}{t_G} \cdot e\pmod p tQ1tG1e(modp),那么直接求逆元即可;第三种情况,所在曲线参数没有问题,但是模数 p p p对于 p + 1 p+1 p+1是光滑数,那么应用Pohlig_hellman算法求解ECDLP问题的方法即可

S o l u t i o n Solution Solution

# type:ignore
from Crypto.Util.number import *
from sympy.polys.galoistools import gf_crt
from sympy.polys.domains import ZZ
from tqdm import tqdm
import requests
import hashlib

class SpecialCurve:
    def __init__(self,p,a,b):
        self.p=p
        self.a=a
        self.b=b

    def __str__(self):
        return f'SpecialCurve({self.p},{self.a},{self.b})'

    def add(self,P1,P2):
        x1,y1=P1
        x2,y2=P2
        if x1==0:
            return P2
        elif x2==0:
            return P1
        elif x1==x2 and (y1+y2)%self.p==0:
            return (0,0)
        if P1==P2:
            t=(2*self.a*x1-self.b)*inverse(2*y1,self.p)%self.p
        else:
            t=(y2-y1)*inverse(x2-x1,self.p)%self.p
        x3=self.b*inverse(self.a-t**2,self.p)%self.p
        y3=x3*t%self.p
        return (x3,y3)

    def mul(self,P,k):
        assert k>=0
        Q=(0,0)
        while k>0:
            if k%2:
                k-=1
                Q=self.add(P,Q)
            else:
                k//=2
                P=self.add(P,P)
        return Q


curve1 = SpecialCurve(233083587295210134948821000868826832947,73126617271517175643081276880688551524,88798574825442191055315385745016140538)
G = (183831340067417420551177442269962013567, 99817328357051895244693615825466756115)
Q = (166671516040968894138381957537903638362, 111895361471674668502480740000666908829)
p = curve1.p
a = curve1.a
theta = sqrt(GF(p)(a))
t_G = GF(p)(G[1]) // GF(p)(G[0])
t_Q = GF(p)(Q[1]) // GF(p)(Q[0])
func_G = (t_G + theta) // (t_G - theta)
func_Q = (t_Q + theta) // (t_Q - theta)
e1 = func_Q.log(func_G)
print(e1)
e1 = 184572164865068633286768057743716588370



curve2 = SpecialCurve(191068609532021291665270648892101370598912795286064024735411416824693692132923,0,58972296113624136043935650439499285317465012097982529049067402580914449774185)
G = (91006613905368145804676933482275735904909223655198185414549961004950981863863, 96989919722797171541882834089135074413922451043302800296198062675754293402989)
Q = (13504049588679281286169164714588439287464466303764421302084687307396426249546, 110661224324697604640962229701359894201176516005657224773855350780007949687952)
p = curve2.p
a = curve2.a
theta = sqrt(GF(p)(a))
t_G = GF(p)(G[1]) // GF(p)(G[0])
t_Q = GF(p)(Q[1]) // GF(p)(Q[0])
e2 = t_G // t_Q
print(e2)
e2 = 131789829046710687154053378348742202935151384644040019239219239301007568911745



curve3 = SpecialCurve(52373730653143623993722188411805072409768054271090317191163373082830382186155222057388907031638565243831629283127812681929449631957644692314271061305360051,28655236915186704327844312279364325861102737672471191366040478446302230316126579253163690638394777612892597409996413924040027276002261574013341150279408716,42416029226399083779760024372262489355327595236815424404537477696856946194575702884812426801334149232783155054432357826688204061261064100317825443760789993)
G = (15928930551986151950313548861530582114536854007449249930339281771205424453985946290830967245733880747219865184207937142979512907006835750179101295088805979, 29726385672383966862722624018664799344530038744596171136235079529609085682764414035677068447708040589338778102975312549905710028842378574272316925268724240)
Q = (38121552296651560305666865284721153617113944344833289618523344614838728589487183141203437711082603199613749216407692351802119887009907921660398772094998382, 26933444836972639216676645467487306576059428042654421228626400416790420281717654664520663525738892984862698457685902674487454159311739553538883303065780163)

def get_factor(n_to_fac: int) -> list:
    response = requests.get(r'http://factordb.com/api', params={"query": str(n_to_fac)})
    facs = []
    for one in response.json().get("factors"):
        facs += [int(one[0])] * one[1]
    facs = [[i, facs.count(i)] for i in set(facs)]
    facs = [facs[i][0] ** facs[i][1] for i in range(len(facs))]
    return facs

def Pohlig_Hellman(G, Q, P, curve):
    p_ = P + 1 # 哪个数是光滑数,就赋值什么数;注意最后结果也要有相应变化
    factors = get_factor(p_)

    INF = (0,0)   #无穷远点
    dlogs = []
    for i in tqdm(factors):
        Now = INF
        tmpG = curve.mul(G, (p_) // i)
        tmpQ = curve.mul(Q, (p_) // i)
        for dlog in range(i):
            Now = curve.add(Now, tmpG)
            if Now == tmpQ:
                dlogs.append(dlog)
                break
    return gf_crt(dlogs, factors, ZZ)

e3 = Pohlig_Hellman(G, Q, curve3.p, curve3) + 1
print(e3)

e3 = 23331486889781766099145299968747599730779731613118514070077298627895623872695507249173953050022392729611030101946661150932813447054695843306184318795467215

enc = 4161358072766336252252471282975567407131586510079023869994510082082055094259455767245295677764252219353961906640516887754903722158044643700643524839069337
flag = bytes_to_long(hashlib.sha512(b'%d-%d-%d'%(e1,e2,e3)).digest()) ^^ enc
print(long_to_bytes(flag))

Large case

(10条消息) SUSCTF_Crypto_large case_复现_M3ng@L的博客-CSDN博客

Paper_Tiger

流密码的相关操作,使用的BM算法(Berlekamp-Massey)来解决最后的问题;(关于具体实现原理和过程我没看懂)

详情可以见 R e f e r e n c e Reference Reference中的参考文章

R e f e r e n c e Reference Reference

SUSCTF2022_official_wp/crypto/SpecialCurve3 at main · susers/SUSCTF2022_official_wp (github.com)

SUSCTF Writeup by X1cT34m | 小绿草信息安全实验室 (njupt.edu.cn)

SUSCTF-Crypto方向所有赛题Love_DengFeng’s Blog(love-deng-feng.top)

SUSCTF 2022 - hash_hash - 博客园 (cnblogs.com)

lfsr-berlekamp-massey/berlekamp_massey.py at master · thewhiteninja/lfsr-berlekamp-massey (github.com)

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

M3ng@L

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值