Saltar al contenido
ecdsa.com
C

libsecp256k1 (Bitcoin Core)

libsecp256k1 de Bitcoin Core, la implementación de referencia de secp256k1: firmas opacas que se serializan en formato compacto o en DER estricto, nonces RFC 6979, low-S al firmar y al verificar, recuperación de la clave pública y Schnorr BIP-340.
Licencia
MIT
Versión comprobada
0.5.0 ·
Código fuente
github.com/bitcoin-core/secp256k1
Documentación
github.com/bitcoin-core/secp256k1/tree/master/include

Compatibilidad de un vistazo

ECDSA

Curvas ECDSA
secp256k1
EdDSA
none
Formatos de clave pública
sec1-uncompressedsec1-compressed

libsecp256k1 implementa una sola curva y lo hace con cuidado: firma de tiempo constante, pruebas exhaustivas y verificación formal de partes de la aritmética del cuerpo. La usan Bitcoin Core, muchas billeteras y bindings como coincurve y el backend cgo de go-ethereum. Los datos de esta página se comprobaron con el paquete de Alpine 3.24.2, versión 0.5.0 con los módulos recovery, extrakeys y schnorrsig; la última versión del proyecto era entonces la 0.8.0.

Las firmas se guardan en una estructura opaca; la codificación se elige al serializar —serialize_compact da 64 bytes de r‖s y serialize_der da DER— y el análisis es igual de explícito. parse_der es estricto: aceptó DER mínimo y rechazó la misma firma con un cero inicial redundante en r, según las reglas BIP-66 de Bitcoin. Las firmas históricas laxas necesitan el analizador laxo que se conserva en el directorio contrib del repositorio.

La operación de firma recibe un hash de 32 bytes —la biblioteca nunca calcula el hash de tu mensaje—, usa RFC 6979 (en la comprobación coincidió con una implementación independiente) y siempre produce firmas low-S. La verificación es la parte estricta: la forma high-S reflejada de una firma válida falló, y secp256k1_ecdsa_signature_normalize existe precisamente para corregir las firmas de firmantes de propósito general. Cuando las firmas de OpenSSL, Java o un KMS en la nube se verifican solo aproximadamente la mitad de las veces, la causa es high-S; el ejemplo normaliza antes de verificar.

Las claves públicas son puntos SEC1, de 65 bytes sin comprimir o de 33 comprimidos; un x‖y de 64 bytes se rechaza, y no hay compatibilidad con SPKI, PEM ni JWK. El módulo recovery recupera la clave pública a partir de una firma —la base de las direcciones de Ethereum— y el módulo schnorrsig implementa BIP-340. Comprueba firmas de mensajes de Bitcoin con el verificador de mensajes de Bitcoin y eventos de Nostr con el verificador de eventos de Nostr.

Datos y las comprobaciones que los respaldan

Nada de lo que sigue está copiado de la documentación. La comprobación content/libraries/verify/libsecp256k1/run.sh ejecutó libsecp256k1 (Bitcoin Core) 0.5.0 en la imagen de contenedor alpine:3.24.2 el 20 de octubre de 2026; la página debe coincidir con lo que imprimió, y cada dato cita esas líneas.

  • Codificación de la firma ECDSA

    structured

    Las firmas son una estructura opaca; serialize_compact escribe 64 bytes de r‖s y serialize_der escribe DER.

    Registrado por la comprobación
    fact ecdsa-signature-encoding = structured
    evidence ecdsa-signature-encoding: secp256k1_ecdsa_signature is opaque: serialize_compact gives 64 bytes, serialize_der 70 bytes
  • Nonce de ECDSA

    rfc6979

    RFC 6979: la firma coincidió con la de una implementación independiente del RFC 6979, con s normalizado.

    Registrado por la comprobación
    fact ecdsa-nonce = rfc6979
    evidence ecdsa-nonce: two signatures identical: yes; equal to the reference RFC 6979 signature with s normalized: yes
  • Low-S al firmar

    low-s

    Ningún valor high-S entre 64 firmas.

    Registrado por la comprobación
    fact ecdsa-low-s-sign = low-s
  • High-S al verificar

    rejects-high-s

    secp256k1_ecdsa_verify rechazó (r, n − s); tras secp256k1_ecdsa_signature_normalize, se verificó.

    Registrado por la comprobación
    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
  • De qué calcula el hash la llamada de firma

    takes-digest

    secp256k1_ecdsa_sign recibe un hash de 32 bytes del mensaje.

    Registrado por la comprobación
    fact ecdsa-prehash = takes-digest
    evidence ecdsa-prehash: secp256k1_ecdsa_sign takes a 32-byte message hash (msghash32)
  • Hash predeterminado

    none

    No se calcula ningún hash al firmar ni al verificar.

    Registrado por la comprobación
    fact ecdsa-default-hash = none
  • Rigor del análisis DER

    rejects-non-minimal

    parse_der aceptó DER mínimo y rechazó una codificación con un byte cero inicial redundante en r.

    Registrado por la comprobación
    fact der-strictness = rejects-non-minimal
    evidence der-strictness: parse_der on minimal DER: 1; with a redundant leading zero in r: 0
  • Firma ECDSA mal formada

    returns-error

    Una entrada mal formada falla en el paso de análisis (parse_der devuelve 0); verify solo recibe firmas ya analizadas.

    Registrado por la comprobación
    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
  • Curvas ECDSA

    secp256k1

    Solo secp256k1.

    Registrado por la comprobación
    fact ecdsa-curves = secp256k1
  • Recuperación de la clave pública

    supported

    El módulo recovery recuperó la clave pública del firmante a partir de una firma recuperable.

    Registrado por la comprobación
    fact public-key-recovery = supported
  • Firmas Schnorr

    BIP-340

    El módulo schnorrsig produjo una firma BIP-340 y la verificó con la clave pública x-only.

    Registrado por la comprobación
    fact schnorr = BIP-340
  • EdDSA

    none

    Sin EdDSA.

    Registrado por la comprobación
    fact eddsa = none
  • Algoritmos poscuánticos

    none

    Sin algoritmos poscuánticos.

    Registrado por la comprobación
    fact pqc = none
  • Formatos de clave pública

    sec1-uncompressedsec1-compressed

    ec_pubkey_parse acepta puntos SEC1 en formato sin comprimir (65 bytes) y en formato comprimido (33 bytes); un x‖y de 64 bytes se rechazó.

    Registrado por la comprobación
    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
  • Formatos de clave privada

    raw-scalar

    Las claves secretas son escalares en bruto de 32 bytes.

    Registrado por la comprobación
    fact private-key-formats = raw-scalar

Uso

Firma un hash de 32 bytes, analiza una firma tal como llegaría desde otro sistema, normaliza s y verifica.
#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;
}

El arnés de pruebas de fragmentos de código ejecuta cada ejemplo tal cual en la imagen de contenedor alpine:3.24.2, y la ejecución falla si la salida no coincide.

Configuración
apk add --no-cache -q build-base libsecp256k1-dev=0.5.0-r1 >/dev/null
Comando
cc -o usage usage.c -lsecp256k1 && ./usage
La salida debe coincidir (expresión regular)
verified: 1

Bibliotecas