Saltar al contenido
ecdsa.com
Python

python-ecdsa

La biblioteca ECDSA y EdDSA en Python puro de tlsfuzzer: firmas r‖s en bruto sobre SHA-1 salvo que se indique otra cosa, nonces aleatorios por defecto con RFC 6979 a un método de distancia y claves públicas como x‖y sin envoltorio.
Licencia
MIT
Versión comprobada
0.19.2 ·
Código fuente
github.com/tlsfuzzer/python-ecdsa
Documentación
ecdsa.readthedocs.io/en/latest/

Compatibilidad de un vistazo

ECDSAEdDSA

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

ecdsa implementa ECDSA y EdDSA en Python puro, sin OpenSSL por debajo. Eso hace que sea fácil de instalar en cualquier sitio y práctica para vectores de prueba y para la enseñanza, y sigue siendo una dependencia de stacks JOSE más antiguos, como python-jose. Sus mantenedores declaran que no está diseñada para resistir ataques de canal lateral, así que la firma en producción corresponde a pyca/cryptography o a coincurve.

Tres valores predeterminados causan casi todos los fallos de interoperabilidad. La función hash predeterminada es SHA-1: una SigningKey creada sin hashfunc firma resúmenes SHA-1, algo que la comprobación confirmó al verificar la firma predeterminada con pyca/cryptography, que la aceptó con SHA-1 y la rechazó con SHA-256. La firma es r‖s en bruto (64 bytes en P-256), no DER. Y VerifyingKey.to_string() devuelve el x‖y de 64 bytes sin envoltorio y sin el prefijo 0x04, que OpenSSL, Go y WebCrypto no leen como un punto.

La firma usa un nonce aleatorio salvo que llames a sign_deterministic(), que reprodujo el vector de prueba P-256 del RFC 6979. s se deja tal como se calcula —alto aproximadamente la mitad de las veces— salvo que elijas uno de los codificadores *_canonize, y la verificación acepta cualquiera de las dos mitades. Todas las opciones de codificación se eligen en cada llamada mediante sigencode= y sigdecode=, así que el lado que firma y el que verifica deben indicar el mismo par; una firma DER pasada al decodificador en bruto predeterminado lanza BadSignatureError: Malformed formatting of signature.

Ofrece la mayor variedad de curvas de las bibliotecas de Python —de P-224 a P-521, secp256k1 y Brainpool—, además de Ed25519 y Ed448. Las claves se cargan como PEM o DER SPKI, puntos SEC1 y escalares en bruto; to_pem() escribe SEC1 EC PRIVATE KEY salvo que pases format="pkcs8".

El ejemplo corrige los tres valores predeterminados a la vez: SHA-256, un nonce determinista y una firma DER canónica low-S. Para comprobar con otra biblioteca una firma que produce, el conversor DER ⇄ en bruto y los vectores de prueba son la vía más rápida.

Datos y las comprobaciones que los respaldan

Nada de lo que sigue está copiado de la documentación. La comprobación content/libraries/verify/python-ecdsa/run.sh ejecutó python-ecdsa 0.19.2 en la imagen de contenedor python:3.14.8-slim-trixie 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

    raw

    sign() devuelve el r‖s en bruto de 64 bytes en P-256; sigencode_der produce DER, y entonces la verificación necesita sigdecode_der.

    Registrado por la comprobación
    fact ecdsa-signature-encoding = raw
    evidence ecdsa-signature-encoding: sigencode_der output parses as DER: True; verifies with sigdecode_der: verified
  • Hash predeterminado

    sha1

    Sin hashfunc, las claves firman resúmenes SHA-1: la firma predeterminada se verificó en pyca/cryptography con SHA-1 y no con SHA-256.

    Registrado por la comprobación
    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 al firmar

    either

    Los codificadores simples dejan s tal como se calcula (aparecieron valores high-S entre 64 firmas); sigencode_string_canonize y sigencode_der_canonize lo normalizan.

    Registrado por la comprobación
    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 al verificar

    accepts-high-s

    Una firma con s sustituido por n − s se verificó.

    Registrado por la comprobación
    fact ecdsa-low-s-verify = accepts-high-s
  • Nonce de ECDSA

    rfc6979-opt-in

    sign() usa un nonce aleatorio; sign_deterministic() reprodujo el apéndice A.2.5 del RFC 6979.

    Registrado por la comprobación
    fact ecdsa-nonce = rfc6979-opt-in
    evidence ecdsa-nonce: sign() repeats: False; sign_deterministic() reproduces RFC 6979 A.2.5: True
  • De qué calcula el hash la llamada de firma

    hashes-message-or-digest

    sign() calcula el hash del mensaje; sign_digest() firma un resumen calculado de antemano, y esa firma se verificó contra el mensaje.

    Registrado por la comprobación
    fact ecdsa-prehash = hashes-message-or-digest
  • Firma ECDSA mal formada

    throws

    Una entrada de longitud incorrecta lanza BadSignatureError: Malformed formatting of signature; una codificación válida sobre el mensaje equivocado lanza BadSignatureError: Signature verification failed.

    Registrado por la comprobación
    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 y las curvas Brainpool r1.

    Registrado por la comprobación
    fact ecdsa-curves = P-224, P-256, P-384, P-521, secp256k1, brainpoolP256r1, brainpoolP384r1, brainpoolP512r1
  • EdDSA

    Ed25519Ed448

    Ed25519 y Ed448.

    Registrado por la comprobación
    fact eddsa = Ed25519, Ed448
  • Algoritmos poscuánticos

    none

    Sin algoritmos poscuánticos.

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

    raw-xy

    VerifyingKey.to_string() devuelve 64 bytes de x‖y sin el prefijo 0x04; pasa "uncompressed" o "compressed" para obtener las codificaciones SEC1.

    Registrado por la comprobación
    fact default-public-key = raw-xy
  • Formatos de clave pública

    spki-pemspki-dersec1-uncompressedsec1-compressedraw-xy

    Las claves públicas se cargaron desde PEM y DER SPKI, puntos SEC1 y el x‖y sin envoltorio.

    Registrado por la comprobación
    fact public-key-formats = spki-pem, spki-der, sec1-uncompressed, sec1-compressed, raw-xy
  • Formatos de clave privada

    pkcs8-pemsec1-pemsec1-derraw-scalar

    Las claves privadas se cargaron desde PEM PKCS#8, PEM y DER SEC1 y escalares en bruto; to_pem() escribe -----BEGIN EC PRIVATE KEY----- por defecto.

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

Firma con SHA-256, un nonce RFC 6979 y una codificación DER canónica low-S, y después verifica la firma.
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])

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

Configuración
pip install --quiet --root-user-action=ignore ecdsa==0.19.2
Comando
python usage.py
La salida debe coincidir (expresión regular)
deterministic: True\nverified: True\n-----BEGIN PUBLIC KEY-----

Bibliotecas