2018年09月05日

ESP32 モジュールのフラッシュメモリ暗号化機構に関するメモ

はてなブックマークに登録

ESP-WROOM-32 の内蔵フラッシュメモリについて調べていたところで Flash Encryption に興味を持ちその便利さを実感しました。 利用方法にやや込み入った要素も含まれるため自分用のメモとして一連の情報を控えておくことにします。

重要: この記事には誤りが含まれている可能性があります。たとえその誤りが原因で何処かで何らかの損害が発生したとしても筆者ならびに当社は一切の責任を負いません。実験や操作は上記公式ドキュメントを熟読の上必ず自己責任で慎重に行って下さい。

概要

ESP32 モジュールのフラッシュメモリは esptool を使えば簡単に読み書きできるためバックアップ等に便利ですが、その一方で、自分の手を離れた場所で装置を稼働させる場合などに他者に不正なクローンを作成されたりコードに含まれるセンシティブな情報を読まれてしまう可能性もあります。 こういった懸念は Flash Encryption 機構を利用すれば格段に軽減されます。所定のモジュールのもとで暗号化したイメージを他のモジュールへ持ち込んで稼働させることはできず、そこから情報を窃視することもできません。

ただし、Flash Encryption の利用には大きくふたつの注意すべき点があります。

  1. ESP32 チップ内の OTP メモリへの不可逆な書き込みを伴う
  2. ひとたび Flash Encryption を適用したモジュールにおいてそれ以降のイメージの更新時に再暗号化を実施可能な回数は「3 回」まで

このように、アクティブな開発や実験のフェーズ向きではなくおおむねプログラム完成段階での本番機や製品への適用を想定した機構と言えるでしょう。そういった事情も相まってか、これを積極的に利用している個人ユーザは今のところあまり多くないように見受けられます。

フラッシュメモリ暗号化の多くのメリットを考えると惜しく感じられますが、実際には上記 2. の制限は「事前生成暗号化キー」を適切に取り回すことで回避が可能です。その方法であれば Flash Encryption をごく手軽に扱えるため、たとえばプロトタイピング段階での試作機での利用など様々な活用方法が考えられるでしょう。

以下の記事では、最初に Flash Encryption のしくみの整理を行い、次に上記の事前生成暗号化キーを利用する手順と実際にその操作を行った記録を列挙、最後に Arduino core for ESP32 環境での対応方法等について手元で行った実験の内容とその結果に触れています。 なお、Espressif は別のセキュリティ機構である Secure Boot との併用を推奨していますが、この記事ではまず Flash Encryption 単体に注目しています。

  • 手元では Linux 環境で作業を行っており、記事中のコマンド発行例などに「/dev/ttyUSB0」等の環境に即した記述が含まれます
  • Flash Encryption の操作で使用する esptool, espefuse, espsecure の各ユーティリティは、Linux, MacOSX 環境用には Python スクリプトとして、Windows 環境用には exe 実行形式で提供されています。記事での構文例には上と同じ理由で ".py" の拡張子を記述している箇所があります

Flash Encryption のしくみ

まず、公式ドキュメントの内容をもとに Flash Encryption のしくみを整理します。

全体像

  

  • Flash Encryption は ESP32 モジュールの内蔵フラッシュメモリ上の所定のパーティションの内容を AES-256 で暗号化するもの
  • 暗号化キーは当該モジュールで初めて暗号化を行う際に ESP32 チップ内の efuse 領域内にランダムに生成され永続的に保持される。生成されたキーへの外部からのアクセスはデフォルトで不可
    • ESP32 efuse の詳細 => espefuse - github.com/espressif
  • 暗号化はブートローダが行う。具体的には、make menuconfig - [Security Features] - [Enable flash encryption boot] オプションを有効にした状態でビルドしたブートローダが、他のパーティションイメージとともに make flash された後の初回起動時に一括して暗号化を実施する
    • つまり、上記オプションはその目的に即したブートローダコードを生成するものであり、その他のパーティションイメージは通常の内容でビルドされる
  • 暗号化の対象は以下のとおり
    • ブートローダ (オフセット 0x1000, 最長サイズ 0x7000 バイト
    • パーティションテーブル (オフセット 0x8000, サイズ 0xC00 バイト
    • Type = app のパーティション
    • encrypted フラグの指定されたパーティション
      • ただし、手元の確認では設定上 spiffs パーティションの定義に「spiffs,data,spiffs,0x291000,0x16F000,encrypted」の要領で指定を付与し暗号化を有効にしたところプログラム実行時に次のエラーが発生した: "spiffs can not run on encrypted partition"
    • nvs パーティション (key-value ストア領域) を暗号化することはできない。 spiffs と同様に nvs にも専用の API セットが用意されておりそっち側の実装とのかねあいだったり?
    • ドキュメント上の関連箇所の引用。ちなみに手元ではまだ使ったことのない「phy」パーティションについての言及があるが、「physical access readout」が NG につき esptool 等で読み出し不能であるためトレードオフを理解した上で利用すべきだろう
      • None of the default partition tables include any encrypted data partitions.
      • It is not necessary to mark “app” partitions as encrypted, they are always treated as encrypted.
      • The “encrypted” flag does nothing if flash encryption is not enabled.
      • It is possible to mark the optional phy partition with phy_init data as encrypted, if you wish to protect this data from physical access readout or modification.
      • It is not possible to mark the nvs partition as encrypted.
  • 稼働中のプログラムからフラッシュメモリ上のデータ参照の際の復号は透過的に行われる

注意点

Flash Encryption においては 8 ビットの「FLASH_CRYPT_CNT」efuse が非常に重要。あらかじめその役割と意味を正しく把握しておく必要がある。

  • まず、手元にある Flash Encryption 未実施の ESP32-DevKitC に対し Espressif 提供の espefuse ユーティリティの summary コマンド, dump コマンドを実行した結果を以下に示す
    $ espefuse.py --port /dev/ttyUSB0 summary
    espefuse.py v2.3.1
    Connecting.....
    Security fuses:
    FLASH_CRYPT_CNT        Flash encryption mode counter                     = 0 R/W (0x0)
    FLASH_CRYPT_CONFIG     Flash encryption config (key tweak bits)          = 0 R/W (0x0)
    CONSOLE_DEBUG_DISABLE  Disable ROM BASIC interpreter fallback            = 0 R/W (0x0)
    ABS_DONE_0             secure boot enabled for bootloader                = 0 R/W (0x0)
    ABS_DONE_1             secure boot abstract 1 locked                     = 0 R/W (0x0)
    JTAG_DISABLE           Disable JTAG                                      = 0 R/W (0x0)
    DISABLE_DL_ENCRYPT     Disable flash encryption in UART bootloader       = 0 R/W (0x0)
    DISABLE_DL_DECRYPT     Disable flash decryption in UART bootloader       = 0 R/W (0x0)
    DISABLE_DL_CACHE       Disable flash cache in UART bootloader            = 0 R/W (0x0)
    BLK1                   Flash encryption key                              
      = 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 R/W 
    BLK2                   Secure boot key                                   
      = 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 R/W 
    BLK3                   Variable Block 3                                  
      = 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 R/W 
    
    Efuse fuses:
    WR_DIS                 Efuse write disable mask                          = 0 R/W (0x0)
    RD_DIS                 Efuse read disablemask                            = 0 R/W (0x0)
    CODING_SCHEME          Efuse variable block length scheme                = 0 R/W (0x0)
    KEY_STATUS             Usage of efuse block 3 (reserved)                 = 0 R/W (0x0)
    
    Config fuses:
    XPD_SDIO_FORCE         Ignore MTDI pin (GPIO12) for VDD_SDIO on reset    = 0 R/W (0x0)
    XPD_SDIO_REG           If XPD_SDIO_FORCE, enable VDD_SDIO reg on reset   = 0 R/W (0x0)
    XPD_SDIO_TIEH          If XPD_SDIO_FORCE & XPD_SDIO_REG, 1=3.3V 0=1.8V   = 0 R/W (0x0)
    SPI_PAD_CONFIG_CLK     Override SD_CLK pad (GPIO6/SPICLK)                = 0 R/W (0x0)
    SPI_PAD_CONFIG_Q       Override SD_DATA_0 pad (GPIO7/SPIQ)               = 0 R/W (0x0)
    SPI_PAD_CONFIG_D       Override SD_DATA_1 pad (GPIO8/SPID)               = 0 R/W (0x0)
    SPI_PAD_CONFIG_HD      Override SD_DATA_2 pad (GPIO9/SPIHD)              = 0 R/W (0x0)
    SPI_PAD_CONFIG_CS0     Override SD_CMD pad (GPIO11/SPICS0)               = 0 R/W (0x0)
    DISABLE_SDIO_HOST      Disable SDIO host                                 = 0 R/W (0x0)
    
    Identity fuses:
    MAC                    MAC Address                                       
      = 30:ae:a4:02:59:c0 (CRC 9b OK) R/W 
    CHIP_VER_REV1          Silicon Revision 1                                = 0 R/W (0x0)
    CHIP_VERSION           Reserved for future chip versions                 = 0 R/W (0x0)
    CHIP_PACKAGE           Chip package identifier                           = 0 R/W (0x0)
    
    Calibration fuses:
    BLK3_PART_RESERVE      BLOCK3 partially served for ADC calibration data  = 0 R/W (0x0)
    ADC_VREF               Voltage reference calibration                     = 1100 R/W (0x0)
    
    Flash voltage (VDD_SDIO) determined by GPIO12 on reset (High for 1.8V, Low/NC for 3.3V).
    
    $ espefuse.py --port /dev/ttyUSB0 dump
    espefuse.py v2.3.1
    Connecting....
    EFUSE block 0:
    00000000 a40259c0 009b30ae 00000000 00000036 00000000 00000000
    EFUSE block 1:
    00000000 00000000 00000000 00000000 00000000 00000000 00000000 00000000
    EFUSE block 2:
    00000000 00000000 00000000 00000000 00000000 00000000 00000000 00000000
    EFUSE block 3:
    00000000 00000000 00000000 00000000 00000000 00000000 00000000 00000000
    
  • FLASH_CRYPT_CNT は暗号化を行った回数と暗号化の有効無効状態を管理する。初期値は上のとおりゼロ (BIN: 0000 0000)であり、ブートローダが初めて暗号化を行った時点で BIN:0000 0001 に変化する (不可逆)
  • FLASH_CRYPT_CNT の ON ビット数が偶数の場合は暗号化が無効な状態、奇数の場合は暗号化が有効な状態であることを示す
  • FLASH_CRYPT_CNT の ON ビット数が奇数、すなわち暗号化が有効な状態で別のプレーンなイメージを make flash すると起動不可となる。これは、ESP32 が efuse 領域に保持しているキーで暗号化されたイメージの期待される状況であるにもかかわらず場違いなイメージに直面した結果の所作
  • 上記のような場合には以下の手順で Flash Encryption を無効化することができる (注:手元ではこの操作は未確認)
    1. make menuconfig - [Security Features] - [Enable flash encryption boot] オプションを確実に無効化して make && make flash
    2. 手作業で FLASH_CRYPT_CNT の ON ビット数を偶数にすることで暗号化無効の状態にする。具体的には、espefuse ユーティリティを使って FLASH_CRYPT_CNT に対し burn_efuse コマンドを発行すると現在もっとも上位の ON ビットの左側のビットが ON になる。つまり、FLASH_CRYPT_CNT = BIN:0000 0001 の状態で実行すると BIN:0000 0011 に変化する => ON ビット数が偶数なので暗号化無効状態
      espefuse.py --port /dev/ttyUSB0 burn_efuse FLASH_CRYPT_CNT
    3. ESP32 ボードをリセットすると正常に稼働する

  • OTP メモリである efuse 領域への変更は不可逆であるため、8 ビットの FLASH_CRYPT_CNT に対する上記の「暗号化と無効化」の上限回数は 4 回。以下に状態の一覧を示す。 9. の無効化 4 回めで全ビットが ON = 0xFF になると当該 ESP32 モジュールでの Flash Encryption は永久に無効となる
    1. BIN:0000 0000 工場出荷時
    2. BIN:0000 0001 暗号化 1 回め
    3. BIN:0000 0011 無効化 1 回め
    4. BIN:0000 0111 暗号化 2 回め
    5. BIN:0000 1111 無効化 2 回め
    6. BIN:0001 1111 暗号化 3 回め
    7. BIN:0011 1111 無効化 3 回め
    8. BIN:0111 1111 暗号化 4 回め
    9. BIN:1111 1111 無効化 4 回め

  • なお、ある時点での FLASH_CRYPT_CNT の状態を永続化させたい場合は espefuse.py の write_protect_efuse コマンドを実行する。誤って実行すると手詰まりになるため要注意
    espefuse.py --port /dev/ttyUSB0 write_protect_efuse FLASH_CRYPT_CNT
  • また、以下の事情にも注意が必要
    • Read- and Write- protecting efuses - espefuse - github.com/espressif
      NOTE that efuses are often read/write protected as a group, so protecting one will cause some related efuses to become protected. espefuse.py will confirm the full list of efuses that will become protected.

事前生成暗号化キーの利用による Flash Encryption

ESP32 チップ内部に保持されるフラッシュメモリ暗号化キーはホスト PC 上で生成することが可能です。キーの生成を ESP32 側で行うとそれ以降の暗号化処理は前述の FLASH_CRYPT_CNT の管理下でブートローダへ委ねることになりますが、ホスト PC 上でキーを生成すれば開発者が主体的に所定のイメージの暗号化を行うことが可能となり、貴重な FLASH_CRYPT_CNT を消費することなく Flash Encryption を継続的に利用できます。ホスト PC 上で生成したキーは espefuse ユーティリティの burn_key コマンドで ESP32 の efuse 領域へ書き込んだ上で使用します。

概要

ドキュメントより。

  • Reflashing via Pregenerated Flash Encryption Key - Flash Encryption - esp-idf.readthedocs.io
    Reflashing via Pregenerated Flash Encryption Key

    It is possible to pregenerate a flash encryption key on the host computer and burn it into the ESP32’s efuse key block. This allows data to be pre-encrypted on the host and flashed to the ESP32 without needing a plaintext flash update.

    This is useful for development, because it removes the 4 time reflashing limit. It also allows reflashing the app with secure boot enabled, because the bootloader doesn’t need to be reflashed each time.

    Important

    This method is intended to assist with development only, not for production devices. If pre-generating flash encryption for production, ensure the keys are generated from a high quality random number source and do not share the same flash encryption key across multiple devices.
           :
    Google 訳:
    事前生成されたフラッシュ暗号化キーによるリフラッシュ

    ホストコンピュータにフラッシュ暗号化キーを事前に生成し、ESP32のefuseキーブロックに書き込むことができます。これにより、データをホスト上で事前暗号化し、平文フラッシュ更新を必要とせずにESP32にフラッシュすることができます。

    これは4回のリフラッシュ制限を取り除くため、開発に役立ちます。また、毎回ブートローダをリフラッシュする必要がないため、セキュアブートを有効にしてアプリをリフラッシュすることもできます。

    重要

    このメソッドは、開発の支援のみ目的としており、本番用のデバイス用ではありません。プロダクション用のフラッシュ暗号化を事前生成する場合は、キーが高品質の乱数ソースから生成され、複数のデバイス間で同じフラッシュ暗号化キーを共有しないようにしてください。
           :
多くの個人ユーザの作業の大部分はまさに「開発」であり、その過程でフラッシュメモリ暗号化の恩恵を享受できることは有り難い。事前生成暗号化キーを利用した Flash Encryption の流れを下図に示す。

手順

以下の一連の手順では、既出の esptool, espefuse に加え、espsecure ユーティリティを使用する。

初回の暗号化を終えるまで

第一段階として、まず初回の暗号化までを完了させる。ホスト PC 上で生成した暗号化キーを ESP32 の efuse へ先行して書き込むことがここでのポイント。

  1. 暗号化キーをホスト PC 上で生成する。初回のみ。絶対に紛失しないこと
    espsecure.py generate_flash_encryption_key ./mykey.bin
  2. 生成したキーを ESP32 の efuse へ書き込む。取り消し不可
    espefuse.py --port /dev/ttyUSB0 burn_key flash_encryption ./mykey.bin
    ※この手順で下記「3. 初回の Flash Encryption」に先行して efuse へ暗号化キーを書き込むことにより、本来はブートローダによる初回の暗号化実行の前処理として ESP32 チップ内で行われる暗号化キーの生成がスキップされる
  3. 初回の Flash Encryption を実行
    初回の暗号化は事前生成キーを使わない場合の手順と同じ
    1. make menuconfig で [Security Features] - [Enable flash encryption boot] オプションを有効にして保存終了
    2. make を実行
    3. make flash monitor を実行
    4. flash 後のリセットを経てブートローダによる暗号化が始まる(完了まで電源を落とさないこと)。シリアル出力の monitor は Ctrl+']' で終了
    5. 暗号化が完了するとブートローダが ESP32 にリセットをかける 〜 暗号化されたイメージでプログラムが起動
    6. 前掲の espefuse.py --port [PORT] summary コマンドを実行し先頭の FLASH_CRYPT_CNT が「1 R/W (0x1)」に変わっていることを確認

ホスト PC 上でのイメージの暗号化とフラッシュメモリへの書き込み

初回の暗号化は上の手順で OK。それ以降にビルドしたイメージについては手元で暗号化を行った上で ESP32 へ転送すればよい。以下の例は app パーティションイメージ分だが、必要に応じて他のイメージも暗号化して書き込む。なお、ビルドしたままの状態のブートローダイメージを再度フラッシュしてはならない。ESP32 側ではブートローダ自体もすでに暗号化されているため。

  1. 更新したプログラムを make してイメージをビルドする。make flash は行わないこと
  2. espsecure.py ユーティリティを使ってホスト PC 上で当該イメージを暗号化する。--address はイメージビルド時点のパーティション定義にそって指定する。 下記例では元のプレーンなイメージが「build/mySimpleApp.bin」であり、出力する暗号化イメージが「build/mySimpleApp_Encrypted.bin」である
    espsecure.py encrypt_flash_data --keyfile ./mykey.bin --address 0x10000 -o build/mySimpleApp_Encrypted.bin build/mySimpleApp.bin
  3. 暗号化したイメージを esptool で ESP32 フラッシュメモリへ書き込んで完了
    esptool.py --port /dev/ttyUSB0 --baud 921600 write_flash 0x10000 build/mySimpleApp_Encrypted.bin

前項の操作を行った記録

手元で実際に前項の操作を行いその様子と結果を記録しました。これが初めての Flash Encryption の操作で、各動画は一発撮りしたものです。

テスト用のプログラムとして以下の「mySimpleApp.c」を使用。LED 点滅ループ中に Flash Encryption の有効無効状態をシリアル出力する内容。

#include <stdio.h>
#include "esp_flash_encrypt.h"
#include "esp_event.h"
#include "esp_log.h"
#include "driver/gpio.h"

const char *secretData = "**** this is secret data ****";
const char *TAG = "TAG";

void app_main()
{
  ESP_LOGI(TAG, "%s", secretData);
  gpio_set_direction(GPIO_NUM_2, GPIO_MODE_OUTPUT);
  int sts = 1;
  for (int i = 0; i < 10; i++) {
    if (esp_flash_encryption_enabled()) {
      ESP_LOGI(TAG, "Flash Encryption is enabled");
    } else {
      ESP_LOGI(TAG, "Flash Encryption is disabled");
    }
    gpio_set_level(GPIO_NUM_2, sts);
    sts = !sts;
    vTaskDelay(1000 / portTICK_PERIOD_MS);
  }
}
まず Flash Encryption 有効化前の素の状態のボードに対し make flash monitor を行った様子。"Flash Encryption is disabled" の出力が見える。

動画 A
  

以下、事前生成暗号化キーを使った Flash Encryption 操作の記録。

  1. 暗号化キーをホスト PC 上で生成 〜 生成したキーを ESP32 の efuse 領域へ書き込む
    $ espsecure.py generate_flash_encryption_key ./mykey.bin
    espsecure.py v2.3.1
    
    $ espefuse.py --port /dev/ttyUSB0 burn_key flash_encryption ./mykey.bin
    espefuse.py v2.3.1
    Connecting......
    Write key in efuse block 1. The key block will be read and write protected (no further changes or readback). This is an irreversible operation.
    Type 'BURN' (all capitals) to continue.
    BURN
    Burned key data. New value: 6a 60 3c a7 d0 3f d4 9f b0 a3 f9 ca 77 61 10 47 30 57 80 c5 5c fd bc a0 ce 30 3e 36 b1 b5 ac c4
    Disabling read/write to key efuse block...
    
    
    動画 B
  2. 上記 1. 操作後の efuse の状態
    $ espefuse.py --port /dev/ttyUSB0 summary
    espefuse.py v2.3.1
    Connecting......
    Security fuses:
    FLASH_CRYPT_CNT        Flash encryption mode counter                     = 0 R/W (0x0)
    FLASH_CRYPT_CONFIG     Flash encryption config (key tweak bits)          = 0 R/W (0x0)
    CONSOLE_DEBUG_DISABLE  Disable ROM BASIC interpreter fallback            = 0 R/W (0x0)
    ABS_DONE_0             secure boot enabled for bootloader                = 0 R/W (0x0)
    ABS_DONE_1             secure boot abstract 1 locked                     = 0 R/W (0x0)
    JTAG_DISABLE           Disable JTAG                                      = 0 R/W (0x0)
    DISABLE_DL_ENCRYPT     Disable flash encryption in UART bootloader       = 0 R/W (0x0)
    DISABLE_DL_DECRYPT     Disable flash decryption in UART bootloader       = 0 R/W (0x0)
    DISABLE_DL_CACHE       Disable flash cache in UART bootloader            = 0 R/W (0x0)
    BLK1                   Flash encryption key                              
      = 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 -/- 
    BLK2                   Secure boot key                                   
      = 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 R/W 
    BLK3                   Variable Block 3                                  
      = 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 R/W 
    
    Efuse fuses:
    WR_DIS                 Efuse write disable mask                          = 128 R/W (0x80)
    RD_DIS                 Efuse read disablemask                            = 1 R/W (0x1)
    CODING_SCHEME          Efuse variable block length scheme                = 0 R/W (0x0)
    KEY_STATUS             Usage of efuse block 3 (reserved)                 = 0 R/W (0x0)
    
    Config fuses:
    XPD_SDIO_FORCE         Ignore MTDI pin (GPIO12) for VDD_SDIO on reset    = 0 R/W (0x0)
    XPD_SDIO_REG           If XPD_SDIO_FORCE, enable VDD_SDIO reg on reset   = 0 R/W (0x0)
    XPD_SDIO_TIEH          If XPD_SDIO_FORCE & XPD_SDIO_REG, 1=3.3V 0=1.8V   = 0 R/W (0x0)
    SPI_PAD_CONFIG_CLK     Override SD_CLK pad (GPIO6/SPICLK)                = 0 R/W (0x0)
    SPI_PAD_CONFIG_Q       Override SD_DATA_0 pad (GPIO7/SPIQ)               = 0 R/W (0x0)
    SPI_PAD_CONFIG_D       Override SD_DATA_1 pad (GPIO8/SPID)               = 0 R/W (0x0)
    SPI_PAD_CONFIG_HD      Override SD_DATA_2 pad (GPIO9/SPIHD)              = 0 R/W (0x0)
    SPI_PAD_CONFIG_CS0     Override SD_CMD pad (GPIO11/SPICS0)               = 0 R/W (0x0)
    DISABLE_SDIO_HOST      Disable SDIO host                                 = 0 R/W (0x0)
    
    Identity fuses:
    MAC                    MAC Address                                       
      = 30:ae:a4:02:59:c0 (CRC 9b OK) R/W 
    CHIP_VER_REV1          Silicon Revision 1                                = 0 -/W (0x0)
    CHIP_VERSION           Reserved for future chip versions                 = 0 -/W (0x0)
    CHIP_PACKAGE           Chip package identifier                           = 0 -/W (0x0)
    
    Calibration fuses:
    BLK3_PART_RESERVE      BLOCK3 partially served for ADC calibration data  = 0 -/W (0x0)
    ADC_VREF               Voltage reference calibration                     = 1100 -/W (0x0)
    
    Flash voltage (VDD_SDIO) determined by GPIO12 on reset (High for 1.8V, Low/NC for 3.3V).
    
    
    $ espefuse.py --port /dev/ttyUSB0 dump
    espefuse.py v2.3.1
    Connecting....
    EFUSE block 0:
    00010080 a40259c0 009b30ae 00000000 00000036 00000000 00000000
    EFUSE block 1:
    00000000 00000000 00000000 00000000 00000000 00000000 00000000 00000000
    EFUSE block 2:
    00000000 00000000 00000000 00000000 00000000 00000000 00000000 00000000
    EFUSE block 3:
    00000000 00000000 00000000 00000000 00000000 00000000 00000000 00000000
    
    
  3. make menuconfig で [Security Features] - [Enable flash encryption boot] を有効化した後で make を実行
    $ make
    CC build/bootloader/bootloader_support/src/bootloader_clock.o
    CC build/bootloader/bootloader_support/src/bootloader_flash.o
    CC build/bootloader/bootloader_support/src/bootloader_init.o
    CC build/bootloader/bootloader_support/src/bootloader_random.o
    CC build/bootloader/bootloader_support/src/bootloader_sha.o
    CC build/bootloader/bootloader_support/src/bootloader_utility.o
    CC build/bootloader/bootloader_support/src/efuse.o
                         (中略)
    CC build/xtensa-debug-module/trax.o
    AR build/xtensa-debug-module/libxtensa-debug-module.a
    LD build/mySimpleApp.elf
    esptool.py v2.3.1
    To flash all build output, run 'make flash' or:
    python /home/t/esp/esp-idf/components/esptool_py/esptool/esptool.py --chip esp32 --port /dev/ttyUSB0 --baud 115200 --before default_reset --after hard_reset write_flash -z --flash_mode dio --flash_freq 40m --flash_size detect 0x1000 /home/t/Arduino/my/for_ESP-IDF/mySimpleApp/build/bootloader/bootloader.bin 0x10000 /home/t/Arduino/my/for_ESP-IDF/mySimpleApp/build/mySimpleApp.bin 0x8000 /home/t/Arduino/my/for_ESP-IDF/mySimpleApp/build/partitions_singleapp.bin
    
    
    余談ながら、上の最終行のコマンド例中の「--after hard_reset」の記述が気になり Advanced Options - espressif/esptool Wiki で確認したところ、「--after no_reset」オプションの存在を知った。デフォルトは「--after hard_reset」。esptool 操作後に自動的にリセットがかかると具合のわるい場合があるが、no_reset 指定でそれを回避できることを覚えた。
    動画 C
  4. make flash monitor --- ブートローダが各パーティションを暗号化している状況が見てとれる。その後起動した mySimpleApp からのメッセージ出力が "Flash Encryption is enabled" に変化している
    $ make flash monitor
    Flashing binaries to serial port /dev/ttyUSB0 (app at offset 0x10000)...
    esptool.py v2.3.1
    Connecting........_
    Chip is ESP32D0WDQ6 (revision 0)
    Features: WiFi, BT, Dual Core
    Uploading stub...
    Running stub...
    Stub running...
    Configuring flash size...
    Auto-detected Flash size: 4MB
    Flash params set to 0x0220
    Compressed 25296 bytes to 14703...
    Wrote 25296 bytes (14703 compressed) at 0x00001000 in 1.3 seconds (effective 156.4 kbit/s)...
    Hash of data verified.
    Compressed 140160 bytes to 68713...
    Wrote 140160 bytes (68713 compressed) at 0x00010000 in 6.1 seconds (effective 184.8 kbit/s)...
    Hash of data verified.
    Compressed 3072 bytes to 103...
    Wrote 3072 bytes (103 compressed) at 0x00008000 in 0.0 seconds (effective 1885.7 kbit/s)...
    Hash of data verified.
    
    Leaving...
    Hard resetting via RTS pin...
    MONITOR
    --- idf_monitor on /dev/ttyUSB0 115200 ---
    --- Quit: Ctrl+] | Menu: Ctrl+T | Help: Ctrl+T followed by Ctrl+H ---
    ets Jun  8 2016 00:22:57
    
    rst:0x1 (POWERON_RESET),boot:0x13 (SPI_FAST_FLASH_BOOT)
    ets Jun  8 2016 00:22:57
    
    rst:0x10 (RTCWDT_RTC_RESET),boot:0x13 (SPI_FAST_FLASH_BOOT)
    configsip: 0, SPIWP:0xee
    clk_drv:0x00,q_drv:0x00,d_drv:0x00,cs0_drv:0x00,hd_drv:0x00,wp_drv:0x00
    mode:DIO, clock div:2
    load:0x3fff0018,len:4
    load:0x3fff001c,len:7400
    load:0x40078000,len:0
    ho 12 tail 0 room 4
    load:0x40078000,len:17796
    entry 0x40078724
    I (31) boot: ESP-IDF v3.1-dev-1171-g358c822 2nd stage bootloader
    I (31) boot: compile time 16:28:39
    I (31) boot: Enabling RNG early entropy source...
    I (37) boot: SPI Speed      : 40MHz
    I (41) boot: SPI Mode       : DIO
    I (45) boot: SPI Flash Size : 4MB
    I (49) boot: Partition Table:
    I (52) boot: ## Label            Usage          Type ST Offset   Length
    I (60) boot:  0 nvs              WiFi data        01 02 00009000 00006000
    I (67) boot:  1 phy_init         RF data          01 01 0000f000 00001000
    I (75) boot:  2 factory          factory app      00 00 00010000 00100000
    I (82) boot: End of partition table
    I (86) esp_image: segment 0: paddr=0x00010020 vaddr=0x3f400020 size=0x05888 ( 22664) map
    I (103) esp_image: segment 1: paddr=0x000158b0 vaddr=0x3ffb0000 size=0x02284 (  8836) load
    I (107) esp_image: segment 2: paddr=0x00017b3c vaddr=0x40080000 size=0x00400 (  1024) load
    0x40080000: _iram_start at /home/t/esp/esp-idf/components/freertos/xtensa_vectors.S:1685
    
    I (113) esp_image: segment 3: paddr=0x00017f44 vaddr=0x40080400 size=0x080cc ( 32972) load
    I (136) esp_image: segment 4: paddr=0x00020018 vaddr=0x400d0018 size=0x11ccc ( 72908) map
    0x400d0018: _flash_cache_start at ??:?
    
    I (161) esp_image: segment 5: paddr=0x00031cec vaddr=0x400884cc size=0x00668 (  1640) load
    0x400884cc: esp_rom_spiflash_program_page_internal at /home/t/esp/esp-idf/components/spi_flash/spi_flash_rom_patch.c:412
    
    I (162) esp_image: segment 6: paddr=0x0003235c vaddr=0x400c0000 size=0x00000 (     0) load
    I (174) boot: Loaded app from partition at offset 0x10000
    I (174) boot: Checking flash encryption...
    W (179) flash_encrypt: Using pre-loaded flash encryption key in EFUSE block 1
    I (187) flash_encrypt: Setting CRYPT_CONFIG efuse to 0xF
    I (205) flash_encrypt: Disable UART bootloader encryption...
    I (205) flash_encrypt: Disable UART bootloader decryption...
    I (208) flash_encrypt: Disable UART bootloader MMU cache...
    I (214) flash_encrypt: Disable JTAG...
    I (218) flash_encrypt: Disable ROM BASIC interpreter fallback...
    I (237) esp_image: segment 0: paddr=0x00001020 vaddr=0x3fff0018 size=0x00004 (     4) 
    I (237) esp_image: segment 1: paddr=0x0000102c vaddr=0x3fff001c size=0x01ce8 (  7400) 
    I (247) esp_image: segment 2: paddr=0x00002d1c vaddr=0x40078000 size=0x00000 (     0) 
    I (253) esp_image: segment 3: paddr=0x00002d24 vaddr=0x40078000 size=0x04584 ( 17796) 
    I (701) esp_image: segment 0: paddr=0x00010020 vaddr=0x3f400020 size=0x05888 ( 22664) map
    I (709) esp_image: segment 1: paddr=0x000158b0 vaddr=0x3ffb0000 size=0x02284 (  8836) 
    I (712) esp_image: segment 2: paddr=0x00017b3c vaddr=0x40080000 size=0x00400 (  1024) 
    0x40080000: _iram_start at /home/t/esp/esp-idf/components/freertos/xtensa_vectors.S:1685
    
    I (716) esp_image: segment 3: paddr=0x00017f44 vaddr=0x40080400 size=0x080cc ( 32972) 
    I (736) esp_image: segment 4: paddr=0x00020018 vaddr=0x400d0018 size=0x11ccc ( 72908) map
    0x400d0018: _flash_cache_start at ??:?
    
    I (762) esp_image: segment 5: paddr=0x00031cec vaddr=0x400884cc size=0x00668 (  1640) 
    0x400884cc: esp_rom_spiflash_program_page_internal at /home/t/esp/esp-idf/components/spi_flash/spi_flash_rom_patch.c:412
    
    I (763) esp_image: segment 6: paddr=0x0003235c vaddr=0x400c0000 size=0x00000 (     0) 
    I (768) flash_encrypt: Encrypting partition 2 at offset 0x10000...
    [0;3oets Jun  8 2016 00:22:57
    
    rst:0x3 (SW_RESET),boot:0x13 (SPI_FAST_FLASH_BOOT)
    configsip: 0, SPIWP:0xee
    clk_drv:0x00,q_drv:0x00,d_drv:0x00,cs0_drv:0x00,hd_drv:0x00,wp_drv:0x00
    mode:DIO, clock div:2
    load:0x3fff0018,len:4
    load:0x3fff001c,len:7400
    load:0x40078000,len:0
    ho 12 tail 0 room 4
    load:0x40078000,len:17796
    entry 0x40078724
    I (30) boot: ESP-IDF v3.1-dev-1171-g358c822 2nd stage bootloader
    I (30) boot: compile time 16:28:39
    I (30) boot: Enabling RNG early entropy source...
    I (36) boot: SPI Speed      : 40MHz
    I (41) boot: SPI Mode       : DIO
    I (45) boot: SPI Flash Size : 4MB
    I (49) boot: Partition Table:
    I (52) boot: ## Label            Usage          Type ST Offset   Length
    I (59) boot:  0 nvs              WiFi data        01 02 00009000 00006000
    I (67) boot:  1 phy_init         RF data          01 01 0000f000 00001000
    I (74) boot:  2 factory          factory app      00 00 00010000 00100000
    I (82) boot: End of partition table
    I (86) esp_image: segment 0: paddr=0x00010020 vaddr=0x3f400020 size=0x05888 ( 22664) map
    I (103) esp_image: segment 1: paddr=0x000158b0 vaddr=0x3ffb0000 size=0x02284 (  8836) load
    I (107) esp_image: segment 2: paddr=0x00017b3c vaddr=0x40080000 size=0x00400 (  1024) load
    0x40080000: _iram_start at /home/t/esp/esp-idf/components/freertos/xtensa_vectors.S:1685
    
    I (113) esp_image: segment 3: paddr=0x00017f44 vaddr=0x40080400 size=0x080cc ( 32972) load
    I (136) esp_image: segment 4: paddr=0x00020018 vaddr=0x400d0018 size=0x11ccc ( 72908) map
    0x400d0018: _flash_cache_start at ??:?
    
    I (163) esp_image: segment 5: paddr=0x00031cec vaddr=0x400884cc size=0x00668 (  1640) load
    0x400884cc: esp_rom_spiflash_program_page_internal at /home/t/esp/esp-idf/components/spi_flash/spi_flash_rom_patch.c:412
    
    I (163) esp_image: segment 6: paddr=0x0003235c vaddr=0x400c0000 size=0x00000 (     0) load
    I (175) boot: Loaded app from partition at offset 0x10000
    I (175) boot: Checking flash encryption...
    I (180) flash_encrypt: flash encryption is enabled (3 plaintext flashes left)
    I (188) boot: Disabling RNG early entropy source...
    I (193) cpu_start: Pro cpu up.
    I (197) cpu_start: Starting app cpu, entry point is 0x40080e4c
    0x40080e4c: call_start_cpu1 at /home/t/esp/esp-idf/components/esp32/cpu_start.c:225
    
    I (0) cpu_start: App cpu up.
    I (208) heap_init: Initializing. RAM available for dynamic allocation:
    I (214) heap_init: At 3FFAE6E0 len 00001920 (6 KiB): DRAM
    I (220) heap_init: At 3FFB32C0 len 0002CD40 (179 KiB): DRAM
    I (227) heap_init: At 3FFE0440 len 00003BC0 (14 KiB): D/IRAM
    I (233) heap_init: At 3FFE4350 len 0001BCB0 (111 KiB): D/IRAM
    I (239) heap_init: At 40088B34 len 000174CC (93 KiB): IRAM
    I (246) cpu_start: Pro cpu start user code
    I (264) cpu_start: Starting scheduler on PRO CPU.
    I (0) cpu_start: Starting scheduler on APP CPU.
    I (265) TAG: **** this is secret data ****
    I (265) TAG: Flash Encryption is enabled
    I (1265) TAG: Flash Encryption is enabled
    I (2265) TAG: Flash Encryption is enabled
    I (3265) TAG: Flash Encryption is enabled
    I (4265) TAG: Flash Encryption is enabled
    I (5265) TAG: Flash Encryption is enabled
    I (6265) TAG: Flash Encryption is enabled
    I (7265) TAG: Flash Encryption is enabled
    I (8265) TAG: Flash Encryption is enabled
    I (9265) TAG: Flash Encryption is enabled
    
    
    動画 D
  5. 上記 4. 初回暗号化完了後の efuse の状態 -- FLASH_CRYPT_CNT efuse の値が正しく「0x01」に変化
    $ espefuse.py --port /dev/ttyUSB0 summary
    
    espefuse.py v2.3.1
    Connecting.....
    Security fuses:
    FLASH_CRYPT_CNT        Flash encryption mode counter                     = 1 R/W (0x1)
    FLASH_CRYPT_CONFIG     Flash encryption config (key tweak bits)          = 15 R/W (0xf)
    CONSOLE_DEBUG_DISABLE  Disable ROM BASIC interpreter fallback            = 1 R/W (0x1)
    ABS_DONE_0             secure boot enabled for bootloader                = 0 R/W (0x0)
    ABS_DONE_1             secure boot abstract 1 locked                     = 0 R/W (0x0)
    JTAG_DISABLE           Disable JTAG                                      = 1 R/W (0x1)
    DISABLE_DL_ENCRYPT     Disable flash encryption in UART bootloader       = 1 R/W (0x1)
    DISABLE_DL_DECRYPT     Disable flash decryption in UART bootloader       = 1 R/W (0x1)
    DISABLE_DL_CACHE       Disable flash cache in UART bootloader            = 1 R/W (0x1)
    BLK1                   Flash encryption key                              
      = 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 -/- 
    BLK2                   Secure boot key                                   
      = 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 R/W 
    BLK3                   Variable Block 3                                  
      = 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 R/W 
    
    Efuse fuses:
    WR_DIS                 Efuse write disable mask                          = 128 R/W (0x80)
    RD_DIS                 Efuse read disablemask                            = 1 R/W (0x1)
    CODING_SCHEME          Efuse variable block length scheme                = 0 R/W (0x0)
    KEY_STATUS             Usage of efuse block 3 (reserved)                 = 0 R/W (0x0)
    
    Config fuses:
    XPD_SDIO_FORCE         Ignore MTDI pin (GPIO12) for VDD_SDIO on reset    = 0 R/W (0x0)
    XPD_SDIO_REG           If XPD_SDIO_FORCE, enable VDD_SDIO reg on reset   = 0 R/W (0x0)
    XPD_SDIO_TIEH          If XPD_SDIO_FORCE & XPD_SDIO_REG, 1=3.3V 0=1.8V   = 0 R/W (0x0)
    SPI_PAD_CONFIG_CLK     Override SD_CLK pad (GPIO6/SPICLK)                = 0 R/W (0x0)
    SPI_PAD_CONFIG_Q       Override SD_DATA_0 pad (GPIO7/SPIQ)               = 0 R/W (0x0)
    SPI_PAD_CONFIG_D       Override SD_DATA_1 pad (GPIO8/SPID)               = 0 R/W (0x0)
    SPI_PAD_CONFIG_HD      Override SD_DATA_2 pad (GPIO9/SPIHD)              = 0 R/W (0x0)
    SPI_PAD_CONFIG_CS0     Override SD_CMD pad (GPIO11/SPICS0)               = 0 R/W (0x0)
    DISABLE_SDIO_HOST      Disable SDIO host                                 = 0 R/W (0x0)
    
    Identity fuses:
    MAC                    MAC Address                                       
      = 30:ae:a4:02:59:c0 (CRC 9b OK) R/W 
    CHIP_VER_REV1          Silicon Revision 1                                = 0 -/W (0x0)
    CHIP_VERSION           Reserved for future chip versions                 = 0 -/W (0x0)
    CHIP_PACKAGE           Chip package identifier                           = 0 -/W (0x0)
    
    Calibration fuses:
    BLK3_PART_RESERVE      BLOCK3 partially served for ADC calibration data  = 0 -/W (0x0)
    ADC_VREF               Voltage reference calibration                     = 1100 -/W (0x0)
    
    Flash voltage (VDD_SDIO) determined by GPIO12 on reset (High for 1.8V, Low/NC for 3.3V).
    
    
    $ espefuse.py --port /dev/ttyUSB0 dump espefuse.py v2.3.1
    Connecting......
    EFUSE block 0:
    00110080 a40259c0 009b30ae 00000000 00000036 f0000000 000003c4
    EFUSE block 1:
    00000000 00000000 00000000 00000000 00000000 00000000 00000000 00000000
    EFUSE block 2:
    00000000 00000000 00000000 00000000 00000000 00000000 00000000 00000000
    EFUSE block 3:
    00000000 00000000 00000000 00000000 00000000 00000000 00000000 00000000
    
    
  6. 上記 4. 初回暗号化完了後にあらためてプレーンな app イメージを esptool で書き込んで make monitor -- アプリの起動が適切に弾かれる
    $ esptool.py --port /dev/ttyUSB0 --baud 921600 write_flash 0x10000 build/mySimpleApp.bin; make monitor
    esptool.py v2.3.1
    Connecting....
    Detecting chip type... ESP32
    Chip is ESP32D0WDQ6 (revision 0)
    Features: WiFi, BT, Dual Core
    Uploading stub...
    Running stub...
    Stub running...
    Changing baud rate to 921600
    Changed.
    Configuring flash size...
    Auto-detected Flash size: 4MB
    Compressed 140160 bytes to 68713...
    Wrote 140160 bytes (68713 compressed) at 0x00010000 in 1.5 seconds (effective 730.5 kbit/s)...
    Hash of data verified.
    
    Leaving...
    Hard resetting via RTS pin...
    MONITOR
    --- idf_monitor on /dev/ttyUSB0 115200 ---
    --- Quit: Ctrl+] | Menu: Ctrl+T | Help: Ctrl+T followed by Ctrl+H ---
    ets Jun  8 2016 00:22:57
    
    rst:0x1 (POWERON_RESET),boot:0x13 (SPI_FAST_FLASH_BOOT)
    ets Jun  8 2016 00:22:57
    
    rst:0x10 (RTCWDT_RTC_RESET),boot:0x13 (SPI_FAST_FLASH_BOOT)
    configsip: 0, SPIWP:0xee
    clk_drv:0x00,q_drv:0x00,d_drv:0x00,cs0_drv:0x00,hd_drv:0x00,wp_drv:0x00
    mode:DIO, clock div:2
    load:0x3fff0018,len:4
    load:0x3fff001c,len:7400
    load:0x40078000,len:0
    ho 12 tail 0 room 4
    load:0x40078000,len:17796
    entry 0x40078724
    I (31) boot: ESP-IDF v3.1-dev-1171-g358c822 2nd stage bootloader
    I (31) boot: compile time 16:28:39
    I (31) boot: Enabling RNG early entropy source...
    I (37) boot: SPI Speed      : 40MHz
    I (41) boot: SPI Mode       : DIO
    I (45) boot: SPI Flash Size : 4MB
    I (49) boot: Partition Table:
    I (52) boot: ## Label            Usage          Type ST Offset   Length
    I (60) boot:  0 nvs              WiFi data        01 02 00009000 00006000
    I (67) boot:  1 phy_init         RF data          01 01 0000f000 00001000
    I (75) boot:  2 factory          factory app      00 00 00010000 00100000
    I (82) boot: End of partition table
    E (86) esp_image: image at 0x10000 has invalid magic byte
    W (92) esp_image: image at 0x10000 has invalid SPI mode 255
    E (99) boot: Factory app partition is not bootable
    E (104) boot: No bootable app partitions in the partition table
    user code done
    
    
    前掲のドキュメント記述のように「flash read err, 1000」のエラー出力とリブートのループを予想したがここではこのように「boot: Factory app partition is not bootable」の表示で停止した。このあたりの事情はまだよくわからない
    動画 E
  7. ここでソースコードに小さく変更を加える。メッセージにビックリマークを書き加えただけ
    変更前
    ESP_LOGI(TAG, "Flash Encryption is enabled");

    変更後
    ESP_LOGI(TAG, "Flash Encryption is enabled!!!!");

  8. make を経て PC 上で app イメージを暗号化し esptool でフラッシュメモリへ書き込むと期待通り上の変更が反映された状態で起動
    $ make
    CC build/main/mySimpleApp.o
    AR build/main/libmain.a
    LD build/mySimpleApp.elf
    esptool.py v2.3.1
    To flash all build output, run 'make flash' or:
    python /home/t/esp/esp-idf/components/esptool_py/esptool/esptool.py --chip esp32 --port /dev/ttyUSB0 --baud 115200 --before default_reset --after hard_reset write_flash -z --flash_mode dio --flash_freq 40m --flash_size detect 0x1000 /home/t/Arduino/my/for_ESP-IDF/mySimpleApp/build/bootloader/bootloader.bin 0x10000 /home/t/Arduino/my/for_ESP-IDF/mySimpleApp/build/mySimpleApp.bin 0x8000 /home/t/Arduino/my/for_ESP-IDF/mySimpleApp/build/partitions_singleapp.bin
    
    $ espsecure.py encrypt_flash_data --keyfile ./mykey.bin --address 0x10000 -o build/mySimpleApp_encrypted.bin build/mySimpleApp.bin
    espsecure.py v2.3.1
    
    $ esptool.py --port /dev/ttyUSB0 --baud 921600 write_flash 0x10000 build/mySimpleApp_encrypted.bin; make monitor
    esptool.py v2.3.1
    Connecting....
    Detecting chip type... ESP32
    Chip is ESP32D0WDQ6 (revision 0)
    Features: WiFi, BT, Dual Core
    Uploading stub...
    Running stub...
    Stub running...
    Changing baud rate to 921600
    Changed.
    Configuring flash size...
    Auto-detected Flash size: 4MB
    Compressed 140176 bytes to 138083...
    Wrote 140176 bytes (138083 compressed) at 0x00010000 in 2.2 seconds (effective 509.0 kbit/s)...
    Hash of data verified.
    
    Leaving...
    Hard resetting via RTS pin...
    MONITOR
    --- idf_monitor on /dev/ttyUSB0 115200 ---
    --- Quit: Ctrl+] | Menu: Ctrl+T | Help: Ctrl+T followed by Ctrl+H ---
    ets Jun  8 2016 00:22:57
    
    rst:0x1 (POWERON_RESET),boot:0x13 (SPI_FAST_FLASH_BOOT)
    ets Jun  8 2016 00:22:57
    
    rst:0x10 (RTCWDT_RTC_RESET),boot:0x13 (SPI_FAST_FLASH_BOOT)
    configsip: 0, SPIWP:0xee
    clk_drv:0x00,q_drv:0x00,d_drv:0x00,cs0_drv:0x00,hd_drv:0x00,wp_drv:0x00
    mode:DIO, clock div:2
    load:0x3fff0018,len:4
    load:0x3fff001c,len:7400
    load:0x40078000,len:0
    ho 12 tail 0 room 4
    load:0x40078000,len:17796
    entry 0x40078724
    I (31) boot: ESP-IDF v3.1-dev-1171-g358c822 2nd stage bootloader
    I (31) boot: compile time 16:28:39
    I (31) boot: Enabling RNG early entropy source...
    I (37) boot: SPI Speed      : 40MHz
    I (41) boot: SPI Mode       : DIO
    I (45) boot: SPI Flash Size : 4MB
    I (49) boot: Partition Table:
    I (52) boot: ## Label            Usage          Type ST Offset   Length
    I (60) boot:  0 nvs              WiFi data        01 02 00009000 00006000
    I (67) boot:  1 phy_init         RF data          01 01 0000f000 00001000
    I (75) boot:  2 factory          factory app      00 00 00010000 00100000
    I (82) boot: End of partition table
    I (86) esp_image: segment 0: paddr=0x00010020 vaddr=0x3f400020 size=0x0588c ( 22668) map
    I (103) esp_image: segment 1: paddr=0x000158b4 vaddr=0x3ffb0000 size=0x02284 (  8836) load
    I (108) esp_image: segment 2: paddr=0x00017b40 vaddr=0x40080000 size=0x00400 (  1024) load
    0x40080000: _iram_start at /home/t/esp/esp-idf/components/freertos/xtensa_vectors.S:1685
    
    I (113) esp_image: segment 3: paddr=0x00017f48 vaddr=0x40080400 size=0x080c8 ( 32968) load
    I (136) esp_image: segment 4: paddr=0x00020018 vaddr=0x400d0018 size=0x11ccc ( 72908) map
    0x400d0018: _flash_cache_start at ??:?
    
    I (163) esp_image: segment 5: paddr=0x00031cec vaddr=0x400884c8 size=0x0066c (  1644) load
    0x400884c8: esp_rom_spiflash_program_page_internal at /home/t/esp/esp-idf/components/spi_flash/spi_flash_rom_patch.c:412
    
    I (164) esp_image: segment 6: paddr=0x00032360 vaddr=0x400c0000 size=0x00000 (     0) load
    I (175) boot: Loaded app from partition at offset 0x10000
    I (175) boot: Checking flash encryption...
    I (180) flash_encrypt: flash encryption is enabled (3 plaintext flashes left)
    I (188) boot: Disabling RNG early entropy source...
    I (194) cpu_start: Pro cpu up.
    I (197) cpu_start: Starting app cpu, entry point is 0x40080e4c
    0x40080e4c: call_start_cpu1 at /home/t/esp/esp-idf/components/esp32/cpu_start.c:225
    
    I (0) cpu_start: App cpu up.
    I (208) heap_init: Initializing. RAM available for dynamic allocation:
    I (215) heap_init: At 3FFAE6E0 len 00001920 (6 KiB): DRAM
    I (221) heap_init: At 3FFB32C0 len 0002CD40 (179 KiB): DRAM
    I (227) heap_init: At 3FFE0440 len 00003BC0 (14 KiB): D/IRAM
    I (233) heap_init: At 3FFE4350 len 0001BCB0 (111 KiB): D/IRAM
    I (240) heap_init: At 40088B34 len 000174CC (93 KiB): IRAM
    I (246) cpu_start: Pro cpu start user code
    I (264) cpu_start: Starting scheduler on PRO CPU.
    I (0) cpu_start: Starting scheduler on APP CPU.
    I (266) TAG: **** this is secret data ****
    I (266) TAG: Flash Encryption is enabled!!!!
    I (1266) TAG: Flash Encryption is enabled!!!!
    I (2266) TAG: Flash Encryption is enabled!!!!
    I (3266) TAG: Flash Encryption is enabled!!!!
    I (4266) TAG: Flash Encryption is enabled!!!!
    I (5266) TAG: Flash Encryption is enabled!!!!
    I (6266) TAG: Flash Encryption is enabled!!!!
    I (7266) TAG: Flash Encryption is enabled!!!!
    I (8266) TAG: Flash Encryption is enabled!!!!
    I (9266) TAG: Flash Encryption is enabled!!!!
    
    
    動画 F
これで FLASH_CRYPT_CNT を気にすることなく Flash Encryption を利用できるようになりました。

Arduino core for ESP32 環境ではどうする?等の実験と結果

ここまでは公式ドキュメントの記述を解釈しながら事前生成暗号化キーを使い ESP-IDF ベースで Flash Encryption の操作を行ってきました。より利用者の多い Arduino core for ESP32 環境での事情が気になります。現時点ではこの点に関する的確な情報が見当たらないため手元で調査と実験を行いました。

実験 1: Flash Encryption 有効状態でのバックアップ・レストアは可能?

前項までの確認の結果、現在手元には FLASH_CRYPT_CNT efuse の値が 0x01 の ESP32 ボードがある。ここにはアプリ「mySimpleApp」の本体のイメージをはじめ暗号化された一式が格納されている。

まず素朴に、この状態でフラッシュメモリ全領域のバックアップとレストアが可能であるか否かに関心を持った。 さっそく esptool を使い下記要領でバックアップとレストアを試したところ、Flash Encryption 不使用時と同様に問題なく処理できることを確認した。物理的な読み書きの際に間で細工をしたりはしないらしい。まずはひと安心。

esptool.py --port /dev/ttyUSB0 --baud 921600 read_flash 0 0x400000 flash4MB.bin

esptool.py --port /dev/ttyUSB0 --baud 921600 write_flash 0 flash4MB.bin

実験 2: パーティションイメージを復号してみる

次に、esptool で読み出した所定の暗号化ずみパーティションを espsecure ユーティリティ の decrypt_flash_data コマンドで正しく復号可能であることを確認した。こういった整合性は一連の文脈の中で適切に保たれている。 以下は暗号化されたパーティションテーブルをホスト PC 上に読み出して復号する例。decrypt_flash_data コマンドにおいても --address 指定が必須であることに注意が必要。

esptool.py --port /dev/ttyUSB0 --baud 921600 read_flash 0x8000 0xc00 partition_table_Encrypted.bin

espsecure.py decrypt_flash_data --keyfile ./mykey.bin --address 0x8000 -o partition_table_plain.bin partiiion_table_Encrypted.bin

なお、サイズ不定のブートローダーについては、プロジェクトのビルド時に build/bootloader/ 下に生成された暗号化前の bootloader.bin のファイルサイズ(ここでは 25,296 バイト)を指定して暗号化イメージを採取。上記と同じ要領で復号可能だった。

esptool.py --port /dev/ttyUSB0 --baud 921600 read_flash 0x1000 25296 bootloader_Encrypted.bin

espsecure.py decrypt_flash_data --keyfile ./mykey.bin --address 0x1000 -o bootloader_plain.bin bootloader_Encrypted.bin

実験 3: 全パーティションのイメージを採取・復号・再暗号化して書き戻すと?

上のようにパーティションテーブルとブートローダのイメージの取得と復号の確認を終えたところで、他のパーティションイメージもすべて採取し、適宜復号と再暗号化を行った上で ESP32 へ書き戻してやれば普通にプログラムが動作するのではないかと考え実際に手を動かして試してみた。

まずパーティション定義を参照し各パーティションイメージを個別に採取する。

# Name,   Type, SubType, Offset,  Size, Flags
nvs,      data, nvs,     0x9000,  0x6000,
phy_init, data, phy,     0xf000,  0x1000,
factory,  app,  factory, 0x10000, 1M,

前掲のように nvs は暗号化されておらず、読み出し不可の phy は中身がすべて 0xFF だった。後者はいずれにせよ mySimpleApp では使用していないためひとまずこのままにしておく。Type = app の factory パーティションについてのみ復号を行った。実行したコマンドを以下に控える。

esptool.py --port /dev/ttyUSB0 --baud 921600 read_flash 0x9000 0x6000 nvs.bin
esptool.py --port /dev/ttyUSB0 --baud 921600 read_flash 0xf000 0x1000 phy_init.bin
esptool.py --port /dev/ttyUSB0 --baud 921600 read_flash 0x10000 0x100000 factory_Encrypted.bin

espsecure.py decrypt_flash_data --keyfile ./mykey.bin --address 0x10000 -o factory_plain.bin factory_Encrypted.bin

次に、復号したブートローダ, パーティションテーブル, factory パーティションをあらためて暗号化する。

espsecure.py encrypt_flash_data --keyfile ./mykey.bin --address 0x1000 -o bootloader_reEncrypted.bin bootloader_plain.bin
espsecure.py encrypt_flash_data --keyfile ./mykey.bin --address 0x8000 -o partition_table_reEncrypted.bin partition_table_plain.bin
espsecure.py encrypt_flash_data --keyfile ./mykey.bin --address 0x10000 -o factory_reEncrypted.bin factory_plain.bin

準備を終えた一式を下記コマンドでフラッシュメモリへ書き戻した。

esptool.py --port /dev/ttyUSB0 --baud 921600 write_flash \
    0x1000 bootloader_reEncrypted.bin \
    0x8000 partition_table_reEncrypted.bin \
    0x9000 nvs.bin \
    0xf000 phy_init.bin \
    0x10000 factory_reEncrypted.bin

この結果、アプリケーション mySimpleApp は問題なく動作した。当初複雑に感じられた Flash Encryption は案外素直な構造らしい。

実験 4: Arduino core for ESP32 環境で開発したアプリ一式を移行してみる

ここまでの結果に続けて、ESP-IDF ではなく Arduino core for ESP32 環境でビルドしたアプリケーションイメージ一式について Flash Encryption 環境での取り回しを試みた。

実験用の題材として Arduino IDE 環境で直近に作成した Google Home に発話させるエージェントプログラムを使った。アプリ内で SPIFFS を利用していることも実験を行う上で何となく好都合。パーティションテーブルは以下の内容。

# Name,   Type, SubType, Offset,  Size, Flags
nvs,      data, nvs,     0x9000,  0x5000,
otadata,  data, ota,     0xe000,  0x2000,
app0,     app,  ota_0,   0x10000, 0x140000,
app1,     app,  ota_1,   0x150000,0x140000,
eeprom,   data, 0x99,    0x290000,0x1000,
spiffs,   data, spiffs,  0x291000,0x16F000,

手元では同アプリを Flash Encryption を適用していない素の ESP32 ボードで運用中。

まず、この非 Flash Encryption なボード A をホスト PC へ接続し、フラッシュメモリからパーティションイメージ一式を採取する。

esptool.py --port /dev/ttyUSB0 --baud 921600 read_flash 0x9000 0x5000 nvs.bin
esptool.py --port /dev/ttyUSB0 --baud 921600 read_flash 0xe000 0x2000 otadata.bin
esptool.py --port /dev/ttyUSB0 --baud 921600 read_flash 0x10000 0x140000 app0.bin
esptool.py --port /dev/ttyUSB0 --baud 921600 read_flash 0x150000 0x140000 app1.bin
esptool.py --port /dev/ttyUSB0 --baud 921600 read_flash 0x290000 0x1000 eeprom.bin
esptool.py --port /dev/ttyUSB0 --baud 921600 read_flash 0x291000 0x16F000 spiffs.bin

esptool.py --port /dev/ttyUSB0 --baud 921600 read_flash 0x8000 0xC00 partition_table.bin

ブートローダはサイズ不定だが、Arduino core for ESP32 の場合、[arduino]/hardware/espressif/esp32/tools/sdk/bin/ ディレクトリ下に SPI モード + Flash Frequency 別に用意されているためこれを利用できる。

$ pwd
/home/t/app/arduino-1.8.5/hardware/espressif/esp32/tools/sdk/bin
$ ls -l
合計 128
-rw-rw-r-- 1 t t 15088  7月 30 21:42 bootloader_dio_40m.bin
-rw-rw-r-- 1 t t 13696  7月 30 21:42 bootloader_dio_80m.bin
-rw-rw-r-- 1 t t 13696  7月 30 21:42 bootloader_dout_40m.bin
-rw-rw-r-- 1 t t 13696  7月 30 21:42 bootloader_dout_80m.bin
-rw-rw-r-- 1 t t 15088  7月 30 21:42 bootloader_qio_40m.bin
-rw-rw-r-- 1 t t 15072  7月 30 21:42 bootloader_qio_80m.bin
-rw-rw-r-- 1 t t 15088  7月 30 21:42 bootloader_qout_40m.bin
-rw-rw-r-- 1 t t 15072  7月 30 21:42 bootloader_qout_80m.bin

本アプリは "QIO" + "80MHz" の指定でビルドしているため「bootloader_qio_80m.bin」をコピーして使う。

  

暗号化の対象であるブートローダー, パーティションテーブル および app0, app1 パーティションのイメージを Flash Encryption 適用ずみの ESP32 ボード B のキーを使って暗号化する。

espsecure.py encrypt_flash_data --keyfile ./mykey.bin --address 0x1000 -o bootloader_Encrypted.bin bootloader_qio_80m.bin
espsecure.py encrypt_flash_data --keyfile ./mykey.bin --address 0x8000 -o partition_table_Encrypted.bin partition_table.bin
espsecure.py encrypt_flash_data --keyfile ./mykey.bin --address 0x10000 -o app0_Encrypted.bin app0.bin
espsecure.py encrypt_flash_data --keyfile ./mykey.bin --address 0x150000 -o app1_Encrypted.bin app1.bin

ここで Flash Encryption 適用ずみの ESP32 ボード B をホスト PC につなぎ替えてから、以下の要領で各パーティションイメージを書き込む。

esptool.py --port /dev/ttyUSB0 --baud 921600 write_flash \
    0x1000 bootloader_Encrypted.bin \
    0x8000 partition_table_Encrypted.bin \
    0x9000 nvs.bin \
    0xe000 otadata.bin \
    0x10000 app0_Encrypted.bin \
    0x150000 app1_Encrypted.bin \
    0x290000 eeprom.bin \
    0x291000 spiffs.bin

以上の手順により、Arduino core for ESP32 環境でビルドを行い素の ESP32 ボード A 上で稼働中の当該アプリが、Flash Encryption 適用ずみのボード B 上で正常に動作した

Flash Encryption を道具として普段使いするために

上の結果を受けて、Flash Encryption を道具として日常的に利用する上でのポイントを想定した。

  • あらかじめ「初回の暗号化」を経て FLASH_CRYPT_CNT = 0x01 状態となったボードを資材として用意しておく
  • 普段は素のボートを使って Arduino IDE 環境なり ESP-IDF 環境なりで普通にプログラム開発を行う
  • 自分の手を離れた場所にボードを設置する場合は上の要領で素のボードから Flash Encryption 有効化ずみボードへイメージを移行して利用する
       ESP32 Board A                         ESP32 Board B
    +------------------+  copy & encrypt  +------------------+
    | Flash Encryption | partition images | Flash Encryption |
    |     DISABLED     | ===============> |     ENABLED      | 
    +------------------+                  +------------------+
    

理屈がわかってしまえば移行方法そのものは単純だが、手順が面倒なので適切に一括操作を行うために Python スクリプトを用意した。Linux, MacOSX, Windows の各環境に対応。なお、前項の実験ではブートローダのイメージを SDK ディレクトリからコピーして利用したが、ブートローダコードは固定長ではないもののサイズの上限は 0x7000 バイトであり、素のボードのフラッシュメモリオフセット 0x1000 から 0x7000 バイト分を読み出して処理したところ問題なく動作したためこの方法を採った。これなら元アプリの開発環境が何であるかにかかわらず対応できる。

  1. Flash Encryption の無効なボード A のフラッシュメモリからブートローダイメージをホスト PC へ採取
  2. 同じくパーティションテーブルイメージを採取
  3. 上のパーティションテーブルの内容に基づき各パーティションイメージを採取
  4. 暗号化の必要なイメージを Flash Encryption の有効なボード B 用のキーで暗号化する
  5. イメージ一式をボード B のフラッシュメモリへ書き込む

以下の動画はこのスクリプトを使って前掲の「Google Home に発話させるエージェントプログラム」を稼働させている素のボード A のフラッシュメモリパーティションイメージ一式を暗号化有効なボード B へ移行した様子。

前半: 素のボード A のフラッシュメモリからの各パーティションイメージ採取と暗号化完了まで

後半: 上のイメージを Flash Encryption の有効なボード B に書き込んで当該アプリの稼働を確認

残っている疑問

今回、所定のボードで Flash Encryption を有効化するまでの過程ではドキュメントの内容どおりに ESP-IDF 環境で make menuconfig での暗号化オプション有効化を経て make && make flash を実行する手順を踏んだが、ボード側のお膳立てとしては FLASH_CRYPT_CNT efuse の値を 0x01 にするだけで事足りる可能性も想像される。

もし仮にそうであれば、素の状態のボードに対し事前生成暗号化キーを書き込んだ上で 前掲のように「espefuse.py --port [PORT] burn_efuse FLASH_CRYPT_CNT」コマンドを一度発行するだけで準備 OK かもしれない。手元ではまだ試せていないが関心はある。この件もおって確認したい。


以上、長いメモでした。いろいろ興味深い機構です。


(tanabe)
klab_gijutsu2 at 19:32│Comments(0)IoT 

この記事にコメントする

名前:
URL:
  情報を記憶: 評価: 顔   
 
 
 
Blog内検索
Archives
このブログについて
DSASとは、KLab が構築し運用しているコンテンツサービス用のLinuxベースのインフラです。現在5ヶ所のデータセンタにて構築し、運用していますが、我々はDSASをより使いやすく、より安全に、そしてより省力で運用できることを目指して、日々改良に勤しんでいます。
このブログでは、そんな DSAS で使っている技術の紹介や、実験してみた結果の報告、トラブルに巻き込まれた時の経験談など、広く深く、色々な話題を織りまぜて紹介していきたいと思います。
最新コメント