Chapter 03
鍵と属性 — 「何の鍵で、何に使えるか」を宣言する
この章のゴール.
psa_key_attributes_tの 6 つの項目(種類・長さ・用途・アルゴリズム・寿命・ID)を、 それぞれ何を守るために存在するのかまで含めて説明でき、 目的に応じた属性の組み合わせを自分で書けるようになること。
1. 鍵は「値」ではなく「値+方針」である
従来の暗号ライブラリでは、鍵は 16 バイトや 32 バイトのただのバイト列だった。 そのバイト列を AES に渡せば AES 鍵として、HMAC に渡せば HMAC 鍵として使えてしまう。 「同じ鍵を 2 つの目的に使う」のは暗号設計の禁じ手であるが、ライブラリはそれを止められなかった。
PSA では、鍵は値と方針(policy)のセットとして管理される。 鍵を作るときに「これは AES 鍵で、CTR モードの暗号化・復号にだけ使う」と宣言し、 以後、それ以外の使い方は PSA_ERROR_NOT_PERMITTED で拒否される。 この宣言を書き込む構造体が psa_key_attributes_t(鍵属性)である。
2. 属性構造体の使い方
属性は不透明な構造体(中身を直接触ってはいけない構造体)で、 必ず PSA_KEY_ATTRIBUTES_INIT で初期化し、setter 関数で項目を設定する。
psa_key_attributes_t attr = PSA_KEY_ATTRIBUTES_INIT;
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_VOLATILE); /* ⑤ 寿命(省略時は揮発) */
/* psa_set_key_id(&attr, ...); ⑥ ID(永続鍵のときだけ) */
psa_status_t st = psa_generate_key(&attr, &key);
psa_reset_key_attributes(&attr); /* 使い終わったら片付ける */psa_reset_key_attributes() は、属性構造体が内部で確保したメモリがあれば解放し、初期状態に戻す。 現在の実装ではほとんど何もしないが、将来の拡張で属性が動的メモリを持つ可能性に備えて仕様が要求している。 psa_get_key_attributes()(既存の鍵から属性を読み出す。04 章)で得た構造体には特に必須である。
6 つの項目を 1 つずつ見る。
3. 種類 psa_key_type_t — その鍵はどの暗号のものか
鍵の種類は、鍵の値のバイト列をどう解釈するかを決める。 AES 鍵はただの 16/24/32 バイトだが、楕円曲線の秘密鍵は「ある曲線上の整数」であり、 RSA 鍵は「複数の大きな整数の組」である。
| 定数 | 値 | 意味 | 鍵長(ビット) |
|---|---|---|---|
PSA_KEY_TYPE_AES | 0x2400 | AES ブロック暗号の鍵 | 128 / 192 / 256 |
PSA_KEY_TYPE_CHACHA20 | 0x2004 | ChaCha20 ストリーム暗号の鍵 | 256 |
PSA_KEY_TYPE_HMAC | 0x1100 | HMAC の鍵 | 任意(ハッシュのブロック長以下が普通) |
PSA_KEY_TYPE_DERIVE | 0x1200 | 鍵導出の入力(マスター秘密)。それ以外に使えない | 任意 |
PSA_KEY_TYPE_RAW_DATA | 0x1001 | 暗号には使えない生データ。ストレージのように扱うためのもの | 任意 |
PSA_KEY_TYPE_PASSWORD | 0x1203 | パスワード(PBKDF2 の入力) | 任意 |
PSA_KEY_TYPE_ECC_KEY_PAIR(family) | 0x7100 ∣ family | 楕円曲線の鍵ペア(秘密鍵。公開鍵は導出できる) | 曲線による(256 など) |
PSA_KEY_TYPE_ECC_PUBLIC_KEY(family) | 0x4100 ∣ family | 楕円曲線の公開鍵だけ | 同上 |
PSA_KEY_TYPE_RSA_KEY_PAIR | 0x7001 | RSA 鍵ペア | 2048 / 3072 / 4096 |
PSA_KEY_TYPE_RSA_PUBLIC_KEY | 0x4001 | RSA 公開鍵 | 同上 |
楕円曲線の family(族)は、曲線の種類を表す 8 ビット値である。
| family | 曲線 | 使う場面 |
|---|---|---|
PSA_ECC_FAMILY_SECP_R1 (0x12) | NIST P-256 / P-384 / P-521 | ECDSA 署名、ECDH。最も一般的。鍵長で曲線を選ぶ(256 → P-256) |
PSA_ECC_FAMILY_MONTGOMERY (0x41) | Curve25519 (255 ビット) / Curve448 | X25519 鍵合意 |
PSA_ECC_FAMILY_TWISTED_EDWARDS (0x42) | Ed25519 (255 ビット) / Ed448 | EdDSA 署名 |
PSA_ECC_FAMILY_SECP_K1 (0x17) | secp256k1 | ブロックチェーン系 |
PSA_ECC_FAMILY_BRAINPOOL_P_R1 (0x30) | Brainpool | 欧州の規格 |
「鍵ペア」と「公開鍵」を別の種類にしている点に注目してほしい。 相手から受け取った公開鍵を psa_import_key() するときは PSA_KEY_TYPE_ECC_PUBLIC_KEY(...)、 自分の秘密鍵を生成するときは PSA_KEY_TYPE_ECC_KEY_PAIR(...) である。 鍵ペアからは psa_export_public_key() で公開鍵部分だけを取り出せる(04 章)。
4. 長さ bits — 生成のときに必要、インポートのときは自動
鍵長はビット単位で指定する。 psa_generate_key() で鍵を作るときは必須で、 psa_import_key() でバイト列から取り込むときは省略できる(バイト列の長さから決まる。指定した場合は一致しないと PSA_ERROR_INVALID_ARGUMENT)。
注意すべき鍵長:
- AES: 128 / 192 / 256。「AES-256 は AES-128 より安全」だが、マイコンでは速度差が 1.4 倍ほどある
- P-256: 256。P-521 は 521(512 ではない)
- Curve25519 と Ed25519: 255(256 ではない。PSA は数学的な曲線の位数の長さで数える)
- RSA: 2048 以上。マイコンでの鍵生成は数十秒かかることがある
- HMAC: ハッシュのブロック長(SHA-256 なら 512 ビット)以下を推奨。長すぎるとハッシュされて短くなる
5. 用途 psa_key_usage_t — 何をしてよいか
用途フラグは、その鍵で呼んでよい操作を 1 ビットずつ表す。 複数を OR で組み合わせる。
| フラグ | 値 | 許す操作 |
|---|---|---|
PSA_KEY_USAGE_ENCRYPT | 0x0100 | psa_cipher_encrypt、psa_aead_encrypt、psa_asymmetric_encrypt |
PSA_KEY_USAGE_DECRYPT | 0x0200 | 上の復号側 |
PSA_KEY_USAGE_SIGN_MESSAGE | 0x0400 | psa_sign_message、psa_mac_compute |
PSA_KEY_USAGE_VERIFY_MESSAGE | 0x0800 | psa_verify_message、psa_mac_verify |
PSA_KEY_USAGE_SIGN_HASH | 0x1000 | psa_sign_hash(SIGN_MESSAGE も暗黙に含む) |
PSA_KEY_USAGE_VERIFY_HASH | 0x2000 | psa_verify_hash(VERIFY_MESSAGE も含む) |
PSA_KEY_USAGE_DERIVE | 0x4000 | 鍵導出・鍵合意の入力にする |
PSA_KEY_USAGE_VERIFY_DERIVATION | 0x8000 | 導出結果の検証(psa_key_derivation_verify_*) |
PSA_KEY_USAGE_EXPORT | 0x0001 | psa_export_key(値を取り出す)を許す |
PSA_KEY_USAGE_COPY | 0x0002 | psa_copy_key を許す |
PSA_KEY_USAGE_CACHE | 0x0004 | 実装が鍵をメモリに保持し続けることを許す(性能のため) |
用途を最小にするのが原則である。 署名鍵に EXPORT を付けてはいけないし、暗号化しかしない鍵に DECRYPT は要らない。 特に PSA_KEY_USAGE_EXPORT は「鍵をアプリケーションに返してよい」という宣言であり、 これを付けない限り、psa_export_key() は PSA_ERROR_NOT_PERMITTED で拒否される。 PSA の「鍵が外に出ない」保証は、このフラグを付けないことで成立している。
なお公開鍵はいつでもエクスポートできる。 psa_export_public_key() は用途フラグに関係なく成功する(公開鍵は秘密ではないので当然である)。
MAC の用途は SIGN / VERIFY.
MAC(メッセージ認証コード)の計算は
PSA_KEY_USAGE_SIGN_MESSAGE、検証はPSA_KEY_USAGE_VERIFY_MESSAGEである。 「MAC は対称鍵だから ENCRYPT では?」と思ってENCRYPTを付けるとNOT_PERMITTEDになる。 PSA は「MAC=対称鍵による署名」と整理している。
6. アルゴリズム psa_algorithm_t — どの計算に使ってよいか
属性の algorithm は、この鍵で使ってよいアルゴリズムを 1 つに固定する。 AES 鍵に PSA_ALG_GCM を設定すれば、その鍵で PSA_ALG_CTR を指定して暗号化しようとすると NOT_PERMITTED になる。
「なぜ 1 つに縛るのか」——同じ鍵を異なるアルゴリズムで使うと、片方の弱点でもう片方が壊れることがあるからだ。 例えば、同じ RSA 鍵を「署名」と「復号」の両方に使うと、攻撃者は署名させることで復号結果を手に入れられる(暗号編)。 鍵ごとにアルゴリズムを 1 つに固定するのは、この種の「用途混同」攻撃を構造的に防ぐためである。
psa_set_key_algorithm(&attr, PSA_ALG_ECDSA(PSA_ALG_SHA_256)); /* この鍵は ECDSA+SHA-256 専用 */例外的に幅を持たせる方法もある。
PSA_ALG_ANY_HASHをハッシュ部分に入れる:PSA_ALG_ECDSA(PSA_ALG_ANY_HASH)は「ECDSA なら、ハッシュは何でも」。TLS のように相手とハッシュを交渉する場合に使うPSA_ALG_AEAD_WITH_AT_LEAST_THIS_LENGTH_TAG(alg, n): タグ長 n 以上の AEAD を許すpsa_set_key_enrollment_algorithm(): 2 つ目のアルゴリズムを許可する。仕様の拡張であり、実装によって対応が違う。使わずに済ませたい
PSA_ALG_NONE(値 0)のままだと、その鍵はどの暗号操作にも使えない(エクスポートや生データの保持だけになる)。
7. 寿命 psa_key_lifetime_t — 電源を切っても残るか、どこに置くか
寿命は 2 つの情報を 1 つの 32 ビット値に詰めたものである。
- 持続性(persistence、下位 8 ビット):
PSA_KEY_PERSISTENCE_VOLATILE(0、電源断で消える)、PSA_KEY_PERSISTENCE_DEFAULT(1、不揮発ストレージに保存)、PSA_KEY_PERSISTENCE_READ_ONLY(0xff、工場で焼かれた鍵など。破棄できない) - 場所(location、上位 24 ビット):
PSA_KEY_LOCATION_LOCAL_STORAGE(0、実装の内部)、PSA_KEY_LOCATION_PRIMARY_SECURE_ELEMENT(1、セキュアエレメント)、ベンダ固有の値(0x800000 以上)
最もよく使う 2 つは定数になっている。
| 定数 | 値 | 意味 |
|---|---|---|
PSA_KEY_LIFETIME_VOLATILE | 0 | 揮発。プログラム終了や psa_destroy_key() で消える。省略時の値 |
PSA_KEY_LIFETIME_PERSISTENT | 1 | 永続。不揮発ストレージ(ITS。13 章)に暗号化して保存され、次回起動でも同じ ID で使える |
永続鍵は 12 章で詳しく扱う。 この章では「揮発鍵は ID が自動で振られ、永続鍵は自分で ID を決める」という違いだけ覚えておけばよい。
8. ID psa_key_id_t — 永続鍵の名前
永続鍵を作るときは、psa_set_key_id() で 1 〜 0x3FFFFFFF の範囲の整数(PSA_KEY_ID_USER_MIN 〜 PSA_KEY_ID_USER_MAX)を自分で決めて設定する。 0x40000000 以上はベンダ・実装が予約している(TF-M の組み込み鍵、Mbed TLS が揮発鍵に割り当てる範囲など)。
#define KEY_ID_DEVICE_SIGN ((psa_key_id_t)0x00000101) /* プロジェクトで一元管理する */
psa_set_key_id(&attr, KEY_ID_DEVICE_SIGN);
psa_set_key_lifetime(&attr, PSA_KEY_LIFETIME_PERSISTENT);揮発鍵に ID を設定すると PSA_ERROR_INVALID_ARGUMENT になる(揮発鍵の ID は実装が振る)。 逆に永続鍵で ID を設定し忘れると、同じく INVALID_ARGUMENT である。
9. 属性の組み合わせ早見表
よく使う目的ごとに、属性の「正解セット」を並べる。 これをコピーして使えば、NOT_PERMITTED と INVALID_ARGUMENT の大半は避けられる。
| 目的 | type | bits | usage | algorithm |
|---|---|---|---|---|
| AES-GCM でデータを暗号化・復号 | AES | 128/256 | ENCRYPT ∣ DECRYPT | PSA_ALG_GCM |
| HMAC-SHA256 で認証タグ | HMAC | 256 | SIGN_MESSAGE ∣ VERIFY_MESSAGE | PSA_ALG_HMAC(PSA_ALG_SHA_256) |
| デバイス署名鍵(ECDSA P-256) | ECC_KEY_PAIR(SECP_R1) | 256 | SIGN_HASH | PSA_ALG_ECDSA(PSA_ALG_SHA_256) |
| サーバの公開鍵で署名検証 | ECC_PUBLIC_KEY(SECP_R1) | 256 | VERIFY_HASH | PSA_ALG_ECDSA(PSA_ALG_SHA_256) |
| ECDH で共有秘密を作る | ECC_KEY_PAIR(SECP_R1) | 256 | DERIVE | PSA_ALG_ECDH または PSA_ALG_KEY_AGREEMENT(ECDH, HKDF(SHA_256)) |
| X25519 鍵合意 | ECC_KEY_PAIR(MONTGOMERY) | 255 | DERIVE | PSA_ALG_ECDH |
| HKDF のマスター秘密 | DERIVE | 任意 | DERIVE | PSA_ALG_HKDF(PSA_ALG_SHA_256) |
| Ed25519 署名 | ECC_KEY_PAIR(TWISTED_EDWARDS) | 255 | SIGN_MESSAGE | PSA_ALG_PURE_EDDSA |
| RSA-OAEP で鍵を包んで送る | RSA_PUBLIC_KEY | 2048 | ENCRYPT | PSA_ALG_RSA_OAEP(PSA_ALG_SHA_256) |
| 相手に渡すための一時鍵(値を取り出す) | AES | 128 | EXPORT (+ 必要な用途) | 任意 |
属性を組み立てる
10. 属性を読み出す
既存の鍵の属性は psa_get_key_attributes() で取り出せる。 ライブラリ関数が「渡された鍵が何者か」を確かめるときに使う。
psa_key_attributes_t a = PSA_KEY_ATTRIBUTES_INIT;
PSA_CHECK(psa_get_key_attributes(key, &a));
psa_key_type_t t = psa_get_key_type(&a);
size_t b = psa_get_key_bits(&a);
psa_key_usage_t u = psa_get_key_usage_flags(&a);
psa_algorithm_t al = psa_get_key_algorithm(&a);
psa_reset_key_attributes(&a); /* 必ず片付ける */
if (!PSA_KEY_TYPE_IS_ECC_KEY_PAIR(t) || !(u & PSA_KEY_USAGE_SIGN_HASH))
return PSA_ERROR_INVALID_ARGUMENT;この章のポイント
- 鍵は値+方針。方針は
psa_key_attributes_tの 6 項目で宣言し、ライブラリが強制する - type(何の鍵か)と algorithm(どの計算に使うか)と usage(何をしてよいか)が三本柱。3 つが揃わないと
NOT_PERMITTEDかINVALID_ARGUMENT - 鍵ペアと公開鍵は別の type。公開鍵はいつでもエクスポートできる
EXPORTフラグを付けない限り、鍵の値は取り出せない。これが PSA の保証の実体- 揮発鍵は ID 自動、永続鍵は ID を自分で決める(1〜0x3FFFFFFF)