LoginSignup
10
5

More than 3 years have passed since last update.

ESP32のSecure Bootの仕組み

Last updated at Posted at 2020-08-14

はじめに

Secure Bootについて知りたくて、手元にあったESP32のSecure Bootの仕組みを調べました。実際にやってしまうと元に戻せなくなるので試してはいません。

主に参考にした資料はこちら。
ESP-IDF Programming Guide - Secure Boot
日本語訳も作ってみました。
https://qiita.com/hisashij/items/3372c93ebc28fa4298f9

手順を確認する際に使った環境はESP-IDF v4.0.1。バージョンが異なるとmenuconfigの構成が少し変わるようです。

前提情報

ESP32の基本的なbootの流れ

  1. まずROMにある1st-stage bootloaderが、フラッシュのオフセット0x1000から2nd-stage bootloaderをRAMに読み込み、処理を渡す。
  2. 2nd-stage bootloaderが、フラッシュからパーティションテーブルとアプリイメージをRAMに読み込み、アプリに処理を渡す。

image.png

ESP32のSecureBootの流れ(概要)

image.png

ESP32のSecure Bootで使われる鍵

以下2つの鍵を使用する。

  • Secure Boot Key

    • AES 256bit
    • ROMにある1st-stage bootloaderが、2nd-stage bootloaderをフラッシュから読み込んだ際の検証(ダイジェスト値の計算)で使われる。
    • 最初にブートされた際にhardware secure boot supportが生成し、eFuseのBLOCK 2に焼きこまれる。当該領域はR/W Protectされており読み出し不可。
  • Secure Boot Signing Key

    • ECDSA 256bit
    • 2nd-stage bootloaderが、パーティションテーブルとアプリイメージをフラッシュから読み込んだ際の署名検証で使われる。
    • 鍵は開発者がビルド前に生成し、厳重に保管しておく。ビルド時にその鍵を使って署名が行われる。検証用の公開鍵は2nd-stage bootloaderに埋め込まれる。

image.png

ESP32のeFuse

  • eFuseとは、一度ビットを1にすると0に戻すことができないメモリ。
  • ESP32は、各256ビットのBLOCKが、0から3まである。

ESP32のSecure Bootの種類

大きく以下3つの方式がある。

  1. Secure Boot: One-Time Flash
    本番環境で使う基本的な方式。一度書き込んだ2nd-stage bootloaderは、hardware内で生成されeFuseに焼きこまれたSecure Boot Keyで保護されており、変更できない。

  2. Secure Boot: Reflashable
    2nd-stage bootloaderを後で変更できる方式。Secure Boot Keyは、hardware内で生成されたものではなく、Secure Boot Signing Keyから導出されたものを使う。

  3. Signed App Verification Without Hardware Secure Boot
    2nd-stage bootloaderの検証は行わず、アプリイメージの署名検証だけを行う方式。

One-Time Flash方式

この手順を行うと元に戻せなくなるのでご注意ください。

ビルドの流れ

  1. menuconfigでの設定。
    「Security features」->「Enable hardware secure boot in bootloader」にチェックを入れる。デフォルトで「Secure bootloader mode」には「One-time flash」が選択されている。

  2. Secure Boot Signing Keyを生成する。
    python espsecure.py generate_signing_key secure_boot_signing_key.pem

  3. 2nd-stage bootloaderをビルドする。
    idf.py bootloader
    これにより、セキュアブートサポートが有効化され、Secure Boot Signing Keyに対応する公開鍵が含まれた、2nd-stage bootloaderが作られる。

  4. 2nd-stage bootloaderをフラッシュへ書き込む。
    idf.py -p (PORT) bootloader-flash

  5. アプリをビルドする。
    idf.py build
    アプリはSecure Boot Signing Keyで署名される。

  6. アプリをフラッシュへ書き込む。
    idf.py -p (PORT) flash

初回ブート時の流れ

  1. まずROMにある1st-stage bootloaderが、フラッシュのオフセット0x1000から2nd-stage bootloaderをRAMに読み込む。
  2. hardware secure boot supportが、Secure Boot Key(AES-256鍵)を生成してeFuse BLOCK 2に焼きこむ。
  3. hardware secure boot supportが、2nd-stage bootloaderのSHA-512ダイジェスト(64バイト)を計算し、計算に使用したIV(128バイト)とともに、フラッシュのオフセット0x0000に書き込む。ダイジェストの計算は以下の通り。
    1. ランダムに生成したIVを2nd-stage bootloaderの先頭に連結。
    2. AES-256で暗号化。
    3. SHA-512でダイジェストを計算。
  4. hardware secure boot supportが、セキュアブートの各種設定をeFuse BLOCK 0に焼きこむともに、BLOCK 0のabstract_done_0 bitを立てることでセキュアブートを有効化し、2nd-stage bootloaderに処理を渡す。
  5. 2nd-stage bootloaderは、フラッシュからパーティションテーブルとアプリイメージをRAMに読み込む。
  6. 2nd-stage bootloaderは、自身に組み込まれている署名検証用公開鍵(ECDSA 256bit)を使って、これらの検証を行った後、アプリに処理を渡す。

2度目以降のブート時の流れ

  1. まずROMにある1st-stage bootloaderが、フラッシュのオフセット0x1000から2nd-stage bootloaderをRAMに読み込む。
  2. eFuse BLOCK 0のabstract_done_0 bitが立っていたらSecure Bootの処理を開始。hardware secure boot supportを使って以下の流れで2nd-stage bootloaderを検証する。
    1. フラッシュのオフセット0x0000から、128バイトのIVと、64バイトのSHA-512ダイジェストデータを読み込む。
    2. eFuse BLOCK 2からAES-256鍵を読み込んで、2nd-stage bootloaderのダイジェストを再計算する。ダイジェストの計算は以下の通り。
      1. 2nd-stage bootloaderの先頭にIVを連結。
      2. AES-256で暗号化。
      3. SHA-512でダイジェストを計算。
    3. 再計算されたダイジェストと、フラッシュ読み込んだダイジェストが一致していることを確認。
  3. 2nd-stage bootloaderが検証されたら、2nd-stage bootloaderに処理を渡す。
  4. 2nd-stage bootloaderは、フラッシュからパーティションテーブルとアプリイメージをRAMに読み込む。
  5. 2nd-stage bootloaderは、自身に組み込まれている署名検証用公開鍵(ECDSA 256bit)を使って、これらの検証を行った後、アプリに処理を渡す。

アプリ置き換え時の流れ

同じSecure Boot Signing Keyがある状態で以下を行えばアプリが置き換えられる(たぶん)。

  1. アプリをビルドする。
    idf.py build
    アプリはSecure Boot Signing Keyで署名される。

  2. アプリをフラッシュへ書き込む。
    idf.py -p (PORT) flash

Reflashable方式

この手順を行うと元に戻せなくなるのでご注意ください。

ビルドの流れ

  1. menuconfigでの設定。
    「Security features」->「Enable hardware secure boot in bootloader」にチェックを入れる。
    続いて「Secure bootloader mode」を「One-time flash」から「Reflashable」に変更する。

  2. Secure Boot Signing Keyを生成する。
    python espsecure.py generate_signing_key secure_boot_signing_key.pem

  3. 2nd-stage bootloaderをビルドする。
    idf.py bootloader
    これにより、セキュアブートサポートが有効化され、Secure Boot Signing Keyに対応する公開鍵が含まれた、2nd-stage bootloaderが作られる。また、Secure Boot Signing Keyから導出したSecure Boot Keyが作られる。

  4. Secure Boot KeyをeFuseへ書き込む。
    ビルド時に表示されるメッセージにしたがって以下のようなコマンドで書き込む。
    python.exe espefuse.py burn_key secure_boot secure-bootloader-key-256.bin

  5. 2nd-stage bootloaderをフラッシュへ書き込む。
    idf.py -p (PORT) bootloader-flash

  6. アプリをビルドする。
    idf.py build
    アプリはSecure Boot Signing Keyで署名される。

  7. アプリをフラッシュへ書き込む。
    idf.py -p (PORT) flash

初回ブート時の流れ

One-Time Flash方式と同じ。ただし、Secure Boot KeyはeFuseに書込み済のものを使用する。

2度目以降のブート時の流れ

One-Time Flash方式と同じ。

2nd-stage bootloader置き換え時の流れ

同じSecure Boot Signing Keyがある状態で以下を行えばbootloaderとアプリが置き換えられる(たぶん)。

  1. 2nd-stage bootloaderをビルドする。
    idf.py bootloader

  2. 2nd-stage bootloaderのダイジェスト値をフラッシュへ書き込む。
    ビルド時に表示されるメッセージにしたがって以下のようなコマンドで書き込む。
    python.exe esptool.py --chip esp32 --port (PORT) --baud (BAUD) --before default_reset --after no_reset write_flash --flash_mode dio --flash_freq 40m --flash_size 2MB -u 0x0 bootloader-reflash-digest.bin

  3. 2nd-stage bootloaderをフラッシュへ書き込む。
    idf.py -p (PORT) bootloader-flash

  4. アプリをビルドする。
    idf.py build
    アプリはSecure Boot Signing Keyで署名される。

  5. アプリをフラッシュへ書き込む。
    idf.py -p (PORT) flash

Signed App Verification Without Hardware Secure Boot方式

ビルドの流れ

  1. menuconfigでの設定。
    「Security features」->「Require signed app amages」にチェックを入れる。チェックを入れると表示される、「Bootloader verifies app signatures」「Verify app signature on update」に必要に応じてチェックを入れる。

  2. Secure Boot Signing Keyを生成する。
    python espsecure.py generate_signing_key secure_boot_signing_key.pem

  3. アプリをビルドする。
    idf.py build
    アプリはSecure Boot Signing Keyで署名される。

  4. アプリをフラッシュへ書き込む。
    idf.py -p (PORT) flash

ブート時の流れ

  1. まずROMにある1st-stage bootloaderが、フラッシュのオフセット0x1000から2nd-stage bootloaderをRAMに読み込んで処理を渡す。
  2. 2nd-stage bootloaderは、フラッシュからパーティションテーブルとアプリイメージをRAMに読み込む。
  3. 2nd-stage bootloaderは、自身に組み込まれている署名検証用公開鍵(ECDSA 256bit)を使って、これらの検証を行った後、アプリに処理を渡す。

アプリ置き換え時の流れ

同じSecure Boot Signing Keyがある状態で以下を行えばアプリが置き換えられる(たぶん)。

  1. アプリをビルドする。
    idf.py build
    アプリはSecure Boot Signing Keyで署名される。

  2. アプリをフラッシュへ書き込む。
    idf.py -p (PORT) flash

10
5
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
10
5