■動機
以前、Jetson NanoでSDカード暗号化のテストを実施した。
2023年現在のRaspberry Piのセキュリティ対策方法を確認するため検証した。
■目指した状態
- Raspberry PiのSDカードを暗号化し、SDカードをコピーされて内容を見られることを防ぎたい。
- 暗号化をLUKS+cryptsetupを利用して行うが、手動でLUKS鍵を入力するのではなく、自動で鍵を入力させたい。
- 鍵入力を自動で行うが、LUKS鍵データをそのままフラッシュに配置しないようにする。
- 複数台を展開した際に、LUKS鍵データが共通にならないようにしたい。
■セキュアエレメントについて
以下は、ChatGPTにまとめてもらった内容となる。
ATECC608の主な特徴:
楕円曲線暗号(ECC): ATECC608は、楕円曲線Diffie-Hellman(ECDH)や楕円曲線デジタル署名アルゴリズム(ECDSA)など、ECCに基づく暗号化操作を高速に実行できます。
ハードウェアキーストレージ: デバイスには、プライベートキー、証明書、またはその他のセキュアデータのセキュアなストレージとして使用されるセキュアなスロットが複数含まれています。
物理的耐タンパ性: デバイスは、物理的または電気的な侵入に対して耐性があります。
セキュアブートサポート: ATECC608は、システムのセキュアブートプロセスをサポートし、起動時にファームウェアの整合性を確認するのに役立ちます。
ランダム数ジェネレータ: 高品質の真のランダム数ジェネレータ(TRNG)を搭載しています。
I2C/SWIインターフェース: 標準の通信プロトコルを使用してホストマイクロコントローラとの通信が可能です。
小さなフットプリント: 超小型のパッケージングオプションが利用可能で、様々なアプリケーションに組み込むことができます。
消費電力が低い: IoTデバイスやバッテリー駆動のアプリケーションに適しています。
これらの特徴により、ATECC608は、IoTデバイスやその他のアプリケーションでのセキュアなデータ保護、デジタル署名、暗号化通信の確保などに使用されます。
■実装
鍵保護のため、ATECC608Bセキュアエレメントを使用して以下のように考えてみた。
- Raspberry PiのSDカードを暗号化し、SDカードをコピーされて内容を見られることを防ぎたい。
- 暗号化をLUKS+cryptsetupを利用して行うが、手動でLUKS鍵を入力するのではなく、自動で鍵を入力させたい。
→ 自動で鍵を入力させ、マウントまで行うためにinitramfsとcryptsetupを利用する。
また、ATECC608Bにアクセスするための実行ファイルをビルドし、組み込む。
コードはGithubに配置した。
apt updateが走ると、initramfsの作り直しが起こり、設定が飛んでしまうことを防ぎたい。
- 鍵入力を自動で行うが、LUKS鍵データをそのままフラッシュに配置しないようにする。
- 複数台を展開した際に、LUKS鍵データが共通にならないようにしたい。
→ この要件は、ATECC608Bへ乱数でAES鍵を作成し保存、その鍵へハッシュを入力し、出力をそのまま鍵として使う方法を考えた。
ハッシュの材料は、Raspberry Piボードが固有で持っているシリアル番号をSHA-256に通して得ることとし、複数台を展開しても鍵が同一となることを避ける。
これにより、特定のRaspberry Piボード、SDカード、ATECC608Bのセットがペアリングされる。
マスターイメージとしてrootfs.imgを作成し、初期鍵を設定するが、実機に展開して1回目の起動時に
- 初期鍵を使ってアクセスし、ATECC608Bを通った鍵を登録
- 初期鍵を削除
上記の動作を行うことで、ATEC608Bを通った鍵のみが登録された状態を作る。
■環境:
Raspberry pi 4 4GB 1台(以下Raspberry Pi)
ATECC608B-SSHDA(i2c接続タイプ)(以下ATECC608B)
128GB MicroSDカード
USB-MicroSDカード変換アダプター
Ubuntu22.04のインストールされたPC(以下Ubuntu PC)
■デバイスセットアップ
Raspberry PiとATECC608Bセキュアエレメントは以下のように接続した。
■イメージ作成
Ubuntu PCでRaspberry pi imagerを使い、USB-MicroSDカード変換アダプターを使ってMicroSDカードへ64ビットのOSを書き込む。
書き込み後、Raspberry Piで起動させずにそのままUbuntu PCにUSB-MicroSDカード変換アダプターを差し込み直すと、/media/<ユーザー>/bootfs /media/<ユーザー>/rootfsとしてマウントされる。
■暗号化されたrootfsを作る
Ubuntu PC上で、以下を実行する。
$ dd if=/dev/zero of=rootfs.img bs=1M count=0 seek=4000
$ sudo losetup -f rootfs.img
$ losetup -l
NAME SIZELIMIT OFFSET AUTOCLEAR RO BACK-FILE DIO LOG-SEC
/dev/loop17 #rootfs.imgがマウントされたloop<番号>を記憶しておく
0 0 0 0 /home/<ユーザー>/rootfs.img
$ sudo cryptsetup luksFormat /dev/loop17 #rootfs.imgがマウントされたloop<番号>
#鍵登録をする際、初期鍵として"kmwebnet"と打ち込む
$ sudo cryptsetup luksOpen /dev/loop17 luks #rootfs.imgがマウントされたloop<番号>
$ sudo mkdir /media/luks
$ sudo mkfs.ext4 /dev/mapper/luks
$ sudo mount /dev/mapper/luks /media/luks
$ sudo rsync -axHAWX --numeric-ids --info=progress2 /media/<ユーザー>/rootfs/ /media/luks
#luksの中身のfstabを以下に書き換える。
$ sudo vi /media/<ユーザー>/rootfs/etc/fstab
proc /proc proc defaults 0 0
/dev/mmcblk0p1 /boot vfat defaults 0 2
/dev/mapper/luks / ext4 defaults,noatime 0 2
#後片付け
$ sudo umount /media/luks
$ sudo cryptsetup luksClose luks
#設定確認
$ sudo cryptsetup luksDump /dev/loop17 #rootfs.imgがマウントされたloop<番号>
LUKS header information
Version: 2
Epoch: 3
Metadata area: 16384 [bytes]
Keyslots area: 16744448 [bytes]
UUID: bd9613a6-d322-414d-91de-9fae5acec5f5
Label: (no label)
Subsystem: (no subsystem)
Flags: (no flags)
Data segments:
0: crypt
offset: 16777216 [bytes]
length: (whole device)
cipher: aes-xts-plain64
sector: 512 [bytes]
Keyslots:
0: luks2
Key: 512 bits
Priority: normal
Cipher: aes-xts-plain64
Cipher key: 512 bits
PBKDF: argon2id
Time cost: 6
Memory: 1048576
Threads: 4
Salt: c3 df f3 b0 9f f2 93 b9 d5 7a e7 0f 27 7a 14 ce
14 6f ed b8 23 ba 52 60 69 45 82 95 f5 5c 1a 5a
AF stripes: 4000
AF hash: sha256
Area offset:32768 [bytes]
Area length:258048 [bytes]
Digest ID: 0
Tokens:
Digests:
0: pbkdf2
Hash: sha256
Iterations: 333516
Salt: 83 11 81 9f 35 44 3b 36 2f 82 05 a6 0f e2 d0 2a
08 08 bb b5 1a eb 99 49 32 da 5d 62 41 e1 d2 8b
Digest: fb 6f 18 b3 d4 0f 0f d7 d5 35 42 86 ff e1 1c fa
63 be ce 09 5f 1a c3 57 66 38 54 04 8c 28 5c 44
$ sudo losetup -d /dev/loop17 // rootfs.imgがマウントされたloop<番号>
■initramfs作成
SDカードをRaspberry Piにさして、起動させる。初回セットアップ後、以下を実行
$ sudo apt install libssl-dev cryptsetup
$ sudo raspi-config #raspi-configでi2cを有効にする。
$ git clone https://github.com/kmwebnet/cryptsetup-pi4.git
$ cd cryptsetup-pi4/
$ sudo ./intramfssetup.sh
$ sudo shutdown -h now
SDカードを取り外し、Ubuntu PCにUSB変換で接続すると勝手にマウントされるのでマウント解除。
$ fdisk -l
Disk /dev/sdb: 115.31 GiB, 123815854080 bytes, 241827840 sectors
Disk model: SD/MMC
Units: sectors of 1 * 512 = 512 bytes
Sector size (logical/physical): 512 bytes / 512 bytes
I/O size (minimum/optimal): 512 bytes / 512 bytes
Disklabel type: dos
Disk identifier: 0x95789778
Device Boot Start End Sectors Size Id Type
/dev/sdb1 8192 532479 524288 256M c W95 FAT32 (LBA)
/dev/sdb2 532480 241827839 241295360 115.1G 83 Linux
$ sudo dd if=/dev/sdb1 of=/home/<ユーザー>/bootpart.img
524288+0 records in
524288+0 records out
268435456 bytes (268 MB, 256 MiB) copied, 18.9553 s, 14.2 MB/s
これでマスターイメージが出来上がった。
■展開
別のSDカードを用意しUbuntu PCからクローン。省略してあるがパーティションは設定しておく必要がある。
$ sudo dd if=/home/<ユーザー>/bootpart.img of=/dev/sdb1
524288+0 records in
524288+0 records out
268435456 bytes (268 MB, 256 MiB) copied, 137.626 s, 2.0 MB/s
km@km-vmware:~$ sudo dd if=/home/<ユーザー>/rootfs.img of=/dev/sdb2 bs=4M status=progress
4194304000 bytes (4.2 GB, 3.9 GiB) copied, 176 s, 23.8 MB/s
1000+0 records in
1000+0 records out
4194304000 bytes (4.2 GB, 3.9 GiB) copied, 387.595 s, 10.8 MB/s
実機にて起動。初回起動はLUKS鍵の入れ替え作業が入り、時間がかかる。
$ df -h
ファイルシス サイズ 使用 残り 使用% マウント位置
udev 1.6G 0 1.6G 0% /dev
tmpfs 380M 1.3M 379M 1% /run
/dev/mapper/luks 117G 3.4G 109G 3% /
tmpfs 1.9G 0 1.9G 0% /dev/shm
tmpfs 5.0M 4.0K 5.0M 1% /run/lock
/dev/mmcblk0p1 255M 47M 209M 19% /boot
tmpfs 380M 20K 380M 1% /run/user/1000
ルートファイルシステムが/dev/mapper/luksとなっており成功。
自動的にパーティションの全体を使う形となっている。
実機で起動した後、以下を実行。
$ sudo apt install libssl-dev cryptsetup
$ sudo raspi-config #raspi-configでi2cを有効にする。
$ git clone https://github.com/kmwebnet/cryptsetup-pi4.git
$ cd cryptsetup-pi4/
$ sudo ./intramfssetup.sh
これでカーネルアップデートなどの、initramfsの更新時にも追従できる状態になった。
■展望
完全な信頼の連鎖(Chain of Trust)を実現するには、ブート開始時からのフラッシュ暗号化+セキュアブートが必要となるが、Raspberry PiではRaspberry Pi 4からブート領域のフラッシュ暗号化は対応していないものの、セキュアブートに対応しているようだ。
今回のテストから、さらにセキュアブートにつなげられるか試行したい。