Saltar al contenido
ecdsa.com
Rust

ring

ring, de Brian Smith, la biblioteca criptográfica de Rust que hay bajo rustls y muchos servidores web: una API deliberadamente pequeña en la que la constante del algoritmo fija la codificación de la firma, las claves privadas son solo PKCS#8 y las curvas son P-256, P-384 y Ed25519.
Licencia
Apache-2.0 AND ISC
Versión comprobada
0.17.14 ·
Código fuente
github.com/briansmith/ring
Documentación
docs.rs/ring/0.17.14/ring/

Compatibilidad de un vistazo

ECDSAEdDSA

Curvas ECDSA
P-256P-384
EdDSA
Ed25519

ring ofrece una lista corta de algoritmos con código ensamblador derivado de BoringSSL y una API resistente al mal uso: no hay forma de elegir un nonce, firmar un resumen precalculado ni cargar una clave en un formato poco habitual. Eso la hace rápida y difícil de usar mal, y es la razón de que el trabajo esté en convertir claves y firmas de otros stacks.

La codificación forma parte del algoritmo: ECDSA_P256_SHA256_FIXED_SIGNING produce r‖s en bruto de 64 bytes y ECDSA_P256_SHA256_ASN1_SIGNING, DER, con sus constantes de verificación _FIXED y _ASN1 correspondientes. Una firma comprobada con la otra constante simplemente falla con Unspecified, así que una firma JWS verificada con la constante ASN1 —o una firma de OpenSSL con FIXED— parece exactamente una falsificación. El ejemplo muestra ambos resultados. Las firmas usan un nonce aleatorio, s no se normaliza y la verificación acepta ambas mitades.

Las claves privadas solo se leen como DER PKCS#8, y las claves Ed25519 deben ser PKCS#8 v2 con la clave pública incluida: Ed25519KeyPair::from_pkcs8 rechazó con VersionNotSupported la forma v1 de 48 bytes que escriben OpenSSL y la mayoría de las herramientas, mientras que from_pkcs8_maybe_unchecked y from_seed_unchecked la aceptaron. Las claves privadas PEM, SEC1 y JWK hay que convertirlas antes, por ejemplo con el conversor de JWK. Las claves públicas son puntos SEC1 sin envoltorio: public_key() devuelve el formato sin comprimir de 65 bytes, y los puntos comprimidos no se aceptan.

La lista de curvas se queda en P-256 y P-384, más Ed25519; no hay P-521, ni secp256k1, ni ningún algoritmo poscuántico. Los proyectos que los necesitan, o que necesitan validación FIPS, se pasan a aws-lc-rs, que ofrece una API compatible con ring.

Para comprobar en qué codificación está una firma de ring, pégala en el conversor DER ⇄ en bruto.

Datos y las comprobaciones que los respaldan

Nada de lo que sigue está copiado de la documentación. La comprobación content/libraries/verify/ring/run.sh ejecutó ring 0.17.14 en la imagen de contenedor rust:1.99.1-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

    der-or-raw

    La constante del algoritmo elige la codificación: *_FIXED firma y verifica r‖s en bruto de 64 bytes, y *_ASN1, DER; una firma no se verifica con la otra constante.

    Registrado por la comprobación
    fact ecdsa-signature-encoding = der-or-raw
    evidence ecdsa-signature-encoding: FIXED signs 64 bytes; ASN1 output starts with 0x30 (a DER SEQUENCE)
    evidence ecdsa-signature-encoding: a signature verifies under the other encoding's constant: false
  • Nonce de ECDSA

    random

    Nonce aleatorio: dos firmas del mismo mensaje son distintas, y no hay opción determinista.

    Registrado por la comprobación
    fact ecdsa-nonce = random
  • Low-S al firmar

    either

    s no se normaliza: aparecieron valores high-S entre 64 firmas.

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

    accepts-high-s

    El verificador FIXED aceptó la firma con s sustituido por n − s.

    Registrado por la comprobación
    fact ecdsa-low-s-verify = accepts-high-s
  • De qué calcula el hash la llamada de firma

    hashes-message

    El hash del mensaje se calcula según indica el algoritmo; no hay API para firmar un resumen precalculado.

    Registrado por la comprobación
    fact ecdsa-prehash = hashes-message
    evidence ecdsa-prehash: the API has no entry point for signing a precomputed digest
  • Firma ECDSA mal formada

    returns-error

    verify() devuelve Err(Unspecified) ante una firma mal formada o incorrecta, sin ningún detalle.

    Registrado por la comprobación
    fact ecdsa-malformed-signature = returns-error
    evidence ecdsa-malformed-signature: verify() of 3 bytes: Err(Unspecified)
  • Curvas ECDSA

    P-256P-384

    Solo P-256 y P-384.

    Registrado por la comprobación
    fact ecdsa-curves = P-256, P-384
    evidence ecdsa-curves: ring defines ECDSA_P256_* and ECDSA_P384_* algorithms only
  • EdDSA

    Ed25519

    Ed25519.

    Registrado por la comprobación
    fact eddsa = Ed25519
  • Claves Ed25519 en PKCS #8 v1

    rejected-by-from-pkcs8

    from_pkcs8 rechazó con VersionNotSupported una clave Ed25519 PKCS#8 v1 de 48 bytes; from_pkcs8_maybe_unchecked y from_seed_unchecked la aceptaron. generate_pkcs8 escribe claves v2 de 83 bytes.

    Registrado por la comprobación
    fact ed25519-pkcs8-v1 = rejected-by-from-pkcs8
    evidence ed25519-pkcs8-v1: generate_pkcs8 writes 83 bytes (PKCS#8 v2 with the public key); a 48-byte v1 key: from_pkcs8 Err(KeyRejected("VersionNotSupported")), from_pkcs8_maybe_unchecked ok true, from_seed_unchecked ok true
  • Algoritmos poscuánticos

    none

    Sin algoritmos poscuánticos.

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

    sec1-uncompressed

    Las claves públicas son puntos sin comprimir en formato SEC1 (65 bytes para P-256); los puntos comprimidos fallaron, y no hay análisis de SPKI, PEM ni JWK.

    Registrado por la comprobación
    fact public-key-formats = sec1-uncompressed
    evidence public-key-formats: public_key() returns 65 bytes (uncompressed SEC1); no SPKI, PEM or JWK parsing
  • Formatos de clave privada

    pkcs8-der

    Las claves privadas solo se cargan desde DER PKCS#8.

    Registrado por la comprobación
    fact private-key-formats = pkcs8-der

Uso

Firma con la constante FIXED (en bruto) y muestra que solo la constante de verificación correspondiente acepta la firma.
use ring::rand::SystemRandom;
use ring::signature::{self, EcdsaKeyPair, KeyPair, UnparsedPublicKey};

fn main() {
    let rng = SystemRandom::new();
    // ring only reads PKCS#8 DER private keys.
    let pkcs8 = EcdsaKeyPair::generate_pkcs8(&signature::ECDSA_P256_SHA256_FIXED_SIGNING, &rng).unwrap();
    let message = b"order #1042 approved";

    // The algorithm constant fixes the encoding: FIXED = raw r||s (JWS, WebCrypto),
    // ASN1 = DER (OpenSSL, Java). Verify with the matching constant.
    let fixed = EcdsaKeyPair::from_pkcs8(&signature::ECDSA_P256_SHA256_FIXED_SIGNING, pkcs8.as_ref(), &rng).unwrap();
    let raw = fixed.sign(&rng, message).unwrap();
    let public = fixed.public_key().as_ref(); // 65-byte uncompressed SEC1 point

    let ok = UnparsedPublicKey::new(&signature::ECDSA_P256_SHA256_FIXED, public).verify(message, raw.as_ref()).is_ok();
    let wrong = UnparsedPublicKey::new(&signature::ECDSA_P256_SHA256_ASN1, public).verify(message, raw.as_ref()).is_ok();
    println!("raw bytes: {}, verified: {ok}, verified as DER: {wrong}", raw.as_ref().len());
}

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

Comando
cargo run --quiet --release
La salida debe coincidir (expresión regular)
raw bytes: 64, verified: true, verified as DER: false

Bibliotecas