光子放出誘導レーザーフォールトインジェクションによるRP2350セキュアデバッグ
差分光子放出顕微鏡はデバッグ有効化レジスタの活動を特定し、SWDの前にレーザー探索範囲を絞り込んだ
日本語
コピー

概要
— 光子放出顕微鏡を用いて、Raspberry Pi マイコンでデバッグ機能を有効にするレジスタを特定した。 — 隣接する2か所にレーザーパルスを照射し、恒久的に無効化されていたチップの Secure world へのデバッガアクセスを復活させた。 — レスキューリセット後にこのアクセスを利用し、ワンタイムプログラマブルメモリから秘密を復元した。リセットはファームウェアが実行時ロックをかける前にチップを中断させるため、当該ページは Secure から読み取り可能なままだった。 — この攻撃には物理的な接触、破壊的な準備、そして約 25万ドル の実験装置が必要になる。
RP2350 のセキュリティモデル
RP2350 は Raspberry Pi のデュアルコアマイコンで、各プロセッサスロットは起動時に Arm Cortex-M33 か RISC-V Hazard3 のどちらかを選べる。ハードウェアセキュリティ機能は次のとおり。
- ワンタイムプログラマブルメモリ(OTP)に焼かれた公開鍵フィンガープリントに基づいて署名済みファームウェアを認証するセキュアブート
- Secure と Non-secure の実行状態を分離する Armv8-M TrustZone
- デバッグを恒久的に無効化する設定
- クロックや電源の操作によるタイミング擾乱を検出するグリッチ検出器
Raspberry Pi は RP2350 Hacking Challenge を通じて、こうした防御策の評価を研究者に積極的に呼びかけている。第1回は2024年8月から12月にかけて、初期のチップを対象に実施された。いくつかの発見が修正された後、Raspberry Pi は A4 リビジョンをリリースした。これが私たちのテストしたバージョンである。
恒久的なセキュリティ設定と起動用公開鍵フィンガープリントはワンタイムプログラマブル(OTP)メモリに保存される。各ビットは 0 から 1 へ一度だけ、不可逆に反転できるため、書き込まれた内容はチップの寿命を通じて変わらない。
OTP は128バイトのページ単位で構成され、2本の永続的な(つまりハードな)ロック行で保護される。ページ n では、PAGEn_LOCK0 が任意の読み取りキーと書き込みキー、およびキーが入力されなかった場合の挙動を設定し、PAGEn_LOCK1 がハードウェアで強制される LOCK_S と LOCK_NS の権限を持つ。これらの状態は読み書き可能から読み取り専用またはアクセス不可へ進めることはできるが、緩めることはできない。
OTP サブシステムはセキュリティ関連フィールドに冗長符号化を使う。RP2350 データシートによれば、重要なフラグは「8本の連続した OTP 行にわたり、8つのうち3つの多数決で符号化」され、OTP ロックビットは「多数決による三重冗長」で符号化される。
OTP リセット時、永続的な LOCK_S と LOCK_NS の値がページごとの実行時ロック(ソフトロックとも呼ばれる)を初期化する。ファームウェアは次の OTP リセットまでこのロックを厳しくできるが、緩めることはできない。実行時の変更はそのリセットをまたいで保持されない。
外部デバッガは Arm の Serial Wire Debug(SWD) インターフェースを通じて RP2350 と通信する。リクエストはまず Serial Wire Debug Port(SW-DP)に届き、そこからアクセスポートへルーティングされる。ここで使われる Cortex-M33 構成では、各コアがシステムバスに接続されたメモリアクセスポート(Mem-AP)を1つ持つ。有効化された Mem-AP は、許可されたメモリとペリフェラルへの読み書きをデバッガに許す。別途、常時オンのアクセスポート RP-AP が、リセットと復旧制御の小さな集合を公開している。
Secure debug とは、Secure 属性を伴う Mem-AP アクセスを指す。デバッガはアクセス制御ロジックが許す Secure メモリマップドリソースとトランザクションでき、Secure 状態で動作するコアを停止させて検査できる。
恒久的な CRIT1.DEBUG_DISABLE フラグはこの経路を閉じるためのものだ。これがセットされると、両方のコア Mem-AP のイネーブル信号をゼロに駆動し、「AP があらゆるバスアクセスを実行するのを防ぎ」、ファクトリテスト用 JTAG インターフェースと RISC-V デバッグモジュールのアクセスポートを無効化する。SW-DP と RP-AP は応答し続けるが、どちらのコア Mem-AP もシステムバスにアクセスできない。
ただし、オーバーライドが1つある。メモリマップドの DEBUGEN レジスタを使うと、Secure ソフトウェアはコアごとに Mem-AP を再有効化し、それを経由した Secure アクセスを個別に再有効化できる。データシートによれば、DEBUG_DISABLE は「このレジスタのすべてのビットをセットすることで完全にオーバーライドできる」。
この実行チェーン上の要となるオーバーライドこそ、私たちがデバッグインターフェースを標的にした理由である。Mem-AP 上の Secure debug アクセスを手に入れることは、汎用のプリミティブを手に入れることに等しい。Secure メモリの読み書き、カーネルの停止とシングルステップ実行、レジスタの閲覧ができる。このレジスタをフォールトでセットできるかどうかが、本稿の残りで答える問いである。
実験セットアップ
ターゲット構成
Raspberry Pi の RP2350 Hacking Challenge は、OTP1 に保存された128ビットの鍵を抽出することを参加者に求める。起動時、署名済みのチャレンジファームウェアはまずページ48が想定どおりの永続ロックを持つことを確認し、次に実行時ロックをかけて、次の OTP リセットまで Secure と Non-secure の両方のアクセスを拒否する。
私たちはベンダー定義のこの構成を、手元の A4 バージョンのチップで再現した。
- 公開鍵の SHA-256 フィンガープリントを
BOOTKEY0に書き込む BOOT_FLAGS1.KEY_VALIDを0x1に、BOOT_FLAGS1.KEY_INVALIDを0xeに設定する- セキュアブートを有効化(
CRIT1.SECURE_BOOT_ENABLE = 1) - デバッグを恒久的に無効化(
CRIT1.DEBUG_DISABLE = 1) - グリッチ検出器を最高感度で有効化(
CRIT1.GLITCH_DETECTOR_ENABLE = 1、CRIT1.GLITCH_DETECTOR_SENS = 3) - チャレンジ構成に従ってページ1とページ2の永続ロックを設定する
- ページ48の永続ロックを
PAGE48_LOCK1 = 0x3c3c3cに設定し、Non-secure アクセスを拒否しつつ(LOCK_NS = INACCESSIBLE)、Secure の読み書きアクセスを保持する(LOCK_S = READ_WRITE)
Secure Boot を有効にすると Cortex-M33 コアしか動作しないため、これらの実験では 2 つのプロセッサスロットがいずれも Arm を使用している。
サンプル作製と実験ベンチ
チップは裏面から開封されており、赤外光は表側の金属層に遮られることなくシリコン基板を透過してトランジスタに到達できる。その後チップはサブボードに実装し直され、そのサブボードは Scaffold に接続される。Scaffold は Ledger Donjon が被試験デバイスの駆動と監視に使うオープンソースプラットフォームだ。チップ裏面のリードフレームを除去すると GND 接続が切れるため、銅線 1 本で復旧させた2。

裏面から開封した RP2350 を解析用サブボードに実装した状態

攻撃に使用した実験ベンチ
DEBUGEN:恒久的なデバッグ無効化の回避
DEBUGEN には 5 つの機能ビットがある:
| ビット | 名称 | 役割 |
|---|---|---|
| 0 | PROC0 | コア 0 のメモリアクセスポートを有効化 |
| 1 | PROC0_SECURE | コア 0 のメモリアクセスポート経由のセキュアアクセスを許可 |
| 2 | PROC1 | コア 1 のメモリアクセスポートを有効化 |
| 3 | PROC1_SECURE | コア 1 のメモリアクセスポート経由のセキュアアクセスを許可 |
| 8 | MISC | クロストリガインターフェースや RISC-V デバッグアクセスポートなど追加のデバッグコンポーネントを有効化 |
コアで Secure debug を有効にするには 2 つのビットが同時に立っている必要がある。一方で Mem-AP を有効化し、もう一方でそれを経由した Secure アクセスを許可する。
OTP のセキュリティフィールドが冗長符号化を採用しているのとは異なり、データシートには DEBUGEN のビット冗長化、パリティ検査、多数決といった仕組みは記載されていない。
そこで我々は、上述のロック済みデバイス上でレーザーパルスによって DEBUGEN のビットをセットできるかを試した。
フォトンエミッションによる位置特定
このテストではまず、どこを撃つべきかを知る必要がある。単一の DEBUGEN ビットをセットするとは、あるレジスタビットの記憶素子に命中させるということであり、海から針を探すようなものだ。これはレーザー故障注入でよくある命令スキップ故障よりも位置特定が難しい。命令スキップでは、コアのパイプラインにある多数のフリップフロップのどれを乱しても同じスキップが得られるため、敏感な領域が十分に広く、ランダムスキャンでも見つかる。単一の DEBUGEN ビットを盲目的にスキャンするのは非現実的だ。
スイッチングトランジスタは微弱な近赤外フォトンを放出し、その量は自身の活動に依存する。したがって、繰り返し実行しながらこれらの放出を収集すれば、選んだ制御信号がどこで状態変化を起こしているかを明らかにできる。これによりフォトンエミッション顕微鏡(PEM)は DEBUGEN にうってつけとなる。メモリマップドレジスタであるため、Secure ソフトウェアはループ内で個々のビットを正確にトグルでき、測定に必要な繰り返しの状態変化を生成できる。我々はこれを第一段階の位置特定手段として使い、得られたマップによって以降のレーザースキャンを数マイクロメートルの範囲に絞り込んだ。
我々は、選んだ DEBUGEN ビットを繰り返しトグルするループ同士を比較した。これらのループは対象ビットだけが異なり、それ以外は完全に同一である。レジスタのフォトンエミッションはカメラ自体のノイズに対して非常に微弱で、温度などの緩やかなドリフトを伴う環境条件にも敏感なため、1 フレームの画像からは何も読み取れない。各ループの複数フレームを平均化してランダムなセンサノイズを抑え、さらに 2 つの平均スタックを減算することで、両ループに共通する成分をすべて相殺した。静的背景、センサバイアス、熱放射、そして選択したビットとは無関係なスイッチング活動である。取得中は 2 つの値を交互にサンプリングし、緩やかなドリフトが減算結果を偏らせないようにした。残るのは、選択したビットに追従するエミッションだけだ。

マスク 0x3 とマスク 0xc の全 200 回の取得を平均化したスタックと、両者の符号付き差分。赤は正で 0x3 のエミッションが強いことを示し、青は負で 0xc のエミッションが強いことを示す。下段の位置特定マップでは、ペアの差分を統合する前に取得順序のバランスも追加で取っている。
異なるビットマスク間で繰り返し比較したところ、カメラ視野の 3 つの領域に DEBUGEN ビット 0–3 に関連するコンパクトな部位が見つかった。

カメラ視野の赤外概観図。3 つの領域を示す。カラーピクセルは DEBUGEN ビット 0–3 に関連する部位を表す。
これらの領域は各 DEBUGEN ビットに関連するスイッチング活動を示しているが、記憶素子を直接指し示しているわけではない。各ビットについて複数のホットスポットが観測され、記憶素子に由来するものもあれば関連ロジックに由来するものもある。レイアウトデータがなければこの 2 つを区別できない。それでも、これらの領域は探索範囲を大幅に狭めた。
発見 1 —— DEBUGEN を誤らせれば Secure debug が得られる
レーザー故障注入(LFI)には 980 nm のパルスレーザーを使用し、最大光出力 2.97 W、実運用は約 40%(約 1.2 W)、パルス幅 100 ns、50x 対物レンズで集光する。各パルスの後、SWD 経由でデバッグアクセスポートを調べる。
PEM で見つけた領域内で LFI スキャンを実行し、SWD のフィードバックを利用して、数マイクロメートル離れた応答のある 2 つの位置を校正した。一方の位置では、パルスによって core 1 Mem-AP 経由のバスアクセスが開き、PROC1 がセットされたことがわかった。もう一方の位置では、Mem-AP の Control/Status Word が SDeviceEn = 1 を報告し、このステータス誘導信号は PROC1_SECURE がセットされた可能性が高いことを示していた。パルスごとにこの 2 つの指標を確認した。

左:DEBUGEN ビットに関連する PEM サイト。右:LFI 赤外線ビュー上のレーザー故障点。
1 つのパルスがあるビットをセットしながら別のビットをクリアすることがあるため、2 つのビットを同時にセットするには反復シーケンスが必要になる。スクリプトは PROC1 の位置にパルスを繰り返し当ててバスアクセスが可能になるまで待ち、次に PROC1_SECURE の位置に SDeviceEn = 1 までパルスを当て、バスアクセスを失うと最初の位置に戻る。位置とパルスパラメータを校正し終えると、このシーケンスは数秒で Secure debug を有効にする。興味深いことに、20x 対物レンズではこのシーケンスを再現できなかった。2 つの位置は数マイクロメートルしか離れておらず、より広いスポットがセット領域とクリア領域の両方に同時に当たった可能性が高く、正しい値を得られない。
一度セットされた 2 つのビットはセットされたままになり、パルスを当て続ける必要もソフトウェア書き込みも要らない。core 1 の Mem-AP 経由で Secure-only の DEBUGEN レジスタを読むと 0xc が返る。DEBUGEN は Secure-only なので、この読み出しが成功したことは、そのトランザクションが Secure としてマークされたことを意味する。
core 1 の Mem-AP 経由で Secure アクセスを有効にすると、デバッガはメモリマップドリソースを読み書きできるようになる。ただし、そのリソースの ACCESSCTRL 権限がデバッガをバスマネージャとして接続させることを許し、かつターゲット固有の制御が Secure AHB トランザクションを許可している場合に限る。こうした直接読み出し以外にも、デバッガはコアを停止させ、シングルステップ実行し、そのレジスタを検査または変更できる。つまり Secure コアを中介として TrustZone のランタイム分離を破れる。とはいえ boot ROM が未認証のファームウェアを受け入れるようになるわけではない。ファームウェアが正常に起動するとき secure boot は依然としてそれを認証するが、認証後にデバッガからアクセス可能なランタイム状態までは保護できない。
Hacking Challenge 構成への適用
上述の Secure 属性の Mem-AP アクセスは Secure ランタイム状態を露出させるが、チャレンジ 48 ページのランタイムロックはファームウェアの実行後、キーへのアクセスを依然として妨げる。このページの永続ロック PAGE48_LOCK1 = 0x3c3c3c は Non-secure 読み出しを拒否するが、LOCK_S を READ_WRITE のままにするので、ランタイムロックが厳しくなる前なら Secure 属性のアクセスで読める。
起動のたびに、ファームウェアは最も制限の厳しいバイナリ値 0b1111 をランタイムロック otp_hw->sw_lock[48] に書き込む。するとこのレジスタはこのページを Secure と Non-secure の両方のアクセスから遮断し、Secure debug も例外ではない。Mem-AP からの Secure トランザクションがキーを読むことはできなくなる。
ドキュメントにあるとおり、ソフトウェアロックは「リセット時に OTP ロックページから初期化され」、書き込みは状態を「次のリセットまで」しか進めない。OTP ブロックをリセットすると 0b1111 が破棄され、PAGE48_LOCK1 から導出された値に戻る。これに対して LOCK_S = READ_WRITE。
残る問題は、ファームウェアにランタイムロックを再適用させずに、ロック済みチップをどうリセットするかである。RP-AP は「外部デバッグが無効化されていても常にアクセス可能」なままである。CTRL.RESCUE_RESTART をセットすると rescue reset が発火する。システム全体のリセットであり、同時に boot ROM がユーザーソフトウェアの実行前に停止するようマークする。
boot ROM は watchdog、flash、USB からの起動より前に POWMAN_CHIP_RESET.RESCUE_FLAG を確認し、それをクリアしてから、core 0 を割り込み禁止の待機ループで、core 1 をその待機ベクタ経路で停止させる。3 データシートには CTRL.RESCUE_RESTART に対する制限は何も記載されていない。
手順は次のとおり:
- 救援リセット。
CTRL.RESCUE_RESTARTを1にセットし、RP-AP 経由で0にクリアする。チップはリセットされ、boot-ROM の待機経路にとどまる。署名済みファームウェアは決して実行されないので、sw_lock[48]が厳しくなることはなく、PAGE48_LOCK1から導出された緩い値——LOCK_S = READ_WRITE——のままになる。 DEBUGENを0xcとして故障注入する。 両方のコアが boot-ROM の待機経路にある状態で、上述のとおりPROC1とPROC1_SECUREをセットする。この 2 回のセットで値0xcが得られる。- core 1 を停止する。そのデバッグ一時停止制御・ステータスレジスタ(
DHCSR)を使い、今や Secure になった Mem-AP 上で実行する。 - キーを読み出す。OTP 行
0xc08–0xc0fから保護された読み出しインターフェース経由で。
このシーケンスを実機で実行し、チャレンジキー全体を復元した。
DEBUGEN_LOCK はレーザーによる変化を防げない
DEBUGEN_LOCK は対応する DEBUGEN ビットへのソフトウェア書き込みを防ぐ。各ロックビットは「DEBUGEN の […] ビットをロックするには 1 を書き込む。一度セットするとクリアできない」ものである。データシートはこれを「意図しない書き込み」を避ける方法として説明している。
ターゲットの DEBUGEN ビットが 0 でそのロックビットが 1 の試行では、パルスは依然として DEBUGEN をセットでき、ロックビットは 1 のままだった。パルスは対応する DEBUGEN の変化の有無にかかわらずロックビットもセットする。成功したシーケンスでは、PROC1 と PROC1_SECURE がどちらもセットされたとき、5 つの機能ロックビットはすべてすでに 1 だった。ロックビットが 1 から 0 に戻るのを見たことは一度もなく、故障が対応するロックビットをセットしてしまえば、その後の DEBUGEN = 0 への書き込みで無効状態を復元することはできない。
ソフトウェアによる緩和策の限界
Mem-AP 経由でセキュアアクセスを有効にすると、セキュア属性だけではデバッガとセキュアファームウェアを区別できなくなる。このアクセスはハードウェア OTP ロックやペリフェラル固有の制御を上書きしない。ACCESSCTRL はデバッガマネージャから特定ターゲットへの直接トランザクションを止められるが、セキュアコアを制御するデバッガがコア起点のアクセスを発生させたり、コアレジスタ経由でロード済みの値を読み出したりすること自体は防げない。レスキューリセット後、ACCESSCTRL はファームウェアが再設定するまでの間、リセット時のデフォルトである全開放状態に戻る。つまり ACCESSCTRL が減らすのは直接的な Mem-AP の露出であって、独立した機密性の境界にはならない。
それでもファームウェアは、ACCESSCTRL でデバッガから機密ターゲットへのアクセスを拒否し、続いて ACCESSCTRL.LOCK にデバッガビットを設定することで、デバッガのトランザクションでその権限を再び開けなくし、起動後の露出を減らすことができる。セキュアファームウェアが DEBUGEN を定期的に確認し、想定外の値ならフェイルセーフリセットを発動してプロセッサのコールドリセットドメインをクリアする、という手もある。いずれもベストエフォートの実行時緩和策だ。有効化されたデバッガは次のチェックまでの間にコアを停止させられるし、レスキューリセットはファームウェアが ACCESSCTRL を設定する前、あるいは監視プログラムが動き出す前に止まってしまう。したがって、ここで示したファームウェア前の鍵読み出しは防げない。
RP2350 のドキュメントに記載された暗号化ブートの流れは、実行時ロックがファームウェア前の段階で抱える限界と、デバッガマネージャのフィルタリングが起動後の段階で抱える限界を、それぞれ別のマシン状態として説明している。レスキューリセット後、boot ROM は復号の前で止まる。この時点で平文ペイロードはまだ存在しないが、OTP ページの永続権限がセキュアアクセスを許し、そのトランザクションを妨げる他のターゲット制御がなければ、復号鍵を直接読めてしまう可能性がある。通常の暗号化ブート後は、SRAM 上に平文が存在する。直接の Mem-AP 読み出しは ACCESSCTRL のデバッガマネージャ権限次第であり、直接読み出しが拒否されても、セキュアコアの制御次第ではコアを介した抽出が可能になりうる。これはアーキテクチャ上の分析であって、暗号化ブートを実際にテストした結果ではない。暗号化ブートは今も外部フラッシュをオフライン検査から保護している。
影響と攻撃条件
ここで示した攻撃シーケンスは、Secure に帰属するメモリへのアクセス、Secure ワールドでの実行制御、そしてリセットがチャレンジプログラムの実行中にページロックを破った後のチャレンジ鍵へのアクセスを可能にする。必要なものは次のとおり。
- 破壊的な物理アクセス。 裏面の開蓋はパッケージを永久に変形させ、ダイを露出させる。
- 専用の実験設備。 上述の一式で約 25 万ドル。
- ハードウェアセキュリティの専門知識。 サンプル作製、ダイのナビゲーション、レーザーパラメータの選定、そしてレーザー制御・ステージ位置決め・SWD 測定の調整が必要になる。
結論
RP2350 は重要なデバッグ無効化フラグを OTP に冗長投票で符号化しているが、DEBUGEN はその効果を上書きでき、データシートには同等の保護が何も記載されていない。私たちの実験では、レーザーパルスが DEBUGEN_LOCK の状態で DEBUGEN を変化させ、ファームウェアが無効化された値を復元できないロックビットを設定できた。また、RP-AP のレスキューリセットはチャレンジプログラムの実行時ページロックを永続値に戻し、同時にユーザーファームウェアの実行を止める。これらのソフトウェアから見える機構はどれも、ドキュメントに書かれたとおりに機能している。問題はレーザー故障との相互作用が Secure デバッグを開き、チャレンジ鍵を復元してしまうことだ。差分 PEM がまずビット相関のある DEBUGEN の活動を突き止め、ガイド付き LFI がその空間的な手がかりを永続的な Secure デバッグに変える。システムレベルでの教訓はこうだ。セキュリティ分析は、永続的な OTP 設定から可変の制御レジスタ、そしてリセット時の挙動まで、実行パス全体をカバーしなければならない。システムのセキュリティは個々の機構が単独で決めるのではなく、このパス全体で決まるからだ。
開示と謝辞
私たちは 2026 年 7 月 28 日にこの故障を Raspberry Pi に開示した。開示に関する議論に応じてくださった Raspberry Pi チームと、セキュリティ研究に対する彼らの透明な姿勢に感謝する。
Antoine Plin、Ledger Donjon ハードウェアセキュリティインターン
脚注
- https://github.com/raspberrypi/rp2350_hacking_challenge RP2350 Hacking Challenge リポジトリ。私たちが再現時に参照した lockdown 設定とファームウェアが含まれる。 ↩
- Courk、Laser Fault Injection on a Budget: RP2350 Edition。 ↩
- レスキュー検査は
src/main/arm/varm_boot_path.cの core 0 起動パスのステップ 1 にある。src/main/arm/arm8_bootrom_rt0.Sではvarm_wait_rescueが割り込み禁止のvarm_dead_quietWFI ループに入り、core 1 は boot ROM の待機ベクタパスに留まる。 ↩