134 lines
3.6 KiB
Python
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))
|