|
| 1 | +# The following functions are based on the BIP 340 reference implementation: |
| 2 | +# https://github.com/bitcoin/bips/blob/master/bip-0340/reference.py |
| 3 | + |
| 4 | +from .secp256k1 import FE, GE, G |
| 5 | +from .util import int_from_bytes, bytes_from_int, xor_bytes, tagged_hash |
| 6 | + |
| 7 | + |
| 8 | +def pubkey_gen(seckey: bytes) -> bytes: |
| 9 | + d0 = int_from_bytes(seckey) |
| 10 | + if not (1 <= d0 <= GE.ORDER - 1): |
| 11 | + raise ValueError("The secret key must be an integer in the range 1..n-1.") |
| 12 | + P = d0 * G |
| 13 | + assert not P.infinity |
| 14 | + return P.to_bytes_xonly() |
| 15 | + |
| 16 | + |
| 17 | +def schnorr_sign( |
| 18 | + msg: bytes, seckey: bytes, aux_rand: bytes, tag_prefix: str = "BIP0340" |
| 19 | +) -> bytes: |
| 20 | + d0 = int_from_bytes(seckey) |
| 21 | + if not (1 <= d0 <= GE.ORDER - 1): |
| 22 | + raise ValueError("The secret key must be an integer in the range 1..n-1.") |
| 23 | + if len(aux_rand) != 32: |
| 24 | + raise ValueError("aux_rand must be 32 bytes instead of %i." % len(aux_rand)) |
| 25 | + P = d0 * G |
| 26 | + assert not P.infinity |
| 27 | + d = d0 if P.has_even_y() else GE.ORDER - d0 |
| 28 | + t = xor_bytes(bytes_from_int(d), tagged_hash(tag_prefix + "/aux", aux_rand)) |
| 29 | + k0 = ( |
| 30 | + int_from_bytes(tagged_hash(tag_prefix + "/nonce", t + P.to_bytes_xonly() + msg)) |
| 31 | + % GE.ORDER |
| 32 | + ) |
| 33 | + if k0 == 0: |
| 34 | + raise RuntimeError("Failure. This happens only with negligible probability.") |
| 35 | + R = k0 * G |
| 36 | + assert not R.infinity |
| 37 | + k = k0 if R.has_even_y() else GE.ORDER - k0 |
| 38 | + e = ( |
| 39 | + int_from_bytes( |
| 40 | + tagged_hash( |
| 41 | + tag_prefix + "/challenge", R.to_bytes_xonly() + P.to_bytes_xonly() + msg |
| 42 | + ) |
| 43 | + ) |
| 44 | + % GE.ORDER |
| 45 | + ) |
| 46 | + sig = R.to_bytes_xonly() + bytes_from_int((k + e * d) % GE.ORDER) |
| 47 | + assert schnorr_verify(msg, P.to_bytes_xonly(), sig, tag_prefix=tag_prefix) |
| 48 | + return sig |
| 49 | + |
| 50 | + |
| 51 | +def schnorr_verify( |
| 52 | + msg: bytes, pubkey: bytes, sig: bytes, tag_prefix: str = "BIP0340" |
| 53 | +) -> bool: |
| 54 | + if len(pubkey) != 32: |
| 55 | + raise ValueError("The public key must be a 32-byte array.") |
| 56 | + if len(sig) != 64: |
| 57 | + raise ValueError("The signature must be a 64-byte array.") |
| 58 | + try: |
| 59 | + P = GE.from_bytes_xonly(pubkey) |
| 60 | + except ValueError: |
| 61 | + return False |
| 62 | + r = int_from_bytes(sig[0:32]) |
| 63 | + s = int_from_bytes(sig[32:64]) |
| 64 | + if (r >= FE.SIZE) or (s >= GE.ORDER): |
| 65 | + return False |
| 66 | + e = ( |
| 67 | + int_from_bytes(tagged_hash(tag_prefix + "/challenge", sig[0:32] + pubkey + msg)) |
| 68 | + % GE.ORDER |
| 69 | + ) |
| 70 | + R = s * G - e * P |
| 71 | + if R.infinity or (not R.has_even_y()) or (R.x != r): |
| 72 | + return False |
| 73 | + return True |
0 commit comments