From ac201c718e378a383a7ec2d05f5f5cec68fea8f6 Mon Sep 17 00:00:00 2001 From: Lex Berezhny Date: Sat, 1 Jan 2022 13:13:03 -0500 Subject: [PATCH] drop dependency on cryptography library in wallet module --- lbry/wallet/bip32.py | 33 ++++++++++++++++++++++++++++++--- lbry/wallet/transaction.py | 24 ++++-------------------- 2 files changed, 34 insertions(+), 23 deletions(-) diff --git a/lbry/wallet/bip32.py b/lbry/wallet/bip32.py index acf0190e0..ff788d0d0 100644 --- a/lbry/wallet/bip32.py +++ b/lbry/wallet/bip32.py @@ -92,6 +92,10 @@ class PublicKey(_KeyBase): else: 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 def _verifying_key_from_pubkey(cls, pubkey): """ Converts a 33-byte compressed pubkey into an coincurve.PublicKey object. """ @@ -137,9 +141,32 @@ class PublicKey(_KeyBase): self.pubkey_bytes ) - def verify(self, signature, data): - """ Produce a signature for piece of data by double hashing it and signing the hash. """ - return self.verifying_key.verify(signature, data, hasher=double_sha256) + def verify(self, signature, data) -> bool: + """ Verify that a signature is valid for data. """ + + if len(signature) != 64: + raise Exception('invalid signature length') + + 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, data, key.public_key + ) + + return bool(verified) class PrivateKey(_KeyBase): diff --git a/lbry/wallet/transaction.py b/lbry/wallet/transaction.py index 67839aa58..a6f3bf376 100644 --- a/lbry/wallet/transaction.py +++ b/lbry/wallet/transaction.py @@ -4,13 +4,6 @@ import typing from binascii import hexlify, unhexlify 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.crypto.hash import hash160, sha256 from lbry.crypto.base58 import Base58 @@ -25,7 +18,7 @@ from .constants import COIN, NULL_HASH32 from .bcd_data_stream import BCDataStream from .hash import TXRef, TXRefImmutable from .util import ReadOnlyList -from .bip32 import PrivateKey +from .bip32 import PrivateKey, PublicKey if typing.TYPE_CHECKING: from lbry.wallet.account import Account @@ -426,18 +419,9 @@ class Output(InputOutput): @staticmethod def is_signature_valid(signature, digest, public_key_bytes): - signature = cdata_to_der(deserialize_compact(signature)) - public_key = cPublicKey(public_key_bytes) - is_valid = public_key.verify(signature, digest, None) - 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 + return PublicKey\ + .from_compressed(public_key_bytes)\ + .verify(signature, digest) def is_signed_by(self, channel: 'Output', ledger=None): return self.is_signature_valid(