summaryrefslogtreecommitdiffhomepage
diff options
context:
space:
mode:
-rw-r--r--packages/server/src/metadata/verifyAttestationWithMetadata.test.ts57
-rw-r--r--packages/server/src/metadata/verifyAttestationWithMetadata.ts36
2 files changed, 83 insertions, 10 deletions
diff --git a/packages/server/src/metadata/verifyAttestationWithMetadata.test.ts b/packages/server/src/metadata/verifyAttestationWithMetadata.test.ts
index 128e26a..9ba01fd 100644
--- a/packages/server/src/metadata/verifyAttestationWithMetadata.test.ts
+++ b/packages/server/src/metadata/verifyAttestationWithMetadata.test.ts
@@ -105,3 +105,60 @@ test('should verify attestation with rsa_emsa_pkcs1_sha256_raw authenticator alg
expect(verified).toEqual(true);
});
+
+test('should not validate certificate path when authenticator is self-referencing its attestation statement certificates', async () => {
+ const metadataStatement: MetadataStatement = {
+ "legalHeader": "https://fidoalliance.org/metadata/metadata-statement-legal-header/",
+ "description": "Virtual Secp256R1 FIDO2 Conformance Testing CTAP2 Authenticator with Self Batch Referencing",
+ "aaguid": "5b65dac1-7af4-46e6-8a4f-8701fcc4f3b4",
+ "alternativeDescriptions": {
+ "ru-RU": "Виртуальный Secp256R1 CTAP2 аутентификатор для тестирование серверов на соответсвие спецификации FIDO2 с одинаковыми сертификатами"
+ },
+ "protocolFamily": "fido2",
+ "authenticatorVersion": 2,
+ "upv": [{ "major": 1, "minor": 0 }],
+ "authenticationAlgorithms": ["secp256r1_ecdsa_sha256_raw"],
+ "publicKeyAlgAndEncodings": ["cose"],
+ "attestationTypes": ["basic_full"],
+ "schema": 3,
+ "userVerificationDetails": [
+ [{ "userVerificationMethod": "none" }],
+ [{ "userVerificationMethod": "presence_internal" }],
+ [{ "userVerificationMethod": "passcode_external", "caDesc": { "base": 10, "minLength": 4 } }],
+ [
+ { "userVerificationMethod": "passcode_external", "caDesc": { "base": 10, "minLength": 4 } },
+ { "userVerificationMethod": "presence_internal" }
+ ]
+ ],
+ "keyProtection": ["hardware", "secure_element"],
+ "matcherProtection": ["on_chip"],
+ "cryptoStrength": 128,
+ "attachmentHint": ["external", "wired", "wireless", "nfc"],
+ "tcDisplay": [],
+ "attestationRootCertificates": [
+ "MIIEQTCCAimgAwIBAgIBATANBgkqhkiG9w0BAQsFADCBoTEYMBYGA1UEAwwPRklETzIgVEVTVCBST09UMTEwLwYJKoZIhvcNAQkBFiJjb25mb3JtYW5jZS10b29sc0BmaWRvYWxsaWFuY2Uub3JnMRYwFAYDVQQKDA1GSURPIEFsbGlhbmNlMQwwCgYDVQQLDANDV0cxCzAJBgNVBAYTAlVTMQswCQYDVQQIDAJNWTESMBAGA1UEBwwJV2FrZWZpZWxkMB4XDTE4MDUyMzE0Mzk0M1oXDTI4MDUyMDE0Mzk0M1owgcIxIzAhBgNVBAMMGkZJRE8yIEJBVENIIEtFWSBwcmltZTI1NnYxMTEwLwYJKoZIhvcNAQkBFiJjb25mb3JtYW5jZS10b29sc0BmaWRvYWxsaWFuY2Uub3JnMRYwFAYDVQQKDA1GSURPIEFsbGlhbmNlMSIwIAYDVQQLDBlBdXRoZW50aWNhdG9yIEF0dGVzdGF0aW9uMQswCQYDVQQGEwJVUzELMAkGA1UECAwCTVkxEjAQBgNVBAcMCVdha2VmaWVsZDBZMBMGByqGSM49AgEGCCqGSM49AwEHA0IABE86Xl6rbB+8rpf232RJlnYse+9yAEAqdsbyMPZVbxeqmZtZf8S/UIqvjp7wzQE/Wrm9J5FL8IBDeMvMsRuJtUajLDAqMAkGA1UdEwQCMAAwHQYDVR0OBBYEFFZN98D4xlW2oR9sTRnzv0Hi/QF5MA0GCSqGSIb3DQEBCwUAA4ICAQCH3aCf+CCJBdEtQc4JpOnUelwGGw7DxnBMokHHBgrzJxDn9BFcFwxGLxrFV7EfYehQNOD+74OS8fZRgZiNf9EDGAYiHh0+CspfBWd20zCIjlCdDBcyhwq3PLJ65JC/og3CT9AK4kvks4DI+01RYxNv9S8Jx1haO1lgU55hBIr1P/p21ZKnpcCEhPjB/cIFrHJqL5iJGfed+LXni9Suq24OHnp44Mrv4h7OD2elu5yWfdfFb+RGG2TYURFIGYGijsii093w0ZMBOfBS+3Xq/DrHeZbZrrNkY455gJCZ5eV83Nrt9J9/UF0VZHl/hwnSAUC/b3tN/l0ZlC9kPcNzJD04l4ndFBD2KdfQ2HGTX7pybWLZ7yH2BM3ui2OpiacaOzd7OE91rHYB2uZyQ7jdg25yF9M8QI9NHM/itCjdBvAYt4QCT8dX6gmZiIGR2F/YXZAsybtJ16pnUmODVbW80lPbzy+PUQYX79opeD9u6MBorzr9g08Elpb1F3DgSd8VSLlsR2QPllKl4AcJDMIOfZHOQGOzatMV7ipEVRa0L5FnjAWpHHvSNcsjD4Cul562mO3MlI2pCyo+US+nIzG5XZmOeu4Db/Kw/dEPOo2ztHwlU0qKJ7REBsbt63jdQtlwLuiLHwkpiwnrAOZfwbLLu9Yz4tL1eJlQffuwS/Aolsz7HA=="
+ ],
+ "supportedExtensions": [{ "id": "hmac-secret", "fail_if_unknown": false }, { "id": "credProtect", "fail_if_unknown": false }
+ ],
+ "authenticatorGetInfo": {
+ "versions": ["U2F_V2", "FIDO_2_0"],
+ "extensions": ["credProtect", "hmac-secret"],
+ "aaguid": "5b65dac17af446e68a4f8701fcc4f3b4",
+ "options": { "plat": false, "rk": true, "clientPin": true, "up": true, "uv": true },
+ "maxMsgSize": 1200,
+ }
+ };
+
+ const x5c = [
+ 'MIIEQTCCAimgAwIBAgIBATANBgkqhkiG9w0BAQsFADCBoTEYMBYGA1UEAwwPRklETzIgVEVTVCBST09UMTEwLwYJKoZIhvcNAQkBFiJjb25mb3JtYW5jZS10b29sc0BmaWRvYWxsaWFuY2Uub3JnMRYwFAYDVQQKDA1GSURPIEFsbGlhbmNlMQwwCgYDVQQLDANDV0cxCzAJBgNVBAYTAlVTMQswCQYDVQQIDAJNWTESMBAGA1UEBwwJV2FrZWZpZWxkMB4XDTE4MDUyMzE0Mzk0M1oXDTI4MDUyMDE0Mzk0M1owgcIxIzAhBgNVBAMMGkZJRE8yIEJBVENIIEtFWSBwcmltZTI1NnYxMTEwLwYJKoZIhvcNAQkBFiJjb25mb3JtYW5jZS10b29sc0BmaWRvYWxsaWFuY2Uub3JnMRYwFAYDVQQKDA1GSURPIEFsbGlhbmNlMSIwIAYDVQQLDBlBdXRoZW50aWNhdG9yIEF0dGVzdGF0aW9uMQswCQYDVQQGEwJVUzELMAkGA1UECAwCTVkxEjAQBgNVBAcMCVdha2VmaWVsZDBZMBMGByqGSM49AgEGCCqGSM49AwEHA0IABE86Xl6rbB-8rpf232RJlnYse-9yAEAqdsbyMPZVbxeqmZtZf8S_UIqvjp7wzQE_Wrm9J5FL8IBDeMvMsRuJtUajLDAqMAkGA1UdEwQCMAAwHQYDVR0OBBYEFFZN98D4xlW2oR9sTRnzv0Hi_QF5MA0GCSqGSIb3DQEBCwUAA4ICAQCH3aCf-CCJBdEtQc4JpOnUelwGGw7DxnBMokHHBgrzJxDn9BFcFwxGLxrFV7EfYehQNOD-74OS8fZRgZiNf9EDGAYiHh0-CspfBWd20zCIjlCdDBcyhwq3PLJ65JC_og3CT9AK4kvks4DI-01RYxNv9S8Jx1haO1lgU55hBIr1P_p21ZKnpcCEhPjB_cIFrHJqL5iJGfed-LXni9Suq24OHnp44Mrv4h7OD2elu5yWfdfFb-RGG2TYURFIGYGijsii093w0ZMBOfBS-3Xq_DrHeZbZrrNkY455gJCZ5eV83Nrt9J9_UF0VZHl_hwnSAUC_b3tN_l0ZlC9kPcNzJD04l4ndFBD2KdfQ2HGTX7pybWLZ7yH2BM3ui2OpiacaOzd7OE91rHYB2uZyQ7jdg25yF9M8QI9NHM_itCjdBvAYt4QCT8dX6gmZiIGR2F_YXZAsybtJ16pnUmODVbW80lPbzy-PUQYX79opeD9u6MBorzr9g08Elpb1F3DgSd8VSLlsR2QPllKl4AcJDMIOfZHOQGOzatMV7ipEVRa0L5FnjAWpHHvSNcsjD4Cul562mO3MlI2pCyo-US-nIzG5XZmOeu4Db_Kw_dEPOo2ztHwlU0qKJ7REBsbt63jdQtlwLuiLHwkpiwnrAOZfwbLLu9Yz4tL1eJlQffuwS_Aolsz7HA'
+ ];
+ const credentialPublicKey = 'pQECAyYgASFYIBdmUVOxrn-OOtkVwGP_vAspH3VkgzcGXVlu3-acb7EZIlggKgDTs0fr2d51sLR6uL3KP2cqR3iIUkKMCjyMJhYOkf4';
+
+ const verified = await verifyAttestationWithMetadata(
+ metadataStatement,
+ base64url.toBuffer(credentialPublicKey),
+ x5c,
+ );
+
+ expect(verified).toEqual(true);
+});
diff --git a/packages/server/src/metadata/verifyAttestationWithMetadata.ts b/packages/server/src/metadata/verifyAttestationWithMetadata.ts
index 940b174..e068a05 100644
--- a/packages/server/src/metadata/verifyAttestationWithMetadata.ts
+++ b/packages/server/src/metadata/verifyAttestationWithMetadata.ts
@@ -92,16 +92,32 @@ export async function verifyAttestationWithMetadata(
);
}
- try {
- await validateCertificatePath(
- x5c.map(convertCertBufferToPEM),
- statement.attestationRootCertificates.map(convertCertBufferToPEM),
- );
- } catch (err) {
- const _err = err as Error;
- throw new Error(
- `Could not validate certificate path with any metadata root certificates: ${_err.message}`,
- );
+ // Prepare to check the certificate chain
+ const authenticatorCerts = x5c.map(convertCertBufferToPEM);
+ const statementRootCerts = statement.attestationRootCertificates.map(convertCertBufferToPEM);
+
+ /**
+ * If an authenticator returns exactly one certificate in its x5c, and that cert is found in the
+ * metadata statement then the authenticator is "self-referencing". In this case we forego
+ * certificate chain validation.
+ */
+ let authenticatorIsSelfReferencing = false;
+ if (
+ authenticatorCerts.length === 1 &&
+ statementRootCerts.indexOf(authenticatorCerts[0]) >= 0
+ ) {
+ authenticatorIsSelfReferencing = true;
+ }
+
+ if (!authenticatorIsSelfReferencing) {
+ try {
+ await validateCertificatePath(authenticatorCerts, statementRootCerts);
+ } catch (err) {
+ const _err = err as Error;
+ throw new Error(
+ `Could not validate certificate path with any metadata root certificates: ${_err.message}`,
+ );
+ }
}
return true;