Pular para o conteúdo
ecdsa.com
Python

python-ecdsa

A biblioteca ECDSA e EdDSA em Python puro do projeto tlsfuzzer: assinaturas r‖s brutas sobre SHA-1, salvo indicação em contrário, nonces aleatórios por padrão, com a RFC 6979 a um método de distância, e chaves públicas como x‖y isolado.
Licença
MIT
Versão verificada
0.19.2 ·
Código-fonte
github.com/tlsfuzzer/python-ecdsa
Documentação
ecdsa.readthedocs.io/en/latest/

Visão geral da compatibilidade

ECDSAEdDSA

Curvas ECDSA
P-224P-256P-384P-521secp256k1brainpoolP256r1brainpoolP384r1brainpoolP512r1
EdDSA
Ed25519Ed448
Formatos de chave pública
spki-pemspki-dersec1-uncompressedsec1-compressedraw-xy
Formatos de chave privada
pkcs8-pemsec1-pemsec1-derraw-scalar

O ecdsa implementa ECDSA e EdDSA em Python puro, sem OpenSSL por baixo. Isso o torna fácil de instalar em qualquer lugar e prático para vetores de teste e para o ensino, e ele ainda é uma dependência de stacks JOSE mais antigas, como o python-jose. Os mantenedores afirmam que ele não foi projetado para resistir a ataques de canal lateral, então assinaturas em produção devem ficar com o pyca/cryptography ou o coincurve.

Três padrões causam quase todas as falhas de interoperabilidade. O hash padrão é SHA-1: uma SigningKey criada sem hashfunc assina digests SHA-1, o que o teste confirmou ao verificar a assinatura padrão com o pyca/cryptography — ela passou com SHA-1 e falhou com SHA-256. A assinatura é r‖s bruta (64 bytes na P-256), não DER. E VerifyingKey.to_string() retorna o x‖y isolado de 64 bytes sem o prefixo 0x04, que o OpenSSL, o Go e o WebCrypto não leem como um ponto.

A assinatura usa um nonce aleatório, a menos que você chame sign_deterministic(), que reproduziu o vetor de teste P-256 da RFC 6979. O s fica como foi calculado — alto em cerca de metade das vezes —, a menos que você escolha um dos codificadores *_canonize, e a verificação aceita qualquer das metades. Toda escolha de codificação é feita a cada chamada, por meio de sigencode= e sigdecode=, então os lados que assinam e que verificam precisam indicar o mesmo par; uma assinatura DER passada ao decodificador bruto padrão lança BadSignatureError: Malformed formatting of signature.

O suporte a curvas é o mais amplo entre as bibliotecas Python — da P-224 à P-521, secp256k1 e Brainpool —, além de Ed25519 e Ed448. As chaves entram como SPKI PEM ou DER, pontos SEC1 e escalares brutos; to_pem() grava SEC1 EC PRIVATE KEY, a menos que você passe format="pkcs8".

O exemplo corrige os três padrões de uma vez: SHA-256, um nonce determinístico e uma assinatura DER canônica low-S. Para conferir uma assinatura gerada por ele com outra biblioteca, o conversor DER ⇄ bruto e os vetores de teste são o caminho mais rápido.

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

Nada abaixo foi copiado da documentação. A verificação content/libraries/verify/python-ecdsa/run.sh executou python-ecdsa 0.19.2 na imagem de contêiner python:3.14.8-slim-trixie 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

    raw

    sign() retorna o r‖s bruto de 64 bytes na P-256; sigencode_der produz DER, e a verificação passa a exigir sigdecode_der.

    Registrado pela verificação
    fact ecdsa-signature-encoding = raw
    evidence ecdsa-signature-encoding: sigencode_der output parses as DER: True; verifies with sigdecode_der: verified
  • Hash padrão

    sha1

    Sem hashfunc, as chaves assinam digests SHA-1: a assinatura padrão foi verificada no pyca/cryptography com SHA-1, e não com SHA-256.

    Registrado pela verificação
    fact ecdsa-default-hash = sha1
    evidence ecdsa-default-hash: checked by verifying the default signature in pyca/cryptography with SHA-1 and with SHA-256
  • Low-S ao assinar

    either

    Os codificadores simples mantêm o s como foi calculado (valores high-S apareceram entre 64 assinaturas); sigencode_string_canonize e sigencode_der_canonize o normalizam.

    Registrado pela verificação
    fact ecdsa-low-s-sign = either
    evidence ecdsa-low-s-sign: high-S among 64 plain signatures: True; among 64 with sigencode_string_canonize: False
    evidence ecdsa-low-s-sign: sigencode_strings_canonize exists: True
  • High-S ao verificar

    accepts-high-s

    Uma assinatura com s substituído por n − s foi verificada.

    Registrado pela verificação
    fact ecdsa-low-s-verify = accepts-high-s
  • Nonce do ECDSA

    rfc6979-opt-in

    sign() usa um nonce aleatório; sign_deterministic() reproduziu o apêndice A.2.5 da RFC 6979.

    Registrado pela verificação
    fact ecdsa-nonce = rfc6979-opt-in
    evidence ecdsa-nonce: sign() repeats: False; sign_deterministic() reproduces RFC 6979 A.2.5: True
  • Entrada do hash na chamada de assinatura

    hashes-message-or-digest

    sign() calcula o hash da mensagem; sign_digest() assina um digest calculado por você, e essa assinatura foi verificada contra a mensagem.

    Registrado pela verificação
    fact ecdsa-prehash = hashes-message-or-digest
  • Assinatura ECDSA malformada

    throws

    Uma entrada de tamanho errado lança BadSignatureError: Malformed formatting of signature; uma codificação válida sobre a mensagem errada lança BadSignatureError: Signature verification failed.

    Registrado pela verificação
    fact ecdsa-malformed-signature = throws
    evidence ecdsa-malformed-signature: DER where raw is expected: BadSignatureError: Malformed formatting of signature
    evidence ecdsa-malformed-signature: 3 bytes: BadSignatureError: Malformed formatting of signature
    evidence ecdsa-malformed-signature: wrong message: BadSignatureError: Signature verification failed
  • Curvas ECDSA

    P-224P-256P-384P-521secp256k1brainpoolP256r1brainpoolP384r1brainpoolP512r1

    P-224, P-256, P-384, P-521, secp256k1 e as curvas Brainpool r1.

    Registrado pela verificação
    fact ecdsa-curves = P-224, P-256, P-384, P-521, secp256k1, brainpoolP256r1, brainpoolP384r1, brainpoolP512r1
  • EdDSA

    Ed25519Ed448

    Ed25519 e Ed448.

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

    none

    Sem algoritmos pós-quânticos.

    Registrado pela verificação
    fact pqc = none
  • Formato padrão da chave pública

    raw-xy

    VerifyingKey.to_string() retorna 64 bytes de x‖y sem o prefixo 0x04; passe "uncompressed" ou "compressed" para obter as codificações SEC1.

    Registrado pela verificação
    fact default-public-key = raw-xy
  • Formatos de chave pública

    spki-pemspki-dersec1-uncompressedsec1-compressedraw-xy

    As chaves públicas foram carregadas de SPKI PEM e DER, de pontos SEC1 e do x‖y isolado.

    Registrado pela verificação
    fact public-key-formats = spki-pem, spki-der, sec1-uncompressed, sec1-compressed, raw-xy
  • Formatos de chave privada

    pkcs8-pemsec1-pemsec1-derraw-scalar

    As chaves privadas foram carregadas de PKCS#8 PEM, SEC1 PEM e DER e de escalares brutos; to_pem() grava -----BEGIN EC PRIVATE KEY----- por padrão.

    Registrado pela verificação
    fact private-key-formats = pkcs8-pem, sec1-pem, sec1-der, raw-scalar
    evidence private-key-formats: to_pem() without format writes -----BEGIN EC PRIVATE KEY-----

Uso

Assine com SHA-256, um nonce RFC 6979 e uma codificação DER canônica low-S e depois verifique.
import hashlib

from ecdsa import NIST256p, SigningKey
from ecdsa.util import sigdecode_der, sigencode_der_canonize

key = SigningKey.generate(curve=NIST256p, hashfunc=hashlib.sha256)  # the default would be SHA-1
public = key.get_verifying_key()
message = b"order #1042 approved"

# RFC 6979 nonce, DER encoding, s normalized to the low half: what OpenSSL-style
# verifiers read and what strict low-S verifiers accept.
signature = key.sign_deterministic(message, sigencode=sigencode_der_canonize)
again = key.sign_deterministic(message, sigencode=sigencode_der_canonize)

print("deterministic:", signature == again)
print("verified:", public.verify(signature, message, sigdecode=sigdecode_der))
print(public.to_pem().decode().splitlines()[0])

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

Configuração
pip install --quiet --root-user-action=ignore ecdsa==0.19.2
Comando
python usage.py
A saída deve corresponder a (expressão regular)
deterministic: True\nverified: True\n-----BEGIN PUBLIC KEY-----

Bibliotecas