😎

Yubikey PIV の Go 実装

2022/12/03に公開

Digital Identity技術勉強会 #iddance Advent Calendar 2022 3 日目の記事です。

https://qiita.com/advent-calendar/2022/iddance

本投稿では、piv-go[1] という Yubikey PIV の Go ライブラリについて記載します。
また、piv-go を利用して Verifiable Presentation を生成する実験的なライブラリおよびサンプル実装も作成したので簡単に説明します。

はじめに

YubiKey PIV については以下の記事に記載しているので、こちらをご覧ください。また、本投稿では、以下の記事[2]で説明している署名を piv-go で行う方法に絞って説明します。

https://zenn.dev/kg0r0/articles/25c403431f81aa

上記の記事に記載の通り、YubiKey を利用してテキストデータなどに署名を行う際、Yubico PIV Tool が利用できます。しかし、もしあなたが YubiKey を利用して署名を行う機能を含むクライアントアプリケーションを実装する場合、Yubico PIV Tool は少し不便かもしれません。例えば、クライアント環境に事前にツールをインストールする必要が生じたり、クライアント環境でビルドする場合も必要なパッケージが複数存在しています。また、クライアントアプリケーションから外部コマンドを実行する実装は、実装不備を作り込んだ場合に重大な脆弱性になる可能性が考えられます。
あくまで例ですが、上記のような理由から、Yubico PIV Tool を利用するのではなく、なるべく依存パッケージの少ないクライアントライブラリから呼び出したい状況があります。こういった状況において、piv-go[1:1] の利用が選択肢の一つになります。
自身で確認した限りだと、YubiKey PIV のハイレベル実装としては、piv-go[1:2] の他に go-ykpiv[3] がありましたが、piv-go は go-ykpiv の代替となるものであり、依存パッケージが最小限になっているように見受けらました。
例えば、piv-go の Installation[4] セクションには以下のように記載されており、依存パッケージが極力少なくなるよう実装されているように見受けられます。

On MacOS, piv-go doesn't require any additional packages.
On Windows: No prerequisites are needed. The default driver by Microsoft supports all functionalities which get tested by unittests. However if you run into problems try the official YubiKey Smart Card Minidriver. Yubico states on their website the driver adds additional smart functionality.

署名

piv-go を利用して署名を行う場合、piv-go の Signing[5] セクションを参考に各関数を呼び出します。また、各関数については Go Doc [6] を適宜参照します。
自身で試したところ、例えば、Yubico PIV Tool による YubiKey を利用した署名[2:1]に記載した署名及び検証手順を piv-go で実装した場合、以下のようになりました。

piv-go example
package main

import (
	"crypto"
	"crypto/ecdsa"
	"crypto/rand"
	"crypto/sha256"
	"encoding/asn1"
	"fmt"
	"math/big"
	"strings"

	"github.com/go-piv/piv-go/piv"
)

func main() {
	cards, err := piv.Cards()
	if err != nil {
		// ...
	}

	var yk *piv.YubiKey
	for _, card := range cards {
		if strings.Contains(strings.ToLower(card), "yubikey") {
			if yk, err = piv.Open(card); err != nil {
				// ...
			}
			break
		}
	}
	if yk == nil {
		// ...
	}

	if err := yk.Reset(); err != nil {
		fmt.Errorf("reset yubikey: %v", err)
	}

	slot := piv.SlotSignature

	key := piv.Key{
		Algorithm:   piv.AlgorithmEC256,
		TouchPolicy: piv.TouchPolicyAlways,
		PINPolicy:   piv.PINPolicyAlways,
	}
	pubKey, err := yk.GenerateKey(piv.DefaultManagementKey, slot, key)
	if err != nil {
		fmt.Errorf("generating key: %v", err)
	}
	pub, ok := pubKey.(*ecdsa.PublicKey)
	if !ok {
		fmt.Errorf("public key is not an ecdsa key")
	}
	auth := piv.KeyAuth{PIN: piv.DefaultPIN}
	data := sha256.Sum256([]byte("hello"))
	priv, err := yk.PrivateKey(slot, pub, auth)
	if err != nil {
		fmt.Errorf("getting private key: %v", err)
	}
	s, ok := priv.(crypto.Signer)
	if !ok {
		fmt.Errorf("expected private key to implement crypto.Signer")
	}
	out, err := s.Sign(rand.Reader, data[:], crypto.SHA256)
	if err != nil {
		fmt.Errorf("signing failed: %v", err)
	}
	var sig struct {
		R, S *big.Int
	}
	if _, err := asn1.Unmarshal(out, &sig); err != nil {
		fmt.Errorf("unmarshaling signature: %v", err)
	}
	if !ecdsa.Verify(pub, data[:], sig.R, sig.S) {
		fmt.Errorf("signature didn't match")
	}
}

上記の通り、piv-go では type Key[7] で TouchPolicy などが定義されており、割と柔軟に YubiKey の呼び出し方法が指定できました。
また、GenerateKey 関数[8]を利用して鍵の生成および指定したスロットへのインポートもできるため、手元で PKCS#12 形式のファイルを作成してインポートする手順が不要でした。なお、YubiKey PIV に関する設定は Reset 関数[9]によりリセットできるようでした。公式の Go Doc[9:1] に記載の以下の通り、Reset 関数は GPG や U2F などの領域には影響しないため、必要に応じて用途を分けて使えるかもしれません。

Reset resets the YubiKey PIV applet to its factory settings, wiping all slots and resetting the PIN, PUK, and Management Key to their default values. This does NOT affect data on other applets, such as GPG or U2F.

参考情報程度ですが、以下に piv-go を利用して署名および検証を行うサンプルコードを置いているので、興味のある方は必要に応じてこちらもご確認ください。

https://github.com/kg0r0/piv-go-example

ykvp

piv-go を利用するユースケースとして、Verifiable Presentation の生成があるのではないかということで、以下にライブラリとサンプル実装を作成して配置しています。
なお、これはハッカソンで突貫で作ったものであり、必ずしも Verifiable Credentials Data Model 仕様[10]に準拠しているものではない実験的な実装である点に注意してください。

https://github.com/KinyouBenkyokai/ykvp

Verifiable Presentation とは Verifiable Credentials Data Model 仕様[10:1]にて定義されているデータモデルです。Holder が Verifier に Verifiable Credential を提示する際に Proof を生成及び付与し、Verifiable Presentation というデータモデルにします。
上記のライブラリでは、Verifiable Presentation の Proof の生成を YubiKey を用いて行います。なお、あくまで Proof の生成のみに焦点を当てており、現時点で、Holder と Verifier 間のトランスポートプロトコルや、検証用の鍵の Resolve の方法についてはスコープ外です。

上記のライブラリを利用したサンプル実装として CLI を用意しています。

https://github.com/KinyouBenkyokai/ykvp/tree/main/cmd/ykvpcli

この CLI は Verifiable Credential を入力として受け取り、Verifiable Presentation を出力します。

$ ykvpcli \ 
    -c '{"context":["https://www.w3.org/2018/credentials/v1"],"type":["GraduationCredential","VerifiableCredential"],"issuanceDate":"2022-11-06T20:40:25.560358+09:00","credentialSubject":{"id":"LS0tLS1CRUdJTiBQVUJMSUMgS0VZLS0tLS0KTUZrd0V3WUhLb1pJemowQ0FRWUlLb1pJemowREFRY0RRZ0FFd2VabHl4emtaL0xFQUdPNE9NMnJCYVliRnplRAorNnYrQmFzTW1hZWx2ZDNZNTZGR2RBQjY4UmZtYk05UVl4NUkvUlg4Qk1KZndSWDVhdFBuUFNXdlp3PT0KLS0tLS1FTkQgUFVCTElDIEtFWS0tLS0tCg==","claim":{"age":24,"universityName":"Oxford","degree":"Bachelor of Science"}},"proof":{"type":"ecdsasecp256k1signature2019","created":"2022-11-06T20:40:25.560671+09:00","creator":{"Curve":{"P":1.1579208921035625e+77,"N":1.1579208921035625e+77,"B":4.105836372515214e+76,"Gx":4.8439561293906455e+76,"Gy":3.6134250956749796e+76,"BitSize":256,"Name":"P-256"},"X":6.72469815416677e+76,"Y":3.327515407817215e+76},"signature":"MEQCIBTOi0ZLu1E58GEhAalQl2FhRuc1EP6kPRj0aUBYV6kGAiBh9kRilqKLj1dk+xeTFmf2PXxjYgR0HEDeHI6xdvk0WA=="}}'

おわりに

本投稿では、piv-go というライブラリを紹介しました。また、piv-go を利用したサンプル実装についても紹介しました。
YubiKey に鍵管理を任せるように実装することが、鍵管理が課題の技術領域における解決策の一つになる可能性があると思い紹介させてもらいました。
ではまた。

脚注
  1. A Go YubiKey PIV implementation https://github.com/go-piv/piv-go ↩︎ ↩︎ ↩︎

  2. Yubico PIV Tool による YubiKey を利用した署名 https://zenn.dev/kg0r0/articles/25c403431f81aa ↩︎ ↩︎

  3. go-ykpiv https://github.com/go-piv/go-ykpiv ↩︎

  4. A Go YubiKey PIV implementation, Installation https://github.com/go-piv/piv-go#installation ↩︎

  5. A Go YubiKey PIV implementation, Signing https://github.com/go-piv/piv-go#signing ↩︎

  6. Documentation https://pkg.go.dev/github.com/go-piv/piv-go/piv ↩︎

  7. type Key https://pkg.go.dev/github.com/go-piv/piv-go/piv#Key ↩︎

  8. func (*YubiKey) GenerateKey https://pkg.go.dev/github.com/go-piv/piv-go/piv#YubiKey.GenerateKey ↩︎

  9. func (*YubiKey) Reset https://pkg.go.dev/github.com/go-piv/piv-go/piv#YubiKey.Reset ↩︎ ↩︎

  10. Verifiable Credentials Data Model v1.1 3.3 Presentations https://www.w3.org/TR/vc-data-model/#presentations ↩︎ ↩︎

GitHubで編集を提案

Discussion