IoT Security 07 · 分離 — TrustZone・MPU・PMP・TEE

Chapter 07

分離 — TrustZone・MPU・PMP・TEE

この章がなぜ必要なのか——「バグはある」という前提で設計するため.

セキュアブートは「不正なコードを実行させない」ための仕組みだった。 だが 04 章で見たとおり、正規に署名された脆弱なコードは通ってしまう。

ネットワークスタック、BLE スタック、JSON パーサ、画像デコーダ—— これらに脆弱性が見つかるのは時間の問題である。

だから 「脆弱性を突かれても、鍵は取られない」 という構造を作る。 それが分離である。

この章で使う既出の用語(定義は各リンク先). セキュアブート(02 章 3 節)、決済(03 章 5 節)

1. 分離の考え方

分離があるかないかで、脆弱性の影響範囲が変わる分離がない場合TCP/IPBLEアプリ暗号鍵TCP/IP の脆弱性任意コード実行鍵を読める分離がある場合Non-secure(広い)TCP/IPBLEアプリSecure(狭い)暗号鍵RoTハードウェアで隔離TCP/IP の脆弱性 → 任意コード実行Secure 側には触れない鍵は「使える」が「読めない」分離は脆弱性を無くさない。影響範囲を閉じ込めるための仕組みである
通信スタックは大きく、脆弱性はいずれ出る。前提を「出る」に置いて、被害を閉じ込める設計にする

鍵を「使える」が「読めない」——これが核心である.

Non-secure 側のアプリは、Secure 側に「この平文を署名して」と依頼できる。 依頼はできるが、鍵そのものを読むことはできない。

だから脆弱性を突かれても、攻撃者にできるのは 「その機器がオンラインな間、署名を作らせる」ことまでで、 鍵を持ち去って別の場所で使うことはできない。

これはとても大きな違いである。攻撃の規模が「1 台・一時的」に閉じ込められる。

2. 分離の 3 つのレベル

レベル何を分離するか実現手段
レベル 1: 特権分離特権/非特権MPU / MMU(従来からある)
レベル 2: セキュリティ状態の分離Secure / Non-secureTrustZone、RISC-V PMP+
レベル 3: 物理的な分離別のプロセッサ/別のチップセキュリティサブシステム、セキュアエレメント

強度は下に行くほど高く、コストも高い。

TrustZone のメモリ分割を見る

領域の属性を変えて、どのアクセスが許され、どれが Fault になるかを確かめられる。

3. Arm TrustZone-M(Armv8-M)

Cortex-M23 / M33 / M35P / M55 / M85 が持つ機構である。

基本の構造

CPU は 2 つの「セキュリティ状態」を持つ。

状態実行できるものメモリアクセス
SecureSecure なコードSecure と Non-secure の両方にアクセスできる
Non-secureNon-secure なコードNon-secure のみ。Secure に触れると Fault

重要: これは特権レベル(Privileged / Unprivileged)とは直交する。 つまり 4 通りの組み合わせがある。

PrivilegedUnprivileged
SecureSecure PrivilegedSecure Unprivileged
Non-secureNon-secure PrivilegedNon-secure Unprivileged

メモリ属性の設定

属性意味
Secure (S)Secure 状態からのみアクセス可
Non-secure (NS)どちらからでもアクセス可
Non-secure Callable (NSC)Non-secure から呼び出せる Secure コードの入口だけを置く特別な領域

属性を決めるのは次のユニットである。

ユニット内容
SAU (Security Attribution Unit)CPU 内蔵。ソフトウェアで設定できる。領域数は実装依存(典型 4〜8)
IDAU (Implementation Defined Attribution Unit)チップベンダが固定で実装する。アドレスマップに従って属性を決める

最終的な属性は SAU と IDAU の両方から決まり、より制限の強い方が勝つ。

NSC と SG 命令 — 越境の唯一の入口

Non-secure から Secure の機能を呼びたい。だが自由に飛び込めては困る。

TrustZone-M の呼び出し — SG 命令が唯一の入口Non-secureresult = secure_sign(...)普通の関数呼び出しに見える飛び先は NSCNSC(呼び出し可能)SGB.W secure_sign_impl★ Secure Gateway 命令Securesecure_sign_impl鍵を使って署名するBXNS lrNon-secure へ戻る専用命令SG 命令のない場所に飛び込むと、その場で Fault になる入口が 1 種類の命令に限定されているので、「途中に飛び込む」攻撃ができない
Secure 側へ入る方法を 1 命令に限定したのが TrustZone-M の要点。任意の番地へは飛び込めない

SG 命令がなぜ必要か.

Non-secure から Secure への分岐は、「NSC 領域にあり、かつ最初の命令が SG である」 場合にしか許されない。 それ以外は SecureFault になる。

これで攻撃者は、Secure コードの途中に飛び込むことができない。 「署名関数の、権限チェックの直後にジャンプする」といった攻撃が原理的にできない。

ハードウェアで入口を 1 点に絞る——非常にうまい設計である。

越境時の注意点

注意点内容
引数のポインタ検証Non-secure から渡されたポインタが本当に Non-secure 領域か確認する(TT 命令)
レジスタのクリアSecure から戻るとき、使ったレジスタに秘密が残らないようにする
スタックの分離Secure と Non-secure でスタックポインタが別(MSP_S / PSP_S / MSP_NS / PSP_NS)
割り込みの扱い割り込みごとに Secure / Non-secure のどちらで処理するか設定する
/* Secure 側で、Non-secure から渡されたポインタを検証する */
#include <arm_cmse.h>

int32_t __attribute__((cmse_nonsecure_entry))
secure_sign(uint8_t *data, size_t len, uint8_t *out)
{
    /* ★ data と out が本当に Non-secure 側の書ける領域か確認する */
    if (cmse_check_address_range(data, len, CMSE_NONSECURE | CMSE_MPU_READ) == NULL)
        return -1;
    if (cmse_check_address_range(out, 64, CMSE_NONSECURE | CMSE_MPU_READWRITE) == NULL)
        return -1;

    /* ここから安全に処理できる */
    return do_sign(data, len, out);
}

この検証を忘れるのが、TrustZone で最も多いバグである.

Non-secure 側が secure_sign(secure_key_address, 32, out) と呼んだら?

検証がなければ、Secure 側が自分の鍵を読んで署名して返してしまう。 分離の意味が完全に失われる。この「Confused Deputy(混乱した代理人)」攻撃は、 特権分離のある全システムに共通する古典的な問題である。

Arm は CMSE(arm_cmse.h)でチェック用の組み込み関数を提供している。必ず使うこと。

周辺回路とバスの分離

CPU だけ分けても足りない。DMA が Secure メモリを読めたら意味がない。

対象分離の機構
メモリSAU / IDAU + メモリ保護コントローラ
周辺回路周辺ごとに Secure / Non-secure を割り当てる(ベンダ実装)
DMA・他のバスマスタバスレベルのフィルタ
割り込みNVIC の各割り込みに Secure / Non-secure 属性

ベンダごとの名称:

ベンダ名称
STGTZC(Global TrustZone Controller)— TZSC / MPCBB / TZIC
NordicSPU(System Protection Unit)
NXPAHB Secure Controller
RenesasTrustZone Filter(バス・周辺ごとの属性設定)
Silicon LabsSMU(Security Management Unit)

ここがベンダごとに最も差が出るところである.

「TrustZone 対応」と書いてあっても、 DMA コントローラが分離されているか、 どの周辺を個別に割り当てられるか、 SRAM をどの粒度で分けられるか(ブロック単位か、バンク単位か) は品種ごとに違う。

リファレンスマニュアルで必ず確認すること。 18 章〜21 章で各社の実装を見る。

4. TF-M(Trusted Firmware-M)

Arm が提供する、TrustZone-M 上のセキュア側ファームウェアの参照実装である。

TF-M のサービス構成Non-secure(NSPE)アプリケーションRTOS(FreeRTOS / Zephyr)TCP/IP・BLE・ミドルウェアPSA Functional API(psa_crypto_* など)Secure(SPE)TF-M Core(SPM:Secure Partition Manager)Crypto ServiceInternal Trusted StorageProtected StorageInitial AttestationFirmware Updateベンダ固有パーティション
PSA API に対して書いておけば、TrustZone がない MCU でも同じコードが動く。移行を見据えるなら最初からこれで書く

TF-M では、通常のアプリケーションが動く Non-secure 側を NSPE (Non-secure Processing Environment)、TF-M 本体が動く Secure 側を SPE(Secure Processing Environment)と呼ぶ。

PSA サービス役割
Crypto暗号処理。鍵はハンドルで参照し、値を返さない
Internal Trusted Storage (ITS)チップ内フラッシュに機密を保存する
Protected Storage (PS)外部メモリでも使えるよう、暗号化して保存する
Initial Attestationアテステーショントークンを生成する(04 章)
Firmware Updateセキュア OTA の制御(13 章)

PSA Crypto API の設計思想が優れている.

/* 鍵は「ハンドル」でしか触れない */
psa_key_id_t key_id;
psa_import_key(&attributes, key_material, len, &key_id);

/* 使うときはハンドルを渡すだけ。鍵の値は返ってこない */
psa_sign_hash(key_id, PSA_ALG_ECDSA(PSA_ALG_SHA_256),
              hash, hash_len, sig, sig_size, &sig_len);

API に「鍵の値を取り出す」関数が、そもそも存在しない(エクスポート可能属性を 明示的に付けた場合を除く)。

こうすると、Non-secure 側のコードは鍵を漏らそうにも漏らせない。 これが「安全な API 設計」の見本である。

TF-M の採用状況:

ベンダ対応
STSTM32L5 / U5 / H5 / WBA 向けに提供
NXPLPC55S6x / i.MX RT 向けに提供
NordicnRF Connect SDK に統合(nRF5340 / 9160 / 54L)
RenesasRA ファミリ向けに提供
InfineonPSoC 64 / Edge 向け
Silicon Labs一部対応

5. MPU による分離(TrustZone がない場合)

TrustZone がない Cortex-M0+/M3/M4/M7 でも、MPU で一定の分離はできる。

できることできないこと
特権/非特権の分離セキュリティ状態の分離(Secure/Non-secure)
タスクごとのメモリ保護ハードウェアによる越境の完全禁止
スタックオーバーフロー検出DMA の制限(バス側の機構が別途必要)
コード領域の書き込み禁止—
TrustZone がない MCU — MPU による特権分離特権モードRTOS カーネルセキュリティ関連処理鍵SVC 呼び出しで依頼する非特権モードアプリケーション通信スタック非特権コードからは、鍵の領域を MPU でアクセス禁止にしておく
TrustZone ほど強くはないが、「通信スタックの脆弱性から鍵を守る」という目的にはかなり効く

MPU による分離は「TrustZone より弱い」ことを理解しておくこと.

決定的な違いは、特権昇格の可能性である。

つまり TrustZone は信頼すべきコードの量を減らす(TCB の縮小)。 これが本質的な進歩である。

とはいえ MPU による分離でも、ないよりは遥かによい。 TrustZone がないチップでも、鍵を扱うコードを特権側に閉じ込めるべきである。

6. RISC-V の分離機構

機構内容
PMP (Physical Memory Protection)物理アドレスに対するアクセス制御。M モードが設定し、S/U モードに適用
ePMP (Smepmp)PMP の拡張。M モード自身にも制限をかけられる
特権モードM(マシン)/ S(スーパーバイザ)/ U(ユーザ)の 3 段
WorldGuardSiFive が提案する、TrustZone に近い多世界分離
Smmtt などメモリ隔離の標準化提案

RISC-V の分離は、まだ標準が固まりきっていない.

PMP は「Arm の MPU 相当」であって、「TrustZone 相当」ではない。 M モードのファームウェアに脆弱性があれば、全部が破れる。

ベンダ独自の拡張(SiFive の WorldGuard、Andes の拡張、 各社の TEE 実装)で埋めているのが現状である。

RISC-V ベースの SoC を選ぶときは、「TrustZone 相当の機構があるか」を 個別に確認する必要がある。 Espressif の ESP32-C6 は APM(Access Permission Management)ベースの ESP-TEE を提供している(20 章)。

7. Cortex-A クラスの分離

アプリケーションプロセッサでは、話が一段複雑になる。

層内容
TrustZone-ASecure World / Normal World。EL3(モニタ)が切り替える
TEE OSSecure World で動く小さな OS。OP-TEE が代表的
Trusted Application (TA)TEE OS 上で動く、署名されたアプリ
仮想化 (EL2)ハイパーバイザによる分離
Armv9 CCARealm という新しい世界を追加。ハイパーバイザからも隔離される
TrustZone-A(Cortex-A)の例外レベルNormal WorldEL2:ハイパーバイザEL1:Linux カーネルEL0:Linux アプリSecure WorldS-EL1:OP-TEE OSS-EL0:Trusted AppEL3:Secure MonitorSMC 命令で切り替えるMCU の TrustZone-M とは別物。名前は同じでも、切り替えの仕組みも粒度も違う
Cortex-A では EL3 のモニタが両世界を切り替える。Cortex-M の TrustZone-M とは実装がまったく異なる
標準内容
GlobalPlatform TEETEE のインタフェース標準(TEE Client API、TEE Internal API)
Arm SMCCCSecure Monitor Call の呼び出し規約
OP-TEEオープンソースの TEE 実装。Linaro が主導

Cortex-A で TEE を使う典型例:

IoT ゲートウェイやエッジ AI 機器でも、 証明書と秘密鍵を OP-TEE の中に閉じ込めるという使い方をする。

8. 分離だけでは足りないもの

攻撃内容対策
Confused DeputySecure 側に「自分のメモリを読ませる」ポインタ検証(本章 3 節)
サイドチャネルSecure の処理を、Non-secure から消費電力やキャッシュで観測するDPA 対策、キャッシュ分離(09 章)
フォールト越境チェックをグリッチで飛ばす冗長チェック(10 章)
Secure 側のバグSecure 側に脆弱性があれば終わりSecure 側を小さく保つ(TCB 最小化)
DMA バイパスバスマスタから Secure メモリを読むバスレベルのフィルタ設定(本章 3 節)

「Secure 側を小さく保つ」が最も重要な設計指針である.

Secure 側にコードを入れれば入れるほど、そこにバグが入る確率が上がる。 そして Secure 側のバグは、分離そのものを無効化する。

Secure 側に入れるべきもの:

Secure 側に入れてはいけないもの:

「入力を解析するコード」は Secure 側に置かない、が経験則である。

9. この章のまとめ

ポイント内容
目的「バグはある」前提で、鍵は取られない構造を作る
核心鍵を「使えるが読めない」——攻撃の規模を 1 台・一時的に閉じ込める
3 つのレベル特権分離(MPU)/セキュリティ状態(TrustZone)/物理(別チップ)
TrustZone-MSecure / Non-secure は特権レベルと直交する
NSC と SG越境の入口を 1 点に絞る。Secure コードの途中に飛び込めない
最頻出のバグポインタ検証の忘れ(Confused Deputy)。CMSE の関数を必ず使う
周辺・DMACPU だけでは足りない。バス側の分離をベンダごとに確認する
TF-M / PSA鍵はハンドルでしか触れない API 設計。エクスポート関数がない
MPU との差MPU は特権昇格で破れる。TrustZone は TCB を小さくする
最重要の指針Secure 側を小さく保つ。入力を解析するコードを入れない

次章では、この全部の土台になっている—— 暗号エンジンと、それ以上に重要な乱数生成器を扱う。