def test_double_mult(): H = ( 0x50929B74C1A04954B78B4B6035E97A5E078A5A0F28EC96D547BFEE9ACE803AC0, 0x31D3C6863973926E049E637CB1B5F40A36DAC28AF1766968C30C2313F3A38904, ) G = secp256k1.G # 0*G + 1*H T = double_mult(1, H, 0, G) assert T == H # 0*G + 2*H T = double_mult(2, H, 0, G) assert T == mult(2, H) # 0*G + 3*H T = double_mult(3, H, 0, G) assert T == mult(3, H) # 1*G + 0*H T = double_mult(0, H, 1, G) assert T == G # 2*G + 0*H T = double_mult(0, H, 2, G) assert T == mult(2, G) # 3*G + 0*H T = double_mult(0, H, 3, G) assert T == mult(3, G) # 0*G + 5*H T = double_mult(5, H, 0, G) assert T == mult(5, H) # 0*G - 5*H T = double_mult(-5, H, 0, G) assert T == mult(-5, H) # 1*G - 5*H U = double_mult(-5, H, 1, G) assert U == secp256k1.add(G, T)
def ckd(xparentkey: octets, index: Union[octets, int]) -> bytes: """Child Key Derivation (CDK) Key derivation is normal if the extended parent key is public or child_index is less than 0x80000000. Key derivation is hardened if the extended parent key is private and child_index is not less than 0x80000000. """ if isinstance(index, int): index = index.to_bytes(4, 'big') elif isinstance(index, str): # hex string index = bytes.fromhex(index) if len(index) != 4: raise ValueError(f"a 4 bytes int is required, not {len(index)}") xparent = base58.decode_check(xparentkey, 78) version = xparent[:4] # serialization data xkey = version # version xkey += (xparent[4] + 1).to_bytes(1, 'big') # (increased) depth if (version in PUB): if xparent[45] not in (2, 3): # not a compressed public key raise ValueError("version/key mismatch in extended parent key") Parent_bytes = xparent[45:] Parent = point_from_octets(ec, Parent_bytes) xkey += _h160(Parent_bytes)[:4] # parent pubkey fingerprint if index[0] >= 0x80: raise ValueError("no private/hardened derivation from pubkey") xkey += index # child index parent_chain_code = xparent[13:45] # normal derivation # actual extended key (key + chain code) derivation h = HMAC(parent_chain_code, Parent_bytes + index, sha512).digest() offset = int.from_bytes(h[:32], 'big') Offset = mult(ec, offset, ec.G) Child = ec.add(Parent, Offset) Child_bytes = octets_from_point(ec, Child, True) xkey += h[32:] # chain code xkey += Child_bytes # public key elif (version in PRV): if xparent[45] != 0: # not a private key raise ValueError("version/key mismatch in extended parent key") parent = int.from_bytes(xparent[46:], 'big') Parent = mult(ec, parent, ec.G) Parent_bytes = octets_from_point(ec, Parent, True) xkey += _h160(Parent_bytes)[:4] # parent pubkey fingerprint xkey += index # child index # actual extended key (key + chain code) derivation parent_chain_code = xparent[13:45] if (index[0] < 0x80): # normal derivation h = HMAC(parent_chain_code, Parent_bytes + index, sha512).digest() else: # hardened derivation h = HMAC(parent_chain_code, xparent[45:] + index, sha512).digest() offset = int.from_bytes(h[:32], 'big') child = (parent + offset) % ec.n child_bytes = b'\x00' + child.to_bytes(32, 'big') xkey += h[32:] # chain code xkey += child_bytes # private key else: raise ValueError("invalid extended key version") return base58.encode_check(xkey)
def test_mult_double_mult(): H = second_generator(secp256k1) G = secp256k1.G # 0*G + 1*H T = double_mult(1, H, 0, G) assert T == H T = multi_mult([1, 0], [H, G]) assert T == H # 0*G + 2*H exp = mult(2, H) T = double_mult(2, H, 0, G) assert T == exp T = multi_mult([2, 0], [H, G]) assert T == exp # 0*G + 3*H exp = mult(3, H) T = double_mult(3, H, 0, G) assert T == exp T = multi_mult([3, 0], [H, G]) assert T == exp # 1*G + 0*H T = double_mult(0, H, 1, G) assert T == G T = multi_mult([0, 1], [H, G]) assert T == G # 2*G + 0*H exp = mult(2, G) T = double_mult(0, H, 2, G) assert T == exp T = multi_mult([0, 2], [H, G]) assert T == exp # 3*G + 0*H exp = mult(3, G) T = double_mult(0, H, 3, G) assert T == exp T = multi_mult([0, 3], [H, G]) assert T == exp # 0*G + 5*H exp = mult(5, H) T = double_mult(5, H, 0, G) assert T == exp T = multi_mult([5, 0], [H, G]) assert T == exp # 0*G - 5*H exp = mult(-5, H) T = double_mult(-5, H, 0, G) assert T == exp T = multi_mult([-5, 0], [H, G]) assert T == exp # 1*G - 5*H exp = secp256k1.add(G, T) T = double_mult(-5, H, 1, G) assert T == exp
import random import time from btclib.curvemult import double_mult, mult from btclib.curves import secp256k1 as ec random.seed(42) # setup k1 = [] k2 = [] Q = [] for _ in range(50): k1.append(random.getrandbits(ec.nlen) % ec.n) k2.append(random.getrandbits(ec.nlen) % ec.n) q = random.getrandbits(ec.nlen) % ec.n Q.append(mult(q, ec.G)) start = time.time() for i in range(len(Q)): ec.add(mult(k1[i], ec.G), mult(k2[i], Q[i])) elapsed1 = time.time() - start start = time.time() for i in range(len(Q)): double_mult(k1[i], ec.G, k2[i], Q[i]) elapsed2 = time.time() - start print(elapsed2 / elapsed1)
mprvkey = random.getrandbits(ec.nlen) % ec.n print('\nmaster private key:', hex(mprvkey)) # Master Pubkey: mpubkey = mult(mprvkey, ec.G) print('Master Public Key:', hex(mpubkey[0])) print(' ', hex(mpubkey[1])) # public random number r = random.getrandbits(ec.nlen) print('\npublic ephemeral key:', format(r, '#064x')) q = [] hint = [] rbytes = r.to_bytes(ec.nsize, 'big') nKeys = 3 for i in range(nKeys): ibytes = i.to_bytes(ec.nsize, 'big') hd = hf(ibytes + rbytes).digest() hint.append(int_from_bits(hd)) q.append((mprvkey + hint[i]) % ec.n) Q = mult(q[i], ec.G) print('\nprvkey#', i, ':', hex(q[i])) print('Pubkey#', i, ':', hex(Q[0])) print(' ', hex(Q[1])) # Pubkeys could be calculated without using prvkeys for i in range(nKeys): Q = ec.add(mpubkey, mult(hint[i], ec.G)) assert Q == mult(q[i], ec.G)
s = (c + r * q) * mod_inv(k, ec.n) % ec.n # if s == 0 (extremely unlikely for large ec.n) go back to a different k assert s != 0 print(" r:", hex(r)) print(" s:", hex(s)) print("*** Verify Signature") w = mod_inv(s, ec.n) u = (c * w) % ec.n v = (r * w) % ec.n assert u != 0 assert v != 0 U = mult(ec, u, ec.G) V = mult(ec, v, Q) x, y = ec.add(U, V) print(r == x % ec.n) print("\n*** Malleated Signature") sm = ec.n - s print(" r:", hex(r)) print(" sm:", hex(sm)) print("*** Malleated Signature Verification") w = mod_inv(sm, ec.n) u = c * w % ec.n v = r * w % ec.n assert u != 0 assert v != 0 U = mult(ec, u, ec.G) V = mult(ec, v, Q)
def test_musig(self): """testing 3-of-3 MuSig https://github.com/ElementsProject/secp256k1-zkp/blob/secp256k1-zkp/src/modules/musig/musig.md https://blockstream.com/2019/02/18/musig-a-new-multisignature-standard/ https://eprint.iacr.org/2018/068 https://blockstream.com/2018/01/23/musig-key-aggregation-schnorr-signatures.html https://medium.com/@snigirev.stepan/how-schnorr-signatures-may-improve-bitcoin-91655bcb4744 """ mhd = hf(b'message to sign').digest() # the signers private and public keys, # including both the curve Point and the BIP340-Schnorr public key q1, x_Q1 = ssa.gen_keys() x_Q1 = x_Q1.to_bytes(ec.psize, 'big') q2, x_Q2 = ssa.gen_keys() x_Q2 = x_Q2.to_bytes(ec.psize, 'big') q3, x_Q3 = ssa.gen_keys() x_Q3 = x_Q3.to_bytes(ec.psize, 'big') # (non interactive) key setup # this is MuSig core: the rest is just Schnorr signature additivity # 1. lexicographic sorting of public keys keys: List[bytes] = list() keys.append(x_Q1) keys.append(x_Q2) keys.append(x_Q3) keys.sort() # 2. coefficients prefix = b''.join(keys) a1 = int_from_bits(hf(prefix + x_Q1).digest(), ec.nlen) % ec.n a2 = int_from_bits(hf(prefix + x_Q2).digest(), ec.nlen) % ec.n a3 = int_from_bits(hf(prefix + x_Q3).digest(), ec.nlen) % ec.n # 3. aggregated public key Q1 = mult(q1) Q2 = mult(q2) Q3 = mult(q3) Q = ec.add(double_mult(a1, Q1, a2, Q2), mult(a3, Q3)) if not ec.has_square_y(Q): # print("Q has been negated") a1 = ec.n - a1 # pragma: no cover a2 = ec.n - a2 # pragma: no cover a3 = ec.n - a3 # pragma: no cover # ready to sign: nonces and nonce commitments k1, _ = ssa.gen_keys() K1 = mult(k1) k2, _ = ssa.gen_keys() K2 = mult(k2) k3, _ = ssa.gen_keys() K3 = mult(k3) # exchange {K_i} (interactive) # computes s_i (non interactive) # WARNING: signers must exchange the nonces commitments {K_i} # before sharing {s_i} # same for all signers K = ec.add(ec.add(K1, K2), K3) if not ec.has_square_y(K): k1 = ec.n - k1 # pragma: no cover k2 = ec.n - k2 # pragma: no cover k3 = ec.n - k3 # pragma: no cover r = K[0] e = ssa._challenge(r, Q[0], mhd, ec, hf) s1 = (k1 + e * a1 * q1) % ec.n s2 = (k2 + e * a2 * q2) % ec.n s3 = (k3 + e * a3 * q3) % ec.n # exchange s_i (interactive) # finalize signature (non interactive) s = (s1 + s2 + s3) % ec.n sig = r, s # check signature is valid ssa._verify(mhd, Q, sig, ec, hf) self.assertTrue(ssa.verify(mhd, Q, sig))
def test_threshold(self): """testing 2-of-3 threshold signature (Pedersen secret sharing)""" # parameters m = 2 H = second_generator(ec, hf) mhd = hf(b'message to sign').digest() # FIRST PHASE: key pair generation ################################### # 1.1 signer one acting as the dealer commits1: List[Point] = list() q1, _ = ssa.gen_keys() q1_prime, _ = ssa.gen_keys() commits1.append(double_mult(q1_prime, H, q1, ec.G)) # sharing polynomials f1 = [q1] f1_prime = [q1_prime] for i in range(1, m): f1.append(ssa.gen_keys()[0]) f1_prime.append(ssa.gen_keys()[0]) commits1.append(double_mult(f1_prime[i], H, f1[i], ec.G)) # shares of the secret alpha12 = 0 # share of q1 belonging to signer two alpha12_prime = 0 alpha13 = 0 # share of q1 belonging to signer three alpha13_prime = 0 for i in range(m): alpha12 += (f1[i] * pow(2, i)) % ec.n alpha12_prime += (f1_prime[i] * pow(2, i)) % ec.n alpha13 += (f1[i] * pow(3, i)) % ec.n alpha13_prime += (f1_prime[i] * pow(3, i)) % ec.n # signer two verifies consistency of his share RHS = INF for i in range(m): RHS = ec.add(RHS, mult(pow(2, i), commits1[i])) t = double_mult(alpha12_prime, H, alpha12, ec.G) assert t == RHS, 'signer one is cheating' # signer three verifies consistency of his share RHS = INF for i in range(m): RHS = ec.add(RHS, mult(pow(3, i), commits1[i])) t = double_mult(alpha13_prime, H, alpha13, ec.G) assert t == RHS, 'signer one is cheating' # 1.2 signer two acting as the dealer commits2: List[Point] = list() q2, _ = ssa.gen_keys() q2_prime, _ = ssa.gen_keys() commits2.append(double_mult(q2_prime, H, q2, ec.G)) # sharing polynomials f2 = [q2] f2_prime = [q2_prime] for i in range(1, m): f2.append(ssa.gen_keys()[0]) f2_prime.append(ssa.gen_keys()[0]) commits2.append(double_mult(f2_prime[i], H, f2[i], ec.G)) # shares of the secret alpha21 = 0 # share of q2 belonging to signer one alpha21_prime = 0 alpha23 = 0 # share of q2 belonging to signer three alpha23_prime = 0 for i in range(m): alpha21 += (f2[i] * pow(1, i)) % ec.n alpha21_prime += (f2_prime[i] * pow(1, i)) % ec.n alpha23 += (f2[i] * pow(3, i)) % ec.n alpha23_prime += (f2_prime[i] * pow(3, i)) % ec.n # signer one verifies consistency of his share RHS = INF for i in range(m): RHS = ec.add(RHS, mult(pow(1, i), commits2[i])) t = double_mult(alpha21_prime, H, alpha21, ec.G) assert t == RHS, 'signer two is cheating' # signer three verifies consistency of his share RHS = INF for i in range(m): RHS = ec.add(RHS, mult(pow(3, i), commits2[i])) t = double_mult(alpha23_prime, H, alpha23, ec.G) assert t == RHS, 'signer two is cheating' # 1.3 signer three acting as the dealer commits3: List[Point] = list() q3, _ = ssa.gen_keys() q3_prime, _ = ssa.gen_keys() commits3.append(double_mult(q3_prime, H, q3, ec.G)) # sharing polynomials f3 = [q3] f3_prime = [q3_prime] for i in range(1, m): f3.append(ssa.gen_keys()[0]) f3_prime.append(ssa.gen_keys()[0]) commits3.append(double_mult(f3_prime[i], H, f3[i], ec.G)) # shares of the secret alpha31 = 0 # share of q3 belonging to signer one alpha31_prime = 0 alpha32 = 0 # share of q3 belonging to signer two alpha32_prime = 0 for i in range(m): alpha31 += (f3[i] * pow(1, i)) % ec.n alpha31_prime += (f3_prime[i] * pow(1, i)) % ec.n alpha32 += (f3[i] * pow(2, i)) % ec.n alpha32_prime += (f3_prime[i] * pow(2, i)) % ec.n # signer one verifies consistency of his share RHS = INF for i in range(m): RHS = ec.add(RHS, mult(pow(1, i), commits3[i])) t = double_mult(alpha31_prime, H, alpha31, ec.G) assert t == RHS, 'signer three is cheating' # signer two verifies consistency of his share RHS = INF for i in range(m): RHS = ec.add(RHS, mult(pow(2, i), commits3[i])) t = double_mult(alpha32_prime, H, alpha32, ec.G) assert t == RHS, 'signer three is cheating' # shares of the secret key q = q1 + q2 + q3 alpha1 = (alpha21 + alpha31) % ec.n alpha2 = (alpha12 + alpha32) % ec.n alpha3 = (alpha13 + alpha23) % ec.n for i in range(m): alpha1 += (f1[i] * pow(1, i)) % ec.n alpha2 += (f2[i] * pow(2, i)) % ec.n alpha3 += (f3[i] * pow(3, i)) % ec.n # 1.4 it's time to recover the public key # each participant i = 1, 2, 3 shares Qi as follows # Q = Q1 + Q2 + Q3 = (q1 + q2 + q3) G A1: List[Point] = list() A2: List[Point] = list() A3: List[Point] = list() for i in range(m): A1.append(mult(f1[i])) A2.append(mult(f2[i])) A3.append(mult(f3[i])) # signer one checks others' values RHS2 = INF RHS3 = INF for i in range(m): RHS2 = ec.add(RHS2, mult(pow(1, i), A2[i])) RHS3 = ec.add(RHS3, mult(pow(1, i), A3[i])) assert mult(alpha21) == RHS2, 'signer two is cheating' assert mult(alpha31) == RHS3, 'signer three is cheating' # signer two checks others' values RHS1 = INF RHS3 = INF for i in range(m): RHS1 = ec.add(RHS1, mult(pow(2, i), A1[i])) RHS3 = ec.add(RHS3, mult(pow(2, i), A3[i])) assert mult(alpha12) == RHS1, 'signer one is cheating' assert mult(alpha32) == RHS3, 'signer three is cheating' # signer three checks others' values RHS1 = INF RHS2 = INF for i in range(m): RHS1 = ec.add(RHS1, mult(pow(3, i), A1[i])) RHS2 = ec.add(RHS2, mult(pow(3, i), A2[i])) assert mult(alpha13) == RHS1, 'signer one is cheating' assert mult(alpha23) == RHS2, 'signer two is cheating' # commitment at the global sharing polynomial A: List[Point] = list() for i in range(m): A.append(ec.add(A1[i], ec.add(A2[i], A3[i]))) # aggregated public key Q = A[0] if not ec.has_square_y(Q): # print('Q has been negated') A[1] = ec.negate(A[1]) # pragma: no cover alpha1 = ec.n - alpha1 # pragma: no cover alpha2 = ec.n - alpha2 # pragma: no cover alpha3 = ec.n - alpha3 # pragma: no cover Q = ec.negate(Q) # pragma: no cover # SECOND PHASE: generation of the nonces' pair ###################### # Assume signer one and three want to sign # 2.1 signer one acting as the dealer commits1: List[Point] = list() k1 = ssa.k(mhd, q1, ec, hf) k1_prime = ssa.k(mhd, q1_prime, ec, hf) commits1.append(double_mult(k1_prime, H, k1, ec.G)) # sharing polynomials f1 = [k1] f1_prime = [k1_prime] for i in range(1, m): f1.append(ssa.gen_keys()[0]) f1_prime.append(ssa.gen_keys()[0]) commits1.append(double_mult(f1_prime[i], H, f1[i], ec.G)) # shares of the secret beta13 = 0 # share of k1 belonging to signer three beta13_prime = 0 for i in range(m): beta13 += (f1[i] * pow(3, i)) % ec.n beta13_prime += (f1_prime[i] * pow(3, i)) % ec.n # signer three verifies consistency of his share RHS = INF for i in range(m): RHS = ec.add(RHS, mult(pow(3, i), commits1[i])) t = double_mult(beta13_prime, H, beta13, ec.G) assert t == RHS, 'signer one is cheating' # 2.2 signer three acting as the dealer commits3: List[Point] = list() k3 = ssa.k(mhd, q3, ec, hf) k3_prime = ssa.k(mhd, q3_prime, ec, hf) commits3.append(double_mult(k3_prime, H, k3, ec.G)) # sharing polynomials f3 = [k3] f3_prime = [k3_prime] for i in range(1, m): f3.append(ssa.gen_keys()[0]) f3_prime.append(ssa.gen_keys()[0]) commits3.append(double_mult(f3_prime[i], H, f3[i], ec.G)) # shares of the secret beta31 = 0 # share of k3 belonging to signer one beta31_prime = 0 for i in range(m): beta31 += (f3[i] * pow(1, i)) % ec.n beta31_prime += (f3_prime[i] * pow(1, i)) % ec.n # signer one verifies consistency of his share RHS = INF for i in range(m): RHS = ec.add(RHS, mult(pow(1, i), commits3[i])) t = double_mult(beta31_prime, H, beta31, ec.G) assert t == RHS, 'signer three is cheating' # 2.3 shares of the secret nonce beta1 = beta31 % ec.n beta3 = beta13 % ec.n for i in range(m): beta1 += (f1[i] * pow(1, i)) % ec.n beta3 += (f3[i] * pow(3, i)) % ec.n # 2.4 it's time to recover the public nonce # each participant i = 1, 3 shares Qi as follows B1: List[Point] = list() B3: List[Point] = list() for i in range(m): B1.append(mult(f1[i])) B3.append(mult(f3[i])) # signer one checks values from signer three RHS3 = INF for i in range(m): RHS3 = ec.add(RHS3, mult(pow(1, i), B3[i])) assert mult(beta31) == RHS3, 'signer three is cheating' # signer three checks values from signer one RHS1 = INF for i in range(m): RHS1 = ec.add(RHS1, mult(pow(3, i), B1[i])) assert mult(beta13) == RHS1, 'signer one is cheating' # commitment at the global sharing polynomial B: List[Point] = list() for i in range(m): B.append(ec.add(B1[i], B3[i])) # aggregated public nonce K = B[0] if not ec.has_square_y(K): # print('K has been negated') B[1] = ec.negate(B[1]) # pragma: no cover beta1 = ec.n - beta1 # pragma: no cover beta3 = ec.n - beta3 # pragma: no cover K = ec.negate(K) # pragma: no cover # PHASE THREE: signature generation ### # partial signatures e = ssa._challenge(K[0], Q[0], mhd, ec, hf) gamma1 = (beta1 + e * alpha1) % ec.n gamma3 = (beta3 + e * alpha3) % ec.n # each participant verifies the other partial signatures # signer one RHS3 = ec.add(K, mult(e, Q)) for i in range(1, m): temp = double_mult(pow(3, i), B[i], e * pow(3, i), A[i]) RHS3 = ec.add(RHS3, temp) assert mult(gamma3) == RHS3, 'signer three is cheating' # signer three RHS1 = ec.add(K, mult(e, Q)) for i in range(1, m): temp = double_mult(pow(1, i), B[i], e * pow(1, i), A[i]) RHS1 = ec.add(RHS1, temp) assert mult(gamma1) == RHS1, 'signer one is cheating' # PHASE FOUR: aggregating the signature ### omega1 = 3 * mod_inv(3 - 1, ec.n) % ec.n omega3 = 1 * mod_inv(1 - 3, ec.n) % ec.n sigma = (gamma1 * omega1 + gamma3 * omega3) % ec.n sig = K[0], sigma self.assertTrue(ssa.verify(mhd, Q, sig)) # ADDITIONAL PHASE: reconstruction of the private key ### secret = (omega1 * alpha1 + omega3 * alpha3) % ec.n self.assertIn((q1 + q2 + q3) % ec.n, (secret, ec.n - secret))
def test_musig(self): """testing 3-of-3 MuSig https://github.com/ElementsProject/secp256k1-zkp/blob/secp256k1-zkp/src/modules/musig/musig.md https://blockstream.com/2019/02/18/musig-a-new-multisignature-standard/ https://eprint.iacr.org/2018/068 https://blockstream.com/2018/01/23/musig-key-aggregation-schnorr-signatures.html https://medium.com/@snigirev.stepan/how-schnorr-signatures-may-improve-bitcoin-91655bcb4744 """ mhd = hf(b'message to sign').digest() # the signers private and public keys, # including both the curve Point and the BIP340-Schnorr public key q1 = 0x010101 Q1 = mult(q1) Q1_x = Q1[0].to_bytes(ec.psize, 'big') q2 = 0x020202 Q2 = mult(q2) Q2_x = Q2[0].to_bytes(ec.psize, 'big') q3 = 0x030303 Q3 = mult(q3) Q3_x = Q3[0].to_bytes(ec.psize, 'big') # ready to sign: nonces and nonce commitments k1 = 1 + secrets.randbelow(ec.n - 1) K1 = mult(k1) k2 = 1 + secrets.randbelow(ec.n - 1) K2 = mult(k2) k3 = 1 + secrets.randbelow(ec.n - 1) K3 = mult(k3) #### (non interactive) key setup # this is MuSig core: the rest is just Schnorr signature additivity # 1. lexicographic sorting of public keys keys: List[bytes] = list() keys.append(Q1_x) keys.append(Q2_x) keys.append(Q3_x) keys.sort() # 2. coefficients prefix = b''.join(keys) a1 = int_from_bits(hf(prefix + Q1_x).digest(), ec.nlen) % ec.n a2 = int_from_bits(hf(prefix + Q2_x).digest(), ec.nlen) % ec.n a3 = int_from_bits(hf(prefix + Q3_x).digest(), ec.nlen) % ec.n # 3. aggregated public key Q = ec.add(double_mult(a1, Q1, a2, Q2), mult(a3, Q3)) #### exchange {K_i} (interactive) #### computes s_i (non interactive) # WARNING: # the signers must exchange the nonces # commitments {K_i} before sharing {s_i} # same for all signers K = ec.add(ec.add(K1, K2), K3) r = K[0] e = ssa._challenge(r, Q[0], mhd, ec, hf) # first signer if not ec.has_square_y(K): k1 = ec.n - k1 s1 = (k1 + e * a1 * q1) % ec.n # second signer if not ec.has_square_y(K): k2 = ec.n - k2 s2 = (k2 + e * a2 * q2) % ec.n # third signer if not ec.has_square_y(K): k3 = ec.n - k3 s3 = (k3 + e * a3 * q3) % ec.n #### exchange s_i (interactive) #### finalize signature (non interactive) s = (s1 + s2 + s3) % ec.n sig = r, s # check signature is valid self.assertTrue(ssa.verify(mhd, Q, sig))
def test_threshold(self): """testing 2-of-3 threshold signature (Pedersen secret sharing)""" # parameters t = 2 H = second_generator(ec, hf) mhd = hf(b'message to sign').digest() ### FIRST PHASE: key pair generation ### # signer one acting as the dealer commits1: List[Point] = list() q1 = (1 + random.getrandbits(ec.nlen)) % ec.n q1_prime = (1 + random.getrandbits(ec.nlen)) % ec.n commits1.append(double_mult(q1_prime, H, q1)) # sharing polynomials f1: List[int] = list() f1.append(q1) f1_prime: List[int] = list() f1_prime.append(q1_prime) for i in range(1, t): temp = (1 + random.getrandbits(ec.nlen)) % ec.n f1.append(temp) temp = (1 + random.getrandbits(ec.nlen)) % ec.n f1_prime.append(temp) commits1.append(double_mult(f1_prime[i], H, f1[i])) # shares of the secret alpha12 = 0 # share of q1 belonging to P2 alpha12_prime = 0 alpha13 = 0 # share of q1 belonging to P3 alpha13_prime = 0 for i in range(t): alpha12 += (f1[i] * pow(2, i)) % ec.n alpha12_prime += (f1_prime[i] * pow(2, i)) % ec.n alpha13 += (f1[i] * pow(3, i)) % ec.n alpha13_prime += (f1_prime[i] * pow(3, i)) % ec.n # player two verifies consistency of his share RHS = INF for i in range(t): RHS = ec.add(RHS, mult(pow(2, i), commits1[i])) assert double_mult(alpha12_prime, H, alpha12) == RHS, 'player one is cheating' # player three verifies consistency of his share RHS = INF for i in range(t): RHS = ec.add(RHS, mult(pow(3, i), commits1[i])) assert double_mult(alpha13_prime, H, alpha13) == RHS, 'player one is cheating' # signer two acting as the dealer commits2: List[Point] = list() q2 = (1 + random.getrandbits(ec.nlen)) % ec.n q2_prime = (1 + random.getrandbits(ec.nlen)) % ec.n commits2.append(double_mult(q2_prime, H, q2)) # sharing polynomials f2: List[int] = list() f2.append(q2) f2_prime: List[int] = list() f2_prime.append(q2_prime) for i in range(1, t): temp = (1 + random.getrandbits(ec.nlen)) % ec.n f2.append(temp) temp = (1 + random.getrandbits(ec.nlen)) % ec.n f2_prime.append(temp) commits2.append(double_mult(f2_prime[i], H, f2[i])) # shares of the secret alpha21 = 0 # share of q2 belonging to P1 alpha21_prime = 0 alpha23 = 0 # share of q2 belonging to P3 alpha23_prime = 0 for i in range(t): alpha21 += (f2[i] * pow(1, i)) % ec.n alpha21_prime += (f2_prime[i] * pow(1, i)) % ec.n alpha23 += (f2[i] * pow(3, i)) % ec.n alpha23_prime += (f2_prime[i] * pow(3, i)) % ec.n # player one verifies consistency of his share RHS = INF for i in range(t): RHS = ec.add(RHS, mult(pow(1, i), commits2[i])) assert double_mult(alpha21_prime, H, alpha21) == RHS, 'player two is cheating' # player three verifies consistency of his share RHS = INF for i in range(t): RHS = ec.add(RHS, mult(pow(3, i), commits2[i])) assert double_mult(alpha23_prime, H, alpha23) == RHS, 'player two is cheating' # signer three acting as the dealer commits3: List[Point] = list() q3 = (1 + random.getrandbits(ec.nlen)) % ec.n q3_prime = (1 + random.getrandbits(ec.nlen)) % ec.n commits3.append(double_mult(q3_prime, H, q3)) # sharing polynomials f3: List[int] = list() f3.append(q3) f3_prime: List[int] = list() f3_prime.append(q3_prime) for i in range(1, t): temp = (1 + random.getrandbits(ec.nlen)) % ec.n f3.append(temp) temp = (1 + random.getrandbits(ec.nlen)) % ec.n f3_prime.append(temp) commits3.append(double_mult(f3_prime[i], H, f3[i])) # shares of the secret alpha31 = 0 # share of q3 belonging to P1 alpha31_prime = 0 alpha32 = 0 # share of q3 belonging to P2 alpha32_prime = 0 for i in range(t): alpha31 += (f3[i] * pow(1, i)) % ec.n alpha31_prime += (f3_prime[i] * pow(1, i)) % ec.n alpha32 += (f3[i] * pow(2, i)) % ec.n alpha32_prime += (f3_prime[i] * pow(2, i)) % ec.n # player one verifies consistency of his share RHS = INF for i in range(t): RHS = ec.add(RHS, mult(pow(1, i), commits3[i])) assert double_mult(alpha31_prime, H, alpha31) == RHS, 'player three is cheating' # player two verifies consistency of his share RHS = INF for i in range(t): RHS = ec.add(RHS, mult(pow(2, i), commits3[i])) assert double_mult(alpha32_prime, H, alpha32) == RHS, 'player two is cheating' # shares of the secret key q = q1 + q2 + q3 alpha1 = (alpha21 + alpha31) % ec.n alpha2 = (alpha12 + alpha32) % ec.n alpha3 = (alpha13 + alpha23) % ec.n for i in range(t): alpha1 += (f1[i] * pow(1, i)) % ec.n alpha2 += (f2[i] * pow(2, i)) % ec.n alpha3 += (f3[i] * pow(3, i)) % ec.n # it's time to recover the public key Q = Q1 + Q2 + Q3 = (q1 + q2 + q3)G A1: List[Point] = list() A2: List[Point] = list() A3: List[Point] = list() # each participant i = 1, 2, 3 shares Qi as follows # he broadcasts these values for i in range(t): A1.append(mult(f1[i])) A2.append(mult(f2[i])) A3.append(mult(f3[i])) # he checks the others' values # player one RHS2 = INF RHS3 = INF for i in range(t): RHS2 = ec.add(RHS2, mult(pow(1, i), A2[i])) RHS3 = ec.add(RHS3, mult(pow(1, i), A3[i])) assert mult(alpha21) == RHS2, 'player two is cheating' assert mult(alpha31) == RHS3, 'player three is cheating' # player two RHS1 = INF RHS3 = INF for i in range(t): RHS1 = ec.add(RHS1, mult(pow(2, i), A1[i])) RHS3 = ec.add(RHS3, mult(pow(2, i), A3[i])) assert mult(alpha12) == RHS1, 'player one is cheating' assert mult(alpha32) == RHS3, 'player three is cheating' # player three RHS1 = INF RHS2 = INF for i in range(t): RHS1 = ec.add(RHS1, mult(pow(3, i), A1[i])) RHS2 = ec.add(RHS2, mult(pow(3, i), A2[i])) assert mult(alpha13) == RHS1, 'player one is cheating' assert mult(alpha23) == RHS2, 'player two is cheating' A: List[Point] = list() # commitment at the global sharing polynomial for i in range(t): A.append(ec.add(A1[i], ec.add(A2[i], A3[i]))) Q = A[0] # aggregated public key ### SECOND PHASE: generation of the nonces' pair ### # This phase follows exactly the key generation procedure # suppose that player one and three want to sign # signer one acting as the dealer commits1: List[Point] = list() k1 = (1 + random.getrandbits(ec.nlen)) % ec.n k1_prime = (1 + random.getrandbits(ec.nlen)) % ec.n commits1.append(double_mult(k1_prime, H, k1)) # sharing polynomials f1: List[int] = list() f1.append(k1) f1_prime: List[int] = list() f1_prime.append(k1_prime) for i in range(1, t): temp = (1 + random.getrandbits(ec.nlen)) % ec.n f1.append(temp) temp = (1 + random.getrandbits(ec.nlen)) % ec.n f1_prime.append(temp) commits1.append(double_mult(f1_prime[i], H, f1[i])) # shares of the secret beta13 = 0 # share of k1 belonging to P3 beta13_prime = 0 for i in range(t): beta13 += (f1[i] * pow(3, i)) % ec.n beta13_prime += (f1_prime[i] * pow(3, i)) % ec.n # player three verifies consistency of his share RHS = INF for i in range(t): RHS = ec.add(RHS, mult(pow(3, i), commits1[i])) assert double_mult(beta13_prime, H, beta13) == RHS, 'player one is cheating' # signer three acting as the dealer commits3: List[Point] = list() k3 = (1 + random.getrandbits(ec.nlen)) % ec.n k3_prime = (1 + random.getrandbits(ec.nlen)) % ec.n commits3.append(double_mult(k3_prime, H, k3)) # sharing polynomials f3: List[int] = list() f3.append(k3) f3_prime: List[int] = list() f3_prime.append(k3_prime) for i in range(1, t): temp = (1 + random.getrandbits(ec.nlen)) % ec.n f3.append(temp) temp = (1 + random.getrandbits(ec.nlen)) % ec.n f3_prime.append(temp) commits3.append(double_mult(f3_prime[i], H, f3[i])) # shares of the secret beta31 = 0 # share of k3 belonging to P1 beta31_prime = 0 for i in range(t): beta31 += (f3[i] * pow(1, i)) % ec.n beta31_prime += (f3_prime[i] * pow(1, i)) % ec.n # player one verifies consistency of his share RHS = INF for i in range(t): RHS = ec.add(RHS, mult(pow(1, i), commits3[i])) assert double_mult(beta31_prime, H, beta31) == RHS, 'player three is cheating' # shares of the secret nonce beta1 = beta31 % ec.n beta3 = beta13 % ec.n for i in range(t): beta1 += (f1[i] * pow(1, i)) % ec.n beta3 += (f3[i] * pow(3, i)) % ec.n # it's time to recover the public nonce B1: List[Point] = list() B3: List[Point] = list() # each participant i = 1, 3 shares Qi as follows # he broadcasts these values for i in range(t): B1.append(mult(f1[i])) B3.append(mult(f3[i])) # he checks the others' values # player one RHS3 = INF for i in range(t): RHS3 = ec.add(RHS3, mult(pow(1, i), B3[i])) assert mult(beta31) == RHS3, 'player three is cheating' # player three RHS1 = INF for i in range(t): RHS1 = ec.add(RHS1, mult(pow(3, i), B1[i])) assert mult(beta13) == RHS1, 'player one is cheating' B: List[Point] = list() # commitment at the global sharing polynomial for i in range(t): B.append(ec.add(B1[i], B3[i])) K = B[0] # aggregated public nonce if not ec.has_square_y(K): beta1 = ec.n - beta1 beta3 = ec.n - beta3 ### PHASE THREE: signature generation ### # partial signatures e = ssa._challenge(K[0], Q[0], mhd, ec, hf) gamma1 = (beta1 + e * alpha1) % ec.n gamma3 = (beta3 + e * alpha3) % ec.n # each participant verifies the other partial signatures # player one if ec.has_square_y(K): RHS3 = ec.add(K, mult(e, Q)) for i in range(1, t): temp = double_mult(pow(3, i), B[i], e * pow(3, i), A[i]) RHS3 = ec.add(RHS3, temp) else: RHS3 = ec.add(ec.opposite(K), mult(e, Q)) for i in range(1, t): temp = double_mult(pow(3, i), ec.opposite(B[i]), e * pow(3, i), A[i]) RHS3 = ec.add(RHS3, temp) assert mult(gamma3) == RHS3, 'player three is cheating' # player three if ec.has_square_y(K): RHS1 = ec.add(K, mult(e, Q)) for i in range(1, t): temp = double_mult(pow(1, i), B[i], e * pow(1, i), A[i]) RHS1 = ec.add(RHS1, temp) else: RHS1 = ec.add(ec.opposite(K), mult(e, Q)) for i in range(1, t): temp = double_mult(pow(1, i), ec.opposite(B[i]), e * pow(1, i), A[i]) RHS1 = ec.add(RHS1, temp) assert mult(gamma1) == RHS1, 'player two is cheating' ### PHASE FOUR: aggregating the signature ### omega1 = 3 * mod_inv(3 - 1, ec.n) % ec.n omega3 = 1 * mod_inv(1 - 3, ec.n) % ec.n sigma = (gamma1 * omega1 + gamma3 * omega3) % ec.n sig = K[0], sigma self.assertTrue(ssa.verify(mhd, Q, sig)) ### ADDITIONAL PHASE: reconstruction of the private key ### secret = (omega1 * alpha1 + omega3 * alpha3) % ec.n self.assertEqual((q1 + q2 + q3) % ec.n, secret)
k1 = int.from_bytes(k_bytes, 'big') % ec.n assert k1 != 0 K1 = mult(k1, ec.G) s1 = (k1 - h1 * q) % ec.n # if s1 == 0 (extremely unlikely for large ec.n) go back to a different ephemeral key assert s1 != 0 print(" r:", hex(K1[0])) print(" s1:", hex(s1)) print("*** Signature Verification") minush1 = -h1 % ec.n V = mult(minush1, Q) V = ec.add(K1, V) print(V == mult(s1, ec.G)) print("\n*** Another message") msg2 = "and Paolo is right to be afraid" print(msg2) print("*** Hash of the message") h_bytes = hf(msg2.encode()).digest() # hash(msg) must be transformed into an integer modulo ec.n: h2 = int.from_bytes(h_bytes, 'big') % ec.n assert h2 != 0 print(" h2:", hex(h2)) print("\n*** Signature") k2 = k1 #very bad! Never reuse the same ephemeral key!!!