Pular para o conteúdo
ecdsa.com
C

libsecp256k1 (Bitcoin Core)

A libsecp256k1 do Bitcoin Core, a implementação de referência da secp256k1: assinaturas opacas serializadas em formato compacto ou DER estrito, nonces RFC 6979, low-S nos dois lados, recuperação da chave pública e Schnorr BIP-340.
Licença
MIT
Versão verificada
0.5.0 ·
Código-fonte
github.com/bitcoin-core/secp256k1
Documentação
github.com/bitcoin-core/secp256k1/tree/master/include

Visão geral da compatibilidade

ECDSA

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

A libsecp256k1 implementa uma única curva, e com cuidado: assinatura em tempo constante, testes exaustivos e verificação formal de partes da aritmética do corpo. Ela é usada pelo Bitcoin Core, por muitas carteiras e por bindings como o coincurve e o backend cgo do go-ethereum. Os fatos desta página foram verificados no pacote do Alpine 3.24.2, versão 0.5.0 com os módulos recovery, extrakeys e schnorrsig; na época, o projeto original já estava na 0.8.0.

As assinaturas ficam em uma struct opaca; a codificação é escolhida na serialização — serialize_compact gera 64 bytes de r‖s, serialize_der gera DER —, e a análise é igualmente explícita. parse_der é estrito: aceitou DER mínimo e recusou a mesma assinatura com um zero à esquerda redundante em r, seguindo as regras do BIP-66 do Bitcoin. Assinaturas históricas fora desse rigor precisam do parser permissivo mantido no diretório contrib do repositório.

Para assinar, a biblioteca recebe um hash de 32 bytes — ela nunca calcula o hash da sua mensagem —, usa a RFC 6979 (o resultado do teste coincidiu com o de uma implementação independente) e sempre produz low-S. A verificação é a parte estrita: a forma high-S espelhada de uma assinatura válida falhou, e secp256k1_ecdsa_signature_normalize existe justamente para corrigir assinaturas de signatários de uso geral. Quando uma assinatura vinda do OpenSSL, do Java ou de um KMS de nuvem passa na verificação em cerca de metade dos casos, a causa é high-S; o exemplo normaliza antes de verificar.

As chaves públicas são pontos SEC1, com 65 bytes na forma não comprimida ou 33 na forma comprimida; um x‖y de 64 bytes é recusado, e não há suporte a SPKI, PEM nem JWK. O módulo recovery recupera a chave pública a partir de uma assinatura — a base dos endereços Ethereum —, e o módulo schnorrsig implementa o BIP-340. Confira assinaturas de mensagens Bitcoin no verificador de mensagens Bitcoin e eventos Nostr no verificador de eventos Nostr.

Os fatos e as verificações por trás deles

Nada abaixo foi copiado da documentação. A verificação content/libraries/verify/libsecp256k1/run.sh executou libsecp256k1 (Bitcoin Core) 0.5.0 na imagem de contêiner alpine:3.24.2 em 20 de outubro de 2026; a página precisa corresponder ao que a verificação imprimiu, e cada fato lista essas linhas.

  • Codificação da assinatura ECDSA

    structured

    As assinaturas são uma struct opaca; serialize_compact grava 64 bytes de r‖s e serialize_der grava DER.

    Registrado pela verificação
    fact ecdsa-signature-encoding = structured
    evidence ecdsa-signature-encoding: secp256k1_ecdsa_signature is opaque: serialize_compact gives 64 bytes, serialize_der 70 bytes
  • Nonce do ECDSA

    rfc6979

    RFC 6979: a assinatura coincidiu com a de uma implementação independente da RFC 6979, com o s normalizado.

    Registrado pela verificação
    fact ecdsa-nonce = rfc6979
    evidence ecdsa-nonce: two signatures identical: yes; equal to the reference RFC 6979 signature with s normalized: yes
  • Low-S ao assinar

    low-s

    Nenhum valor high-S em 64 assinaturas.

    Registrado pela verificação
    fact ecdsa-low-s-sign = low-s
  • High-S ao verificar

    rejects-high-s

    secp256k1_ecdsa_verify rejeitou (r, n − s); depois de secp256k1_ecdsa_signature_normalize, a assinatura foi verificada.

    Registrado pela verificação
    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
  • Entrada do hash na chamada de assinatura

    takes-digest

    secp256k1_ecdsa_sign recebe o hash de 32 bytes da mensagem.

    Registrado pela verificação
    fact ecdsa-prehash = takes-digest
    evidence ecdsa-prehash: secp256k1_ecdsa_sign takes a 32-byte message hash (msghash32)
  • Hash padrão

    none

    Nenhum hash é calculado durante a assinatura ou a verificação.

    Registrado pela verificação
    fact ecdsa-default-hash = none
  • Rigor com a codificação DER

    rejects-non-minimal

    parse_der aceitou DER mínimo e recusou uma codificação com um byte zero à esquerda redundante em r.

    Registrado pela verificação
    fact der-strictness = rejects-non-minimal
    evidence der-strictness: parse_der on minimal DER: 1; with a redundant leading zero in r: 0
  • Assinatura ECDSA malformada

    returns-error

    Uma entrada malformada falha na etapa de análise (parse_der retorna 0); verify só recebe assinaturas já analisadas.

    Registrado pela verificação
    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

    Apenas secp256k1.

    Registrado pela verificação
    fact ecdsa-curves = secp256k1
  • Recuperação da chave pública

    supported

    O módulo recovery recuperou a chave pública do signatário a partir de uma assinatura recuperável.

    Registrado pela verificação
    fact public-key-recovery = supported
  • Assinaturas Schnorr

    BIP-340

    O módulo schnorrsig produziu e verificou uma assinatura BIP-340 com a chave pública x-only.

    Registrado pela verificação
    fact schnorr = BIP-340
  • EdDSA

    none

    Sem EdDSA.

    Registrado pela verificação
    fact eddsa = none
  • Algoritmos pós-quânticos

    none

    Sem algoritmos pós-quânticos.

    Registrado pela verificação
    fact pqc = none
  • Formatos de chave pública

    sec1-uncompressedsec1-compressed

    ec_pubkey_parse aceita pontos SEC1 de 65 bytes na forma não comprimida e de 33 bytes na forma comprimida; um x‖y de 64 bytes foi recusado.

    Registrado pela verificação
    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 chave privada

    raw-scalar

    As chaves secretas são escalares brutos de 32 bytes.

    Registrado pela verificação
    fact private-key-formats = raw-scalar

Uso

Assine um hash de 32 bytes, analise uma assinatura como ela chegaria de outro sistema, normalize o s e verifique.
#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;
}

O executor de trechos de código roda cada exemplo literalmente na imagem de contêiner alpine:3.24.2, e a execução falha se a saída não corresponder.

Configuração
apk add --no-cache -q build-base libsecp256k1-dev=0.5.0-r1 >/dev/null
Comando
cc -o usage usage.c -lsecp256k1 && ./usage
A saída deve corresponder a (expressão regular)
verified: 1

Bibliotecas