PGP鍵をYubiKeyで管理する手引き

## はじめに
   
この記事は[限界開発鯖 Advent Calendar 2022](https://qiita.com/advent-calendar/2022/approvers)の12日目の記事です．
この記事ではYubiKey 5等に備わっているOpenPGP SmartCardの機能を利用してYubiKeyを使ってPGP鍵を管理する方法を紹介します．「PGP鍵って何？」という方は[前日のPGP鍵の管理についての記事](https://qiita.com/shun-shobon/items/a944416bebb6207016fb)をご覧ください．

## YubiKeyとは？

YubiKeyはYubico社が販売しているハードウェア認証デバイスです．USBメモリほどの小さなデバイスで様々な形式の認証が可能となっています．
YubiKeyにも色々ありますが，一番多機能なのはYubiKey 5 NFCシリーズです．今回紹介するOpenPGP SmartCard以外にも，FIDO U2F/FIDO2[^FIDO]，OATH-TOTP[^OTP]などに対応しています．[公式ストア](https://www.yubico.com/jp/store/)で$45〜ほどで売っています．

[^FIDO]: WebAuthnなどで使用される技術の一つ．
[^OTP]: いわゆるワンタイムパスワード．多要素認証で用いられる．有名なのはGoogle Authenticatorなど．

## OpenPGP SmartCardとは？

OpenPGP SmartCardという規格に対応しているデバイスではデバイス上で様々なOpenPGPの操作を行うことができます．例として，署名・暗号化・鍵の生成などが上げられます．また．SmartCard上にPGP鍵を保管することができますが，この鍵スロットはWrite Onlyなので，**一度書き込むと二度と秘密鍵を読み出すことはできなくなります．** なので，PC上に秘密鍵を保管するよりもより安全にPGP鍵を取り扱う事ができます．

## ykmanのインストール

OpenPGP関連の操作・設定はGnuPGから出来ますが，YubiKeyに特化した設定は`ykman`というコマンドラインツールが必要です．例えばmacOSではHomeBrewでインストールが可能です．

```shell
brew install ykman
```

## セットアップ

はじめの設定はGnuPGから行います．YubiKeyの設定は`gpg --edit-card`から可能です．

```console
# YubiKeyの編集をする
$ gpg --edit-card

# 使えるコマンドを見る
# はじめは使えるコマンドはほとんどありません
gpg/card> help

# adminモードに入る
# adminモードでは管理系のコマンドが使えるようになります
gpg/card> admin

# もう一度使えるコマンドを見る
# adminモードに入ったので使えるコマンドが増えます
gpg/card> help
```

### KDF有効化

Key Derived Function(KDF)を有効にすると，YubiKeuのPINの保存形式が平文からハッシュになります．平文が保存されないので万が一YubiKey内のデータが漏れた場合にも安全です．
なお，PINをデフォルトから変更した後にこれを有効にすると正しいPINを入力しても失敗してしまうようなので気をつけましょう．

```console
gpg/card> kdf-setup
```

### PINの変更

PIN/Admin PIN/Reset Codeの変更をします．`passwd`で各種PINの設定を行うことができます．既存のPINを入力→新しいPINの入力→再入力の順でPINを変更することができます．なお，変更に失敗した場合はきちんとその旨がコンソールに出力されるのでよく確認してください．

それぞれのPINの役割とデフォルトの値は以下のとおりです．
- PIN
  - : 普段の暗号化・署名・認証時に使用される。設定した回数以上失敗するとロックされ、Admin PINでのアンロックが必要。初期PINは`123456`。
- Admin PIN
  - : YubiKey自体に関わる設定や鍵の転送などに必要。設定した試行回数以上失敗するとロックされ、ファクトリーリセットが必要(データが全て吹き飛ぶ)。初期Admin PINは`12345678`。
- Reset Code
  - : ファクトリーリセット時に必要。忘れるとファクトリーリセットが出来なくなるので注意。初期状態では未設定。


### 名前/呼び方/言語/アカウント名(メールアドレス)の設定

YubiKeyに各種情報を設定します．別に設定しなくとも特に支障はありません．

```console
# 名前の設定(姓、名の順)
gpg/card> name

# 呼び方(性別)の設定(Mr., Ms., 無しにすることも可能)
gpg/card> salutation

# 言語の設定(英語の場合はen、日本語の場合はja)
gpg/card> lang

# アカウント名(メールアドレス)の設定
gpg/card> login

# 確認
gpg/card> list
```

### 公開鍵のURL設定

YubiKeyに入れてある鍵ペアの公開鍵が置いてある場所を指定しておくと，新たなPCにて，`YubiKey`を挿して`gpg --edit-card`からの`fetch`でOpenPGPのセットアップが一式完了するので設定しておくと良いです．

```console
gpg/card> url
```

### 鍵の転送

いよいよ鍵の転送を行います．一度`--edit-card`でのプロンプトは終了させます(`quit`で可能です)．
**なお，鍵を転送するとPC上からは自動的に削除され，YubiKeyからは二度と取り出せなくなるので注意しましょう．** 困る場合は事前にバックアップを取っておきましょう．
鍵編集プロンプトでは`key <番号>`で鍵を選択することができます．選択した鍵は`ssb*`となるのでその状態で`keytocard`すると鍵がYubiKeyに転送されます．

```shell
gpg --edit-key <鍵ID>
```
```console
sec  ed25519/<KEYID>
     created: <ISSUEDATE>  expires: never       usage: C
     trust: ultimate      validity: ultimate
ssb  ed25519/<KEYID>
     created: <ISSUDATE>  expires: <EXPIRESDATE>  usage: S
ssb  ed25519/<KEYID>
     created: <ISSUDATE>  expires: <EXPIRESDATE>  usage: E
ssb  ed25519/<KEYID>
     created: <ISSUDATE>  expires: <EXPIRESDATE>  usage: A

# 鍵を選択(この場合はusage: Sの鍵が選択される)
gpg> key 1
# 鍵を転送
gpg> keytocard
# YubiKeyの中のどのスロットに転送するか聞かれるので鍵の用途に合わせて選択する
# 転送時には主鍵のパスフレーズとYubiKeyのAdmin PINが聞かれる

# 先に選択していた1番の選択を解除する
gpg> key 1
# 新しく鍵を選択する
gpg> key 2
# 後は同様に鍵転送を繰り返す
```

終わったら`save`して抜けます。ちなみに`save`せず抜けると本来PCから削除されるはずの鍵が残ったままになるので，複数のYubiKeyに転送する場合はその方がよいでしょう．
`gpg -K`で秘密鍵一覧を見てみると、`ssb>`になっていることがわかります。これは鍵のポインタのみが残っており、鍵自体は別の場所(YubiKey)にあることを意味しています。


### PINの試行回数の設定

`ykman`を使うとPINの試行回数を設定できます．ここで設定した回数PIN入力に失敗すると，ロックがかかるので注意が必要です．デフォルトではPIN，Admin PINどちらも3回ですが，個人的には少ないと感じるので少し増やしています．

```shell
# 数字は左からPIN，Reset Code，Admin PINの試行回数となっている
ykman openpgp access set-retries 5 10 10
```

### タッチポリシーの設定

署名・暗号化・認証など，YubiKey内の鍵を使用する際にYubiKeyについている金属のボタン部分に触れないと鍵を使用できないようにタッチポリシーを設定することができます．基本的に鍵を使用する際はPINの入力が必要ですが，YubiKeyを挿したままにしている場合，PINの入力がキャッシュされるため，タッチポリシーを設定することでより安全にYubiKeyを利用できます．

タッチポリシーは以下の5つがあります．
- `Off`: タッチしなくても鍵を使用できる(デフォルト)
- `On`: 鍵を使用する度にタッチが必要
- `Cached`: 鍵を使用する度にタッチが必要だが，タッチしてから15秒間はタッチが不要になる(タッチが15秒間キャッシュされる)．
- `Fixed`: `On`と同じだが，一度設定するとリセットするまでポリシーの変更が出来なくなる．
- `Cached-Fixed`: `Fixed`と同じだが，一度設定するとリセットするまでポリシーの変更が出来なくなる．

```shell
# <sig|enc|aut>はそれぞれ署名・暗号化・認証の鍵スロットを示している
ykman openpgp keys set-touch <sig|enc|aut> Cached
```

## ファクトリリセット

YubiKeyのOpenPGP機能を初期化したい場合やAdmin PINを間違いすぎてロックが掛かってしまった場合はファクトリリセットを行い，工場出荷状態に戻すことができます．GnuPGにもファクトリリセットのコマンドがありますが，**必ず`ykman`を使って行いましょう．** 何らかの理由で`ykman`が使用できない場合は，[公式ドキュメント](https://support.yubico.com/hc/en-us/articles/360013761339-Resetting-the-OpenPGP-Application-on-the-YubiKey)に従ってリセットします．
なお，このファクトリリセットはあくまでYubiKeyの数ある機能のうちのOpenPGP機能のみの初期化なので，他のFIDO U2F/FIDO2やOATH-TOTPなどの機能には影響を与えることはありません．

```shell
ykman openpgp reset
```

## さいごに

YubiKeyを使うとPGP鍵をより安全に利用することができます．PGP鍵以外にもYubiKeyには様々なセキュリティにまつわる機能が搭載されているので皆さん是非YubiKeyを購入しましょう！