Aller au contenu
ecdsa.com
C · C++

BoringSSL

BoringSSL de Google, le fork d’OpenSSL utilisé dans Chrome et Android : ECDSA en DER sur les seules courbes NIST, Ed25519, ainsi que ML-KEM, ML-DSA et SLH-DSA via des API dédiées, le tout sans versions publiées ni promesse de stabilité.
Licence
Apache-2.0
Version vérifiée
2026-10-19-b50cad27ee80 ·
Code source
boringssl.googlesource.com/boringssl
Documentation
commondatastorage.googleapis.com/chromium-boringssl-docs/headers.html

Compatibilité en un coup d’œil

ECDSAEdDSAPost-quantique

Courbes ECDSA
P-224P-256P-384P-521
EdDSA
Ed25519
Algorithmes post-quantiques
ML-KEMML-DSASLH-DSA

BoringSSL est le fork d’OpenSSL maintenu par Google, utilisé dans Chrome et Android ; AWS-LC, la bibliothèque sous-jacente à aws-lc-rs, en est à son tour un fork, et ring réutilise son code assembleur. Le projet ne publie pas de versions et ne promet aucune stabilité d’API : ses utilisateurs épinglent un commit. Les faits présentés ici ont été vérifiés en compilant depuis les sources le commit b50cad27ee80 (19 octobre 2026).

ECDSA reprend les conventions d’OpenSSL : signatures DER, nonces aléatoires, aucune normalisation low-S et une vérification qui accepte high-S ; ECDSA_sign signe un condensat que vous avez calculé. Contrairement à OpenSSL, une entrée mal formée n’est qu’un simple échec de vérification : EVP_DigestVerify a renvoyé 0 pour une signature r‖s brute et pour un bloc de 3 octets, si bien que la file d’erreurs est le seul endroit où un problème de format apparaît. Vérifiez les encodages avec le convertisseur DER ⇄ brut.

Les courbes sont volontairement limitées à P-224, P-256, P-384 et P-521 (EC_GROUP_new_by_curve_name(NID_secp256k1) a renvoyé NULL), et Ed25519 est la seule variante d’EdDSA qui fonctionne : EVP_PKEY_ED448 est défini pour compatibilité, mais la génération d’une clé Ed448 via EVP a échoué. Le code qui passe d’OpenSSL à BoringSSL perd secp256k1, Brainpool et Ed448 sans erreur de compilation, jusqu’au moment où l’algorithme est demandé.

La prise en charge post-quantique est étendue et se présente sous forme d’API distinctes et typées plutôt que via EVP : MLKEM768_* et MLKEM1024_*, MLDSA65_* et les tailles apparentées, ainsi que SLHDSA_SHA2_128S_*, qui ont tous fonctionné en aller-retour lors des essais ; ssl.h définit aussi le groupe TLS hybride X25519MLKEM768 (0x11EC). L’exemple utilise directement ML-KEM-768 et ML-DSA-65. Voyez ce que négocie votre navigateur avec le test TLS post-quantique du navigateur.

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/boringssl/run.sh a exécuté BoringSSL 2026-10-19-b50cad27ee80 dans l’image de conteneur debian:trixie-20261005-slim 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

    EVP_DigestSign produit de l’ASN.1 DER.

    Relevé par la vérification
    fact ecdsa-signature-encoding = der
  • Nonce ECDSA

    random

    Nonce aléatoire : deux signatures du même message diffèrent.

    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

    Une signature DER dont s a été remplacé par n − s a été vérifiée.

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

    hashes-message-or-digest

    EVP_DigestSign hache les données ; ECDSA_sign signe un condensat, et cette signature a été vérifiée sur le message.

    Relevé par la vérification
    fact ecdsa-prehash = hashes-message-or-digest
  • Signature ECDSA mal formée

    returns-false

    EVP_DigestVerify a renvoyé 0 pour une signature r‖s brute et pour un bloc de 3 octets.

    Relevé par la vérification
    fact ecdsa-malformed-signature = returns-false
    evidence ecdsa-malformed-signature: EVP_DigestVerify returned 0 for raw r||s and 0 for 3 bytes (1 = valid, 0 = invalid)
  • Courbes ECDSA

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

    P-224, P-256, P-384 et P-521 ; il n’existe pas de groupe secp256k1.

    Relevé par la vérification
    fact ecdsa-curves = P-224, P-256, P-384, P-521
    evidence ecdsa-curves: EC_GROUP_new_by_curve_name(NID_secp256k1): NULL
  • EdDSA

    Ed25519

    Ed25519 via ED25519_sign et ED25519_verify ; EVP_PKEY_ED448 est défini, mais la génération d’une clé Ed448 a échoué.

    Relevé par la vérification
    fact eddsa = Ed25519
    evidence eddsa: EVP_PKEY_ED448 is defined; generating an Ed448 key through EVP: fails
  • Algorithmes post-quantiques

    ML-KEMML-DSASLH-DSA

    Encapsulation et décapsulation réussies avec ML-KEM-768, signature et vérification avec ML-DSA-65 et SLH-DSA-SHA2-128s, via leurs en-têtes dédiés.

    Relevé par la vérification
    fact pqc = ML-KEM, ML-DSA, SLH-DSA
    evidence pqc: ssl.h defines SSL_GROUP_X25519_MLKEM768 = 0x11ec
    evidence pqc: ML-DSA-65 signature 3309 bytes

Utilisation

Effectuer une encapsulation ML-KEM-768 et une signature ML-DSA-65 avec les API post-quantiques dédiées de BoringSSL.
#include <openssl/mldsa.h>
#include <openssl/mlkem.h>
#include <openssl/bytestring.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>

int main(void) {
    /* ML-KEM-768: the receiver publishes the encoded public key. */
    uint8_t pub[MLKEM768_PUBLIC_KEY_BYTES], ct[MLKEM768_CIPHERTEXT_BYTES];
    uint8_t ss_sender[MLKEM_SHARED_SECRET_BYTES], ss_receiver[MLKEM_SHARED_SECRET_BYTES];
    struct MLKEM768_private_key priv;
    struct MLKEM768_public_key pk;
    MLKEM768_generate_key(pub, NULL, &priv);
    CBS cbs;
    CBS_init(&cbs, pub, sizeof pub);
    if (!MLKEM768_parse_public_key(&pk, &cbs)) return 1;
    MLKEM768_encap(ct, ss_sender, &pk);
    MLKEM768_decap(ss_receiver, ct, sizeof ct, &priv);
    printf("ML-KEM-768 secrets match: %d\n", memcmp(ss_sender, ss_receiver, sizeof ss_sender) == 0);

    /* ML-DSA-65 with an empty context string. */
    static const uint8_t msg[] = "release manifest";
    uint8_t dpub[MLDSA65_PUBLIC_KEY_BYTES], seed[MLDSA_SEED_BYTES];
    struct MLDSA65_private_key *dpriv = malloc(sizeof *dpriv);
    struct MLDSA65_public_key *dpk = malloc(sizeof *dpk);
    uint8_t *sig = malloc(MLDSA65_SIGNATURE_BYTES);
    MLDSA65_generate_key(dpub, seed, dpriv);
    MLDSA65_public_from_private(dpk, dpriv);
    MLDSA65_sign(sig, dpriv, msg, sizeof msg - 1, NULL, 0);
    printf("ML-DSA-65 verified: %d\n", MLDSA65_verify(dpk, sig, MLDSA65_SIGNATURE_BYTES, msg, sizeof msg - 1, NULL, 0));
    free(dpriv); free(dpk); free(sig);
    return 0;
}

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

Mise en place
apt-get -qq update >/dev/null && apt-get -qq install -y --no-install-recommends build-essential cmake ninja-build git ca-certificates perl golang >/dev/null 2>&1 && git clone -q --filter=blob:none https://boringssl.googlesource.com/boringssl /bssl && git -C /bssl checkout -q b50cad27ee8013f241648ef6a7adf7f6f6b3d25b && cmake -S /bssl -B /bssl/build -G Ninja -DCMAKE_BUILD_TYPE=Release >/dev/null 2>&1 && ninja -C /bssl/build crypto >/dev/null
Commande
cc -I/bssl/include -o usage usage.c /bssl/build/libcrypto.a -lstdc++ -lpthread && ./usage
La sortie doit correspondre à cette expression régulière
ML-KEM-768 secrets match: 1\nML-DSA-65 verified: 1

Bibliothèques