mirror of
https://github.com/bitcoin/bips.git
synced 2024-11-19 01:40:05 +01:00
33a99a1a17
* reference.py contains the silent payment specific code * secp256k1.py for doing the EC operations * bech32m.py contains code for encoding/decoding bech32(m) addresses * bitcoin_utils.py contains some helper code, not specific to silent payments * send_and_receive_test_vectors.json contains the wallet unit test vectors Co-Authored-By: S3RK <1466284+S3RK@users.noreply.github.com> Co-Authored-By: Oghenovo Usiwoma <37949128+Eunovo@users.noreply.github.com> Co-authored-by: S.Blagogee <34041358+setavenger@users.noreply.github.com>
697 lines
23 KiB
Python
697 lines
23 KiB
Python
# Copyright (c) 2019 Pieter Wuille
|
|
# Distributed under the MIT software license, see the accompanying
|
|
# file COPYING or http://www.opensource.org/licenses/mit-license.php.
|
|
"""Test-only secp256k1 elliptic curve implementation
|
|
|
|
WARNING: This code is slow, uses bad randomness, does not properly protect
|
|
keys, and is trivially vulnerable to side channel attacks. Do not use for
|
|
anything but tests."""
|
|
import random
|
|
import hashlib
|
|
import hmac
|
|
|
|
def TaggedHash(tag, data):
|
|
ss = hashlib.sha256(tag.encode('utf-8')).digest()
|
|
ss += ss
|
|
ss += data
|
|
return hashlib.sha256(ss).digest()
|
|
|
|
def modinv(a, n):
|
|
"""Compute the modular inverse of a modulo n
|
|
|
|
See https://en.wikipedia.org/wiki/Extended_Euclidean_algorithm#Modular_integers.
|
|
"""
|
|
t1, t2 = 0, 1
|
|
r1, r2 = n, a
|
|
while r2 != 0:
|
|
q = r1 // r2
|
|
t1, t2 = t2, t1 - q * t2
|
|
r1, r2 = r2, r1 - q * r2
|
|
if r1 > 1:
|
|
return None
|
|
if t1 < 0:
|
|
t1 += n
|
|
return t1
|
|
|
|
def jacobi_symbol(n, k):
|
|
"""Compute the Jacobi symbol of n modulo k
|
|
|
|
See http://en.wikipedia.org/wiki/Jacobi_symbol
|
|
|
|
For our application k is always prime, so this is the same as the Legendre symbol."""
|
|
assert k > 0 and k & 1, "jacobi symbol is only defined for positive odd k"
|
|
n %= k
|
|
t = 0
|
|
while n != 0:
|
|
while n & 1 == 0:
|
|
n >>= 1
|
|
r = k & 7
|
|
t ^= (r == 3 or r == 5)
|
|
n, k = k, n
|
|
t ^= (n & k & 3 == 3)
|
|
n = n % k
|
|
if k == 1:
|
|
return -1 if t else 1
|
|
return 0
|
|
|
|
def modsqrt(a, p):
|
|
"""Compute the square root of a modulo p when p % 4 = 3.
|
|
|
|
The Tonelli-Shanks algorithm can be used. See https://en.wikipedia.org/wiki/Tonelli-Shanks_algorithm
|
|
|
|
Limiting this function to only work for p % 4 = 3 means we don't need to
|
|
iterate through the loop. The highest n such that p - 1 = 2^n Q with Q odd
|
|
is n = 1. Therefore Q = (p-1)/2 and sqrt = a^((Q+1)/2) = a^((p+1)/4)
|
|
|
|
secp256k1's is defined over field of size 2**256 - 2**32 - 977, which is 3 mod 4.
|
|
"""
|
|
if p % 4 != 3:
|
|
raise NotImplementedError("modsqrt only implemented for p % 4 = 3")
|
|
sqrt = pow(a, (p + 1)//4, p)
|
|
if pow(sqrt, 2, p) == a % p:
|
|
return sqrt
|
|
return None
|
|
|
|
def int_or_bytes(s):
|
|
"Convert 32-bytes to int while accepting also int and returning it as is."
|
|
if isinstance(s, bytes):
|
|
assert(len(s) == 32)
|
|
s = int.from_bytes(s, 'big')
|
|
elif not isinstance(s, int):
|
|
raise TypeError
|
|
return s
|
|
|
|
class EllipticCurve:
|
|
def __init__(self, p, a, b):
|
|
"""Initialize elliptic curve y^2 = x^3 + a*x + b over GF(p)."""
|
|
self.p = p
|
|
self.a = a % p
|
|
self.b = b % p
|
|
|
|
def affine(self, p1):
|
|
"""Convert a Jacobian point tuple p1 to affine form, or None if at infinity.
|
|
|
|
An affine point is represented as the Jacobian (x, y, 1)"""
|
|
x1, y1, z1 = p1
|
|
if z1 == 0:
|
|
return None
|
|
inv = modinv(z1, self.p)
|
|
inv_2 = (inv**2) % self.p
|
|
inv_3 = (inv_2 * inv) % self.p
|
|
return ((inv_2 * x1) % self.p, (inv_3 * y1) % self.p, 1)
|
|
|
|
def has_even_y(self, p1):
|
|
"""Whether the point p1 has an even Y coordinate when expressed in affine coordinates."""
|
|
return not (p1[2] == 0 or self.affine(p1)[1] & 1)
|
|
|
|
def negate(self, p1):
|
|
"""Negate a Jacobian point tuple p1."""
|
|
x1, y1, z1 = p1
|
|
return (x1, (self.p - y1) % self.p, z1)
|
|
|
|
def on_curve(self, p1):
|
|
"""Determine whether a Jacobian tuple p is on the curve (and not infinity)"""
|
|
x1, y1, z1 = p1
|
|
z2 = pow(z1, 2, self.p)
|
|
z4 = pow(z2, 2, self.p)
|
|
return z1 != 0 and (pow(x1, 3, self.p) + self.a * x1 * z4 + self.b * z2 * z4 - pow(y1, 2, self.p)) % self.p == 0
|
|
|
|
def is_x_coord(self, x):
|
|
"""Test whether x is a valid X coordinate on the curve."""
|
|
x_3 = pow(x, 3, self.p)
|
|
return jacobi_symbol(x_3 + self.a * x + self.b, self.p) != -1
|
|
|
|
def lift_x(self, x):
|
|
"""Given an X coordinate on the curve, return a corresponding affine point."""
|
|
x_3 = pow(x, 3, self.p)
|
|
v = x_3 + self.a * x + self.b
|
|
y = modsqrt(v, self.p)
|
|
if y is None:
|
|
return None
|
|
return (x, y, 1)
|
|
|
|
def double(self, p1):
|
|
"""Double a Jacobian tuple p1
|
|
|
|
See https://en.wikibooks.org/wiki/Cryptography/Prime_Curve/Jacobian_Coordinates - Point Doubling"""
|
|
x1, y1, z1 = p1
|
|
if z1 == 0:
|
|
return (0, 1, 0)
|
|
y1_2 = (y1**2) % self.p
|
|
y1_4 = (y1_2**2) % self.p
|
|
x1_2 = (x1**2) % self.p
|
|
s = (4*x1*y1_2) % self.p
|
|
m = 3*x1_2
|
|
if self.a:
|
|
m += self.a * pow(z1, 4, self.p)
|
|
m = m % self.p
|
|
x2 = (m**2 - 2*s) % self.p
|
|
y2 = (m*(s - x2) - 8*y1_4) % self.p
|
|
z2 = (2*y1*z1) % self.p
|
|
return (x2, y2, z2)
|
|
|
|
def add_mixed(self, p1, p2):
|
|
"""Add a Jacobian tuple p1 and an affine tuple p2
|
|
|
|
See https://en.wikibooks.org/wiki/Cryptography/Prime_Curve/Jacobian_Coordinates - Point Addition (with affine point)"""
|
|
x1, y1, z1 = p1
|
|
x2, y2, z2 = p2
|
|
assert(z2 == 1)
|
|
# Adding to the point at infinity is a no-op
|
|
if z1 == 0:
|
|
return p2
|
|
z1_2 = (z1**2) % self.p
|
|
z1_3 = (z1_2 * z1) % self.p
|
|
u2 = (x2 * z1_2) % self.p
|
|
s2 = (y2 * z1_3) % self.p
|
|
if x1 == u2:
|
|
if (y1 != s2):
|
|
# p1 and p2 are inverses. Return the point at infinity.
|
|
return (0, 1, 0)
|
|
# p1 == p2. The formulas below fail when the two points are equal.
|
|
return self.double(p1)
|
|
h = u2 - x1
|
|
r = s2 - y1
|
|
h_2 = (h**2) % self.p
|
|
h_3 = (h_2 * h) % self.p
|
|
u1_h_2 = (x1 * h_2) % self.p
|
|
x3 = (r**2 - h_3 - 2*u1_h_2) % self.p
|
|
y3 = (r*(u1_h_2 - x3) - y1*h_3) % self.p
|
|
z3 = (h*z1) % self.p
|
|
return (x3, y3, z3)
|
|
|
|
def add(self, p1, p2):
|
|
"""Add two Jacobian tuples p1 and p2
|
|
|
|
See https://en.wikibooks.org/wiki/Cryptography/Prime_Curve/Jacobian_Coordinates - Point Addition"""
|
|
x1, y1, z1 = p1
|
|
x2, y2, z2 = p2
|
|
# Adding the point at infinity is a no-op
|
|
if z1 == 0:
|
|
return p2
|
|
if z2 == 0:
|
|
return p1
|
|
# Adding an Affine to a Jacobian is more efficient since we save field multiplications and squarings when z = 1
|
|
if z1 == 1:
|
|
return self.add_mixed(p2, p1)
|
|
if z2 == 1:
|
|
return self.add_mixed(p1, p2)
|
|
z1_2 = (z1**2) % self.p
|
|
z1_3 = (z1_2 * z1) % self.p
|
|
z2_2 = (z2**2) % self.p
|
|
z2_3 = (z2_2 * z2) % self.p
|
|
u1 = (x1 * z2_2) % self.p
|
|
u2 = (x2 * z1_2) % self.p
|
|
s1 = (y1 * z2_3) % self.p
|
|
s2 = (y2 * z1_3) % self.p
|
|
if u1 == u2:
|
|
if (s1 != s2):
|
|
# p1 and p2 are inverses. Return the point at infinity.
|
|
return (0, 1, 0)
|
|
# p1 == p2. The formulas below fail when the two points are equal.
|
|
return self.double(p1)
|
|
h = u2 - u1
|
|
r = s2 - s1
|
|
h_2 = (h**2) % self.p
|
|
h_3 = (h_2 * h) % self.p
|
|
u1_h_2 = (u1 * h_2) % self.p
|
|
x3 = (r**2 - h_3 - 2*u1_h_2) % self.p
|
|
y3 = (r*(u1_h_2 - x3) - s1*h_3) % self.p
|
|
z3 = (h*z1*z2) % self.p
|
|
return (x3, y3, z3)
|
|
|
|
def mul(self, ps):
|
|
"""Compute a (multi) point multiplication
|
|
|
|
ps is a list of (Jacobian tuple, scalar) pairs.
|
|
"""
|
|
r = (0, 1, 0)
|
|
for i in range(255, -1, -1):
|
|
r = self.double(r)
|
|
for (p, n) in ps:
|
|
if ((n >> i) & 1):
|
|
r = self.add(r, p)
|
|
return r
|
|
|
|
SECP256K1_FIELD_SIZE = 2**256 - 2**32 - 977
|
|
SECP256K1 = EllipticCurve(SECP256K1_FIELD_SIZE, 0, 7)
|
|
SECP256K1_G = (0x79BE667EF9DCBBAC55A06295CE870B07029BFCDB2DCE28D959F2815B16F81798, 0x483ADA7726A3C4655DA4FBFC0E1108A8FD17B448A68554199C47D08FFB10D4B8, 1)
|
|
SECP256K1_ORDER = 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFEBAAEDCE6AF48A03BBFD25E8CD0364141
|
|
SECP256K1_ORDER_HALF = SECP256K1_ORDER // 2
|
|
NUMS_H = 0x50929b74c1a04954b78b4b6035e97a5e078a5a0f28ec96d547bfee9ace803ac0
|
|
|
|
class ECPubKey():
|
|
"""A secp256k1 public key"""
|
|
|
|
def __init__(self):
|
|
"""Construct an uninitialized public key"""
|
|
self.valid = False
|
|
|
|
def __repr__(self):
|
|
return self.get_bytes().hex()
|
|
|
|
def __eq__(self, other):
|
|
assert isinstance(other, ECPubKey)
|
|
return self.get_bytes() == other.get_bytes()
|
|
|
|
def __hash__(self):
|
|
return hash(self.get_bytes())
|
|
|
|
def set(self, data):
|
|
"""Construct a public key from a serialization in compressed or uncompressed DER format or BIP340 format"""
|
|
if (len(data) == 65 and data[0] == 0x04):
|
|
p = (int.from_bytes(data[1:33], 'big'), int.from_bytes(data[33:65], 'big'), 1)
|
|
self.valid = SECP256K1.on_curve(p)
|
|
if self.valid:
|
|
self.p = p
|
|
self.compressed = False
|
|
elif (len(data) == 33 and (data[0] == 0x02 or data[0] == 0x03)):
|
|
x = int.from_bytes(data[1:33], 'big')
|
|
if SECP256K1.is_x_coord(x):
|
|
p = SECP256K1.lift_x(x)
|
|
# if the oddness of the y co-ord isn't correct, find the other
|
|
# valid y
|
|
if (p[1] & 1) != (data[0] & 1):
|
|
p = SECP256K1.negate(p)
|
|
self.p = p
|
|
self.valid = True
|
|
self.compressed = True
|
|
else:
|
|
self.valid = False
|
|
elif (len(data) == 32):
|
|
x = int.from_bytes(data[0:32], 'big')
|
|
if SECP256K1.is_x_coord(x):
|
|
p = SECP256K1.lift_x(x)
|
|
# if the oddness of the y co-ord isn't correct, find the other
|
|
# valid y
|
|
if p[1]%2 != 0:
|
|
p = SECP256K1.negate(p)
|
|
self.p = p
|
|
self.valid = True
|
|
self.compressed = True
|
|
else:
|
|
self.valid = False
|
|
else:
|
|
self.valid = False
|
|
return self
|
|
|
|
@property
|
|
def is_compressed(self):
|
|
return self.compressed
|
|
|
|
@property
|
|
def is_valid(self):
|
|
return self.valid
|
|
|
|
def get_y(self):
|
|
return SECP256K1.affine(self.p)[1]
|
|
|
|
def get_x(self):
|
|
return SECP256K1.affine(self.p)[0]
|
|
|
|
def get_bytes(self, bip340=True):
|
|
assert(self.valid)
|
|
p = SECP256K1.affine(self.p)
|
|
if p is None:
|
|
return None
|
|
if bip340:
|
|
return bytes(p[0].to_bytes(32, 'big'))
|
|
elif self.compressed:
|
|
return bytes([0x02 + (p[1] & 1)]) + p[0].to_bytes(32, 'big')
|
|
else:
|
|
return bytes([0x04]) + p[0].to_bytes(32, 'big') + p[1].to_bytes(32, 'big')
|
|
|
|
def verify_ecdsa(self, sig, msg, low_s=True):
|
|
"""Verify a strictly DER-encoded ECDSA signature against this pubkey.
|
|
|
|
See https://en.wikipedia.org/wiki/Elliptic_Curve_Digital_Signature_Algorithm for the
|
|
ECDSA verifier algorithm"""
|
|
assert(self.valid)
|
|
|
|
# Extract r and s from the DER formatted signature. Return false for
|
|
# any DER encoding errors.
|
|
if (sig[1] + 2 != len(sig)):
|
|
return False
|
|
if (len(sig) < 4):
|
|
return False
|
|
if (sig[0] != 0x30):
|
|
return False
|
|
if (sig[2] != 0x02):
|
|
return False
|
|
rlen = sig[3]
|
|
if (len(sig) < 6 + rlen):
|
|
return False
|
|
if rlen < 1 or rlen > 33:
|
|
return False
|
|
if sig[4] >= 0x80:
|
|
return False
|
|
if (rlen > 1 and (sig[4] == 0) and not (sig[5] & 0x80)):
|
|
return False
|
|
r = int.from_bytes(sig[4:4+rlen], 'big')
|
|
if (sig[4+rlen] != 0x02):
|
|
return False
|
|
slen = sig[5+rlen]
|
|
if slen < 1 or slen > 33:
|
|
return False
|
|
if (len(sig) != 6 + rlen + slen):
|
|
return False
|
|
if sig[6+rlen] >= 0x80:
|
|
return False
|
|
if (slen > 1 and (sig[6+rlen] == 0) and not (sig[7+rlen] & 0x80)):
|
|
return False
|
|
s = int.from_bytes(sig[6+rlen:6+rlen+slen], 'big')
|
|
|
|
# Verify that r and s are within the group order
|
|
if r < 1 or s < 1 or r >= SECP256K1_ORDER or s >= SECP256K1_ORDER:
|
|
return False
|
|
if low_s and s >= SECP256K1_ORDER_HALF:
|
|
return False
|
|
z = int.from_bytes(msg, 'big')
|
|
|
|
# Run verifier algorithm on r, s
|
|
w = modinv(s, SECP256K1_ORDER)
|
|
u1 = z*w % SECP256K1_ORDER
|
|
u2 = r*w % SECP256K1_ORDER
|
|
R = SECP256K1.affine(SECP256K1.mul([(SECP256K1_G, u1), (self.p, u2)]))
|
|
if R is None or R[0] != r:
|
|
return False
|
|
return True
|
|
|
|
def verify_schnorr(self, sig, msg):
|
|
assert(len(msg) == 32)
|
|
assert(len(sig) == 64)
|
|
assert(self.valid)
|
|
r = int.from_bytes(sig[0:32], 'big')
|
|
if r >= SECP256K1_FIELD_SIZE:
|
|
return False
|
|
s = int.from_bytes(sig[32:64], 'big')
|
|
if s >= SECP256K1_ORDER:
|
|
return False
|
|
e = int.from_bytes(TaggedHash("BIP0340/challenge", sig[0:32] + self.get_bytes() + msg), 'big') % SECP256K1_ORDER
|
|
R = SECP256K1.mul([(SECP256K1_G, s), (self.p, SECP256K1_ORDER - e)])
|
|
if not SECP256K1.has_even_y(R):
|
|
return False
|
|
if ((r * R[2] * R[2]) % SECP256K1_FIELD_SIZE) != R[0]:
|
|
return False
|
|
return True
|
|
|
|
def __add__(self, other):
|
|
"""Adds two ECPubKey points."""
|
|
assert isinstance(other, ECPubKey)
|
|
assert self.valid
|
|
assert other.valid
|
|
ret = ECPubKey()
|
|
ret.p = SECP256K1.add(other.p, self.p)
|
|
ret.valid = True
|
|
ret.compressed = self.compressed
|
|
return ret
|
|
|
|
def __radd__(self, other):
|
|
"""Allows this ECPubKey to be added to 0 for sum()"""
|
|
if other == 0:
|
|
return self
|
|
else:
|
|
return self + other
|
|
|
|
def __mul__(self, other):
|
|
"""Multiplies ECPubKey point with a scalar(int/32bytes/ECKey)."""
|
|
if isinstance(other, ECKey):
|
|
assert self.valid
|
|
assert other.secret is not None
|
|
multiplier = other.secret
|
|
else:
|
|
# int_or_bytes checks that other is `int` or `bytes`
|
|
multiplier = int_or_bytes(other)
|
|
|
|
assert multiplier < SECP256K1_ORDER
|
|
multiplier = multiplier % SECP256K1_ORDER
|
|
ret = ECPubKey()
|
|
ret.p = SECP256K1.mul([(self.p, multiplier)])
|
|
ret.valid = True
|
|
ret.compressed = self.compressed
|
|
return ret
|
|
|
|
def __rmul__(self, other):
|
|
"""Multiplies a scalar(int/32bytes/ECKey) with an ECPubKey point"""
|
|
return self * other
|
|
|
|
def __sub__(self, other):
|
|
"""Subtract one point from another"""
|
|
assert isinstance(other, ECPubKey)
|
|
assert self.valid
|
|
assert other.valid
|
|
ret = ECPubKey()
|
|
ret.p = SECP256K1.add(self.p, SECP256K1.negate(other.p))
|
|
ret.valid = True
|
|
ret.compressed = self.compressed
|
|
return ret
|
|
|
|
def tweak_add(self, tweak):
|
|
assert(self.valid)
|
|
t = int_or_bytes(tweak)
|
|
if t >= SECP256K1_ORDER:
|
|
return None
|
|
tweaked = SECP256K1.affine(SECP256K1.mul([(self.p, 1), (SECP256K1_G, t)]))
|
|
if tweaked is None:
|
|
return None
|
|
ret = ECPubKey()
|
|
ret.p = tweaked
|
|
ret.valid = True
|
|
ret.compressed = self.compressed
|
|
return ret
|
|
|
|
def mul(self, data):
|
|
"""Multiplies ECPubKey point with scalar data."""
|
|
assert self.valid
|
|
other = ECKey()
|
|
other.set(data, True)
|
|
return self * other
|
|
|
|
def negate(self):
|
|
self.p = SECP256K1.affine(SECP256K1.negate(self.p))
|
|
|
|
def rfc6979_nonce(key):
|
|
"""Compute signing nonce using RFC6979."""
|
|
v = bytes([1] * 32)
|
|
k = bytes([0] * 32)
|
|
k = hmac.new(k, v + b"\x00" + key, 'sha256').digest()
|
|
v = hmac.new(k, v, 'sha256').digest()
|
|
k = hmac.new(k, v + b"\x01" + key, 'sha256').digest()
|
|
v = hmac.new(k, v, 'sha256').digest()
|
|
return hmac.new(k, v, 'sha256').digest()
|
|
|
|
class ECKey():
|
|
"""A secp256k1 private key"""
|
|
|
|
def __init__(self):
|
|
self.valid = False
|
|
|
|
def __repr__(self):
|
|
return str(self.secret)
|
|
|
|
def __eq__(self, other):
|
|
assert isinstance(other, ECKey)
|
|
return self.secret == other.secret
|
|
|
|
def __hash__(self):
|
|
return hash(self.secret)
|
|
|
|
def set(self, secret, compressed=True):
|
|
"""Construct a private key object from either 32-bytes or an int secret and a compressed flag."""
|
|
secret = int_or_bytes(secret)
|
|
|
|
self.valid = (secret > 0 and secret < SECP256K1_ORDER)
|
|
if self.valid:
|
|
self.secret = secret
|
|
self.compressed = compressed
|
|
return self
|
|
|
|
def generate(self, compressed=True):
|
|
"""Generate a random private key (compressed or uncompressed)."""
|
|
self.set(random.randrange(1, SECP256K1_ORDER).to_bytes(32, 'big'), compressed)
|
|
return self
|
|
|
|
def get_bytes(self):
|
|
"""Retrieve the 32-byte representation of this key."""
|
|
assert(self.valid)
|
|
return self.secret.to_bytes(32, 'big')
|
|
|
|
def as_int(self):
|
|
return self.secret
|
|
|
|
def from_int(self, secret, compressed=True):
|
|
self.valid = (secret > 0 and secret < SECP256K1_ORDER)
|
|
if self.valid:
|
|
self.secret = secret
|
|
self.compressed = compressed
|
|
|
|
def __add__(self, other):
|
|
"""Add key secrets. Returns compressed key."""
|
|
assert isinstance(other, ECKey)
|
|
assert other.secret > 0 and other.secret < SECP256K1_ORDER
|
|
assert self.valid is True
|
|
ret_data = ((self.secret + other.secret) % SECP256K1_ORDER).to_bytes(32, 'big')
|
|
ret = ECKey()
|
|
ret.set(ret_data, True)
|
|
return ret
|
|
|
|
def __radd__(self, other):
|
|
"""Allows this ECKey to be added to 0 for sum()"""
|
|
if other == 0:
|
|
return self
|
|
else:
|
|
return self + other
|
|
|
|
def __sub__(self, other):
|
|
"""Subtract key secrets. Returns compressed key."""
|
|
assert isinstance(other, ECKey)
|
|
assert other.secret > 0 and other.secret < SECP256K1_ORDER
|
|
assert self.valid is True
|
|
ret_data = ((self.secret - other.secret) % SECP256K1_ORDER).to_bytes(32, 'big')
|
|
ret = ECKey()
|
|
ret.set(ret_data, True)
|
|
return ret
|
|
|
|
def __mul__(self, other):
|
|
"""Multiply a private key by another private key or multiply a public key by a private key. Returns compressed key."""
|
|
if isinstance(other, ECKey):
|
|
assert other.secret > 0 and other.secret < SECP256K1_ORDER
|
|
assert self.valid is True
|
|
ret_data = ((self.secret * other.secret) % SECP256K1_ORDER).to_bytes(32, 'big')
|
|
ret = ECKey()
|
|
ret.set(ret_data, True)
|
|
return ret
|
|
elif isinstance(other, ECPubKey):
|
|
return other * self
|
|
else:
|
|
# ECKey().set() checks that other is an `int` or `bytes`
|
|
assert self.valid
|
|
second = ECKey().set(other, self.compressed)
|
|
return self * second
|
|
|
|
def __rmul__(self, other):
|
|
return self * other
|
|
|
|
def add(self, data):
|
|
"""Add key to scalar data. Returns compressed key."""
|
|
other = ECKey()
|
|
other.set(data, True)
|
|
return self + other
|
|
|
|
def mul(self, data):
|
|
"""Multiply key secret with scalar data. Returns compressed key."""
|
|
other = ECKey()
|
|
other.set(data, True)
|
|
return self * other
|
|
|
|
def negate(self):
|
|
"""Negate a private key."""
|
|
assert self.valid
|
|
self.secret = SECP256K1_ORDER - self.secret
|
|
|
|
@property
|
|
def is_valid(self):
|
|
return self.valid
|
|
|
|
@property
|
|
def is_compressed(self):
|
|
return self.compressed
|
|
|
|
def get_pubkey(self):
|
|
"""Compute an ECPubKey object for this secret key."""
|
|
assert(self.valid)
|
|
ret = ECPubKey()
|
|
p = SECP256K1.mul([(SECP256K1_G, self.secret)])
|
|
ret.p = p
|
|
ret.valid = True
|
|
ret.compressed = self.compressed
|
|
return ret
|
|
|
|
def sign_ecdsa(self, msg, low_s=True, rfc6979=False):
|
|
"""Construct a DER-encoded ECDSA signature with this key.
|
|
|
|
See https://en.wikipedia.org/wiki/Elliptic_Curve_Digital_Signature_Algorithm for the
|
|
ECDSA signer algorithm."""
|
|
assert(self.valid)
|
|
z = int.from_bytes(msg, 'big')
|
|
# Note: no RFC6979 by default, but a simple random nonce (some tests rely on distinct transactions for the same operation)
|
|
if rfc6979:
|
|
k = int.from_bytes(rfc6979_nonce(self.secret.to_bytes(32, 'big') + msg), 'big')
|
|
else:
|
|
k = random.randrange(1, SECP256K1_ORDER)
|
|
R = SECP256K1.affine(SECP256K1.mul([(SECP256K1_G, k)]))
|
|
r = R[0] % SECP256K1_ORDER
|
|
s = (modinv(k, SECP256K1_ORDER) * (z + self.secret * r)) % SECP256K1_ORDER
|
|
if low_s and s > SECP256K1_ORDER_HALF:
|
|
s = SECP256K1_ORDER - s
|
|
# Represent in DER format. The byte representations of r and s have
|
|
# length rounded up (255 bits becomes 32 bytes and 256 bits becomes 33
|
|
# bytes).
|
|
rb = r.to_bytes((r.bit_length() + 8) // 8, 'big')
|
|
sb = s.to_bytes((s.bit_length() + 8) // 8, 'big')
|
|
return b'\x30' + bytes([4 + len(rb) + len(sb), 2, len(rb)]) + rb + bytes([2, len(sb)]) + sb
|
|
|
|
def sign_schnorr(self, msg, aux=None):
|
|
"""Create a Schnorr signature (see BIP340)."""
|
|
if aux is None:
|
|
aux = bytes(32)
|
|
|
|
assert self.valid
|
|
assert len(msg) == 32
|
|
assert len(aux) == 32
|
|
|
|
t = (self.secret ^ int.from_bytes(TaggedHash("BIP0340/aux", aux), 'big')).to_bytes(32, 'big')
|
|
kp = int.from_bytes(TaggedHash("BIP0340/nonce", t + self.get_pubkey().get_bytes() + msg), 'big') % SECP256K1_ORDER
|
|
assert kp != 0
|
|
R = SECP256K1.affine(SECP256K1.mul([(SECP256K1_G, kp)]))
|
|
k = kp if SECP256K1.has_even_y(R) else SECP256K1_ORDER - kp
|
|
e = int.from_bytes(TaggedHash("BIP0340/challenge", R[0].to_bytes(32, 'big') + self.get_pubkey().get_bytes() + msg), 'big') % SECP256K1_ORDER
|
|
return R[0].to_bytes(32, 'big') + ((k + e * self.secret) % SECP256K1_ORDER).to_bytes(32, 'big')
|
|
|
|
def tweak_add(self, tweak):
|
|
"""Return a tweaked version of this private key."""
|
|
assert(self.valid)
|
|
t = int_or_bytes(tweak)
|
|
if t >= SECP256K1_ORDER:
|
|
return None
|
|
tweaked = (self.secret + t) % SECP256K1_ORDER
|
|
if tweaked == 0:
|
|
return None
|
|
ret = ECKey()
|
|
ret.set(tweaked.to_bytes(32, 'big'), self.compressed)
|
|
return ret
|
|
|
|
def generate_key_pair(secret=None, compressed=True):
|
|
"""Convenience function to generate a private-public key pair."""
|
|
d = ECKey()
|
|
if secret:
|
|
d.set(secret, compressed)
|
|
else:
|
|
d.generate(compressed)
|
|
|
|
P = d.get_pubkey()
|
|
return d, P
|
|
|
|
def generate_bip340_key_pair():
|
|
"""Convenience function to generate a BIP0340 private-public key pair."""
|
|
d = ECKey()
|
|
d.generate()
|
|
P = d.get_pubkey()
|
|
if P.get_y()%2 != 0:
|
|
d.negate()
|
|
P.negate()
|
|
return d, P
|
|
|
|
def generate_schnorr_nonce():
|
|
"""Generate a random valid BIP340 nonce.
|
|
|
|
See https://github.com/bitcoin/bips/blob/master/bip-0340.mediawiki.
|
|
This implementation ensures the y-coordinate of the nonce point is even."""
|
|
kp = random.randrange(1, SECP256K1_ORDER)
|
|
assert kp != 0
|
|
R = SECP256K1.affine(SECP256K1.mul([(SECP256K1_G, kp)]))
|
|
k = kp if R[1] % 2 == 0 else SECP256K1_ORDER - kp
|
|
k_key = ECKey()
|
|
k_key.set(k.to_bytes(32, 'big'), True)
|
|
return k_key
|