Merge pull request #3534 from lbryio/normalize_signatures

drop dependency on cryptography library in wallet module
This commit is contained in:
Lex Berezhny 2022-01-17 13:38:20 -05:00 committed by GitHub
commit b5ead91746
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
2 changed files with 37 additions and 23 deletions

View file

@ -92,6 +92,10 @@ class PublicKey(_KeyBase):
else: else:
self.verifying_key = self._verifying_key_from_pubkey(pubkey) self.verifying_key = self._verifying_key_from_pubkey(pubkey)
@classmethod
def from_compressed(cls, public_key_bytes, ledger=None) -> 'PublicKey':
return cls(ledger, public_key_bytes, bytes((0,)*32), 0, 0)
@classmethod @classmethod
def _verifying_key_from_pubkey(cls, pubkey): def _verifying_key_from_pubkey(cls, pubkey):
""" Converts a 33-byte compressed pubkey into an coincurve.PublicKey object. """ """ Converts a 33-byte compressed pubkey into an coincurve.PublicKey object. """
@ -137,9 +141,35 @@ class PublicKey(_KeyBase):
self.pubkey_bytes self.pubkey_bytes
) )
def verify(self, signature, data): def verify(self, signature, digest) -> bool:
""" Produce a signature for piece of data by double hashing it and signing the hash. """ """ Verify that a signature is valid for a 32 byte digest. """
return self.verifying_key.verify(signature, data, hasher=double_sha256)
if len(signature) != 64:
raise ValueError('Signature must be 64 bytes long.')
if len(digest) != 32:
raise ValueError('Digest must be 32 bytes long.')
key = self.verifying_key
raw_signature = libsecp256k1_ffi.new('secp256k1_ecdsa_signature *')
parsed = libsecp256k1.secp256k1_ecdsa_signature_parse_compact(
key.context.ctx, raw_signature, signature
)
assert parsed == 1
normalized_signature = libsecp256k1_ffi.new('secp256k1_ecdsa_signature *')
libsecp256k1.secp256k1_ecdsa_signature_normalize(
key.context.ctx, normalized_signature, raw_signature
)
verified = libsecp256k1.secp256k1_ecdsa_verify(
key.context.ctx, normalized_signature, digest, key.public_key
)
return bool(verified)
class PrivateKey(_KeyBase): class PrivateKey(_KeyBase):

View file

@ -4,13 +4,6 @@ import typing
from binascii import hexlify, unhexlify from binascii import hexlify, unhexlify
from typing import List, Iterable, Optional, Tuple from typing import List, Iterable, Optional, Tuple
from coincurve import PublicKey as cPublicKey
from coincurve.ecdsa import deserialize_compact, cdata_to_der
from cryptography.hazmat.primitives import hashes
from cryptography.hazmat.primitives.asymmetric import ec
from cryptography.hazmat.primitives.asymmetric.utils import Prehashed
from cryptography.exceptions import InvalidSignature
from lbry.error import InsufficientFundsError from lbry.error import InsufficientFundsError
from lbry.crypto.hash import hash160, sha256 from lbry.crypto.hash import hash160, sha256
from lbry.crypto.base58 import Base58 from lbry.crypto.base58 import Base58
@ -25,7 +18,7 @@ from .constants import COIN, NULL_HASH32
from .bcd_data_stream import BCDataStream from .bcd_data_stream import BCDataStream
from .hash import TXRef, TXRefImmutable from .hash import TXRef, TXRefImmutable
from .util import ReadOnlyList from .util import ReadOnlyList
from .bip32 import PrivateKey from .bip32 import PrivateKey, PublicKey
if typing.TYPE_CHECKING: if typing.TYPE_CHECKING:
from lbry.wallet.account import Account from lbry.wallet.account import Account
@ -426,18 +419,9 @@ class Output(InputOutput):
@staticmethod @staticmethod
def is_signature_valid(signature, digest, public_key_bytes): def is_signature_valid(signature, digest, public_key_bytes):
signature = cdata_to_der(deserialize_compact(signature)) return PublicKey\
public_key = cPublicKey(public_key_bytes) .from_compressed(public_key_bytes)\
is_valid = public_key.verify(signature, digest, None) .verify(signature, digest)
if not is_valid: # try old way
# ytsync signed claims don't seem to validate with coincurve
try:
pk = ec.EllipticCurvePublicKey.from_encoded_point(ec.SECP256K1(), public_key_bytes)
pk.verify(signature, digest, ec.ECDSA(Prehashed(hashes.SHA256())))
return True
except (ValueError, InvalidSignature):
pass
return is_valid
def is_signed_by(self, channel: 'Output', ledger=None): def is_signed_by(self, channel: 'Output', ledger=None):
return self.is_signature_valid( return self.is_signature_valid(