diff options
-rw-r--r-- | packages/server/src/helpers/convertPEMToBytes.test.ts | 37 | ||||
-rw-r--r-- | packages/server/src/helpers/convertPEMToBytes.ts | 2 | ||||
-rw-r--r-- | packages/server/src/metadata/verifyJWT.ts | 20 | ||||
-rw-r--r-- | packages/server/src/services/metadataService.e2e.test.ts | 19 |
4 files changed, 71 insertions, 7 deletions
diff --git a/packages/server/src/helpers/convertPEMToBytes.test.ts b/packages/server/src/helpers/convertPEMToBytes.test.ts new file mode 100644 index 0000000..9a7a517 --- /dev/null +++ b/packages/server/src/helpers/convertPEMToBytes.test.ts @@ -0,0 +1,37 @@ +import { isoBase64URL } from './iso'; + +import { convertPEMToBytes } from './convertPEMToBytes'; + +test('should handle malformed cert with leading whitespaces', () => { + const output = convertPEMToBytes(malformedLeadingWhitespace); + + expect( + isoBase64URL.fromBuffer(output) + ).toEqual( + 'MIIDXzCCAkegAwIBAgILBAAAAAABIVhTCKIwDQYJKoZIhvcNAQELBQAwTDEgMB4GA1UECxMXR2xvYmFsU2lnbiBSb290IENBIC0gUjMxEzARBgNVBAoTCkdsb2JhbFNpZ24xEzARBgNVBAMTCkdsb2JhbFNpZ24wHhcNMDkwMzE4MTAwMDAwWhcNMjkwMzE4MTAwMDAwWjBMMSAwHgYDVQQLExdHbG9iYWxTaWduIFJvb3QgQ0EgLSBSMzETMBEGA1UEChMKR2xvYmFsU2lnbjETMBEGA1UEAxMKR2xvYmFsU2lnbjCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBAMwldpB5BngiFvXAg7aEyiie_QV2EcWtiHL8RgJDx7KKnQRfJMsuS-FggkbhUqsMgUdwbN1k0ev1LKMPgj0MK66X17YUhhB5uzsTgHeMCOFJ0mpiLx9e-pZo34knlTifBtc-ycsmWQ1z3rDI6SYOgxXG71uL0gRgykmmKPZpO_bLyCiR5Z2KYVc3rHQU3HTgOu5yLy6c-9C7v_U9AOEGM-iCK65TpjoWc4zdQQ4gOsC0p6Hpsk-QLjJg6VfLuQSSaGjlOCZgdbKfd_-RFO-uIEn8rUAVSNECMWEZXriX7613t2Saer9fwRPvm2L7DWzgVGkWqQPabumDk3F2xmmFghcCAwEAAaNCMEAwDgYDVR0PAQH_BAQDAgEGMA8GA1UdEwEB_wQFMAMBAf8wHQYDVR0OBBYEFI_wS3-oLkUkrk1Q-mOai97i3Ru8MA0GCSqGSIb3DQEBCwUAA4IBAQBLQNvAUKr-yAzv95ZURUm7lgAJQayzE4aGKAczymvmdLm6AC2upArT9fHxD4q_c2dKg8dEe3jgr25sbwMpjjM5RcOO5LlXbKr8EpbsU8Yt5CRsuZRj-9xTaGdWPoO4zzUhw8lo_s7awlOqzJCK6fBdRoyV3XpYKBovHd7NADdBj-1EbddTKJd-82cEHhXXipa0095MJ6RMG3NzdvQXmcIfeg7jLQitChws_zyrVQ4PkX4268NXSb7hLi18YIvDQVETI53O9zJrlAGomecsMx86OyXShkDOOyyGeMlhLxS67ttVb9-E7gUJTb0o2HLO02JQZR7rkpeDMdmztcpHWD9f' + ); +}); + +// Each line of this PEM cert should have a space in front of it +const malformedLeadingWhitespace = `-----BEGIN CERTIFICATE----- + MIIDXzCCAkegAwIBAgILBAAAAAABIVhTCKIwDQYJKoZIhvcNAQELBQAwTDEgMB4G + A1UECxMXR2xvYmFsU2lnbiBSb290IENBIC0gUjMxEzARBgNVBAoTCkdsb2JhbFNp + Z24xEzARBgNVBAMTCkdsb2JhbFNpZ24wHhcNMDkwMzE4MTAwMDAwWhcNMjkwMzE4 + MTAwMDAwWjBMMSAwHgYDVQQLExdHbG9iYWxTaWduIFJvb3QgQ0EgLSBSMzETMBEG + A1UEChMKR2xvYmFsU2lnbjETMBEGA1UEAxMKR2xvYmFsU2lnbjCCASIwDQYJKoZI + hvcNAQEBBQADggEPADCCAQoCggEBAMwldpB5BngiFvXAg7aEyiie/QV2EcWtiHL8 + RgJDx7KKnQRfJMsuS+FggkbhUqsMgUdwbN1k0ev1LKMPgj0MK66X17YUhhB5uzsT + gHeMCOFJ0mpiLx9e+pZo34knlTifBtc+ycsmWQ1z3rDI6SYOgxXG71uL0gRgykmm + KPZpO/bLyCiR5Z2KYVc3rHQU3HTgOu5yLy6c+9C7v/U9AOEGM+iCK65TpjoWc4zd + QQ4gOsC0p6Hpsk+QLjJg6VfLuQSSaGjlOCZgdbKfd/+RFO+uIEn8rUAVSNECMWEZ + XriX7613t2Saer9fwRPvm2L7DWzgVGkWqQPabumDk3F2xmmFghcCAwEAAaNCMEAw + DgYDVR0PAQH/BAQDAgEGMA8GA1UdEwEB/wQFMAMBAf8wHQYDVR0OBBYEFI/wS3+o + LkUkrk1Q+mOai97i3Ru8MA0GCSqGSIb3DQEBCwUAA4IBAQBLQNvAUKr+yAzv95ZU + RUm7lgAJQayzE4aGKAczymvmdLm6AC2upArT9fHxD4q/c2dKg8dEe3jgr25sbwMp + jjM5RcOO5LlXbKr8EpbsU8Yt5CRsuZRj+9xTaGdWPoO4zzUhw8lo/s7awlOqzJCK + 6fBdRoyV3XpYKBovHd7NADdBj+1EbddTKJd+82cEHhXXipa0095MJ6RMG3NzdvQX + mcIfeg7jLQitChws/zyrVQ4PkX4268NXSb7hLi18YIvDQVETI53O9zJrlAGomecs + Mx86OyXShkDOOyyGeMlhLxS67ttVb9+E7gUJTb0o2HLO02JQZR7rkpeDMdmztcpH + WD9f + -----END CERTIFICATE----- +`; diff --git a/packages/server/src/helpers/convertPEMToBytes.ts b/packages/server/src/helpers/convertPEMToBytes.ts index 8749d03..7958635 100644 --- a/packages/server/src/helpers/convertPEMToBytes.ts +++ b/packages/server/src/helpers/convertPEMToBytes.ts @@ -7,7 +7,7 @@ export function convertPEMToBytes(pem: string): Uint8Array { const certBase64 = pem .replace('-----BEGIN CERTIFICATE-----', '') .replace('-----END CERTIFICATE-----', '') - .replace(/\n/g, ''); + .replace(/[\n ]/g, ''); return isoBase64URL.toBuffer(certBase64, 'base64'); } diff --git a/packages/server/src/metadata/verifyJWT.ts b/packages/server/src/metadata/verifyJWT.ts index d7e64ec..d9c933a 100644 --- a/packages/server/src/metadata/verifyJWT.ts +++ b/packages/server/src/metadata/verifyJWT.ts @@ -1,13 +1,13 @@ import { convertX509PublicKeyToCOSE } from '../helpers/convertX509PublicKeyToCOSE'; import { isoBase64URL, isoUint8Array } from '../helpers/iso'; -import { COSEALG, COSEKEYS, isCOSEPublicKeyEC2 } from '../helpers/cose'; +import { COSEALG, COSEKEYS, isCOSEPublicKeyEC2, isCOSEPublicKeyRSA } from '../helpers/cose'; import { verifyEC2 } from '../helpers/iso/isoCrypto/verifyEC2'; +import { verifyRSA } from '../helpers/iso/isoCrypto/verifyRSA'; /** - * Lightweight verification for FIDO MDS JWTs. + * Lightweight verification for FIDO MDS JWTs. Supports use of EC2 and RSA. * - * Currently assumes `"alg": "ES256"` in the JWT header, it's what FIDO MDS uses. If this ever - * needs to support more JWS algorithms, here's the list of them: + * If this ever needs to support more JWS algorithms, here's the list of them: * * https://www.rfc-editor.org/rfc/rfc7518.html#section-3.1 * @@ -17,14 +17,22 @@ export async function verifyJWT(jwt: string, leafCert: Uint8Array): Promise<bool const [header, payload, signature] = jwt.split('.'); const certCOSE = convertX509PublicKeyToCOSE(leafCert); + const data = isoUint8Array.fromUTF8String(`${header}.${payload}`); + const signatureBytes = isoBase64URL.toBuffer(signature); if (isCOSEPublicKeyEC2(certCOSE)) { return verifyEC2({ - data: isoUint8Array.fromUTF8String(`${header}.${payload}`), - signature: isoBase64URL.toBuffer(signature), + data, + signature: signatureBytes, cosePublicKey: certCOSE, shaHashOverride: COSEALG.ES256, }); + } else if (isCOSEPublicKeyRSA(certCOSE)) { + return verifyRSA({ + data, + signature: signatureBytes, + cosePublicKey: certCOSE, + }) } const kty = certCOSE.get(COSEKEYS.kty); diff --git a/packages/server/src/services/metadataService.e2e.test.ts b/packages/server/src/services/metadataService.e2e.test.ts new file mode 100644 index 0000000..e2d8d5b --- /dev/null +++ b/packages/server/src/services/metadataService.e2e.test.ts @@ -0,0 +1,19 @@ +import { BaseMetadataService } from './metadataService'; + +describe('end-to-end MetadataService tests', () => { + test('should be able to load from FIDO MDS and get statement for YubiKey 5', async () => { + const service = new BaseMetadataService(); + + await service.initialize(); + + /** + * From Yubico's list of AAGUIDs + * + * See https://support.yubico.com/hc/en-us/articles/360016648959-YubiKey-Hardware-FIDO2-AAGUIDs + */ + const aaguidYubiKey5 = 'ee882879-721c-4913-9775-3dfcce97072a'; + const statement = await service.getStatement(aaguidYubiKey5); + + expect(statement).toBeDefined(); + }); +}); |