diff options
author | Pieter Wuille <pieter@wuille.net> | 2021-10-27 15:46:03 -0400 |
---|---|---|
committer | Pieter Wuille <pieter@wuille.net> | 2021-11-12 12:04:20 -0500 |
commit | ca83ffc2ea5fe08f16fff7df71c040d067f2afb0 (patch) | |
tree | 803e6de3f7b48b54e4e608482f7124d49a7e79fa | |
parent | c98c53f20cadeda53f6a9323f72363593d174f68 (diff) |
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).
-rwxr-xr-x | test/functional/feature_taproot.py | 10 | ||||
-rw-r--r-- | test/functional/test_framework/key.py | 20 |
2 files changed, 25 insertions, 5 deletions
diff --git a/test/functional/feature_taproot.py b/test/functional/feature_taproot.py index 085b37a74d..f2ae8b302d 100755 --- a/test/functional/feature_taproot.py +++ b/test/functional/feature_taproot.py @@ -253,14 +253,18 @@ def default_key_tweaked(ctx): def default_signature(ctx): """Default expression for "signature": BIP340 signature or ECDSA signature depending on mode.""" sighash = get(ctx, "sighash") + deterministic = get(ctx, "deterministic") if get(ctx, "mode") == "taproot": key = get(ctx, "key_tweaked") flip_r = get(ctx, "flag_flip_r") 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: key = get(ctx, "key") - return key.sign_ecdsa(sighash) + return key.sign_ecdsa(sighash, rfc6979=deterministic) def default_hashtype_actual(ctx): """Default expression for "hashtype_actual": hashtype, unless mismatching SIGHASH_SINGLE in taproot.""" @@ -392,6 +396,8 @@ DEFAULT_CONTEXT = { "leaf": None, # The input arguments to provide to the executed script "inputs": [], + # Use deterministic signing nonces + "deterministic": False, # == Parameters to be set before evaluation: == # - mode: what spending style to use ("taproot", "witv0", or "legacy"). diff --git a/test/functional/test_framework/key.py b/test/functional/test_framework/key.py index 26526e35fa..e5dea66963 100644 --- a/test/functional/test_framework/key.py +++ b/test/functional/test_framework/key.py @@ -8,6 +8,7 @@ keys, and is trivially vulnerable to side channel attacks. Do not use for anything but tests.""" import csv import hashlib +import hmac import os import random import unittest @@ -326,6 +327,16 @@ def generate_privkey(): """Generate a valid random 32-byte private key.""" 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(): """A secp256k1 private key""" @@ -368,15 +379,18 @@ class ECKey(): ret.compressed = self.compressed 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. 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, but a simple random nonce (some tests rely on distinct transactions for the same operation) - k = random.randrange(1, SECP256K1_ORDER) + # 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 |