跳到主要内容
ecdsa.com
C

Mbed TLS (TF-PSA-Crypto)

Mbed TLS 4.x 是嵌入式 TLS 协议栈,其密码学功能现已改由 PSA Crypto API 提供:ECDSA 签名采用原始 r‖s 格式,确定性 ECDSA 是一个单独的算法,密钥策略为每个密钥固定一种算法。
许可证
Apache-2.0 OR GPL-2.0-or-later
检查的版本
4.1.1 ·
源代码
github.com/Mbed-TLS/mbedtls
文档
mbed-tls.readthedocs.io/

兼容性概览

ECDSA后量子

ECDSA 曲线
P-256P-384P-521secp256k1brainpoolP256r1brainpoolP384r1brainpoolP512r1
EdDSA
none
公钥格式
spki-dersec1-uncompressed

Mbed TLS 4 把密码学功能迁移到了 TF-PSA-Crypto 和 PSA Crypto API(psa_sign_message、psa_verify_message、psa_generate_key),Arm 的 Trusted Firmware 使用的也是这套 API。本页的事实基于 Alpine 3.24.2 的软件包 Mbed TLS 4.1.1 检查得出(当时上游已发布到 4.2.0);与所有嵌入式库一样,可用功能取决于构建配置。

与源自 OpenSSL 的代码不同的是签名编码:PSA 返回原始 r‖s 形式(P-256 上为 64 字节),而不是 DER。用于 X.509、CMS 或 OpenSSL 验证方的签名必须包装为 DER,来自这些对端的 DER 签名也必须先解包,才能交给 psa_verify_message,否则它会报告 PSA_ERROR_INVALID_SIGNATURE——与伪造签名或 3 字节数据块得到的状态相同。可以用 DER ⇄ 原始格式转换器转换。

每个密钥都带有一条只允许一种算法的策略,因此在 PSA_ALG_ECDSA(随机 nonce)和 PSA_ALG_DETERMINISTIC_ECDSA(RFC 6979——复现了该 RFC 的测试向量)之间的选择,要像示例那样在创建密钥时做出。s 没有经过规范化,验证时也接受 high-S。psa_sign_hash 对预先计算好的摘要签名。

这个构建在 P-256、P-384、P-521、secp256k1 和 Brainpool r1 曲线上的签名与验证均成功;P-224 不可用,Ed25519 密钥生成被拒绝(PSA_ERROR_NOT_SUPPORTED)。后量子支持仅限于验证 LMS 这种基于哈希的签名(MBEDTLS_LMS_C);没有 ML-KEM 或 ML-DSA 密钥类型,来自 mldsa-native 的实验性 ML-DSA 驱动(TF_PSA_CRYPTO_PQCP_MLDSA_ENABLED)在该构建的配置中被注释掉了。公钥通过 PSA 导出为 SEC1 未压缩点,通过 pk 层导出为 SPKI DER。

事实及其依据的检查

以下内容均非抄自文档。检查脚本 content/libraries/verify/mbedtls/run.sh 于2026年10月20日在容器镜像 alpine:3.24.2 中运行了 Mbed TLS (TF-PSA-Crypto) 4.1.1;本页内容必须与它的输出一致,每条事实都列出了对应的输出行。

  • ECDSA 签名编码

    raw

    在 P-256 上,psa_sign_message 返回 64 字节的原始 r‖s。

    检查记录
    fact ecdsa-signature-encoding = raw
    evidence ecdsa-signature-encoding: psa_sign_message returned 64 bytes
  • ECDSA 的 nonce

    rfc6979-opt-in

    PSA_ALG_ECDSA 使用随机 nonce;PSA_ALG_DETERMINISTIC_ECDSA 复现了 RFC 6979 附录 A.2.5 的结果。

    检查记录
    fact ecdsa-nonce = rfc6979-opt-in
    evidence ecdsa-nonce: PSA_ALG_ECDSA repeats: no; PSA_ALG_DETERMINISTIC_ECDSA reproduces the RFC 6979 A.2.5 r: yes (status 0)
  • 签名时的 low-S

    either

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

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

    accepts-high-s

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

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

    hashes-message-or-digest

    psa_sign_message 会先计算哈希;psa_sign_hash 对摘要签名,生成的签名用 psa_verify_message 验证通过。

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

    returns-false

    DER 签名、3 字节的数据块以及针对其他数据的有效签名,都返回 PSA_ERROR_INVALID_SIGNATURE(-149)。

    检查记录
    fact ecdsa-malformed-signature = returns-false
    evidence ecdsa-malformed-signature: psa_verify_message status: DER -149, 3 bytes -149, wrong message -149 (PSA_ERROR_INVALID_SIGNATURE = -149)
  • ECDSA 曲线

    P-256P-384P-521secp256k1brainpoolP256r1brainpoolP384r1brainpoolP512r1

    P-256、P-384、P-521、secp256k1 和 brainpoolP256r1/P384r1/P512r1 的签名与验证均成功;在这个构建中,P-224 密钥生成失败。

    检查记录
    fact ecdsa-curves = P-256, P-384, P-521, secp256k1, brainpoolP256r1, brainpoolP384r1, brainpoolP512r1
  • EdDSA

    none

    Ed25519 密钥生成返回 PSA_ERROR_NOT_SUPPORTED(-134)。

    检查记录
    fact eddsa = none
    evidence eddsa: psa_generate_key for an Ed25519 key: status -134
  • 后量子算法

    LMS

    MBEDTLS_LMS_C 已启用(LMS 签名验证);没有定义 ML-KEM 或 ML-DSA 的 PSA 密钥类型,实验性的 mldsa-native 驱动也没有编译进来。

    检查记录
    fact pqc = LMS
    evidence pqc: MBEDTLS_LMS_C is enabled (LMS hash-based signature verification, <mbedtls/lms.h>)
    evidence pqc: no ML-DSA or ML-KEM PSA key type is defined in these headers
    evidence pqc: psa/crypto_config.h of this build: //#define TF_PSA_CRYPTO_PQCP_MLDSA_ENABLED;//#define TF_PSA_CRYPTO_PQCP_MLDSA_87_ENABLED (commented out = the experimental mldsa-native driver is not built)
  • 公钥格式

    spki-dersec1-uncompressed

    psa_export_public_key 返回 65 字节的 SEC1 未压缩点;SPKI DER 通过 pk 层写出和解析。

    检查记录
    fact public-key-formats = spki-der, sec1-uncompressed
    evidence public-key-formats: psa_export_public_key returns 65 bytes starting 0x04 (an uncompressed SEC1 point)

用法

创建策略允许确定性 ECDSA 的 P-256 密钥,对消息签名,并验证 64 字节的原始签名。
#include <psa/crypto.h>
#include <stdio.h>

int main(void) {
    if (psa_crypto_init() != PSA_SUCCESS) return 1;
    /* The algorithm is part of the key policy: here deterministic ECDSA (RFC 6979) with SHA-256. */
    psa_algorithm_t alg = PSA_ALG_DETERMINISTIC_ECDSA(PSA_ALG_SHA_256);
    psa_key_attributes_t attr = PSA_KEY_ATTRIBUTES_INIT;
    psa_set_key_type(&attr, PSA_KEY_TYPE_ECC_KEY_PAIR(PSA_ECC_FAMILY_SECP_R1));
    psa_set_key_bits(&attr, 256);
    psa_set_key_usage_flags(&attr, PSA_KEY_USAGE_SIGN_MESSAGE | PSA_KEY_USAGE_VERIFY_MESSAGE);
    psa_set_key_algorithm(&attr, alg);
    psa_key_id_t key;
    if (psa_generate_key(&attr, &key) != PSA_SUCCESS) return 1;

    const uint8_t msg[] = "order #1042 approved";
    uint8_t sig[PSA_SIGNATURE_MAX_SIZE];
    size_t sig_len;
    psa_sign_message(key, alg, msg, sizeof msg - 1, sig, sizeof sig, &sig_len);

    /* PSA signatures are raw r||s (64 bytes on P-256): wrap them in DER for X.509 or OpenSSL peers. */
    psa_status_t status = psa_verify_message(key, alg, msg, sizeof msg - 1, sig, sig_len);
    printf("signature bytes: %zu, verified: %d\n", sig_len, status == PSA_SUCCESS);
    psa_destroy_key(key);
    return 0;
}

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

环境准备
apk add --no-cache -q build-base mbedtls-dev=4.1.1-r0 >/dev/null
命令
cc -o usage usage.c -lmbedcrypto -ltfpsacrypto && ./usage
输出必须匹配(正则表达式)
signature bytes: 64, verified: 1

库