writeups/2023/csawq/attack_blocky.sage

134 lines
3.6 KiB
Python

import nclib
import ecdsa
import hashlib
import binascii
from Crypto.Util.number import *
def _hex(x):
return binascii.hexlify(x).decode()
def _hash(msg):
return bytes_to_long(hashlib.sha1(msg).digest())
conn = nclib.Netcat(("crypto.csaw.io", 5002))
# conn = nclib.Netcat(("localhost", 1337))
N = 6
h = []
sgns = []
msgs = []
def get_messages(count):
all_msgs = []
conn.recv_until(b": ")
conn.send_line(b"2")
for i in range(count):
conn.recv_until(f"Block {i}\n".encode())
msg_line = conn.recv_line()
print(msg_line)
msg = binascii.unhexlify(msg_line.strip().split(b" ")[1])
sig_line = conn.recv_line()
sig_rs = sig_line.decode().split("(")[1].split(")")[0]
r, s = sig_rs.split(", ")
r = int(r)
s = int(s)
all_msgs.append((msg, r, s))
return all_msgs
for i in range(N):
msg = f"meow{i}".encode()
msgs.append(msg)
conn.recv_until(b": ")
conn.send_line(b"1")
conn.send_line(_hex(msg))
all_msgs = get_messages(N+1)
for i in range(N):
(_, r, s) = all_msgs[i+1]
prev_msg = all_msgs[i][0]
real_hash = _hash(prev_msg + msgs[i])
h.append(real_hash)
sgns.append((r,s))
order=115792089237316195423570985008687907852837564279074904382605163141518161494337
s_inv = []
s = []
r = []
for i in range(N):
s.append(sgns[i][1])
r.append(sgns[i][0])
s_inv.append(ecdsa.numbertheory.inverse_mod(s[i], order))
Z = GF(order)
R = PolynomialRing(Z, names=('dd',))
(dd,) = R._first_ngens(1)
# the polynomial we construct will have degree 1 + Sum_(i=1)^(i=N-3)i in dd
# our task here is to compute this polynomial in a constructive way starting from the N signatures in the given list order
# the generic formula will be given in terms of differences of nonces, i.e. k_ij = k_i - k_j where i and j are the signature indexes
# each k_ij is a first-degree polynomial in dd
# this function has the goal of returning it given i and j
def k_ij_poly(i, j):
hi = Z(h[i])
hj = Z(h[j])
s_invi = Z(s_inv[i])
s_invj = Z(s_inv[j])
ri = Z(r[i])
rj = Z(r[j])
poly = dd*(ri*s_invi - rj*s_invj) + hi*s_invi - hj*s_invj
return poly
# the idea is to compute the polynomial recursively from the given degree down to 0
# the algorithm is as follows:
# for 4 signatures the second degree polynomial is:
# k_12*k_12 - k_23*k_01
# so we can compute its coefficients.
# the polynomial for N signatures has degree 1 + Sum_(i=1)^(i=N-3)i and can be derived from the one for N-1 signatures
# let's define dpoly(i, j) recursively as the dpoly of degree i starting with index j
def dpoly(n, i, j):
if i == 0:
return (k_ij_poly(j+1, j+2))*(k_ij_poly(j+1, j+2)) - (k_ij_poly(j+2, j+3))*(k_ij_poly(j+0, j+1))
else:
left = dpoly(n, i-1, j)
for m in range(1,i+2):
left = left*(k_ij_poly(j+m, j+i+2))
right = dpoly(n, i-1, j+1)
for m in range(1,i+2):
right = right*(k_ij_poly(j, j+m))
return (left - right)
def print_dpoly(n, i, j):
if i == 0:
print('(k', j+1, j+2, '*k', j+1, j+2, '-k', j+2, j+3, '*k', j+0, j+1, ')', sep='', end='')
else:
print('(', sep='', end='')
print_dpoly(n, i-1, j)
for m in range(1,i+2):
print('*k', j+m, j+i+2, sep='', end='')
print('-', sep='', end='')
print_dpoly(n, i-1, j+1)
for m in range(1,i+2):
print('*k', j, j+m, sep='', end='')
print(')', sep='', end='')
print_dpoly(N-4, N-4, 0)
poly_target = dpoly(N-4, N-4, 0)
d_guesses = poly_target.roots()
print("results!!!!!\n\n\n")
print(d_guesses)
if len(d_guesses) == 1:
conn.recv_until(b": ")
conn.send_line(b"4")
res = str(d_guesses[0][0]).encode()
print("SENDING", res)
conn.send_line(res)
print(conn.recv_all(timeout=5))