wolfBootの機能
署名
wolfBoot鍵ツールのインストール
Python、wolfCrypt-Pyモジュール、ファームウェアの署名と鍵生成のためのwolfBootをセットアップするための手順を説明します。
注:利用可能な鍵ツールの純粋なCバージョンもあります。以下のC言語鍵ツールを参照してください。
Python3のインストール
-
最新のPython 3.xをダウンロードして、インストーラーを実行します。
-
Python 3.xをパスに追加するというボックスにチェックをいれてください。
wolfcryptのインストール
git clone https://github.com/wolfSSL/wolfssl.git
cd wolfssl
./configure --enable-keygen --enable-rsa --enable-ecc --enable-ed25519 --enable-ed448 --enable-des3 CFLAGS="-DWOLFSSL_PUBLIC_MP"
make
sudo make install
wolfCrypt-pyのインストール
git clone https://github.com/wolfSSL/wolfcrypt-py.git
cd wolfcrypt-py
sudo USE_LOCAL_WOLFSSL=/usr/local pip3 install .
wolfBootのインストール
git clone https://github.com/wolfSSL/wolfBoot.git
cd wolfBoot
git submodule update --init
## Setup configuration (or copy template from ./config/examples)
make config
## Build the wolfBoot binary and sign an example test application
make
C言語-鍵ツール
KeygenツールのスタンドアロンCバージョンは、./tools/keytools
に格納されています。
これらは、make
を使用してtools/keytools
に生成されます。またはwolfBootのルートからmake keytools
を使用してビルドすることもできます。
鍵ツールのCバージョンが存在する場合、wolfBootで使用されます(デフォルトはPythonスクリプトです)。
Windows Visual Studio
wolfBootSignTool.vcxproj
Visual Studio Projectを使用して、Windowsで使用するsign.exe
およびkeygen.exe
ツールをビルドできます。
コマンドラインの使用方法
Keygen tool
./tools/keytools/keygen [--ed25519 | --ed448 | --ecc256 | --rsa2048 | --rsa4096 ] pub_key_file.c
keygenは鍵ストアを有効にし、既存あるいは新規に作成した公開鍵を管理するために使われます。2つのオプションがサポートされています: - [-g privkey.der]は新規鍵ペアを生成します。生成した公開鍵は鍵ストアに追加し、秘密鍵はprickey.derファイルとして出力します。 - [-i existing.der]は既存の公開鍵をファイルexisting.derファイルからインポートします。
引数は排他的ではありませんし、複数の鍵を鍵ストアに格納するために一度以上繰り返して指定できます。 鍵ストアで使用するアルゴリズムを一つは指定する必要があります(即ち、--ed25519か--rsa3072)。利用可能なオプションは”公開鍵署名オプション”を参照してください。
keygenツールで生成されるファイルは以下のものがあります: - Cファイル src/keystore.cは生成されたCコードでプロビジョニングされる場合はwolfBootイメージとリンクされます - バイナリーファイル keystore.imgは代替ストレージを通じてプロビジョニングされた公開鍵が使われる場合には利用可能です - コマンドラインから"-g"オプションとともに指定された秘密鍵
署名ツール
sign と sign.pyはwolfBootがサポートする形式のマニフェストヘッダーを生成することで単一のファームウェアイメージを生成します。
sign[.py] [OPTIONS] IMAGE.BIN KEY.DER VERSION
- IMAGE.BIN: 署名対象のバイナリーファームウェア
- KEY.DER:バイナリーファームウェアに署名を行う秘密鍵で、DER形式
- VERSION: 署名されたイメージに関連付けられたバージョン
- OPTIONS: なしあるいは以下に示すオプション:
- --ed25519 ED25519アルゴリズムを署名に使用する。KEY.DERファイルはこの鍵フォーマットであることを期待している
- --ed448 ED448アルゴリズムを署名に使用する。KEY.DERファイルはこの鍵フォーマットであることを期待している
- --ecc256 ecc256アルゴリズムを署名に使用する。KEY.DERファイルはこの鍵フォーマットであることを期待している
- --ecc384 ecc448アルゴリズムを署名に使用する。KEY.DERファイルはこの鍵フォーマットであることを期待している
- --rsa2048 rsa2048アルゴリズムを署名に使用する。KEY.DERファイルはこの鍵フォーマットであることを期待している
- --rsa3072 rsa3072アルゴリズムを署名に使用する。KEY.DERファイルはこの鍵フォーマットであることを期待している
- --rsa4096 rsa4096アルゴリズムを署名に使用する。KEY.DERファイルはこの鍵フォーマットであることを期待している
- --no-sign セキュアブートで署名検証を使用しない。KEY.DER引数は無視される
鍵生成と管理
KeyStoreはwolfBootによって使用されるメカニズムの呼び名です。ここでは、ファームウエアの更新の署名検証に使われるすべての公開鍵の保管を行っています。
wolfBootの鍵生成ツールは一つ以上の鍵を生成することができます。makeコマンドを最初に使用する際に単一の秘密鍵wolfboot_signing_private_key.derを生成しkeystoreモジュールに追加します。この鍵はどのファームウエアの実行あるいは更新に於いて署名するのに使用されるべきです。
加えて、keygenツールは、KeyStoreの異なる表現を持つ追加ファイルを作成します - .c ファイル (src/keystore.c) wolfboot.elf でキーストアをリンクすることにより、ブートローダー自体の一部として公開鍵を展開するために使用できる - .bin ファイル (keystore.bin) カスタム メモリ サポートでホストできるキーストアを含む。
キーストアにアクセスするには、小さなドライバーが必要です (以下のセクション「インターフェース API」を参照)。
デフォルトでは、src/keystore.c のKeyStoreオブジェクトは、ビルドにそのシンボルを含めることにより、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 は、鍵スロット識別子で、0 から始まります。
- key_type は鍵のアルゴリズムを記述します。 AUTH_KEY_ECC256 または AUTH_KEY_RSA3072
- mask は、鍵のアクセス許可を記述します。 これは、この鍵を検証に使用できるパーティション ID のビットマップです
- pubkey_size 公開鍵バッファーのサイズ
- pubkey 公開鍵を生の形式で保持する実際のバッファ
起動時に、wolfBoot は署名付きファームウェアイメージに関連付けられた公開鍵を自動的に選択し、検証が実行されているパーティション ID の許可マスクと一致することを確認してから、選択した公開鍵スロットを使用してイメージの署名を認証します。
複数鍵の生成
KeyGenは複数の秘密鍵生成のサポートのために複数のファイル名を受け付けます。
- "-g priv.der" は新たに鍵ペアを生成します。秘密鍵はpriv.derファイルに、公開鍵はKeyStoreに格納します
- "-i pub.der" は既存の公開鍵をpub.derファイルからインポートしKeyStoreaに格納します
ED25519鍵を使ってKeyStoreを作成する例を示します:
./tools/keytools/keygen.py --ed25519 -g first.der -g second.der
この例は次のファイルを生成します:
- first.der 第1の秘密鍵
- second.der 第2の秘密鍵
- src/keystore.c 第1,第2の秘密鍵に対応した2つの公開鍵を含んだC KeyStore
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
},
},
};
公開鍵とパーミッション
デフォルトでは、新しいKeyStoreが作成されると、パーミッションマスクが KEY_VERIFY_ALL に設定されます。これは、キーを使用して、任意のパーティションID を対象とするファームウェアを検証できることを意味します。
単一のキーのアクセス許可を制限するには、part_id_mask 属性の値を変更するだけで十分です。
part_id_mask 値はビットマスクで、各ビットは異なるパーティションを表します。 ビット「0」は wolfBoot の自己更新用に予約されていますが、通常、メイン ファームウェア パーティションは ID 1 に関連付けられているため、ビット「1」が設定された鍵が必要です。 つまり、 --id 3 でパーティションに署名するには、マスクのビット '3' をオンにする必要があります。つまり、(1U << 3) を追加する必要があります。
KEY_VERIFY_ALL のほかに、定義済みのマスク値もここで使用できます。
- KEY_VERIFY_APP_ONLY は、パーティション ID が 1 のメイン アプリケーションのみを検証します
- KEY_VERIFY_SELF_ONLY は、wolfBoot 自己更新の認証にのみ使用できます (id = 0)
- キーの使用を特定のパーティション ID N に制限するために使用できる KEY_VERIFY_ONLY_ID(N) マクロ
ファームウェアへの署名
-
./rsa2048.der
、./rsa4096.der
、./ed25519.der
、ecc256.der
、または./ed448.der
にサインするために使用する秘密鍵をロードする -
非対称アルゴリズム、ハッシュアルゴリズム、ファイルへのファイル、鍵、バージョンを使用して、署名ツールを実行します。
./tools/keytools/sign --rsa2048 --sha256 test-app/image.bin rsa2048.der 1
## OR
python3 ./tools/keytools/sign.py --rsa2048 --sha256 test-app/image.bin rsa2048.der 1
注:最後の引数は「バージョン」番号です。
外部秘密鍵(HSM)でファームウェアに署名する
外部鍵ソースを使用してファームウェアに手動で署名するための手順。
## 公開鍵ファイルを生成
openssl rsa -inform DER -outform DER -in rsa2048.der -out rsa2048_pub.der -pubout
## 署名のためのハッシュを生成
./tools/keytools/sign --rsa2048 --sha-only --sha256 test-app/image.bin rsa2048_pub.der 1
## または
python3 ./tools/keytools/sign.py --rsa2048 --sha-only --sha256 test-app/image.bin rsa4096_pub.der 1
## ハッシュで署名 (HSMを使用する場合)
openssl rsautl -sign -keyform der -inkey rsa2048.der -in test-app/image_v1_digest.bin > test-app/image_v1.sig
## 最終バイナリを生成
./tools/keytools/sign --rsa2048 --sha256 --manual-sign test-app/image.bin rsa2048_pub.der 1 test-app/image_v1.sig
## または
python3 ./tools/keytools/sign.py --rsa2048 --sha256 --manual-sign test-app/image.bin rsa4096_pub.der 1 test-app/image_v1.sig
## ファクトリーイメージに組み込み
cat wolfboot-align.bin test-app/image_v1_signed.bin > factory.bin
wolfBootを使用した管理ブート
wolfBootは、信頼できるプラットフォームモジュール(TPM)を使用してシステムブートプロセスの状態を記録および追跡する方法である、簡略化された管理されたブート実装を提供します。
レコードは、Platform Configuration Registerと呼ばれるTPMの特別なレジスタによって改ざん防止されています。次に、ファームウェアアプリケーションであるRTOSまたはRICH OS(Linux)は、TPMのPCRを読み取ることにより、情報のログにアクセスできます。
wolfTPMとの統合により、wolfBootはTPM2.0チップと対話できます。wolfTPMは、Microsoft WindowsとLinuxのネイティブサポートを備えており、StandaloneまたはwolfBootと一緒に使用できます。wolfBootとwolfTPMの組み合わせにより、開発者は、ブート中および起動後にシステムを保護するための改ざん防止セキュアなストレージを提供します。
コンセプト
通常、システムは安全なブートを使用して、正しいファームウェアとその署名を確認することで起動されることを保証します。その後、この知識はシステムに知られていません。アプリケーションは、システムが良好な既知の状態で始まったかどうかを知りません。時には、この保証がファームウェア自体によって必要です。そのようなメカニズムを提供するために、測定されたブーツの概念が存在します。
管理ブートを使用して、設定やユーザー情報(ユーザーパーティション)など、すべての起動コンポーネントを確認できます。チェックの結果は、PCRと呼ばれる特別なレジスタに保存されます。このプロセスはPCR拡張と呼ばれ、TPM測定と呼ばれます。PCRレジスタは、TPM Power-Onでのみリセットできます。
TPM測定値を使用すると、WindowsやLinuxなどのファームウェアまたはオペレーティングシステム(OS)が、システムを制御する前にロードされたソフトウェアが信頼でき、変更されていないことを知る方法を提供します。
wolfBootでは、メインファームウェアイメージである単一のコンポーネントを測定するために、コンセプトが簡素化されます。ただし、これは、より多くのPCRレジスタを使用することで簡単に拡張できます。
コンフィグレーション
管理ブートを有効にするには、wolfBoot ConfigにMEASURED_BOOT=1
設定を追加します。
また、管理が保存されるPCR(インデックス)を選択する必要があります。
MEASURED_BOOT_PCR_A=[index]
設定を使用して選択が行われます。この設定をwolfboot configに追加し、[index]
を0〜23の数字に置き換えます。以下に、PCRインデックスを選択するためのガイドラインがあります。
すべてのTPMには、最低24のPCRレジスタがあります。それらの典型的な使用目的は次のとおりです。
インデックス | 典型的な使用目的 | 推奨する環境 |
---|---|---|
0 | 信頼および/またはBIOS測定のコアルート | ベアメタル、RTOS |
1 | プラットフォーム構成データの測定 | ベアメタル、RTOS |
2-3 | オプションROMコード測定 | ベアメタル、RTOS |
4-5 | マスターブートレコード測定 | ベアメタル、RTOS |
6 | 状態移行 | ベアメタル、RTOS |
7 | ベンダー固有の | ベアメタル、RTOS |
8-9 | パーティション測定 | ベアメタル、RTOS |
10 | ブートマネージャーの測定 | ベアメタル、RTOS |
11 | 通常、Microsoft BitLockerで使用されます | ベアメタル、RTOS |
12-15 | あらゆる用途で利用可能 | ベアメタル、RTOS、Linux、Windows |
16 | デバッグ | テスト目的でのみ使用 |
17 | DRTM | 信頼できるブートローダ |
18-22 | 信頼できるOS | 信頼できる実行環境(TEE) |
23 | アプリケーション | 一時的な測定にのみ使用 |
PCRインデックスを選択するための推奨事項:
-
開発中、テストを目的としたPCR16を使用することをお勧めします。
-
生産時には、ベアメタルファームウェアまたはRTOSを実行している場合は、DRTMおよび信頼できるOS(PCR17-23)を除き、ほぼすべてのPCR(PCR0-15)を使用できます。
-
LinuxまたはWindowsを実行している場合、Linux IMAやMicrosoft BitLockerなどのLinux内からPCRを使用している可能性のある他のソフトウェアとの競合を回避するために、PCR12-15を生産対応ファームウェアに選択できます。
開発中のwolfboot .configの一部です。
MEASURED_BOOT?=1
MEASURED_PCR_A?=16
コード
wolfBootは、すぐに使えるソリューションを提供しています。測定されたブートを使用するために、開発者がwolfBootコードにタッチする必要がありません。コードを確認する場合は、src/image.c
、より具体的にはmeasure_boot()
関数を調べます。そこには、wolfTPMへのいくつかのTPM2ネイティブAPI呼び出しがあります。wolfTPMの詳細については、GitHubリポジトリを確認できます。
ファームウェアイメージ
ファームウェアエントリポイント
wolfBootは、メモリ内の特定のエントリポイントからファームウェアイメージをチェーンロードおよび実行できます。これは、埋め込みアプリケーションのリンカースクリプトのフラッシュメモリの原点として指定する必要があります。これは、フラッシュメモリの最初のパーティションに対応します。
複数のファームウェアイメージをこの方法で作成し、2つの異なるパーティションに保存できます。ブートローダーは、選択したファームウェアを最初の(ブート)パーティションに移動する前に、イメージをチェーンする前に処理します。
イメージヘッダーが存在するため、アプリケーションのエントリポイントには、フラッシュパーティションの開始から256Bの固定追加オフセットがあります。
ファームウェアイメージヘッダー
各(署名された)ファームウェアイメージには、ファームウェアに関する有用な情報が含まれている固定サイズimage headerが事前に塗装されています。image headerは、実際のファームウェアのエントリポイントが256バイトのアラインされたアドレスから開始されるフラッシュに保存されることを保証するために、256Bに収まるようにパディングされています。これにより、ブートローダーがベクトルテーブルを再配置することができます。
イメージヘッダーはスロットの先頭に保存され、実際のファームウェアイメージは256バイトから始まります
イメージヘッダー:タグ
image headerには、単一の4バイトのマジック番号が追加され、その後にファームウェアイメージ(ヘッダーを除く)を示す4バイトフィールドが続きます。ヘッダーのすべての数値は、リトルエンディアン形式で保存されます。
2つの固定フィールドの後に1つ以上のタグが続きます。各タグは次のように構成されています。
-typer - タグのsizeを示すtyper -2バイトを示す2バイト、タイプとサイズのバイト - Nタグコンテンツのバイトを除く
次の例外を除きます。-タイプフィールドの「0xff」は、単純なパディングバイトを示します。「パディング」バイトにはsizeフィールドはありません。次のバイトはtyperとして処理する必要があります。各typerには異なる意味があり、ファームウェアに関する情報を統合します。ファームウェアイメージを検証するには、次のタグが必須です。-「バージョン」タグ(タイプ:0x0001、サイズ:4バイト)イメージに保存されているファームウェアのバージョン番号を示す - 「タイムスタンプ」タグ(タイプ:0x0002、サイズ8バイト)ファームウェアの作成のためのUnix秒のタイムスタンプを示す - ファームウェアの整合性チェックに使用される「SHA256ダイジェスト」タグ(タイプ:0x0003、サイズ:32バイト) - 「ファームウェア署名」タグ(タイプ:0x0020:0x0020、サイズ:64バイト)既知の公開鍵に対してファームウェアで保存されている署名を検証するために使用されます - 「ファームウェアタイプ」タグ(タイプ:0x0030、サイズ:2バイト)のファームウェアの種類と認証メカニズム使用する。
オプションで、「公開鍵ヒントダイジェスト」タグをヘッダーに送信できます(タイプ:0x10、サイズ:32バイト)。このタグには、署名ツールで使用される公開鍵のSHA256ダイジェストが含まれています。ブートローダーは、このフィールドを使用して、複数の鍵が利用可能な場合に正しい公開鍵を見つけることができます。
wolfBootは、すべての場合において、組み込みのデジタル署名認証メカニズムを使用して検証および認証できないイメージの起動を拒否します。
イメージ署名ツール
イメージ署名ツールは、コンパイルされたイメージに必要なすべてのタグを使用してヘッダーを生成し、デバイス上のプライマリスロットに保存するか、後でデバイスに送信して安全なチャネルを介してデバイスに送信できる出力ファイルに追加します。アップデート。
ファームウェアイメージの保存
ファームウェアイメージは、システム上のパーティションの先頭にフルヘッダーで保存されます。wolfBootは、更新パーティションに2番目のファームウェアイメージを保持しながら、ブートパーティションからイメージのみを起動できます。
別のイメージを起動するには、wolfBootは2つのイメージのコンテンツを交換する必要があります。
ファームウェアイメージの保存方法の詳細については、2つのパーティション内で、フラッシュパーティションを参照してください。
ファームウェアの更新
このセクションでは、完全なファームウェア更新手順を文書化し、既存の組み込みアプリケーションのセキュアブートを有効にします。
マイクロコントローラーフラッシュの更新
wolfBootでファームウェアアップデートを完了する手順は次のとおりです。-正しいエントリポイントでファームウェアをコンパイルします - ファームウェアに署名します - 安全な接続を使用してイメージを転送し、セカンダリファームウェアスロットに保存 - イメージスワップをトリガーします - 再起動してブートローダーはイメージスワップを開始します
いつでも、wolfBootシステムで実行されているアプリケーションまたはOSは、それ自体の更新されたバージョンを受信し、Flashメモリの2番目のパーティションに更新されたイメージを保存できます。
アプリケーションまたはOSスレッドは、APIをエクスポートして次の再起動時にアップデートをトリガーするlibwolfbootライブラリにリンクし、一部のヘルパー関数はフラッシュパーティションにアクセスして、ターゲット固有のハルを介して消去/書き込みを行うことができます。
更新手順の説明
wolfBootは、アプリケーションに提供されているAPIを使用して、更新を開始、確認、またはロールバックする可能性を提供します。
更新パーティションに新しいファームウェアイメージを保存した後、アプリケーションはwolfBoot_update_trigger()
を呼び出して更新を開始する必要があります。次の再起動時に、wolfBootは次の手順を実行します:
- 更新パーティションに保存されている新しいファームウェアイメージを検証します
- ブートローダーイメージに保存されている既知の公開鍵に対して添付された署名を確認します
- ブートコンテンツと更新パーティションのコンテンツを交換します
- 新しいファームウェアに状態
STATE_TESTING
のマークを付けます - 新しく受信したファームウェアを起動
スワップ操作と再起動中にシステムが中断された場合、wolfBootは中断したところからピックアップし、更新手順を継続します。
ブート成功
ブートが成功すると、システムが再び稼働していることを確認した後、wolfBoot_success()
を呼び出してブートローダーに通知する必要があります。この操作により、新しいファームウェアの更新が確認されます。
次の再起動前にブートパーティションをSTATE_SUCCESS
に設定するのに失敗すると、ロールバック操作がトリガーされます。ロールバックは、新しいアップデートをトリガーすることによりブートローダーによって開始されます。今回は、元の(プレ・アップデート前)ファームウェアのバックアップコピーから始まります。これは、以前に発生したスワップのために更新パーティションに保存されています。
新しいファームウェアイメージのビルド
ファームウェアイメージは位置に依存しており、Flashのブートパーティションの原点からのみ起動できます。この設計上の制約は、選択したファームウェアが常に boot パーティションに保存されていることを意味し、wolfBootは更新イメージを事前に検証し、正しいアドレスにコピーする責任があります。
したがって、すべてのファームウェアイメージには、 boot パーティションの開始に対応するアドレスにエントリポイントを設定する必要があります。さらに、イメージヘッダーを考慮して256バイトのオフセットが必要です。
ファームウェアがコンパイルされてリンクされたら、sign
ツールを使用して署名する必要があります。このツールは、検証に現在使用されている公開鍵に対応する同じ鍵を使用して、安全な接続を使用してターゲットに転送できる署名付きイメージを生成します。
このツールは、ファームウェアの署名とSHA256ハッシュを含む、すべての必要なタグをイメージヘッダーに追加します。
セルフアップデート
RAM_CODE
が設定されている場合、wolfBootは自分自身を更新できます。この手順は、いくつかの重要な違いがありますが、通常のファームウェアアップデートとほぼ同じ動作をします。アップデートのヘッダーは、ブートローダーアップデートとしてマークされています(サインツールに--wolfboot-update
を使用)。
署名されている新しいwolfbootイメージは、更新パーティションにロードされ、ファームウェアの更新と同じようにトリガーされます。スワップを実行する代わりに、イメージが検証され、署名検証された後、ブートローダーが消去され、新しいイメージがFlashに書き込まれます。この操作は、中断されると「安全ではありません」。中断すると、デバイスが再起動できなくなります。
wolfBootは、新しいブートローダーバージョンと更新鍵を展開するために使用できます。
インクリメンタルアップデート(別名:「デルタ」更新)
wolfBootは、特定の古いバージョンに基づいて、インクリメンタル更新をサポートしています。サインツールは、ターゲットで現在実行されているバージョンと更新パッケージのバージョンのバイナリの違いのみを含む小さな「パッチ」を作成できます。これにより、ターゲットに転送されるイメージのサイズが縮小され、公開鍵の検証を通じて同じレベルのセキュリティを維持し、繰り返しチェック(パッチと結果のイメージ)による整合性を維持します。
パッチの形式は、Bentley/Mcllroyによって提案されたメカニズムに基づいています。これは、小さなバイナリパッチを生成するのに特に効果的です。これは、更新を転送、認証、インストールするために必要な時間とリソースを最小限に抑えるのに役立ちます。
どのように動作するのか
ファームウェアイメージ全体を転送する代わりに、鍵ツールは、以前にアップロードされたベースバージョンと新しい更新されたイメージの間にバイナリdiffを作成します。
結果のバンドル(Delta Update)には、基本バージョンから始まるファームウェアのバージョン「2」のコンテンツを導き出すための情報が含まれています。バージョン「2」をバージョンに戻すには、新しいバージョンを実行している場合にバージョンに戻ります。
デバイス側では、wolfBootは、パッチを現在のファームウェアに適用する前に、Deltaアップデートの信頼性を認識して検証します。新しいファームウェアは適切に再ビルドされ、(認証された)「デルタアップデート」バンドルの表示に従ってブートパーティションのコンテンツを置き換えます。
2ステップ検証
バイナリパッチは、署名されたファームウェアイメージを比較することによって作成されます。wolfBootは、パッチ後の結果のイメージの整合性と信頼性をチェックすることにより、パッチが正しく適用されることを確認します。
パッチを含むデルタアップデートバンドル自体には、パッチの詳細を説明するマニフェストヘッダーが付いており、通常のフルアップデートバンドルのように署名されています。
これは、wolfBootが2つのレベルの認証を適用することを意味します。デルタバンドルが処理されたときの最初のレベル(アップデートがトリガーされたとき)、2番目のレベルは、パッチが適用されるか、または逆になって、起動前にファームウェアイメージを検証するために、。
これらの手順は、例で説明されているように、--delta
オプションを使用する場合、鍵ツールによって自動的に実行されます。
更新の確認
アプリケーションの観点から見ると、通常の「完全な」更新ケースから変わるものはありません。アプリケーションは、更新されたバージョンを使用して最初のブーツでwolfBoot_success()
を呼び出して、更新が確認されていることを確認する必要があります。
アップデートの成功を確認できないと、wolfBootが更新中に適用されたパッチを元に戻します。「Delta Update」バンドルには逆パッチも含まれており、更新を戻してファームウェアのベースバージョンを復元できます。
以下の図は、認証手順と両方向のdiff/パッチプロセスを示しています(確認のための更新とロールバック)。
インクリメンタル更新:例
要件:wolfBootはDELTA_UPDATES=1
でコンパイルされています
バージョン「1」は、スタンドアロンのイメージとして、通常どおり署名されています。
tools/keytools/sign.py --ecc256 --sha256 test-app/image.bin ecc256.der 1
バージョン1からバージョン2に更新する場合、サインツールを次のように呼び出すことができます。
tools/keytools/sign.py --delta test-app/image_v1_signed.bin --ecc256 --sha256 test-app/image.bin ecc256.der 2
通常の出力ファイルimage_v2_signed.bin
に加えて、符号ツールは、2つのバイナリファイルに重複領域が含まれている限り、サイズが著しく小さくなる必要がある追加のimage_v2_signed_diff.bin
を作成します。
これは、最初のパッチが適用された後、バージョン1からバージョン2を更新するためのパッチを含む署名付きパッケージ、および必要に応じてバージョン1にロールバックするデルタアップデートバンドルです。
デルタバンドルimage_v2_signed_diff.bin
は、完全な更新イメージのようにターゲットの更新パーティションに転送できるようになりました。
次回の再起動では、wolfBootはインクリメンタルアップデートを認識し、パッチの整合性、信頼性、およびバージョンをチェックします。すべてのチェックが成功した場合、現在のファームウェアイメージにパッチを適用することにより、新しいバージョンがインストールされます。
更新が確認されていない場合、次回の再起動時にwolfBootは、Delta Updateバンドルに含まれる逆パッチを使用して、元のベースimage_v1_signed.bin
を復元します。
UART経由のリモート外部フラッシュメモリサポート
wolfBootは、近隣システムとのUART通信を使用して外部パーティションをエミュレートできます。この機能は、外部処理ユニットの支援を受けて更新を保存できる非同期マルチプロセスアーキテクチャで特に役立ちます。
ブートローダセットアップ
この機能をアクティブにするオプションはUART_FLASH=1
です。この構成オプションは、外部フラッシュAPIに依存します。つまり、オプションEXT_FLASH=1
はブートローダーをコンパイルするためにも必須です。
ターゲットシステムのHALは、搭載されたUARTコントローラーの1つを使用してリモートフラッシュのコンテンツにアクセスするためにブートローダーが使用する単純なUARTドライバーを含むように拡張する必要があります。
サポートされているいくつかのプラットフォームのUARTドライバーの例は、hal/uart
ディレクトリにあります。
サポートされているターゲットのUARTHAR拡張機能によって公開されたAPIは、次の機能によって構成されています。
int uart_init(uint32_t bitrate, uint8_t data, char parity, uint8_t stop);
int uart_tx(const uint8_t c);
int uart_rx(uint8_t *c);
まだ正式にサポートされていない場合、プラットフォームで外部フラッシュメモリサポートを使用する場合は、提供された例に基づいてこれら3つの機能を実装することを検討してください。
ホスト側:UART Flash Server
ターゲットの外部パーティションイメージをホストするリモートシステムでは、Flash-Access固有の呼び出しを提供するために、UARTメッセージの上に簡単なプロトコルを実装できます。
GNU/Linuxホストで実行し、ファイルシステム上のローカルファイルを使用して外部パーティションをエミュレートするように設計されたUART-Flash-Serverデーモンの例は、ツール/uart-flash-serverで入手できます。
外部フラッシュ更新メカニズム
wolfBootは、外部の更新を扱い、パーティションをローカルSPIフラッシュにマッピングしたときと同じ方法でパーティションを交換します。読み取りおよび書き込み操作は、UARTを介してリモートプロシージャコールに翻訳されます。これは、リモートアプリケーションによって解釈され、ホストがのみアクセスできる実際のストレージ要素への読み取りおよび書き込みアクセスを提供できます。
これは、更新が成功した後、以前のファームウェアのコピーがリモートパーティションに保存され、他のすべてのユースケースで利用可能なまったく同じ更新メカニズムを提供することを意味します。唯一の違いは、物理的な保管エリアにアクセスする方法にありますが、より高いレベルのすべてのメカニズムは同じままです。
暗号化された外部パーティション
wolfBootは、更新パーティション全体のコンテンツを暗号化する可能性を提供します。この暗号化には、より安全な非揮発性メモリ領域に一時的に保存できる事前共有対称鍵を使用します。
スワップパーティションは同じ鍵を使用して一時的に暗号化されるため、外部フラッシュのダンプでは、ファームウェアアップデートパッケージのコンテンツが表示されません。
根拠
外部パーティションの暗号化は、外部フラッシュインターフェイスのレベルで機能します。
ブートローダーから外部パーティションへのすべての書き込みコールは、追加の暗号化ステップを実行して、外部の不揮発性メモリの実際のコンテンツを非表示にします。
逆に、すべての読み取り操作は、機能が有効になったときに保存されたデータを復号化します。
署名後にファームウェアアップデートを暗号化するためのsign.py
サインツールに追加のオプションが提供されます。これにより、アプリケーションによって外部メモリに保存され、更新を確認して開始するためにブートローダーによって復号化されます。インストール。
一時的な鍵ストレージ
デフォルトでは、wolfBootは、内部フラッシュ上の一時的な領域に暗号化に使用される事前共有対称鍵を保存します。これにより、一時的な鍵を隠すために読み出しの保護を使用できます。
あるいは、一時的な鍵を別の鍵ストレージに保存するために、より安全なメカニズムを利用できます(たとえば、ハードウェアセキュリティモジュールまたはTPMデバイスを使用)。
一時的な鍵は、アプリケーションによって実行時に設定でき、ブートローダーが次の更新を確認してインストールするために、ブートローダーで1回だけ使用できます。鍵は、たとえば、安全な通信を使用して更新プロセス中にバックエンドから受信し、libwolfboot
APIを使用してアプリケーションによって設定され、次のブート時にwolfBootが使用します。
一時的な鍵を設定することとは別に、更新メカニズムは、wolfBootを介したファームウェアの更新の配布、アップロード、インストールの場合と同じままです。
libwolfboot ライブラリーAPI
アプリケーションからブートローダーと通信するAPIは、この機能が有効になっているときに拡張され、一時的な鍵を設定して次の更新を処理します。
関数
int wolfBoot_set_encrypt_key(const uint8_t *key, const uint8_t *nonce);
int wolfBoot_erase_encrypt_key(void);
外部パーティションの一時的な暗号化鍵を設定するために、またはそれぞれ以前に設定された鍵を消去するために使用できます。
さらに、libwolfboot
を使用して、アプリケーションからwolfBoot HALを使用して外部フラッシュにアクセスしても、暗号化は使用されません。このようにして、既にOriginで暗号化された受信した更新は、変更されていない外部メモリに保存でき、暗号化された形式で取得できます。再起動する前に転送が成功していることを確認します。
対称暗号アルゴリズム
暗号化は、ENCRYPT=1
を使用してwolfBootで有効にできます。
外部パーティションでデータを暗号化および復号化するために使用されるデフォルトのアルゴリズムはChacha20-256です。AES-128、AES-256オプションも利用可能で、ENCRYPT_WITH_AES128=1
またはENCRYPT_WITH_AES256=1
を使用して選択できます。
Chacha20-256
Chacha20が選択されたとき:
-wolfBoot_set_encrypt_key()
に提供されるkey
は、正確に32バイトの長さでなければなりません。-nonce
引数は、暗号化と復号化のためにIVとして使用するには、96ビット(12バイト)ランダムに生成されたバッファーでなければなりません。
Chacha20-256での使用例
sign.py
ツールは、単一のコマンドでイメージに署名して暗号化できます。暗号化のシークレットは、32B Chacha-256鍵と12B NonCEの連結を含むバイナリファイルで提供されます。
提供されている例では、テストアプリケーションは次のパラメーターを使用します。
key="0123456789abcdef0123456789abcdef"
nonce="0123456789ab"
したがって、テストスクリプトまたはコマンドラインから暗号化のシークレットを次のコマンドで簡単に準備できます。
echo -n "0123456789abcdef0123456789abcdef0123456789ab" > enc_key.der
sign.py
スクリプトを呼び出して、追加の引数--encrypt
を使用して署名+暗号化されたイメージを作成するように呼び出すことができます。
./tools/keytools/sign.py --encrypt enc_key.der test-app/image.bin ecc256.der 24
ファイルtest-app/image_v24_signed_and_encrypted.bin
を出力すると生成され、ターゲットの外部デバイスに転送できます。
AES-CTR
AES-CTRモードが使用されます。AESが選択された場合:-wolfBoot_set_encrypt_key()
に提供されるkey
は、16バイト(AES128)または32バイト(AES256)の長さでなければなりません。-nonce
引数は、暗号化と復号化の初期カウンターとして使用される128ビット(16Byte)ランダムに生成されたバッファーです。
AES-256での使用例
AES-256の場合、暗号化のシークレットは、32バイトの鍵と16バイトのIVの連結を含むバイナリファイルで提供されます。
提供されている例では、テストアプリケーションは次のパラメーターを使用します。
key="0123456789abcdef0123456789abcdef"
iv="0123456789abcdef"
したがって、テストスクリプトまたはコマンドラインから暗号化のシークレットを次のコマンドで簡単に準備できます。
echo -n "0123456789abcdef0123456789abcdef0123456789abcdef" > enc_key.der
sign.py
スクリプトを呼び出して、追加の引数--encrypt
に続いてeCeCretファイルを使用して、署名+暗号化されたイメージを作成するように呼び出すことができます。AES-256を選択するには、--aes256
オプションを使用します。
./tools/keytools/sign.py --aes256 --encrypt enc_key.der test-app/image.bin ecc256.der 24
ファイルtest-app/image_v24_signed_and_encrypted.bin
を出力すると生成され、ターゲットの外部デバイスに転送できます。
アプリケーションでのAPIの使用
イメージを転送する場合、アプリケーションは引き続きLibwolfboot API関数を使用して、暗号化されたファームウェアを保存できます。アプリケーションから呼び出されると、関数ext_flash_write
は、誘引payloadが暗号化されていない保存されます。
更新をトリガーするには、wolfBoot_update_trigger
を呼び出す前に、wolfBoot_set_encrypt_key
を呼び出してブートローダーが使用する一時鍵を設定する必要があります。
暗号化された更新トリガーの例は、STM32WBテストアプリケーションソースコード(../test-app/app_stm32wb.c
)に記載されています。
ブートローダーとの対話のためのアプリケーションインターフェイス
wolfBootは、パーティションに保存されているイメージと対話し、更新を明示的に開始し、以前にスケジュールした更新の成功を確認するための小さなインターフェイスを提供します。
libwolfbootとのコンパイルとリンク
wolfBootとの対話を必要とするアプリケーションには、ヘッダーファイルを含める必要があります。
#include <wolfboot/wolfboot.h>
これにより、API関数宣言と、2つのパーティションのファームウェアイメージと一緒に保存されたフラグとタグの事前定義値をエクスポートします。
フラッシュパーティション、フラグ、および状態の詳細については、フラッシュパーティションを参照してください。
API
libwolfbootは、フラッシュパーティション状態に低レベルのアクセスインターフェイスを提供します。各パーティションの状態は、アプリケーションによって取得および変更できます。
アプリケーションからの基本的な相互作用は、次の高レベル関数呼び出しを介して提供されます。
uint32_t wolfBoot_get_image_version(uint8_t part)
void wolfBoot_update_trigger(void)
void wolfBoot_success(void)
ファームウェアバージョン
現在(ブート)ファームウェアと更新ファームウェアバージョンは、以下を使用してアプリケーションから取得できます。
uint32_t wolfBoot_get_image_version(uint8_t part)
またはショートカットマクロを介して:
wolfBoot_current_firmware_version()
と
wolfBoot_update_firmware_version()
更新をトリガー
-wolfBoot_update_trigger()
は、次の再起動時に更新をトリガーするために使用され、通常、実行中のファームウェアの新しいバージョンを取得し、フラッシュ上の更新パーティションに保存した更新アプリケーションで使用されます。この関数は、更新パーティションの状態をSTATE_UPDATING
に設定し、ブートローダーに次の実行時に更新を実行するように指示します(再起動後)。
wolfBoot Updateプロセスは、一時的なシングルブロックスワップスペースを使用して、アップデートの内容とブートパーティションをスワップします。
現在のイメージの確認
wolfBoot_success()
は、新しいファームウェアのブートが成功したことを示します。これはいつでもアプリケーションで呼び出すことができますが、現在のファームウェア(ブートパーティション内)を状態STATE_SUCCESS
でマークするのは効果的であり、ロールバックが不要であることを示します。通常、アプリケーションは、基本的なシステム機能が稼働していることを確認した後にのみ、wolfBoot_success()
を呼び出す必要があります。
アップグレードと再起動の後、wolfBootがアクティブなファームウェアがまだSTATE_TESTING
状態にあることを検出した場合、それはアプリケーションのために成功したブートが確認されておらず、2つのイメージを再度交換して更新を戻そうとすることを意味します。
更新プロセスの詳細については、ファームウェアの更新を参照してください
イメージ形式については、ファームウェアイメージを参照してください