まずはこちらの動画をご覧ください。
VIDEO youtu.be
棒状のモノをSTM32マイコン に押し当てたら緑色のLEDが点灯しましたね。何が起きているのかというと、この棒の先端から電磁波が照射されていて、電磁波によって実行中のプログラムに影響が出るようなエラー(fault)を検知したらLEDが点灯するようになっています。このように電磁波をハードウェアに照射してエラーを誘発することをElectromagnetic Fault Injection (EMFI)と呼びます(ちなみにFault injectionはよく故障注入と日本語訳されるんですが、ダサいのでぼくは好きじゃないです。Fault injectionはFault injectionです)。
さて、今回はタイトルにあるようにNewAE社のPico EMPを使ったEMFI実験を行います。本当は暗号実装に対するEMFIを行いたかったんですが、ターゲットの実装と照射タイミング・場所の特定が面倒 大変なのでサンプルコードを動かす程度になります。つまり冒頭の動画で見せたようなLEDチカチカですね。
使用機器:
Pico EMP ChipSHOUTER PicoEMP
9月上旬にカナダで開催されたCHES 2024に参加したときに、NewAE社のデモブースの懸賞に当たり無料でいただきました。通販で$60で購入できます。もっと使い勝手の良いデバイス もありますが(NAE-CW520 )、こちらは60万円ほどするのでPico EMPのほうがお手頃です。
STM32VLDISCOVERY
本実験のターゲットデバイス です。大学近くのマルツで買いました。2,500円ほど。バイナリ書き込み用にUSB A-to-mini Bをアマゾンで買いました。1,000円くらい。
実験を行うにはターゲットデバイス とPico EMPにそれぞれプログラムを書き込む必要があるのですが、Pico EMPのほうには購入時点で既にファームウェア が書き込まれています。付属の電池をボードのソケットに接続して電源スイッチをONにするだけで電磁波照射の準備ができます。パルス幅の変更や照射タイミングを自分で設定したいなど、ファームウェア を修正する場合は公式からコードが提供されている*1 のでそちらを利用するといいでしょう。
次にターゲットデバイス に書き込むプログラムですが、こちらもサンプルが公式から提供されているので少し修正して書き込みます。
github.com
実際のプログラムが以下です。main.cに記述します。GPIOピンについては、STM32CubeIDE上でピン設定をしてからGenerate codeを実行すると自動でmain.hに記述されます。PC8, PC9ピンにそれぞれLED1, LED2というラベルを付けました。
#define RUN_CNT 2000
#define OUTER_LOOP_CNT 400
#define INNER_LOOP_CNT 400
void glitch_loop(void)
{
volatile uint32_t i, j;
volatile uint32_t cnt;
uint32_t blink_status = 1;
uint32_t run_cnt = 0;
for(run_cnt = 0; run_cnt < RUN_CNT; run_cnt++){
// LED1をblinkさせる(動作中かどうかわからないとき、人間は不安になる。)
HAL_GPIO_WritePin(LED1_GPIO_Port, LED1_Pin, blink_status);
blink_status ^= 1;
cnt = 0;
for(i = 0; i < OUTER_LOOP_CNT; i++) {
for(j=0; j < INNER_LOOP_CNT; j++){
cnt++;
}
} // No errorのとき、cnt == OUTER_LOOP_CNT * INNER_LOOP_CNT
//look for glitch
if (i != OUTER_LOOP_CNT || j != INNER_LOOP_CNT ||
cnt != (OUTER_LOOP_CNT * INNER_LOOP_CNT) ) {
// Glitchが発生したときにLED2を点灯させる
HAL_GPIO_WritePin(LED2_GPIO_Port, LED2_Pin, GPIO_PIN_SET);
HAL_Delay(2000);
HAL_GPIO_WritePin(LED2_GPIO_Port, LED2_Pin, GPIO_PIN_RESET);
run_cnt = 0;
}
}
}
int main(void) {
/* Reset of all peripherals, Initializes the Flash interface and the Systick. */
HAL_Init();
/* Configure the system clock */
SystemClock_Config();
/* Initialize all configured peripherals */
MX_GPIO_Init();
/* Infinite loop */
while (1) {
glitch_loop(); // main関数内部で新たに記述したのはここだけ
}
}
エラー(glitch)がOUTER_LOOP, INNER_LOOP内部で発生することを想定しています。具体的なエラーとは、電磁波照射によって命令がスキップされると思ってください。
エラーが発生していないとき、ループ用カウンタi, jはそれぞれOUTER_LOOP_CNT, INNER_LOOP_CNTと等しく、またcntとOUTER_LOOP_CNT * INNER_LOOP_CNTも等しくなるはずです。一方で、命令スキップが一回でも発生すると、少なくともcnt==OUTER_LOOP_CNT * INNER_LOOP_CNTは成り立たなくなりますよね。ということでLED2が2秒間点灯します。
いかがでしたか? この実験においてはEMを照射する場所(とタイミング)がとても重要で、動画でやっているように手当たり次第撃ち込んでいます。大規模なEMFI実験を行う際は固定台などを用います。
有名なfalt injection攻撃としては中国の剰余定理を利用したRSA 実装への攻撃例があるので、興味がある方は調べてみてください。NewAE社のブースではRSA への攻撃デモを見せてくれました。
宣伝:
本記事執筆にあたり参考にしたもの。感謝します。
[電子書籍版]MacではじめるSTM32第二版 - kotetsu-yama - BOOTH
STM32CubeIDEでHEXファイルを作成する方法 – ゆっきぶろぐ
【STM32備忘録】GPIOの設定方法とビルドと書き込み - ハサミの技術備忘録