package protocol import ( "crypto/ecdsa" "crypto/ed25519" "crypto/elliptic" "crypto/rand" "crypto/rsa" "crypto/sha256" "crypto/x509" "crypto/x509/pkix" "encoding/asn1" "encoding/binary" "encoding/pem" "errors" "math" "testing" "github.com/google/go-tpm/tpm2" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" "github.com/go-webauthn/webauthn/protocol/webauthncbor" "github.com/go-webauthn/webauthn/protocol/webauthncose" ) func TestForEachSAN(t *testing.T) { t.Run("ShouldReturnErrorWhenAsn1UnmarshalFails", func(t *testing.T) { err := forEachSAN([]byte{0x01, 0x02, 0x03}, func(tag int, data []byte) error { return nil }) require.Error(t, err) }) t.Run("ShouldReturnErrorWhenTrailingDataExists", func(t *testing.T) { extension := []byte{0x30, 0x00, 0x00} err := forEachSAN(extension, func(tag int, data []byte) error { return nil }) assert.EqualError(t, err, "x509: trailing data after X.509 extension") }) t.Run("ShouldReturnStructuralErrorWhenNotACompoundSequence", func(t *testing.T) { extension, marshalErr := asn1.Marshal([]byte{0xAA, 0xBB}) require.NoError(t, marshalErr) err := forEachSAN(extension, func(tag int, data []byte) error { return nil }) require.EqualError(t, err, "asn1: structure error: bad SAN sequence") var structuralErr asn1.StructuralError require.True(t, errors.As(err, &structuralErr)) }) t.Run("ShouldReturnErrorWhenInnerAsn1UnmarshalFails", func(t *testing.T) { extension := []byte{0x30, 0x01, 0xFF} err := forEachSAN(extension, func(tag int, data []byte) error { return nil }) assert.EqualError(t, err, "asn1: syntax error: truncated base 128 integer") }) } func TestParseSANExtension(t *testing.T) { makeSANWithDN := func(t *testing.T, dnDER []byte) []byte { t.Helper() gn := asn1.RawValue{ Class: asn1.ClassContextSpecific, Tag: nameTypeDN, IsCompound: true, Bytes: dnDER, } sanDER, err := asn1.Marshal([]asn1.RawValue{gn}) require.NoError(t, err) return sanDER } t.Run("ShouldReturnErrorWhenAsn1UnmarshalOfRdnSequenceFails", func(t *testing.T) { san := makeSANWithDN(t, []byte{0x01, 0x02, 0x03}) manufacturer, model, version, err := parseSANExtension(san) assert.EqualError(t, err, "asn1: structure error: tags don't match (16 vs {class:0 tag:1 length:2 isCompound:false}) {optional:false explicit:false application:false private:false defaultValue: tag: stringType:0 timeType:0 set:false omitEmpty:false} RDNSequence @2") assert.Empty(t, manufacturer) assert.Empty(t, model) assert.Empty(t, version) }) t.Run("ShouldSkipEmptyRdnSets", func(t *testing.T) { rdnSeq := pkix.RDNSequence{ {}, { { Type: oidTCGAtTpmManufacturer, Value: "id:414D4400", }, { Type: oidTCGAtTpmModel, Value: "ModelX", }, { Type: oidTCGAtTPMVersion, Value: "id:00070002", }, }, } dnDER, err := asn1.Marshal(rdnSeq) require.NoError(t, err) san := makeSANWithDN(t, dnDER) manufacturer, model, version, err := parseSANExtension(san) require.NoError(t, err) assert.Equal(t, "414D4400", manufacturer) assert.Equal(t, "ModelX", model) assert.Equal(t, "00070002", version) }) t.Run("ShouldSkipNonStringAttributeValues", func(t *testing.T) { rdnSeq := pkix.RDNSequence{ { { Type: oidTCGAtTpmManufacturer, Value: asn1.RawValue{ Class: asn1.ClassUniversal, Tag: asn1.TagOctetString, Bytes: []byte{0xAA, 0xBB}, }, }, { Type: oidTCGAtTpmModel, Value: "ModelX", }, { Type: oidTCGAtTPMVersion, Value: "id:00070002", }, }, } dnDER, err := asn1.Marshal(rdnSeq) require.NoError(t, err) san := makeSANWithDN(t, dnDER) manufacturer, model, version, err := parseSANExtension(san) require.NoError(t, err) assert.Empty(t, manufacturer) assert.Equal(t, "ModelX", model) assert.Equal(t, "00070002", version) }) } func TestIsValidTPMManufacturer(t *testing.T) { testCases := []struct { name string id string want bool }{ { name: "ShouldReturnTrueForKnownManufacturer", id: "414D4400", want: true, }, { name: "ShouldReturnFalseForUnknownManufacturer", id: "DEADBEEF", want: false, }, } for _, tc := range testCases { t.Run(tc.name, func(t *testing.T) { assert.Equal(t, tc.want, isValidTPMManufacturer(tc.id)) }) } } func TestTpmRemoveEKU(t *testing.T) { t.Run("ShouldFailWhenAikEkuMissing", func(t *testing.T) { cert := &x509.Certificate{ UnknownExtKeyUsage: []asn1.ObjectIdentifier{ {1, 2, 3, 4}, }, } assert.EqualError(t, tpmRemoveEKU(cert), "Attestation Identity Key certificate missing required Extended Key Usage.") }) t.Run("ShouldRemoveAikAndMicrosoftEkuButKeepOtherUnknownEku", func(t *testing.T) { other := asn1.ObjectIdentifier{1, 2, 3, 4} cert := &x509.Certificate{ UnknownExtKeyUsage: []asn1.ObjectIdentifier{ oidMicrosoftKpPrivacyCA, other, oidTCGKpAIKCertificate, }, } require.Nil(t, tpmRemoveEKU(cert)) require.Len(t, cert.UnknownExtKeyUsage, 1) assert.True(t, cert.UnknownExtKeyUsage[0].Equal(other)) }) } func TestTpmParseSANExtension(t *testing.T) { makeSAN := func(t *testing.T, manufacturer, model, version string) []byte { t.Helper() rdnSeq := pkix.RDNSequence{ { { Type: oidTCGAtTpmManufacturer, Value: manufacturer, }, { Type: oidTCGAtTpmModel, Value: model, }, { Type: oidTCGAtTPMVersion, Value: version, }, }, } nameDER, err := asn1.Marshal(rdnSeq) require.NoError(t, err) gn := asn1.RawValue{ Class: asn1.ClassContextSpecific, Tag: nameTypeDN, IsCompound: true, Bytes: nameDER, } sanDER, err := asn1.Marshal([]asn1.RawValue{gn}) require.NoError(t, err) return sanDER } t.Run("ShouldFailWhenSanExtensionMalformed", func(t *testing.T) { cert := &x509.Certificate{ Extensions: []pkix.Extension{ {Id: oidExtensionSubjectAltName, Value: []byte{0x01, 0x02, 0x03}}, }, } protoErr := tpmParseSANExtension(cert) require.EqualError(t, protoErr, "Authenticator with invalid Authenticator Identity Key SAN data encountered during attestation validation.") assert.Equal(t, ErrInvalidAttestation.Type, protoErr.Type) }) t.Run("ShouldFailWhenSanDataMissing", func(t *testing.T) { cert := &x509.Certificate{ Extensions: []pkix.Extension{}, } assert.EqualError(t, tpmParseSANExtension(cert), "Invalid SAN data in AIK certificate.") }) t.Run("ShouldRemoveUnhandledCriticalSanExtensionWhenPresent", func(t *testing.T) { san := makeSAN(t, "id:414D4400", "ModelX", "id:00070002") other := asn1.ObjectIdentifier{1, 2, 3, 4} cert := &x509.Certificate{ Extensions: []pkix.Extension{ {Id: oidExtensionSubjectAltName, Value: san}, }, UnhandledCriticalExtensions: []asn1.ObjectIdentifier{ oidExtensionSubjectAltName, other, }, } protoErr := tpmParseSANExtension(cert) require.Nil(t, protoErr) require.Len(t, cert.UnhandledCriticalExtensions, 1) assert.True(t, cert.UnhandledCriticalExtensions[0].Equal(other)) }) } func TestTpmParseAIKAttCA(t *testing.T) { makeSAN := func(t *testing.T, manufacturer, model, version string) []byte { t.Helper() rdnSeq := pkix.RDNSequence{ { { Type: oidTCGAtTpmManufacturer, Value: manufacturer, }, { Type: oidTCGAtTpmModel, Value: model, }, { Type: oidTCGAtTPMVersion, Value: version, }, }, } nameDER, err := asn1.Marshal(rdnSeq) require.NoError(t, err) gn := asn1.RawValue{ Class: asn1.ClassContextSpecific, Tag: nameTypeDN, IsCompound: true, Bytes: nameDER, } sanDER, err := asn1.Marshal([]asn1.RawValue{gn}) require.NoError(t, err) return sanDER } t.Run("ShouldFailWhenSanParsingFails", func(t *testing.T) { leaf := &x509.Certificate{ Extensions: []pkix.Extension{ {Id: oidExtensionSubjectAltName, Value: []byte{0x01, 0x02, 0x03}}, }, UnknownExtKeyUsage: []asn1.ObjectIdentifier{ oidTCGKpAIKCertificate, }, } err := tpmParseAIKAttCA(leaf, nil) require.Error(t, err) assert.Equal(t, ErrInvalidAttestation.Type, err.Type) assert.Equal(t, "Authenticator with invalid Authenticator Identity Key SAN data encountered during attestation validation.", err.Details) assert.Equal(t, "Error occurred parsing SAN extension: asn1: syntax error: data truncated", err.DevInfo) }) t.Run("ShouldFailWhenParentMissingAikEku", func(t *testing.T) { otherEku := asn1.ObjectIdentifier{1, 2, 3, 4} leaf := &x509.Certificate{ Extensions: []pkix.Extension{ {Id: oidExtensionSubjectAltName, Value: makeSAN(t, "id:414D4400", "ModelX", "id:00070002")}, }, UnknownExtKeyUsage: []asn1.ObjectIdentifier{ oidTCGKpAIKCertificate, oidMicrosoftKpPrivacyCA, otherEku, }, } parent := &x509.Certificate{ UnknownExtKeyUsage: []asn1.ObjectIdentifier{ oidMicrosoftKpPrivacyCA, otherEku, }, } err := tpmParseAIKAttCA(leaf, []*x509.Certificate{parent}) assert.EqualError(t, err, "Attestation Identity Key certificate missing required Extended Key Usage.") }) t.Run("ShouldSucceedAndMutateCertificates", func(t *testing.T) { otherEku := asn1.ObjectIdentifier{1, 2, 3, 4} otherCritical := asn1.ObjectIdentifier{2, 999, 1} leaf := &x509.Certificate{ Extensions: []pkix.Extension{ {Id: oidExtensionSubjectAltName, Value: makeSAN(t, "id:414D4400", "ModelX", "id:00070002")}, }, UnhandledCriticalExtensions: []asn1.ObjectIdentifier{ oidExtensionSubjectAltName, otherCritical, }, UnknownExtKeyUsage: []asn1.ObjectIdentifier{ oidTCGKpAIKCertificate, oidMicrosoftKpPrivacyCA, otherEku, }, } parent := &x509.Certificate{ UnknownExtKeyUsage: []asn1.ObjectIdentifier{ oidTCGKpAIKCertificate, oidMicrosoftKpPrivacyCA, otherEku, }, } err := tpmParseAIKAttCA(leaf, []*x509.Certificate{parent}) require.Nil(t, err) require.Len(t, leaf.UnhandledCriticalExtensions, 1) assert.True(t, leaf.UnhandledCriticalExtensions[0].Equal(otherCritical)) require.Len(t, leaf.UnknownExtKeyUsage, 1) assert.True(t, leaf.UnknownExtKeyUsage[0].Equal(otherEku)) require.Len(t, parent.UnknownExtKeyUsage, 1) assert.True(t, parent.UnknownExtKeyUsage[0].Equal(otherEku)) }) t.Run("ShouldFailWhenLeafMissingAikEku", func(t *testing.T) { leaf := &x509.Certificate{ Extensions: []pkix.Extension{ {Id: oidExtensionSubjectAltName, Value: makeSAN(t, "id:414D4400", "ModelX", "id:00070002")}, }, UnknownExtKeyUsage: []asn1.ObjectIdentifier{ oidMicrosoftKpPrivacyCA, }, } err := tpmParseAIKAttCA(leaf, nil) assert.EqualError(t, err, "Attestation Identity Key certificate missing required Extended Key Usage.") }) } func TestTpm2Exponent(t *testing.T) { testCases := []struct { name string params *tpm2.TPMSRSAParms want uint32 }{ { name: "ShouldReturnDefaultWhenExponentIsZero", params: &tpm2.TPMSRSAParms{Exponent: 0}, want: 65537, }, { name: "ShouldReturnExponentWhenNonZero", params: &tpm2.TPMSRSAParms{Exponent: 3}, want: 3, }, } for _, tc := range testCases { t.Run(tc.name, func(t *testing.T) { assert.Equal(t, tc.want, tpm2Exponent(tc.params)) }) } } func TestTpm2NameDigest(t *testing.T) { t.Run("ShouldRejectNameTooShort", func(t *testing.T) { _, _, err := tpm2NameDigest(tpm2.TPM2BName{Buffer: []byte{0x00, 0x0B}}) assert.EqualError(t, err, "name too short") }) t.Run("ShouldRejectInvalidHashAlgorithm", func(t *testing.T) { buf := []byte{0xFF, 0xFF, 0x01} _, _, err := tpm2NameDigest(tpm2.TPM2BName{Buffer: buf}) assert.EqualError(t, err, "invalid hash algorithm: unsupported hash algorithm: 65535") }) t.Run("ShouldRejectDigestLengthMismatch", func(t *testing.T) { buf := []byte{0x00, 0x0B, 0xAA} _, _, err := tpm2NameDigest(tpm2.TPM2BName{Buffer: buf}) assert.EqualError(t, err, "invalid name digest length: 1") }) t.Run("ShouldReturnAlgAndDigestWhenValid", func(t *testing.T) { digest := make([]byte, sha256.Size) for i := range digest { digest[i] = byte(i) } buf := append([]byte{0x00, 0x0B}, digest...) alg, gotDigest, err := tpm2NameDigest(tpm2.TPM2BName{Buffer: buf}) require.NoError(t, err) assert.Equal(t, tpm2.TPMAlgSHA256, alg) assert.Equal(t, digest, gotDigest) }) } func TestTPM2NameMatch(t *testing.T) { t.Run("ShouldReturnFalseWhenInputsNil", func(t *testing.T) { match, err := tpm2NameMatch(nil, nil) require.NoError(t, err) assert.False(t, match) }) t.Run("ShouldReturnFalseWhenAttestedNameDoesNotMatchPubArea", func(t *testing.T) { pubArea := makeTPMTPublicRSA(nil) qualifiedSigner := tpm2.TPM2BName{ Buffer: append([]byte{0x00, 0x0B}, make([]byte, sha256.Size)...), } certInfo := &tpm2.TPMSAttest{ Attested: tpm2.NewTPMUAttest[*tpm2.TPMSCertifyInfo]( tpm2.TPMSTAttestCertify, &tpm2.TPMSCertifyInfo{ Name: tpm2.TPM2BName{Buffer: []byte{0xDE, 0xAD, 0xBE, 0xEF}}, QualifiedName: tpm2.TPM2BName{Buffer: []byte{0x00}}, }, ), QualifiedSigner: qualifiedSigner, } match, err := tpm2NameMatch(certInfo, &pubArea) require.NoError(t, err) assert.False(t, match) }) } func TestTPMAttestationVerificationSuccess(t *testing.T) { for i := range testAttestationTPMResponses { t.Run("ShouldPassStandardScenario", func(t *testing.T) { pcc := attestationTestUnpackResponse(t, testAttestationTPMResponses[i]) clientDataHash := sha256.Sum256(pcc.Raw.AttestationResponse.ClientDataJSON) attestationType, _, err := attestationFormatValidationHandlerTPM(pcc.Response.AttestationObject, clientDataHash[:], nil) require.NoError(t, err) assert.Equal(t, "attca", attestationType) }) } } func TestTPMAttestationVerificationFailAttStatement(t *testing.T) { testCases := []struct { name string att AttestationObject attestationType string x5cs []any err string }{ { "ShouldNotParseAttStatementMissingVer", AttestationObject{}, "", nil, "Error retrieving ver value", }, { "ShouldNotParseAttStatementWithVersionNot2Point0", AttestationObject{AttStatement: map[string]any{stmtVersion: "foo.bar", stmtAlgorithm: int64(0), stmtX5C: []any{}, stmtSignature: []byte{}, stmtCertInfo: []byte{}, stmtPubArea: []byte{}}}, "", nil, "WebAuthn only supports TPM 2.0 currently", }, { "ShouldNotParseAttStatementWithNoAlg", AttestationObject{AttStatement: map[string]any{stmtVersion: "2.0"}}, "", nil, "Error retrieving alg value", }, { "ShouldNotParseAttStatementWithoutX5C", AttestationObject{AttStatement: map[string]any{stmtVersion: "2.0", stmtAlgorithm: int64(0), stmtSignature: []byte{}, stmtCertInfo: []byte{}, stmtPubArea: []byte{}}}, "", nil, ErrNotImplemented.Details, }, { "ShouldNotParseAttStatementWithECDAAKID", AttestationObject{AttStatement: map[string]any{stmtVersion: "2.0", stmtAlgorithm: int64(0), stmtX5C: []any{}, stmtECDAAKID: []byte{}, stmtSignature: []byte{}, stmtCertInfo: []byte{}, stmtPubArea: []byte{}}}, "", nil, ErrNotImplemented.Details, }, { "ShouldNotParseAttStatementWithNoSig", AttestationObject{AttStatement: map[string]any{stmtVersion: "2.0", stmtAlgorithm: int64(0), stmtX5C: []any{}}}, "", nil, "Error retrieving sig value", }, { "ShouldNotParseAttStatementCertInfoNotPresent", AttestationObject{AttStatement: map[string]any{stmtVersion: "2.0", stmtAlgorithm: int64(0), stmtX5C: []any{}, stmtSignature: []byte{}}}, "", nil, "Error retrieving certInfo value", }, { "ShouldNotParseAttStatementPubAreaNotPresent", AttestationObject{AttStatement: map[string]any{stmtVersion: "2.0", stmtAlgorithm: int64(0), stmtX5C: []any{}, stmtSignature: []byte{}, stmtCertInfo: []byte{}}}, "", nil, "Error retrieving pubArea value", }, { "ShouldNotParseAttStatementPubAreaNotCorrectValue", AttestationObject{AttStatement: defaultAttStatement}, "", nil, "Unable to decode TPMT_PUBLIC in attestation statement", }, { "ShouldNotParseAttStatementUnsupportedPublicKeyType", AttestationObject{AttStatement: map[string]any{stmtVersion: "2.0", stmtAlgorithm: int64(0), stmtX5C: []any{}, stmtSignature: []byte{}, stmtCertInfo: []byte{}, stmtPubArea: tpm2.Marshal(makeTPMTPublicRSA(nil))}, AuthData: AuthenticatorData{AttData: AttestedCredentialData{CredentialPublicKey: []byte{}}}}, "", nil, "Unsupported Public Key Type", }, } for _, tc := range testCases { t.Run(tc.name, func(t *testing.T) { attestationType, x5cs, err := attestationFormatValidationHandlerTPM(tc.att, nil, nil) assert.Equal(t, tc.attestationType, attestationType) assert.Equal(t, tc.x5cs, x5cs) if tc.err != "" { assert.EqualError(t, err, tc.err) } else { assert.NoError(t, err) } }) } } func TestTPMAttestationVerificationFailPubArea(t *testing.T) { epk, rpk, opk, rsaKey, eccKey, err := getTPMAttestionKeys() require.NoError(t, err) require.GreaterOrEqual(t, rsaKey.E, 0) require.LessOrEqual(t, int64(rsaKey.E), int64(4294967295)) e := uint32(rsaKey.E) //nolint:gosec testCases := []struct { name string keyType webauthncose.COSEKeyType rsaParams *TPMRSATestParameters eccParams *TPMECCTestParameters cpk []byte err string }{ { "CurveMismatch", webauthncose.EllipticKey, nil, &TPMECCTestParameters{Curve: tpm2.TPMECCNistP224, X: eccKey.X.Bytes(), Y: eccKey.Y.Bytes()}, epk, "Mismatch between ECCParameters in pubArea and credentialPublicKey", }, { "XMismatch", webauthncose.EllipticKey, nil, &TPMECCTestParameters{X: corruptBytes(eccKey.X.Bytes()), Y: eccKey.Y.Bytes()}, epk, "Mismatch between ECCParameters in pubArea and credentialPublicKey", }, { "YMismatch", webauthncose.EllipticKey, nil, &TPMECCTestParameters{X: eccKey.X.Bytes(), Y: corruptBytes(eccKey.Y.Bytes())}, epk, "Mismatch between ECCParameters in pubArea and credentialPublicKey", }, { "NMismatch", webauthncose.RSAKey, &TPMRSATestParameters{Modulus: corruptBytes(rsaKey.N.Bytes()), Exponent: e}, nil, rpk, "Mismatch between RSAParameters in pubArea and credentialPublicKey", }, { "EMismatch", webauthncose.RSAKey, &TPMRSATestParameters{Modulus: rsaKey.N.Bytes(), Exponent: e + 1}, nil, rpk, "Mismatch between RSAParameters in pubArea and credentialPublicKey", }, { "UnsupportedKeyType", webauthncose.OctetKey, nil, nil, opk, "Unsupported Public Key Type", }, } for _, tc := range testCases { t.Run(tc.name, func(t *testing.T) { attStmt := make(map[string]any, len(defaultAttStatement)) for id, v := range defaultAttStatement { attStmt[id] = v } var public tpm2.TPMTPublic switch tc.keyType { case webauthncose.EllipticKey: public = makeTPMTPublicECDSA(tc.eccParams) case webauthncose.RSAKey: public = makeTPMTPublicRSA(tc.rsaParams) case webauthncose.OctetKey: public = makeTPMTPublicECDSA(nil) default: t.Fatal("invalid key type") } attStmt[stmtPubArea] = tpm2.Marshal(public) att := AttestationObject{ AttStatement: attStmt, AuthData: AuthenticatorData{ AttData: AttestedCredentialData{ CredentialPublicKey: tc.cpk, }, }, } attestationType, _, err := attestationFormatValidationHandlerTPM(att, nil, nil) if tc.err != "" { assert.EqualError(t, err, tc.err) } else { assert.Equal(t, "attca", attestationType) } }) } } func TestTPMAttestationVerificationFailCertInfo(t *testing.T) { h := webauthncose.HasherFromCOSEAlg(webauthncose.AlgRS256) extraData := h.Sum(nil) testCases := []struct { name string certInfo tpm2.TPMSAttest err string }{ { "CertInfoMagicValueNoCorrect", tpm2.TPMSAttest{Magic: 42, Type: tpm2.TPMSTAttestCreation, Attested: tpm2.NewTPMUAttest[*tpm2.TPMSCreationInfo](tpm2.TPMSTAttestCreation, &tpm2.TPMSCreationInfo{})}, "Magic is not set to TPM_GENERATED_VALUE", }, { "CertInfoTypeNotTagAttestCreation", tpm2.TPMSAttest{Magic: tpm2.TPMGeneratedValue, Type: tpm2.TPMSTAttestCreation, Attested: tpm2.NewTPMUAttest[*tpm2.TPMSCreationInfo](tpm2.TPMSTAttestCreation, &tpm2.TPMSCreationInfo{})}, "Type is not set to TPM_ST_ATTEST_CERTIFY", }, { "CertInfoTypeNotTagAttestCertify", tpm2.TPMSAttest{Magic: tpm2.TPMGeneratedValue, Type: tpm2.TPMSTAttestCertify, Attested: tpm2.NewTPMUAttest[*tpm2.TPMSCertifyInfo](tpm2.TPMSTAttestCertify, &tpm2.TPMSCertifyInfo{})}, "ExtraData is not set to hash of attToBeSigned", }, { "CertInfoPubAreaNameMismatch", tpm2.TPMSAttest{Magic: tpm2.TPMGeneratedValue, Type: tpm2.TPMSTAttestCertify, Attested: tpm2.NewTPMUAttest[*tpm2.TPMSCertifyInfo](tpm2.TPMSTAttestCertify, &tpm2.TPMSCertifyInfo{Name: tpm2.TPM2BName{Buffer: nil}, QualifiedName: tpm2.TPM2BName{Buffer: nil}}), ExtraData: tpm2.TPM2BData{Buffer: extraData}}, "Signature validation error: crypto/rsa: verification error", }, { "CertInfoPubAreaHashMismatch", tpm2.TPMSAttest{Magic: tpm2.TPMGeneratedValue, Type: tpm2.TPMSTAttestCertify, Attested: tpm2.NewTPMUAttest[*tpm2.TPMSCertifyInfo](tpm2.TPMSTAttestCertify, &tpm2.TPMSCertifyInfo{Name: tpm2.TPM2BName{Buffer: tpm2.Marshal(tpm2.TPMTHA{HashAlg: tpm2.TPMAlgSHA256, Digest: extraData})}, QualifiedName: tpm2.TPM2BName{Buffer: nil}}), ExtraData: tpm2.TPM2BData{Buffer: h.Sum(nil)}}, "Signature validation error: crypto/rsa: verification error", }, } for _, tc := range testCases { t.Run(tc.name, func(t *testing.T) { attStmt := make(map[string]any, len(defaultAttStatement)) for id, v := range defaultAttStatement { attStmt[id] = v } attStmt[stmtX5C] = []any{mustConvertPEMToPEMBytes(t, certificateAndroidKeyRoot1)} rsaKey, _ := rsa.GenerateKey(rand.Reader, 2048) require.GreaterOrEqual(t, rsaKey.E, 0) require.LessOrEqual(t, int64(rsaKey.E), int64(4294967295)) e := uint32(rsaKey.E) //nolint:gosec r := webauthncose.RSAPublicKeyData{ PublicKeyData: webauthncose.PublicKeyData{ KeyType: int64(webauthncose.RSAKey), Algorithm: int64(webauthncose.AlgRS256), }, Modulus: rsaKey.N.Bytes(), Exponent: uint32ToBytes(e), } attStmt[stmtPubArea] = tpm2.Marshal(makeTPMTPublicRSA(&TPMRSATestParameters{Modulus: rsaKey.N.Bytes(), Exponent: e})) rpk, _ := webauthncbor.Marshal(r) att := AttestationObject{ AttStatement: attStmt, AuthData: AuthenticatorData{ AttData: AttestedCredentialData{ CredentialPublicKey: rpk, }, }, } att.AttStatement[stmtCertInfo] = tpm2.Marshal(tc.certInfo) attestationType, _, err := attestationFormatValidationHandlerTPM(att, nil, nil) if tc.err != "" { assert.EqualError(t, err, tc.err) } else { assert.Equal(t, "attca", attestationType) } }) } } func TestTPMAttestationVerificationFailX5c(t *testing.T) { attStmt := make(map[string]any, len(defaultAttStatement)) for id, v := range defaultAttStatement { attStmt[id] = v } rsaKey, _ := rsa.GenerateKey(rand.Reader, 2048) require.GreaterOrEqual(t, rsaKey.E, 0) require.LessOrEqual(t, int64(rsaKey.E), int64(4294967295)) e := uint32(rsaKey.E) //nolint:gosec r := webauthncose.RSAPublicKeyData{ PublicKeyData: webauthncose.PublicKeyData{ KeyType: int64(webauthncose.RSAKey), Algorithm: int64(webauthncose.AlgRS256), }, Modulus: rsaKey.N.Bytes(), Exponent: uint32ToBytes(e), } public := makeTPMTPublicRSA(&TPMRSATestParameters{Modulus: rsaKey.N.Bytes(), Exponent: e}) pubBytes := tpm2.Marshal(public) attStmt[stmtPubArea] = pubBytes rpk, _ := webauthncbor.Marshal(r) att := AttestationObject{ AttStatement: attStmt, AuthData: AuthenticatorData{ AttData: AttestedCredentialData{ CredentialPublicKey: rpk, }, }, } h := webauthncose.HasherFromCOSEAlg(webauthncose.AlgRS256) h.Write(att.RawAuthData) extraData := h.Sum(nil) h.Reset() h.Write(pubBytes) pubName := h.Sum(nil) certInfo := tpm2.TPMSAttest{ Magic: tpm2.TPMGeneratedValue, Type: tpm2.TPMSTAttestCertify, Attested: tpm2.NewTPMUAttest[*tpm2.TPMSCertifyInfo](tpm2.TPMSTAttestCertify, &tpm2.TPMSCertifyInfo{ Name: tpm2.TPM2BName{ Buffer: tpm2.Marshal(tpm2.TPMTHA{ HashAlg: tpm2.TPMAlgSHA256, Digest: pubName, }), }, }), ExtraData: tpm2.TPM2BData{ Buffer: extraData, }, } attStmt[stmtCertInfo] = tpm2.Marshal(certInfo) makeX5c := func(b []byte) []any { q := make([]any, 1) q[0] = b return q } testCases := []struct { name string x5c []any err string }{ { "TPMNegativeTestX5CEmpty", make([]any, 1), "Error getting certificate from x5c cert chain", }, { "TPMNegativeTestX5CCanNotParse", makeX5c(make([]byte, 1)), "Error parsing certificate from ASN.1", }, } for _, tc := range testCases { t.Run(tc.name, func(t *testing.T) { att.AttStatement[stmtX5C] = tc.x5c attestationType, _, err := attestationFormatValidationHandlerTPM(att, nil, nil) if tc.err != "" { assert.EqualError(t, err, tc.err) } else { assert.Equal(t, "attca", attestationType) } }) } } type TPMECCTestParameters struct { Curve tpm2.TPMECCCurve X []byte Y []byte } type TPMRSATestParameters struct { KeyBits tpm2.TPMIRSAKeyBits Exponent uint32 Modulus []byte } 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 makeTPMTPublicRSA(params *TPMRSATestParameters) (public tpm2.TPMTPublic) { var ( bits tpm2.TPMIRSAKeyBits unique tpm2.TPMUPublicID exp uint32 ) if params != nil { exp = params.Exponent if params.KeyBits != 0 { bits = params.KeyBits } else { bits = tpm2.TPMIRSAKeyBits(2048) } if len(params.Modulus) > 0 { unique = tpm2.NewTPMUPublicID[*tpm2.TPM2BPublicKeyRSA](tpm2.TPMAlgRSA, &tpm2.TPM2BPublicKeyRSA{Buffer: params.Modulus}) } } else { bits = tpm2.TPMIRSAKeyBits(2048) } public = tpm2.TPMTPublic{ Type: tpm2.TPMAlgRSA, NameAlg: tpm2.TPMAlgSHA256, ObjectAttributes: tpm2.TPMAObject{ SignEncrypt: true, Restricted: true, FixedTPM: true, FixedParent: true, SensitiveDataOrigin: true, UserWithAuth: true, }, AuthPolicy: tpm2.TPM2BDigest{}, Parameters: tpm2.NewTPMUPublicParms[*tpm2.TPMSRSAParms](tpm2.TPMAlgRSA, &tpm2.TPMSRSAParms{ Symmetric: tpm2.TPMTSymDefObject{ Algorithm: tpm2.TPMAlgNull, }, Scheme: tpm2.TPMTRSAScheme{ Scheme: tpm2.TPMAlgRSASSA, Details: tpm2.NewTPMUAsymScheme[*tpm2.TPMSSigSchemeRSASSA](tpm2.TPMAlgRSASSA, &tpm2.TPMSSigSchemeRSASSA{ HashAlg: tpm2.TPMAlgSHA256, }), }, Exponent: exp, KeyBits: bits, }), Unique: unique, } return public } func makeTPMTPublicECDSA(params *TPMECCTestParameters) tpm2.TPMTPublic { var ( curve tpm2.TPMECCCurve x, y []byte ) if params != nil { if params.Curve != tpm2.TPMECCNone { curve = params.Curve } x, y = params.X, params.Y } if curve == tpm2.TPMECCNone { curve = tpm2.TPMECCNistP256 } return tpm2.TPMTPublic{ Type: tpm2.TPMAlgECC, NameAlg: tpm2.TPMAlgSHA256, ObjectAttributes: tpm2.TPMAObject{ SignEncrypt: true, Restricted: true, FixedTPM: true, FixedParent: true, SensitiveDataOrigin: true, UserWithAuth: true, }, AuthPolicy: tpm2.TPM2BDigest{}, Parameters: tpm2.NewTPMUPublicParms[*tpm2.TPMSECCParms](tpm2.TPMAlgECC, &tpm2.TPMSECCParms{ Symmetric: tpm2.TPMTSymDefObject{ Algorithm: tpm2.TPMAlgNull, }, Scheme: tpm2.TPMTECCScheme{ Scheme: tpm2.TPMAlgECDSA, Details: tpm2.NewTPMUAsymScheme[*tpm2.TPMSSigSchemeECDSA](tpm2.TPMAlgECDSA, &tpm2.TPMSSigSchemeECDSA{ HashAlg: tpm2.TPMAlgSHA256, }), }, CurveID: curve, KDF: tpm2.TPMTKDFScheme{Scheme: tpm2.TPMAlgNull}, }), Unique: tpm2.NewTPMUPublicID( tpm2.TPMAlgECC, &tpm2.TPMSECCPoint{ X: tpm2.TPM2BECCParameter{Buffer: x}, Y: tpm2.TPM2BECCParameter{Buffer: y}, }, ), } } var defaultAttStatement = map[string]any{stmtVersion: "2.0", stmtAlgorithm: int64(-257), stmtX5C: []any{}, stmtSignature: []byte{}, stmtCertInfo: []byte{}, stmtPubArea: []byte{}} type CredentialPublicKey struct { KeyType int64 `cbor:"1,keyasint" json:"kty"` Algorithm int64 `cbor:"3,keyasint" json:"alg"` Curve int64 `cbor:"-1,keyasint,omitempty" json:"crv"` XCoord []byte `cbor:"-2,keyasint,omitempty" json:"x"` YCoord []byte `cbor:"-3,keyasint,omitempty" json:"y"` Modulus []byte `cbor:"-1,keyasint,omitempty" json:"n"` Exponent []byte `cbor:"-2,keyasint,omitempty" json:"e"` } func corruptBytes(in []byte) []byte { out := make([]byte, len(in)) copy(out, in) out[len(in)-1] ^= 0xff return out } func uint32ToBytes(i uint32) []byte { t, o := make([]byte, 4), make([]byte, 3) binary.LittleEndian.PutUint32(t, i) copy(o, t) return o } func getTPMAttestionKeys() ([]byte, []byte, []byte, rsa.PrivateKey, ecdsa.PrivateKey, error) { rsaKey, err := rsa.GenerateKey(rand.Reader, 2048) if err != nil { return nil, nil, nil, rsa.PrivateKey{}, ecdsa.PrivateKey{}, err } if rsaKey.E < 0 || int64(rsaKey.E) > int64(math.MaxUint32) { return nil, nil, nil, rsa.PrivateKey{}, ecdsa.PrivateKey{}, errors.New("invalid E value") } exp := uint32(rsaKey.E) r := webauthncose.RSAPublicKeyData{ PublicKeyData: webauthncose.PublicKeyData{ KeyType: int64(webauthncose.RSAKey), Algorithm: int64(webauthncose.AlgRS256), }, Modulus: rsaKey.N.Bytes(), Exponent: uint32ToBytes(exp), } eccKey, err := ecdsa.GenerateKey(elliptic.P256(), rand.Reader) if err != nil { return nil, nil, nil, rsa.PrivateKey{}, ecdsa.PrivateKey{}, err } c := CredentialPublicKey{ KeyType: int64(webauthncose.EllipticKey), Algorithm: int64(webauthncose.AlgES256), Curve: int64(webauthncose.P256), XCoord: eccKey.X.Bytes(), YCoord: eccKey.Y.Bytes(), } e := webauthncose.EC2PublicKeyData{ PublicKeyData: webauthncose.PublicKeyData{ KeyType: c.KeyType, Algorithm: c.Algorithm, }, Curve: c.Curve, XCoord: c.XCoord, YCoord: c.YCoord, } okpKey, _, err := ed25519.GenerateKey(rand.Reader) if err != nil { return nil, nil, nil, rsa.PrivateKey{}, ecdsa.PrivateKey{}, err } o := webauthncose.OKPPublicKeyData{ PublicKeyData: webauthncose.PublicKeyData{ KeyType: int64(webauthncose.OctetKey), Algorithm: int64(webauthncose.AlgEdDSA), }, Curve: int64(webauthncose.Ed25519), XCoord: okpKey, } epk, err := webauthncbor.Marshal(e) if err != nil { return nil, nil, nil, rsa.PrivateKey{}, ecdsa.PrivateKey{}, err } rpk, err := webauthncbor.Marshal(r) if err != nil { return nil, nil, nil, rsa.PrivateKey{}, ecdsa.PrivateKey{}, err } opk, err := webauthncbor.Marshal(o) return epk, rpk, opk, *rsaKey, *eccKey, err } func mustConvertPEMToPEMBytes(t *testing.T, in string) []byte { data, rest := pem.Decode([]byte(in)) require.NotNil(t, data) require.Len(t, rest, 0) return data.Bytes }