@@ -342,7 +342,13 @@ def ecdsa_verify_recoverable(self, sig65: bytes, msg32: bytes) -> bool:
342342 # check message
343343 return self .ecdsa_verify (sig65 [1 :], msg32 )
344344
345- def ecdsa_verify (self , sig64 : bytes , msg32 : bytes ) -> bool :
345+ def ecdsa_verify (
346+ self ,
347+ sig64 : bytes ,
348+ msg32 : bytes ,
349+ * ,
350+ enforce_low_s : bool = True , # policy/standardness rule
351+ ) -> bool :
346352 assert_bytes (sig64 )
347353 if len (sig64 ) != 64 :
348354 return False
@@ -353,7 +359,8 @@ def ecdsa_verify(self, sig64: bytes, msg32: bytes) -> bool:
353359 ret = _libsecp256k1 .secp256k1_ecdsa_signature_parse_compact (_libsecp256k1 .ctx , sig , sig64 )
354360 if 1 != ret :
355361 return False
356- ret = _libsecp256k1 .secp256k1_ecdsa_signature_normalize (_libsecp256k1 .ctx , sig , sig )
362+ if not enforce_low_s :
363+ ret = _libsecp256k1 .secp256k1_ecdsa_signature_normalize (_libsecp256k1 .ctx , sig , sig )
357364
358365 pubkey = self ._to_libsecp256k1_pubkey_ptr ()
359366 if 1 != _libsecp256k1 .secp256k1_ecdsa_verify (_libsecp256k1 .ctx , sig , msg32 , pubkey ):
@@ -439,7 +446,8 @@ def verify_usermessage_with_address(address: str, sig65: bytes, message: bytes,
439446 else :
440447 return False
441448 # check message
442- return public_key .ecdsa_verify (sig65 [1 :], h )
449+ # note: `$ bitcoin-cli verifymessage` does NOT enforce the low-S rule for ecdsa sigs
450+ return public_key .ecdsa_verify (sig65 [1 :], h , enforce_low_s = False )
443451
444452
445453def is_secret_within_curve_range (secret : Union [int , bytes ]) -> bool :
@@ -566,6 +574,8 @@ def schnorr_sign(self, msg32: bytes, *, aux_rand32: bytes = None) -> bytes:
566574 return sig64
567575
568576 def ecdsa_sign_recoverable (self , msg32 : bytes , * , is_compressed : bool ) -> bytes :
577+ assert len (msg32 ) == 32 , len (msg32 )
578+
569579 def bruteforce_recid (sig64 : bytes ):
570580 for recid in range (4 ):
571581 sig65 = construct_ecdsa_sig65 (sig64 , recid , is_compressed = is_compressed )
0 commit comments