From 6be1bd6daf4269ff4ff26a39e928c28914f1b022 Mon Sep 17 00:00:00 2001 From: James Elliott Date: Tue, 16 Aug 2022 09:37:06 +1000 Subject: [PATCH] fix: google tpm ec mapping (#43) This fixes an issue mapping Google TPM EC Curves from Webauthn COSE EC Curves. Co-authored-by: Alex Seigler --- .golangci.yml | 49 +++++++ go.mod | 8 +- go.sum | 18 ++- protocol/attestation_tpm.go | 15 ++- protocol/attestation_tpm_test.go | 180 ++++++++++++++++++++++++++ protocol/webauthncose/webauthncose.go | 40 +++++- 6 files changed, 299 insertions(+), 11 deletions(-) create mode 100644 .golangci.yml create mode 100644 protocol/attestation_tpm_test.go diff --git a/.golangci.yml b/.golangci.yml new file mode 100644 index 00000000..d2d559d2 --- /dev/null +++ b/.golangci.yml @@ -0,0 +1,49 @@ +--- +run: + timeout: 3m + +linters-settings: + goconst: + min-len: 2 + min-occurrences: 2 + gocyclo: + min-complexity: 15 + godot: + check-all: true + goimports: + local-prefixes: github.com/go-webauthn/webauthn + +linters: + enable: + - asciicheck + - goconst + - gocritic + - gocyclo + - godot + - gofmt + - goimports + - gosec + - misspell + - nolintlint + - prealloc + - revive + - unconvert + - unparam + - whitespace + - wsl + +issues: + exclude: + - Error return value of .((os\.)?std(out|err)\..*|.*Close|.*Flush|os\.Remove(All)?|.*printf?|os\.(Un)?Setenv). is not checked # yamllint disable-line rule:line-length + - func name will be used as test\.Test.* by other packages, and that stutters; consider calling this + - (possible misuse of unsafe.Pointer|should have signature) + - ineffective break statement. Did you mean to break out of the outer loop + - Use of unsafe calls should be audited + - Subprocess launch(ed with variable|ing should be audited) + - (G104|G307) + - (Expect directory permissions to be 0750 or less|Expect file permissions to be 0600 or less) + - Potential file inclusion via variable + exclude-use-default: false + max-issues-per-linter: 0 + max-same-issues: 0 +... diff --git a/go.mod b/go.mod index ee217438..64346e69 100644 --- a/go.mod +++ b/go.mod @@ -8,7 +8,13 @@ require ( github.com/golang-jwt/jwt/v4 v4.4.2 github.com/google/uuid v1.3.0 github.com/mitchellh/mapstructure v1.5.0 + github.com/stretchr/testify v1.8.0 golang.org/x/crypto v0.0.0-20220622213112-05595931fe9d ) -require github.com/x448/float16 v0.8.4 // indirect +require ( + github.com/davecgh/go-spew v1.1.1 // indirect + github.com/pmezard/go-difflib v1.0.0 // indirect + github.com/x448/float16 v0.8.4 // indirect + gopkg.in/yaml.v3 v3.0.1 // indirect +) diff --git a/go.sum b/go.sum index 050ec32f..2c01adf9 100644 --- a/go.sum +++ b/go.sum @@ -1,7 +1,8 @@ +github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= +github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= +github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/fxamacker/cbor/v2 v2.4.0 h1:ri0ArlOR+5XunOP8CRUowT0pSJOwhW098ZCUyskZD88= github.com/fxamacker/cbor/v2 v2.4.0/go.mod h1:TA1xS00nchWmaBnEIxPSE5oHLuJBAVvqrtAnWBwBCVo= -github.com/go-webauthn/revoke v0.1.1 h1:3Ana75ARxM19cq1llWzYbfmGIQ8woDWtSJ/6Y+iQxeo= -github.com/go-webauthn/revoke v0.1.1/go.mod h1:dh18I4Pd+1bgMckV9KfzN1aRz3vvps7ARZTKCb8qKB0= github.com/go-webauthn/revoke v0.1.2 h1:k1CiG5nPtKmVkH2XucYWcbRARwL8GhqFZ8N57wPrgXk= github.com/go-webauthn/revoke v0.1.2/go.mod h1:fPsKNzp6BcGKuQnsB+3gw0KCTr8tY7HOIrphBjZZL10= github.com/golang-jwt/jwt/v4 v4.4.2 h1:rcc4lwaZgFMCZ5jxF9ABolDcIHdBytAFgqFPbSJQAYs= @@ -10,9 +11,15 @@ github.com/google/uuid v1.3.0 h1:t6JiXgmwXMjEs8VusXIJk2BXHsn+wx8BZdTaoZ5fu7I= github.com/google/uuid v1.3.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= github.com/mitchellh/mapstructure v1.5.0 h1:jeMsZIYE/09sWLaz43PL7Gy6RuMjD2eJVyuac5Z2hdY= github.com/mitchellh/mapstructure v1.5.0/go.mod h1:bFUtVrKA4DC2yAKiSyO/QUcy7e+RRV2QTWOzhPopBRo= +github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= +github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= +github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= +github.com/stretchr/objx v0.4.0/go.mod h1:YvHI0jy2hoMjB+UWwv71VJQ9isScKT/TqJzVSSt89Yw= +github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= +github.com/stretchr/testify v1.8.0 h1:pSgiaMZlXftHpm5L7V1+rVB+AZJydKsMxsQBIJw4PKk= +github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU= github.com/x448/float16 v0.8.4 h1:qLwI1I70+NjRFUR3zs1JPUCgaCXSh3SW62uAKT1mSBM= github.com/x448/float16 v0.8.4/go.mod h1:14CWIYCyZA/cWjXOioeEpHeN/83MdbZDRQHoFcYsOfg= -golang.org/x/crypto v0.0.0-20220411220226-7b82a4e95df4/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4= golang.org/x/crypto v0.0.0-20220622213112-05595931fe9d h1:sK3txAijHtOK88l68nt020reeT1ZdKLIYetKl95FzVY= golang.org/x/crypto v0.0.0-20220622213112-05595931fe9d/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4= golang.org/x/net v0.0.0-20211112202133-69e39bad7dc2/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= @@ -22,3 +29,8 @@ golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBc golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= golang.org/x/text v0.3.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= +gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405 h1:yhCVgyC4o1eVCa2tZl7eS0r+SDo693bJlVdllGtEeKM= +gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= +gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= +gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA= +gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= diff --git a/protocol/attestation_tpm.go b/protocol/attestation_tpm.go index f84efd02..f1672103 100644 --- a/protocol/attestation_tpm.go +++ b/protocol/attestation_tpm.go @@ -84,15 +84,15 @@ func verifyTPMFormat(att AttestationObject, clientDataHash []byte) (string, []in switch k := key.(type) { case webauthncose.EC2PublicKeyData: - if pubArea.ECCParameters.CurveID != googletpm.EllipticCurve(k.Curve) || - 0 != pubArea.ECCParameters.Point.X.Cmp(new(big.Int).SetBytes(k.XCoord)) || - 0 != pubArea.ECCParameters.Point.Y.Cmp(new(big.Int).SetBytes(k.YCoord)) { + if pubArea.ECCParameters.CurveID != k.TPMCurveID() || + pubArea.ECCParameters.Point.X.Cmp(new(big.Int).SetBytes(k.XCoord)) != 0 || + pubArea.ECCParameters.Point.Y.Cmp(new(big.Int).SetBytes(k.YCoord)) != 0 { return tpmAttestationKey, nil, ErrAttestationFormat.WithDetails("Mismatch between ECCParameters in pubArea and credentialPublicKey") } case webauthncose.RSAPublicKeyData: mod := new(big.Int).SetBytes(k.Modulus) exp := uint32(k.Exponent[0]) + uint32(k.Exponent[1])<<8 + uint32(k.Exponent[2])<<16 - if 0 != pubArea.RSAParameters.Modulus.Cmp(mod) || + if pubArea.RSAParameters.Modulus.Cmp(mod) != 0 || pubArea.RSAParameters.Exponent != exp { return tpmAttestationKey, nil, ErrAttestationFormat.WithDetails("Mismatch between RSAParameters in pubArea and credentialPublicKey") } @@ -121,7 +121,7 @@ func verifyTPMFormat(att AttestationObject, clientDataHash []byte) (string, []in f := webauthncose.HasherFromCOSEAlg(coseAlg) h := f() h.Write(attToBeSigned) - if 0 != bytes.Compare(certInfo.ExtraData, h.Sum(nil)) { + if !bytes.Equal(certInfo.ExtraData, h.Sum(nil)) { return tpmAttestationKey, nil, ErrAttestationFormat.WithDetails("ExtraData is not set to hash of attToBeSigned") } // 4/4 Verify that attested contains a TPMS_CERTIFY_INFO structure as specified in @@ -131,7 +131,7 @@ func verifyTPMFormat(att AttestationObject, clientDataHash []byte) (string, []in f, err = certInfo.AttestedCertifyInfo.Name.Digest.Alg.HashConstructor() h = f() h.Write(pubAreaBytes) - if 0 != bytes.Compare(h.Sum(nil), certInfo.AttestedCertifyInfo.Name.Digest.Value) { + if !bytes.Equal(h.Sum(nil), certInfo.AttestedCertifyInfo.Name.Digest.Value) { return tpmAttestationKey, nil, ErrAttestationFormat.WithDetails("Hash value mismatch attested and pubArea") } @@ -175,6 +175,9 @@ func verifyTPMFormat(att AttestationObject, clientDataHash []byte) (string, []in for _, ext := range aikCert.Extensions { if ext.Id.Equal([]int{2, 5, 29, 17}) { manufacturer, model, version, err = parseSANExtension(ext.Value) + if err != nil { + return tpmAttestationKey, nil, err + } } } diff --git a/protocol/attestation_tpm_test.go b/protocol/attestation_tpm_test.go new file mode 100644 index 00000000..b52d9a6c --- /dev/null +++ b/protocol/attestation_tpm_test.go @@ -0,0 +1,180 @@ +package protocol + +import ( + "crypto/sha256" + "testing" + + "github.com/go-webauthn/webauthn/protocol/googletpm" + "github.com/stretchr/testify/assert" +) + +func TestTPMAttestationVerificationSuccess(t *testing.T) { + for i := range testAttestationTPMResponses { + t.Run("TPM Positive tests", func(t *testing.T) { + pcc := attestationTestUnpackResponse(t, testAttestationTPMResponses[i]) + clientDataHash := sha256.Sum256(pcc.Raw.AttestationResponse.ClientDataJSON) + + attestationKey, _, err := verifyTPMFormat(pcc.Response.AttestationObject, clientDataHash[:]) + if err != nil { + t.Fatalf("Not valid: %+v", err) + } + assert.Equal(t, "tpm", attestationKey) + }) + } +} + +var testAttestationTPMResponses = []string{ + // TPM attestation with ECC P256 + `{ + "id": "hsS2ywFz_LWf9-lC35vC9uJTVD3ZCVdweZvESUbjXnQ", + "rawId": "hsS2ywFz_LWf9-lC35vC9uJTVD3ZCVdweZvESUbjXnQ", + "type": "public-key", + "response": { + "attestationObject": "o2NmbXRjdHBtZ2F0dFN0bXSmY2FsZzn__mNzaWdZAQCqAcGoi2IFXCF5xxokjR5yOAwK_11iCOqt8hCkpHE9rW602J3KjhcRQzoFf1UxZvadwmYcHHMxDQDmVuOhH-yW-DfARVT7O3MzlhhzrGTNO_-jhGFsGeEdz0RgNsviDdaVP5lNsV6Pe4bMhgBv1aTkk0zx1T8sxK8B7gKT6x80RIWg89_aYY4gHR4n65SRDp2gOGI2IHDvqTwidyeaAHVPbDrF8iDbQ88O-GH_fheAtFtgjbIq-XQbwVdzQhYdWyL0XVUwGLSSuABuB4seRPkyZCKoOU6VuuQzfWNpH2Nl05ybdXi27HysUexgfPxihB3PbR8LJdi1j04tRg3JvBUvY3ZlcmMyLjBjeDVjglkFuzCCBbcwggOfoAMCAQICEGEZiaSlAkKpqaQOKDYmWPkwDQYJKoZIhvcNAQELBQAwQTE_MD0GA1UEAxM2RVVTLU5UQy1LRVlJRC1FNEE4NjY2RjhGNEM2RDlDMzkzMkE5NDg4NDc3ODBBNjgxMEM0MjEzMB4XDTIyMDExMjIyMTUxOFoXDTI3MDYxMDE4NTQzNlowADCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBAKo-7DHdiipZTzfA9fpTaIMVK887zM0nXAVIvU0kmGAsPpTYbf7dn1DAl6BhcDkXs2WrwYP02K8RxXWOF4jf7esMAIkr65zPWqLys8WRNM60d7g9GOADwbN8qrY0hepSsaJwjhswbNJI6L8vJwnnrQ6UWVCm3xHqn8CB2iSWNSUnshgTQTkJ1ZEdToeD51sFXUE0fSxXjyIiSAAD4tCIZkmHFVqchzfqUgiiM_mbbKzUnxEZ6c6r39ccHzbm4Ir-u62repQnVXKTpzFBbJ-Eg15REvw6xuYaGtpItk27AXVcEodfAylf7pgQPfExWkoMZfb8faqbQAj5x29mBJvlzj0CAwEAAaOCAeowggHmMA4GA1UdDwEB_wQEAwIHgDAMBgNVHRMBAf8EAjAAMG0GA1UdIAEB_wRjMGEwXwYJKwYBBAGCNxUfMFIwUAYIKwYBBQUHAgIwRB5CAFQAQwBQAEEAIAAgAFQAcgB1AHMAdABlAGQAIAAgAFAAbABhAHQAZgBvAHIAbQAgACAASQBkAGUAbgB0AGkAdAB5MBAGA1UdJQQJMAcGBWeBBQgDMFAGA1UdEQEB_wRGMESkQjBAMT4wEAYFZ4EFAgIMB05QQ1Q3NXgwFAYFZ4EFAgEMC2lkOjRFNTQ0MzAwMBQGBWeBBQIDDAtpZDowMDA3MDAwMjAfBgNVHSMEGDAWgBQ3yjAtSXrnaSNOtzy1PEXxOO1ZUDAdBgNVHQ4EFgQU1ml3H5Tzrs0Nev69tFNhPZnhaV0wgbIGCCsGAQUFBwEBBIGlMIGiMIGfBggrBgEFBQcwAoaBkmh0dHA6Ly9hemNzcHJvZGV1c2Fpa3B1Ymxpc2guYmxvYi5jb3JlLndpbmRvd3MubmV0L2V1cy1udGMta2V5aWQtZTRhODY2NmY4ZjRjNmQ5YzM5MzJhOTQ4ODQ3NzgwYTY4MTBjNDIxMy9lMDFjMjA2Mi1mYmRjLTQwYTUtYTQwZi1jMzc3YzBmNzY1MWMuY2VyMA0GCSqGSIb3DQEBCwUAA4ICAQAz-YGrj0S841gyMZuit-qsKpKNdxbkaEhyB1baexHGcMzC2y1O1kpTrpaH3I80hrIZFtYoA2xKQ1j67uoC6vm1PhsJB6qhs9T7zmWZ1VtleJTYGNZ_bYY2wo65qJHFB5TXkevJUVe2G39kB_W1TKB6g_GSwb4a5e4D_Sjp7b7RZpyIKHT1_UE1H4RXgR9Qi68K4WVaJXJUS6T4PHrRc4PeGUoJLQFUGxYokWIf456G32GwGgvUSX76K77pVv4Y-kT3v5eEJdYxlS4EVT13a17KWd0DdLje0Ae69q_DQSlrHVLUrADvuZMeM8jxyPQvDb7ETKLsSUeHm73KOCGLStcGQ3pB49nt3d9XdWCcUwUrmbBF2G7HsRgTNbj16G6QUcWroQEqNrBG49aO9mMZ0NwSn5d3oNuXSXjLdGBXM1ukLZ-GNrZDYw5KXU102_5VpHpjIHrZh0dXg3Q9eucKe6EkFbH65-O5VaQWUnR5WJpt6-fl_l0iHqHnKXbgL6tjeerCqZWDvFsOak05R-hosAoQs_Ni0EsgZqHwR_VlG86fsSwCVU3_sDKTNs_Je08ewJ_bbMB5Tq6k1Sxs8Aw8R96EwjQLp3z-Zva1myU-KerYYVDl5BdvgPqbD8Xmst-z6vrP3CJbtr8jgqVS7RWy_cJOA8KCZ6IS_75QT7Gblq6UGFkG7zCCBuswggTToAMCAQICEzMAAAbTtnznKsOrB-gAAAAABtMwDQYJKoZIhvcNAQELBQAwgYwxCzAJBgNVBAYTAlVTMRMwEQYDVQQIEwpXYXNoaW5ndG9uMRAwDgYDVQQHEwdSZWRtb25kMR4wHAYDVQQKExVNaWNyb3NvZnQgQ29ycG9yYXRpb24xNjA0BgNVBAMTLU1pY3Jvc29mdCBUUE0gUm9vdCBDZXJ0aWZpY2F0ZSBBdXRob3JpdHkgMjAxNDAeFw0yMTA2MTAxODU0MzZaFw0yNzA2MTAxODU0MzZaMEExPzA9BgNVBAMTNkVVUy1OVEMtS0VZSUQtRTRBODY2NkY4RjRDNkQ5QzM5MzJBOTQ4ODQ3NzgwQTY4MTBDNDIxMzCCAiIwDQYJKoZIhvcNAQEBBQADggIPADCCAgoCggIBAJA7GLwHWWbn2H8DRppxQfre4zll1sgE3Wxt9DTYWt5-v-xKwCQb6z_7F1py7LMe58qLqglAgVhS6nEvN2puZ1GzejdsFFxz2gyEfH1y-X3RGp0dxS6UKwEtmksaMEKIRQn2GgKdUkiuvkaxaoznuExoTPyu0aXk6yFsX5KEDu9UZCgt66bRy6m3KIRnn1VK2frZfqGYi8C8x9Q69oGG316tUwAIm3ypDtv3pREXsDLYE1U5Irdv32hzJ4CqqPyau-qJS18b8CsjvgOppwXRSwpOmU7S3xqo-F7h1eeFw2tgHc7PEPt8MSSKeba8Fz6QyiLhgFr8jFUvKRzk4B41HFUMqXYawbhAtfIBiGGsGrrdNKb7MxISnH1E6yLVCQGGhXiN9U7V0h8Gn56eKzopGlubw7yMmgu8Cu2wBX_a_jFmIBHnn8YgwcRm6NvT96KclDHnFqPVm3On12bG31F7EYkIRGLbaTT6avEu9rL6AJn7Xr245Sa6dC_OSMRKqLSufxp6O6f2TH2g4kvT0Go9SeyM2_acBjIiQ0rFeBOm49H4E4VcJepf79FkljovD68imeZ5MXjxepcCzS138374Jeh7k28JePwJnjDxS8n9Dr6xOU3_wxS1gN5cW6cXSoiPGe0JM4CEyAcUtKrvpUWoTajxxnylZuvS8ou2thfH2PQlAgMBAAGjggGOMIIBijAOBgNVHQ8BAf8EBAMCAoQwGwYDVR0lBBQwEgYJKwYBBAGCNxUkBgVngQUIAzAWBgNVHSAEDzANMAsGCSsGAQQBgjcVHzASBgNVHRMBAf8ECDAGAQH_AgEAMB0GA1UdDgQWBBQ3yjAtSXrnaSNOtzy1PEXxOO1ZUDAfBgNVHSMEGDAWgBR6jArOL0hiF-KU0a5VwVLscXSkVjBwBgNVHR8EaTBnMGWgY6Bhhl9odHRwOi8vd3d3Lm1pY3Jvc29mdC5jb20vcGtpb3BzL2NybC9NaWNyb3NvZnQlMjBUUE0lMjBSb290JTIwQ2VydGlmaWNhdGUlMjBBdXRob3JpdHklMjAyMDE0LmNybDB9BggrBgEFBQcBAQRxMG8wbQYIKwYBBQUHMAKGYWh0dHA6Ly93d3cubWljcm9zb2Z0LmNvbS9wa2lvcHMvY2VydHMvTWljcm9zb2Z0JTIwVFBNJTIwUm9vdCUyMENlcnRpZmljYXRlJTIwQXV0aG9yaXR5JTIwMjAxNC5jcnQwDQYJKoZIhvcNAQELBQADggIBAFZTSitCISvll6i6rPUPd8Wt2mogRw6I_c-dWQzdc9-SY9iaIGXqVSPKKOlAYU2ju7nvN6AvrIba6sngHeU0AUTeg1UZ5-bDFOWdSgPaGyH_EN_l-vbV6SJPzOmZHJOHfw2WT8hjlFaTaKYRXxzFH7PUR4nxGRbWtdIGgQhUlWg5oo_FO4bvLKfssPSONn684qkAVierq-ly1WeqJzOYhd4EylgVJ9NL3YUhg8dYcHAieptDzF7OcDqffbuZLZUx6xcyibhWQcntAh7a3xPwqXxENsHhme_bqw_kqa-NVk-Wz4zdoiNNLRvUmCSL1WLc4JPsFJ08Ekn1kW7f9ZKnie5aw-29jEf6KIBt4lGDD3tXTfaOVvWcDbu92jMOO1dhEIj63AwQiDJgZhqnrpjlyWU_X0IVQlaPBg80AE0Y3sw1oMrY0XwdeQUjSpH6e5fTYKrNB6NMT1jXGjKIzVg8XbPWlnebP2wEhq8rYiDR31b9B9Sw_naK7Xb-Cqi-VQdUtknSjeljusrBpxGUx-EIJci0-dzeXRT5_376vyKSuYxA1Xd2jd4EknJLIAVLT3rb10DCuKGLDgafbsfTBxVoEa9hSjYOZUr_m3WV6t6I9WPYjVyhyi7fCEIG4JE7YbM4na4jg5q3DM8ibE8jyufAq0PfJZTJyi7c2Q2N_9NgnCNwZ3B1YkFyZWFYdgAjAAsABAByACCd_8vzbDg65pn7mGjcbcuJ1xU4hL4oA5IsEkFYv60irgAQABAAAwAQACAek7g2C8TeORRoKxuN7HrJ5OinVGuHzEgYODyUsF9D1wAggXPPXn-Pm_4IF0c4XVaJjmHO3EB2KBwdg_L60N0IL9xoY2VydEluZm9Yof9UQ0eAFwAiAAvQNGTLa2wT6u8SKDDdwkgaq5Cmh6jcD_6ULvM9ZmvdbwAUtMInD3WtGSdWHPWijMrW_TfYo-gAAAABPuBems3Sywu4aQsGAe85iOosjtXIACIAC5FPRiZSJzjYMNnAz9zFtM62o57FJwv8F5gNEcioqhHwACIACyVXxq1wZhDsqTqdYr7vQUUJ3vwWVrlN0ZQv5HFnHqWdaGF1dGhEYXRhWKR0puqSE8mcL3SyJJKzIM9AJiqUwalQoDl_KSULYIQe8EUAAAAACJhwWMrcS4G24TDeUNy-lgAghsS2ywFz_LWf9-lC35vC9uJTVD3ZCVdweZvESUbjXnSlAQIDJiABIVggHpO4NgvE3jkUaCsbjex6yeTop1Rrh8xIGDg8lLBfQ9ciWCCBc89ef4-b_ggXRzhdVomOYc7cQHYoHB2D8vrQ3Qgv3A", + "clientDataJSON": "eyJ0eXBlIjoid2ViYXV0aG4uY3JlYXRlIiwiY2hhbGxlbmdlIjoidXpuOXUwVHgtTEJkdEdnRVJzYmtIUkJqaVV0NWkycnZtMkJCVFpyV3FFbyIsIm9yaWdpbiI6Imh0dHBzOi8vd2ViYXV0aG4uaW8iLCJjcm9zc09yaWdpbiI6ZmFsc2V9" + } + }`, + // TPM attestation with RSA SHA1 + `{ + "rawId": "UJDoUJoGiDQF_EEZ3G_z9Lfq16_KFaXtMTjwTUrrRlc", + "id": "UJDoUJoGiDQF_EEZ3G_z9Lfq16_KFaXtMTjwTUrrRlc", + "response": { + "clientDataJSON": "eyJvcmlnaW4iOiJodHRwczovL2xvY2FsaG9zdDo0NDMyOSIsImNoYWxsZW5nZSI6IjlKeVVmSmtnOFBxb0tadUQ3Rkh6T0U5ZGJ5Y3VsQzl1ckdUcEdxQm5Fd25oS21uaTRyR1JYeG0zLVpCSEs4eDZyaUpRcUlwQzhxRWEtVDBxSUZUS1RRIiwidHlwZSI6IndlYmF1dGhuLmNyZWF0ZSJ9", + "attestationObject": "o2NmbXRjdHBtZ2F0dFN0bXSmY2FsZzn__mNzaWdZAQBIwu9LPAl-LgxlRzPlvn7L-0yuMnFFn1XALxXtGnmC5-oMIIqfUJWFbgBbkN2l2zPsqOCRT5GQU8ucKNI6HrlbuDAUIq7wjcxG5TzgQt3YtGMWtgEcrZn2ecUlQFKjY67_wZIuHLy443Ki1SjErNPrMrkIPe9lyFhIalMgrWLCol40gYIVr_9xLfgyX55c7XiB-XbUKhDLUv5uPA3CSAiWeWwWx26K2BTV85vHsaG6f2YFTfcQTFs1cTSwMm7A9C2SiQ7N01ENwM1urVxlCvuEsBgiXapR70Oyq_cfiENYY0ti7_w2fvikmfv0z0O1cJOAyUlYWjnWhT707chrVmkFY3ZlcmMyLjBjeDVjglkEXzCCBFswggNDoAMCAQICDwRsOt2imXnV5Z4BftcqfzANBgkqhkiG9w0BAQsFADBBMT8wPQYDVQQDEzZOQ1UtTlRDLUtFWUlELTM2MTA0Q0U0MEJCQ0MxRjQwRDg0QTRCQkQ1MEJFOTkwMjREOTU3RDQwHhcNMTgwMjAxMDAwMDAwWhcNMjUwMTMxMjM1OTU5WjAAMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAmw-4ficURR_sgVfW7cs1iRoDGdxjBpCczF233ba_5WTP-RrsYZPlzWgSN9WXptuywzjZoDlbid7NlduSR1ZFsds4bW71LyKDL62eyqaiAc645gocXAyxdDIDJAeo-3N9Dm4vsw-Gy_0sd2v1UEkBhWjuE1gL5hcaB9EtXSDvHPwmrf0eYn_4cWu9AxqSxpn79JIPYEOUrURr2H8zyG4_P0j1a3MVBmtAymhpXBn9ila-bW7K_k0JYXBh5yAYZDsmHgFsXbUauDWdja3HYzkep9jXkFcegXOMjPr_QSqWRjawEvzoprnJ-QqoWNbaRhuD-UnfgCNbwseU8kZ0aQNjBQIDAQABo4IBjzCCAYswDgYDVR0PAQH_BAQDAgeAMAwGA1UdEwEB_wQCMAAwUwYDVR0gAQH_BEkwRzBFBgkrBgEEAYI3FR8wODA2BggrBgEFBQcCAjAqEyhGQUtFIEZJRE8gVENQQSBUcnVzdGVkIFBsYXRmb3JtIElkZW50aXR5MBAGA1UdJQQJMAcGBWeBBQgDMEoGA1UdEQEB_wRAMD6kPDA6MTgwDgYFZ4EFAgMMBWlkOjEzMBAGBWeBBQICDAdOUENUNnh4MBQGBWeBBQIBDAtpZDpGRkZGRjFEMDAfBgNVHSMEGDAWoBRRfyLI5lOlfNVM3TBYfjD_ZzaMXTAdBgNVHQ4EFgQUO6SUmiOhCHVZcq-88acg2uQkQz8weAYIKwYBBQUHAQEEbDBqMGgGCCsGAQUFBzAChlxodHRwczovL2ZpZG9hbGxpYW5jZS5jby5uei90cG1wa2kvTkNVLU5UQy1LRVlJRC0zNjEwNENFNDBCQkNDMUY0MEQ4NEE0QkJENTBCRTk5MDI0RDk1N0Q0LmNydDANBgkqhkiG9w0BAQsFAAOCAQEAIIyVBkck_SD2nbj4KOwUI6cYZHrjwrcULoEiOSXn9TjTIiB5MdBMvqqNyAXiyWoWd1GEc_MI3mKOzu4g5UTVQQqfiOTrqfuZrpoU0tAeojKnZLj2wYj5GpyOfEkPK3m9qVaDxiYrh6aS8a3w_Iog878EiIaoVALbBt5uAfh0TAHHwSdxHtU8DRJrC43yIqcP9byRqssJmgSNcpMAjw_hcKJxDMD2UurvsMasqyWvK533yNA0-VwXvk3HI0ItSOw_g352D-qOTHI82lJIjc3yKoaNeYKn7RzgcLAF7AesTiiJReY2kU_vLyf-wH54-08T3oyBBJpBCHc1y_Lt5d2qWFkGCDCCBgQwggPsoAMCAQICENBTpEeEh5lpTgeR7VT9oQcwDQYJKoZIhvcNAQELBQAwgb8xCzAJBgNVBAYTAlVTMQswCQYDVQQIDAJNWTESMBAGA1UEBwwJV2FrZWZpZWxkMRYwFAYDVQQKDA1GSURPIEFsbGlhbmNlMQwwCgYDVQQLDANDV0cxNjA0BgNVBAMMLUZJRE8gRmFrZSBUUE0gUm9vdCBDZXJ0aWZpY2F0ZSBBdXRob3JpdHkgMjAxODExMC8GCSqGSIb3DQEJARYiY29uZm9ybWFuY2UtdG9vbHNAZmlkb2FsbGlhbmNlLm9yZzAeFw0xNzAyMDEwMDAwMDBaFw0zNTAxMzEyMzU5NTlaMEExPzA9BgNVBAMTNk5DVS1OVEMtS0VZSUQtMzYxMDRDRTQwQkJDQzFGNDBEODRBNEJCRDUwQkU5OTAyNEQ5NTdENDCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBANc-c30RpQd-_LCoiLJbXz3t_vqciOIovwjez79_DtVgi8G9Ph-tPL-lC0ueFGBMSPcKd_RDdSFe2QCYQd9e0DtiFxra-uWGa0olI1hHI7bK2GzNAZSTKEbwgqpf8vXMQ-7SPajg6PfxSOLH_Nj2yd6tkNkUSdlGtWfY8XGB3n-q--nt3UHdUQWEtgUoTe5abBXsG7MQSuTNoad3v6vk-tLd0W44ivM6pbFqFUHchx8mGLApCpjlVXrfROaCoc9E91hG9B-WNvekJ0dM6kJ658Hy7yscQ6JdqIEolYojCtWaWNmwcfv--OE1Ax_4Ub24gl3hpB9EOcBCzpb4UFmLYUECAwEAAaOCAXcwggFzMAsGA1UdDwQEAwIBhjAWBgNVHSAEDzANMAsGCSsGAQQBgjcVHzAbBgNVHSUEFDASBgkrBgEEAYI3FSQGBWeBBQgDMBIGA1UdEwEB_wQIMAYBAf8CAQAwHwYDVR0OBBgEFsIUUX8iyOZTpXzVTN0wWH4w_2c2jF0wHwYDVR0jBBgwFqAUXH82LZCtWry6jnXa3jqg7cFOAoswaAYDVR0fBGEwXzBdoFugWYZXaHR0cHM6Ly9maWRvYWxsaWFuY2UuY28ubnovdHBtcGtpL2NybC9GSURPIEZha2UgVFBNIFJvb3QgQ2VydGlmaWNhdGUgQXV0aG9yaXR5IDIwMTguY3JsMG8GCCsGAQUFBwEBBGMwYTBfBggrBgEFBQcwAoZTaHR0cHM6Ly9maWRvYWxsaWFuY2UuY28ubnovdHBtcGtpL0ZJRE8gRmFrZSBUUE0gUm9vdCBDZXJ0aWZpY2F0ZSBBdXRob3JpdHkgMjAxOC5jcnQwDQYJKoZIhvcNAQELBQADggIBAG138t55DF9nPJbvbPQZOypmyTPpNne0A5fh69P1fHZ5qdE2PDz3cf5Tl-8OPI4xQniEFNPcXMb7KlhMM6zCl4GkZtNN4MxygdFjQ1gTZOBDpt7Dwziij0MakmwyC0RYTNtbSyVhHUevgw9rnu13EzqxPyL5JD-UqADh2Y51MS0qy7IOgegLQv-eJzSNUgHxFJreUzz4PU6yzSsTyyYDW-H4ZjAQKienVp8ewZf8oHGWHGQFGa5E9m1P8vxCMZ7pIzeQweCVYrs3q7unu4nzBAIXLPI092kYFUgyz3lIaSB3XEiPBokpupX6Zmgrfphb-XX3tbenH5hkxfumueA5RMHTMu5TVjhJXiV0yM3q5W5xrQHdJlF5nOdJDEE-Kb7nm6xaT1DDpafqBc5vEDMkJmBA4AXHUY7JPGqEEzEenT7k6Wn5IQLZg4qc8Irnj__yM7xUhJWJam47KVbLA4WFu-IKvJrkP5GSglZ9qASOCxBHaOL2UcTAg50uvhUSwur2KSak2vlENdmAijwdAL4LLQWrkFd-9NBwcNwTdfK4ekEHP1l4BwJtkNwW6etUgeA5rkW2JLocXoBq5v7GSk4_CBoKhyiahQGQQ9SZFGeBJhzzkK9yN-yKskcVjjjInSHPl-ZpeOK3sI08sEyTH0gxlTtRoX0MKDsMAHEVToe5o1u9Z3B1YkFyZWFZATYAAQALAAYEcgAgnf_L82w4OuaZ-5ho3G3LidcVOIS-KAOSLBJBWL-tIq4AEAAQCAAAAAAAAQCl9siJwqoHJ2pCwEKyLQ_u6zGcZDKZtA0jtvtn1aPlIe7wFAvQNgjI6KDiQsDPTCVeJj_RA441VbV0Z4oX2b68quDY0Gf4VpF4KWfNPdKH6H4E882m8OnBb10mhaNbPxTmDVDZLQZjh3ubX1Z56FNg6cQmz4bEnHF-7X1l7AcNORhzdzgM7uRXhwo9UsAzpu4Io1OCTsb5DaDnng3f3Y9qDn8OG3MI_5IYtm1qGgmY72nSEiIhhPCk2lvmajN6A4tWgUstc7QtdlKEPBd-ITtGdKYTSwqihaHzBQd8D-d_HDqgcOWECLKo51_YqyaEiuGlv6sPon1LMsEL6PlVw47PaGNlcnRJbmZvWKH_VENHgBcAIgALEeaO1E21Ny4UKW4vhKzHg5h1GIGSHjD8IqBvi3PHlFMAFF6MXAvgUX_Rbc04fmdB2TyLG-mdAAAAAUdwF0hVaXtLxoVgpQFzfvmNNFZV-wAiAAuYlrm-5Jg3251TsEdZ8NV11xd4X5O3q0AFLmammw658QAiAAtuzX-04mcxAHq9kO70Ew3vJCOmCS0UvQzZB2CNCeGXpWhhdXRoRGF0YVkBZ0mWDeWIDoxodDQXD2R2YFuP5K65ooYyx5lc87qDHZdjQQAAAHXyRLZ-U2RP1Z-Qw5YicxfbACBQkOhQmgaINAX8QRncb_P0t-rXr8oVpe0xOPBNSutGV6QBAwM5__4gWQEApfbIicKqBydqQsBCsi0P7usxnGQymbQNI7b7Z9Wj5SHu8BQL0DYIyOig4kLAz0wlXiY_0QOONVW1dGeKF9m-vKrg2NBn-FaReClnzT3Sh-h-BPPNpvDpwW9dJoWjWz8U5g1Q2S0GY4d7m19WeehTYOnEJs-GxJxxfu19ZewHDTkYc3c4DO7kV4cKPVLAM6buCKNTgk7G-Q2g554N392Pag5_DhtzCP-SGLZtahoJmO9p0hIiIYTwpNpb5mozegOLVoFLLXO0LXZShDwXfiE7RnSmE0sKooWh8wUHfA_nfxw6oHDlhAiyqOdf2KsmhIrhpb-rD6J9SzLBC-j5VcOOzyFDAQAB" + }, + "type": "public-key" + }`, + // TPM attestation with RSA SHA256 + `{ + "rawId": "h9XMhkVePN1Prq9Ks_VfwIsVZvt-jmSRTEnevTc-KB8", + "id": "h9XMhkVePN1Prq9Ks_VfwIsVZvt-jmSRTEnevTc-KB8", + "response": { + "clientDataJSON": "eyJvcmlnaW4iOiJodHRwczovL2xvY2FsaG9zdDo0NDMyOSIsImNoYWxsZW5nZSI6ImdIckFrNHBOZTJWbEIwSExlS2NsSTJQNlFFYTgzUHVHZWlqVEhNdHBiaFk5S2x5YnlobHdGX1Z6UmU3eWhhYlhhZ1d1WTZya0RXZnZ2aE5xZ2gybzdBIiwidHlwZSI6IndlYmF1dGhuLmNyZWF0ZSJ9", + "attestationObject": "o2NmbXRjdHBtZ2F0dFN0bXSmY2FsZzkBAGNzaWdZAQA6Gh1Oa3-8vCY8bTrpUHA4zp4UCsbuh36tH09G-qWlvQdoqEQsJJQu1Rz61_mFes9CXE2cxiJV8pEwxtUUTSZQWnamVU1x9bBk07qcHqAuamP_NDAahHhZ9D46q9JklT3aVdhbaZVh0y5b8NZB2eUfKqcUmM0JCxLP9ZfSe7XcVguhQVEduM6Qnl9R1zRh7cquOa8UOEpdXkt1-drsOtrA9c0UJPYzkI8qscCDc-xfzo2xv12tLXjRq395JnynHhjzJIz8Ch2IYQUiMSM6TQDcnvzDEvRgril9NC0aIkHd79omIZNnBjEDfjyqOZbBffjGyvt1Eikz4M0EE8e7N4uRY3ZlcmMyLjBjeDVjglkEXzCCBFswggNDoAMCAQICDwQ_ozlil_l5hh6NlMsLzzANBgkqhkiG9w0BAQsFADBBMT8wPQYDVQQDEzZOQ1UtTlRDLUtFWUlELTM2MTA0Q0U0MEJCQ0MxRjQwRDg0QTRCQkQ1MEJFOTkwMjREOTU3RDQwHhcNMTgwMjAxMDAwMDAwWhcNMjUwMTMxMjM1OTU5WjAAMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAor_6-4WYizZdOQ9Ia_offaIdL2BVGtGDq8jQxo16ymBSOWCP15gZt9QAkqowS3ayqEh48Pg5SdA7F5kcjD_FqKaZDBOqkjvJivdo7FKv7EaUI2al9B7h0pXIRb97jn2z0zPlXz6RV_RmBe3CCljyxrhav7bTkCXEJUnkNgxsWgLGBIW6VSVct0z42xBB6_6mYekWIej5vXLqB8AuzsqnLbU5jOohfJiI5urFso12j6YCWZ_kXK4j8e4IoHUOjWgtHXdb3kP8PvI948hcJpIEpuuLDZDDOCOPI1wAlryGwz_tJLarODZzD1XhG3BMlXi1TG7x1s-AriC3A7B89wuSpwIDAQABo4IBjzCCAYswDgYDVR0PAQH_BAQDAgeAMAwGA1UdEwEB_wQCMAAwUwYDVR0gAQH_BEkwRzBFBgkrBgEEAYI3FR8wODA2BggrBgEFBQcCAjAqEyhGQUtFIEZJRE8gVENQQSBUcnVzdGVkIFBsYXRmb3JtIElkZW50aXR5MBAGA1UdJQQJMAcGBWeBBQgDMEoGA1UdEQEB_wRAMD6kPDA6MTgwDgYFZ4EFAgMMBWlkOjEzMBAGBWeBBQICDAdOUENUNnh4MBQGBWeBBQIBDAtpZDpGRkZGRjFEMDAfBgNVHSMEGDAWoBRRfyLI5lOlfNVM3TBYfjD_ZzaMXTAdBgNVHQ4EFgQUS1ZtGu6ZoewTH3mq04Ytxa4kOQcweAYIKwYBBQUHAQEEbDBqMGgGCCsGAQUFBzAChlxodHRwczovL2ZpZG9hbGxpYW5jZS5jby5uei90cG1wa2kvTkNVLU5UQy1LRVlJRC0zNjEwNENFNDBCQkNDMUY0MEQ4NEE0QkJENTBCRTk5MDI0RDk1N0Q0LmNydDANBgkqhkiG9w0BAQsFAAOCAQEAbp-Xp9W0vyY08YUHxerc6FnFdXZ6KFuQTZ4hze60BWexCSQOee25gqOoQaQr9ufS3ImLAoV4Ifc3vKVBQvBRwMjG3pJINoWr0p2McI0F2SNclH4M0sXFYHRlmHQ2phZB6Ddd-XL8PsGyiXRI6gVacVw5ZiVEBsRrekLH-Zy25EeqS3SxaBVnEd-HZ6BGGgbflgFtyGP9fQ5YSORC-Btno_uJbmRiZm4iHiEULp9wWEWOJIOXv9tVQKsYpPg58L1_Dgc8oml1YG5a8qK3jaR77tcUgZyYy5GOk1zIsXv36f0SkmLcNTiTjrhdGVcKs2KpW5fQgm_llQ5cvhR1jlY6dFkGCDCCBgQwggPsoAMCAQICENBTpEeEh5lpTgeR7VT9oQcwDQYJKoZIhvcNAQELBQAwgb8xCzAJBgNVBAYTAlVTMQswCQYDVQQIDAJNWTESMBAGA1UEBwwJV2FrZWZpZWxkMRYwFAYDVQQKDA1GSURPIEFsbGlhbmNlMQwwCgYDVQQLDANDV0cxNjA0BgNVBAMMLUZJRE8gRmFrZSBUUE0gUm9vdCBDZXJ0aWZpY2F0ZSBBdXRob3JpdHkgMjAxODExMC8GCSqGSIb3DQEJARYiY29uZm9ybWFuY2UtdG9vbHNAZmlkb2FsbGlhbmNlLm9yZzAeFw0xNzAyMDEwMDAwMDBaFw0zNTAxMzEyMzU5NTlaMEExPzA9BgNVBAMTNk5DVS1OVEMtS0VZSUQtMzYxMDRDRTQwQkJDQzFGNDBEODRBNEJCRDUwQkU5OTAyNEQ5NTdENDCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBANc-c30RpQd-_LCoiLJbXz3t_vqciOIovwjez79_DtVgi8G9Ph-tPL-lC0ueFGBMSPcKd_RDdSFe2QCYQd9e0DtiFxra-uWGa0olI1hHI7bK2GzNAZSTKEbwgqpf8vXMQ-7SPajg6PfxSOLH_Nj2yd6tkNkUSdlGtWfY8XGB3n-q--nt3UHdUQWEtgUoTe5abBXsG7MQSuTNoad3v6vk-tLd0W44ivM6pbFqFUHchx8mGLApCpjlVXrfROaCoc9E91hG9B-WNvekJ0dM6kJ658Hy7yscQ6JdqIEolYojCtWaWNmwcfv--OE1Ax_4Ub24gl3hpB9EOcBCzpb4UFmLYUECAwEAAaOCAXcwggFzMAsGA1UdDwQEAwIBhjAWBgNVHSAEDzANMAsGCSsGAQQBgjcVHzAbBgNVHSUEFDASBgkrBgEEAYI3FSQGBWeBBQgDMBIGA1UdEwEB_wQIMAYBAf8CAQAwHwYDVR0OBBgEFsIUUX8iyOZTpXzVTN0wWH4w_2c2jF0wHwYDVR0jBBgwFqAUXH82LZCtWry6jnXa3jqg7cFOAoswaAYDVR0fBGEwXzBdoFugWYZXaHR0cHM6Ly9maWRvYWxsaWFuY2UuY28ubnovdHBtcGtpL2NybC9GSURPIEZha2UgVFBNIFJvb3QgQ2VydGlmaWNhdGUgQXV0aG9yaXR5IDIwMTguY3JsMG8GCCsGAQUFBwEBBGMwYTBfBggrBgEFBQcwAoZTaHR0cHM6Ly9maWRvYWxsaWFuY2UuY28ubnovdHBtcGtpL0ZJRE8gRmFrZSBUUE0gUm9vdCBDZXJ0aWZpY2F0ZSBBdXRob3JpdHkgMjAxOC5jcnQwDQYJKoZIhvcNAQELBQADggIBAG138t55DF9nPJbvbPQZOypmyTPpNne0A5fh69P1fHZ5qdE2PDz3cf5Tl-8OPI4xQniEFNPcXMb7KlhMM6zCl4GkZtNN4MxygdFjQ1gTZOBDpt7Dwziij0MakmwyC0RYTNtbSyVhHUevgw9rnu13EzqxPyL5JD-UqADh2Y51MS0qy7IOgegLQv-eJzSNUgHxFJreUzz4PU6yzSsTyyYDW-H4ZjAQKienVp8ewZf8oHGWHGQFGa5E9m1P8vxCMZ7pIzeQweCVYrs3q7unu4nzBAIXLPI092kYFUgyz3lIaSB3XEiPBokpupX6Zmgrfphb-XX3tbenH5hkxfumueA5RMHTMu5TVjhJXiV0yM3q5W5xrQHdJlF5nOdJDEE-Kb7nm6xaT1DDpafqBc5vEDMkJmBA4AXHUY7JPGqEEzEenT7k6Wn5IQLZg4qc8Irnj__yM7xUhJWJam47KVbLA4WFu-IKvJrkP5GSglZ9qASOCxBHaOL2UcTAg50uvhUSwur2KSak2vlENdmAijwdAL4LLQWrkFd-9NBwcNwTdfK4ekEHP1l4BwJtkNwW6etUgeA5rkW2JLocXoBq5v7GSk4_CBoKhyiahQGQQ9SZFGeBJhzzkK9yN-yKskcVjjjInSHPl-ZpeOK3sI08sEyTH0gxlTtRoX0MKDsMAHEVToe5o1u9Z3B1YkFyZWFZATYAAQALAAYEcgAgnf_L82w4OuaZ-5ho3G3LidcVOIS-KAOSLBJBWL-tIq4AEAAQCAAAAAAAAQDPtSggWlsjcFiQO61-hUF8i-3FPcyvuARcy3p1seZ-_B4ClhNh5U-T0v0flMU5p6nsNDWj4f6-soe-2vVJMTm2d26uKYD2zwdrkrYYXRu5IFqUXqF-kY99v8RcrAF7DQKDo-E4XhiMz6uECvnjEloGfTYZrVuQ1mdjQ8Qki7U-9SQHMW_IsaI8ZKHtupXNhM5YPQyFbDHHXSE_iyPGh2mY4SR466ouesIuG0NccCUk5UDIvS__OUmNaX7aBrKTlnkMFjkCA1ZDFC99ZQoLFCJQHqnOU7m8zSvTJpUyG2feWgAL2Gl05V3I_lb_v5yELXcihFoA33QIOSpDmKqKV3SXaGNlcnRJbmZvWK3_VENHgBcAIgALEeaO1E21Ny4UKW4vhKzHg5h1GIGSHjD8IqBvi3PHlFMAIBo8rAwJFDGsmQjauX_FCBQenvBa2ApBcR_gOx2qW2QAAAAAAUdwF0hVaXtLxoVgpQFzfvmNNFZV-wAiAAsXPoJSq0uhvU6VLf0uIelHBNFHEanasKAoTp-lQ2dRGAAiAAuO1HPzTRRabZhwPvHQh0b1MnLIG8EVGNfpshASWSfjQWhhdXRoRGF0YVkBZ0mWDeWIDoxodDQXD2R2YFuP5K65ooYyx5lc87qDHZdjQQAAAEOn1tk6ig0R6JqUps9xBy9zACCH1cyGRV483U-ur0qz9V_AixVm-36OZJFMSd69Nz4oH6QBAwM5AQAgWQEAz7UoIFpbI3BYkDutfoVBfIvtxT3Mr7gEXMt6dbHmfvweApYTYeVPk9L9H5TFOaep7DQ1o-H-vrKHvtr1STE5tndurimA9s8Ha5K2GF0buSBalF6hfpGPfb_EXKwBew0Cg6PhOF4YjM-rhAr54xJaBn02Ga1bkNZnY0PEJIu1PvUkBzFvyLGiPGSh7bqVzYTOWD0MhWwxx10hP4sjxodpmOEkeOuqLnrCLhtDXHAlJOVAyL0v_zlJjWl-2gayk5Z5DBY5AgNWQxQvfWUKCxQiUB6pzlO5vM0r0yaVMhtn3loAC9hpdOVdyP5W_7-chC13IoRaAN90CDkqQ5iqild0lyFDAQAB" + }, + "type": "public-key" + }`, +} + +func TestTPMAttestationVerificationFailAttStatement(t *testing.T) { + tests := []struct { + name string + att AttestationObject + wantErr string + }{ + { + "TPM Negative Test AttStatement Missing Ver", + AttestationObject{}, + "Error retreiving ver value", + }, + { + "TPM Negative Test AttStatement Ver not 2.0", + AttestationObject{AttStatement: map[string]interface{}{"ver": "foo.bar"}}, + "WebAuthn only supports TPM 2.0 currently", + }, + { + "TPM Negative Test AttStatement Alg not present", + AttestationObject{AttStatement: map[string]interface{}{"ver": "2.0"}}, + "Error retreiving alg value", + }, + { + "TPM Negative Test AttStatement x5c not present", + AttestationObject{AttStatement: map[string]interface{}{"ver": "2.0", "alg": int64(0)}}, + ErrNotImplemented.Details, + }, + { + "TPM Negative Test AttStatement ecdaaKeyId present", + AttestationObject{AttStatement: map[string]interface{}{"ver": "2.0", "alg": int64(0), "x5c": []interface{}{}, "ecdaaKeyId": []byte{}}}, + ErrNotImplemented.Details, + }, + { + "TPM Negative Test AttStatement sig not present", + AttestationObject{AttStatement: map[string]interface{}{"ver": "2.0", "alg": int64(0), "x5c": []interface{}{}}}, + "Error retreiving sig value", + }, + { + "TPM Negative Test AttStatement certInfo not present", + AttestationObject{AttStatement: map[string]interface{}{"ver": "2.0", "alg": int64(0), "x5c": []interface{}{}, "sig": []byte{}}}, + "Error retreiving certInfo value", + }, + { + "TPM Negative Test AttStatement pubArea not present", + AttestationObject{AttStatement: map[string]interface{}{"ver": "2.0", "alg": int64(0), "x5c": []interface{}{}, "sig": []byte{}, "certInfo": []byte{}}}, + "Error retreiving pubArea value", + }, + { + "TPM Negative Test pubArea not TPMT_PUBLIC", + AttestationObject{AttStatement: defaultAttStatement}, + "Unable to decode TPMT_PUBLIC in attestation statement", + }, + } + for _, tt := range tests { + attestationKey, _, err := verifyTPMFormat(tt.att, nil) + if tt.wantErr != "" { + assert.Contains(t, err.Error(), tt.wantErr) + } else { + assert.Equal(t, "tpm", attestationKey) + } + } +} + +var defaultAttStatement = map[string]interface{}{"ver": "2.0", "alg": int64(0), "x5c": []interface{}{}, "sig": []byte{}, "certInfo": []byte{}, "pubArea": []byte{}} + +func TestTPMAttestationVerificationFailPubArea(t *testing.T) { + tests := []struct { + name string + public googletpm.Public + wantErr string + }{} + for _, tt := range tests { + attStmt := make(map[string]interface{}, len(defaultAttStatement)) + for id, v := range defaultAttStatement { + attStmt[id] = v + } + + //attStmt["pubArea"], _ = tt.public.Encode() + att := AttestationObject{ + AttStatement: attStmt, + } + attestationKey, _, err := verifyTPMFormat(att, nil) + if tt.wantErr != "" { + assert.Contains(t, err.Error(), tt.wantErr) + } else { + assert.Equal(t, "tpm", attestationKey) + } + } +} + +func TestTPMAttestationVerificationFailCertInfo(t *testing.T) { + tests := []struct { + name string + att AttestationObject + clientDataHash [32]byte + wantErr string + }{} + for _, tt := range tests { + attestationKey, _, err := verifyTPMFormat(tt.att, tt.clientDataHash[:]) + if tt.wantErr != "" { + assert.Contains(t, err.Error(), tt.wantErr) + } else { + assert.Equal(t, "tpm", attestationKey) + } + } +} + +func TestTPMAttestationVerificationFailX5c(t *testing.T) { + tests := []struct { + name string + att AttestationObject + clientDataHash [32]byte + wantErr string + }{} + for _, tt := range tests { + attestationKey, _, err := verifyTPMFormat(tt.att, tt.clientDataHash[:]) + if tt.wantErr != "" { + assert.Contains(t, err.Error(), tt.wantErr) + } else { + assert.Equal(t, "tpm", attestationKey) + } + } +} diff --git a/protocol/webauthncose/webauthncose.go b/protocol/webauthncose/webauthncose.go index 61bbcfb4..9c566944 100644 --- a/protocol/webauthncose/webauthncose.go +++ b/protocol/webauthncose/webauthncose.go @@ -14,6 +14,7 @@ import ( "golang.org/x/crypto/ed25519" + "github.com/go-webauthn/webauthn/protocol/googletpm" "github.com/go-webauthn/webauthn/protocol/webauthncbor" ) @@ -221,7 +222,8 @@ func ParseFIDOPublicKey(keyBytes []byte) (interface{}, error) { // COSEAlgorithmIdentifier From ยง5.10.5. A number identifying a cryptographic algorithm. The algorithm // identifiers SHOULD be values registered in the IANA COSE Algorithms registry // [https://www.w3.org/TR/webauthn/#biblio-iana-cose-algs-reg], for instance, -7 for "ES256" -// and -257 for "RS256". +// +// and -257 for "RS256". type COSEAlgorithmIdentifier int const ( @@ -261,6 +263,42 @@ const ( RSAKey COSEKeyType = 3 ) +// The COSE Elliptic Curves +// https://www.iana.org/assignments/cose/cose.xhtml#elliptic-curves +type COSEEllipticCurve int + +const ( + // EC2 NIST P-256 also known as secp256r1 + P256 COSEEllipticCurve = 1 + // EC2 NIST P-384 also known as secp384r1 + P384 COSEEllipticCurve = 2 + // EC2 NIST P-521 also known as secp521r1 + P521 COSEEllipticCurve = 3 + // OKP X25519 for use w/ ECDH only + X25519 COSEEllipticCurve = 4 + // OKP X448 for use w/ ECDH only + X448 COSEEllipticCurve = 5 + // OKP Ed25519 for use w/ EdDSA only + Ed25519 COSEEllipticCurve = 6 + // OKP Ed448 for use w/ EdDSA only + Ed448 COSEEllipticCurve = 7 + // EC2 SECG secp256k1 curve + secp256k1 COSEEllipticCurve = 8 +) + +func (k *EC2PublicKeyData) TPMCurveID() googletpm.EllipticCurve { + switch COSEEllipticCurve(k.Curve) { + case P256: + return googletpm.CurveNISTP256 // TPM_ECC_NIST_P256 + case P384: + return googletpm.CurveNISTP384 // TPM_ECC_NIST_P384 + case P521: + return googletpm.CurveNISTP521 // TPM_ECC_NIST_P521 + default: + return googletpm.EllipticCurve(0) // TPM_ECC_NONE + } +} + func VerifySignature(key interface{}, data []byte, sig []byte) (bool, error) { switch k := key.(type) { case OKPPublicKeyData: