PSA APIs 13 · セキュアストレージ — ITS と PS で小さな秘密を保存する

Chapter 13

セキュアストレージ — ITS と PS で小さな秘密を保存する

この章のゴール.

psa_its_set/get/get_info/remove の 4 関数と、Protected Storage との違いを理解し、 何を ITS に、何を PS に、何を PSA 鍵にすべきかを決められるようになること。 WRITE_ONCE フラグとリプレイ保護の意味を説明できること。

この章で使う既出の用語(定義は各リンク先). PSA(01 章 1 節)、ビット(02 章 4 節)、type(03 章 11 節)、場所(03 章 7 節)、作成(04 章 1 節)、MAC(06 章 1 節)、AES(07 章 1 節)、カウンタ(08 章 4 節)、ノンス(08 章 1 節)、平文(08 章 1 節)、HKDF(10 章)、info(10 章 3 節)、パスワード(10 章 2 節)、容量(10 章 5 節)

1. 2 つのストレージ API

PSA Secure Storage API は、小さなデータを「UID」という番号で保存する、単純な鍵値ストアである。 ファイルシステムではない——ディレクトリもファイル名もなく、64 ビットの整数で 1 つのデータ塊を指す。

ITS(Internal Trusted Storage、内部信頼ストレージ)PS(Protected Storage、保護ストレージ)
ヘッダpsa/internal_trusted_storage.hpsa/protected_storage.h
関数の接頭辞psa_its_psa_ps_
置き場所チップ内部のフラッシュ(セキュア側)外部フラッシュでもよい
保護の仕組み「内部にあるから読めない」(フラッシュ読み出し保護+TrustZone)暗号化+改ざん検出+リプレイ防止をソフトウェアで行う(AEAD + ITS に保存したカウンタ)
容量小さい(数 KB〜数十 KB)大きい(外部フラッシュのサイズ次第)
速さ速い遅い(暗号化と MAC の計算)
誰が使うかPSA Crypto の永続鍵の保存先。アテステーションのカウンタ。ブートの状態アプリケーションの設定、証明書、ログ、資格情報
呼べる側セキュア側の内部サービス。TF-M では非セキュア側からも呼べる設定が既定アプリケーション(非セキュア側)

ITS の "Trusted" は「信頼できる場所にある」であって「暗号化されている」ではない。 ITS 自体は平文で書く(TF-M の既定)。 その代わり ITS のフラッシュ領域はセキュア側だけがアクセスでき、チップ外から読めない前提で使う。

PS はその前提が成り立たない場所(外部 SPI フラッシュなど)のためのもので、書く前に AEAD で暗号化し、ITS に保存した鍵とカウンタで改ざんとロールバック(古いデータを書き戻す攻撃)を検出する。

ITS と PSITS — Internal Trusted Storageチップ内部のフラッシュ(セキュア側)小さい・速い平文で書く「内部にあるから信頼」。読み出し保護が根拠PSA Crypto の永続鍵の保存先アテステーションのカウンタ、ブート状態psa_its_set / get / get_info / removePS — Protected Storage外部フラッシュでもよい大きい・遅いAEAD で暗号化+改ざん検出+リプレイ防止鍵は HUK から導出、表は ITS にアプリケーションの設定・資格情報・証明書psa_ps_set / get / get_info / remove (+create, set_extended)どちらも UID(64 ビット整数)で引く鍵値ストア。ファイルシステムではない
ITS は「場所」で守り、PS は「暗号」で守る

2. ITS の 4 関数

typedef uint64_t psa_storage_uid_t;
typedef uint32_t psa_storage_create_flags_t;

struct psa_storage_info_t {
    size_t capacity;                        /* 確保された容量 */
    size_t size;                            /* 実際のデータ長 */
    psa_storage_create_flags_t flags;       /* 作成時のフラグ */
};

psa_status_t psa_its_set(psa_storage_uid_t uid, size_t data_length, const void *p_data,
                         psa_storage_create_flags_t create_flags);
psa_status_t psa_its_get(psa_storage_uid_t uid, size_t data_offset, size_t data_size,
                         void *p_data, size_t *p_data_length);
psa_status_t psa_its_get_info(psa_storage_uid_t uid, struct psa_storage_info_t *p_info);
psa_status_t psa_its_remove(psa_storage_uid_t uid);
関数何をするか注意
psa_its_setUID にデータを書く。あれば上書き、なければ作る部分書き込みはない。常にデータ全体を渡す。WRITE_ONCE のデータに再度 set すると NOT_PERMITTED
psa_its_getUID からデータを読む。data_offset からの部分読み出しができる出力の 3 つ組(buf, size, &len)。data_size が実データより大きければ実データ長だけ返る(BUFFER_TOO_SMALL ではない)
psa_its_get_info長さとフラグを調べる「存在するか」の確認にも使う(なければ DOES_NOT_EXIST)
psa_its_remove消すWRITE_ONCE のデータは消せない(NOT_PERMITTED)
#define UID_BOOT_COUNT   ((psa_storage_uid_t)0x0000000100000001ULL)

psa_status_t bump_boot_count(uint32_t *count)
{
    size_t len;
    psa_status_t st = psa_its_get(UID_BOOT_COUNT, 0, sizeof *count, count, &len);
    if (st == PSA_ERROR_DOES_NOT_EXIST) { *count = 0; st = PSA_SUCCESS; }
    if (st != PSA_SUCCESS || (len != 0 && len != sizeof *count)) return st ? st : PSA_ERROR_DATA_CORRUPT;
    (*count)++;
    return psa_its_set(UID_BOOT_COUNT, sizeof *count, count, PSA_STORAGE_FLAG_NONE);
}

UID は 64 ビットで、0 は無効である。 鍵 ID と同じく、プロジェクトのヘッダで台帳を作る。 TF-M では、ITS の UID は呼び出したクライアント(パーティション)ごとに名前空間が分かれる——非セキュア側の UID 5 とセキュア側の Crypto パーティションの UID 5 は別物である。だから PSA 永続鍵のストレージと衝突する心配はない。

ITS の 4 関数psa_its_set(uid, len, data, flags)あれば上書き、なければ作る。全体を渡すpsa_its_get(uid, offset, size, buf, &len)offset からの部分読み出し可psa_its_get_info(uid, &info)capacity / size / flags。存在確認にpsa_its_remove(uid)WRITE_ONCE は消せないフラグは最初に作るときだけ有効WRITE_ONCE = 1(二度と変えられない)、NO_CONFIDENTIALITY = 2、NO_REPLAY_PROTECTION = 4TF-M では UID の名前空間がクライアントごと非セキュア側の UID 5 と Crypto パーティションの UID 5 は別物。永続鍵と衝突しない
UID は 64 ビット。0 は無効。プロジェクトの台帳で管理する

3. 作成フラグ

psa_its_set() の最後の引数は、最初に作るときだけ意味を持つフラグである(上書き時は無視される)。

フラグ値意味
PSA_STORAGE_FLAG_NONE0通常。上書き・削除できる
PSA_STORAGE_FLAG_WRITE_ONCE1一度書いたら二度と変えられない・消せない。工場出荷時のデータ(装置 ID、ルート公開鍵)向け。テスト中に使うと消せなくなるので注意
PSA_STORAGE_FLAG_NO_CONFIDENTIALITY2秘匿性は不要(改ざん検出だけ)。PS で暗号化を省いて速くする
PSA_STORAGE_FLAG_NO_REPLAY_PROTECTION4リプレイ防止は不要。PS でカウンタ更新(ITS 書き込み)を省いて速くする

後の 2 つは PS で意味を持つ。ITS はフラグを記録するだけで動作は変わらない(実装による)。

4. PS の関数

PS は ITS と同じ 4 関数(psa_ps_set/get/get_info/remove)に加え、次の拡張がある。

psa_status_t psa_ps_create(psa_storage_uid_t uid, size_t capacity, psa_storage_create_flags_t create_flags);
psa_status_t psa_ps_set_extended(psa_storage_uid_t uid, size_t data_offset, size_t data_length, const void *p_data);
uint32_t     psa_ps_get_support(void);

PS の内部動作(TF-M の場合)を知っておくと、性能と安全性の限界が分かる。

  1. データを AEAD(AES-GCM)で暗号化する。鍵はハードウェア固有鍵(HUK、Hardware Unique Key。チップごとに違う焼き込み済みの秘密)から HKDF で導出
  2. 各オブジェクトにバージョン番号を付け、それを AD に含める
  3. オブジェクト表(どの UID がどこにあるか)を ITS に保存する。これがリプレイ防止の根拠——外部フラッシュを丸ごと古い状態に戻しても、ITS 側の表と食い違う
  4. フラッシュへの書き込みはセクタ単位なので、小さなデータの更新でも数 ms〜数十 ms かかる
PS の内部(TF-M)— ITS の上に暗号を重ねるpsa_ps_set(uid, data)HUK → HKDF → PS 鍵ハードウェア固有鍵から導出AES-GCM で暗号化バージョン番号を AD に外部フラッシュに書く暗号文+タグオブジェクト表(UID → 位置・バージョン)を ITS に保存外部フラッシュを古い状態に戻しても表と食い違う → リプレイ検出書き込みごとに ITS も更新するので遅い。NO_REPLAY_PROTECTION で省ける
PS の安全性は ITS と HUK に依存する

5. 何をどこに置くか

データ置き場所理由
秘密鍵、対称鍵PSA 永続鍵(12 章)ストレージに自分で書かない。用途制限と非エクスポートが効く
相手の公開鍵、ルート証明書PSA 永続鍵(公開鍵型)または ITS に WRITE_ONCE差し替えられないことが重要
装置 ID、シリアル番号ITS WRITE_ONCE小さく、変わらない
起動回数、AEAD ノンスのカウンタ、ロールバック番号ITS小さく、頻繁に更新。改ざんされたくない
Wi-Fi のパスワード、クラウドの接続情報PS秘密だがサイズが大きめ。アプリケーションが読み書きする
装置証明書(自分の)PS または ITS秘密ではないが改ざんは困る。サイズは 500〜1500 バイト
設定値(ユーザー設定)PS秘密性は低いが改ざん検出は欲しい
ログ、計測データ通常のファイルシステムセキュアストレージは遅く小さい。必要なら AEAD で自分で暗号化

原則: 鍵は鍵 API に、小さな不変データは ITS に、アプリケーションの秘密は PS に、大きなデータはセキュアストレージの外に。

「パスワードを ITS に入れればよいのでは」.

ITS は非セキュア側からも呼べる(TF-M の既定)ので、非セキュア側のコードが乗っ取られれば psa_its_get() で読める。 セキュアストレージは「物理的な読み出し」と「他のパーティションからのアクセス」を防ぐが、書いた本人からは読める。 だから「アプリケーションが読める必要のない秘密」は PSA 鍵にして、値を取り出せない形で持つのが正しい。 Wi-Fi パスワードのように、アプリケーションが平文で必要な秘密だけを PS に置く。

6. ITS の実装と設定(Mbed TLS / TF-M)

環境ITS の実体設定
Mbed TLS(PC)MBEDTLS_PSA_ITS_FILE_C: カレントディレクトリのファイルMBEDTLS_PSA_CRYPTO_STORAGE_C と組で有効化
Mbed TLS(マイコン、単体)自分で psa_its_* を実装する(内蔵フラッシュに書く。ウェアレベリング——書き込み回数を均して寿命を延ばす処理——は自前)。または Zephyr(オープンソースの RTOS、リアルタイム OS)などのセキュアストレージ実装MBEDTLS_PSA_CRYPTO_STORAGE_C を有効にし、psa_its_* をリンクする
TF-MITS パーティション(TFM_PARTITION_INTERNAL_TRUSTED_STORAGE)。フラッシュ領域は ITS_FLASH_AREA_ADDR / ITS_FLASH_AREA_SIZE で指定TFM_PARTITION_PROTECTED_STORAGE で PS を追加。ITS_MAX_ASSET_SIZE(既定 512 バイト前後)を超えるデータは書けない

TF-M の ITS_MAX_ASSET_SIZE は初心者が必ず踏む制限である。 2048 ビットの RSA 鍵ペア(約 1200 バイト)を永続鍵にしようとして PSA_ERROR_INSUFFICIENT_STORAGE や INVALID_ARGUMENT が出たら、これを疑う。 ビルド時に -DITS_MAX_ASSET_SIZE=2048 のように広げる(フラッシュ領域も増える)。

7. 手を動かす

ITS を操作する

8. 仕様書に逃がす

関数・定数用途
psa_ps_create() / psa_ps_set_extended() / psa_ps_get_support()部分書き込み(対応実装のみ)
PSA_STORAGE_SUPPORT_SET_EXTENDED上の対応を示すビット
PSA_ITS_API_VERSION_MAJOR/MINOR、PSA_PS_API_VERSION_*API バージョン
PSA_ERROR_DATA_CORRUPT / PSA_ERROR_DATA_INVALID保存データの破損 / 形式不正。バックアップからの復旧やリセットの判断に使う

この章のポイント