summaryrefslogtreecommitdiff
path: root/bip-0340/reference.py
diff options
context:
space:
mode:
authorPieter Wuille <github-sipa@wuille.net>2020-03-10 06:28:20 -0700
committerGitHub <noreply@github.com>2020-03-10 06:28:20 -0700
commitf71b5cbb5c16dc68d0e110febcfc3ca88cca5740 (patch)
treebccd2fb0300cdf353af3ad45f22e56eebf1e3538 /bip-0340/reference.py
parent9abbfa53c9ff9630c292b69a0cf1be9c19ecc84b (diff)
parent9bfa53e9fb4af9f17d63806fe0710f18203c94c9 (diff)
downloadbips-f71b5cbb5c16dc68d0e110febcfc3ca88cca5740.tar.xz
Merge pull request #196 from jonasnick/update-ref
Update reference code and test vectors
Diffstat (limited to 'bip-0340/reference.py')
-rw-r--r--bip-0340/reference.py40
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: