feat: NWC use coincurve instead of secp (#3455)
This commit is contained in:
+16
-20
@@ -4,7 +4,7 @@ import json
|
||||
import re
|
||||
from urllib.parse import urlparse
|
||||
|
||||
import secp256k1
|
||||
import coincurve
|
||||
from bech32 import bech32_decode, bech32_encode, convertbits
|
||||
from Cryptodome import Random
|
||||
from Cryptodome.Cipher import AES
|
||||
@@ -19,22 +19,22 @@ def generate_keypair() -> tuple[str, str]:
|
||||
|
||||
|
||||
def encrypt_content(
|
||||
content: str, service_pubkey: secp256k1.PublicKey, account_private_key_hex: str
|
||||
content: str, service_pubkey: coincurve.PublicKey, account_private_key_hex: str
|
||||
) -> str:
|
||||
"""
|
||||
Encrypts the content to be sent to the service.
|
||||
|
||||
Args:
|
||||
content (str): The content to be encrypted.
|
||||
service_pubkey (secp256k1.PublicKey): The service provider's public key.
|
||||
service_pubkey (coincurve.PublicKey): The service provider's public key.
|
||||
account_private_key_hex (str): The account private key in hex format.
|
||||
|
||||
Returns:
|
||||
str: The encrypted content.
|
||||
"""
|
||||
shared = service_pubkey.tweak_mul(
|
||||
bytes.fromhex(account_private_key_hex)
|
||||
).serialize()[1:]
|
||||
shared = service_pubkey.multiply(bytes.fromhex(account_private_key_hex)).format()[
|
||||
1:
|
||||
]
|
||||
# random iv (16B)
|
||||
iv = Random.new().read(AES.block_size)
|
||||
aes = AES.new(shared, AES.MODE_CBC, iv)
|
||||
@@ -52,22 +52,22 @@ def encrypt_content(
|
||||
|
||||
|
||||
def decrypt_content(
|
||||
content: str, service_pubkey: secp256k1.PublicKey, account_private_key_hex: str
|
||||
content: str, service_pubkey: coincurve.PublicKey, account_private_key_hex: str
|
||||
) -> str:
|
||||
"""
|
||||
Decrypts the content coming from the service.
|
||||
|
||||
Args:
|
||||
content (str): The encrypted content.
|
||||
service_pubkey (secp256k1.PublicKey): The service provider's public key.
|
||||
service_pubkey (coincurve.PublicKey): The service provider's public key.
|
||||
account_private_key_hex (str): The account private key in hex format.
|
||||
|
||||
Returns:
|
||||
str: The decrypted content.
|
||||
"""
|
||||
shared = service_pubkey.tweak_mul(
|
||||
bytes.fromhex(account_private_key_hex)
|
||||
).serialize()[1:]
|
||||
shared = service_pubkey.multiply(bytes.fromhex(account_private_key_hex)).format()[
|
||||
1:
|
||||
]
|
||||
# extract iv and content
|
||||
(encrypted_content_b64, iv_b64) = content.split("?iv=")
|
||||
encrypted_content = base64.b64decode(encrypted_content_b64.encode("ascii"))
|
||||
@@ -105,16 +105,14 @@ def verify_event(event: dict) -> bool:
|
||||
if event_id != event["id"]:
|
||||
return False
|
||||
pubkey_hex = event["pubkey"]
|
||||
pubkey = secp256k1.PublicKey(bytes.fromhex("02" + pubkey_hex), True)
|
||||
if not pubkey.schnorr_verify(
|
||||
bytes.fromhex(event_id), bytes.fromhex(event["sig"]), None, raw=True
|
||||
):
|
||||
pubkey = coincurve.PublicKeyXOnly(bytes.fromhex(pubkey_hex))
|
||||
if not pubkey.verify(bytes.fromhex(event["sig"]), bytes.fromhex(event_id)):
|
||||
return False
|
||||
return True
|
||||
|
||||
|
||||
def sign_event(
|
||||
event: dict, account_public_key_hex: str, account_private_key: secp256k1.PrivateKey
|
||||
event: dict, account_public_key_hex: str, account_private_key: coincurve.PrivateKey
|
||||
) -> dict:
|
||||
"""
|
||||
Signs the event (in place) with the service secret
|
||||
@@ -122,7 +120,7 @@ def sign_event(
|
||||
Args:
|
||||
event (Dict): The event to be signed.
|
||||
account_public_key_hex (str): The account public key in hex format.
|
||||
account_private_key (secp256k1.PrivateKey): The account private key.
|
||||
account_private_key (coincurve.PrivateKey): The account private key.
|
||||
|
||||
Returns:
|
||||
Dict: The input event with the signature added.
|
||||
@@ -141,9 +139,7 @@ def sign_event(
|
||||
event["id"] = event_id
|
||||
event["pubkey"] = account_public_key_hex
|
||||
|
||||
signature = (
|
||||
account_private_key.schnorr_sign(bytes.fromhex(event_id), None, raw=True)
|
||||
).hex()
|
||||
signature = account_private_key.sign_schnorr(bytes.fromhex(event_id)).hex()
|
||||
event["sig"] = signature
|
||||
return event
|
||||
|
||||
|
||||
@@ -7,8 +7,8 @@ from collections.abc import AsyncGenerator
|
||||
from typing import cast
|
||||
from urllib.parse import parse_qs, unquote, urlparse
|
||||
|
||||
import secp256k1
|
||||
from bolt11 import decode as bolt11_decode
|
||||
from coincurve import PrivateKey, PublicKey
|
||||
from loguru import logger
|
||||
from websockets import connect as ws_connect
|
||||
|
||||
@@ -306,15 +306,15 @@ class NWCConnection:
|
||||
# Parse pairing url (if invalid an exception is raised)
|
||||
|
||||
# Extract keys (used to sign nwc events+identify NWC user)
|
||||
self.account_private_key = secp256k1.PrivateKey(bytes.fromhex(secret))
|
||||
self.account_private_key = PrivateKey(bytes.fromhex(secret))
|
||||
self.account_private_key_hex = secret
|
||||
self.account_public_key = self.account_private_key.pubkey
|
||||
self.account_public_key = self.account_private_key.public_key
|
||||
if not self.account_public_key:
|
||||
raise ValueError("Missing account public key")
|
||||
self.account_public_key_hex = self.account_public_key.serialize().hex()[2:]
|
||||
self.account_public_key_hex = self.account_public_key.format().hex()[2:]
|
||||
|
||||
# Extract service key (used for encryption to identify the nwc service provider)
|
||||
self.service_pubkey = secp256k1.PublicKey(bytes.fromhex("02" + pubkey), True)
|
||||
self.service_pubkey = PublicKey(bytes.fromhex("02" + pubkey))
|
||||
self.service_pubkey_hex = pubkey
|
||||
|
||||
# Extract relay url
|
||||
|
||||
Reference in New Issue
Block a user