ArchLinux にSecure Boot を導入する

# Secure Boot について
Secure Boot とはOS 起動時にそのローダーがマシン(ファームウェア)にとって信頼のある署名がされている場合のみローダーを実行し、信頼出来ない不正なローダーは実行はされないようにする機能のことです。
この機能を使用することにより、知らないうちにあなたが使用しているPC のUSB やHDD、SSD に悪意のあるOS イメージが入ったものが装着されていた場合にそれを起動してしまうのを防いだり、前回OS 起動時にローダが書き換えられて悪意のあるイメージを起動させようとした攻撃に対処することができます。

今回はSecure Boot を導入するにあたって、Think Pad T460s を使用しました。

# Boot 時の概略図
Secure Boot は以下のコンポーネントから構成されます。
![Linux_SecureBoot0001.png](https://qiita-image-store.s3.amazonaws.com/0/70152/a10b5a68-6956-725f-8f3b-5a69940f3a35.png)


* Secure boot を構成する秘密鍵一覧
<table><tbody>
  <tr>
    <th> 秘密鍵</th>
    <th> 役割</th>
  </tr>
  <tr>
    <td> Platform Key (PK)</td>
    <td> db とdbx を編集するための鍵。一般的にはハードウェアベンダから提供(OEM)される。</td>
  </tr>
  <tr>
    <td> Key Exchange Key (KEK)</td>
    <td> PK とKEK を編集するための鍵。</td>
  </tr>
  <tr>
    <td> Authorized DB (db)</td>
    <td> 信頼のおけるbootloader の検証を行うための証明書とハッシュ値。複数もありうる。例えばMicrosoft Windows Production CA など。</td>
  </tr>
  <tr>
    <td> Unauthorized DB (dbx)</td>
    <td> 不適切なbootloader を検証するための証明書とハッシュ。複数もありうる。一般的に呼ばれるCertificate Revocation List(CRL) と似たもの。</td>
  </tr>
</tbody></table>

Secure boot がbootloader を検証して安全にOS を起動するまでの処理シーケンスは次のように図示することができます。
下図のboot シーケンスは既にPK を使用してdb 及びdbx の設定が完了していることが前提になります。
![Linux_SecureBoot0000.png](https://qiita-image-store.s3.amazonaws.com/0/70152/73ff4b93-f017-e5e5-3ce4-b96bd259cb52.png)

- Trying out UEFI boot security on a recent Linux system
    - http://www.linux-magazine.com/index.php/layout/set/print/Issues/2014/164/The-State-of-Secure-Boot/(tagID)/154

## shim
shim は自己CA 証明書を格納したfirst stage boot loader でで、GRUB 2 やELILO などのboot loader をロードすることが役割です(今回はGRUB 2 を使用しますので、GRUB 2 についてのみ記述します)。
このCA 証明書はGRUB 2 boot loader の署名を検証するために使用されます。
そしてGRUB 2 は後ほどkernel の署名を検証するためにshim の処理をcall back します。

TODO: 今回実施したArch Linux でSecure boot を実現する方法では、shim を使用しているのか否か、もしくは他のpreloader を 使っているのかどうか確認することができませんでした。情報求む...。

# Secure boot 環境構築
secure boot の概要を説明したので、ここからはArch Linux でsecure boot を行うための手順を説明していきます。

## パッケージのインストール
secure boot を実現するためにArch Linux で必要なパッケージをインストールします。


```console:必要なパッケージのインストール
# pacman -S sbsigntools
# pacman -S openssl
```


## 署名用の鍵の作成
`openssl` コマンドを使用して`PK`, `KEK`, `db` に署名を行うための鍵を生成します。


```console:openssl(RSA)
# openssl genrsa -out my-arch-linux.key 2048
# openssl req -new -x509 -sha256 -subj '/CN=my-arch-linux' -key my-arch-linux.key -out my-arch-linux.pem
# openssl x509 -in my-arch-linux.pem -inform PEM -out my-arch-linux.der -outform DER
```


`uuidgen` を使用して鍵の所有者を表すためのGUID を作成します。

```console
# guid=$(uuidgen)
# echo $guid
```


次に`EFI_SIGNATURE_LIST` (署名リスト)を作成していきましょう。
ここで登録される`my-arch-linux.der` の署名は自己署名となります。

```console
# sbsiglist --owner $guid --type x509 --output my-arch-linux.der.siglist my-arch-linux.der
```


`sbvarsign` コマンドを使用して各鍵情報、変数とその値に対して署名を行っていきます。


```bash:PKKEKdbのsignedupdateを作成する
for n in PK KEK db
do
    sbvarsign --key my-arch-linux.key --cert my-arch-linux.pem --output my-arch-linux.der.siglist.$n.signed $n my-arch-linux.der.siglist
done
```


次にkeystore を作成していきます。


```console
# mkdir -p /etc/secureboot/keys/{PK,KEK,db,dbx}
# cp *.PK.signed /etc/secureboot/keys/PK/
# cp *.KEK.signed /etc/secureboot/keys/KEK/
# cp *.db.signed /etc/secureboot/keys/db/
```


`sbkeysync` コマンドを使用してfirmware の鍵データベースと情報を同期させます。
上記で`/etc/secureboot/keys` ディレクトリ配下に鍵を作成しましたが、これ以外のディレクトリを使用したい場合は`sbkeysync` コマンドのオプションに`--keystore` and/or `--no-default-keystores` オプションを加えて実行してください。

まずは`--dry-run` オプションを付けて、テストモードで実行してみましょう。


```console:sbkeysync(dry-run)
# sbkeysync --verbose --pk --dry-run
```


結果に問題無いようであれば、今度は`--dry-run` オプションを外してコマンドを実行します(firemware 上の鍵DB が更新されま すので、覚悟を決めてください…)。
ここで注意していただきたいのは、firmware によってはこのコマンドを実行することで、自動的に`set up モード`から`secure boot 強制モード`へ移行してしまうものがあるそうです。
そのため、やっぱりsecure boot をoff にしたい場合の保険の意味も込めて、事前にお使いのコンピュータのfirmware の設定画面からsecure boot をOFF にする方法やPK を削除する方法があることを確認しておいてください。


```console:sbkeysync
# sbkeysync --verbose --pk

... 以下のようなエラーが出た ...
New keys in filesystem:
 /etc/secureboot/keys/db/my-arch-linux.der.siglist.db.signed
 /etc/secureboot/keys/KEK/my-arch-linux.der.siglist.KEK.signed
 /etc/secureboot/keys/PK/my-arch-linux.der.siglist.PK.signed
Inserting key update /etc/secureboot/keys/db/my-arch-linux.der.siglist.db.signed into db
Can't create key file /sys/firmware/efi/efivars/db-d719b2cb-3d3a-4596-a3bc-dad00e67656f: Permission denied
Error syncing keystore file /etc/secureboot/keys/db/my-arch-linux.der.siglist.db.signed
Inserting key update /etc/secureboot/keys/KEK/my-arch-linux.der.siglist.KEK.signed into KEK
Can't create key file /sys/firmware/efi/efivars/KEK-8be4df61-93ca-11d2-aa0d-00e098032b8c: Permission denied
Error syncing keystore file /etc/secureboot/keys/KEK/my-arch-linux.der.siglist.KEK.signed
```


`--dry-run` モードで成功しても実際に同期しようとすると失敗しました。
原因はUEFI のsecure boot モードが`setup モード`になっていないためでした。
お使いのマシンによってエラーになったり、ならなかったりは異なってくると思いますので、次の対応はあくまで参考程度にとど めておいてください。

secure boot モードを`setup モード`へ変更するために、一旦マシンを再起動し、Lenovo セットアップ画面メニューから設定を変更し、再度OS を起動させます。

```text:Lenovoのセットアップ画面から...
Security
  -> Secure Boot
    -> Reset to Setup Mode [Enter]
    -> Clear All Secure Boot Keys [Enter]

// "Reset to Setup Mode" でEnter キーを押してSetup Mode をリセット
// 念のため"Clear All Secure Boot Keys" でもEnter キーを押下してすべての鍵をリセット
```


## bootloader に署名を行う
bootloader に署名を行います。
今回はノートPC 上で既に起動しているArch Linux のbootloader に対して署名を行います。

実際、重要なLinux システムを想定したSecure Boot を行う場合は、署名を行うbootloader(OS) と鍵を管理するOS は別の物であ ることがセキュリティ上は好ましいでが、今回は動作確認目的なので、そのようなことは気にせずに署名を行っていきます。


```console:bootloaderに署名を行う
# sbsign --key my-arch-linux.key --cert my-arch-linux.pem --output grubx64.efi /boot/efi/EFI/arch/grubx64.efi
# cp /boot/efi/EFI/arch/grubx64.efi{,.bak}
# cp grubx64.efi /boot/efi/EFI/arch/
```


## Secure Boot モードで起動する
bootloader への署名も完了したら、マシンを再起動し、ハードウェアのセットアップ画面からSecure Boot モードがON になって いることを確認します。

```text:例:LenovoT460sの場合
// Lenovo のセットアップ画面から...
Security
  -> Secure Boot
    -> Secure Boot [Enabled]
```


Secure Boot モードがON になっていることを確認したら、そのままOS のboot プロセスへ進み、正常にLinux が起動できれば成功です。

正しくSecure Boot モードでLinux が起動しているかどうかを確認したい場合は、USB メモリに他のLinux イメージを入れてUSB  から起動させてみてください。
その時にUSB にインストールした署名が行われていない、もしくは署名検証にパスできずに起動に失敗すれば、Secure Boot の動 作として正常に運転していることが確認できます。

# setup モード(もしくはSecure Boot モードOFF)へ切り戻しする
Secure Boot モードをリセットしたい、もしくはSecure Boot モードを無効にしたい場合は以下の手順を実施します。

## 方法1: ハードウェアベンダのセットアップ画面からSecure Boot モードをOFF にする
今回検証に使用したLenovo T460s のように、ハードウェア側のセットアップ画面にSecure Boot を設定するメニューがある場合は、そちらからOFF にすることができます。


```text:例:LenovoT460sの場合
// Lenovo のセットアップ画面から...
Security
  -> Secure Boot
    -> Reset to Setup Mode [Enter]   // Enter を押下する
    -> Clear All Secure Boot Keys [Enter] // Enter を押下する
```


更にSecure Boot モードをOFF にしたい場合は"Secure Boot" もDisabled に設定します。


```text:例:LenovoT460sの場合
// Lenovo のセットアップ画面から...
Security
  -> Secure Boot
    -> Secure Boot [Disabled]
```


## 方法2: コマンドラインからfirmware 上のメモリを書き換える
(この方法を実施しても戻らないfirmware があるようです。実行は自己責任でおねがいします...)
コマンドラインからsetup モードへ切り戻しをしたい場合、以下のような手順で空っぽのPK を上書きしてあげることで行うことができます。


```console
# : > empty
# sbvarsign --key my-arch-linux.key --cert my-arch-linux.pem --attrs NON_VOLATILE,BOOTSERVICE_ACCESS,RUNTIME_ACCESS --include-attrs --output empty.PK.signed PK empty
# dd bs=4K if=empty.PK.signed of=/sys/firmware/efi/efivars/PK-xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx
(xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx は使っている環境ごとに異なる)
```


# 備忘録: 署名ECDSA 秘密鍵で作成する場合
最後に...本手順は以上で終了ですが、ECDSA でも署名が行うことができるかどうかについてテストをしてみました。
結果から先にいうと、今回自分が使用したハードウェアでは、ECDSA な秘密鍵を使った方法は実施不能でした。
備忘録。2016/08 現在、ECDSA にて登録しようとしても、firmware と情報の同期を行うときにエラーが発生して登録することができません。
お使いのハードウェア環境によっても変わってくる可能性があるため、あくまで参考程度にしておいてください。

```console:openssl(ECDSA)
# openssl ecparam -list_curves
...
secp521r1
...

# openssl ecparam -out my-arch-linux.key -name secp521r1 -genkey
# openssl ecparam -out my-arch-linux.key -name prime256v1 -genkey
# openssl ecparam -out my-arch-linux.key -name secp384r1 -genkey

# openssl req -new -x509 -sha256 -subj '/CN=my-arch-linux' -key my-arch-linux.key -out my-arch-linux.pem
# openssl x509 -in my-arch-linux.pem -inform PEM -out my-arch-linux.der -outform DER
```


楕円曲線としてメジャーどころである`prime256v1`, `secp384r1`, `secp521r1` あたりを試してみたがどれもダメ。


```text:prime256v1,secp384r1,secp521r1
Inserting key update /etc/secureboot/keys/db/my-arch-linux.der.siglist.db.signed into db
Error writing key update: Invalid argument
Error syncing keystore file /etc/secureboot/keys/db/my-arch-linux.der.siglist.db.signed
Inserting key update /etc/secureboot/keys/KEK/my-arch-linux.der.siglist.KEK.signed into KEK
Error writing key update: Invalid argument
Error syncing keystore file /etc/secureboot/keys/KEK/my-arch-linux.der.siglist.KEK.signed
```


# 参考
- Shim
    - https://docs.fedoraproject.org/en-US/Fedora/18/html/UEFI_Secure_Boot_Guide/sect-UEFI_Secure_Boot_Guide-Implementation_of_UEFI_Secure_Boot-Shim.html

- Secure Boot 参考
    - https://wiki.ubuntu.com/SecurityTeam/SecureBoot
    - http://jk.ozlabs.org/docs/sbkeysync-maintaing-uefi-key-databases/

- アーキテクチャにつ入れは以下のページが参考になる
    - http://www.linux-magazine.com/Issues/2014/164/The-State-of-Secure-Boot

- openssl などがサポートしている楕円曲線について
    - https://en.wikipedia.org/wiki/Comparison_of_TLS_implementations#Supported_elliptic_curves

- Which elliptic curve should I use?
    - http://security.stackexchange.com/questions/78621/which-elliptic-curve-should-i-use