GPG Agent のプロンプトが別のタブで出ていた時の話

## 概要
以前、 Mac で GPG を使うための環境設定をしました。

その中で SSH に PGP 認証鍵を使うようにしましたが、、パスフレーズや card の PIN を聞くプロンプトをうまく出すのに苦労しました。

二、三時間ほど格闘することになったので、原因と解決法をまとめておきます。

## TL;DR
`pinentry-program` として `pinentry-mac` を使う。

```sh
brew install pinentry-mac
which pinentry-mac # 出力をメモする
```

```gpg-agent.conf
enable-ssh-support
pinentry-program /opt/homebrew/bin/pinentry-mac # 先ほどメモしたパス
```

## 詳細
### 問題発生の背景
SSH を PGP 鍵で行うための設定として、 gg って出てきたものを適当に突っ込んだ [^conf] 。以下抜粋。

[^conf]: 設定方法自体については別の記事を読んでください。あるいは筆者が別の記事で書くかもです。

```~/.gnupg/gpg-agent.conf
enable-ssh-support
```

```~/.gnupg/sshcontrol
{鍵 keygrip}
```

```..zshrc
gpg-connect-agent updatestartuptty /bye > /dev/null
export SSH_AUTH_SOCK="$(gpgconf --list-dirs agent-ssh-socket)"
export GPG_TTY=$(tty)
```

これらのうち正しい (必要な) 設定もあったが、間違いもあったため余計に苦しむこととなった。

### 問題
上記の設定で gpg-agent 経由で SSH 公開鍵認証を行おうとすると、プロンプトがおかしなことになる。

たとえば、二つのタブ 1, 2 を持つターミナルのウィンドウがあるとする。
ターミナルのタブ 1 で ssh をしようとすると、なぜかタブ 2 で card PIN のプロンプトが出てしまう。 (画像)

![Screen Shot 2021-11-18 at 12.52.42.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/508344/3537a1ec-792e-ca5b-4989-f2955309fe50.png)
![Screen Shot 2021-11-18 at 12.52.46.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/508344/cf42a9aa-eda0-8d68-a786-b070e2ef426b.png)

最初はそういう仕様なのかとも思ったが、実はこのプロンプトではキー操作がまともにできず、矢印キーが効いたり効かなかったりする。
したがって、意図した挙動ではないことが推測できた。

### 解決策1: プロンプトを出すターミナルを指定する方法
gg ったところ、ある [Stack Overflow の質問](https://unix.stackexchange.com/questions/280879/how-to-get-pinentry-curses-to-start-on-the-correct-tty) を見つけた。

そこでは、 SSH する前に以下のコマンドを実行することで解決が図れるとしていた。 [^nousi-config]

[^nousi-config]: おそらく筆者はセットアップ中にも同じページを見つけていたらしく、何も考えず `.zshrc` に書き込んだようである。

```sh
gpg-connect-agent updatestartuptty /bye
```

たしかに、これを実行してから SSH すると、プロンプトが同じタブで表示される。

回答からリンクされている [gpg-agent の man ページ](https://www.gnupg.org/documentation/manuals/gnupg/Agent-Options.html) を 抜粋する。

>
Note: in case the gpg-agent receives a signature request, the user might need to be prompted for a passphrase, which is necessary for decrypting the stored key. Since the ssh-agent protocol does not contain a mechanism for telling the agent on which display/terminal it is running, gpg-agent’s ssh-support will use the TTY or X display where gpg-agent has been started. To switch this display to the current one, the following command may be used:
>
```
gpg-connect-agent updatestartuptty /bye
```

要約すると、

- PGP 認証鍵を使うためにはユーザがパスフレーズを入力しないといけない
- しかし SSH Agent プロトコルは、自分が動いているディスプレイやターミナルを伝える手段を持たない
    - つまり、どのディスプレイ／ターミナルにプロンプトすればいいかわからない
- そのため、 `gpg-agent` は、とりあえず自身が起動された TTY / X ディスプレイ を使う

そして、プロンプトが出されるウィンドウ／ターミナルを変えるためのコマンドが `gpg-connect-agent updatestartuptty /bye` というわけである。
これを実行することで、 `gpg-agent` からするとどのターミナルにプロンプトを出せばいいのかを知ることができ、ユーザの意図通りのタブにプロンプトが現れるという流れであるらしい。 [^nousi-config-2]

[^nousi-config-2]: 筆者はその設定を脳死で `.zshrc` にコピペしたため、プロンプトがあっちに行ったりこっちに行ったりしていた

これで問題の解決自体は達成できたが、ぶっちゃけ毎回打ち込むのは面倒であるし、むやみに alias も増やす等もできれば避けたい。 [^another-way]
そのため、別の解決法を探った。

[^another-way]: Stack Overflow では ssh_config を編集する方法も紹介されているが、これも美しくない気がした

### 解決策2: pinentry を変える
さらに別の情報を探したところ、 HomeBrew の GitHub リポジトリに立てられた [issue](https://github.com/Homebrew/homebrew-core/issues/12816) に遭遇した。

issue 主はどうやら筆者と似たことで悩んだようである。 
コメントでは、ざっくり pinentry を変えろ、という指摘がされていた。

pinentry とは、

>
pinentry is a small collection of dialog programs that allow GnuPG to read passphrases and PIN numbers in a secure manner. There are versions for the common GTK and Qt toolkits as well as for the text terminal (Curses).

らしい。([GNU の公式ページ](https://gnupg.org/related_software/pinentry/index.html) より) [^nanmo-wakaran]
つまりダイアログを出すためのプログラムのようだ。

[^nanmo-wakaran]: この部分しか読んでいないので筆者はよくわかっていない

したがって上掲の issue では、 gpg-agent が使うプロンプト用プログラム = pinentry を変えることでいい感じにできそうだ、というアドバイスがされていると理解できる。
また、デフォルトが `pinentry-curses` で、ほかに `pinentry`, `pinentry-mac` があるようなことが書いてある。

これに従って、使用する pinentry プログラムを変えてみることにする。
`pinentry` は CLI らしいが、どうせなら人間の使いやすいインタフェースがいいので、 `pinentry-mac` を使ってみることにした。
手順は以下の通りである。

まず、 `pinentry-mac` をインストールする。

```sh
brew install pinentry-mac
which pinentry-mac # 出力をメモする
```

そして、 `~/.gnupg/gpg-agent.conf` を編集し、以下を追記する。

```~/.gnupg/gpg-agent.conf`
pinentry-program ${さっきメモしたパス}
```

その後、 gpg-agent の設定をリロードする。

```sh
gpgconf --reload gpg-agent
```

このようにすることで、 `gpg-agent` が `pinentry-mac` を使うように指定でき、次回の SSH から、以下のように人間らしいインタフェースが現れる。

![Screen Shot 2021-11-18 at 13.44.48.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/508344/900dbf50-8990-9c3b-6718-5195f5494a90.png)
![Screen Shot 2021-11-18 at 13.44.51.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/508344/6f076758-8f3a-653c-c5b3-2ea94a6982d2.png)

これで、当初の目的通りプロンプトをいい感じに出すことに成功した。

## まとめ
gpg-agent を使って SSH の認証を行う際に、パスフレーズや PIN のプロンプトがうまく出ない問題を解決した。

解決法としては大きく二つあるが、筆者としては二つめの pinentry を変える方法がよいように思われた。

以上です。誰かの参考になれば嬉しいです。