Midnightsun CTF 2021 Quals | dbcsig

#midnightsunctf2021quals

from hashlib import sha256


def keygen(password):
    while True:
        p = 2 * random_prime(2 ^ 521) + 1
        if p.is_prime(proof=False):
            break
    base, h = 3, password
    for i in range(256):
        h = sha256(h).digest()
    x = int.from_bytes(h * 2, "big")
    return base, p, pow(base, x, p), x


def sign(message, priv):
    h = int(sha256(message).hexdigest(), 16)
    k = next_prime(int.from_bytes(
        sha256(message + priv.to_bytes(128, "big")).digest() + sha256(message).digest(),
        "big"
    ))
    r = int(pow(g, (k - 1) / 2, p))
    s = int(Zmod((p - 1) / 2)(-r * priv + h) / k)
    return r, s


g, p, pub, priv = keygen(b"[redacted]")
r, s = sign(b"blockchain-ready deterministic signatures", priv)


'''

----------------------------------------------------------------------------------------X8
                 
sage: p                                                                                                                                                                                                                                                                                                                                                               
403564885370838178925695432427367491470237155186244212153913898686763710896400971013343861778118177227348808022449550091155336980246939657874541422921996385839128510463
sage: pub                                                                                                                                                                                                                                                                                                                                                             
246412225456431779180824199385732957003440696667152337864522703662113001727131541828819072458270449510317065822513378769528087093456569455854781212817817126406744124198
sage: r                                                                                                                                                                                                                                                                                                                                                               
195569213557534062135883086442918136431967939088647809625293990874404630325238896363416607124844217333997865971186768485716700133773423095190740751263071126576205643521
sage: s                                                                                                                                                                                                                                                                                                                                                               
156909661984338007650026461825579179936003525790982707621071330974873615448305401425316804780001319386278769029432437834130771981383408535426433066382954348912235133967
sage: "midnight{" + str(priv) + "}" == flag                                                                                                                                                                                                                                                                                                                                                                                                                        
True
sage: time
CPU times: user 4 µs, sys: 0 ns, total: 4 µs
Wall time: 6.91 µs
Hammer time: yes

'''

https://furutsuki.hatenablog.com/entry/2021/04/11/092829#dbcsig_64434

DSA だが d = 2^{256}d' + d'だし、 k = k_{high} + h + \alpha

multivariate coppersmithする

# https://raw.githubusercontent.com/defund/coppersmith/master/coppersmith.sage
...

from hashlib import sha256

g = 3
p = 403564885370838178925695432427367491470237155186244212153913898686763710896400971013343861778118177227348808022449550091155336980246939657874541422921996385839128510463
pub = 246412225456431779180824199385732957003440696667152337864522703662113001727131541828819072458270449510317065822513378769528087093456569455854781212817817126406744124198
r = 195569213557534062135883086442918136431967939088647809625293990874404630325238896363416607124844217333997865971186768485716700133773423095190740751263071126576205643521
s = 156909661984338007650026461825579179936003525790982707621071330974873615448305401425316804780001319386278769029432437834130771981383408535426433066382954348912235133967
message = b"blockchain-ready deterministic signatures"
h = int(sha256(message).hexdigest(), 16)

q = (p-1) // 2
# assert q.is_prime ????????

PR.<d_, kh, a> = PolynomialRing(Zmod(q))

f = 2^256 * s * kh + s * (h + a) + r * (d_ + 2^256 * d_) - h
roots = small_roots(f, [2^256, 2^256, 1000], d=4, m=4)

for root in roots:
    dval = Integer(root[0])
    dval = dval * 2^256 + dval
    if pow(g, dval, p) == pub:
        print("[+] found: {}".format(dval))
        break
    else:
        print("[-] wrong: {}".format(dval))

Inequality_Solving_with_CVP のテクを使って、次のようにも解ける

import os
from hashlib import sha256

g = 3
# while True:  
#     p = 2*random_prime(2^521) + 1
#     if p.is_prime(proof=False):
#         break
p = 2208999820857629084844898049184204625514077638848903812266763331630270258760615385915643248593594110916852840723890149166178203356254570529299804284681970479
q = (p-1) // 2
d = int.from_bytes(sha256(os.urandom(16)).digest() * 2, "big")
pub = pow(g, d, p)

message = b"takoyakitabetai"
h = int(sha256(message).hexdigest(), 16)  
k = int.from_bytes(sha256(d.to_bytes(128, "big")).digest(), "big")*2^256 + h

r = int(pow(g,(k-1)//2,p))  
s = int((-r*d+h) * inverse_mod(k, q) % q)  

# (k_, d1, d2, t)
M = matrix(ZZ, [
    [2^256*s, 2^256*r, r,-q],
    [      1,       0, 0, 0],
    [      0,       1, 0, 0],
    [      0,       0, 1, 0],
    [      0,       1,-1, 0], #lb = 0, ub = 0
    [      0,       0, 0, 1],
])
c = -s*h + h
lb = [c,     0,     0,     0, 0,     0]
ub = [c, 2^256, 2^256, 2^256, 0,     q]

results, weights = solve(M.transpose(), lb, ub)
print([results[i] // weights[i] for i in range(len(results))])
print([d % 2^256, k >> 256])