coincurve enveloppe libsecp256k1 avec CFFI et distribue des paquets wheel précompilés, si bien que le code Python obtient, sans compilateur, la même implémentation de secp256k1 en temps constant que celle de Bitcoin Core. La version 21.0.0 ne publie des wheels que pour CPython 3.10 à 3.13 ; sous Python 3.14, pip se rabat sur un build depuis les sources, qui échoue dans les images slim dépourvues de chaîne de compilation C. C’est pourquoi les essais ont été menés sous Python 3.12.
Les valeurs par défaut conviennent à Bitcoin plutôt qu’à Ethereum. sign() hache le message avec SHA-256, utilise un nonce RFC 6979 (le résultat des essais correspondait à celui d’un signataire de référence indépendant) et renvoie une signature DER dont s est déjà dans la moitié inférieure. Ethereum, Nostr et la plupart des portefeuilles hachent avec keccak-256 ou signent un condensat de 32 octets : passez donc un hasher ou hasher=None ; conserver la valeur par défaut produit des signatures d’apparence valide, mais calculées sur le mauvais condensat.
La vérification hérite de la rigueur de libsecp256k1. verify() a renvoyé False pour la forme high-S symétrique d’une signature valide : une signature issue d’OpenSSL, de Java ou d’un KMS cloud échoue environ une fois sur deux si s n’est pas normalisé au préalable. Une entrée qui n’est pas en DER lève ValueError: The DER-encoded signature could not be parsed. au lieu de renvoyer False ; les signatures brutes de 64 octets doivent être converties en encodage DER avant d’être passées à verify(), et le convertisseur DER ⇄ brut montre les deux formes.
Au-delà de l’ECDSA simple, sign_recoverable() renvoie la forme r ‖ s ‖ identifiant de récupération de 65 octets et PublicKey.from_signature_and_message() récupère la clé, ce qui permet de dériver des adresses Ethereum à partir de signatures ; sign_schnorr() produit des signatures BIP-340 pour Taproot et Nostr. Les clés publiques sont des points SEC1, compressés par défaut ; les clés privées sont des scalaires bruts de 32 octets, et to_pem()/to_der() écrivent du PKCS#8.
L’exemple montre les deux modes : SHA-256 par défaut, et un condensat keccak signé de façon récupérable, comme l’attend Ethereum. Contrôlez les résultats avec le vérificateur de messages Bitcoin ou le vérificateur de signatures Ethereum.