From 92b6ccf83ce0eded405de9fb5e81eaf3f4c29e16 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?dni=20=E2=9A=A1?= Date: Wed, 28 Jan 2026 12:46:51 +0100 Subject: [PATCH] feat: use coincurve instead of ecdsa for lnurlauth --- lnurl/helpers.py | 33 ++++++++++++++------------------- pyproject.toml | 2 +- uv.lock | 25 ++----------------------- 3 files changed, 17 insertions(+), 43 deletions(-) diff --git a/lnurl/helpers.py b/lnurl/helpers.py index fbc84a9..613e2be 100644 --- a/lnurl/helpers.py +++ b/lnurl/helpers.py @@ -5,10 +5,9 @@ from bech32 import bech32_decode, bech32_encode, convertbits from bip32 import BIP32 +from coincurve import PrivateKey, PublicKey from Cryptodome import Random from Cryptodome.Cipher import AES -from ecdsa import SECP256k1, SigningKey, VerifyingKey -from ecdsa.util import sigdecode_der, sigencode_der from .exceptions import InvalidLnurl, InvalidUrl @@ -67,11 +66,9 @@ def lnurlauth_signature(k1: str, linking_key: bytes) -> tuple[str, str]: Obtain the linking_key from lnurlauth_derive_linking_key or lnurlauth_derive_linking_key_sign_message. """ - auth_key = SigningKey.from_string(linking_key, curve=SECP256k1, hashfunc=sha256) - sig = auth_key.sign_digest_deterministic(bytes.fromhex(k1), sigencode=sigencode_der) - if not auth_key.verifying_key: - raise ValueError("LNURLauth verifying_key does not exist") - key = auth_key.verifying_key.to_string("compressed") + auth_key = PrivateKey(linking_key) + sig = auth_key.sign(bytes.fromhex(k1), hasher=None) + key = auth_key.public_key.format(compressed=True) return key.hex(), sig.hex() @@ -80,12 +77,13 @@ def lnurlauth_verify(k1: str, key: str, sig: str) -> bool: Verify a k1 with a key and a signature. """ try: - verifying_key = VerifyingKey.from_string(bytes.fromhex(key), hashfunc=sha256, curve=SECP256k1) - verifying_key.verify_digest(bytes.fromhex(sig), bytes.fromhex(k1), sigdecode=sigdecode_der) - return True - except Exception as exc: - print(exc) + key_bytes = bytes.fromhex(key) + sig_bytes = bytes.fromhex(sig) + digest = bytes.fromhex(k1) + except ValueError: return False + verifying_key = PublicKey(key_bytes) + return verifying_key.verify(sig_bytes, digest, hasher=None) # LUD-05: BIP32-based seed generation for auth protocol. @@ -138,13 +136,10 @@ def lnurlauth_derive_linking_key_sign_message(domain: str, sig: bytes) -> tuple[ Derive a key from a from signMessage from a node. param `sig` is a RFC6979 deterministic signature. """ - hashing_key = SigningKey.from_string(sha256(sig).digest(), curve=SECP256k1, hashfunc=sha256) - linking_key = SigningKey.from_string( - hmac.digest(hashing_key.to_string(), domain.encode(), "sha256"), curve=SECP256k1, hashfunc=sha256 - ) - assert linking_key.privkey and linking_key.verifying_key - pubkey = linking_key.verifying_key.to_string("compressed") - return linking_key.privkey, pubkey + hashing_key = PrivateKey(sha256(sig).digest()) + linking_key = PrivateKey(hmac.digest(hashing_key.secret, domain.encode(), "sha256")) + pubkey = linking_key.public_key.format(compressed=True) + return linking_key.secret, pubkey def _bech32_decode(bech32: str, *, allowed_hrp: Optional[Set[str]] = None) -> Tuple[str, List[int]]: diff --git a/pyproject.toml b/pyproject.toml index 8ca7749..1de4ec5 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -12,9 +12,9 @@ dependencies = [ "pycryptodomex>=3.21.0", "bip32>=5.0.0", "bech32", - "ecdsa", "bolt11", "httpx", + "coincurve>=20.0.0", ] [dependency-groups] diff --git a/uv.lock b/uv.lock index f92e1bb..7582975 100644 --- a/uv.lock +++ b/uv.lock @@ -363,18 +363,6 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/33/6b/e0547afaf41bf2c42e52430072fa5658766e3d65bd4b03a563d1b6336f57/distlib-0.4.0-py2.py3-none-any.whl", hash = "sha256:9659f7d87e46584a30b5780e43ac7a2143098441670ff0a49d5f9034c54a6c16", size = 469047, upload-time = "2025-07-17T16:51:58.613Z" }, ] -[[package]] -name = "ecdsa" -version = "0.19.1" -source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "six" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/c0/1f/924e3caae75f471eae4b26bd13b698f6af2c44279f67af317439c2f4c46a/ecdsa-0.19.1.tar.gz", hash = "sha256:478cba7b62555866fcb3bb3fe985e06decbdb68ef55713c4e5ab98c57d508e61", size = 201793, upload-time = "2025-03-13T11:52:43.25Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/cb/a3/460c57f094a4a165c84a1341c373b0a4f5ec6ac244b998d5021aade89b77/ecdsa-0.19.1-py2.py3-none-any.whl", hash = "sha256:30638e27cf77b7e15c4c4cc1973720149e1033827cfd00661ca5c8cc0cdb24c3", size = 150607, upload-time = "2025-03-13T11:52:41.757Z" }, -] - [[package]] name = "exceptiongroup" version = "1.3.1" @@ -508,7 +496,7 @@ dependencies = [ { name = "bech32" }, { name = "bip32" }, { name = "bolt11" }, - { name = "ecdsa" }, + { name = "coincurve" }, { name = "httpx" }, { name = "pycryptodomex" }, { name = "pydantic" }, @@ -531,7 +519,7 @@ requires-dist = [ { name = "bech32" }, { name = "bip32", specifier = ">=5.0.0" }, { name = "bolt11" }, - { name = "ecdsa" }, + { name = "coincurve", specifier = ">=20.0.0" }, { name = "httpx" }, { name = "pycryptodomex", specifier = ">=3.21.0" }, { name = "pydantic", specifier = ">=1.10.0,<2.0.0" }, @@ -854,15 +842,6 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/9e/6a/40fee331a52339926a92e17ae748827270b288a35ef4a15c9c8f2ec54715/ruff-0.14.14-py3-none-win_arm64.whl", hash = "sha256:56e6981a98b13a32236a72a8da421d7839221fa308b223b9283312312e5ac76c", size = 10920448, upload-time = "2026-01-22T22:30:15.417Z" }, ] -[[package]] -name = "six" -version = "1.17.0" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/94/e7/b2c673351809dca68a0e064b6af791aa332cf192da575fd474ed7d6f16a2/six-1.17.0.tar.gz", hash = "sha256:ff70335d468e7eb6ec65b95b99d3a2836546063f63acc5171de367e834932a81", size = 34031, upload-time = "2024-12-04T17:35:28.174Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/b7/ce/149a00dd41f10bc29e5921b496af8b574d8413afcd5e30dfa0ed46c2cc5e/six-1.17.0-py2.py3-none-any.whl", hash = "sha256:4721f391ed90541fddacab5acf947aa0d3dc7d27b2e1e8eda2be8970586c3274", size = 11050, upload-time = "2024-12-04T17:35:26.475Z" }, -] - [[package]] name = "tomli" version = "2.4.0"