本文へスキップ
ecdsa.com
C

Mbed TLS (TF-PSA-Crypto)

Mbed TLS 4.x は、暗号処理が PSA Crypto API に移った組み込み向けの TLS スタックです。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 は DER ではなく生の r‖s 形式(P-256 では 64 バイト)を返します。X.509 や CMS で使う署名と OpenSSL で検証する署名は DER で包む必要があり、それらの相手から受け取った DER 署名は psa_verify_message に渡す前に展開しなければなりません。展開しないと PSA_ERROR_INVALID_SIGNATURE が返ります。これは偽造された署名や 3 バイトのデータに対するものと同じステータスです。変換には DER ⇄ 生形式の変換ツールを使ってください。

各鍵には許可するアルゴリズムを 1 つだけ定めたポリシーが付いているため、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 として書き出されます。

事実とその根拠となるチェック

以下の内容はドキュメントからの転記ではありません。2026年10月20日に、チェック content/libraries/verify/mbedtls/run.sh がコンテナイメージ 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

ライブラリ