ecc: API changes: verify_message_hash to return bool instead of raising
verify_message_hash and verify_message_for_address now return bool instead of raising Exceptions on bad signatures.
This commit is contained in:
@@ -159,7 +159,7 @@ class ECPubkey(object):
|
|||||||
assert_bytes(sig_string)
|
assert_bytes(sig_string)
|
||||||
if len(sig_string) != 64:
|
if len(sig_string) != 64:
|
||||||
raise Exception(f'wrong encoding used for signature? len={len(sig_string)} (should be 64)')
|
raise Exception(f'wrong encoding used for signature? len={len(sig_string)} (should be 64)')
|
||||||
if recid < 0 or recid > 3:
|
if not (0 <= recid <= 3):
|
||||||
raise ValueError('recid is {}, but should be 0 <= recid <= 3'.format(recid))
|
raise ValueError('recid is {}, but should be 0 <= recid <= 3'.format(recid))
|
||||||
sig65 = create_string_buffer(65)
|
sig65 = create_string_buffer(65)
|
||||||
ret = _libsecp256k1.secp256k1_ecdsa_recoverable_signature_parse_compact(
|
ret = _libsecp256k1.secp256k1_ecdsa_recoverable_signature_parse_compact(
|
||||||
@@ -177,7 +177,7 @@ class ECPubkey(object):
|
|||||||
if len(sig) != 65:
|
if len(sig) != 65:
|
||||||
raise Exception(f'wrong encoding used for signature? len={len(sig)} (should be 65)')
|
raise Exception(f'wrong encoding used for signature? len={len(sig)} (should be 65)')
|
||||||
nV = sig[0]
|
nV = sig[0]
|
||||||
if nV < 27 or nV >= 35:
|
if not (27 <= nV <= 34):
|
||||||
raise Exception("Bad encoding")
|
raise Exception("Bad encoding")
|
||||||
if nV >= 31:
|
if nV >= 31:
|
||||||
compressed = True
|
compressed = True
|
||||||
@@ -290,33 +290,36 @@ class ECPubkey(object):
|
|||||||
raise TypeError('comparison not defined for ECPubkey and {}'.format(type(other)))
|
raise TypeError('comparison not defined for ECPubkey and {}'.format(type(other)))
|
||||||
return (self.x() or 0) < (other.x() or 0)
|
return (self.x() or 0) < (other.x() or 0)
|
||||||
|
|
||||||
def verify_message_for_address(self, sig65: bytes, message: bytes, algo=lambda x: sha256d(msg_magic(x))) -> None:
|
def verify_message_for_address(self, sig65: bytes, message: bytes, algo=lambda x: sha256d(msg_magic(x))) -> bool:
|
||||||
assert_bytes(message)
|
assert_bytes(message)
|
||||||
h = algo(message)
|
h = algo(message)
|
||||||
public_key, compressed = self.from_signature65(sig65, h)
|
try:
|
||||||
|
public_key, compressed = self.from_signature65(sig65, h)
|
||||||
|
except Exception:
|
||||||
|
return False
|
||||||
# check public key
|
# check public key
|
||||||
if public_key != self:
|
if public_key != self:
|
||||||
raise Exception("Bad signature")
|
return False
|
||||||
# check message
|
# check message
|
||||||
self.verify_message_hash(sig65[1:], h)
|
return self.verify_message_hash(sig65[1:], h)
|
||||||
|
|
||||||
# TODO return bool instead of raising
|
def verify_message_hash(self, sig_string: bytes, msg_hash: bytes) -> bool:
|
||||||
def verify_message_hash(self, sig_string: bytes, msg_hash: bytes) -> None:
|
|
||||||
assert_bytes(sig_string)
|
assert_bytes(sig_string)
|
||||||
if len(sig_string) != 64:
|
if len(sig_string) != 64:
|
||||||
raise Exception(f'wrong encoding used for signature? len={len(sig_string)} (should be 64)')
|
return False
|
||||||
if not (isinstance(msg_hash, bytes) and len(msg_hash) == 32):
|
if not (isinstance(msg_hash, bytes) and len(msg_hash) == 32):
|
||||||
raise Exception("msg_hash must be bytes, and 32 bytes exactly")
|
return False
|
||||||
|
|
||||||
sig = create_string_buffer(64)
|
sig = create_string_buffer(64)
|
||||||
ret = _libsecp256k1.secp256k1_ecdsa_signature_parse_compact(_libsecp256k1.ctx, sig, sig_string)
|
ret = _libsecp256k1.secp256k1_ecdsa_signature_parse_compact(_libsecp256k1.ctx, sig, sig_string)
|
||||||
if not ret:
|
if not ret:
|
||||||
raise Exception("Bad signature")
|
return False
|
||||||
ret = _libsecp256k1.secp256k1_ecdsa_signature_normalize(_libsecp256k1.ctx, sig, sig)
|
ret = _libsecp256k1.secp256k1_ecdsa_signature_normalize(_libsecp256k1.ctx, sig, sig)
|
||||||
|
|
||||||
pubkey = self._to_libsecp256k1_pubkey_ptr()
|
pubkey = self._to_libsecp256k1_pubkey_ptr()
|
||||||
if 1 != _libsecp256k1.secp256k1_ecdsa_verify(_libsecp256k1.ctx, sig, msg_hash, pubkey):
|
if 1 != _libsecp256k1.secp256k1_ecdsa_verify(_libsecp256k1.ctx, sig, msg_hash, pubkey):
|
||||||
raise Exception("Bad signature")
|
return False
|
||||||
|
return True
|
||||||
|
|
||||||
def encrypt_message(self, message: bytes, magic: bytes = b'BIE1') -> bytes:
|
def encrypt_message(self, message: bytes, magic: bytes = b'BIE1') -> bytes:
|
||||||
"""
|
"""
|
||||||
@@ -364,33 +367,28 @@ def msg_magic(message: bytes) -> bytes:
|
|||||||
|
|
||||||
|
|
||||||
def verify_signature(pubkey: bytes, sig: bytes, h: bytes) -> bool:
|
def verify_signature(pubkey: bytes, sig: bytes, h: bytes) -> bool:
|
||||||
try:
|
return ECPubkey(pubkey).verify_message_hash(sig, h)
|
||||||
ECPubkey(pubkey).verify_message_hash(sig, h)
|
|
||||||
except:
|
|
||||||
return False
|
|
||||||
return True
|
|
||||||
|
|
||||||
def verify_message_with_address(address: str, sig65: bytes, message: bytes, *, net=None):
|
|
||||||
|
def verify_message_with_address(address: str, sig65: bytes, message: bytes, *, net=None) -> bool:
|
||||||
from .bitcoin import pubkey_to_address
|
from .bitcoin import pubkey_to_address
|
||||||
assert_bytes(sig65, message)
|
assert_bytes(sig65, message)
|
||||||
if net is None: net = constants.net
|
if net is None: net = constants.net
|
||||||
|
h = sha256d(msg_magic(message))
|
||||||
try:
|
try:
|
||||||
h = sha256d(msg_magic(message))
|
|
||||||
public_key, compressed = ECPubkey.from_signature65(sig65, h)
|
public_key, compressed = ECPubkey.from_signature65(sig65, h)
|
||||||
# check public key using the address
|
|
||||||
pubkey_hex = public_key.get_public_key_hex(compressed)
|
|
||||||
for txin_type in ['p2pkh','p2wpkh','p2wpkh-p2sh']:
|
|
||||||
addr = pubkey_to_address(txin_type, pubkey_hex, net=net)
|
|
||||||
if address == addr:
|
|
||||||
break
|
|
||||||
else:
|
|
||||||
raise Exception("Bad signature")
|
|
||||||
# check message
|
|
||||||
public_key.verify_message_hash(sig65[1:], h)
|
|
||||||
return True
|
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
_logger.info(f"Verification error: {repr(e)}")
|
|
||||||
return False
|
return False
|
||||||
|
# check public key using the address
|
||||||
|
pubkey_hex = public_key.get_public_key_hex(compressed)
|
||||||
|
for txin_type in ['p2pkh','p2wpkh','p2wpkh-p2sh']:
|
||||||
|
addr = pubkey_to_address(txin_type, pubkey_hex, net=net)
|
||||||
|
if address == addr:
|
||||||
|
break
|
||||||
|
else:
|
||||||
|
return False
|
||||||
|
# check message
|
||||||
|
return public_key.verify_message_hash(sig65[1:], h)
|
||||||
|
|
||||||
|
|
||||||
def is_secret_within_curve_range(secret: Union[int, bytes]) -> bool:
|
def is_secret_within_curve_range(secret: Union[int, bytes]) -> bool:
|
||||||
@@ -476,7 +474,8 @@ class ECPrivkey(ECPubkey):
|
|||||||
r, s = sign_with_extra_entropy(extra_entropy=extra_entropy)
|
r, s = sign_with_extra_entropy(extra_entropy=extra_entropy)
|
||||||
|
|
||||||
sig_string = sig_string_from_r_and_s(r, s)
|
sig_string = sig_string_from_r_and_s(r, s)
|
||||||
self.verify_message_hash(sig_string, msg_hash)
|
if not self.verify_message_hash(sig_string, msg_hash):
|
||||||
|
raise Exception("sanity check failed: signature we just created does not verify!")
|
||||||
|
|
||||||
sig = sigencode(r, s)
|
sig = sigencode(r, s)
|
||||||
return sig
|
return sig
|
||||||
@@ -488,11 +487,9 @@ class ECPrivkey(ECPubkey):
|
|||||||
def bruteforce_recid(sig_string):
|
def bruteforce_recid(sig_string):
|
||||||
for recid in range(4):
|
for recid in range(4):
|
||||||
sig65 = construct_sig65(sig_string, recid, is_compressed)
|
sig65 = construct_sig65(sig_string, recid, is_compressed)
|
||||||
try:
|
if not self.verify_message_for_address(sig65, message, algo):
|
||||||
self.verify_message_for_address(sig65, message, algo)
|
|
||||||
return sig65, recid
|
|
||||||
except Exception as e:
|
|
||||||
continue
|
continue
|
||||||
|
return sig65, recid
|
||||||
else:
|
else:
|
||||||
raise Exception("error: cannot sign message. no recid fits..")
|
raise Exception("error: cannot sign message. no recid fits..")
|
||||||
|
|
||||||
|
|||||||
@@ -508,7 +508,8 @@ def lndecode(invoice: str, *, verbose=False, net=None) -> LnAddr:
|
|||||||
#
|
#
|
||||||
# A reader MUST use the `n` field to validate the signature instead of
|
# A reader MUST use the `n` field to validate the signature instead of
|
||||||
# performing signature recovery if a valid `n` field is provided.
|
# performing signature recovery if a valid `n` field is provided.
|
||||||
ecc.ECPubkey(addr.pubkey).verify_message_hash(sigdecoded[:64], hrp_hash)
|
if not ecc.ECPubkey(addr.pubkey).verify_message_hash(sigdecoded[:64], hrp_hash):
|
||||||
|
raise LnDecodeException("bad signature")
|
||||||
pubkey_copy = addr.pubkey
|
pubkey_copy = addr.pubkey
|
||||||
class WrappedBytesKey:
|
class WrappedBytesKey:
|
||||||
serialize = lambda: pubkey_copy
|
serialize = lambda: pubkey_copy
|
||||||
|
|||||||
@@ -43,11 +43,7 @@ try:
|
|||||||
# verify a signature (65 bytes) over the session key, using the master bip32 node
|
# verify a signature (65 bytes) over the session key, using the master bip32 node
|
||||||
# - customized to use specific EC library of Electrum.
|
# - customized to use specific EC library of Electrum.
|
||||||
pubkey = BIP32Node.from_xkey(expect_xpub).eckey
|
pubkey = BIP32Node.from_xkey(expect_xpub).eckey
|
||||||
try:
|
return pubkey.verify_message_hash(sig[1:65], self.session_key)
|
||||||
pubkey.verify_message_hash(sig[1:65], self.session_key)
|
|
||||||
return True
|
|
||||||
except:
|
|
||||||
return False
|
|
||||||
|
|
||||||
except ImportError as e:
|
except ImportError as e:
|
||||||
if not (isinstance(e, ModuleNotFoundError) and e.name == 'ckcc'):
|
if not (isinstance(e, ModuleNotFoundError) and e.name == 'ckcc'):
|
||||||
|
|||||||
@@ -152,7 +152,7 @@ class Test_bitcoin(ElectrumTestCase):
|
|||||||
|
|
||||||
signature = eck.sign_message(message, True)
|
signature = eck.sign_message(message, True)
|
||||||
#print signature
|
#print signature
|
||||||
eck.verify_message_for_address(signature, message)
|
self.assertTrue(eck.verify_message_for_address(signature, message))
|
||||||
|
|
||||||
def test_ecc_sanity(self):
|
def test_ecc_sanity(self):
|
||||||
G = ecc.GENERATOR
|
G = ecc.GENERATOR
|
||||||
|
|||||||
@@ -2025,10 +2025,7 @@ class PartialTransaction(Transaction):
|
|||||||
continue
|
continue
|
||||||
pubkey_hex = public_key.get_public_key_hex(compressed=True)
|
pubkey_hex = public_key.get_public_key_hex(compressed=True)
|
||||||
if pubkey_hex in pubkeys:
|
if pubkey_hex in pubkeys:
|
||||||
try:
|
if not public_key.verify_message_hash(sig_string, pre_hash):
|
||||||
public_key.verify_message_hash(sig_string, pre_hash)
|
|
||||||
except Exception:
|
|
||||||
_logger.exception('')
|
|
||||||
continue
|
continue
|
||||||
_logger.info(f"adding sig: txin_idx={i}, signing_pubkey={pubkey_hex}, sig={sig}")
|
_logger.info(f"adding sig: txin_idx={i}, signing_pubkey={pubkey_hex}, sig={sig}")
|
||||||
self.add_signature_to_txin(txin_idx=i, signing_pubkey=pubkey_hex, sig=sig)
|
self.add_signature_to_txin(txin_idx=i, signing_pubkey=pubkey_hex, sig=sig)
|
||||||
|
|||||||
Reference in New Issue
Block a user