mirror of
https://github.com/bitcoin/bitcoin.git
synced 2025-02-21 22:42:04 +01:00
tests: add deterministic signing mode to ECDSA
This does the following: * Adds a rfc6979 argument to test_framework/key.py's sign_ecdsa to select (deterministic) RFC6979-based nonce generation. * Add a flag in feature_taproot.py's framework called "deterministic". * Make the Schnorr signing in feature_taproot.py randomized by default, reverting to the old deterministic (aux_rnd=0x0000...00) behavior if the deterministic context flag is set. * Make the ECDSA signing in feature_taproot.py use RFC6979-based nonces when the deterministic context flag is set (keeping the old randomized behavior otherwise).
This commit is contained in:
parent
c98c53f20c
commit
ca83ffc2ea
2 changed files with 25 additions and 5 deletions
|
@ -253,14 +253,18 @@ def default_key_tweaked(ctx):
|
||||||
def default_signature(ctx):
|
def default_signature(ctx):
|
||||||
"""Default expression for "signature": BIP340 signature or ECDSA signature depending on mode."""
|
"""Default expression for "signature": BIP340 signature or ECDSA signature depending on mode."""
|
||||||
sighash = get(ctx, "sighash")
|
sighash = get(ctx, "sighash")
|
||||||
|
deterministic = get(ctx, "deterministic")
|
||||||
if get(ctx, "mode") == "taproot":
|
if get(ctx, "mode") == "taproot":
|
||||||
key = get(ctx, "key_tweaked")
|
key = get(ctx, "key_tweaked")
|
||||||
flip_r = get(ctx, "flag_flip_r")
|
flip_r = get(ctx, "flag_flip_r")
|
||||||
flip_p = get(ctx, "flag_flip_p")
|
flip_p = get(ctx, "flag_flip_p")
|
||||||
return sign_schnorr(key, sighash, flip_r=flip_r, flip_p=flip_p)
|
aux = bytes([0] * 32)
|
||||||
|
if not deterministic:
|
||||||
|
aux = random.getrandbits(256).to_bytes(32, 'big')
|
||||||
|
return sign_schnorr(key, sighash, flip_r=flip_r, flip_p=flip_p, aux=aux)
|
||||||
else:
|
else:
|
||||||
key = get(ctx, "key")
|
key = get(ctx, "key")
|
||||||
return key.sign_ecdsa(sighash)
|
return key.sign_ecdsa(sighash, rfc6979=deterministic)
|
||||||
|
|
||||||
def default_hashtype_actual(ctx):
|
def default_hashtype_actual(ctx):
|
||||||
"""Default expression for "hashtype_actual": hashtype, unless mismatching SIGHASH_SINGLE in taproot."""
|
"""Default expression for "hashtype_actual": hashtype, unless mismatching SIGHASH_SINGLE in taproot."""
|
||||||
|
@ -392,6 +396,8 @@ DEFAULT_CONTEXT = {
|
||||||
"leaf": None,
|
"leaf": None,
|
||||||
# The input arguments to provide to the executed script
|
# The input arguments to provide to the executed script
|
||||||
"inputs": [],
|
"inputs": [],
|
||||||
|
# Use deterministic signing nonces
|
||||||
|
"deterministic": False,
|
||||||
|
|
||||||
# == Parameters to be set before evaluation: ==
|
# == Parameters to be set before evaluation: ==
|
||||||
# - mode: what spending style to use ("taproot", "witv0", or "legacy").
|
# - mode: what spending style to use ("taproot", "witv0", or "legacy").
|
||||||
|
|
|
@ -8,6 +8,7 @@ keys, and is trivially vulnerable to side channel attacks. Do not use for
|
||||||
anything but tests."""
|
anything but tests."""
|
||||||
import csv
|
import csv
|
||||||
import hashlib
|
import hashlib
|
||||||
|
import hmac
|
||||||
import os
|
import os
|
||||||
import random
|
import random
|
||||||
import unittest
|
import unittest
|
||||||
|
@ -326,6 +327,16 @@ def generate_privkey():
|
||||||
"""Generate a valid random 32-byte private key."""
|
"""Generate a valid random 32-byte private key."""
|
||||||
return random.randrange(1, SECP256K1_ORDER).to_bytes(32, 'big')
|
return random.randrange(1, SECP256K1_ORDER).to_bytes(32, 'big')
|
||||||
|
|
||||||
|
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():
|
class ECKey():
|
||||||
"""A secp256k1 private key"""
|
"""A secp256k1 private key"""
|
||||||
|
|
||||||
|
@ -368,15 +379,18 @@ class ECKey():
|
||||||
ret.compressed = self.compressed
|
ret.compressed = self.compressed
|
||||||
return ret
|
return ret
|
||||||
|
|
||||||
def sign_ecdsa(self, msg, low_s=True):
|
def sign_ecdsa(self, msg, low_s=True, rfc6979=False):
|
||||||
"""Construct a DER-encoded ECDSA signature with this key.
|
"""Construct a DER-encoded ECDSA signature with this key.
|
||||||
|
|
||||||
See https://en.wikipedia.org/wiki/Elliptic_Curve_Digital_Signature_Algorithm for the
|
See https://en.wikipedia.org/wiki/Elliptic_Curve_Digital_Signature_Algorithm for the
|
||||||
ECDSA signer algorithm."""
|
ECDSA signer algorithm."""
|
||||||
assert(self.valid)
|
assert(self.valid)
|
||||||
z = int.from_bytes(msg, 'big')
|
z = int.from_bytes(msg, 'big')
|
||||||
# Note: no RFC6979, but a simple random nonce (some tests rely on distinct transactions for the same operation)
|
# Note: no RFC6979 by default, but a simple random nonce (some tests rely on distinct transactions for the same operation)
|
||||||
k = random.randrange(1, SECP256K1_ORDER)
|
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 = SECP256K1.affine(SECP256K1.mul([(SECP256K1_G, k)]))
|
||||||
r = R[0] % SECP256K1_ORDER
|
r = R[0] % SECP256K1_ORDER
|
||||||
s = (modinv(k, SECP256K1_ORDER) * (z + self.secret * r)) % SECP256K1_ORDER
|
s = (modinv(k, SECP256K1_ORDER) * (z + self.secret * r)) % SECP256K1_ORDER
|
||||||
|
|
Loading…
Add table
Reference in a new issue