セキュリティ

この章では、Armadillo を利用したシステム上に存在する情報資産を守るための方法について説明する。

11.1. セキュリティの費用対効果

製品開発に費やすコストには様々なものがありますが、セキュリティのような機能性につながらないコストは軽視されがちです。しかし、一旦セキュリティ問題が発生してしまうと、業務停止など直接的な影響だけでなく、社会的な信用の低下、損害賠償など様々な被害につながってしまう可能性があります。守るべき情報資産が明確に存在するのであれば、セキュリティは重要視されるべきです。しかし、コスト度外視でありったけの対策を盛り込むのではコストが膨らみます。適切かつ適度なセキュリティ対策を講じることが大切です。製品開発の早い段階で守るべき情報資産を認識し、それら情報資産に対する脅威とリスクを分析して、リスクを評価する作業を行うことをお勧めします。そのうえで、費用対効果に見合ったセキュリティ技術を選択するとよいでしょう。

11.2. Armadillo Base OS のセキュリティ

幅広い利用者のユースケースに沿った製品セキュリティ実現のため、Armadillo Base OS に様々なセキュリティ技術を組み込むことが可能です。

  • ソフトウェア実行環境の保護

    • TEE (Trusted Execution Environment)
  • セキュアブート、チェーンオブトラスト

    • HAB (High Assurance Boot)
    • SE050 secure storage
  • ストレージの秘匿化

    • パーティション/ファイシステムの暗号化
  • キーストレージ

    • SE050
  • メモリの完全性チェック

    • RTIC (Run-Time Integrity Checker)
  • 暗号処理のアクセラレータ

    • CAAM (Cryptographic Acceleration and Assurance Module)

次に各セキュリティ技術のカバーする範囲を示します。セキュリティ技術を採用する際には、採用する技術によってどこまでの範囲が守られるのかを把握することが大切です。設定次第でそれぞれの技術は高いセキュリティ性能を実現するものになり得ますが、単独で利用するだけではその効果は限定的で回避可能なものになってしまいます。そのため、いくつかの技術を適切に組み合わせて利用することで、より広範囲をカバーする回避困難なセキュリティを実現できます。そして、カバーする範囲が重なりあうことで突破困難なセキュリティを実現します。

images/common-images/security/security_tech_vs_req.png

図11.1 セキュリティ技術のカバーする範囲


*1 i.MX 8M Plus には RTIC (Run-time Integrity Checker) が含まれます。この機能を利用することでブート時、ランタイム時の周辺メモリの完全性をチェックすることが可能です

11.2.1. モデルユースケース

Armadillo は様々な製品、様々な環境で利用されることが想定されます。それぞれのケースによって考慮すべき脅威が存在します。それらを正しく把握してセキュリティ技術を選択、組み合わせることが 大切です。 組み合わせの例としていくつかのモデルユースケースを示します。

  • デフォルト

  • クラウドサービスのデバイス認証が可能なレベル

    • 方針

      • ネットワークを突破されても情報資産は守る
      • 部分的な情報資産(暗号処理や鍵)の保護
      • デバイスへの直接攻撃は想定しない
    • 技術

      • +OP-TEE
      • +鍵のストレージは SE050
      • +ファイルシステムの暗号化
  • 決済処理が可能なレベル

    • 方針

      • ネットワークを突破されても情報資産は守る
      • スクリプトキディによるデバイスへの直接攻撃は守るが、ラボレベルの攻撃は想定しない
    • 技術

      • +セキュアブート
      • +チェーンオブトラストを使った鍵のストレージ
      • +パーティションの暗号化
      • +メモリの完全性チェック

11.3. データの保護

この章では情報資産を保護するための方法を説明します。説明するセキュリティ技術は以下のとおり。

  • SE050 セキュアストレージ

11.3.1. データの保護

情報資産はデータとして永続的にストレージや一次的にRAM上に保存されます。セキュリティ対策が講じられていないデバイスでは情報資産が露出していることになり、改竄や盗聴につながる可能性があります。 鍵や証明書をストレージに保存する、暗号処理のために一次的に鍵や証明書をRAM上に展開するケースでデータ保護を採用することが考えられます。

11.3.2. SE050 とは

NXP Semiconductors の EdgeLock SE050 は IoT アプリケーション向けのセキュアエレメントです。様々なアルゴリズムに対応した暗号エンジン、セキュアストレージを搭載します。GlobalPlatform が規定する Secure Channel Protocol 03 に準拠し、バスレベル暗号化 (AES)、ホストとカードの相互認証 (CMAC ベース) を行います。

[ティップ]

SE050 の詳細については以下の NXP Semiconductors のページから検索して、ご確認ください。

11.3.3. ビルド環境を構築する

  1. Plug and trust ミドルウェアをダウンロードします

    以下のサイトからダウンロードします。ここではユーザーが NXP Semiconductors のサイトで最新版を検索してダウンロードすることを想定しています。

    コンテナ上から参照できる位置に配置しておいてください。

    [注記]
    • ダウンロードには NXP Semiconductors サイトへのユーザー登録が必要になります
    • 本マニュアル作成時点では 04.00.00 を利用しました
  2. コンテナを立ち上げる

    NXP Semiconductors のビルド環境はターゲットボード上のコンテナになります。ここでは alpine を利用します。

    [armadillo ~]# podman run -it --name=dev_se050 --device=/dev/i2c-2 \
            -v "$(pwd)":/mnt docker.io/alpine /bin/sh
  3. ビルド環境を構築する

    plug and trust をビルドするために必要なパッケージをインストールします。

    [container ~]# apk add musl-dev gcc make g++ file \
            linux-headers openssl-dev cmake python3

11.3.3.1. SE050 を有効にする

Armadillo は消費電力の削減のため SE050 を Deep Power-down モードに設定してパワーゲーティングしている。Deep Power-down モードを解除して SE050 を利用するためには、i.MX 8M Plus に接続されている SE050 の ENA ピンをアサートする必要があります。

表11.1 SE050 ena pin

SE050 PINi.MX8MP portinitial port status

ENA

GPIO1_IO12

GPIO input


sysfs を利用して GPIO1_IO12 を出力ポートに変更して high にする。

[armadillo ~]# echo 12 > /sys/class/gpio/export
[armadillo ~]# echo "out" > /sys/class/gpio/gpio12/direction
[armadillo ~]# echo 1 > /sys/class/gpio/gpio12/value

11.3.3.2. Plug and trust ミドルウェアをビルドする

  1. Plug and trust ミドルウェアを展開します

    [container /mnt]# unzip SE-PLUG-TRUST-MW.zip
  2. ビルドスクリプトを変更する

    コンテナ上でビルドスクリプトを実行すると正しくプラットフォームを認識できないために、異なるプラットフォーム向けのバイナリを作ってしまいます。imx_native_se050_t1oi2c_openssl_el2go 向けを強制的に True になるように変更する必要があります。以下が変更箇所です。i.MX6UL/ULL, i.MX8M Mini 向けの条件文が真になるように変更します。

    [container /mnt]# cd simw-top
    [container /mnt/simw-top]# vi scripts/create_cmake_projects.py
    : (省略)
        # i.MX6UL/ULL and i.MX8M Mini EVK
        if True: #gc.imx_native_compilation() and gc.is_with_el2go(): 1
            e = gc.generate_native("imx_native_se050_t1oi2c_openssl_el2go", {
                "PTMW_Applet": "SE05X_C",
                "PTMW_SE05X_Auth": "None",
    : (省略)

    1

    if 文を強制的に True にする。元の行はコメントアウトした

  3. ビルドする

    プロジェクトをつくるスクリプトを実行する。

    [container /mnt]# cd simw-top
    [container /mnt/simw-top]# python3 scripts/create_cmake_projects.py
    ### Native compilation on iMX Linux for SE05X using T=1 Over I2C for EdgeLock 2GO
    #cmake -DPTMW_Applet=SE05X_C -DPTMW_SE05X_Auth=None (省略)...
    -- The C compiler identification is GNU 10.3.1
    -- The CXX compiler identification is GNU 10.3.1
    : (省略)
    -- Generating done
     Build files have been written to: /simw-top_build/(省略)...

    simw-top_build に移動してビルドする。

    [container /mnt]# cd simw-top_build/imx_native_se050_t1oi2c_openssl_el2go
    [container /mnt/simw-top_build/imx_native_(省略)...]# cmake --build .
    : (省略)
    [  1%] Linking C shared library libmwlog.so
    [  1%] Built target mwlog
    [100%] Linking C executable ../../../bin/nxp_iot_agent_demo
    [100%] Built target nxp_iot_agent_demo
  4. ビルド結果の確認

    以下の2つのディレクトリができる。

    [container /mnt/simw-top_build]# ls
    imx_native_se050_t1oi2c_openssl_el2go  simw-top-eclipse_jrcpv1

    以下のデモアプリケーションがビルドされる。

    [container /mnt/simw-top_build/imx_native_se050_t1oi2c_openssl_el2go]# ls bin
    MQTTVersion                      se05x_ConcurrentEcc
    accessManager                    se05x_ConcurrentSymm
    apdu_player_demo                 se05x_Delete_and_test_provision
    claimcode_inject                 se05x_GetInfo
    ex_attest_ecc                    se05x_I2cMaster
    ex_attest_mont                   se05x_I2cMasterWithAttestation
    ex_ecc                           se05x_InjectCertificate
    ex_ecdaa                         se05x_InvokeGarbageCollection
    ex_ecdh                          se05x_MandatePlatformSCP
    ex_hkdf                          se05x_Minimal
    ex_hmac                          se05x_MultiThread
    ex_md                            se05x_MultipleDigestCryptoObj
    ex_policy                        se05x_PCR
    ex_rsa                           se05x_ReadWithAttestation
    ex_se05x_WiFiKDF_derive          se05x_TimeStamp
    ex_se05x_WiFiKDF_inject          se05x_TransportLock
    ex_symmetric                     se05x_TransportUnLock
    generate_certificate             se05x_ex_export_se_to_host
    generate_certificate_key         se05x_ex_import_host_to_se
    jrcpv1_server                    seTool
    nxp_iot_agent_demo               test_Crypto
    remote_provisioning_client
  5. デモアプリケーションで動作を確認する

    se05x_GetInfo を実行する。SE050 にアクセスできると以下のようなログが出力される。

    [container /mnt/simw-top_build/imx_native_(省略)/bin]# ./se05x_GetInfo \
     /dev/i2c-2:0x48
    App   :INFO :PlugAndTrust_v03.03.00_20210528
    App   :INFO :Running ./se05x_GetInfo
    App   :INFO :Using PortName='/dev/i2c-2:0x48' (CLI)
    : (省略)
    App   :WARN :#####################################################
    App   :INFO :Applet Major = 3
    App   :INFO :Applet Minor = 1
    App   :INFO :Applet patch = 1
    App   :INFO :AppletConfig = 6FFF
    App   :INFO :With    ECDAA
    App   :INFO :With    ECDSA_ECDH_ECDHE
    App   :INFO :With    EDDSA
    App   :INFO :With    DH_MONT
    App   :INFO :With    HMAC
    App   :INFO :With    RSA_PLAIN
    App   :INFO :With    RSA_CRT
    App   :INFO :With    AES
    App   :INFO :With    DES
    App   :INFO :With    PBKDF
    App   :INFO :With    TLS
    App   :INFO :With    MIFARE
    App   :INFO :With    I2CM
    : (省略)

11.3.4. アプリケーションの作成

plug and trust ミドルウェアにはでもアプリケーションが含まれます。そちらを参考にしてアプリケーションを作成することができます。

詳しくはミドルウェア内の doc ディレクトリ以下を参考にしてください。

11.4. ソフトウェア実行環境の保護

この章ではソフトウェア実行環境を守るセキュリティ技術を適用する方法を説明します。 説明するセキュリティ技術は以下のとおりです。

  • OP-TEE

    • CAAM を利用した OP-TEE
    • SE050 を利用した OP-TEE

11.4.1. OP-TEE

11.4.1.1. Arm TrustZone と TEE の活用

Linux を利用するシステムでは、自社開発したソフトウェアだけでなく複数の OSS が導入されるケースがあります。このような状況下では、自社開発したタスクだけでなく、同時にOSSのタスクが実行されることになり、システムのどこかに悪意のあるコードが含まれるのか、その可能性を排除することは困難です。仮にすべての OSS が信頼できるものであったとしても、不具合がないことを保証することはほぼ不可能であり、結局のところどのソフトウェアが脆弱性のきっかけになるかは分からないのです。

そういった背景から Arm は TrustZone 技術を導入しました。リソースアクセス制限によって敵対的なソフトウェアからソフトウェア実行環境を隔離することができます。TrustZone の導入によって、自社開発以外のソフトウェアが多数動作する状況においても、通常はセキュアワールドにその影響が及びません。TrustZone を利用したものとして、GlobalPlatform の TEE (Trusted Execution Environment) を実現するソフトウェアがいくつか存在します。TEE を活用すると secure world において信頼できるソフトウェアだけを実行させることが可能です。

では、どういったケースで TEE を採用するべきでしょうか。一概には言えませんが、情報資産とその資産を処理するライブラリなどをまとめて保護するケースで利用することが考えられます。具体的には、電子決済処理、証明書の処理、有料コンテンツの処理、個人情報の処理などで利用するケースが考えられます。

11.4.1.2. OP-TEE とは

商用、OSS の TEE などいくつかの TEE 実装が存在します。Armadillo Base OS では OP-TEE を採用します。OSS である OP-TEE は Arm コア向け TEE 実装の1つです。Arm TrustZone テクノロジーによって情報資産とその処理をセキュアワールドに隔離して攻撃から保護します。OP-TEE は GlobalPlatform によって定義される TEE Client API や TEE Core API といった GlobalPlatformAPI に準拠したライブラリを提供しています。これらのAPI を利用することでユーザーはカスタムアプリケーションを開発することが可能です。imx-optee-xxx は NXP による i.MX ボートのサポート対応が含まれる OP-TEE の派生プロジェクトです。

[注記]

OP-TEE の詳しい情報は公式のドキュメントを参照してください。

11.4.2. OP-TEE の構成

OP-TEE を構成する主要なリポジトリは以下のとおりです。

  • imx-optee-os

    • セキュアワールドで動作する TEE。optee-os の派生
  • imx-optee-client

    • TEE を呼び出すためのノンセキュア、セキュアワールド向けの API ライブラリ。optee-client の派生
  • imx-optee-test

    • OP-TEE の基本動作のテスト、パフォーマンス測定を行う。optee-test の派生。NXP 独自のテストが追加される
  • optee_examples

    • サンプルアプリケーション

このうち、imx- というプレフィックスが付加されるリポジトリは、アップストリームのリポジトリに対して NXP による i.MX シリーズ向けの対応が入ったリポジトリになります。OP-TEE を利用するためには imx-optee-os をブートローダーに配置するだけでなく、Linux 上で動作するコンパニオン環境 (imx-optee-client) が必要です。また、TEE を利用するために CA (Client Application), TA (Trusted Application) を、imx-optee-os と imx-optee-client を用いてビルドします。必要に応じてテスト環境 (imx-optee-test) も追加してください。

[注記]

詳しい OP-TEE のシステム構成については 「ソフトウェア全体像」 を参照してください。

[注記]

OP-TEE git に関する詳しい情報は公式のドキュメントを参照してください。

11.4.2.1. Armadillo Base OS への組み込み

前節で説明した OP-TEE の主要なリポジトリを実際に Armdillo BaseOS に適用する場合、Armadillo Base OS とどのように関係するのか全体像を説明します。

以下が Armadillo Base OS との関係を表した全体像。

images/common-images/security/charge_of_preparation.png

図11.2 Armadillo Base OS と OP-TEE の担当範囲


  • 青色の部分は Armadillo Base OS によってカバーされる範囲
  • 赤色の部分は OP-TEE を組み込むために用意しなければならない部分
  • 紫色の部分は デフォルトで Armadillo Base OS に組み込まれているが更新が必要な部分

imx-optee-os を組み込む作業は煩雑です。ユーザーの手間を省くために Armadillo Base OS には常に imx-optee-os バイナリがブートローダーに組み込むように実装されています。工場出荷状態イメージや製品アップデート向け Armadillo Base OS イメージにも含まれます。しかし、セキュリティ上の懸念から OP-TEE が利用できる状態で組み込まれていません。詳しくは 「鍵の更新」 で説明します。

11.4.3. OP-TEE を利用する前に

OP-TEE を組み込むための準備を行います。

11.4.3.1. 鍵の更新

imx-optee-os リポジトリには TA を署名するためのデフォルトの秘密鍵が配置されています。その秘密鍵はあくまでテストや試行のためのものです。そのまま同じ秘密鍵を使い続けると、攻撃者がデフォルトの秘密鍵で署名した TA が動作する環境になってしまいます。各ユーザーが新しい鍵を用意する必要があります。

  1. ソースコードを取得する 「Armadilloのソフトウェアをビルドする」 の章を参考にしてビルド環境の構築と、ブートローダーのソースコードの取得を行ってください。
  2. RSA 鍵ペアを生成します。

    次のコマンドを実行します。RSA/ECC を選択できます。

    [PC ~]$ cd imx-boot/imx-optee-os/keys
    [PC ~/imx-boot/imx-optee-os/keys]$ openssl genrsa -out rsa4096.pem 4096
    [PC ~/imx-boot/imx-optee-os/keys]$ openssl rsa -in rsa4096.pem -pubout -out rsa4096_pub.pem
[重要項目]

生成した秘密鍵 (上記の rsa4096.pem) は TA を更新していくために、今後も利用するものです。壊れにくい、セキュアなストレージにコピーしておくことをお勧めします。

[重要項目]

鍵の更新は計画性を持って行ってください。たとえば開発時のみ利用する鍵、運用時に利用する鍵を使い分ける。また、鍵は定期的に更新が必要です。以下を参考にしてください。

[注記]

鍵の更新に関する詳しい情報は公式のドキュメントを参照してください。

11.4.4. CAAM を活用した TEE を構築する

i.MX 8M Plus には CAAM (Cryptographic Acceleration and Assurance Module) と呼ばれる高機能暗号アクセラレータが搭載されています。CAAM は SoC 内部にあるためセキュアかつ高速に暗号処理を行うことが可能です。

11.4.4.1. ビルドの流れ

OP-TEE が含まれるブートローダーをビルドしていきます。Armadillo Base OS を利用した OP-TEE のビルドの流れは以下のとおりです。

  1. ブートローダーをビルドする
  2. imx-optee-client をビルドする
  3. TA, CA をビルドする
  4. ビルド結果を集める

本マニュアルで説明するビルド環境でのディレクトリ構成の概略は以下のとおりです。

optee_examples はユーザーアプリケーションを配置する場所です。本マニュアルでは Linaro が提供するアプリケーションのサンプルプログラムである optee_examples としました。本来は各ユーザーのアプリケーションを配置する場所になります。

├── imx-boot
│   ├── imx-atf
│   ├── imx-mkimage
│   ├── imx-optee-os
│   └── uboot-imx
├── imx-optee-client
├── imx-optee-test
├── optee_examples
└── out

11.4.4.2. ビルド環境を構築する

「Armadilloのソフトウェアをビルドする」 の章を参考にしてビルド環境の構築からブートローダーのビルドまでを事前に行ってください。

imx-optee-test をビルドするために必要になります。

[PC ~]$ sudo apt install g++-aarch64-linux-gnu

11.4.4.3. ブートローダーを再ビルドする

新しい鍵を取り込むためにブートローダーを再ビルドする必要があります。

  1. uboot-imx のバージョンを変更する

    この後の作業で swupdate を利用してアップデートを行いますが、ターゲットボード上の uboot-imx と同じバージョンではアップデートが実行されないので、imx-boot を更新するたびに uboot-imx のバージョンも更新する必要があります。

    uboot-imx ディレクトリに localvesion ファイルを作ります。

    [PC ~]$ cd imx-boot/uboot-imx
    [PC ~/imx-boot/uboot-imx]$ echo "-localv1.0.0" > localversion
    [ティップ]

    localversion の仕様はユーザーによって定義されます。

  2. ブートローダーをビルドする

    次のコマンドを実行します。 デフォルトの設定ではデフォルトの鍵を利用してしまうので make 引数で鍵のパスを渡す必要があります。「鍵の更新」 で生成した鍵のパスを変数 TA_SIGN_KEY で渡します。ここでは鍵名を rsa4096 としましたが、各ユーザーの環境に合わせた鍵名と読み替えてください。

    [PC ~]$ make CFG_CC_OPT_LEVEL=2 \
            TA_SIGN_KEY="${PWD}/imx-boot/imx-optee-os/keys/rsa4096.pem" \
            -C imx-boot imx-boot_armadillo_x2
    [ティップ]

    引数 TA_SIGN_KEY で秘密鍵をわたすことで、ビルド時に TA の署名に利用されます。また、実行時の署名確認のためには公開鍵を取り出して実行バイナリに取り込みます。

    ビルド結果をコピーしておきます。

    [PC ~]$ install -D -t ${PWD}/out/lib/optee_armtz \
            -m644 ${PWD}/imx-boot/imx-optee-os/out/export-ta_arm64/ta/*

11.4.4.4. imx-optee-client をビルドする

imx-optee-client は TA, CA が利用するライブラリ。アプリケーションをビルドする前にビルド必要がある。

[注記]

imx-optee-client は imx-optee-os のビルド結果を参照しているので、事前にブートローダーをビルドする必要があります。

  1. imx-optee-client をクローンする

    imx-boot のディレクトリと並列に配置されるように imx-optee-client をクローンして、必要に応じて適切なブランチなどをチェックアウトしてください。

    [PC ~]$ git clone https://source.codeaurora.org/external/imx/imx-optee-client.git \
            -b lf-5.10.y_2.0.0
  2. imx-optee-client をビルドする

    基本的な情報を参照するために TA_DEV_KIT_DIR を渡します。フォルトのターゲットでインストールも合わせて行うので DESTDIR を渡します。アプリケーションをビルドする際に API ライブラリとして利用されます。

    [PC ~]$ make -C imx-optee-client \
            DESTDIR="${PWD}/out" \
            CROSS_COMPILE="aarch64-linux-gnu-" \
            TA_DEV_KIT_DIR="${PWD}/imx-boot/imx-optee-os/out/export-ta_arm64"

11.4.4.5. アプリケーションをビルドする

本来はユーザーが独自に作ったアプリケーションをビルドしますが、ここでは参考までにサンプルアプリケーション optee_examples をビルドします。アプリケーション開発の参考にしてください。

[注記]
  • アプリケーションをビルドするためには imx-optee-os, imx-optee-client のビルド結果を参照しているので、一度は imx-boot, imx-optee-client をビルドする必要があります
  • optee_examples にはインストールする make ターゲットがないので、ビルド後に手動で集める作業が必要があります
  1. optee_examples をクローンする

    imx-boot や imx-optee-client ディレクトリと並列に配置されるように optee_examples をクローンして、必要に応じて適切なブランチなどをチェックアウトしてください。

    [PC ~]$ git clone https://github.com/linaro-swg/optee_examples.git \
            -b 3.13.0
  2. optee_examples をビルドする

    基本的な情報を参照するために TA_DEV_KIT_DIR、TA を署名するために TA_SIGN_KEY、ライブラリ参照のために TEEC_EXPORT を渡します。インストールターゲットがないので後ほど手動でビルド結果を収集する必要があります。

    [PC ~]$ make -C optee_examples \
            TA_CROSS_COMPILE="aarch64-linux-gnu-" \
            HOST_CROSS_COMPILE="aarch64-linux-gnu-" \
            TA_DEV_KIT_DIR="${PWD}/imx-boot/imx-optee-os/out/export-ta_arm64" \
            TEEC_EXPORT="${PWD}/out/usr" \
            TA_SIGN_KEY="$PWD/imx-boot/imx-optee-os/keys/rsa4096.pem"

    ビルド結果をコピーしておきます。

    [PC ~]$ install -D -t ${PWD}/out/lib/optee_armtz -m644 ${PWD}/optee_examples/out/ta/*
    [PC ~]$ install -D -t ${PWD}/out/usr/bin -m755 ${PWD}/optee_examples/out/ca/*

11.4.4.6. imx-optee-test をビルドする

imx-optee-test は必ずしも必要ではありません。利用機会は限られますが、OP-TEE の基本動作を確認するため、パフォーマンスを計測するために imx-optee-test を利用することができます。組み込むかどうかの判断はお任せします。

[注記]

アプリケーションをビルドするためには imx-optee-os, imx-optee-client のビルド結果を参照しているので、一度は imx-boot, imx-optee-client をビルドする必要があります。

  1. imx-optee-test をクローンする

    imx-boot や imx-optee-client ディレクトリと並列に配置されるように imx-optee-client をクローンして、必要に応じて適切なブランチなどをチェックアウトしてください。

    [PC ~]$ git clone https://source.codeaurora.org/external/imx/imx-optee-test.git \
            -b lf-5.10.y_2.0.0
  2. imx-optee-test をビルドする

    基本的な情報を参照するために TA_DEV_KIT_DIR、TA を署名するために TA_SIGN_KEY、ライブラリ参照のために TEEC_EXPORT を渡します。ビルドとインストールは別のターゲットなのでそれぞれ make を実行する必要があります。

    [PC ~]$ CFLAGS=-O2 make -R -C imx-optee-test \
            CROSS_COMPILE="aarch64-linux-gnu-" \
            TA_DEV_KIT_DIR="${PWD}/imx-boot/imx-optee-os/out/export-ta_arm64" \
            OPTEE_CLIENT_EXPORT="${PWD}/out/usr" \
            TA_SIGN_KEY="${PWD}/imx-boot/imx-optee-os/keys/rsa4096.pem"

    インストールします。

    [PC ~]$ make -C imx-optee-test install \
            DESTDIR="${PWD}/out" \
            TA_DEV_KIT_DIR="${PWD}/imx-boot/imx-optee-os/out/export-ta_arm64" \
            OPTEE_CLIENT_EXPORT="${PWD}/out/usr"
[注記]

本リリース時点では xtest 1014 にてエラーになる問題があります。そのため環境変数で CFLAGS=-O2 を渡しています。make 引数で渡すとヘッダファイルの include 設定がなくなります。 ブートローダーのビルド時に O2 としているのも同様の理由です。

11.4.4.7. ビルド結果の確認とまとめる

imx-optee-os, imx-optee-client の最小構成では以下のとおりです。

out/
|-- lib
|   `-- optee_armtz 1
|       |-- 023f8f1a-292a-432b-8fc4-de8471358067.ta
|       |-- f04a0fe7-1f5d-4b9b-abf7-619b85b4ce8c.ta
|       `-- fd02c9da-306c-48c7-a49c-bbd827ae86ee.ta
`-- usr
    |-- include
    |   |-- ck_debug.h
    |   |-- optee_client_config.mk
    |   |-- pkcs11.h
    |   |-- pkcs11_ta.h
    |   |-- tee_bench.h
    |   |-- tee_client_api.h
    |   |-- tee_client_api_extensions.h
    |   |-- tee_plugin_method.h
    |   `-- teec_trace.h
    |-- lib 2
    |   |-- libckteec.a
    |   |-- libckteec.so -> libckteec.so.0
    |   |-- libckteec.so.0 -> libckteec.so.0.1
    |   |-- libckteec.so.0.1 -> libckteec.so.0.1.0
    |   |-- libckteec.so.0.1.0
    |   |-- libteec.a
    |   |-- libteec.so -> libteec.so.1
    |   |-- libteec.so.1 -> libteec.so.1.0.0
    |   |-- libteec.so.1.0 -> libteec.so.1.0.0
    |   `-- libteec.so.1.0.0
    `-- sbin 3
        `-- tee-supplicant

1

Dynamic TA。Linux のファイルシステムに保存される

2

TEE client API, TEE, internal core API

3

Linux 上で動作する OP-TEE の補助的な機能をもつ

imx-optee-test, optee_examples を含めたビルド結果は以下のとおりです。

out
|-- bin
|   `-- xtest
|-- lib
|   `-- optee_armtz
|       |-- 023f8f1a-292a-432b-8fc4-de8471358067.ta
|       |-- 2a287631-de1b-4fdd-a55c-b9312e40769a.ta
|       |-- 380231ac-fb99-47ad-a689-9e017eb6e78a.ta
|       |-- 484d4143-2d53-4841-3120-4a6f636b6542.ta
|       |-- 528938ce-fc59-11e8-8eb2-f2801f1b9fd1.ta
|       |-- 5b9e0e40-2636-11e1-ad9e-0002a5d5c51b.ta
|       |-- 5ce0c432-0ab0-40e5-a056-782ca0e6aba2.ta
|       |-- 5dbac793-f574-4871-8ad3-04331ec17f24.ta
|       |-- 614789f2-39c0-4ebf-b235-92b32ac107ed.ta
|       |-- 690d2100-dbe5-11e6-bf26-cec0c932ce01.ta
|       |-- 731e279e-aafb-4575-a771-38caa6f0cca6.ta
|       |-- 873bcd08-c2c3-11e6-a937-d0bf9c45c61c.ta
|       |-- 8aaaf200-2450-11e4-abe2-0002a5d5c51b.ta
|       |-- a4c04d50-f180-11e8-8eb2-f2801f1b9fd1.ta
|       |-- a734eed9-d6a1-4244-aa50-7c99719e7b7b.ta
|       |-- b3091a65-9751-4784-abf7-0298a7cc35ba.ta
|       |-- b689f2a7-8adf-477a-9f99-32e90c0ad0a2.ta
|       |-- b6c53aba-9669-4668-a7f2-205629d00f86.ta
|       |-- c3f6e2c0-3548-11e1-b86c-0800200c9a66.ta
|       |-- cb3e5ba0-adf1-11e0-998b-0002a5d5c51b.ta
|       |-- d17f73a0-36ef-11e1-984a-0002a5d5c51b.ta
|       |-- e13010e0-2ae1-11e5-896a-0002a5d5c51b.ta
|       |-- e626662e-c0e2-485c-b8c8-09fbce6edf3d.ta
|       |-- e6a33ed4-562b-463a-bb7e-ff5e15a493c8.ta
|       |-- f04a0fe7-1f5d-4b9b-abf7-619b85b4ce8c.ta
|       |-- f157cda0-550c-11e5-a6fa-0002a5d5c51b.ta
|       |-- f4e750bb-1437-4fbf-8785-8d3580c34994.ta
|       |-- fd02c9da-306c-48c7-a49c-bbd827ae86ee.ta
|       `-- ffd2bded-ab7d-4988-95ee-e4962fff7154.ta
`-- usr
    |-- bin
    |   |-- optee_example_acipher
    |   |-- optee_example_aes
    |   |-- optee_example_hello_world
    |   |-- optee_example_hotp
    |   |-- optee_example_plugins
    |   |-- optee_example_random
    |   `-- optee_example_secure_storage
    |-- include
    |   |-- ck_debug.h
    |   |-- optee_client_config.mk
    |   |-- pkcs11.h
    |   |-- pkcs11_ta.h
    |   |-- tee_bench.h
    |   |-- tee_client_api.h
    |   |-- tee_client_api_extensions.h
    |   |-- tee_plugin_method.h
    |   `-- teec_trace.h
    |-- lib
    |   |-- libckteec.a
    |   |-- libckteec.so -> libckteec.so.0
    |   |-- libckteec.so.0 -> libckteec.so.0.1
    |   |-- libckteec.so.0.1 -> libckteec.so.0.1.0
    |   |-- libckteec.so.0.1.0
    |   |-- libteec.a
    |   |-- libteec.so -> libteec.so.1
    |   |-- libteec.so.1 -> libteec.so.1.0.0
    |   |-- libteec.so.1.0 -> libteec.so.1.0.0
    |   |-- libteec.so.1.0.0
    |   `-- tee-supplicant
    |       `-- plugins
    |           `-- f07bfc66-958c-4a15-99c0-260e4e7375dd.plugin
    `-- sbin
        `-- tee-supplicant

ターゲット上のコンテナに展開するために、tarball で固めます。

[PC ~]$ tar -caf optee.tar.gz -C out .
[ティップ]

「アプリケーションをコンテナで実行する」 を参考にしてコンテナ作成の時に組み込むことをお勧めします

11.4.4.8. OP-TEE を組み込む

ターゲットデバイスで debian コンテナを起動して、その上で OP-TEE を動作させます。

  1. swu でブートローダーをアップデートする 「Armadilloのソフトウェアをアップデートする」 を参考にアップデートしてください。
  2. ビルド結果が置かれているパスでコンテナを立ち上げます

    ここでは debian を利用しています。

    [armadillo ~]# podman run -it --name=dev_optee --device=/dev/tee0 \
            --device=/dev/teepriv0 -v "$(pwd)":/mnt docker.io/debian /bin/bash
  3. ビルド結果を展開する

    tarball を展開します。

    [container ~]# tar -xaf /mnt/optee.tar.gz -C /
  4. tee-supplicant を起動する

    [container ~]# tee-supplicant -d
  5. xtest で動作を確認する

    xtest で OP-TEE の基本動作を確認します。以下のログは全テストをパスしたログです。

    [container ~]# xtest
    Run test suite with level=0
    
    TEE test application started over default TEE instance
    ######################################################
    #
    # regression+pkcs11+regression_nxp
    #
    ######################################################
    
    * regression_1001 Core self tests
      regression_1001 OK
    (省略)
    +-----------------------------------------------------
    33939 subtests of which 0 failed
    114 test cases of which 0 failed
    0 test cases were skipped
    TEE test application done!
[注記]

xtest の全テストをパスできない場合は環境構築から見直していただくことをお勧めします。問題が解決できないようであればサポートにご連絡ください。

  1. アプリケーションを起動する

    ビルド結果を展開したことで CA も TA も配置されました。目的の CA を起動してください。ここでは optee_examples の optee_example_hello_world を実行します。

    [container ~]# optee_example_hello_world
    D/TA:  TA_CreateEntryPoint:39 has been called
    D/TA:  TA_OpenSessionEntryPoint:68 has been called
    I/TA: Hello World!
    D/TA:  inc_value:105 has been called
    I/TA: Got value: 42 from NW
    I/TA: Increase value to: 43
    I/TA: Goodbye!
    Invoking TA to increment 42
    TA incremented value to 43
    D/TA:  TA_DestroyEntryPoint:50 has been called
[ティップ]

tee-supplicant は OP-TEE の linux 環境のコンパニオンプロセスです。OP-TEE を利用するためにはなくてはならないものです。自動起動することをお勧めします。詳しくは、「アプリケーションをコンテナで実行する」 を参考にしてください。

11.4.5. パフォーマンスを測定する

xtest を利用することで AES, SHA アルゴリズムの OP-TEE OS のパフォーマンスを測定することができます。

AES のパフォーマンスを計測するために次のコマンドを実行します。この結果は例になります。

[container ~]# xtest --aes-perf
min=113.753us max=191.881us mean=116.426us stddev=4.10202us (cv 3.5233%) (8.38786MiB/s)

SHA のパフォーマンスを計測するためのコマンドを実行します。この結果も例になります。

[container ~]# xtest --sha-perf
min=50.876us max=123.003us mean=52.8036us stddev=2.4365us (cv 4.61427%) (18.494

11.4.6. SE050 を活用した TEE を構築する

NXP Semiconductors の EdgeLock SE050 は IoT アプリケーション向けのセキュアエレメントです。様々なアルゴリズムに対応した暗号エンジン、セキュアストレージを搭載します。GlobalPlatform によって標準化されている Secure Channel Protocol 03 に準拠し、バスレベル暗号化 (AES)、ホストとカードの相互認証 (CMAC ベース) を行います。

OP-TEE で SE050 を用いるユースケースとしては、IoT アプリケーション向けに特化された豊富な機能を活用した上で、ホスト側の処理を守りたい場合に利用することが考えられます。OP-TEE を組み合わせることで SE050 へアクセスする部分、保存された情報資産を取り出して実際に処理する部分を守ることができます。

[ティップ]

ユーザーが SE050 にアクセスする場合は、OP-TEE の TEE Client API や TEE Core API といった GlobalPlatformAPI を呼び出すことになります。デバイスの変更などの状況で比較的容易に移植が可能になります。

[ティップ]

SE050 の詳細については以下の NXP Semiconductors のページから検索して、ご確認ください。

11.4.6.1. OP-TEE 向け plug-and-trust ライブラリ

NXP Semiconductors が開発するライブラリ plug-and-trust を利用してSE050 にアクセスします。OP-TEE への移植は Foundries.io によって行われ、Github にて公開されています。現状、SE050 の全ての機能を使えるわけではありません。主に暗号強度が弱い鍵長が無効化されています。

現状で対応する処理:

  • RSA 2048, 4096 encrypt/decrypt/sign/verify
  • ECC sign/verify
  • AES CTR
  • RNG
  • SCP03 (i2c communications between the processor and the device are encrypted)
  • DieID generation
  • cryptoki integration
[ティップ]

OP-TEE 向け plug-and-trust の詳細の情報は以下を参照してください。

11.4.6.2. ビルドの流れ

基本的には CAAM の場合と同様の流れになる。

  1. ブートローダーをビルドする
  2. imx-optee-client をビルドする
  3. TA, CA をビルドする
  4. ビルド結果を集める

ディレクトリ構成の概略は以下のとおりです。

├── imx-boot
│   ├── imx-atf
│   ├── imx-mkimage
│   ├── imx-optee-os
│   └── uboot-imx
├── imx-optee-client
├── imx-optee-test
├── optee_examples
├── plug-and-trust
└── out

11.4.6.3. ビルド環境を構築する

OP-TEE 向け plug-and-trust をビルドするために必要なパッケージをインストールします。

[PC ~]$ sudo apt install cmake

11.4.6.4. OP-TEE 向け plug-and-trust をビルドする

  1. OP-TEE 向け plug-and-trust をクローンする

    imx-boot や imx-optee-client ディレクトリと並列に配置されるように OP-TEE 向け plug-and-trust をクローンして、必要に応じて適切なブランチなどをチェックアウトしてください。

    [PC ~]$ git clone https://github.com/foundriesio/plug-and-trust.git -b optee_lib
  2. OP-TEE 向け plug-and-trust をビルドする

    [PC ~]$ mkdir -p plug-and-trust/optee_lib/build
    [PC ~]$ cd plug-and-trust/optee_lib/build
    [PC ~/plug-and-trust/optee_lib/build]$ cmake \
            -DCMAKE_C_FLAGS="-mstrict-align -mgeneral-regs-only" \
            -DCMAKE_C_COMPILER=aarch64-linux-gnu-gcc \
            -DOPTEE_TREE="${PWD}/../../../imx-boot/imx-optee-os" ..
    
    -- The C compiler identification is GNU 10.2.1
    -- The CXX compiler identification is GNU 10.2.1
    : (省略)
    -- Generating done
    -- Build files have been written to: /path/plug-and-trust/optee_lib/build
    [PC ~/plug-and-trust/optee_lib/build]$ make
    make
    Consolidate compiler generated dependencies of target se050
    [  4%] Building C object CMakeFiles/se050.dir/path/plug-and-trust/hostlib/hostLib/libCommon/infra/global_platf.c.o
    [  8%] Building C object CMakeFiles/se050.dir/path/plug-and-trust/hostlib/hostLib/libCommon/infra/sm_apdu.c.o
    : (省略)
    [100%] Linking C static library libse050.a
    [100%] Built target se050

11.4.6.5. imx-optee-os のコンフィグの修正

SE050 を crypto driver として利用するためにコンフィグを修正する。

SE050 向けにビルドするために imx-boot/Makefile を追加する。

$(OPTEE)/out/tee.bin: $(OPTEE)/.git FORCE
        $(MAKE) -C $(OPTEE) O=out ARCH=arm PLATFORM=imx CFG_WERROR=y \
        PLATFORM_FLAVOR=mx8mpevk \
        CFG_NXP_SE05X=y \ 1
        CFG_IMX_I2C=y \ 2
        CFG_CORE_SE05X_I2C_BUS=2 \
        CFG_CORE_SE05X_BAUDRATE=400000 \
        CFG_CORE_SE05X_OEFID=0xA200 \
        CFG_IMX_CAAM=n \ 3
        CFG_NXP_CAAM=n \
        CFG_CRYPTO_WITH_CE=y \ 4
        CFG_STACK_THREAD_EXTRA=8192 \ 5
        CFG_STACK_TMP_EXTRA=8192 \
        CFG_NUM_THREADS=1 \ 6
        CFG_WITH_SOFTWARE_PRNG=n \ 7
        CFG_NXP_SE05X_PLUG_AND_TRUST_LIB=~/plug-and-trust/optee_lib/build/libse050.a \ 8
        CFG_NXP_SE05X_PLUG_AND_TRUST=~/plug-and-trust/

コンフィグの修正に関する詳細

1

SE050 を利用するために有効にする

2

imx-i2c ドライバ を有効にする

3

CAAM は無効化する

4

AES や SHA は高速な Arm CE を利用する

5

スタックを通常よりも多く消費するためにスタックを増量する

6

スレッドによる複数のコンテキストに対応していないためスレッドを1つとする

7

ハードウェア乱数発生器を利用するために無効にする

8

SE050 のドライバの実装は OP-TEE 向け plug-and-trust ライブラリ内に存在する

[注記]
  • SE050 の host 接続用 I2C は最大 3.2 MHz (high speed)ですが、i.MX 8M Plus の i2c の最大周波数は 400kHz のため、遅い通信速度で実装されています
  • CAAM と SE050 の共存は、SE050 を有効にすることによって CAAM の個別のドライバの依存関係が不正になるため、実行時にエラーになる問題があります

11.4.6.6. uboot-imx の修正

Armadillo は消費電力の削減のため SE050 を Deep Power-down モードに設定してパワーゲーティングしている。Deep Power-down モードを解除して SE050 を利用するためには、i.MX 8M Plus に接続されている SE050 の ENA ピンをアサートする必要があります。ENA ピンをアサートすると SE050 は一定時間の後に起動するので SE050 を利用すためには若干の待ち時間が必要となります。OP-TEE OS は起動時にドライバの初期化等を行う実装になっている。そのため、OP-TEE OS が起動する前に生存している SPL (Secondary Program Loader) で Deep Power-down を解除することで待ち時間を稼いでいる。

以下はシステムの起動と SE050 の関係を示したシーケンス図。

images/common-images/security/se050_optee_boot_sequence.png

図11.3 SE050 向け OP-TEE の起動シーケンス図


ENA をアサートするために以下のように変更する

diff --git a/board/atmark-techno/armadillo_x2/spl.c b/board/atmark-techno/armadillo_x2/spl.c
index a26bc85633..88f5b8560c 100644
--- a/board/atmark-techno/armadillo_x2/spl.c
+++ b/board/atmark-techno/armadillo_x2/spl.c
@@ -111,6 +111,11 @@ static struct fsl_esdhc_cfg usdhc_cfg[2] = {
        {USDHC3_BASE_ADDR, 0, 8},
 };

+#define SE_RST_N IMX_GPIO_NR(1, 12)
+static iomux_v3_cfg_t const se_rst_n_pads[] = {
+       MX8MP_PAD_GPIO1_IO12__GPIO1_IO12 | MUX_PAD_CTRL(NO_PAD_CTRL),
+};
+
 int board_mmc_init(bd_t *bis)
 {
        int i, ret;
@@ -228,6 +233,12 @@ void spl_board_init(void)
        clock_enable(CCGR_GIC, 1);
 #endif

+       imx_iomux_v3_setup_multiple_pads(se_rst_n_pads,
+                                        ARRAY_SIZE(se_rst_n_pads));
+
+       gpio_request(SE_RST_N, "se_rst_n");
+       gpio_direction_output(SE_RST_N, 1);
+
        puts("Normal Boot\n");
 }

11.4.6.7. imx-optee-os の imx-i2c ドライバの修正

imx-optee-os の imx-i2c ドライバには i.MX 8M Plus の対応が入っていないため、レジスタ等の定義を追加する必要がある。imx-optee-os には lf-5.10.y_2.0.0 からSE050 ドライバが取り込まれている。

以下のように修正する。

diff --git a/core/arch/arm/plat-imx/conf.mk b/core/arch/arm/plat-imx/conf.mk
index b4fbfed5..3f6388f3 100644
--- a/core/arch/arm/plat-imx/conf.mk
+++ b/core/arch/arm/plat-imx/conf.mk
@@ -555,7 +555,7 @@ endif

 else

-$(call force,CFG_CRYPTO_DRIVER,n)
-$(call force,CFG_WITH_SOFTWARE_PRNG,y)
+#$(call force,CFG_CRYPTO_DRIVER,n) 1
+#$(call force,CFG_WITH_SOFTWARE_PRNG,y) 2

 ifneq (,$(filter y, $(CFG_MX6) $(CFG_MX7) $(CFG_MX7ULP)))
diff --git a/core/arch/arm/plat-imx/registers/imx8m.h b/core/arch/arm/plat-imx/registers/imx8m.h
index 9b6a50ee..59fcea88 100644
--- a/core/arch/arm/plat-imx/registers/imx8m.h
+++ b/core/arch/arm/plat-imx/registers/imx8m.h
@@ -42,6 +42,17 @@
 #define IOMUXC_I2C1_SDA_CFG_OFF        0x480
 #define IOMUXC_I2C1_SCL_MUX_OFF        0x214
 #define IOMUXC_I2C1_SDA_MUX_OFF        0x218
+#elif defined(CFG_MX8MP)
+#define I2C1_BASE              0x30a20000
+#define I2C2_BASE              0x30a30000
+#define I2C3_BASE              0x30a40000
+
+#define IOMUXC_I2C1_SCL_CFG_OFF        0x460
+#define IOMUXC_I2C1_SDA_CFG_OFF        0x464
+#define IOMUXC_I2C1_SCL_MUX_OFF        0x200
+#define IOMUXC_I2C1_SDA_MUX_OFF        0x204
+#define IOMUXC_I2C1_SCL_INP_OFF        0x5A4
+#define IOMUXC_I2C1_SDA_INP_OFF        0x5A8
 #endif

 #endif /* __IMX8M_H__ */
diff --git a/core/drivers/imx_i2c.c b/core/drivers/imx_i2c.c
index a318c32c..a9dab31c 100644
--- a/core/drivers/imx_i2c.c
+++ b/core/drivers/imx_i2c.c
@@ -34,6 +34,16 @@
 /* Clock */
 #define I2C_CLK_CGRBM(__x)     0 /* Not implemented */
 #define I2C_CLK_CGR(__x)       CCM_CCRG_I2C##__x
+#elif defined(CFG_MX8MP)
+/* IOMUX */
+#define I2C_INP_SCL(__x)       (IOMUXC_I2C1_SCL_INP_OFF + ((__x) - 1) * 0x8)
+#define I2C_INP_SDA(__x)       (IOMUXC_I2C1_SDA_INP_OFF + ((__x) - 1) * 0x8)
+#define I2C_INP_VAL(__x)       (((__x) == 1 || (__x) == 2) ? 0x2 : 0x4)
+#define I2C_MUX_VAL(__x)       0x010
+#define I2C_CFG_VAL(__x)       0x1c6
+/* Clock */
+#define I2C_CLK_CGRBM(__x)     0 /* Not implemented */
+#define I2C_CLK_CGR(__x)       CCM_CCRG_I2C##__x
 #elif defined(CFG_MX6ULL)
 /* IOMUX */
 #define I2C_INP_SCL(__x)       (IOMUXC_I2C1_SCL_INP_OFF + ((__x) - 1) * 0x8)
@@ -182,7 +192,7 @@ static void i2c_set_bus_speed(uint8_t bid, int bps)
        vaddr_t addr = i2c_clk.base.va;
        uint32_t val = 0;

-#if defined(CFG_MX8MM)
+#if defined(CFG_MX8MM) || defined(CFG_MX8MP)
        addr += CCM_CCGRx_SET(i2c_clk.i2c[bid]);
        val = CCM_CCGRx_ALWAYS_ON(0);
 #elif defined(CFG_MX6ULL)

以下は imx プラットフォームの makefile の問題です。回避するためにコメントアウトします。

1

imx プラットフォームで CAAM 以外の crypto driver を利用することを想定していない

2

CAAM の HWRNG を利用しないということは PRNG を使うことしか想定しない

11.4.6.8. ビルドとターゲットボードへの組み込み

修正した後は、ビルドからターゲットボードへの組み込みまで CAAM 向けの OP-TEE と同様の手順で作業することが可能です。 「ブートローダーを再ビルドする」 の作業から開始して組み込みしてください。

11.4.6.9. xtest の制限

「OP-TEE 向け plug-and-trust ライブラリ」 で説明したように一部のアルゴリズムに制限があります。そのため xtest の全てのテスト項目をパスするわけではありません。以下に失敗するテスト項目を列挙する。

  • 1009 TEE Wait cancel

    • キャンセル処理をするために OP-TEE はマルチスレッドが有効な構成でなくてはならない。SE050 へのアクセスをシリアライズする利用するために imx-optee-os の make 時にスレッドを1つにしているため
  • regression_4007_rsa.1 Generate RSA-256 key

    • RSA 256 は対応していない鍵長
  • regression_4006 Test TEE Internal API Asymmetric Cipher operations

    • RSA 1024 は対応していない鍵長
  • regression_4009 Test TEE Internal API Derive key ECDH

    • E/TC:? 0 shared_secret:333 private key must be stored in SE050 flash
    • provisioning が必要
  • regression_4011 Test TEE Internal API Bleichenbacher attack

    • RSA 512 は対応していない鍵長
  • regression_6018 Large object

    • 原因が不明。ヒープが不足している可能性がある

以下は特に問題はないが時間がかかるためにフリーズしているかのように見える。

  • 1006 Secure time source

    • 大変時間がかかるため固まったかのように見えます
  • regression_nxp_0001, regression_nxp_0003

    • 大変時間がかかるため固まったかのように見えます
[ティップ]

xtest を行う際にはデフォルトでテストに失敗しても先に進む設定となっています。ただ、時間のかかるテストは無効にすることも可能です。

[container ~]# xtest -x 1006 -x regression_nxp_0003

11.4.7. imx-optee-os 技術情報

11.4.7.1. ソフトウェア全体像

OP-TEE のアーキテクチャの概要を説明する。ここでは i.MX 8M Plus に搭載される Cortex-A53 コアのアーキテクチャである aarch64 を前提に話を進める。

以下にのシステム図を示す。

images/common-images/security/optee_system_diagram.png

図11.4 OPTEE のシステム図


以下のコンポーネントによってシステムが構成される。概要と主な責務について説明する。

  • OP-TEE

    • GlobalPlatform の TEE 実装。secure EL1 に配置される
  • Trusted Firmware-A

    • Linaro によって開発される Secure monitor の実装
    • secure state と non-secure state の遷移管理、PSCI に準拠した電源管理などを担当する
    • optee dispatcher と呼ばれる OP-TEE の呼び出しモジュールを内部に持つ
  • Client Application (CA)

    • TA を呼び出すアプリケーション
  • Trusted Application (TA)

    • TEE 上で CA からの呼び出しを処理
    • Dynamic TA と Pesudo TA がある

      • Pesudo は TA ではありません。通常は Dynamic TA を利用してください。Pesudo TA は OP-TEE OS に直接リンクされるため TEE Internal Core API は呼べません
    • Dynamic TA は Linux のファイルシステム上に配置される。tee-supplicant によって OP-TEE OS に引き渡される
  • tee-supplicant

    • Linux user 空間で動作する OP-TEE を補うプロセス。目的は Linux のリソースを OP-TEE OS が利用するため

11.4.7.2. フロー

TEE を呼び出すフローについて説明する。

CA が OP-TEE 上の TA とのセッションを確立する流れ

  1. Linux 上の CA が、セッションを開くために uuid を指定して TEE Client API を呼び出す

    • システムコールで tee driver が呼ばれる
  2. tee driver は セキュアモニタコールで Trusted Firmware-A (ATF) 上の OP-TEE dispacher を呼び出す
  3. OP-TEE dispatcher は optee vector table に登録されている OP-TEE のハンドラを呼び出す

    • この段階ではまだ EL3 の状態
  4. OP-TEE OS は自ら SEL1 に落ちて、内部処理をしてから、ここまでの逆順で tee-supplicant を呼び出す
  5. tee-supplicant は uuid を基に TA をロードして共有メモリに配置して OP-TEE OS を呼び出す
  6. OP-TEE は TA をロードする
  7. セッションができる

CA が TEE Client API を通して TA 上である処理を実行する流れ

  1. Linux 上の CA が TEE Client API を呼び出す

    • システムコールで tee driver が呼ばれる
  2. tee driver は セキュアモニタコールで Trusted Firmware-A (ATF) 上の OP-TEE dispacher を呼び出す
  3. OP-TEE dispatcher は optee vector table に登録されている OP-TEE のハンドラを呼び出す
  4. ハンドラ (OP-TEE OS) は自ら SEL1 に落ちる。内部処理をしてから、SEL0 に落ちて TA を呼び出す
  5. TA は API の引数を基にある処理を実行する
  6. ここまでの逆順で CA まで戻る
[注記]

より詳しい内容については公式ドキュメントをご覧ください。

11.4.7.3. メモリマップ

セキュリティ関連の領域を含めた i.MX 8M Plus の物理メモリマップを次に示します。

images/common-images/security/memory_mapping.png

図11.5 i.MX 8M Plus の物理メモリマップ


imx-optee-os のメモリマップを次に示します。デバッグ等にお役立てください。

表11.2 OP-TEE メモリマップ

type virtual address physical address size description

TEE_RAM_RX/RW

0x5600_0000.. 0x561f_ffff

0x5600_0000.. 0x561f_ffff

0x0020_0000 (smallpg)

OP-TEE text + data セクション

IO_SEC

0x5620_0000.. 0x5620_ffff

0x32f8_0000.. 0x32f8_ffff

0x0001_0000 (smallpg)

TZASC

SHM_VASPACE

0x5640_0000.. 0x583f_ffff

0x0000_0000.. 0x01ff_ffff

0x0200_0000 (pgdir)

OP-TEE dynamic shared memory area, va の確保のみ

RES_VASPACE

0x5840_0000.. 0x58df_ffff

0x0000_0000.. 0x009f_ffff

0x00a0_0000 (pgdir)

OP-TEE 予約領域 (late mapping), va の確保のみ

IO_SEC

0x58e0_0000.. 0x591f_ffff

0x3020_0000.. 0x305f_ffff

0x0040_0000 (pgdir)

AIPS1(GPIO1, GPT, IOMUXC など)

IO_NSEC

0x5920_0000.. 0x595f_ffff

0x3080_0000.. 0x30bf_ffff

0x0040_0000 (pgdir)

AIPS3(UART2, CAAM, I2C など)

IO_SEC

0x5960_0000.. 0x597f_ffff

0x3880_0000.. 0x389f_ffff

0x0020_0000 (pgdir)

GICv3

TA_RAM

0x5980_0000.. 0x5b1f_ffff

0x5620_0000.. 0x57bf_ffff

0x01a0_0000 (pgdir)

TA ロード、実行領域

NSEC_SHM

0x5b20_0000.. 0x5b5f_ffff

0x57c0_0000.. 0x57ff_ffff

0x0040_0000 (pgdir)

OP-TEE contignuous shared memory area


11.5. セキュアブート

この章では起動ソフトウェアを認証するセキュリティ技術を適用する方法を説明します。

11.5.1. セキュアブートとチェーンオブトラスト

組み込みデバイスへの攻撃は様々な方向から行われます。ある方向のセキュリティ対策が強固な場合、攻撃者は回避可能な別の方向がないのか模索します。攻撃者のコードを何らかの方法でデバイスに組み込んで対策を回避するのが、単純ですが有効な方法でしょう。IoT デバイスはネットワーク上のサービスとデータのやり取りを行います。通信路の暗号化、サーバーとデバイスの相互認証などの対策を講じたとしても、IoT デバイス上にあるソフトウェアに攻撃者のコードを組み込むことで対策を回避してシステムに侵入される可能性があるのです。

セキュアブートは、起動ソフトウェアのディジタル署名を用いて正規ソフトウェアであることを確認してから起動する処理のことです。攻撃者によって作られた不正なコードを実行前に検出することができます。セキュアブートはチェーンオブトラスト (chain of trust) と表裏一体に実装されます。チェーンオブトラストとは、その名の通り、信頼を繋いでいく形態のことを指します。ルートオブトラストと呼ばれる基礎となる情報から枝葉のように繋がれた情報を認証していくことで、繋がれた個々のコンポーネントだけでなくシステムを信頼できるものにしてくれます。セキュアブートは、起動時にソフトウェアを順番に認証することで信頼を次に繋いでいるのです。セキュアブートの範囲をどこまでにするかによりますが、起動時に認証されたソフトウェアで別の情報を認証すれば、チェーンオブトラストを繋げていくことができます。また、IoT デバイスとクラウドサービスから構成されるような広範囲に及ぶシステムは特に信頼が必要になります。信頼できるセキュリティ基盤を構築するためには、構成するソフトウェアが正規のリリース物であることを確認することが重要になります。チェーンオブトラストを採用することでより信頼できる IoT システムとなり得るのです。

images/common-images/security/chain_of_trust.png

図11.6 チェーンオブトラスト


では、どういったケースでセキュアブートを採用するべきでしょうか。セキュアブートはセキュアな組み込みデバイスを実現する上で最初のステップにするべき技術です。しかし、導入するのであればコスト面にも配慮することが必要でしょう。まず、製造時の追加コストが必要です。鍵等を書き込む工程が必要になります。ただ書き込めばよいわけではなく、漏洩があってはいけないので物理的な隔離などセキュアに書き込む必要があります。メンテナンスにも追加のコストが必要です。ソフトウェアのリリース時にはソフトウェアの署名が必要になります。こちらも、物理的な隔離などの漏洩、汚染対策が必要になります。また、どこまでやるかによりますが、定期的な鍵の更新、インシデントや製品寿命による鍵のリボーケーションなどのメンテナンスコストが必要になってきます。費用対効果を検討してからの導入をお勧めします。

11.5.2. HAB とは

HAB (High Assurance Booting) は、NXP Semiconductors が提供するセキュアブートの実装です。i.MX 8M Plus の BootROM には HABv4 が組み込まれます。デフォルトでは無効な状態になっていますが、一度、eFuse に情報を書き込むことでセキュアブートが有効になり、それ以降、有効な状態のまま変更不可能になります。HABv4 で規定する仕様では次の情報をブートローダーイメージに追加することで BootROM が起動時にブートローダーの認証を行います。

  • CSF (Command Sequence File)、IVT (Image Vector Table)
  • SRK (Super Root Key)、CSF、IMG 署名確認鍵
  • イメージの署名

NXP Semiconductors は署名ツールとして CST (Code Signing Tool) をリリースしています。本来、署名範囲などは環境によって様々な実装がなされるべきなので署名ツール自体にはその辺りの仕様が含まれません。ブートローダーの実装仕様に依存します。Armadillo Base OS で採用される uboot-imx のセキュアブート処理では、Trusted Firmware-A (ATF)、OP-TEE OS、Linux カーネルイメージまでが認証の対象になります。署名に関する概要は以下のとおりです。

  • 署名確認用の鍵は X.509 証明書に対応する
  • 署名は RSA に対応する

    • 1024, 2048, 3072, 4096 bits
  • 署名のダイジェストは SHA256 のみ
[注記]

HAB の詳しい仕様は以下を参照してください。

11.5.3. 署名環境を構築する

NXP Semiconductors からリリースされる署名ツール (cts)、設定ファイルを準備してから、鍵の証明書を生成します。

11.5.3.1. 署名環境の構成

ディレクトリ構成の一部抜粋は以下のとおり。

├── cst_3.3.1 1
│   ├── crts 2
│   ├── keys 3
│   └── linux64/bin 4
├── imx-boot
├── linux-5.10
├── alpine
│   └── build-rootfs-[VERSION]
├── csf 5
└── out 6

1

本マニュアルでは v3.3.1 を利用した

2

ブートローダーに組み込む自己証明書群が配置される

3

鍵の生成ツール、生成された鍵が配置される

4

ビルド済み署名ツールが配置される

5

CSF が配置される

6

署名済みイメージが配置される

11.5.3.2. 署名ツールを準備する

  1. NXP Semiconductors から署名ツールをダウンロードします

    以下のサイトからダウンロードします。ここではユーザーが NXP Semiconductors のサイトで最新版を検索してダウンロードすることを想定しています。

    [注記]
    • ダウンロードには NXP Semiconductors サイトへのユーザー登録が必要になります
    • 本マニュアル作成時点では v3.3.1 を利用しました
  2. ツールを展開します

    [PC ~]$ tar -xaf cst-3.3.1.tgz

11.5.3.3. シリアル番号、パスワードを設定する

PKI 証明書を作るために、証明書のシリアル番号、パスワードを事前に作っておく必要があります。 シリアル番号ファイルとパスワードファイルを作ります。

シリアル番号とパスワードは任意の値を設定してください。

[PC ~]$ cd cst-3.1.1/keys
[PC ~/cst-3.1.1/keys]$ echo "12345678" > serial
[PC ~/cst-3.1.1/keys]$ echo "pass_phrase" > key_pass.txt
[PC ~/cst-3.1.1/keys]$ echo "pass_phrase" >> key_pass.txt

11.5.3.4. 鍵を生成する

セキュアブート用の PKI tree を作ります。生成する鍵とその証明書は以下のとおりです。

表11.3 セキュアブート用の鍵と証明書

namedescriptionfile name

CA 鍵ペア

ルート CA

CA1_sha256_[alg]_v3_ca_crt

SRK 鍵ペア

Super Root Key 用の鍵ペア

SRK[n]_sha256_[alg]_v3_ca_key

CSF 鍵ペア

CSF 用の鍵ペア

CSF[n]_sha256_[alg]_v3_usr_key

IMG 鍵ペア

イメージ用の鍵ペア

IMG[n]_sha256_[alg]_v3_usr_key

SRK 証明書

CA 鍵によって署名されたSRK 公開鍵を含んだ証明書

SRK[n]_sha256_[alg]_v3_ca_crt

CSF 証明書

SRK によって署名された CSF 公開鍵を含んだ証明書

CSF[n]_sha256_[alg]_v3_usr_crt

IMG 証明書

SRK によって署名された IMG 公開鍵を含んだ証明書

IMG[n]_sha256_[alg]_v3_usr_crt


  • [alg]: アルゴリズム
  • [n]: n = 1,2,3,4

    1. 鍵を生成する

      既存の CA 証明書を利用せずに、例として期限5年の4本の RSA-2048 の鍵を生成する。

      [PC ~/cst-3.1.1/keys]$ ./hab4_pki_tree.sh -existing-ca n \ 1
              -use-ecc n -kl 2048 \ 2
              -duration 5 \ 3
              -num-srk 4 \ 4
              -srk-ca y 5

      1

      既存の CA 証明書を利用するかどうか

      2

      -use-ecc は ECC を利用するかどうか。-kl は鍵長。-use-ecc が n の場合は RSA になる

      3

      期限は5年

      4

      SRK の数は4本

      5

      標準的な PKI ツリー

      [重要項目]

      生成した鍵は今後も利用するものです。壊れにくい、セキュアなストレージにコピーしておくことをお勧めします。

      [重要項目]

      鍵の更新は計画性を持って行ってください。たとえば開発時のみ利用する鍵、運用時に利用する鍵を使い分ける。また、鍵は定期的に更新が必要です。以下を参考にしてください。

      [ティップ]
      • 詳しくは cst 内の docs ディレクトリにある CST_UG.pdf をご参照ください
      • 自己署名証明書の場合は、hab4_pki_tree.sh の実装として、openssl-req の subj オプションで CN=CA1_sha256_[alg]_v3_ca のみを設定している
    2. 生成された鍵の確認

      以下のファイルが生成されたことを確認してください。

      cst-3.3.1
          ├── crts
          │   ├── CA1_sha256_2048_65537_v3_ca_crt.der
          │   ├── CA1_sha256_2048_65537_v3_ca_crt.pem
          │   ├── CSF1_1_sha256_2048_65537_v3_usr_crt.der
          │   ├── CSF1_1_sha256_2048_65537_v3_usr_crt.pem
          │   ├── CSF2_1_sha256_2048_65537_v3_usr_crt.der
          │   ├── CSF2_1_sha256_2048_65537_v3_usr_crt.pem
          │   ├── CSF3_1_sha256_2048_65537_v3_usr_crt.der
          │   ├── CSF3_1_sha256_2048_65537_v3_usr_crt.pem
          │   ├── CSF4_1_sha256_2048_65537_v3_usr_crt.der
          │   ├── CSF4_1_sha256_2048_65537_v3_usr_crt.pem
          │   ├── IMG1_1_sha256_2048_65537_v3_usr_crt.der
          │   ├── IMG1_1_sha256_2048_65537_v3_usr_crt.pem
          │   ├── IMG2_1_sha256_2048_65537_v3_usr_crt.der
          │   ├── IMG2_1_sha256_2048_65537_v3_usr_crt.pem
          │   ├── IMG3_1_sha256_2048_65537_v3_usr_crt.der
          │   ├── IMG3_1_sha256_2048_65537_v3_usr_crt.pem
          │   ├── IMG4_1_sha256_2048_65537_v3_usr_crt.der
          │   ├── IMG4_1_sha256_2048_65537_v3_usr_crt.pem
          │   ├── SRK1_sha256_2048_65537_v3_ca_crt.der
          │   ├── SRK1_sha256_2048_65537_v3_ca_crt.pem
          │   ├── SRK2_sha256_2048_65537_v3_ca_crt.der
          │   ├── SRK2_sha256_2048_65537_v3_ca_crt.pem
          │   ├── SRK3_sha256_2048_65537_v3_ca_crt.der
          │   ├── SRK3_sha256_2048_65537_v3_ca_crt.pem
          │   ├── SRK4_sha256_2048_65537_v3_ca_crt.der
          │   └── SRK4_sha256_2048_65537_v3_ca_crt.pem
          └── keys
              ├── CA1_sha256_2048_65537_v3_ca_key.der
              ├── CA1_sha256_2048_65537_v3_ca_key.pem
              ├── CSF1_1_sha256_2048_65537_v3_usr_key.der
              ├── CSF1_1_sha256_2048_65537_v3_usr_key.pem
              ├── CSF2_1_sha256_2048_65537_v3_usr_key.der
              ├── CSF2_1_sha256_2048_65537_v3_usr_key.pem
              ├── CSF3_1_sha256_2048_65537_v3_usr_key.der
              ├── CSF3_1_sha256_2048_65537_v3_usr_key.pem
              ├── CSF4_1_sha256_2048_65537_v3_usr_key.der
              ├── CSF4_1_sha256_2048_65537_v3_usr_key.pem
              ├── IMG1_1_sha256_2048_65537_v3_usr_key.der
              ├── IMG1_1_sha256_2048_65537_v3_usr_key.pem
              ├── IMG2_1_sha256_2048_65537_v3_usr_key.der
              ├── IMG2_1_sha256_2048_65537_v3_usr_key.pem
              ├── IMG3_1_sha256_2048_65537_v3_usr_key.der
              ├── IMG3_1_sha256_2048_65537_v3_usr_key.pem
              ├── IMG4_1_sha256_2048_65537_v3_usr_key.der
              ├── IMG4_1_sha256_2048_65537_v3_usr_key.pem
              ├── SRK1_sha256_2048_65537_v3_ca_key.der
              ├── SRK1_sha256_2048_65537_v3_ca_key.pem
              ├── SRK2_sha256_2048_65537_v3_ca_key.der
              ├── SRK2_sha256_2048_65537_v3_ca_key.pem
              ├── SRK3_sha256_2048_65537_v3_ca_key.der
              ├── SRK3_sha256_2048_65537_v3_ca_key.pem
              ├── SRK4_sha256_2048_65537_v3_ca_key.der
              └── SRK4_sha256_2048_65537_v3_ca_key.pem
    3. SRK テーブルと SRK ハッシュテーブルを生成する

      以下のコマンドを実行してください。

      [PC ~/]$ cd cst-3.3.1/crts
      [PC ~/cst-3.3.1/crts]$ ../linux64/bin/srktool --hab_ver 4 --digest sha256 \
              --table SRK_1_2_3_4_table.bin --efuses SRK_1_2_3_4_fuse.bin --fuse_format 1 \
              --certs "./SRK1_sha256_2048_65537_v3_ca_crt.pem,./SRK2_sha256_2048_65537_v3_ca_crt.pem,./SRK3_sha256_2048_65537_v3_ca_crt.pem,./SRK4_sha256_2048_65537_v3_ca_crt.pem"

      以下の2つのファイルが生成されていれば成功です。

      [PC ~/cst-3.3.1/crts]$ ls
      : (省略)
      SRK_1_2_3_4_fuse.bin
      SRK_1_2_3_4_table.bin
      : (省略)

11.5.4. セキュアブートを有効にする

生成した鍵と証明書を用いてセキュアブートを有効にしていきます。

[重要項目]

セキュアブート有効後は、基本的に起動毎に署名確認が実行されるようになります。既に eMMC に書かれている署名されていないファームウェアは起動に失敗するようになります。そのため、署名済みイメージを書き込む作業が必要になります。セキュアブートを有効にする前に、一度 「セキュアブート有効後のファームウェアの書き込みについて」 に目を通すことをお勧めします。

11.5.4.1. eFuse に SRK ハッシュを書き込む

セキュアブートを有効にするためには、i.MX 8M Plus の eFuse に SRK のハッシュ値を書く必要があります。i.MX 8M Plus の OCOTP (On-chip One-Time Programmable Element Controller) のレジスタ経由で書き込むことになります。uboot-imx には OCOTPA へのアクセスを行う fuse コマンドがあります。

  1. 生成されたハッシュ値を確認する

    PC 上の cst ディレクトリで、以下のコマンドでハッシュ値を表示する

    [PC ~]$ cd cst/crts
    [PC ~/cst/crts]$ hexdump -e '1/4 "0x"' -e '1/4 "%X""\n"' SRK_1_2_3_4_fuse.bin

    結果の例 (ハッシュ値は生成された鍵毎に異なります)

    0x72DBC22F
    0xDCAB0F6E
    0xBEBA9104
    0x35E61298
    0x768FA4B5
    0x2179343B
    0x92BF13D4
    0x461BAE7C
  2. ハッシュ値を書き込む

    [注意]

    eFuse は OTP なので一度作業を行うと変更はできません。注意して作業してください。

    [注意]

    ハッシュ値は生成された鍵毎にことなります。例の値をそのまま書かないで下さい。

    書き込みコマンドのヘルプです。

    fuse prog [-y] <bank> <word> <hexval> [<hexval>...]

    u-boot コマンドを利用して、確認したハッシュ値を eFuse に書いていきます。u-boot プロンプトを立ち上げてください。

    以下に例を示します (あくまで例なのでそのまま書かないでください)。

    u-boot=> fuse prog 6 0 0x72DBC22F
    u-boot=> fuse prog 6 1 0xDCAB0F6E
    u-boot=> fuse prog 6 2 0xBEBA9104
    u-boot=> fuse prog 6 3 0x35E61298
    u-boot=> fuse prog 7 0 0x768FA4B5
    u-boot=> fuse prog 7 1 0x2179343B
    u-boot=> fuse prog 7 2 0x92BF13D4
    u-boot=> fuse prog 7 3 0x461BAE7C

    1つのコマンドのログは以下のように出力されます。

    u-boot=> fuse prog 6 0 0x72DBC22F
    Programming bank 6 word 0x00000000 to 0x72dbc22f...
    Warning: Programming fuses is an irreversible operation!
            This may brick your system.
            Use this command only if you are sure of what you are doing!
    
    Really perform this fuse programming? <y/N>
    y
  3. SRK 領域をロックする

    このままでは SRK 領域はビットの状態によっては書き込み可能な状態なのでロックする必要があります。

    u-boot=> fuse prog 0 0 0x200

11.5.5. セキュアブート有効後のファームウェアの書き込みについて

セキュアブートを有効にした直後の初回書き込みは、再起動による再試行が可能な SD ブートを利用します。SD ブートを利用して署名済みイメージの書き込みが成功したのちには、Armadillo Base OS のファームウェアアップデート機能の利用が再び可能になります。

以下は初回とそれ以降のファームウェア書き込みの流れです。

セキュアブート有効直後の初回書き込み

二回目以降の書き込み

11.5.6. ブートローダーイメージを署名する

セキュアブートで利用するブートローダーイメージを署名していきます。 Armadillo Base OS を利用した署名済みイメージを生成するまでの流れは以下のとおりです。

  1. ブートローダーをビルドする
  2. イメージ情報を抽出する
  3. CSF 設定ファイルを書く
  4. CST を実行する
  5. ブートローダーイメージに CSF ファイルを書き込む

11.5.6.1. ブートローダーをビルドする

ソースコードの取得を行うために 「Armadilloのソフトウェアをビルドする」 の章を参考にしてビルド環境の構築からブートローダーのビルドまでを事前に行ってください。

  1. uboot-imx のコンフィグを変更する

    x2_defconfig に CONFIG_IMX_HAB=y を追加する

    [PC ~]$ cd imx-boot/uboot-imx/
    [PC ~/imx-boot/uboot-imx]$ vi configs/x2_defconfig
    CONFIG_IMX_HAB=y
  2. ブートローダーを再ビルドする

    「ブートローダーを再ビルドする」 を参考にして、ブートローダーのバージョンを変更してから、ブートローダーのビルドを行ってください。

11.5.6.2. ブートローダーイメージの情報を取得する

NXP Semiconductors が提供する cst (code signing tool) はイメージを署名をする機能、CSF をつくる機能を持っているだけで、実際にどの部分を署名するのかは実装依存となっています。Armadillo Base OS では uboot-imx を採用しているので、uboot-imx のガイドに従うことになります。uboot-imx のガイドによると、ビルドログに含まれている情報を抜き出して CSF ファイルを作成するように書かれています。イメージの詳しい情報については 図11.7「ブートローダーの署名済みイメージ」 を参照してください。

取得する情報は以下のとおりです。

表11.4 署名済みイメージ向けの情報

namedescriptionsourceexample

csf_off

SPL CSF の情報

ビルドログ OFFSET dump

csf_off 0x22e00

sld_csf_off

2nd loader CSF の情報

ビルドログ 中の OFFSET dump

sld_csf_off 0x59020

spl hab block

SPL IVT, SPL イメージの情報

ビルドログ 中の OFFSET dump

spl hab block: 0x91ffc0 0x0 0x22e00

sld hab block

2nd loader IVT (uboot, atf, op-tee, dtb が記載される) の情報

ビルドログ 中の OFFSET dump

sld hab block: 0x401fcdc0 0x58000 0x1020

fit_hab

2nd loader (uboot, atf, op-tee, dtb) イメージの情報

make print_fit_hab

0x40200000 0x5B000 0xCC1E8 …


  1. ビルドログから情報を抽出する

    ブートローダーのビルドログの一番最後のログに情報があります。抜き出してください。以下の結果は例となります。そのまま利用しないでください。

    ========= OFFSET dump =========
    Loader IMAGE:
     header_image_off       0x0
     dcd_off                0x0
     image_off              0x40
     csf_off                0x22e00 1
     spl hab block:         0x91ffc0 0x0 0x22e00 2
    
    Second Loader IMAGE:
     sld_header_off         0x58000
     sld_csf_off            0x59020 3
     sld hab block:         0x401fcdc0 0x58000 0x1020 4

    1

    SPL CSF の情報

    2

    SPL IVT と SPL image の情報

    3

    2nd loader CSF の情報

    4

    2nd loader IVT の情報

  2. print_fit_hab から情報を抽出する

    以下コマンドを実行して <1> の部分をメモする。以下の結果は例となります。そのまま利用しないでください。

    [PC ~]$ cd imx-boot
    [PC ~/imx-boot]$ make -C imx-mkimage/armadillo_x2 -f soc.mak SOC=iMX8MP print_fit_hab
    make: Entering directory '/home/xxx/imx-boot/imx-mkimage/armadillo_x2'
    ./../scripts/dtb_check.sh imx8mp-evk.dtb evk.dtb
    Use u-boot DTB: imx8mp-evk.dtb
    ./../scripts/pad_image.sh tee.bin
    ./../scripts/pad_image.sh bl31.bin
    ./../scripts/pad_image.sh u-boot-nodtb.bin evk.dtb
    u-boot-nodtb.bin + evk.dtb are padded to 866464
    TEE_LOAD_ADDR=0x56000000 ATF_LOAD_ADDR=0x00970000 VERSION=v2 ./print_fit_hab.sh 0x60000 evk.dtb
    0x40200000 0x5B000 0xCC1E8 1
    0x402CC1E8 0x1271E8 0x76B8
    0x970000 0x12E8A0 0xA150
    0x56000000 0x1389F0 0x206600
    make: Leaving directory '/home/xxx/imx-boot/imx-mkimage/armadillo_x2'

    1

    2nd loader (u-boot, atf, op-tee, dtb) のイメージ情報

11.5.6.3. CSF 設定ファイルを書く

  1. csf_spl.txt を作る

    以下のコマンドを実行して下さい。

    [PC ~]$ mkdir -p csf
    [PC ~]$ cd csf
    [PC ~/csf]$ vi csf_spl.txt

    以下は csf_spl.txt のサンプルです。注釈のある行以外はそのまま利用してください。

    [Header]
    Version = 4.3
    Hash Algorithm = sha256
    Engine = CAAM
    Engine Configuration = 0
    Certificate Format = X509
    Signature Format = CMS
    
    [Install SRK]
    File = "cst-3.3.1/crts/SRK_1_2_3_4_table.bin" 1
    Source index = 0 2
    
    [Install CSFK]
    File = "cst-3.3.1/crts/CSF1_1_sha256_2048_65537_v3_usr_crt.pem" 3
    
    [Authenticate CSF]
    
    [Install Key]
    Verification index = 0
    Target index = 2
    File = "cst-3.3.1/crts/IMG1_1_sha256_2048_65537_v3_usr_crt.pem" 4
    
    [Authenticate Data]
    Verification index = 2
    Blocks = 0x91ffc0 0x0 0x22e00 "imx-boot/imx-boot_armadillo_x2" 5
    
    [Unlock]
    Engine = CAAM
    Features = MID

    1

    SRK テーブルファイルを指定する

    2

    SRK1 (0 はじまり) を指定する

    3

    CSF1 ファイルを指定する

    4

    IMG1 ファイルを指定する

    5

    spl hab block の情報を書き込む

  2. csf_fit.txt をつくる

    以下のコマンドを実行して下さい。

    [PC ~]$ cd csf
    [PC ~/csf]$ vi csf_fit.txt

    以下は csf_fit.txt のサンプルです。ここでは csf_spl と鍵は同一です。注釈のある行以外はそのまま利用してください。

    [Header]
    Version = 4.3
    Hash Algorithm = sha256
    Engine = CAAM
    Engine Configuration = 0
    Certificate Format = X509
    Signature Format = CMS
    
    [Install SRK]
    File = "cst-3.3.1/crts/SRK_1_2_3_4_table.bin"
    Source index = 0
    
    [Install CSFK]
    File = "cst-3.3.1/crts/CSF1_1_sha256_2048_65537_v3_usr_crt.pem"
    
    [Authenticate CSF]
    
    [Install Key]
    Verification index = 0
    Target index = 2
    File = "cst-3.3.1/crts/IMG1_1_sha256_2048_65537_v3_usr_crt.pem"
    
    [Authenticate Data]
    Verification index = 2
    Blocks = 0x401fcdc0 0x58000 0x1020 "imx-boot/imx-boot_armadiilo_x2", \ 1
    0x40200000 0x5B000 0xCC1E8 "imx-boot/imx-boot_armadiilo_x2", \ 2
    0x402CC1E8 0x1271E8 0x76B8 "imx-boot/imx-boot_armadiilo_x2", \
    0x970000 0x12E8A0 0xA150 "imx-boot/imx-boot_armadiilo_x2", \
    0x56000000 0x1389F0 0x206600 "imx-boot/imx-boot_armadiilo_x2"

    1

    sld hab block の情報を1行目

    2

    それ以降の署名対象について fit_hab の情報を書き込む

11.5.6.4. CST を実行する

CSF 設定ファイルから CSF ファイルを作ります。

csf_spl.bin を作ります。

[PC ~]$ mkdir -p out
[PC ~]$ ./cst-3.3.1/linux64/bin/cst -i csf/csf_spl.txt -o out/csf_spl.bin
Install SRK
Install CSFK
Authenticate CSF
Install key
Authenticate data
CSF Processed successfully and signed data available in out/csf_spl.bin

csf_fit.bin を作ります。

[PC ~]$ ./cst-3.3.1/linux64/bin/cst -i csf/csf_fit.txt -o out/csf_fit.bin
Install SRK
Install CSFK
Authenticate CSF
Install key
Authenticate data
CSF Processed successfully and signed data available in out/csf_fit.bin

以下のファイルができていれば成功です。

[PC ~]$ ls out
csf_fit.bin  csf_spl.bin

11.5.6.5. ブートローダーイメージに CSF ファイルを書き込む

CSF をブートローダーイメージ内に予約された領域に書き込みます。seek で書き込み位置を合わせます。ここでのアドレス 0x22e0 と 0x59020 は例です。そのまま実行しないでください。

以下のコマンドを実行する。

[PC ~]$ cp imx-boot/imx-boot_armadiilo_x2 out/signed_imx-boot_armadiilo_x2
[PC ~]$ dd if=out/csf_spl.bin of=out/signed_imx-boot_armadiilo_x2 seek=$((0x22e00)) \
        oflag=seek_bytes bs=4K conv=notrunc 1
[PC ~]$ dd if=out/csf_fit.bin of=out/signed_imx-boot_armadiilo_x2 seek=$((0x59020)) \
        oflag=seek_bytes bs=4K conv=notrunc 2

1

csf_off の情報を引数とする

2

sld_csf_off の情報を引数とする

11.5.7. Linux カーネルイメージを署名する

セキュアブートで利用する Linux カーネルイメージを署名していきます。 Armadillo Base OS を利用した署名済みイメージを生成するまでの流れは以下のとおりです。

  1. イメージ情報を抽出して加工する
  2. IVT を生成する
  3. CSF 設定ファイルを書く
  4. CST を実行する
  5. Linux カーネルイメージに CSF を追加する

11.5.7.1. イメージ情報を抽出して加工する

署名確認対象となる Linux カーネルイメージが実際にメモリに展開されるサイズを取得します。ビルドされた Linux カーネルイメージ (Image) は bss 領域などの領域が省略されます。そのため、ファイルサイズではなく、イメージの内部に組み込まれている値を取得します。

イメージ情報を抽出するために Linux カーネルイメージが必要です。ブートローダーのようにセキュアブート処理を有効にするために再ビルドが必要なわけではないので、ビルド済みイメージを用意するか、「Armadilloのソフトウェアをビルドする」 を参考に Linux カーネルをビルドするかしてください。

  1. イメージ情報を抽出する

    ファイルから16バイト目を32ビット分取得する。

    [PC ~]$ hexdump -s 16 -n 4 -e '"0x""%X""\n"' linux-5.10/arch/arm64/boot/Image

    以下は結果の例です。

    0x1E80000
  2. イメージにパディングを追加する

    省略される領域をパディングとして追加します。--pad-to に取得したサイズを入力します。ここでは 0x1E80000 を入力します。

    [PC ~]$ objcopy -I binary -O binary --pad-to 0x1E80000 --gap-fill=0x00 \
            linux-5.10/arch/arm64/boot/Image out/Image_pad.bin

11.5.7.2. IVT を生成する

  1. IVT 生成スクリプト (genIVT.pl) を編集する

コピーしてから編集します。

[PC ~]$ cp imx-boot/uboot-imx/doc/imx/habv4/script_examples/genIVT.pl out/
[PC ~]$ cd out
[PC ~/out]$ vi genIVT.pl

イメージの編集箇所は以下のとおりです。Self Pointer と CSF Pointer を抽出した実際のサイズを基に変更します。イメージの構成についての詳細は 図11.8「Linux カーネルの署名済みイメージ」 を参考にしてください。それ以外の行はそのまま利用してください。

#! /usr/bin/perl -w
use strict;
open(my $out, '>:raw', 'ivt.bin') or die "Unable to open: $!";
print $out pack("V", 0x412000D1); # Signature
print $out pack("V", 0x40480000); # Load Address (*load_address)
print $out pack("V", 0x0); # Reserved
print $out pack("V", 0x0); # DCD pointer
print $out pack("V", 0x0); # Boot Data
print $out pack("V", 0x42300000); # Self Pointer (*ivt) 1
print $out pack("V", 0x42300020); # CSF Pointer (*csf) 2
print $out pack("V", 0x0); # Reserved
close($out);

1

0x42300000 = [load_address] + [actual_image_size] = 0x40480000 + 0x1E80000

2

0x42300020 = [ivt] + 0x20 = 0x42300000 + 0x20

  1. IVT を生成する

    以下のコマンドを実行してください。

    [PC ~/out]$ perl genIVT.pl
  2. IVT を追加する

    パッドされた Linux カーネルイメージに IVT を追加します。以下のコマンドを実行してください。

    [PC ~/out]$ cat Image_pad.bin ivt.bin > Image_pad_ivt.bin

11.5.7.3. CSF 設定ファイルを書く

  1. デバイスツリーブロブのサイズを調べる

    以下のコマンドを実行してください。

    [PC ~]$ printf '0x%X\n' $(stat -c '%s' \
            linux-5.10/arch/arm64/boot/dts/freescale/armadillo_iotg_g4.dtb)

    以下は結果の例です。

    0xD112
  2. csf_linux.txt を作る

    以下のコマンドを実行して下さい。

    [PC ~]$ cd csf
    [PC ~/csf]$ vi csf_linux.txt

    以下は csf_linux.txt のサンプルです。注釈のある行以外はそのまま利用してください。

    [Header]
    Version = 4.5
    Hash Algorithm = sha256
    Engine = CAAM
    Engine Configuration = 0
    Certificate Format = X509
    Signature Format = CMS
    
    [Install SRK]
    File = "cst-3.3.1/crts/SRK_1_2_3_4_table.bin"
    Source index = 0
    
    [Install CSFK]
    File = "cst-3.3.1/crts/CSF1_1_sha256_2048_65537_v3_usr_crt.pem"
    
    [Authenticate CSF]
    
    [Install Key]
    Verification index = 0
    Target index = 2
    File = "cst-3.3.1/crts/IMG1_1_sha256_2048_65537_v3_usr_crt.pem"
    
    [Authenticate Data]
    Verification index = 2
    Blocks = 0x40480000 0x00000000 0x01E80020 "out/Image_pad_ivt.bin", \ 1
    0x45000000 0x00000000 0xD112 "linux-5.10/arch/arm64/boot/dts/freescale/armadillo_iotg_g4.dtb" 2

    1

    Image_pad_ivt.bin のサイズを第3引数 (length) に入力する。ここでは 0x01E80020 の箇所。

    2

    dtb のサイズを第3引数に入力する。ここでは 0xD112 の箇所。

11.5.7.4. CST を実行する

CSF 設定ファイルから CSF ファイルを作ります。

csf_linux.bin を作ります。

[PC ~]$ ./cst-3.3.1/linux64/bin/cst -i csf/csf_linux.txt -o out/csf_linux.bin
Install SRK
Install CSFK
Authenticate CSF
Install key
Authenticate data
CSF Processed successfully and signed data available in out/csf_linux.bin

11.5.7.5. Linux カーネルイメージに CSF を追加する

Linux カーネルイメージに CSF を追加することで署名が付加されることになります。

以下のコマンドを実行します。

[PC ~]$ cat out/Image_pad_ivt.bin out/csf_linux.bin > out/signed_Image

11.5.8. セキュアブート有効後の初回ファームウェアアップデート

セキュアブートを有効にすると、既に書かれている署名されていないファームウェアでは基本的に起動ができなくなります。そのためファームウェアアップデートによって署名済みイメージを書き込む作業が必要になります。ただし、ファームウェアアップデートには注意が必要です。アップデート途中の電源断などが原因で Armadillo を再起動してしまうと、既に書かれていたファームウェアを起動することになるので起動に失敗してしまいます。そのため、セキュアブートを有効にした直後の書き込みは署名済みイメージが書かれた SD ブートを利用して eMMC を書き込みます。起動メディアを壊すことがないので再起動による再試行が可能です。

流れは以下の通りです。

  1. 署名イメージをつくる

  2. リポジトリ alpine/build-rootfs を取得する

  3. イメージを作る
  4. イメージを microSD カードに書き込む
  5. SD ブートでデバイスの eMMC を書き換える

11.5.8.1. イメージの作成と書き込み

PC 上で SD ブート用 microSD カードを作り、Armadillo に挿入して署名済みイメージを書いていきます。ここでは 「Alpine Linux ルートファイルシステムをビルドする」 で説明されているソースツリー alpine/build-rootfs にあるスクリプト build_image を利用します。

  1. Linux カーネルイメージを参照するように変更する

    以下のファイルを開きます。

    [PC ~]$ vi ~/build-rootfs-[VERSION]/ax2/packages

    linux-at の行を消します。

    : (省略)
    dosfstools
    atmark-x2-base
    
    linux-at
    
    crun
    podman
    : (省略)
  2. Linux カーネルイメージとデバイスツリーを配置する

    [PC ~]$ mkdir -p alpine/build-rootfs-[VERSION]/ax2/resources/boot
    [PC ~]$ cp out/signed_Image alpine/build-rootfs-[VERSION]/ax2/resources/boot/Image
    [PC ~]$ cp linux-[VERSION]/arch/arm64/boot/dts/freescale/armadillo_iotg_g4.dtb \
            alpine/build-rootfs-[VERSION]/ax2/resources/boot/
  3. ルートファイルシステムをビルドする

    以下のコマンドを実行します。

    [PC ~]$ cd build-rootfs-[VERSION]
    [PC ~/build-rootfs-[VERSION]$ sudo ./build_image.sh -B ./signed_armadillo_iotg_g4

    成功すると以下の2つのファイルが生成されます。ファイル名に含まれる日付やバージョンはあくまで例です。

    baseos-x2-3.14.3-at.1.20211124.img baseos-x2-3.14.3-at.1.20211124.tar.gz
  4. イメージを作る

    以下のコマンドを実行します。

    [PC ~/build-rootfs-[VERSION]]$ sudo ./build_image.sh -B ./signed_armadillo_iotg_g4 \
            --installer ./baseos-x2-[VERSION].img

    成功すると以下のファイルが生成されます。ファイル名に含まれる日付やバージョンはあくまで例です。

    baseos-x2-3.14.3-at.1.20211124-installer.img
  5. SD ブート用 microSD カードをつくり、SD ブートする

    「SDブートの活用」 を参考に baseos-x2-installer.img を microSD カードに書いて起動してください。このを microSD カードを利用すると SD ブートイメージのコピー (自分自身) を eMMC に書き込むように作られているので、セキュアブート用の署名付きイメージを書き込むことができます。最後にブートモードを戻して再起動すると署名済みイメージから起動するはずです。

11.5.8.2. セキュアブートの確認

Linux カーネルが立ち上がることを確認してください。 Linux カーネルまで立ち上がらない場合、uboot-imx のコマンドで HAB の状態を確認することができます。

Linux 起動まで正常な起動ログ

: (省略)
Booting from mmc ...

## Checking Image at 40480000 ...
Unknown image format!
53522 bytes read in 22 ms (2.3 MiB/s)

Authenticate image from DDR location 0x40480000...

Secure boot enabled 1

HAB Configuration: 0xcc, HAB State: 0x99
No HAB Events Found! 2

## Flattened Device Tree blob at 45000000
   Booting using the fdt blob at 0x45000000
   Using Device Tree in place at 0000000045000000, end 0000000045010111

Starting kernel ...
: (省略)

1

セキュアブートが有効な場合に表示されます

2

問題がない場合はイベントが表示されません

ブートローダーに問題がある場合の起動ログ

: (省略)
spl: ERROR:  image authentication unsuccessful
### ERROR ### Please RESET the board ###
: (省略)

Linux カーネルイメージに問題がある場合の起動ログ

: (省略)
Authenticate image from DDR location 0x40480000...
bad magic magic=0x14 length=0xa1 version=0x0
bad length magic=0x14 length=0xa1 version=0x0
bad version magic=0x14 length=0xa1 version=0x0
Error: Invalid IVT structure

Allowed IVT structure:
IVT HDR      = 0x4X2000D1
IVT ENTRY    = 0xXXXXXXXX
IVT RSV1      = 0x0
IVT DCD      = 0x0
IVT BOOT_DATA = 0xXXXXXXXX
IVT SELF      = 0xXXXXXXXX
IVT CSF      = 0xXXXXXXXX
IVT RSV2      = 0x0
Authenticate Image Fail, Please check 1
: (省略)

1

認証に失敗しています

u-boot コマンドの hab_status

問題がない場合

u-boot=> hab_status

Secure boot enabled

HAB Configuration: 0xcc, HAB State: 0x99
No HAB Events Found!

Linux カーネルイメージの署名確認で問題がある場合

u-boot=> hab_status

Secure boot disabled

HAB Configuration: 0xf0, HAB State: 0x66

--------- HAB Event 1 -----------------
event data:
        0xdb 0x00 0x14 0x45 0x33 0x0c 0xa0 0x00
        0x00 0x00 0x00 0x00 0x40 0x1f 0xdd 0xc0
        0x00 0x00 0x00 0x20

STS = HAB_FAILURE (0x33)
RSN = HAB_INV_ASSERTION (0x0C)
CTX = HAB_CTX_ASSERT (0xA0)
ENG = HAB_ENG_ANY (0x00)

11.5.9. SRK の無効化と切り替え

何らかのインシデント対応による鍵更新、また、鍵の定期更新などが必要な場合、その時点で利用している鍵を無効化して、別の鍵に切り替えることが可能です。ただし、その場合は複数 (i.MX 8M Plus の場合、最大4つ) の SRK が書かれていることが前提となります。

11.5.9.1. SRK の無効化 (revocation)

ここでは SRK1 から SRK2 に変更する例を説明します。

  1. csf_spl.txt に revocation のロックを解除するブロックを追加します

    デフォルトでは eFuse の revoke レジスタはロックされているので書き込みできません。csf ファイルでロックを解除することができます。常にロックを解除すると攻撃者に悪用される可能性があるので通常時のセキュアブートではロックされるべきです。

    csf_spl.txt を開いて、最後に Unlock ブロックを追加します。

    [PC ~]$ vi csf/csf_spl.txt
    : (省略)
    [Unlock]
    Engine = OCOTP
    Features = SRK REVOKE
  2. 署名済みイメージを書き込む

    「ブートローダーイメージを署名する」 を参考に署名済みイメージを生成して、イメージを書き込んでください。

  3. 再起動
  4. Unlock を確認する

    再起動時の uboot-imx のプロンプトを立ち上げてレジスタ値を確認します。以下のコマンドを実行してください。bit1 (SRK_REVOKE_LOCK) が落ちていると Unlock 状態です。

    u-boot=> md 0x30350050 1
    30350050: 00007dbc 1

    1

    7dbc の bit 1 が経っていないので unlock 状態

  5. SRK を無効化する

    ビットフィールドはビットは 0 はじまりで、鍵の番号は 1 はじまり (1,2,3,4) になります。 bit0 が SRK1、bit1 が SRK2、bit2 が SRK3、bit3 が SRK4 です。

    [注意]

    以下のコマンドはあくまで例なので、そのまま実行しないで下さい。

    SRK1 を無効化する場合は以下のコマンドを実行してください。最終引数が無効化する鍵の設定値です。

    u-boot=> fuse prog 9 3 1

11.5.9.2. SRK の切り替え

ここでは SRK1 から SRK2 に変更する例を説明します。

  1. SRK の変更

    csf_spl.txt を開いて、[Install SRK] ブロックのインデックス (0,1,2,3) と [Install CSFK], [Install Key] のファイル番号(1,2,3,4) を目的のSRKへ変更する。

    [PC ~]$ vi csf/csf_spl.txt
    : (省略)
    [Install SRK]
    File = "~/cst-3.3.1/crts/SRK_1_2_3_4_table.bin"
    Source index = 1 1
    
    [Install CSFK]
    File = "~/cst-3.3.1/crts/CSF2_1_sha256_2048_65537_v3_usr_crt.pem" 2
    
    [Install Key]
    File = "~/cst-3.3.1/crts/IMG2_1_sha256_2048_65537_v3_usr_crt.pem" 3

    1

    SRK のインデックス (0,1,2,3) を変更先のインデックスに切り替える

    2

    CSF ファイルの番号 (1,2,3,4) を変更先の番号に切り替える

    3

    IMG ファイルの番号 (1,2,3,4) を変更先の番号に切り替える

  2. 再署名する

    「ブートローダーイメージを署名する」 を参考に署名済みイメージを作成してください。

11.5.10. 技術情報

11.5.10.1. 署名済みイメージと展開先

以下にブートローダーの署名済みイメージと展開先についての例を示します。緑色の部分が BootROM によって署名検証される部分、橙色の部分は SPL によって署名検証される部分になります。

images/common-images/security/loadimage_spl_sld.png

図11.7 ブートローダーの署名済みイメージ


Linux の署名済みイメージと展開先の例は以下のとおりです。水色の部分は u-boot によって署名検証される部分になります。

images/common-images/security/loadimage_linux.png

図11.8 Linux カーネルの署名済みイメージ


11.5.10.2. セキュアブートのフロー

以下に SPL (Secondary Program Loader) のブートフローの概要を示します。点線で囲っている部分はセキュアブートで有効になる処理です。

images/common-images/security/spl_boot_sequence.png

図11.9 SPL セキュアブートのフロー


u-boot のブートフローの概要は以下のとおりです。SPL と同様に点線で囲っている部分はセキュアブートで有効になる処理です。

images/common-images/security/u-boot_boot_sequence.png

図11.10 u-boot セキュアブートのフロー