mizdra's blog

ぽよぐらみんぐ

Secure Enclave で git commit の署名鍵を管理する

id:mizdra は Git の commit 署名をしていて、その署名鍵を 1Password で管理している。commit をする度に 1Password による生体認証を求められるが、その分安全に署名鍵を扱える。

今まではそれで不満は無かったのだけど、Coding Agent を使うようになってからというもの、この仕組みが足かせになっている。具体的には、Coding Agent にタスクを投げて人間が他のことをしている際に commit が試行され、人間が時間内に生体認証できずタイムアウトする、というもの。これのせいで、「人間はこれから寝るからこれやっておいて!」と投げたタスクが途中で止まってしまう。

折角 Coding Agent が備えている自律性が損なわれているのは勿体ないので、なんとかしてみる。

基本的な方針

ファイルシステムに署名鍵置いてそれを使うというのが最も簡単な方法だと思う。けどどうせなら、現代的で格好良い方法を採用したい。具体的には、暗号ハードウェアやセキュリティチップに保存された鍵を使って署名することで、秘密鍵を端末の外に持ち出すことを不可能にし、鍵ファイルの漏洩や窃取によるなりすましができないようにしたい。

実は Macbook Pro にもそうしたセキュリティチップが組み込まれていて、それを制御するシステムがある。それが「Secure Enclave」である。

Secure Enclave 内に鍵を生成して git から利用するテクニックは割と知られていて、インターネット上にもいくつか記事がある。しかし、いずれも Secretive という 3rd-party ソフトウェアを利用する方法だった。悪くないけど、id:mizdra としては 3rd-party ソフトウェアに一切依存しない方法を採用したい。

粘り強く調べてみると、macOS 14.0 Sonoma で追加された sc_auth create-ctk-identity というコマンドで同等のことができるようだった (詳しくは後述)。今回はそれを使ってみる。

やり方

1. Secure Enclave 内に鍵を生成する

以下のコマンドで生成できる。

sc_auth create-ctk-identity -l git-sign -k p-256-ne -t none

sc_auth はスマートカード (IC カードのようなもの) を使ったユーザー認証の設定をするためのコマンド。スマートカード上の証明書を macOS のユーザーアカウントに紐付けて、スマートカードをアカウントのログインに利用できる。

元々はスマートカードを前提としたコマンドであったが、macOS 14.0 Sonoma になって Secure Enclave を扱うためのサブコマンドが追加された *1。それが sc_auth create-ctk-identity である。

sc_auth create-ctk-identity は Secure Enclave 内に CTK identity を生成する。CTK とは CryptoTokenKit の略称で、スマートカードやハードウェアトークンなどの暗号トークンを扱うための Apple のフレームワークのこと。そして CTK identity とは「秘密鍵+それに対応する自己署名証明書」のペアのこと。つまり sc_auth create-ctk-identity は Secure Enclave 内に秘密鍵とそれに対応する自己署名証明書を生成するコマンドということ。

...という前提を踏まえたうえで、オプションの意味について解説する。

  • -l git-sign: 鍵の名前。何でも良い。
  • -k p-256-ne: 鍵の種類。ECDSA P-256 で ne = non-exportable な鍵を意味してる。
  • -t none: 鍵の保護方法の指定。none だと Touch ID やパスコードを要求しなくなる。

まとめると、このステップでは Secure Enclave 内に non-exportable な ECDSA P-256 鍵ペアと自己署名証明書を生成してる。

2. SSH 鍵ファイルの書き出し

Secure Enclave 内に生成された鍵ペアはそのままでは git から扱えない。そこで git から署名鍵として扱える SSH 鍵ファイルを書き出す。以下のコマンドでできる。

ssh-keygen -w /usr/lib/ssh-keychain.dylib -K -N ""

オプションの意味はそれぞれ以下の通り。

  • -w /usr/lib/ssh-keychain.dylib: セキュリティキーとの通信に使うプロバイダライブラリを指定するオプション
    • ssh-keychain.dylib は Secure Enclave を FIDO セキュリティキーとして OpenSSH に見せるプロバイダライブラリ
  • -K: セキュリティキーに保存された鍵を参照するための情報 (key handle) と公開鍵を取得し、ファイルに書き出すオプション
  • -N "": 書き出されるファイルのパスフレーズを空に設定する

これで SSH 鍵ファイルが id_ecdsa_sk_rk, id_ecdsa_sk_rk.pub という名前で書き出される。id_ecdsa_sk_rk は Secure Enclave 内の秘密鍵を指す参照のようなもので、秘密鍵そのものは含まれない。

3. SSH 鍵ファイルの rename & ~/.ssh 配下に移動

このままだと名前があんまりなので、分かりやすい名前に rename し、~/.ssh 配下に移動する。

mv id_ecdsa_sk_rk ~/.ssh/id_git_sign
mv id_ecdsa_sk_rk.pub ~/.ssh/id_git_sign.pub

4. SSH 公開鍵を GitHub に登録する

id_git_sign.pubhttps://github.com/settings/keys から Signing keys として登録する。

5. gitconfig を編集する

作成した鍵を使って commit 署名できるよう、gitconfig を編集する。

[user]
    # ...
    signingkey = ~/.ssh/id_git_sign

[gpg]
    format = ssh

[gpg "ssh"]
    program = ~/.local/bin/ssh-sign

[commit]
    gpgsign = true

[tag]
    gpgSign = true

commit 署名するためのコマンドも必要なので作成する。~/.local/bin/ssh-sign に以下を作成し、実行権限を付与すれば OK。

#!/bin/sh
export SSH_SK_PROVIDER=/usr/lib/ssh-keychain.dylib
exec /usr/bin/ssh-keygen "$@"
$ chmod +x ~/.local/bin/ssh-sign

これで完成。

感想

今のところこれで元気に動いてる。commit 署名するたびに生体認証求められることもないし、安全だし、快適。あと 1Password に鍵を問い合わせることがなくなったので、commit が爆速で完了するようになった。ちゃんと測ってないけど、体感 1s => 0.1s くらいになった。

id_git_sign, id_git_sign.pub は端末ごとに生成する必要があるけど、id_git_sign の置き場さえ揃えておけば gitconfig の書き方はマシン間で共通化できるので、gitconfig の dotfiles 管理は特に支障無くできる。

秘密鍵が Secure Enclave の外に持ち出せないとはいえ、端末内であれば好き勝手に commit 署名可能なので、端末上で動作してるマルウェアから commit 署名されるのは防げない。けどそもそも端末上でマルウェアが動作してる時点でもっと他に心配することがあるので、どうでも良さそう。

同じ方法で Authentication keys (GitHub への push/pull に使うやつ) も Secure Enclave 管理にできると思う。けど push はうっかりすると機密情報の漏洩につながる危険性のある操作なので、個人的には Coding Agent に自由に push させたくない。Secure Enclave 管理にしても良いかもしれないけど、今は一旦様子見するつもり。

参考

ポケットモンスター・ポケモン・Pokémon・は任天堂・クリーチャーズ・ゲームフリークの登録商標です.

当ブログは @mizdra 個人により運営されており, 株式会社ポケモン及びその関連会社とは一切関係ありません.