Skip to content

Commit

Permalink
Allow ExtKeyUsageAny for Webauthn attestation certificates
Browse files Browse the repository at this point in the history
  • Loading branch information
codingllama committed Jan 17, 2025
1 parent 8bc4b53 commit 13e1e48
Show file tree
Hide file tree
Showing 2 changed files with 102 additions and 2 deletions.
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(),
"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-----`

0 comments on commit 13e1e48

Please sign in to comment.