Skip to content

Commit ca83ffc

Browse files
committed
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).
1 parent c98c53f commit ca83ffc

File tree

2 files changed

+25
-5
lines changed

2 files changed

+25
-5
lines changed

test/functional/feature_taproot.py

Lines changed: 8 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -253,14 +253,18 @@ def default_key_tweaked(ctx):
253253
def default_signature(ctx):
254254
"""Default expression for "signature": BIP340 signature or ECDSA signature depending on mode."""
255255
sighash = get(ctx, "sighash")
256+
deterministic = get(ctx, "deterministic")
256257
if get(ctx, "mode") == "taproot":
257258
key = get(ctx, "key_tweaked")
258259
flip_r = get(ctx, "flag_flip_r")
259260
flip_p = get(ctx, "flag_flip_p")
260-
return sign_schnorr(key, sighash, flip_r=flip_r, flip_p=flip_p)
261+
aux = bytes([0] * 32)
262+
if not deterministic:
263+
aux = random.getrandbits(256).to_bytes(32, 'big')
264+
return sign_schnorr(key, sighash, flip_r=flip_r, flip_p=flip_p, aux=aux)
261265
else:
262266
key = get(ctx, "key")
263-
return key.sign_ecdsa(sighash)
267+
return key.sign_ecdsa(sighash, rfc6979=deterministic)
264268

265269
def default_hashtype_actual(ctx):
266270
"""Default expression for "hashtype_actual": hashtype, unless mismatching SIGHASH_SINGLE in taproot."""
@@ -392,6 +396,8 @@ def default_scriptsig(ctx):
392396
"leaf": None,
393397
# The input arguments to provide to the executed script
394398
"inputs": [],
399+
# Use deterministic signing nonces
400+
"deterministic": False,
395401

396402
# == Parameters to be set before evaluation: ==
397403
# - mode: what spending style to use ("taproot", "witv0", or "legacy").

test/functional/test_framework/key.py

Lines changed: 17 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@
88
anything but tests."""
99
import csv
1010
import hashlib
11+
import hmac
1112
import os
1213
import random
1314
import unittest
@@ -326,6 +327,16 @@ def generate_privkey():
326327
"""Generate a valid random 32-byte private key."""
327328
return random.randrange(1, SECP256K1_ORDER).to_bytes(32, 'big')
328329

330+
def rfc6979_nonce(key):
331+
"""Compute signing nonce using RFC6979."""
332+
v = bytes([1] * 32)
333+
k = bytes([0] * 32)
334+
k = hmac.new(k, v + b"\x00" + key, 'sha256').digest()
335+
v = hmac.new(k, v, 'sha256').digest()
336+
k = hmac.new(k, v + b"\x01" + key, 'sha256').digest()
337+
v = hmac.new(k, v, 'sha256').digest()
338+
return hmac.new(k, v, 'sha256').digest()
339+
329340
class ECKey():
330341
"""A secp256k1 private key"""
331342

@@ -368,15 +379,18 @@ def get_pubkey(self):
368379
ret.compressed = self.compressed
369380
return ret
370381

371-
def sign_ecdsa(self, msg, low_s=True):
382+
def sign_ecdsa(self, msg, low_s=True, rfc6979=False):
372383
"""Construct a DER-encoded ECDSA signature with this key.
373384
374385
See https://en.wikipedia.org/wiki/Elliptic_Curve_Digital_Signature_Algorithm for the
375386
ECDSA signer algorithm."""
376387
assert(self.valid)
377388
z = int.from_bytes(msg, 'big')
378-
# Note: no RFC6979, but a simple random nonce (some tests rely on distinct transactions for the same operation)
379-
k = random.randrange(1, SECP256K1_ORDER)
389+
# Note: no RFC6979 by default, but a simple random nonce (some tests rely on distinct transactions for the same operation)
390+
if rfc6979:
391+
k = int.from_bytes(rfc6979_nonce(self.secret.to_bytes(32, 'big') + msg), 'big')
392+
else:
393+
k = random.randrange(1, SECP256K1_ORDER)
380394
R = SECP256K1.affine(SECP256K1.mul([(SECP256K1_G, k)]))
381395
r = R[0] % SECP256K1_ORDER
382396
s = (modinv(k, SECP256K1_ORDER) * (z + self.secret * r)) % SECP256K1_ORDER

0 commit comments

Comments
 (0)