Saltar al contenido
ecdsa.com
C · C++

BoringSSL

BoringSSL de Google, el fork de OpenSSL que usan Chrome y Android: ECDSA con DER solo en las curvas NIST, Ed25519, y ML-KEM, ML-DSA y SLH-DSA con API propias, sin versiones publicadas ni promesa de estabilidad.
Licencia
Apache-2.0
Versión comprobada
2026-10-19-b50cad27ee80 ·
Código fuente
boringssl.googlesource.com/boringssl
Documentación
commondatastorage.googleapis.com/chromium-boringssl-docs/headers.html

Compatibilidad de un vistazo

ECDSAEdDSACriptografía poscuántica

Curvas ECDSA
P-224P-256P-384P-521
EdDSA
Ed25519
Algoritmos poscuánticos
ML-KEMML-DSASLH-DSA

BoringSSL es el fork de OpenSSL de Google, usado en Chrome y Android; AWS-LC, la biblioteca que hay debajo de aws-lc-rs, es a su vez un fork de BoringSSL, y ring reutiliza su código ensamblador. No publica versiones ni promete estabilidad de la API: quienes lo usan fijan un commit. Los datos de esta página se comprobaron compilando desde el código fuente el commit b50cad27ee80 (19 de octubre de 2026).

ECDSA mantiene las convenciones de OpenSSL: firmas DER, nonces aleatorios, sin normalización low-S y una verificación que acepta high-S; ECDSA_sign firma un resumen que calculas tú. A diferencia de OpenSSL, una entrada mal formada es simplemente una verificación fallida: EVP_DigestVerify devolvió 0 con una firma r‖s en bruto y con un blob de 3 bytes, así que la cola de errores es el único lugar donde aparece un problema de formato. Comprueba las codificaciones con el conversor DER ⇄ en bruto.

Las curvas se limitan deliberadamente a P-224, P-256, P-384 y P-521 —EC_GROUP_new_by_curve_name(NID_secp256k1) devolvió NULL— y Ed25519 es la única variante de EdDSA que funciona: EVP_PKEY_ED448 está definido por compatibilidad, pero generar una clave Ed448 a través de EVP falló. El código que pasa de OpenSSL a BoringSSL pierde secp256k1, Brainpool y Ed448 sin ningún error de compilación hasta que se solicita el algoritmo.

La compatibilidad poscuántica es amplia y llega como API separadas y tipadas en lugar de a través de EVP: MLKEM768_* y MLKEM1024_*, MLDSA65_* y los tamaños relacionados, y SLHDSA_SHA2_128S_*, que superaron todos la prueba de ida y vuelta en la comprobación; ssl.h también define el grupo TLS híbrido X25519MLKEM768 (0x11EC). El ejemplo usa ML-KEM-768 y ML-DSA-65 directamente. Mira qué negocia tu navegador con la comprobación de TLS poscuántico del navegador.

Datos y las comprobaciones que los respaldan

Nada de lo que sigue está copiado de la documentación. La comprobación content/libraries/verify/boringssl/run.sh ejecutó BoringSSL 2026-10-19-b50cad27ee80 en la imagen de contenedor debian:trixie-20261005-slim 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

    EVP_DigestSign produce DER ASN.1.

    Registrado por la comprobación
    fact ecdsa-signature-encoding = der
  • Nonce de ECDSA

    random

    Nonce aleatorio: dos firmas del mismo mensaje son distintas.

    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

    Se verificó una firma DER 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-or-digest

    EVP_DigestSign calcula el hash; ECDSA_sign firma un resumen, y esa firma se verificó contra el mensaje.

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

    returns-false

    EVP_DigestVerify devolvió 0 con una firma r‖s en bruto y con un blob de 3 bytes.

    Registrado por la comprobación
    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)
  • Curvas ECDSA

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

    P-224, P-256, P-384 y P-521; no hay grupo secp256k1.

    Registrado por la comprobación
    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 mediante ED25519_sign y ED25519_verify; EVP_PKEY_ED448 está definido, pero generar una clave Ed448 falló.

    Registrado por la comprobación
    fact eddsa = Ed25519
    evidence eddsa: EVP_PKEY_ED448 is defined; generating an Ed448 key through EVP: fails
  • Algoritmos poscuánticos

    ML-KEMML-DSASLH-DSA

    Con ML-KEM-768 se encapsuló y desencapsuló, y con ML-DSA-65 y SLH-DSA-SHA2-128s se firmó y verificó, a través de sus archivos de cabecera dedicados.

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

Uso

Ejecuta una encapsulación ML-KEM-768 y una firma ML-DSA-65 con las API poscuánticas dedicadas 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;
}

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

Configuración
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
Comando
cc -I/bssl/include -o usage usage.c /bssl/build/libcrypto.a -lstdc++ -lpthread && ./usage
La salida debe coincidir (expresión regular)
ML-KEM-768 secrets match: 1\nML-DSA-65 verified: 1

Bibliotecas