-
Notifications
You must be signed in to change notification settings - Fork 67
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Fix attestation manufacture bug in old yubikeys #91
Fix attestation manufacture bug in old yubikeys #91
Conversation
7c72c40
to
f0cbbf9
Compare
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Thanks! Left a comment
piv/key.go
Outdated
var roots []*x509.Certificate | ||
|
||
if v.Root != nil { | ||
roots = append(roots, v.Root) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
nit: if this option is provided, don't modify it.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
addressed, thanks. now if the option is provided, it is used as is
piv/key.go
Outdated
root := v.Root | ||
if root == nil { | ||
ca, err := yubicoCA() | ||
var roots []*x509.Certificate |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The x509 package actually already has builtin support for validating a certificate against multiple roots. I think it'd be a good simplification to see if the upstream methods work. Does using a x509.CertPool
instead of a x509.Certificate
correctly validate? If so can we update the code to be something like:
o := x509.VerifyOptions{KeyUsages: []x509.ExtKeyUsage{x509.ExtKeyUsageAny}}
o.Roots = v.Roots
if o.Roots != nil {
cas, err := yubicoRoots()
if err != nil {
// ...
}
o.Roots = cas
}
o.Intermediates := x509.NewCertPool()
o.Intermediates.AddCert(attestationCert)
if err := slotCert.Verify(o); err != nil {
// ...
}
return parseAttestation(slotCert)
I'm also happy to send that myself.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yes, the x509.Certificate.Verify()
function returns one or more verified certificate chains given a set of roots and intermediates and would simplify this. In this use case, the returned chains can be ignored as we only care that there isn't a verification error.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think this is a great idea. We shouldn't be duplicating validation logic that already lives in x509
. I've added a change in 87636ea to use CertPool
and x509.Certificate.Verify()
. It works after fixing two wrinkles:
- the U2F root cert has an invalid
pathlen
x509 basic constraint of 0. as per RFC 5280 this means that no intermediate cert is allowed in the validation path. this isn't the first time this happens with yubico CAs, see https://labanskoller.se/blog/2019/12/30/pki-is-hard-how-yubico-trusted-openssl-and-got-it-wrong/ for a similar bug in another yubico root cert. I work around this by setting pathlen to 1 in the root cert. - the device attestation certs from the lucky batch do not encode X509v3 Basic Constraints. this makes
x509.Certificate.Verify()
fail. We need to set this constraint in the device cert.
Overall I'm not too surprised of these wrinkles --after all, x509.Certificate.Verify()
performs a more strict validation conforming to RFC 5280. I know this isn't pretty, but we're fixing a hardware bug after all, and hardware bugs are never pretty to fix 🙂
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Some nits, but overall lgtm.
Please update and squash your commits (so there's only one commit) and I'll merge. Thanks for your work here!
This changeset works around a manufacture bug in some 4C Nano yubikeys from the 853#### range. The PIV attestation on these yubikeys doesn't work as expected because the PIV device certificate was certified using the U2F root CA (instead of the PIV root CA). Yubico has acknowledged this issue recently in [0]. Thanks to Chris Halos from Yubico for helping root cause this. This changeset adds the U2F root CA as an additional CA to work around this. After this PR, go-piv attestation works with both yubikeys from the lucky batch and more recent yubikeys that don't exhibit the bug. Since testing this without a Yubikey from this series is difficult, I've added new positive and negative tests for both attestation chains. [0] Yubico/developers.yubico.com@a58f100 This is a snippet of a device cert. Notice the root U2F CA certifying a PIV attestation cert ``` Data: Version: 3 (0x2) Serial Number: 13542434377556493093 (0xbbf06450c44b0325) Signature Algorithm: sha256WithRSAEncryption Issuer: CN=Yubico U2F Root CA Serial 457200631 Validity Not Before: Aug 1 00:00:00 2014 GMT Not After : Sep 4 00:00:00 2050 GMT Subject: CN=Yubico PIV Attestation ```
87636ea
to
b371d5f
Compare
Address your comments and squashed. Thanks for the detailed review! |
This changeset works around a manufacture bug in some 4C Nano yubikeys from the 853#### range. The PIV attestation on these yubikeys doesn't work as expected because the PIV device certificate was certified using the U2F root CA (instead of the PIV root CA). Yubico has acknowledged this issue recently in [0]. Thanks to Chris Halos from Yubico for helping root cause this.
This changeset adds the U2F root CA as an additional CA to work around this. After this PR, go-piv attestation works with both yubikeys from the lucky batch and more recent yubikeys that don't exhibit the bug.
Since testing this without a Yubikey from this series is difficult, I've added new positive and negative tests for both attestation chains.
[0] Yubico/developers.yubico.com@a58f100
This is a snippet of a device cert. Notice the root U2F CA certifying a PIV attestation cert