課題
ネットワークに接続する機器のほとんどは、ファームウェアの署名、TLSセッションの認証、保存データの暗号化のいずれかのために秘密鍵を保持しています。その鍵を攻撃者の手から守ることが難しく、しかもこれは小型の機器に限った問題ではありません。オンチップまたは外付けのフラッシュに保存すれば、JTAG、露出したデバッグポート、フラッシュバスへのアクセス手段のいずれかを持つ者に読み出されてしまいます。eFuseに焼き込んでも、ダイを開封すれば復元される可能性が残ります。セキュアエレメントやTPMを使えばこの問題は解決しますが、コスト、基板面積、そして認定すべき調達先がもう1つ増えます。そして規模の大小を問わず、それらを搭載せずに出荷される設計は数多くあります。
wolfCryptに新たに追加したSRAM PUF対応は、まさにそうした設計のためのソフトウェアによる選択肢です。チップの電源投入直後にRAMブロックが保持しているランダムな値から、デバイス固有の鍵を構成します。この起動時の値はチップごとに異なる一方で、同一のチップでは繰り返し同じ値が現れます。そのため誤り訂正と組み合わせることで、wolfCryptは起動のたびに同一の鍵を再構築します。フラッシュにもヒューズにも何も書き込みません。鍵はRAM上に、それも使用している間だけ存在します。
仕組み
SRAMは電源が入ると、各セルが製造上の微細なばらつきに応じて0または1に落ち着きます。このばらつきは制御することもクローンすることもできません。結果として現れるパターンはチップ間ではランダムでありながら、同一のチップでは再現性を持ちます。いわばシリコンの指紋です。ただしわずかにノイズを含み、起動のたびに数ビットが反転するため、生のビット列をそのまま鍵として使うことはできません。
wolfCryptではBCH(127,64,t=10)のファジーエクストラクタでこれを整えます。16個の符号語それぞれが最大10ビットの反転を訂正します。続いてHKDF-SHA256により256ビットのデバイス鍵を生成します。処理は次の2つのフェーズに分かれます。
- 登録はプロビジョニング時に一度だけ実行します。wc_PufEnrollがSRAMを読み取り、公開してよいヘルパーデータとデバイス識別情報を生成します。ヘルパーデータはそのままフラッシュに保存して構いません。鍵に関する情報は一切含まず、ノイズを含む将来の読み取り値を登録時の値に引き戻す方法を誤り訂正器に伝えるだけです
- 再構成は起動のたびに実行します。wc_PufReconstructが保存済みのヘルパーデータを使って全く同じ安定したビット列を復元し、wc_PufDeriveKeyが鍵を再生成します
鍵は使用される瞬間だけ存在し、その後wc_PufZeroizeが消去します。フラッシュを取り外しても、そこにあるのは公開のヘルパーデータだけです。基板の電源を切れば、SRAMは単なるランダムなノイズに戻ります。取り出せるものは何もありません。
使い方
有効化するには、–enable-puf(HKDFも自動的に取り込まれます)、CMakeではWOLFSSL_PUF=yes、あるいはuser_settings.hへのWOLFSSL_PUFの定義を使用します。必要なのは既知のアドレスに置かれた未初期化のSRAMブロックだけで、NOLOADのリンカ領域として確保します。そのためベンダー固有の部品を必要とせず、事実上どのMCUでも動作します。この機能全体で増えるサイズはおよそ13KBです。さらにホスト/合成モードを用意しているため、ハードウェアなしでもCI上で開発とテストを行えます。
#include <wolfssl/wolfcrypt/puf.h>
wc_PufCtx ctx;
byte helper[WC_PUF_HELPER_BYTES]; /* 公開情報。フラッシュに保存する */
byte key[WC_PUF_KEY_SZ]; /* 導出される32バイトの鍵 */
/* プロビジョニング時に一度だけ登録する */
wc_PufInit(&ctx);
wc_PufReadSram(&ctx, (const byte*)PUF_SRAM_ADDR, WC_PUF_RAW_BYTES);
wc_PufEnroll(&ctx);
/* ctx.helperData を以降の起動のためにフラッシュへ保存する */
/* 起動のたびに再構成して鍵を導出する */
wc_PufInit(&ctx);
wc_PufReadSram(&ctx, (const byte*)PUF_SRAM_ADDR, WC_PUF_RAW_BYTES);
wc_PufReconstruct(&ctx, helper, sizeof(helper));
wc_PufDeriveKey(&ctx, (const byte*)"tls-identity", 12, key, sizeof(key));
/* ... 鍵を使用する ... */
wc_PufZeroize(&ctx);
ForceZero(key, sizeof(key));
wc_PufDeriveKeyは呼び出し側がラベルを指定するHKDFであるため、1つのPUFから独立した用途別の鍵をいくつも導出できます。TLSの識別用の鍵、ファームウェア復号用の鍵、保存データ用の鍵といった具合に、追加のストレージは必要ありません。ベアメタルの完全なサンプルはwolfssl-examplesリポジトリにあります。STM32H5(NUCLEO-H563ZI)で動作を確認しており、Xilinx Zynq-7000 をはじめとする他のプラットフォームでの検証も進めています。
詳細は以下をご覧ください。
- サンプルと解説(ビルド、書き込み、実機での実行):https://github.com/wolfSSL/wolfssl-examples/tree/master/puf
- PUF API リファレンス(wc_PufEnroll、wc_PufReconstruct、wc_PufDeriveKeyなど):https://github.com/wolfSSL/wolfssl/blob/master/wolfssl/wolfcrypt/puf.h
- マージ済みのプルリクエスト:wolfssl #10066、wolfssl-examples #565
もし弊社製品についてご質問があれば、お問い合わせ窓口info@wolfssl.jpまでご連絡をお願い致します。
原文:https://www.wolfssl.com/wolfcrypt-now-supports-sram-puf-for-device-unique-keys/
