diff options
Diffstat (limited to 'bip-0340/reference.py')
-rw-r--r-- | bip-0340/reference.py | 40 |
1 files changed, 30 insertions, 10 deletions
diff --git a/bip-0340/reference.py b/bip-0340/reference.py index f2a944f..79f9578 100644 --- a/bip-0340/reference.py +++ b/bip-0340/reference.py @@ -51,7 +51,10 @@ def bytes_from_int(x): def bytes_from_point(P): return bytes_from_int(x(P)) -def point_from_bytes(b): +def xor_bytes(b0, b1): + return bytes(x ^ y for (x, y) in zip(b0, b1)) + +def lift_x_square_y(b): x = int_from_bytes(b) if x >= p: return None @@ -61,6 +64,13 @@ def point_from_bytes(b): return None return [x, y] +def lift_x_even_y(b): + P = lift_x_square_y(b) + if P is None: + return None + else: + return [x(P), y(P) if y(P) % 2 == 0 else p - y(P)] + def int_from_bytes(b): return int.from_bytes(b, byteorder="big") @@ -73,6 +83,9 @@ def is_square(x): def has_square_y(P): return not is_infinity(P) and is_square(y(P)) +def has_even_y(P): + return y(P) % 2 == 0 + def pubkey_gen(seckey): x = int_from_bytes(seckey) if not (1 <= x <= n - 1): @@ -80,21 +93,27 @@ def pubkey_gen(seckey): P = point_mul(G, x) return bytes_from_point(P) -def schnorr_sign(msg, seckey0): +def schnorr_sign(msg, seckey0, aux_rand): if len(msg) != 32: raise ValueError('The message must be a 32-byte array.') seckey0 = int_from_bytes(seckey0) if not (1 <= seckey0 <= n - 1): raise ValueError('The secret key must be an integer in the range 1..n-1.') + if len(aux_rand) != 32: + raise ValueError('aux_rand must be 32 bytes instead of %i.' % len(aux_rand)) P = point_mul(G, seckey0) - seckey = seckey0 if has_square_y(P) else n - seckey0 - k0 = int_from_bytes(tagged_hash("BIPSchnorrDerive", bytes_from_int(seckey) + msg)) % n + seckey = seckey0 if has_even_y(P) else n - seckey0 + t = xor_bytes(bytes_from_int(seckey), tagged_hash("BIP340/aux", aux_rand)) + k0 = int_from_bytes(tagged_hash("BIP340/nonce", t + bytes_from_point(P) + msg)) % n if k0 == 0: raise RuntimeError('Failure. This happens only with negligible probability.') R = point_mul(G, k0) k = n - k0 if not has_square_y(R) else k0 - e = int_from_bytes(tagged_hash("BIPSchnorr", bytes_from_point(R) + bytes_from_point(P) + msg)) % n - return bytes_from_point(R) + bytes_from_int((k + e * seckey) % n) + e = int_from_bytes(tagged_hash("BIP340/challenge", bytes_from_point(R) + bytes_from_point(P) + msg)) % n + sig = bytes_from_point(R) + bytes_from_int((k + e * seckey) % n) + if not schnorr_verify(msg, bytes_from_point(P), sig): + raise RuntimeError('The signature does not pass verification.') + return sig def schnorr_verify(msg, pubkey, sig): if len(msg) != 32: @@ -103,14 +122,14 @@ def schnorr_verify(msg, pubkey, sig): raise ValueError('The public key must be a 32-byte array.') if len(sig) != 64: raise ValueError('The signature must be a 64-byte array.') - P = point_from_bytes(pubkey) + P = lift_x_even_y(pubkey) if (P is None): return False r = int_from_bytes(sig[0:32]) s = int_from_bytes(sig[32:64]) if (r >= p or s >= n): return False - e = int_from_bytes(tagged_hash("BIPSchnorr", sig[0:32] + pubkey + msg)) % n + e = int_from_bytes(tagged_hash("BIP340/challenge", sig[0:32] + pubkey + msg)) % n R = point_add(point_mul(G, s), point_mul(P, n - e)) if R is None or not has_square_y(R) or x(R) != r: return False @@ -127,7 +146,7 @@ def test_vectors(): reader = csv.reader(csvfile) reader.__next__() for row in reader: - (index, seckey, pubkey, msg, sig, result, comment) = row + (index, seckey, pubkey, aux_rand, msg, sig, result, comment) = row pubkey = bytes.fromhex(pubkey) msg = bytes.fromhex(msg) sig = bytes.fromhex(sig) @@ -140,7 +159,8 @@ def test_vectors(): print(' * Failed key generation.') print(' Expected key:', pubkey.hex().upper()) print(' Actual key:', pubkey_actual.hex().upper()) - sig_actual = schnorr_sign(msg, seckey) + aux_rand = bytes.fromhex(aux_rand) + sig_actual = schnorr_sign(msg, seckey, aux_rand) if sig == sig_actual: print(' * Passed signing test.') else: |