Aller au contenu
ecdsa.com
Rust

ring

ring de Brian Smith, la bibliothèque cryptographique Rust sous-jacente à rustls et à de nombreux serveurs web : une API volontairement réduite où la constante d’algorithme fixe l’encodage de la signature, les clés privées sont uniquement en PKCS#8 et les courbes sont P-256, P-384 et Ed25519.
Licence
Apache-2.0 AND ISC
Version vérifiée
0.17.14 ·
Code source
github.com/briansmith/ring
Documentation
docs.rs/ring/0.17.14/ring/

Compatibilité en un coup d’œil

ECDSAEdDSA

Courbes ECDSA
P-256P-384
EdDSA
Ed25519
Formats de clé publique
sec1-uncompressed

ring propose une courte liste d’algorithmes, avec de l’assembleur dérivé de BoringSSL et une API résistante aux mauvais usages : impossible de choisir un nonce, de signer un condensat précalculé ou de charger une clé dans un format inhabituel. ring est ainsi rapide et difficile à mal utiliser, et c’est pourquoi l’essentiel du travail consiste à convertir les clés et les signatures venues d’autres piles.

L’encodage fait partie de l’algorithme : ECDSA_P256_SHA256_FIXED_SIGNING produit du r‖s brut de 64 octets et ECDSA_P256_SHA256_ASN1_SIGNING du DER, avec les constantes de vérification _FIXED et _ASN1 correspondantes. Une signature contrôlée sous l’autre constante échoue simplement avec Unspecified : une signature JWS vérifiée avec la constante ASN1, ou une signature OpenSSL avec FIXED, ressemble donc exactement à une falsification. L’exemple montre les deux issues. Les signatures utilisent un nonce aléatoire, s n’est pas normalisé et la vérification accepte les deux moitiés.

Les clés privées ne se lisent qu’en PKCS#8 DER, et les clés Ed25519 doivent être en PKCS#8 v2 avec la clé publique incluse : la forme v1 de 48 octets qu’écrivent OpenSSL et la plupart des outils a été rejetée par Ed25519KeyPair::from_pkcs8 avec VersionNotSupported, alors que from_pkcs8_maybe_unchecked et from_seed_unchecked l’ont acceptée. Les clés privées PEM, SEC1 et JWK doivent d’abord être converties, par exemple avec le convertisseur JWK. Les clés publiques sont des points SEC1 nus : public_key() renvoie la forme non compressée de 65 octets, et les points compressés ne sont pas acceptés.

La liste des courbes s’arrête à P-256 et P-384, plus Ed25519 : pas de P-521, pas de secp256k1 et aucun algorithme post-quantique. Les projets qui en ont besoin, ou qui exigent une validation FIPS, passent à aws-lc-rs, qui offre une API compatible avec ring.

Pour savoir dans quel encodage est une signature produite par ring, collez-la dans le convertisseur DER ⇄ brut.

Les faits et les vérifications qui les fondent

Rien de ce qui suit n’est copié d’une documentation. La vérification content/libraries/verify/ring/run.sh a exécuté ring 0.17.14 dans l’image de conteneur rust:1.99.1-trixie le 20 octobre 2026 ; la page doit correspondre à ce que la vérification a affiché, et chaque fait cite les lignes en question.

  • Encodage des signatures ECDSA

    der-or-raw

    La constante d’algorithme choisit l’encodage : *_FIXED signe et vérifie du r‖s brut de 64 octets, *_ASN1 du DER ; une signature ne passe pas la vérification sous l’autre constante.

    Relevé par la vérification
    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 ECDSA

    random

    Nonce aléatoire : deux signatures du même message diffèrent, et il n’existe pas d’option déterministe.

    Relevé par la vérification
    fact ecdsa-nonce = random
  • Low-S à la signature

    either

    s n’est pas normalisé : des valeurs high-S sont apparues parmi 64 signatures.

    Relevé par la vérification
    fact ecdsa-low-s-sign = either
  • High-S à la vérification

    accepts-high-s

    Le vérificateur FIXED a accepté la signature dont s a été remplacé par n − s.

    Relevé par la vérification
    fact ecdsa-low-s-verify = accepts-high-s
  • Ce que l’appel de signature hache

    hashes-message

    Le message est haché selon ce qu’indique l’algorithme ; il n’existe pas d’API pour signer un condensat précalculé.

    Relevé par la vérification
    fact ecdsa-prehash = hashes-message
    evidence ecdsa-prehash: the API has no entry point for signing a precomputed digest
  • Signature ECDSA mal formée

    returns-error

    verify() renvoie Err(Unspecified) pour une signature mal formée ou incorrecte, sans aucun détail.

    Relevé par la vérification
    fact ecdsa-malformed-signature = returns-error
    evidence ecdsa-malformed-signature: verify() of 3 bytes: Err(Unspecified)
  • Courbes ECDSA

    P-256P-384

    P-256 et P-384 uniquement.

    Relevé par la vérification
    fact ecdsa-curves = P-256, P-384
    evidence ecdsa-curves: ring defines ECDSA_P256_* and ECDSA_P384_* algorithms only
  • EdDSA

    Ed25519

    Ed25519.

    Relevé par la vérification
    fact eddsa = Ed25519
  • Clés Ed25519 PKCS #8 v1

    rejected-by-from-pkcs8

    Une clé Ed25519 PKCS#8 v1 de 48 octets a été refusée par from_pkcs8 avec VersionNotSupported ; from_pkcs8_maybe_unchecked et from_seed_unchecked l’ont acceptée. generate_pkcs8 écrit des clés v2 de 83 octets.

    Relevé par la vérification
    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
  • Algorithmes post-quantiques

    none

    Aucun algorithme post-quantique.

    Relevé par la vérification
    fact pqc = none
  • Formats de clé publique

    sec1-uncompressed

    Les clés publiques sont des points non compressés au format SEC1 (65 octets pour P-256) ; les points compressés ont échoué, et il n’y a pas d’analyse SPKI, PEM ni JWK.

    Relevé par la vérification
    fact public-key-formats = sec1-uncompressed
    evidence public-key-formats: public_key() returns 65 bytes (uncompressed SEC1); no SPKI, PEM or JWK parsing
  • Formats de clé privée

    pkcs8-der

    Les clés privées ne se chargent que depuis PKCS#8 DER.

    Relevé par la vérification
    fact private-key-formats = pkcs8-der

Utilisation

Signer avec la constante FIXED (brute) et montrer que seule la constante de vérification correspondante accepte la signature.
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());
}

Le banc de test des extraits de code exécute chaque exemple tel quel dans l’image de conteneur rust:1.99.1-trixie, et l’exécution échoue si la sortie ne correspond pas.

Commande
cargo run --quiet --release
La sortie doit correspondre à cette expression régulière
raw bytes: 64, verified: true, verified as DER: false

Bibliothèques