跳到主要内容
ecdsa.com
C · C++

BoringSSL

Google 的 BoringSSL,用于 Chrome 和 Android 的 OpenSSL 分支:ECDSA 使用 DER 签名且只支持 NIST 曲线,另有 Ed25519,以及通过专用 API 提供的 ML-KEM、ML-DSA 和 SLH-DSA——没有发布版本,也不承诺稳定性。
许可证
Apache-2.0
检查的版本
2026-10-19-b50cad27ee80 ·
源代码
boringssl.googlesource.com/boringssl
文档
commondatastorage.googleapis.com/chromium-boringssl-docs/headers.html

兼容性概览

ECDSAEdDSA后量子

ECDSA 曲线
P-224P-256P-384P-521
EdDSA
Ed25519
后量子算法
ML-KEMML-DSASLH-DSA

BoringSSL 是 Google 维护的 OpenSSL 分支,用于 Chrome 和 Android;aws-lc-rs 底层的 AWS-LC 库又是它的分支,ring 则复用了它的汇编代码。它没有发布版本,也不承诺 API 稳定性——使用方需要锁定某个提交。本页的事实是从源代码构建提交 b50cad27ee80(2026年10月19日)后检查得出的。

ECDSA 沿用 OpenSSL 的惯例:DER 签名、随机 nonce、不做 low-S 规范化,验证时接受 high-S;ECDSA_sign 对你自行计算的摘要签名。与 OpenSSL 不同,格式错误的输入只会表现为验证失败——对于原始 r‖s 签名和 3 字节的数据块,EVP_DigestVerify 都返回 0,因此格式问题只会体现在错误队列中。可以用 DER ⇄ 原始格式转换器检查编码。

曲线刻意只保留 P-224、P-256、P-384 和 P-521——EC_GROUP_new_by_curve_name(NID_secp256k1) 返回 NULL——而 Ed25519 是唯一可用的 EdDSA 变体:为了兼容定义了 EVP_PKEY_ED448,但通过 EVP 生成 Ed448 密钥失败。从 OpenSSL 迁移到 BoringSSL 的代码会失去 secp256k1、Brainpool 和 Ed448,而且在真正请求这些算法之前不会出现任何编译错误。

后量子支持很全面,并且以独立的强类型 API 提供,而不是通过 EVP:MLKEM768_* 和 MLKEM1024_*、MLDSA65_* 及相关规格,以及 SLHDSA_SHA2_128S_*,它们在检查中均完成了往返测试;ssl.h 还定义了 X25519MLKEM768 混合 TLS 组(0x11EC)。示例直接使用 ML-KEM-768 和 ML-DSA-65。可以用浏览器后量子检测查看你的浏览器协商的是什么。

事实及其依据的检查

以下内容均非抄自文档。检查脚本 content/libraries/verify/boringssl/run.sh 于2026年10月20日在容器镜像 debian:trixie-20261005-slim 中运行了 BoringSSL 2026-10-19-b50cad27ee80;本页内容必须与它的输出一致,每条事实都列出了对应的输出行。

  • ECDSA 签名编码

    der

    EVP_DigestSign 生成 ASN.1 DER。

    检查记录
    fact ecdsa-signature-encoding = der
  • ECDSA 的 nonce

    random

    随机 nonce:同一条消息的两个签名不同。

    检查记录
    fact ecdsa-nonce = random
  • 签名时的 low-S

    either

    s 没有经过规范化:64 个签名中出现了 high-S 值。

    检查记录
    fact ecdsa-low-s-sign = either
  • 验证时的 high-S

    accepts-high-s

    把 s 替换为 n − s 的 DER 签名验证通过。

    检查记录
    fact ecdsa-low-s-verify = accepts-high-s
  • 签名调用对哪些内容做哈希

    hashes-message-or-digest

    EVP_DigestSign 会先计算哈希;ECDSA_sign 对摘要签名,该签名针对原消息验证通过。

    检查记录
    fact ecdsa-prehash = hashes-message-or-digest
  • 格式错误的 ECDSA 签名

    returns-false

    对于原始 r‖s 签名和 3 字节的数据块,EVP_DigestVerify 都返回 0。

    检查记录
    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)
  • ECDSA 曲线

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

    P-224、P-256、P-384 和 P-521;没有 secp256k1 曲线群。

    检查记录
    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_sign 和 ED25519_verify 支持 Ed25519;EVP_PKEY_ED448 已定义,但生成 Ed448 密钥失败。

    检查记录
    fact eddsa = Ed25519
    evidence eddsa: EVP_PKEY_ED448 is defined; generating an Ed448 key through EVP: fails
  • 后量子算法

    ML-KEMML-DSASLH-DSA

    通过各自的专用头文件,ML-KEM-768 的封装与解封装、ML-DSA-65 和 SLH-DSA-SHA2-128s 的签名与验证均成功。

    检查记录
    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

用法

用 BoringSSL 的专用后量子 API 执行一次 ML-KEM-768 封装和一次 ML-DSA-65 签名。
#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;
}

代码片段测试工具会在容器镜像 debian:trixie-20261005-slim 中原样运行每个示例,只要输出不匹配,运行就会失败。

环境准备
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
命令
cc -I/bssl/include -o usage usage.c /bssl/build/libcrypto.a -lstdc++ -lpthread && ./usage
输出必须匹配(正则表达式)
ML-KEM-768 secrets match: 1\nML-DSA-65 verified: 1

库