Skip to content
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

Merged
merged 1 commit into from
Jul 19, 2021

Conversation

oreparaz
Copy link
Contributor

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

@oreparaz oreparaz force-pushed the oscar/fix-attestation-old-yubi branch from 7c72c40 to f0cbbf9 Compare July 16, 2021 20:38
Copy link
Collaborator

@ericchiang ericchiang left a 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)
Copy link
Collaborator

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.

Copy link
Contributor Author

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
Copy link
Collaborator

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.

Copy link
Contributor

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.

Copy link
Contributor Author

@oreparaz oreparaz Jul 18, 2021

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:

  1. 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.
  2. 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 🙂

@oreparaz oreparaz requested a review from ericchiang July 18, 2021 20:19
Copy link
Collaborator

@ericchiang ericchiang left a 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!

piv/key.go Outdated Show resolved Hide resolved
piv/key_test.go Outdated Show resolved Hide resolved
piv/key_test.go Outdated Show resolved Hide resolved
piv/key_test.go Outdated Show resolved Hide resolved
piv/key_test.go Outdated Show resolved Hide resolved
piv/key.go Show resolved Hide resolved
piv/key.go Outdated Show resolved Hide resolved
piv/key.go Outdated Show resolved Hide resolved
piv/key.go Outdated Show resolved Hide resolved
piv/key.go Outdated Show resolved Hide resolved
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
```
@oreparaz oreparaz force-pushed the oscar/fix-attestation-old-yubi branch from 87636ea to b371d5f Compare July 19, 2021 16:39
@oreparaz
Copy link
Contributor Author

Address your comments and squashed. Thanks for the detailed review!

@ericchiang ericchiang merged commit e5b16c5 into go-piv:master Jul 19, 2021
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

None yet

3 participants