2023 N1CTF

WriteUp 9个月前 admin
90 0 0

e2W@rmup

import hashlib
import ecdsa
from Crypto.Cipher import AES
from Crypto.Util.Padding import pad
from Crypto.Util.number import *
from secret import flag

def gen():
    curve = ecdsa.NIST256p.generator
    order = curve.order()
    d = randint(1, order-1)
    while d.bit_length() != 256:
        d = randint(1, order-1)
    pubkey = ecdsa.ecdsa.Public_key(curve, curve * d)
    privkey = ecdsa.ecdsa.Private_key(pubkey, d)
    return pubkey, privkey, d

def nonce_gen(msg, d):
    msg_bin = bin(msg)[2:].zfill(256)
    d_bin = bin(d)[2:].zfill(256)
    nonce = int(msg_bin[:128] + d_bin[:128], 2)
    return nonce

def sign(msg, privkey, d):
    msg_hash = bytes_to_long(hashlib.sha256(msg).digest())
    nonce = nonce_gen(msg_hash, d)
    sig = privkey.sign(msg_hash, nonce)
    s, r = sig.s, sig.r
    return s, r

pk, sk, d = gen()
msg = b'welcome to n1ctf2023!'
s, r = sign(msg, sk, d)
print(f's = {s}')
print(f'r = {r}')

m = pad(flag, 16)
aes = AES.new(long_to_bytes(d), mode=AES.MODE_ECB)
cipher = aes.encrypt(m)
print(f'cipher = {cipher}')

"""
s = 98064531907276862129345013436610988187051831712632166876574510656675679745081
r = 9821122129422509893435671316433203251343263825232865092134497361752993786340
cipher = b'xf3#xffx17xdfxbbxc0xc6vx1bgxc7x8a6xf2xdf~x12xd8]xc5x02Otx99x9fxf7xf3x98xbcx045x08xfbxce1@exbcg[Ixd1xbfxf8xean-'
"""

根据 ECDSA 签名公式

不过这里的

其中 是一整个未知的

签名公式调整为

我们将已知的 放一块,未知的有 ,整理一下就是

其中未知量 是 128 比特的,模数 是 256 比特的,

于是考虑构造格子

使得  

本地生成数据测试一下先

import hashlib
import ecdsa
from Crypto.Util.number import *

curve = ecdsa.NIST256p.generator
order = curve.order()
a=ecdsa.NIST256p.curve.a()
b=ecdsa.NIST256p.curve.b()
p=ecdsa.NIST256p.curve.p()
d_ = 112799702410807302994951924898034373856576906910372741339939214376262921112129
s = 109639753269686143977071246486442277683571473955044500468119095108455162948055
r = 115148860739222900018017222670090085973159375004845247957903412786080971680513
msg = b'welcome to n1ctf2023!'
msg_hash = bytes_to_long(hashlib.sha256(msg).digest())

msg_bin = bin(msg_hash)[2:].zfill(256)
M = int(msg_bin[:128], 2) << 128

shift = 2^1024
L = Matrix(ZZ,[[shift*(2^128 * r - s),1,0,0],
               [shift*(r),0,1,0],
               [shift*(M*s-msg_hash),0,0,2^128],
               [shift*(order),0,0,0]])
L=L.LLL()

sage: d_>>128
331488532395847874510754715973804122202
sage: d_%2^128
274665248672883640880810570625320766017


sage: L[1]
(0-297127138964097507062129757594500328317-308074196458482485875868587492327724533340282366920938463463374607431768211456)


测试发现得到的答案并不是 dh 和 dl, 尝试固定 dh 和 dl 的高位试一试

bruce1 = 0b11<<126
bruce2 = 0b11<<126
shift = 2^1024
tmp = M*s-msg_hash-(2^128 * r - s)*bruce1-r*bruce1
L = Matrix(ZZ,[[shift*(2^128 * r - s),1,0,0],
               [shift*(r),0,1,0],
               [shift*(tmp),0,0,2^126],
               [shift*(order),0,0,0]])
L=L.LLL()

打印结果和 d_ 的高低位对比了一下,也是不太行。尝试换个方式构造格子

由于

于是有

于是考虑构造格子

使得  

R = inverse(r,order)

L = Matrix(ZZ,[[R*(2^128 * r - s),1,0],
               [R*(M*s-msg_hash),0,2^128],
               [(order),0,0]])
L=L.LLL()

sage: L[1]
(308074196458482485875868587492327724533-297127138964097507062129757594500328317340282366920938463463374607431768211456)

还是不太对,,也爆破两个高位试试

R = inverse(r,order)

bruce1 = 0b11<<126
bruce2 = 0b11<<126
tmp = R*(M*s-msg_hash) - bruce1 * R*(2^128 * r - s) - bruce2
L = Matrix(ZZ,[[R*(2^128 * r - s),1,0],
               [tmp,0,2^126],
               [(order),0,0]])
L=L.LLL()

sage: L[1]
(52862421267778638278337631918501565941-4191536377339365946459880202067416972585070591730234615865843651857942052864)

还是不对,,不知道问题在哪。

但是拿着题目的数据来搞是可以搞出 d 的,emmmmm搞不懂了

import hashlib
import ecdsa
from Crypto.Util.number import *

curve = ecdsa.NIST256p.generator
order = curve.order()
a=ecdsa.NIST256p.curve.a()
b=ecdsa.NIST256p.curve.b()
p=ecdsa.NIST256p.curve.p()

s = 98064531907276862129345013436610988187051831712632166876574510656675679745081
r = 9821122129422509893435671316433203251343263825232865092134497361752993786340
msg = b'welcome to n1ctf2023!'
msg_hash = bytes_to_long(hashlib.sha256(msg).digest())

msg_bin = bin(msg_hash)[2:].zfill(256)
M = int(msg_bin[:128], 2) << 128
R = inverse(r,order)

bruce1 = 0b11<<126
bruce2 = 0b11<<126
tmp = R*(M*s-msg_hash) - bruce1 * R*(2^128 * r - s) - bruce2
L = Matrix(ZZ,[[R*(2^128 * r - s),1,0],
               [tmp,0,2^126],
               [(order),0,0]])
L=L.LLL()
dh = bruce1-L[0][1]
dl = bruce2+L[0][0]
d=(dh<<128)+dl
from Crypto.Cipher import AES
cipher = b'xf3#xffx17xdfxbbxc0xc6vx1bgxc7x8a6xf2xdf~x12xd8]xc5x02Otx99x9fxf7xf3x98xbcx045x08xfbxce1@exbcg[Ixd1xbfxf8xean-'

aes = AES.new(long_to_bytes(d), mode=AES.MODE_ECB)
m = aes.decrypt(cipher)
print(f'm = {m}')

其实也可以用另一种做法,因为我们已经搞出式子

未知数是 ,可以直接去搞一下二元 copper

import itertools
 
def small_roots(f, bounds, m=1, d=None):
        if not d:
                d = f.degree()
 
        R = f.base_ring()
        N = R.cardinality()
        
        f /= f.coefficients().pop(0)
        f = f.change_ring(ZZ)
 
        G = Sequence([], f.parent())
        for i in range(m+1):
                base = N^(m-i) * f^i
                for shifts in itertools.product(range(d), repeat=f.nvariables()):
                        g = base * prod(map(power, f.variables(), shifts))
                        G.append(g)
 
        B, monomials = G.coefficient_matrix()
        monomials = vector(monomials)
 
        factors = [monomial(*bounds) for monomial in monomials]
        for i, factor in enumerate(factors):
                B.rescale_col(i, factor)
 
        B = B.dense_matrix().LLL()
 
        B = B.change_ring(QQ)
        for i, factor in enumerate(factors):
                B.rescale_col(i, 1/factor)
 
        H = Sequence([], f.parent().change_ring(QQ))
        for h in filter(None, B*monomials):
                H.append(h)
                I = H.ideal()
                if I.dimension() == -1:
                        H.pop()
                elif I.dimension() == 0:
                        roots = []
                        for root in I.variety(ring=ZZ):
                                root = tuple(R(root[var]) for var in f.variables())
                                roots.append(root)
                        return roots
        return []

 

import hashlib
import ecdsa
from Crypto.Util.number import *

curve = ecdsa.NIST256p.generator
order = curve.order()
a=ecdsa.NIST256p.curve.a()
b=ecdsa.NIST256p.curve.b()
p=ecdsa.NIST256p.curve.p()

s = 98064531907276862129345013436610988187051831712632166876574510656675679745081
r = 9821122129422509893435671316433203251343263825232865092134497361752993786340
msg = b'welcome to n1ctf2023!'
msg_hash = bytes_to_long(hashlib.sha256(msg).digest())

msg_bin = bin(msg_hash)[2:].zfill(256)
M = int(msg_bin[:128], 2) << 128
R = inverse_mod(r,order)

bruce1 = 0b11<<126
bruce2 = 0b11<<126
tmp = int(R*(M*s-msg_hash) - bruce1 * R*(2^128 * r - s) - bruce2)

bounds = (2^1262^126)
P.<x, y> = PolynomialRing(Zmod(order))

f = tmp - R*(2^128 * r - s)*x - y

res = (small_roots(f, bounds,m=3,d=4))[0]
dh = (bruce1 + res[0])  % order   
dl = (bruce2 + res[1])  % order   
d = (dh<<128)+dl

sage: d
75767369414377063170504861698029562004628781620076152109208613361284011206099

e20k

#!/usr/bin/sage
from Crypto.Util.Padding import pad
from Crypto.Util.number import *
from Crypto.Cipher import AES
from hashlib import md5
import signal
import random
import os
FLAG = os.environ.get('FLAG''n1ctf{XXXXFAKE_FLAGXXXX}')

class ECPrng:
    def __init__(self):
        self.N = self.keygen()
        self.E = EllipticCurve(Zmod(self.N), [37])
        self.bias = random.randint(12^128)
        self.state = None
        print(f"N = {self.N}")
        
    def keygen(self):
        P = getPrime(256)
        while True:
            q = getPrime(256)
            if is_prime(2*q-1):
                Q = 2*q^2-q
                return P*Q
    
    def setState(self, x0, y0):
        self.state = self.E(x0, y0)
    
    def next(self):
        self.state = 4*self.state
        return int(self.state.xy()[0])+self.bias

def v2l(v):
    tp = []
    for item in v:
        tp.append(item.list())
    return tp

def Sample(eta, num, signal=0):
    if signal:
        random.seed(prng.next())
    s = []
    for _ in range(num):
        if random.random() < eta:
            s.append(1)
        else:
            s.append(0)
    return Rq(s)

class P:
    def __init__(self, A, s, t):
        self.A = A
        self.s = s
        self.t = t
        self.y = None
    
    def generateCommit(self, Verifier):
        self.y = vector(Rq, [Sample(0.3, N, 1for _ in range(m)])
        w = self.A * self.y
        Verifier.w = w
        print(f"w = {w.list()}")
        
    def generateProof(self, Verifier, c):
        z = self.s*c + self.y
        print(f"z = {v2l(z)}")
        Verifier.verifyProof(z)

class V:
    def __init__(self, A, t):
        self.A = A
        self.t = t
        self.w = None
        self.c = None
        
    def challenge(self, Prover):
        self.c = Sample(0.3, N)
        print(f"c = {self.c.list()}")
        Prover.generateProof(self, self.c)
        
    def verifyProof(self, z):
        if self.A*z == self.t*self.c + self.w:
            return True
        return False

def Protocol(A, secret, t):
    prover = P(A, secret, t)
    verifier = V(A, t)
    prover.generateCommit(verifier)
    verifier.challenge(prover)

if __name__ == "__main__":
    try:
        signal.alarm(120)
        print("ECPRNG init ...")
        prng = ECPrng()
        x0, y0 = input("PLZ SET PRNG SEED > ").split(',')
        prng.setState(int(x0), int(y0))
        N, q, m = (256419782115)
        PRq.<a> = PolynomialRing(Zmod(q))
        Rq = PRq.quotient(a^N + 1'x')
        A = vector(Rq, [Rq.random_element() for _ in range(m)])
        secret = vector(Rq, [Sample(0.3, N) for _ in range(m)])
        t = A*secret
        print(f"A = {v2l(A)}")
        print(f"t = {t.list()}")
        Protocol(A, secret, t)
        cipher = AES.new(md5(str(secret).encode()).digest(), mode=AES.MODE_ECB)
        print(f"ct = {cipher.encrypt(pad(FLAG.encode(), 16)).hex()}")
    except:
        print("error!")

这个代码比较多,我们从后面往前看,题目最终用 secret 作为 AES 的 key 加密了 FLAG,于是这道题就是需要我们利用信息求出  secret,其中 secret = vector(Rq, [Sample(0.3, N) for _ in range(m)]),打印一下这个 secret 是一个含有 15 个随即多项式的数组,很麻

2023 N1CTF

看到它是由 Sample 函数生成的,

def Sample(eta, num, signal=0):
    if signal:
        random.seed(prng.next())
    s = []
    for _ in range(num):
        if random.random() < eta:
            s.append(1)
        else:
            s.append(0)
    return Rq(s)

Sample 函数调用了 random.seed(prng.next()),种子由 prng 也就是 ECPrng()决定。

class ECPrng:
    def __init__(self):
        self.N = self.keygen()
        self.E = EllipticCurve(Zmod(self.N), [37])
        self.bias = random.randint(12^128)
        self.state = None
        print(f"N = {self.N}")
        
    def keygen(self):
        P = getPrime(256)
        while True:
            q = getPrime(256)
            if is_prime(2*q-1):
                Q = 2*q^2-q
                return P*Q
    
    def setState(self, x0, y0):
        self.state = self.E(x0, y0)
    
    def next(self):
        self.state = 4*self.state
        return int(self.state.xy()[0])+self.bias

ECPrng 每次状态更新函数是计算 self.state = 4*self.state,并且我们可以控制第一个 state: x0, y0 = input("PLZ SET PRNG SEED > ").split(',') 因此如果我们能够在这个曲线上找到一个阶为 3 的点,那么这个状态就永远不会变了。(虽然目前也还不知道有啥用)

另外题目给出的数据是 v2l(A)t = A*secret,A 和 secret 都是有 15 个多项式元素的向量, t 是它们的内积,如果 secret 的每个元素都一样(前面我们控制state的操作有意义了),那么就有 ,于是只需要根据 v2l(A) 恢复 A 就行。

def v2l(v):
    tp = []
    for item in v:
        tp.append(item.list())
    return tp

v2l 只是一个向量转list的函数啊,emmm,那这题好像就这么轻易的解决啦?只需要找到一个 3-torsion

class ECPrng:
    def __init__(self):
        self.N = self.keygen()
        self.E = EllipticCurve(Zmod(self.N), [37])
        self.bias = random.randint(12^128)
        self.state = None
        print(f"N = {self.N}")
        
    def keygen(self):
        P = getPrime(256)
        while True:
            q = getPrime(256)
            if is_prime(2*q-1):
                Q = 2*q^2-q
                return P*Q

这里曲线的模数不是素数,所以我们还得先分解一下。

 为素数,于是 ,那么

因此 的一个倍数,回想一下 William’s p+1攻击 的条件,对于 ,如果能够找到 的倍数 ,那么,那么就可以通过计算 计算出 的分解 ,其中 是 Lucas 序列,那么这里,我们就有

# https://tover.xyz/p/2023-N1CTF-Crypto-Part/#e20k

def V(k, N, A=5):
    M = matrix(Zmod(N), [[A, -1],[10]])
    v = vector(Zmod(N), [A, 2])
    vk = M^k * v
    return Integer(vk[1])

def factorN(N):
    A = 5
    while True:
        v2n = V(2*N, N, A)
        g = gcd(v2n-2, N)
        print('[Log] %d - %d' % (A, g))
        if g.nbits() >= 256 and is_prime(g):
            r = g
            q = (r+1) // 2
            p = N // (q * r)
            if p * q * r == N:
                return p, q, r
        A = next_prime(A)

至于找 3 – torsion,就先分别在子群 下找到 3 – torsion,然后再 crt 一下应该就是模 n 下的 3 – torsion 了叭(虽然我暂时并不能给出证明,但直觉是这样的)。不过并不是所有曲线都一定会有 3 – torsion 的,所以还是要多交互几次,多试几个 N(大概十个里面有一个可以)。

# https://tover.xyz/p/2023-N1CTF-Crypto-Part/#e20k

def check_order_k(fN, k):
    p, q, r = fN
    Ep = EllipticCurve(GF(p), [37])
    Eq = EllipticCurve(GF(q), [37])
    Er = EllipticCurve(GF(r), [37])
    for Ex in (Ep, Eq, Er):
        if Ex.order() % k != 0:
            return False
    return True

def EN_random_element_k(fN, k=3):
    p, q, r = fN
    Ep = EllipticCurve(GF(p), [37])
    Eq = EllipticCurve(GF(q), [37])
    Er = EllipticCurve(GF(r), [37])     
    Ps = [(Ex.order() // k) * Ex.random_element() for Ex in (Ep, Eq, Er)]
    x = crt([Integer(Px.xy()[0]) for Px in Ps], [p, q, r])
    y = crt([Integer(Px.xy()[1]) for Px in Ps], [p, q, r])

    En = EllipticCurve(Zmod(N), [37])
    assert En(x,y)*4 ==  En(x,y)
    return x, y

def keygen():
    P = getPrime(256)
    while True:
        q = getPrime(256)
        if is_prime(2*q-1):
            Q = 2*q^2-q
            return P*Q
while True:  
    N = keygen()
    fN = factorN(N)
    if check_order_k(fN,3):
        print(N)
        x,y = EN_random_element_k(fN, k=3)
        print(x,y)
        break
        
#1013875582627032602677626533185964955011127677196354420434828713957246193599624662633588734228349095074683816857070250517758699617638835360531234909866250719846403738306106812969931362411442466901415440311143618623850290712922404891
#388270114163740316018563212433699905540435323654437709674213902800454865413107430369939099091487225499733029234058897062202031722366681230348387684468253806971170013436257172428790862397692286579969065262734353479934623815448967554 39264094293498434137845233422870293264878580043367647949186920341399958082223132643327768740861704251226249769008326413513274274898546333080951020122589704432717184022958189792274187727413181175120896520213476497528219270487500650

本以为这题到此为止了,只需要再简单的计算一下 就可以了,测试时才发现

def Sample(eta, num, signal=0):
    if signal:
        random.seed(prng.next())
    ...
    
def generateCommit(self, Verifier):
    self.y = vector(Rq, [Sample(0.3, N, 1for _ in range(m)])
    w = self.A * self.y
    Verifier.w = w
    print(f"w = {w.list()}")

secret = vector(Rq, [Sample(0.3, N) for _ in range(m)])  

只有当 Sample 函数的 signal=1 的时候才触发种子的更新,因此我们只能控制 的每一个元素相同。

于是有

题目还给出了  z = self.s*c + self.y

因此我们还能计算

从而有 (小绕了一下,问题不大~)

PRq.<a> = PolynomialRing(Zmod(q))
Rq = PRq.quotient(a^N + 1'x')
A = vector(Rq, [Rq(i) for i in A])
t = Rq(t)
w = Rq(w)
c = Rq(c)
z = vector(Rq, [Rq(i) for i in z])
y = w/sum(A)
y = vector(Rq, [y]*15)
res = z - y
secret=[]
for i in range(15):
    secret.append(res[i]/c)
secret = vector(Rq,secret)

e2D1p

from Crypto.Util.number import *
import os
FLAG = os.environ.get('FLAG'b'n1ctf{XXXXFAKE_FLAGXXXX}')
assert FLAG[:6] == b'n1ctf{' and FLAG[-1:] == b'}'
FLAG = FLAG[6:-1]


def keygen(nbits):
    while True:
        q = getPrime(nbits)
        if isPrime(2*q+1):
            if pow(0x10001, q, 2*q+1) == 1:
                return 2*q+1

def encrypt(key, message, mask):
    return pow(0x10001, message^mask, key)


p = keygen(512)
flag = bytes_to_long(FLAG)
messages = [getRandomNBitInteger(flag.bit_length()) for i in range(200)]
enc = [encrypt(p, message, flag) for message in messages]

print(f'message = {messages}')
print(f'enc = {enc}')

题目生成了 200 个随机消息 message,并给出了对应密文,加密方式为 ,设 ,简记为 ,不过 未知

看起来像是根据 解出幂次 的 dlp 问题,但是这里并不知道 p,并且 也并不光滑,应该是要利用 200 条 message 构造出些什么。

首先第一步肯定还是要想办法把这个 给恢复出来的,我们手里的密文 是与 p 相关的。

根据异或的性质,我们可以这样表示


于是


如果我们能够找到比较小的 使得

,再把这些 分为正负两部分 ,就有 。那么多找几个这样的 就能通过 gcd 求出 p 了。


展开,得到

两边取对数

因此我们只需要使得 即可

我们构造如下矩阵,

其中 是一个放大系数,为了使得目标向量对应元素为 0 。

# https://github.com/Nu1LCTF/n1ctf-2023/blob/main/crypto/e2D1p/solution/sol.sage

m_l = 159
dim = 200
L = matrix(ZZ, dim, dim+m_l+1)
g = 2^512
for i in range(dim):
    L[i, i] = 1
    L[i, dim+m_l] = g

for i in range(dim):
    line = bin(message[i])[2:].rjust(m_l, '0')
    for j in range(len(line)):
        if int(line[j]):
            L[i, dim+j] = g
        else:
            L[i, dim+j] = 0


basis = L.LLL()[:40]
p = []
for item in basis:
    g1 = 1
    g2 = 1
    vec = item[:dim]
    for i in range(len(vec)):
        if vec[i] >= 0:
            g1 *= pow(enc[i], vec[i])
        else:
            g2 *= pow(enc[i], -vec[i])
    p.append(g1-g2)
    
p = list(factor(reduce(GCD, p)))[-1][0]
# 25070940894294854944213724808057610995878580501834029791436842569011975159898869595617649716256078890953233822913946718277574163417715224718477846001735447

有了 之后回到式子

构造矩阵

尝试解 ,发现无解,即意味着 时,

无解


,发现有解,即意味着 时,

有解


再尝试解 ,即意味着求


,如果 ,则说明 ,如果 ,则说明

# https://blog.maple3142.net/2023/10/23/n1ctf-2023-writeups/#e2d1p

from sage.all import *
from Crypto.Util.number import *
from subprocess import check_output
from re import findall
from binteger import Bin

# ref: https://affine.group/writeup/2022-01-TetCTF#fault
bl = 159
mat = matrix(
    ZZ,
    [
        Bin(e, bl).tuple[::-1] + (1,) for e, c in zip(message, enc)
    ],  # 1 for the base b = 0x10001^flag
)


def field_exp(el, e):
    a, b = e.numerator(), e.denominator()
    el **= a
    q = el.parent().characteristic() // 2
    d = inverse_mod(b, q)
    return el**d

p = 25070940894294854944213724808057610995878580501834029791436842569011975159898869595617649716256078890953233822913946718277574163417715224718477846001735447
F = GF(p)
sol = mat.solve_left(vector([0] * (bl - 1) + [11]))  # pick a base that works
y = product([field_exp(F(y), z) for y, z in zip(enc, sol)])

recovered_bits = [0] * bl
recovered_bits[bl - 1] = 1
for i in reversed(range(bl - 1)):
    base = vector([0] * (bl - 1) + [11])
    base[i] = 1
    sol = mat.solve_left(base)
    x = product([field_exp(F(y), z) for y, z in zip(enc, sol)])
    z = F(0x10001) ** (2**i)
    if x == y * z:
        recovered_bits[i] = 0
    else:
        recovered_bits[i] = 1
    flag = Bin(recovered_bits[::-1]).bytes
    print(flag)
# n1ctf{s0o0_ezzz_dLLLp%$#@!}

e2ls0

from string import ascii_letters
from sympy import sqrt
import random
import signal
import os
FLAG = os.environ.get('FLAG''n1ctf{XXXXFAKE_FLAGXXXX}')    
    
def banner():
    print("""
░░░░░░░ ░░░░░░  ░░ ░░░░░░░  ░░░░░░  
▒▒           ▒▒ ▒▒ ▒▒      ▒▒    ▒▒ 
▒▒▒▒▒    ▒▒▒▒▒  ▒▒ ▒▒▒▒▒▒▒ ▒▒    ▒▒ 
▓▓      ▓▓      ▓▓      ▓▓ ▓▓    ▓▓ 
███████ ███████ ██ ███████  ██████  
    """
)

def curve_init():
    p = random_prime(2^256)
    F.<i> = GF(p^2, modulus = x**2 + 1)
    R.<t> = PolynomialRing(GF(p))
    guess = ''.join(random.choices(ascii_letters, k=20))
    RR = RealField(256)
    num = RR(int(guess.encode().hex(),16))
    j = F(str(sqrt(num)).split('.')[1])
    E = EllipticCurve(j=j)
    P = E(0).division_points(3)
    P.remove(E(0))
    phi = E.isogeny(random.choice(P))
    E2 = phi.codomain()
    j2 = E2.j_invariant()
    assert list(R(j2))[1] != 0
    return E2, p, guess

def leak(E, p):
    F = Zmod(p^2)
    R.<t> = PolynomialRing(GF(p))
    r = random.getrandbits(20)
    x = F(input("your magic number?n$ "))^r - 1
    j_ = E.j_invariant()^x
    print(list(R(j_))[0])

def main():
    signal.alarm(120)
    banner()
    para = None
    print("Curve Initialization...")
    while not para:
        try:
            para = curve_init()
        except:
            continue
    E, p, guess = para
    print(f"einfo: {E.base_ring()}")
    leak(E, p)
    if input("guess > ").strip('n') == guess:
        print(f"Congratz, your flag: {FLAG}")
    else:
        print("Game over!")
        

if __name__ == "__main__":
    try:
        main()
    except:
        print("error!")

椭圆曲线的同源,仍然在知识盲区,这个还得去学一下才能做,放入 todo list 先 ~



原文始发于微信公众号(Van1sh):2023 N1CTF

版权声明:admin 发表于 2024年2月26日 下午3:05。
转载请注明:2023 N1CTF | CTF导航

相关文章