1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139
| from sage.all import GF from pwn import process, remote from fastecdsa import keys, point import ast, random from tss2 import hash_transcript, verify, curve, TARGET from tqdm import tqdm from concurrent.futures import ThreadPoolExecutor
sk2, pk2 = keys.gen_keypair(curve)
def connect(i): io = remote("challs.actf.co", 31302) return io
def sign(sk, pk, msg): k, R = keys.gen_keypair(curve) c = hash_transcript(pk, R, msg) s = (k - c * sk) % curve.q return c, s
def establish(io, sk2, pk2): io.recvuntil(b"my public key: ") pk_tpl = ast.literal_eval(io.recvline().strip().decode()) pk1 = point.Point(*pk_tpl, curve=curve) io.sendlineafter(b"x: ", str(pk2.x).encode()) io.sendlineafter(b"y: ", str(pk2.y).encode()) c, s = sign(sk2, pk2, b"foo") io.sendlineafter(b"c: ", str(c).encode()) io.sendlineafter(b"s: ", str(s).encode()) apk = pk1 + pk2 return apk
def do_sign_get_nonce_R1(io, msg): io.sendlineafter(b"sign?", msg.hex().encode()) io.recvuntil(b"my nonce: ") R1_tpl = ast.literal_eval(io.recvline().strip().decode()) R1 = point.Point(*R1_tpl, curve=curve) return R1
def send_nonce_R2_get_s(io, R2): io.sendlineafter(b"x: ", str(R2.x).encode()) io.sendlineafter(b"y: ", str(R2.y).encode())
io.recvuntil(b"my share of the signature: ") s1 = int(io.recvline().strip().decode()) return s1
Zp = GF(curve.q) ell = 256 messages = [f"message{i}".encode() for i in range(ell)]
with ThreadPoolExecutor(max_workers=32) as executor: ios = list(tqdm(executor.map(connect, range(ell)), desc="Init", total=ell)) apks = list( tqdm( executor.map(establish, ios, [sk2] * ell, [pk2] * ell), desc="Establish", total=ell, ) ) apk = apks[0] R1 = list( tqdm( executor.map(do_sign_get_nonce_R1, ios, messages), desc="Get nonce", total=ell, ) ) print("done, forging signature...")
k2 = [[random.randrange(curve.q), random.randrange(curve.q)] for i in range(ell)] R2 = [[x * curve.G, y * curve.G] for x, y in k2]
c = [ [hash_transcript(apk, R1[i] + R2[i][b], messages[i]) for b in range(2)] for i in range(ell) ] g_func = lambda x, z=0: sum( [int(Zp(2) ** i / (c[i][1] - c[i][0])) * x[i] for i in range(ell)], z ) forged_R = g_func(R1, z=point.Point.IDENTITY_ELEMENT) forged_message = b"flag" forged_c = hash_transcript(apk, forged_R, forged_message) bits = [ int(b) for b in bin((forged_c - g_func([c[i][0] for i in range(ell)])) % curve.q)[ 2: ].rjust(256, "0") ][::-1] chosen_R2 = [R2[i][b] for (i, b) in enumerate(bits)]
with ThreadPoolExecutor(max_workers=32) as executor: s1 = list( tqdm( executor.map(send_nonce_R2_get_s, ios, chosen_R2), desc="Get signature share", total=ell, ) )
signatures = [ (int(c[i][bits[i]]), int(s1[i] + k2[i][bits[i]] - c[i][bits[i]] * sk2)) for i in range(ell) ] forged_signature = ( forged_c, (g_func(s1) - forged_c * sk2) % curve.q, )
print([verify(apk, messages[i], signatures[i]) for i in range(ell)]) print(verify(apk, forged_message, forged_signature))
c, s = forged_signature io = ios[0] io.sendlineafter(b"c: ", str(c).encode()) io.sendlineafter(b"s: ", str(s).encode()) io.interactive()
|