YubiKey のバックアップキーが欲しいけど、使用せずにスタンバイ状態で保存しておくだけなので勿体ないと躊躇していたところ、自分でセキュリティキーを DIY できるオープンソースのソフトウェアがあることを知った。これはぜひ試してみたいと思い、ちょうど送料無料キャンペーンをやっていたマルツ(Digi-Key)で nRF52840-Dongle を試しに1つ購入して実際に作ってみた。
オープンソースで公開されていて nRF52840 で動作するセキュリティキー・ソフトウェアとしては、次のようなものがある。それぞれ開発言語が異なっているが、特に言語によって実用上の違いはないと思われる。
- OpenSK ・・・ Rust
- CanoKey nRF52 ・・・ C
- pmvr/pykey ・・・ CircuitPython
この中では、CanoKey が圧倒的に高機能で、FIDO2 / U2F の他に OpenPGP、TOTP / HOTP と PIV に対応していて機能的にはほぼ YubiKey 5シリーズである。2つ目の OpenSK は Google が公開しているもので FIDO2 / U2Fのみ対応、FIDO® Certified を取得している。また、量子コンピュータによる攻撃から保護できる暗号を利用したブランチも実験的ながら公開されている。1 3つ目の pykey も同じく FIDO2 / U2F のみサポート、CircuitPython で実装されている。乱数生成機能は nRF52840 に搭載されている ARM CryptoCell-310 暗号化アクセラレータを使用しているとのこと。今回は、このうち CanoKey と OpenSK を実際に試してみた。
注文した nRF52840-Dongle は海外から10日間ほどで手元に届いた。
開封して早速、CanoKey をビルドして書き込んでみた。しかし、 WebAuthn.io で動作確認してみたところ、macOS では問題なく動作するものの、Windowsでは何度試してもキー登録ができなかった。また各種設定はWebブラウザで CanoKey Management Tool にアクセスしてできるはずなのだけど、これもエラーが出て動作しなかった。GitHub には issue がひとつも上がっておらず、コードの最終コミットも1年以上前だったのであまり深追いせず素直に OpenSK にすることにした。今回はメインの YubiKey を紛失したりして使えなくなった時のバックアップ用なので、 FIDO2 / U2F が使えれば問題ない。
OpenSK のビルド&書き込み
というわけで、OpenSK をビルドして nRF52840-Dongle に書き込み、ログイン認証に使えるように設定してみる。環境は VMware 上の Ubuntu 22.04 を使用した。
1. Ubuntu パッケージのインストール
$ sudo apt install libssl-dev pkg-config python3-pip uuid-runtime
2. Rust toolchain のインストール
Ubuntu のパッケージは古いので自前でインストールする。
$ curl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs | sh
$ source $HOME/.cargo/env
1) Proceed with installation (default)
はそのまま Enter を入力。
3. Python モジュールのインストール
$ pip3 install --user nrfutil
4. ビルド
$ git clone --recursive https://github.com/google/OpenSK.git
$ cd OpenSK
$ ./setup.sh
$ export PATH=$PWD/elf2tab/bin:$PATH
5. udev 自動認識設定
sudo cp rules.d/55-opensk.rules /etc/udev/rules.d/
sudo udevadm control --reload
6. DFUモードにしてデータ領域のクリア
ここで、Dongle を USB 端子に挿し、RESET ボタン(下の赤丸で囲った横向きのボタン)を押して DFU モードにする。すると LED が赤く明滅するようになるので、DFU モードに入ったことが分かる。
ここはハマりポイントなのだが、違うファームウェアを書き込む場合は必ずデータ領域をクリアしておく必要がある。一応、ドキュメント(の奥深く)にも記載があるけど、登録/認証の際に青と緑の LED が激しく点滅して認証できない場合はこの問題が発生している。2 最初からクリアしておいてくれよと思ったが、同じファームウェアでちょっと設定を変更して再書き込みする場合は内部の鍵や証明書が消えないようになっている。
ここでは、忘れないようにクリアしておく。
$ ./deploy.py --board=nrf52840_dongle_dfu --programmer=nordicdfu --erase_storage
※エラーになる場合は /dev/ttyACM*
のパーミッションを確認すること。
7. ファームウェアの書き込み
Dongle を再び DFU モードにして、--no-u2f
オプションを付けて nRF52840-Dongle にファームウェアを書き込む。
$ ./deploy.py --board=nrf52840_dongle_dfu --programmer=nordicdfu --opensk --no-u2f
記事タイトルには「FIDO2 / FIDO U2F対応」と書いたが、自分は U2F を無効にして FIDO2 のみで利用している。サイトによっては、 FIDO2 のほうを使って欲しいのに U2F が使われてしまって、自己署名証明書の問題でキー登録に失敗することがあるからだ。あえて秘密鍵の同期ができないセキュリティキーを使いたいようなサービスはたいてい FIDO2 に対応しているので U2F は無効にして問題ない。
【FIDO U2F にも対応させたい場合】
もし、FIDO2 / FIDO U2F 両対応させたい場合は、最後の --no-u2f
オプションを外して実行する。
$ ./deploy.py --board=nrf52840_dongle_dfu --programmer=nordicdfu --opensk
次にアテステーション証明書と秘密鍵の書き込みをする。これはDFUモードではなく、一度 USB を抜き差しして通常モードで行う。もし、“device not found” エラーになる場合は /dev/hidraw*
のパーミッションを確認すること。
$ ./tools/configure.py \
--certificate=crypto_data/opensk_cert.pem \
--private-key=crypto_data/opensk.key
以上で OpenSK セキュリティキーの作成は完了。
WebAuthn.io で動作確認
それでは実際に WebAuthn.io で動作確認を行う。試しに登録は Windows で行って、認証は macOS でやってみることにする。
1. 登録
適当なユーザー名を入力して[Register]ボタンを押す。
初回利用時には PIN の登録を要求されるので設定する。この PIN はネットワーク上に流れないのでパスワードのように長くて複雑なものにする必要はないが、少なくとも6文字以上の PIN を設定することが推奨されている(NIST 800-63B で“SHALL”扱い)。
PIN を入力して「セキュリティキーにタッチしてください」と言われたら、Dongle の LED が点滅するので側面の白いボタンを押す。するとキーの登録が完了する。
2. 認証
最初の画面で今度は[Authenticate]ボタンを押す。ユーザー名はセキュリティキー内に保存されているので、入力しても未入力でもかまわない。
Dongle を挿して白いボタンを押して、
先ほど設定した PIN を入力して、
もう一度白いボタンを押す。
ユーザー名を入力しなかった場合はユーザー選択画面になるので、ユーザーを選ぶと認証が完了する。 macOS の場合、なぜか2回ボタンを押さなければいけない。 Safari でも試したが同じく2回タップが必要だった。これはちょっと面倒なので、そのうち改善されることを期待したい。
わざとユーザー名や PIN を間違えて認証できないこともちゃんと確認しておく。
各種サービスにて登録・認証できるか確認
以下のサービスで問題なく利用できることを確認した。実装の違いによって macOS では問題ないが、 Windows では使えないみたいなこともあるので、複数 OS で使う予定ならそのあたりもちゃんと確認したほうがいい。先ほどの例とは逆に macOS でキーを登録して、 Windows でログインというのも試したが、自分の環境では特に問題は発生しなかった。
- Amazon
- AWS
- Cloudflare
- GitHub
- Microsoft
ただ、Amazonは… パスキーなのに追加で SMS か TOTP の2要素認証が要求されるという謎仕様。このセキュリティキー単独で「所有」と「知識」の2要素を満たしているというのに。また、複数のパスキーを登録した際に名前を付けることができず、個別に登録解除もできないのでまとめて削除するしかない。残念ながら、あまりにも使い勝手が悪いので自分は使うのを諦めた。
3Dプリンターでケースを製作
一通り動作が確認できたので、ケースを3Dプリンターで製作した。端面スルーホールの部分がショートしないように保護できればいいので、あえて基板が見えるようにシースルー(というか露出状態)にしてみた。
3Dプリンターなんて面倒だという場合は、 nRF52840-Dongle とほぼ同じ構成で最初からケース入りの makerdiary の製品が購入できる。自分は後から知って、最初からこちらを買えばよかったと少し後悔した。
nRF52840 MDK USB Dongle w/ Case – makerdiary
まとめ
以上で nRF52840 と OpenSK を使って FIDO2 / FIDO U2F対応セキュリティキーを作って実際に主要サービスで利用できることが分かった。
最後にひとつ大切な注意点がある。 nRF52840-Dongle を使用して作成した FIDO2 / FIDO U2F対応セキュリティキーは毎日持ち歩く用途としては適していない。YubiKey などのまともなセキュリティキーは秘密鍵や証明書を外部から読み出すことのできないセキュアエレメントに保存しているが、nRF52840-Dongle + OpenSK の場合は通常の Flash メモリーに保存されている。また、防水性も無いので雨などで濡れたら壊れてしまう。万が一、盗難や紛失した場合に秘密鍵が流出する可能性があるので、普段は安全な場所に保管して持ち歩きはせずに自宅でのみ利用するようにした方がいい。
そしてこれはYubiKey などの製品にも言えることだが、セキュリティキーを紛失したり、盗難されたりしたら、すぐに無効化できるようにどのサービスで使っているかは忘れないようにしておきたい。
脚注