go-piv / piv-go

Keys and certificates for YubiKeys, written in Go
Apache License 2.0
366 stars 65 forks source link

Support GET METADATA command and use it to derive default PIN policy #129

Closed Merovius closed 1 year ago

Merovius commented 1 year ago

(*Yubikey).PrivateKey requires you to give the PIN policy for the used slot, otherwise it derives a default from the attestation certificate. This fails if the key has not been generated on the hardware, as the Yubikey won't provide an attestation certificate in that case. So there is no robust way, currently, to set the correct PIN policy for such keys.

YubiKeys after 5.3 support a vendor-specific command to get metadata about a given key slot. In particular, that metadata contains the PIN/Touch policy for the given key slot. There is reference code to retrieve and parse the metadata.

I would like to add support to a) retrieve that metadata, and b) use that to derive the default PIN policy in PrivateKey, if it is available. My proposal is to add some public API:

// KeyInfo holds unprotected metadata about a key slot.
type KeyInfo struct {
    Algorithm   Algorithm
    PINPolicy   PINPolicy
    TouchPolicy TouchPolicy
    Origin      Origin
    PublicKey   crypto.PublicKey
}

func (yk *YubiKey) KeyInfo(slot Slot) (KeyInfo, error)

// Origin represents whether a key was generated on the hardware, or has been
// imported into it.
type Origin int

const (
    OriginGenerated Origin = iota + 1
    OriginImported
)

I chose the name KeyInfo, because Metadata (which seems a more canonical name, based on the command) is already taken for "PIN protected Metadata" (currently only the management key).

I have a PoC implemented and verified with my own YubiKey that it works. If this is wanted, I could clean it up, add some tests and send it as a PR.

ericchiang commented 1 year ago

Yep, this API seems great. I don't know if I actually have a key that supports this myself, but feel free to add a test regardless