Aller au contenu
ecdsa.com
C

libsecp256k1 (Bitcoin Core)

La libsecp256k1 de Bitcoin Core, l’implémentation de référence de secp256k1 : des signatures opaques sérialisées au format compact ou en DER strict, des nonces RFC 6979, low-S à la signature comme à la vérification, la récupération de la clé publique et Schnorr BIP-340.
Licence
MIT
Version vérifiée
0.5.0 ·
Code source
github.com/bitcoin-core/secp256k1
Documentation
github.com/bitcoin-core/secp256k1/tree/master/include

Compatibilité en un coup d’œil

ECDSA

Courbes ECDSA
secp256k1
EdDSA
none
Formats de clé publique
sec1-uncompressedsec1-compressed

libsecp256k1 ne gère qu’une seule courbe, et la gère avec soin : signature en temps constant, tests exhaustifs et vérification formelle d’une partie de l’arithmétique du corps. Bitcoin Core, de nombreux portefeuilles et des bindings comme coincurve ou le backend cgo de go-ethereum l’utilisent. Les faits présentés ici ont été vérifiés sur le paquet Alpine 3.24.2, version 0.5.0 avec les modules recovery, extrakeys et schnorrsig ; le projet amont en était alors à la 0.8.0.

Les signatures résident dans une structure opaque ; l’encodage est choisi au moment de la sérialisation (serialize_compact donne 64 octets de r‖s, serialize_der donne du DER), et l’analyse est tout aussi explicite. parse_der est strict : il a accepté le DER minimal et refusé la même signature comportant un zéro initial superflu dans r, conformément aux règles BIP-66 de Bitcoin. Les anciennes signatures non strictes nécessitent l’analyseur permissif conservé dans le répertoire contrib du dépôt.

La signature prend un condensat de 32 octets (la bibliothèque ne hache jamais votre message), utilise la RFC 6979 (le résultat des essais correspondait à celui d’une implémentation indépendante) et produit toujours du low-S. La vérification est la partie stricte : la forme high-S symétrique d’une signature valide a échoué, et secp256k1_ecdsa_signature_normalize existe précisément pour corriger les signatures issues de signataires généralistes. Quand une signature produite par OpenSSL, Java ou un KMS cloud ne passe la vérification qu’environ une fois sur deux, c’est le high-S qui en est la cause ; l’exemple normalise avant de vérifier.

Les clés publiques sont des points SEC1, de 65 octets en forme non compressée ou de 33 en forme compressée ; un x‖y de 64 octets est refusé, et SPKI, PEM et JWK ne sont pas pris en charge. Le module recovery récupère la clé publique à partir d’une signature, mécanisme sur lequel reposent les adresses Ethereum, et le module schnorrsig implémente BIP-340. Vérifiez les signatures de messages Bitcoin avec le vérificateur de messages Bitcoin et les événements Nostr avec le vérificateur d’événements Nostr.

Les faits et les vérifications qui les fondent

Rien de ce qui suit n’est copié d’une documentation. La vérification content/libraries/verify/libsecp256k1/run.sh a exécuté libsecp256k1 (Bitcoin Core) 0.5.0 dans l’image de conteneur alpine:3.24.2 le 20 octobre 2026 ; la page doit correspondre à ce que la vérification a affiché, et chaque fait cite les lignes en question.

  • Encodage des signatures ECDSA

    structured

    Les signatures sont une structure opaque ; serialize_compact écrit 64 octets de r‖s et serialize_der écrit du DER.

    Relevé par la vérification
    fact ecdsa-signature-encoding = structured
    evidence ecdsa-signature-encoding: secp256k1_ecdsa_signature is opaque: serialize_compact gives 64 bytes, serialize_der 70 bytes
  • Nonce ECDSA

    rfc6979

    RFC 6979 : la signature était identique à celle d’une implémentation indépendante de la RFC 6979, avec s normalisé.

    Relevé par la vérification
    fact ecdsa-nonce = rfc6979
    evidence ecdsa-nonce: two signatures identical: yes; equal to the reference RFC 6979 signature with s normalized: yes
  • Low-S à la signature

    low-s

    Aucune valeur high-S parmi 64 signatures.

    Relevé par la vérification
    fact ecdsa-low-s-sign = low-s
  • High-S à la vérification

    rejects-high-s

    secp256k1_ecdsa_verify a rejeté (r, n − s) ; après secp256k1_ecdsa_signature_normalize, la signature a passé la vérification.

    Relevé par la vérification
    fact ecdsa-low-s-verify = rejects-high-s
    evidence ecdsa-low-s-verify: verify on (r, n - s): 0; signature_normalize reports it was high: 1; normalized verifies: 1
  • Ce que l’appel de signature hache

    takes-digest

    secp256k1_ecdsa_sign prend un condensat de message de 32 octets.

    Relevé par la vérification
    fact ecdsa-prehash = takes-digest
    evidence ecdsa-prehash: secp256k1_ecdsa_sign takes a 32-byte message hash (msghash32)
  • Fonction de hachage par défaut

    none

    Aucun hachage n’a lieu pendant la signature ni pendant la vérification.

    Relevé par la vérification
    fact ecdsa-default-hash = none
  • Rigueur de l’analyse DER

    rejects-non-minimal

    parse_der a accepté le DER minimal et refusé un encodage comportant un octet nul initial superflu dans r.

    Relevé par la vérification
    fact der-strictness = rejects-non-minimal
    evidence der-strictness: parse_der on minimal DER: 1; with a redundant leading zero in r: 0
  • Signature ECDSA mal formée

    returns-error

    Une entrée mal formée échoue dès l’étape d’analyse (parse_der renvoie 0) ; verify ne reçoit que des signatures déjà analysées.

    Relevé par la vérification
    fact ecdsa-malformed-signature = returns-error
    evidence ecdsa-malformed-signature: parsing is a separate call: parse_der on 3 bytes returned 0; verify only ever sees parsed signatures
  • Courbes ECDSA

    secp256k1

    secp256k1 uniquement.

    Relevé par la vérification
    fact ecdsa-curves = secp256k1
  • Récupération de la clé publique

    supported

    Le module recovery a récupéré la clé publique du signataire à partir d’une signature récupérable.

    Relevé par la vérification
    fact public-key-recovery = supported
  • Signatures Schnorr

    BIP-340

    Le module schnorrsig a produit une signature BIP-340 et l’a vérifiée avec la clé publique x-only.

    Relevé par la vérification
    fact schnorr = BIP-340
  • EdDSA

    none

    Pas d’EdDSA.

    Relevé par la vérification
    fact eddsa = none
  • Algorithmes post-quantiques

    none

    Aucun algorithme post-quantique.

    Relevé par la vérification
    fact pqc = none
  • Formats de clé publique

    sec1-uncompressedsec1-compressed

    ec_pubkey_parse accepte les points SEC1 de 65 octets en forme non compressée et de 33 octets en forme compressée ; un x‖y de 64 octets a été refusé.

    Relevé par la vérification
    fact public-key-formats = sec1-uncompressed, sec1-compressed
    evidence public-key-formats: ec_pubkey_parse: 65-byte uncompressed 1, 33-byte compressed 1, 64-byte x||y 0
  • Formats de clé privée

    raw-scalar

    Les clés secrètes sont des scalaires bruts de 32 octets.

    Relevé par la vérification
    fact private-key-formats = raw-scalar

Utilisation

Signer un condensat de 32 octets, analyser une signature telle qu’elle arriverait d’ailleurs, normaliser s et vérifier.
#include <secp256k1.h>
#include <stdio.h>
#include <string.h>

int main(void) {
    secp256k1_context *ctx = secp256k1_context_create(SECP256K1_CONTEXT_NONE);
    unsigned char seckey[32], msghash[32];
    memset(seckey, 0x11, 32);   /* use a random 32-byte key in real code */
    memset(msghash, 0x22, 32);  /* the caller hashes: libsecp256k1 signs a 32-byte digest */

    secp256k1_pubkey pub;
    secp256k1_ecdsa_signature sig;
    if (!secp256k1_ec_pubkey_create(ctx, &pub, seckey) || !secp256k1_ecdsa_sign(ctx, &sig, msghash, seckey, NULL, NULL)) return 1;

    /* A signature from elsewhere: parse (strict DER or 64-byte compact), then normalize s,
       because verify rejects high-S signatures that OpenSSL or Java produce half the time. */
    unsigned char compact[64];
    secp256k1_ecdsa_signature_serialize_compact(ctx, compact, &sig);
    secp256k1_ecdsa_signature parsed;
    if (!secp256k1_ecdsa_signature_parse_compact(ctx, &parsed, compact)) return 1;
    secp256k1_ecdsa_signature_normalize(ctx, &parsed, &parsed);

    printf("verified: %d\n", secp256k1_ecdsa_verify(ctx, &parsed, msghash, &pub));
    secp256k1_context_destroy(ctx);
    return 0;
}

Le banc de test des extraits de code exécute chaque exemple tel quel dans l’image de conteneur alpine:3.24.2, et l’exécution échoue si la sortie ne correspond pas.

Mise en place
apk add --no-cache -q build-base libsecp256k1-dev=0.5.0-r1 >/dev/null
Commande
cc -o usage usage.c -lsecp256k1 && ./usage
La sortie doit correspondre à cette expression régulière
verified: 1

Bibliothèques