iot-reference-rx (Renesas RX用のFreeRTOS IoTリファレンス実装)でmbedtlsのPSA化

## はじめに
[iot-reference-rx](https://github.com/renesas/iot-reference-rx) は，ルネサスRXマイコン用にルネサス社が提供している，FreeRTOSを用いたIoTリファレンス実装です。iot-reference-rxではFreeRTOSに加え，TCP/IPスタックやMQTTライブラリ，ファイルシステムなどが統合されています。

iot-reference-rxがTLS実装として利用している [mbedtls](https://github.com/Mbed-TLS/mbedtls) は，暗号プリミティブとして[PSA](https://arm-software.github.io/psa-api/)を利用することができます。PSAは現在オプションですが，セキュアエレメントや暗号アクセラレータの[ドライバを追加](https://qiita.com/liveinvalley/items/c8060dda447447a01bbc)することができ，将来的には唯一の暗号プリミティブになることが期待されています（mbedtlsはPSAのリファレンス実装でもあります）。

mbedtlsでクライアント証明書を利用する場合，通常は秘密鍵をmbedtls_pk_context へ読み込むために，秘密鍵のデータをmbedtls_pk_parse_key()関数に与える必要があります。
一方，アプリケーションコードで秘密鍵のデータ読み込みを行うことを避けるため，iot-reference-rx はFreeRTOSの[リポジトリ](https://github.com/FreeRTOS/FreeRTOS/blob/main/FreeRTOS-Plus/Source/Application-Protocols/network_transport)から[pkcs#11ベースのmbedtls_pk_contextカスタム実装](https://github.com/renesas/iot-reference-rx/tree/main/Middleware/network_transport/using_mbedtls_pkcs11)を利用しています。このカスタム実装は，pkcs#11インターフェース経由でmbedtlsを呼び出し暗号鍵を注入します。ただ，このコードはPSAを暗号プリミティブとして使うとECCのみで動作し，RSAでは動作しなくなってしまいます。

これを何とかするために，どのようにPSAに移行するか試行錯誤した... というのがこの記事の内容です。分かりにくくってすみません。

## 前提知識
iot-reference-rx が利用する秘密鍵には2つあります：
* デバイス秘密鍵：IoT Coreに接続するときに利用する秘密鍵です
* クレーム秘密鍵：Fleet provisioning でデバイス証明書をセットアップするときに使う，秘密鍵です

以後の説明は，以下のgithubリポジトリ（forkしたもの）を使います。
（いろいろなユースケースをPSAに移行することができるかファームウェア一式をビルドするためにforkしています）
* [iot-reference-rx](https://github.com/liveinvalley/iot-reference-rx)
* [mbedtls](https://github.com/liveinvalley/mbedtls)

## 当初案：PSAドライバを作る
builtin keyをもつPSAドライバを書くことを考えました。このドライバは，key buffer にキーのインデックスのみを保存し，そのインデックスに対応するデータをPSAの内部関数に渡すだけのシンプルなものです。インデックスは今のところ0だけでデータはそのまま保存していますが，ドライバ内部で複数キーを管理し暗号化するよう拡張できます。
* [psa_builtin_keys.h](https://github.com/liveinvalley/iot-reference-rx/blob/network_transport_psa/Projects/aws_da16600_ck_rx65n/e2studio_gcc/src/application_code/psa_builtin_keys.h)
* [psa_builtin_keys.c](https://github.com/liveinvalley/iot-reference-rx/blob/network_transport_psa/Projects/aws_da16600_ck_rx65n/e2studio_gcc/src/application_code/psa_builtin_keys.c)

mbedtlsでは，このドライバを利用するようにdriver wrapper を修正します。本当はdriver description file を書くようですが，mbedtls 3.2.1では不完全なので手動です。また，PSAを有効にするようにconfigを修正します。
* [psa_driver_wrappers.c](https://github.com/liveinvalley/mbedtls/blob/add-driver-wrapper/library/psa_crypto_driver_wrappers.c)
* [aws_mbedtls_config.h](https://github.com/liveinvalley/iot-reference-rx/blob/network_transport_psa/Middleware/3rdparty/mbedtls_config/aws_mbedtls_config.h)
 MBEDTLS_PSA_CRYPTO_DRIVERS定義
 MBEDTLS_USE_PSA_CRYPTO定義
 MBEDTLS_PSA_CRYPTO_C定義
 MBEDTLS_PSA_CRYPTO_BUILTIN_KEYS定義

これでデバイス秘密鍵をアプリケーションコードで安全に渡す（インデックスだけなので安全）ことができるようになります。一方，Fleet Provisioning 有効時は以下の操作が必要です：
* デバイス秘密鍵の作り直し

デバイス秘密鍵を作りなおすためには，以下の変更が必要です：
* key persistence を変更
現在は PSA_KEY_PERSISTENCE_READ_ONLY としているが，128-254のいずれかで[定義する必要がある](https://arm-software.github.io/psa-api/crypto/1.1/api/keys/lifetimes.html#c.psa_key_persistence_t) 
* キーの[破棄](https://arm-software.github.io/psa-api/crypto/1.1/api/keys/management.html#c.psa_destroy_key)
read onlyなキーでは破棄できないため
* キーの[生成](https://arm-software.github.io/psa-api/crypto/1.1/api/keys/management.html#c.psa_generate_key)
key idを指定すればそのIDでキーを作れる
* PSAドライバでのキーの保存
まだstableなAPIがないみたい

... なかなか難しそうです。
よく調べてみると，key lifetime には PSA_KEY_LIFETIME_PERSISTENT があり，このlifetime で作ったキーはPSAが自動的にストレージに保存してくれるようです。
それで，PSAドライバを書くのをやめて，このlifetimeでキーを作ってみることにしました。

## 良さそうな案：PSA_KEY_LIFETIME_PERSISTENT活用
iot-reference-rx では，PSA key の上記lifetime をサポートしていないようです。
有効化するために，PSAのストレージ利用を有効化し，littlefsでのITS実装を追加します。
* [aws_mbedtls_config.h](https://github.com/liveinvalley/iot-reference-rx/blob/network_transport_psa/Middleware/3rdparty/mbedtls_config/aws_mbedtls_config.h)
MBEDTLS_PSA_CRYPTO_STORAGE_C定義
* [internal_trusted_storage.h](https://github.com/liveinvalley/iot-reference-rx/blob/network_transport_psa/Projects/aws_da16600_ck_rx65n/e2studio_gcc/src/application_code/internal_trusted_storage.h)
* [internal_trusted_storage.c](https://github.com/liveinvalley/iot-reference-rx/blob/network_transport_psa/Projects/aws_da16600_ck_rx65n/e2studio_gcc/src/application_code/internal_trusted_storage.c)

なお，MBEDTLS_PSA_ITS_FILE_CでmbedtlsのITSを有効化できますが，これはstdioベースのためiot-reference-rxでは使えません。

これで，以下のようにして秘密鍵をimportできます。PSA_KEY_ID_USER_MINはユーザ定義キーの最小値で，実際にはアプリケーションコードで意味のある名前をdefineすることが良いです。
```
uint8_t key_buffer[32] = {...}; // secp256r1 private key (d) is 32 byte
int length = 32;

psa_key_attributes_t attributes = psa_key_attributes_init();
psa_set_key_type(&attributes, PSA_KEY_TYPE_ECC_KEY_PAIR(PSA_ECC_FAMILY_SECP_R1));
psa_set_key_bits(&attributes, 256);
psa_set_key_usage_flags(&attributes,PSA_KEY_USAGE_SIGN_HASH);
psa_set_key_algorithm(&attributes, PSA_ALG_ECDSA(PSA_ALG_SHA_256));
psa_set_key_id(&attributes, PSA_KEY_ID_USER_MIN);
psa_set_key_lifetime(&attributes, PSA_KEY_LIFETIME_PERSISTENT);

psa_key_id_t keypair;
psa_import_key(&attributes, key_buffer, length, &keypair)
```

importした秘密鍵は，以下のようにして使えます。
```
mbedtls_pk_setup_opaque(&pxCtx->privKey, PSA_KEY_ID_USER_MIN);
```

秘密鍵を再生成する（破棄して生成する）ためには以下のようにします。
```
psa_destroy_key(PSA_KEY_ID_USER_MIN);

psa_key_attributes_t attributes = psa_key_attributes_init();
psa_set_key_type(&attributes, PSA_KEY_TYPE_ECC_KEY_PAIR(PSA_ECC_FAMILY_SECP_R1));
psa_set_key_bits(&attributes, 256);
psa_set_key_usage_flags(&attributes,PSA_KEY_USAGE_SIGN_HASH);
psa_set_key_algorithm(&attributes, PSA_ALG_ECDSA(PSA_ALG_SHA_256));
psa_set_key_id(&attributes, PSA_KEY_ID_USER_MIN);
psa_set_key_lifetime(&attributes, PSA_KEY_LIFETIME_PERSISTENT);

psa_key_id_t keypair;
psa_generate_key(&attributes, &keypair);
 ```

もしキーの暗号化が必要であれば，PSAのストレージ実装で保存内容を暗号化することもできます。
PSAドライバはいらなかった...