justCTF 2020 | 25519

#justCTF_2020

#!/use/bin/env sage

from sys import exit
from hashlib import sha256


FLAG = open('./flag.txt').read()

ec = EllipticCurve(GF(2**255-19), [0, 486662, 0, 1, 0])
p = ec.order()
ZmodP = Zmod(p)
G = ec.lift_x(9)

ha = lambda x: x if isinstance(x, int) or isinstance(x, Integer) else product(x.xy())
hashs = lambda *x: int.from_bytes(sha256(b'.'.join([b'%X' % ha(x) for x in x])).digest(), 'little') % p


def hashp(x):
    x = hashs((x))
    while True:
        try:
            return ec.lift_x(x)
        except:
            x = hashs((x))


def keygen():
    x = randint(1, p-1)
    P = x * G
    return x, P


def verify(signature, P, m):
    I, e, s = signature
    return e == hashs(m, s*G + e*P, s*hashp(P) + e*I)


if __name__ == "__main__":
    x, P = keygen()
    m = randint(1, p-1)
    print(x, P, m)

    spent = set()
    for i in range(8):
        Ix = int(input('I (x): '))
        Iy = int(input('I (y): '))
        I = ec(Ix, Iy)
        e = int(input('e: '))
        s = int(input('s: '))
        if verify((I, e, s), P, m) and I not in spent:
            print('ok')
            spent.add(I)
        else:
            print('nope')
            exit(1)

    print(FLAG)

要するにEllipticCurve上の  x, P, mが与えられるので

 e = H_s(m, sG + eP, s*H_p(P) + eI)を満たす e, s, Iを探してこい、という問題

両辺に eがあって、かつハッシュ関数を経由しているため、一見不可能に見えるが、 s, Iをうまく使えばなんとかなる。 sG + eP s*H_p(P) + eIをそれぞれ適当な点 cGに一致させることを考える

まず sG + eP = (s + ex)G = cG で、 sG = (c -ex)Gより、 s=c-exという制約を作る

続いて

 s*H_p(P) + eI = cGについても適当に変形して eI = cG - s*H_p(P)より I = e^{-1}(cG - sH_p(P))

したがって、適当な cを作って e = H_s(m, cG, cG)と計算して、 s,  Iの順番で計算するだけで良い

from hashlib import sha256

ec = EllipticCurve(GF(2**255-19), [0, 486662, 0, 1, 0])
p = ec.order()
ZmodP = Zmod(p)
G = ec.lift_x(9)

ha = lambda x: x if isinstance(x, int) or isinstance(x, Integer) else product(x.xy())
hashs = lambda *x: int.from_bytes(sha256(b'.'.join([b'%X' % ha(x) for x in x])).digest(), 'little') % p


def hashp(x):
    x = hashs((x))
    while True:
        try:
            return ec.lift_x(x)
        except:
            x = hashs((x))

def keygen():
    x = randint(1, p-1)
    P = x * G
    return x, P

def verify(signature, P, m):
    I, e, s = signature
    return e == hashs(m, s*G + e*P, s*hashp(P) + e*I)

def solve(x, P, m):
    while True:
        try:
            c = randint(2, p-1)
            cG = c * G
            e = hashs(m, cG, cG)
            s = (c - e*x) % p
            I = inverse_mod(e, p) * (cG - s*hashp(P))
            break
        except ZeroDivisionError:
            pass
    return I, e, s

if __name__ == "__main__":
    x, P = keygen()
    m = randint(1, p-1)
    print(x, P, m)

    spent = set()
    for i in range(8):
        I, e, s = solve(x, P, m)
        if verify((I, e, s), P, m) and I not in spent:
            print('ok')
            spent.add(I)
        else:
            print('nope')
            exit(1)

    print("CLEAR!")

https://www.josephsurin.me/posts/2021-01-31-justctf-2020-crypto-writeups