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: Allow ExtKeyUsageAny for Webauthn attestation certificates #51201

Open
wants to merge 1 commit into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
34 changes: 32 additions & 2 deletions lib/auth/webauthn/attestation.go
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@ import (
"context"
"crypto/x509"
"encoding/pem"
"errors"
"log/slog"
"slices"

Expand Down Expand Up @@ -68,6 +69,18 @@ func verifyAttestation(cfg *types.Webauthn, obj protocol.AttestationObject) erro
return trace.Wrap(err, "invalid webauthn attestation_denied_ca")
}

verifyOptsBase := x509.VerifyOptions{
// TPM-bound certificates, like those issued for Windows Hello, set
// ExtKeyUsage OID 2.23.133.8.3, aka "AIK (Attestation Identity Key)
// certificate".
//
// There isn't an ExtKeyUsage constant for that, so we allow any.
//
// - https://learn.microsoft.com/en-us/windows/apps/develop/security/windows-hello#attestation
// - https://oid-base.com/get/2.23.133.8.3
KeyUsages: []x509.ExtKeyUsage{x509.ExtKeyUsageAny},
}

// Attestation check works as follows:
// 1. At least one certificate must belong to the allowed pool.
// 2. No certificates may belong to the denied pool.
Expand All @@ -78,11 +91,28 @@ func verifyAttestation(cfg *types.Webauthn, obj protocol.AttestationObject) erro
// so both checks (allowed and denied) may be true for the same cert.
allowed := len(cfg.AttestationAllowedCAs) == 0
for _, cert := range attestationChain {
if _, err := cert.Verify(x509.VerifyOptions{Roots: allowedPool}); err == nil {
opts := verifyOptsBase // take copy
opts.Roots = allowedPool
if _, err := cert.Verify(opts); err == nil {
allowed = true // OK, but keep checking
} else {
log.DebugContext(context.Background(),
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Added some logging to help future debugging. The first certificate in the Hello chain fails validation, which is good to know (the second passes so thankfully it works).

For the curious, the error is "x509: unhandled critical extension".

"Attestation check for allowed CAs failed",
"subject", cert.Subject,
"error", err,
)
}
if _, err := cert.Verify(x509.VerifyOptions{Roots: deniedPool}); err == nil {

opts = verifyOptsBase // take copy
opts.Roots = deniedPool
if _, err := cert.Verify(opts); err == nil {
return trace.BadParameter("attestation certificate %q from issuer %q not allowed", cert.Subject, cert.Issuer)
} else if !errors.As(err, new(x509.UnknownAuthorityError)) {
log.DebugContext(context.Background(),
"Attestation check for denied CAs failed",
"subject", cert.Subject,
"error", err,
)
}
}
if !allowed {
Expand Down
70 changes: 70 additions & 0 deletions lib/auth/webauthn/attestation_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -24,15 +24,18 @@ import (
"crypto/rand"
"crypto/x509"
"crypto/x509/pkix"
"encoding/base64"
"fmt"
"math"
"math/big"
"testing"
"time"

"github.com/go-webauthn/webauthn/protocol"
"github.com/go-webauthn/webauthn/protocol/webauthncbor"
"github.com/go-webauthn/webauthn/protocol/webauthncose"
"github.com/gravitational/trace"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"

"github.com/gravitational/teleport/api/types"
Expand Down Expand Up @@ -382,3 +385,70 @@ func makeCertificate(template, parent *x509.Certificate, signingKey *ecdsa.Priva
cert, err := x509.ParseCertificate(certBytes)
return cert, certKey, trace.Wrap(err)
}

func TestVerifyAttestation_windowsHello(t *testing.T) {
// Attestation object captured from a Windows Hello registration.
// Holds 2 certificates in its x5c chain, the second of which chains to
// microsoftTPMRootCA2014
// - x5c[0]: subject=""
// issuer="EUS-NTC-KEYID-667D154665CAC01F70CB40D8DB33594C90B4D911"
// - x5c[1]: subject="EUS-NTC-KEYID-667D154665CAC01F70CB40D8DB33594C90B4D911"
// issuer="Microsoft TPM Root Certificate Authority 2014"
const rawAttObjB64 = `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`

// Decode and unmarshal attestation object.
rawAttObj, err := base64.StdEncoding.DecodeString(rawAttObjB64)
require.NoError(t, err, "Decode B64 attestation object")
obj := &protocol.AttestationObject{}
require.NoError(t,
webauthncbor.Unmarshal(rawAttObj, obj),
"Unmarshal CBOR attestation object",
)

webConfig := &types.Webauthn{
RPID: "localhost", // unimportant for the test
AttestationAllowedCAs: []string{
microsoftTPMRootCA2014,
},
}
assert.NoError(t,
wanlib.VerifyAttestation(webConfig, *obj),
"VerifyAttestation failed unexpectedly",
)
}

// http://www.microsoft.com/pkiops/certs/Microsoft%20TPM%20Root%20Certificate%20Authority%202014.crt
const microsoftTPMRootCA2014 = `-----BEGIN CERTIFICATE-----
MIIF9TCCA92gAwIBAgIQXbYwTgy/J79JuMhpUB5dyzANBgkqhkiG9w0BAQsFADCB
jDELMAkGA1UEBhMCVVMxEzARBgNVBAgTCldhc2hpbmd0b24xEDAOBgNVBAcTB1Jl
ZG1vbmQxHjAcBgNVBAoTFU1pY3Jvc29mdCBDb3Jwb3JhdGlvbjE2MDQGA1UEAxMt
TWljcm9zb2Z0IFRQTSBSb290IENlcnRpZmljYXRlIEF1dGhvcml0eSAyMDE0MB4X
DTE0MTIxMDIxMzExOVoXDTM5MTIxMDIxMzkyOFowgYwxCzAJBgNVBAYTAlVTMRMw
EQYDVQQIEwpXYXNoaW5ndG9uMRAwDgYDVQQHEwdSZWRtb25kMR4wHAYDVQQKExVN
aWNyb3NvZnQgQ29ycG9yYXRpb24xNjA0BgNVBAMTLU1pY3Jvc29mdCBUUE0gUm9v
dCBDZXJ0aWZpY2F0ZSBBdXRob3JpdHkgMjAxNDCCAiIwDQYJKoZIhvcNAQEBBQAD
ggIPADCCAgoCggIBAJ+n+bnKt/JHIRC/oI/xgkgsYdPzP0gpvduDA2GbRtth+L4W
UyoZKGBw7uz5bjjP8Aql4YExyjR3EZQ4LqnZChMpoCofbeDR4MjCE1TGwWghGpS0
mM3GtWD9XiME4rE2K0VW3pdN0CLzkYbvZbs2wQTFfE62yNQiDjyHFWAZ4BQH4eWa
8wrDMUxIAneUCpU6zCwM+l6Qh4ohX063BHzXlTSTc1fDsiPaKuMMjWjK9vp5UHFP
a+dMAWr6OljQZPFIg3aZ4cUfzS9y+n77Hs1NXPBn6E4Db679z4DThIXyoKeZTv1a
aWOWl/exsDLGt2mTMTyykVV8uD1eRjYriFpmoRDwJKAEMOfaURarzp7hka9TOElG
yD2gOV4Fscr2MxAYCywLmOLzA4VDSYLuKAhPSp7yawET30AvY1HRfMwBxetSqWP2
+yZRNYJlHpor5QTuRDgzR+Zej+aWx6rWNYx43kLthozeVJ3QCsD5iEI/OZlmWn5W
Yf7O8LB/1A7scrYv44FD8ck3Z+hxXpkklAsjJMsHZa9mBqh+VR1AicX4uZG8m16x
65ZU2uUpBa3rn8CTNmw17ZHOiuSWJtS9+PrZVA8ljgf4QgA1g6NPOEiLG2fn8Gm+
r5Ak+9tqv72KDd2FPBJ7Xx4stYj/WjNPtEUhW4rcLK3ktLfcy6ea7Rocw5y5AgMB
AAGjUTBPMAsGA1UdDwQEAwIBhjAPBgNVHRMBAf8EBTADAQH/MB0GA1UdDgQWBBR6
jArOL0hiF+KU0a5VwVLscXSkVjAQBgkrBgEEAYI3FQEEAwIBADANBgkqhkiG9w0B
AQsFAAOCAgEAW4ioo1+J9VWC0UntSBXcXRm1ePTVamtsxVy/GpP4EmJd3Ub53JzN
BfYdgfUL51CppS3ZY6BoagB+DqoA2GbSL+7sFGHBl5ka6FNelrwsH6VVw4xV/8kl
IjmqOyfatPYsz0sUdZev+reeiGpKVoXrK6BDnUU27/mgPtem5YKWvHB/soofUrLK
zZV3WfGdx9zBr8V0xW6vO3CKaqkqU9y6EsQw34n7eJCbEVVQ8VdFd9iV1pmXwaBA
fBwkviPTKEP9Cm+zbFIOLr3V3CL9hJj+gkTUuXWlJJ6wVXEG5i4rIbLAV59UrW4L
onP+seqvWMJYUFxu/niF0R3fSGM+NU11DtBVkhRZt1u0kFhZqjDz1dWyfT/N7Hke
3WsDqUFsBi+8SEw90rWx2aUkLvKo83oU4Mx4na+2I3l9F2a2VNGk4K7l3a00g51m
iPiq0Da0jqw30PaLluTMTGY5+RnZVh50JD6nk+Ea3wRkU8aiYFnpIxfKBZ72whmY
Ya/egj9IKeqpR0vuLebbU0fJBf880K1jWD3Z5SFyJXo057Mv0OPw5mttytE585ZI
y5JsaRXlsOoWGRXE3kUT/MKR1UoAgR54c8Bsh+9Dq2wqIK9mRn15zvBDeyHG6+cz
urLopziOUeWokxZN1syrEdKlhFoPYavm6t+PzIcpdxZwHA+V3jLJPfI=
-----END CERTIFICATE-----`
Loading