Chapter 17
実践パターン — よくある要件を PSA の呼び出し列に翻訳する
この章のゴール.
「装置を認証したい」「設定を暗号化して保存したい」「更新イメージを検証したい」といった要件を、 どの章のどの関数を、どの順で呼ぶかに翻訳できるようになること。 各パターンについて、鍵の属性・寿命・保存場所まで含めて決められること。
この章で使う既出の用語(定義は各リンク先). PSA(01 章 1 節)、algorithm(03 章 11 節)、type(03 章 11 節)、usage(03 章 11 節)、公開鍵だけ(03 章 3 節)、場所(03 章 7 節)、持続性(03 章 7 節)、generate(04 章 10 節)、import(04 章 10 節)、ハッシュ(05 章 1 節)、乱数(05 章 1 節)、HMAC-SHA256(06 章 1 節)、AES(07 章 1 節)、カウンタ(08 章 4 節)、ノンス(08 章 1 節)、チャレンジ(09 章 5 節)、否認防止(09 章 1 節)、認証(09 章 1 節)、HKDF(10 章)、info(10 章 3 節)、salt(10 章 3 節)、パスワード(10 章 2 節)、ECDH(11 章 1 節)、共有秘密(11 章 1 節)、前方秘匿性(11 章 5 節)、ITS(13 章 1 節)、IAK(14 章 4 節)、Nonce(14 章 3 節)、アテステーション(14 章 1 節)、トークン(14 章)
1. パターンの見取り図
| 要件 | 主役の関数 | 鍵 | 章 |
|---|---|---|---|
| A. 装置の認証(クラウド接続) | psa_sign_hash / mbedtls_pk_setup_opaque | 永続 ECDSA P-256 鍵ペア、装置内生成 | 04・09・15 |
| B. 設定・資格情報の暗号化保存 | psa_aead_encrypt/decrypt + ITS | 永続 AES-256-GCM 鍵、装置内生成。または HUK からの導出 | 08・12・13 |
| C. OTA イメージの検証 | 分割ハッシュ + psa_verify_hash | 公開鍵(WRITE_ONCE ITS または read-only 鍵) | 05・09・13 |
| D. 装置間の暗号化通信 | ECDH + HKDF + AEAD、署名で認証 | 一時 ECDH 鍵、長期署名鍵 | 09・10・11 |
| E. センサーデータの改ざん防止 | psa_mac_compute | 永続 HMAC 鍵、または導出鍵 | 06・10 |
| F. 製造時の鍵注入と個体化 | psa_import_key + psa_key_derivation_* | マスター鍵から装置鍵を導出 | 04・10・12 |
| G. 健全性の証明 | psa_initial_attest_get_token | IAK(TF-M が管理) | 14 |
2. パターン A — 装置の認証
要件: クラウド(AWS IoT、Azure IoT Hub、自社サーバ)に TLS で接続し、装置が本物であることを証明する。
設計:
- 初回起動時、装置内で ECDSA P-256 鍵ペアを永続鍵として生成する(
SIGN_HASH、EXPORTなし) - 公開鍵をエクスポートし、CSR(証明書署名要求)を作って製造ラインの CA(認証局)に送り、装置証明書を受け取って PS に保存する。または公開鍵だけをサーバに登録する
- 実運用では、TLS クライアント認証の秘密鍵として
mbedtls_pk_setup_opaque()で PSA 鍵を指す
/* 初回のみ */
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_HASH);
psa_set_key_algorithm(&attr, PSA_ALG_ECDSA(PSA_ALG_ANY_HASH)); /* TLS はハッシュを交渉する */
psa_set_key_lifetime(&attr, PSA_KEY_LIFETIME_PERSISTENT);
psa_set_key_id(&attr, KEY_ID_DEVICE_SIGN);
PSA_CHECK(psa_generate_key(&attr, &key));
/* 毎回 */
mbedtls_pk_setup_opaque(&pk, KEY_ID_DEVICE_SIGN);
mbedtls_ssl_conf_own_cert(&conf, &crt, &pk);要点: アルゴリズムに PSA_ALG_ANY_HASH を使うのは、TLS 1.2/1.3 でサーバが SHA-256 以外を要求することがあるためである。 自社プロトコルなら PSA_ALG_SHA_256 に固定する。 TF-M 環境では、この鍵は Crypto パーティションの所有であり、非セキュア側が乗っ取られても署名を「依頼」することしかできない。 アテステーション(パターン G)を組み合わせると、乗っ取られた状態での依頼を検証側で拒否できる。
3. パターン B — 設定・資格情報の暗号化保存
要件: Wi-Fi パスワードやクラウドのトークンを、外部フラッシュや通常のファイルシステムに保存したい。
設計:
- TF-M があるなら、PS を使うだけでよい(13 章)。PS が AEAD とリプレイ防止を行う
- TF-M がない(Mbed TLS 単体)なら、自分で AEAD を組む
/* 鍵: 初回起動で生成、永続、AES-256-GCM、装置固有 */
psa_set_key_type(&attr, PSA_KEY_TYPE_AES);
psa_set_key_bits(&attr, 256);
psa_set_key_usage_flags(&attr, PSA_KEY_USAGE_ENCRYPT | PSA_KEY_USAGE_DECRYPT);
psa_set_key_algorithm(&attr, PSA_ALG_GCM);
psa_set_key_lifetime(&attr, PSA_KEY_LIFETIME_PERSISTENT);
psa_set_key_id(&attr, KEY_ID_CONFIG_AEAD);
/* 保存: [version(4)] [nonce(12)] [ciphertext+tag] を書く。version と "config" ラベルを AD に */
uint8_t ad[10] = "config"; put_be32(ad + 6, version);
psa_generate_random(nonce, 12);
psa_aead_encrypt(KEY_ID_CONFIG_AEAD, PSA_ALG_GCM, nonce, 12, ad, 10, plain, plen, ct, sizeof ct, &ct_len);
/* 読み出し: version を AD に含めて復号。失敗したら「壊れている/改ざん」として工場出荷状態へ */要点:
- AD にバージョンや保存場所の名前を入れると、別スロットのデータや古いデータの流用(ロールバック)を検出できる
- ロールバックを完全に防ぐには、最新バージョン番号を ITS に別途保存し、復号後に比較する(PS がやっていることと同じ)
- 鍵が「装置内で生成した永続鍵」であれば、フラッシュを丸ごとコピーして別の装置に載せても復号できない——ただし、その鍵自体が ITS にあり、ITS も同じフラッシュにあるなら意味がない。ITS がセキュア側にある(TF-M)か、鍵を HUK(ハードウェア固有鍵)から導出する必要がある
4. パターン C — OTA イメージの検証
要件: 無線で受け取ったファームウェアが、正規の署名者によるものか確認してから書き込む・起動する。
設計(MCUboot を使う場合は、MCUboot がこれを行う。以下は自前で行う場合):
- サーバは、イメージの SHA-256 に ECDSA で署名し、
[image] [manifest: version, size, hash, signature]を配布する - 装置は、受信しながら分割ハッシュを取り(05 章)、受信完了後に
psa_verify_hash()で署名を検証する - 検証成功後、バージョンが現在より新しいことを確認してから(ロールバック防止)、スロットを切り替える
/* 検証用公開鍵: 差し替えられない場所に */
/* TF-M: read-only の組み込み鍵 / MCUboot: ブートローダのコードに埋め込み / 単体: ITS WRITE_ONCE */要点:
- 「受信バッファに溜めてから一括ハッシュ」はメモリを食う。受信しながら
psa_hash_update()が定石 - 署名検証の前にイメージを実行してはいけないのは当然だが、検証前にフラッシュに書くのは可(書いてから検証し、失敗したら無効化する)。ただし AEAD で暗号化されたイメージを分割復号しながら書く場合、
verify前の出力を信用しないこと(08 章) - バージョン比較を忘れると、署名は正しいが脆弱性のある古いイメージに戻される
5. パターン D — 装置間の暗号化通信
要件: ゲートウェイとセンサー間で、盗聴・改ざん・なりすましを防いだ通信を自前プロトコルで行う(TLS を使えない無線など)。
設計(簡略化した TLS 1.3 の形):
- 両者は相手の長期署名公開鍵を事前に持つ(製造時登録、または証明書)
- 接続開始: 各自が一時 ECDH 鍵ペアを生成し、
(一時公開鍵 ∥ 乱数)に長期鍵で署名して送る - 受信した署名を検証し、
psa_key_derivation_key_agreement()で共有秘密を KDF に入れ、両者の乱数を salt に、方向を info にして送信鍵・受信鍵を導出する - 以後、AEAD でカウンタノンスを使って通信する。カウンタは方向ごとに独立
- 一定回数ごとに 2 からやり直す(鍵の更新)
要点:
- 「送信鍵」と「受信鍵」を分ける(info を変える)ことで、反射攻撃を防ぐ
- 乱数(nonce)を KDF の salt に混ぜることで、同じ長期鍵でも毎回違うセッション鍵になる
- 一時鍵は使い終わったら
psa_destroy_key()(前方秘匿性) - 「自前プロトコル」は落とし穴が多い。可能なら DTLS 1.2/1.3(UDP——順序も到達も保証しない軽量な転送方式——の上で動く TLS)か Noise プロトコルの既存実装を使い、PSA 鍵を
mbedtls_pk_setup_opaque()で渡す
6. パターン E — センサーデータの改ざん防止
要件: 暗号化は不要だが、記録されたデータが後から書き換えられていないことを保証したい。
設計:
- 各レコード
[timestamp] [value]に、HMAC-SHA256(切り詰め 8〜16 バイト)を付ける - 前のレコードの MAC を次のレコードの MAC 対象に含める(チェーン)と、途中のレコードの削除も検出できる
- 鍵は装置固有の永続 HMAC 鍵。検証側(サーバ)が同じ鍵を持つ必要があるので、パターン F の導出鍵にすると鍵の配布が楽になる
「否認防止」(装置が「そんな記録は出していない」と言えないようにする)が要件なら、MAC ではなく署名(09 章)を使う。ただし署名は 1 レコードあたり数 ms かかるので、まとめて(1 分分のレコードのハッシュに)署名する。
7. パターン F — 製造時の鍵注入と個体化
要件: 何万台もの装置に、装置ごとに違う秘密を安全に持たせたい。
設計 — 2 つの方式:
| 方式 | 手順 | 長所 | 短所 |
|---|---|---|---|
| 装置内生成 | 初回起動で鍵ペアを生成し、公開鍵だけ取り出して登録 | 秘密鍵が装置の外に一度も存在しない | 公開鍵の回収と登録のラインが要る。対称鍵には使えない |
| 導出注入 | 製造ラインの HSM がマスター鍵から HKDF(master, info=シリアル番号) で装置鍵を計算し、psa_import_key() で永続鍵として書き込む | サーバはマスター鍵とシリアル番号だけで全装置の鍵を再現でき、鍵データベースが要らない | マスター鍵の管理が要(HSM)。注入経路(治具と装置の間)の盗聴対策が要る |
要点:
- 注入用のコードとインタフェースは、製品ファームウェアから外すか、ライフサイクル状態で無効化する
- 注入した鍵は
EXPORTなし、READ_ONLY持続性にできる実装ならそうする - 導出鍵の info には、シリアル番号だけでなく用途と世代を入れる(
"dev-key/v1/" ∥ serial)。将来マスター鍵を更新したとき、世代で区別できる
8. パターン G — 健全性の証明
要件: サーバが「乗っ取られていない、正規ファームウェアの装置」だけを受け入れたい。
設計: パターン A の TLS 接続後、サーバがチャレンジを送り、装置は psa_initial_attest_get_token() の結果を返す(14 章)。 サーバは署名・Nonce・ライフサイクル・ソフトウェア構成要素を検証し、合格した接続だけにデータや鍵を渡す。
TLS の認証(パターン A)が「鍵を持っているか」、アテステーションが「何を動かしているか」——2 つは補完関係であり、両方あって初めて「正規の装置が正規のソフトで動いている」と言える。
9. 共通の設計チェック
どのパターンでも、実装前に次を紙に書き出す。
- 鍵の一覧: 名前、type、bits、usage、algorithm、lifetime、ID、生成方法(generate / import / derive)、誰が公開鍵を持つか
- ノンス・カウンタの管理: 誰が作り、どこに保存し、再起動でどうなるか
- 失敗時の動作: 復号失敗・検証失敗で何をするか(工場出荷状態、ログ、拒否)
- 鍵の更新: 更新の手順と、更新中に電源が切れたときの状態
- ストレージの位置: ITS / PS / 通常のフラッシュのどれに何が乗っているか。フラッシュを丸ごと読まれたとき何が漏れるか
10. 手を動かす
要件から呼び出し列を組み立てる
この章のポイント
- 要件は「鍵の属性 + 関数の呼び出し列 + 保存場所」に翻訳する。7 つのパターンで大半をカバーできる
- 装置の認証: 永続 ECDSA 鍵を装置内生成、TLS には
mbedtls_pk_setup_opaque() - 保存データ: TF-M があれば PS。なければ AEAD + AD にバージョン + ITS にロールバック番号
- OTA: 受信しながら分割ハッシュ、
verify_hash、バージョン比較 - 通信: 一時 ECDH + 署名 + HKDF(方向別 info)+ AEAD(カウンタノンス)。可能なら DTLS を使う
- 製造: 装置内生成(非対称)か マスター鍵からの導出注入(対称)