KeyStore構造:複数の公開鍵のサポート
wolfBoot KeyStoreとは
KeyStoreは、現在のファームウェアと更新の署名を認証するためにwolfBootが使用する、すべての公開鍵を格納するメカニズムです。
wolfBootの鍵生成ツールは1つまたは複数の鍵を生成するために使用できます。
デフォルトでは、初めてmake
を実行すると、単一の鍵wolfboot_signing_private_key.der
が作成され、鍵ストアモジュールに追加されます。
この鍵は、ターゲット上で実行されるファームウェアだけでなく、ファームウェア更新バイナリにも署名するために使用する必要があります。
さらに、keygen
ツールは鍵ストアの異なる表現を含む追加ファイルを作成します。
- .cファイル(
src/keystore.c
):鍵ストアをwolfboot.elf
にリンクすることで、ブートローダー自体の一部として公開鍵をデプロイするために使用できます。 - .binファイル(
keystore.bin
):カスタムメモリサポートでホストできる鍵ストアを含みます。鍵ストアにアクセスするには、小さなドライバーが必要です(以下の「インターフェースAPI」セクションを参照)。
デフォルトの使用法(組み込み鍵ストア)
デフォルトでは、src/keystore.c
の鍵ストアオブジェクトは、そのシンボルをビルドに含めることでwolfBootによってアクセスされます。
生成されると、このファイルには、ターゲットシステム上のwolfBootで利用できる各公開鍵を記述する構造体の配列が含まれます。
さらに、公開鍵スロットの詳細と内容にアクセスするためのwolfBoot鍵ストアAPIに接続するいくつかの関数があります。
公開鍵は次の構造体によって記述されます。
struct keystore_slot {
uint32_t slot_id;
uint32_t key_type;
uint32_t part_id_mask;
uint32_t pubkey_size;
uint8_t pubkey[KEYSTORE_PUBKEY_SIZE];
};
slot_id
は、順番につけられたIDで0から始まります。key_type
は、鍵のアルゴリズム(例:AUTH_KEY_ECC256
またはAUTH_KEY_RSA3072
)を記述します。mask
は、鍵の権限を記述します。これは、この鍵を検証に使用できるパーティションIDのビットマップです。pubkey_size
は公開鍵バッファのサイズです。pubkey
は、生の形式で公開鍵を含む実際のバッファです。
起動時、wolfBootは署名されたファームウェアイメージに関連付けられた公開鍵を自動的に選択し、検証が実行されているパーティションIDのアクセス許可マスクと一致することを確認してから、選択された公開鍵スロットを使用してイメージの署名を認証しようとします。
複数の鍵の作成
keygen
は秘密鍵の複数のファイル名を受け入れます。
2種類の引数が用意されています。
-g priv.der
新しい鍵ペアを生成し、秘密鍵をpriv.derに保存し、公開鍵を鍵ストアに追加します-i pub.der
既存の公開鍵をインポートして鍵ストアに追加します
2つのED25519鍵を持つ鍵ストアを作成する場合、次のように実行します。
./tools/keytools/keygen --ed25519 -g first.der -g second.der
これにより、以下のファイルが作成されます。
first.der
最初の秘密鍵second.der
2番目の秘密鍵src/keystore.c
first.der
とsecond.der
に関連付けられた両方の公開鍵を含むC鍵ストア
生成されたkeystore.c
は次のようになります。
#define NUM_PUBKEYS 2
const struct keystore_slot PubKeys[NUM_PUBKEYS] = {
/* Key associated to private key 'first.der' */
{
.slot_id = 0,
.key_type = AUTH_KEY_ED25519,
.part_id_mask = KEY_VERIFY_ALL,
.pubkey_size = KEYSTORE_PUBKEY_SIZE_ED25519,
.pubkey = {
0x21, 0x7B, 0x8E, 0x64, 0x4A, 0xB7, 0xF2, 0x2F,
0x22, 0x5E, 0x9A, 0xC9, 0x86, 0xDF, 0x42, 0x14,
0xA0, 0x40, 0x2C, 0x52, 0x32, 0x2C, 0xF8, 0x9C,
0x6E, 0xB8, 0xC8, 0x74, 0xFA, 0xA5, 0x24, 0x84
},
},
/* Key associated to private key 'second.der' */
{
.slot_id = 1,
.key_type = AUTH_KEY_ED25519,
.part_id_mask = KEY_VERIFY_ALL,
.pubkey_size = KEYSTORE_PUBKEY_SIZE_ED25519,
.pubkey = {
0x41, 0xC8, 0xB6, 0x6C, 0xB5, 0x4C, 0x8E, 0xA4,
0xA7, 0x15, 0x40, 0x99, 0x8E, 0x6F, 0xD9, 0xCF,
0x00, 0xD0, 0x86, 0xB0, 0x0F, 0xF4, 0xA8, 0xAB,
0xA3, 0x35, 0x40, 0x26, 0xAB, 0xA0, 0x2A, 0xD5
},
},
};
権限
デフォルトでは、新しい鍵ストアが作成されると、アクセス許可マスクはKEY_VERIFY_ALL
に設定されます。
これは、任意のパーティションIDをターゲットとするファームウェアを検証するために鍵を使用できることを意味します。
part_id_mask
値はビットマスクであり、各ビットは異なるパーティションを表します。
ビット「0」はwolfBootの自己更新用に予約されている一方、通常、メインファームウェアパーティションはID 1に関連付けられているため、ビット「1」が設定された鍵が必要です。
つまり、--id 3
でパーティションに署名するには、マスクでビット「3」をオンにする、つまり(1U << 3)を追加する必要があります。
単一の鍵のアクセス許可を制限するには、各鍵のpart_id_mask
の値を変更するだけで十分です。
これは、keygen用の--id
コマンドラインオプションを通じて行われます。
生成またはインポートされた各鍵は、パーティションIDをカンマ区切りのリストで渡すことにより、複数のパーティションと関連付けることができます。
使用例
keygen --ecc256 -g generic.key --id 1,2,3 -g restricted.key
2つの鍵ペア、generic.key
とrestricted.key
を生成します。
前者はデフォルトのマスクKEY_VERIFY_ALL
を想定しており、システムコンポーネントのいずれかを認証するために使用することが可能です。
後者は代わりに、ビット「1」、「2」、および「3」だけがセットされたマスク(mask = b00001110 =0x000e)を持ち、割り当てられたパーティションIDでのみ使用を許可します。
公開鍵のインポート
-i
オプションは、既存の公開鍵を鍵Vaultにインポートするために使用されます。
使用法は -g
オプションと同じですが、提供されるファイルが存在し、指定されたアルゴリズムと鍵サイズの有効な公開鍵を含んでいる必要があります。
異なるタイプの鍵の生成とインポート
デフォルトでは、wolfBootはすべての署名検証操作に使用される鍵のタイプを鍵ストア形式にハードコードします。
あるいは、wolfBootはWOLFBOOT_UNIVERSAL_KEYSTORE=1
オプションでコンパイルすることもできます。
これはコンパイル時のチェックを無効にし、異なるタイプの鍵を鍵ストアに追加することを可能にします。
例えば、異なるECC曲線を持つ2つの鍵ペアを作成し、さらに既存のRSA2048公開鍵ファイルrsa-pub.der
を保存したい場合、次のように実行します。
keygen --ecc256 -g a.key --ecc384 -g b.key --rsa2048 -i rsa-pub.der
上記のコマンドは、実行時にブートローダーがアクセスできる3つの公開鍵を持つ鍵ストアを生成します。
デフォルトでは、wolfBootはSIGN=
オプションで選択されたもの以外の公開鍵アルゴリズム実装を含まないことに注意してください。
そのため、通常この機能は、Root of Trust内の他のポリシーやコンポーネントが、異なる目的のために異なる鍵タイプを保存する必要がある特定のユースケースに限定されます。
外部鍵VaultでのKeyStoreの使用
外部NVM、鍵Vault、または任意の一般的なサポートを使用してKeyStoreにアクセスすることが可能です。
この場合、wolfBootは生成されたkeystore.c
を直接リンクするのではなく、keystore.c
によって実装されるのと同じAPIを、エクスポートする外部インターフェイスに依存することになります。
APIは、以下に説明するいくつかの関数で構成しています。
インターフェースAPI
鍵ストア内の鍵の数
int keystore_num_pubkeys(void)
鍵ストア内のスロットの数を返します。
現在のファームウェアを認証したい場合は、少なくとも1つのスロットが配置されているはずです。
インターフェイスは、スロットが0からkeystore_num_pubkeys() - 1
まで順番に番号付けされていると想定しています。
このAPIを通じてこれらのスロットにアクセスすると、常に有効な公開鍵が返されるはずです。
スロット内の公開鍵のサイズ
int keystore_get_size(int id)
スロットid
に保存されている公開鍵のサイズを返します。
エラーの場合、負の値を返します。
実際の公開鍵バッファ(メモリにマップ/コピーされる)
uint8_t *keystore_get_buffer(int id)
スロットid
に関連付けられた公開鍵を含むバッファを含む、メモリ内のアクセス可能な領域へのポインタを返します。
権限マスク
uint32_t keystore_get_mask(int id)
スロットid
に保存されている公開鍵の権限マスクを32ビットwordとして返します。