PSA APIs 01 · PSA とは何か — 仕様・実装・認証の関係をほどく

Chapter 01

PSA とは何か — 仕様・実装・認証の関係をほどく

この章のゴール.

「PSA」という 3 文字が、文脈によって仕様を指したり認証を指したり 実装を指したりすることを理解し、 自分が書こうとしているコードがそのどこに乗るのかを地図の上で指せるようになること。

1. PSA は 1 つの物ではない

PSA(Platform Security Architecture、プラットフォームセキュリティアーキテクチャ)は、 Arm 社が 2017 年に提唱した、IoT 機器(Internet of Things、ネットにつながる組み込み機器)のセキュリティを「設計の型」として標準化する枠組みの総称である。 初心者が混乱する最大の理由は、この 1 つの名前の下に、性質の違う 3 種類のものが束ねられていることにある。

名前何か読者にとっての意味
PSA Certified(PSA 認証)第三者機関がデバイスやチップのセキュリティを評価する認証制度。Level 1〜3 がある製品を出すときに取るもの。コードには直接関係しない
PSA Certified APIs(PSA 機能 API)暗号・ストレージ・アテステーションなどの C 言語の関数仕様。仕様書は無償公開本シリーズの主題。読者が呼ぶ関数はここで定義されている
参照実装(TF-M、Mbed TLS)上の API を実際に動かすオープンソースのコード読者がリンクするライブラリ。関数の中身はここにある

つまり「PSA を使う」と言ったとき、コードを書く人にとっては「PSA Certified APIs という関数群を呼ぶ」という意味である。 そして、その関数の実体は Mbed TLS や TF-M(Trusted Firmware-M)という実装が提供する。

「PSA」と呼ばれる 3 つのものPSA Certified(認証制度)第三者機関がチップ・製品のセキュリティを評価。Level 1〜3。製品を出すときに取るPSA Certified APIs(関数仕様)Crypto / Storage / Attestation / FWU の C 関数仕様。無償公開。本編の主題参照実装(コード)TF-M と Mbed TLS / TF-PSA-Crypto。API の中身を実際に動かすオープンソース製品の評価に使うコードに直接は関係しない読者が呼ぶ関数はここで定義psa_import_key() など読者がリンクするライブラリ関数の中身はここにある「PSA を使う」=コードを書く人にとっては「PSA Certified APIs の関数を呼ぶ」こと
認証制度・関数仕様・参照実装の 3 つが同じ名前で呼ばれる。本編は真ん中の「関数仕様」を扱う

なぜ「API を標準化する」ことに価値があるのか.

PSA 以前、マイコンで AES 暗号を使うコードは、チップベンダごとに違う関数名・違う引数・違う鍵の扱いで書かれていた。 ST のチップ用に書いたコードは NXP に持っていけば全部書き直しであり、 ハードウェア暗号エンジン(暗号計算を専用回路で行う周辺機器)を使うか ソフトウェアで計算するかによっても、呼び方が変わった。

PSA Crypto API は、「鍵を作る」「署名する」「暗号化する」という操作の呼び方を 1 つに固定した。 中身がソフトウェアでも、ハードウェア暗号エンジンでも、セキュアエレメント(鍵を外に出さずに暗号処理する専用チップ)でも、 アプリケーションのコードは同じである。 これが、本シリーズで関数の説明に力を入れる理由でもある——一度覚えれば、どのチップでも使えるからだ。

2. PSA Certified APIs の一覧

PSA 機能 API は、用途ごとに独立した仕様書として公開されている。 本シリーズで扱う範囲と、扱わない範囲を先に示す。

APIヘッダ何をするか本シリーズ
Crypto APIpsa/crypto.h鍵管理、ハッシュ、MAC、暗号、AEAD、署名、鍵導出、鍵合意、乱数03 章〜12 章(中心)
Secure Storage APIpsa/internal_trusted_storage.h、psa/protected_storage.h小さなデータを改ざん・漏洩から守って保存する13 章
Attestation APIpsa/initial_attestation.h「このデバイスは何者で、どんなソフトが動いているか」の署名付き証明書を作る14 章
Firmware Update APIpsa/update.hファームウェア更新の受け取り・検証・切り替えを標準化18 章で概要のみ
Secure Partition / Firmware Framework(FF-M)psa/service.h、psa/client.hセキュア側のサービスを部屋(パーティション)に分けて動かす仕組み16 章で必要な分だけ
Status codespsa/error.h全 API 共通の戻り値 psa_status_t02 章

API の数で圧倒される必要はない。 実際のアプリケーションが日常的に呼ぶ関数は、Crypto API の中でも 20〜30 個に収まる。 本シリーズはその「よく使う 30 個」を丁寧に説明し、残りは「そういう関数もある。必要になったら仕様書のこの節を見よ」という形で逃がす。

PSA Certified APIs の一覧と、本編で扱う範囲Crypto APIpsa/crypto.h — 鍵管理・ハッシュ・MAC・暗号・AEAD・署名・鍵導出・鍵合意・乱数03〜12 章(中心)Secure Storage APIpsa/internal_trusted_storage.h, psa/protected_storage.h — 小さなデータを守って保存13 章Attestation APIpsa/initial_attestation.h — 「何者で何を動かしているか」の署名付き申告14 章Firmware Update APIpsa/update.h — 更新イメージの受け取り・検証・切り替え18 章で概要のみFirmware Framework(FF-M)psa/service.h, psa/client.h — セキュア側の部屋(パーティション)の仕組み16 章で必要な分Status codespsa/error.h — 全 API 共通の戻り値 psa_status_t02 章実際のアプリケーションが日常的に呼ぶのは、Crypto API の 20〜30 関数に収まる
色の濃い 3 つが本編の中心。薄い 3 つは概要と参照先だけを示す

3. コードはどこで動くのか — 2 つの配置

PSA API を呼ぶコードには、2 つのまったく違う動かし方がある。 これを最初に区別しておかないと、後の章の「鍵は外に出ない」という話が理解できない。

3.1 ライブラリとして同じ場所で動かす(Mbed TLS 単体)

Mbed TLS(Arm が主導するオープンソースの暗号・TLS ライブラリ)は、PSA Crypto API を普通の C ライブラリとして提供する。 アプリケーションと同じメモリ空間、同じ特権レベルで動く。 psa_import_key() を呼べば、鍵はライブラリ内部の配列に置かれる。

3.2 セキュア側に置いて、壁越しに呼ぶ(TF-M)

TF-M(Trusted Firmware-M。Cortex-M(Arm のマイコン向け CPU コア)向けのセキュアファームウェアの参照実装)は、 PSA API の実体をセキュア領域(Arm TrustZone で隔離された、通常のコードから読めないメモリと CPU 状態)に置く。 アプリケーションは非セキュア領域で動き、psa_import_key() を呼ぶと、 その呼び出しは薄い橋渡しコード(ベニア、veneer)を通ってセキュア側に渡され、 セキュア側の本物の関数が実行されて、結果だけが返ってくる。

同じソースコードが動く 2 つの配置A. ライブラリとして同じ場所で(Mbed TLS 単体)アプリケーションpsa_import_key(...) を呼ぶMbed TLS(PSA Crypto 実装)普通の C 関数。同じメモリ空間・同じ特権鍵ストア(ライブラリ内の配列)アプリのバグで読まれ得るB. セキュア側に置いて壁越しに(TF-M)非セキュア領域アプリケーションpsa_import_key(...) ← 呼び方は同じNS インタフェース引数を包んで渡すセキュア領域ベニア(入口)SG 命令で状態切替Crypto パーティション本物の psa_import_key鍵ストア非セキュア側から物理的に読めないTrustZone の壁PSA API は「関数の呼び方」だけを決め、「どこで実行されるか」は決めない
左: アプリと同じ場所で動く(覚えるのに最適)。右: セキュア側で動き、鍵は壁の向こう(製品向け)。呼び出すコードは同じ

重要なのは、どちらの配置でもアプリケーションのソースコードが同じだということである。 PSA API は「関数の呼び方」だけを決め、「どこで実行されるか」は決めない。 だから、まず Mbed TLS 単体で API を覚え、製品では TF-M に載せ替える、という進め方ができる。 本シリーズの 03 章〜14 章は配置に依存しない書き方をし、15 章と 16 章で 2 つの配置それぞれの具体的な作り方を扱う。

4. 「鍵が外に出ない」という設計思想

PSA Crypto API のすべての設計は、1 つの思想から派生している。

鍵の値をアプリケーションに渡さない。アプリケーションは鍵の「番号」だけを持つ。

従来のライブラリでは、AES で暗号化するには 16 バイトの鍵をバイト配列としてアプリケーションが持ち、それを関数に渡した。 PSA では、鍵をライブラリに預ける(psa_import_key())と、 引き換えに 鍵 ID(psa_key_id_t、32 ビットの整数)が返ってくる。 以後、暗号化も署名も、アプリケーションは鍵 ID を渡すだけであり、鍵の値には二度と触らない。

psa_key_id_t key;                     /* 鍵の「番号」。値ではない */
psa_import_key(&attributes, key_bytes, 16, &key);
memset(key_bytes, 0, 16);             /* 預けたので、手元の値はもう要らない */

/* 以後は番号だけで使う */
psa_cipher_encrypt(key, PSA_ALG_CTR, plain, plain_len, out, sizeof out, &out_len);

この設計が、03 章で扱う「属性」という概念につながる。 鍵を預けるときに「この鍵は AES-CTR の暗号化にしか使えない」「エクスポート(値の取り出し)は禁止」と用途を宣言し、 以後ライブラリがそれを強制する。 用途を宣言し忘れると、後で PSA_ERROR_NOT_PERMITTED というエラーで止まる——初心者が最初に踏む地雷である。

鍵の「値」ではなく「番号」を持つアプリケーション鍵のバイト列を一時的に持つpsa_import_key(預ける)PSA の鍵ストア値+方針(属性)を保持鍵 ID(32 ビット整数)が返るアプリケーション以後は鍵 ID だけを持つ。値は memset で消すpsa_sign_hash(key_id, …)PSA が鍵を使って計算方針に合わない使い方は NOT_PERMITTED結果(署名など)だけ返る値を取り出す関数がないpsa_export_key は属性に EXPORT 用途を付けた鍵にしか効かない。付けなければ、乗っ取られたコードも鍵を読めない鍵を預けるときに用途を宣言し、以後ライブラリがそれを強制する(3 章)
鍵の値はライブラリに預け、アプリケーションは番号(鍵 ID)で使う。値を返す関数は原則として存在しない

5. 本シリーズの読み方

各章は次の構成で書かれている。

  1. その章の操作が「何をするものか」を、暗号の予備知識なしで説明する
  2. 一発関数(single-part)——1 回の呼び出しで完結する関数を先に覚える
  3. 分割関数(multi-part)——大きなデータを少しずつ処理する関数群を、状態遷移として理解する
  4. サイズを決めるマクロ——バッファをいくつ確保すべきかを計算するマクロ
  5. 落とし穴——実際に踏む順に並べたエラーの原因
  6. 仕様書に逃がす——めったに使わない関数の名前と、仕様書のどこを見るか

暗号そのものの理論(AES の内部構造、楕円曲線の数学など)は扱わない。 それは 暗号編 にある。 本シリーズは「その暗号を PSA の関数でどう呼ぶか」に集中する。

仕様書の読み方.

PSA Certified Crypto API の仕様書(バージョン 1.3 が 2025 年時点の最新)は、 arm-software.github.io/psa-api で HTML として公開されている。 各関数のページは「Parameters / Returns / Description」の形式で、関数を知っている人が引数を確認するための辞書である。 本シリーズは、その辞書を引けるようになるまでの「読み物」だと思ってほしい。

6. 開発環境を用意する

API を覚えるだけなら、PC 上の Mbed TLS で十分である。 PSA Crypto API は Mbed TLS 2.x 系の後半から入り、3.x 系で成熟し、Mbed TLS 4.0(2025 年末)からは PSA が唯一の暗号 APIになった (暗号部分は TF-PSA-Crypto という別リポジトリに切り出された。15 章)。

git clone https://github.com/Mbed-TLS/mbedtls.git
cd mbedtls
git submodule update --init          # 4.x では tf-psa-crypto を取り込む
cmake -B build
cmake --build build

最初のプログラムは、初期化してハッシュを 1 つ計算するだけでよい。

#include "psa/crypto.h"
#include <stdio.h>

int main(void)
{
    psa_status_t st = psa_crypto_init();      /* 全 API の前に必ず 1 回 */
    if (st != PSA_SUCCESS) { printf("init failed: %d\n", (int)st); return 1; }

    const char *msg = "hello";
    uint8_t hash[PSA_HASH_LENGTH(PSA_ALG_SHA_256)];   /* 32 バイト */
    size_t hash_len;
    st = psa_hash_compute(PSA_ALG_SHA_256, (const uint8_t *)msg, 5,
                          hash, sizeof hash, &hash_len);
    printf("status=%d len=%u first=%02x\n", (int)st, (unsigned)hash_len, hash[0]);
    return 0;
}

この 10 行に、PSA API の作法がほぼ全部入っている—— 初期化、戻り値 psa_status_t、アルゴリズムを表す定数、サイズを計算するマクロ、出力バッファと実際の長さのペア。 次章で、この作法を 1 つずつ確かめる。

7. 実装と仕様の対応を確かめる

仕様と実装の地図を歩く


この章のポイント