@simplewebauthn/server
Advanced tools
Comparing version 4.3.0 to 4.4.0
@@ -5,3 +5,3 @@ /// <reference types="node" /> | ||
credential: AuthenticationCredentialJSON; | ||
expectedChallenge: string; | ||
expectedChallenge: string | ((challenge: string) => boolean); | ||
expectedOrigin: string | string[]; | ||
@@ -8,0 +8,0 @@ expectedRPID: string | string[]; |
@@ -56,3 +56,8 @@ "use strict"; | ||
// Ensure the device provided the challenge we gave it | ||
if (challenge !== expectedChallenge) { | ||
if (typeof expectedChallenge === 'function') { | ||
if (!expectedChallenge(challenge)) { | ||
throw new Error(`Custom challenge verifier returned false for registration response challenge "${challenge}"`); | ||
} | ||
} | ||
else if (challenge !== expectedChallenge) { | ||
throw new Error(`Unexpected authentication response challenge "${challenge}", expected "${expectedChallenge}"`); | ||
@@ -59,0 +64,0 @@ } |
@@ -24,2 +24,3 @@ "use strict"; | ||
let invalidSubjectAndIssuerError = false; | ||
let certificateNotYetValidOrExpiredErrorMessage = undefined; | ||
for (const rootCert of rootCertificates) { | ||
@@ -29,4 +30,6 @@ try { | ||
await _validatePath(certsWithRoot); | ||
// If we successfully validated a path then there's no need to continue | ||
// If we successfully validated a path then there's no need to continue. Reset any existing | ||
// errors that were thrown by earlier root certificates | ||
invalidSubjectAndIssuerError = false; | ||
certificateNotYetValidOrExpiredErrorMessage = undefined; | ||
break; | ||
@@ -38,2 +41,5 @@ } | ||
} | ||
else if (err instanceof CertificateNotYetValidOrExpired) { | ||
certificateNotYetValidOrExpiredErrorMessage = err.message; | ||
} | ||
else { | ||
@@ -48,2 +54,5 @@ throw err; | ||
} | ||
else if (certificateNotYetValidOrExpiredErrorMessage) { | ||
throw new CertificateNotYetValidOrExpired(certificateNotYetValidOrExpiredErrorMessage); | ||
} | ||
return true; | ||
@@ -61,4 +70,6 @@ } | ||
subjectCert.readCertPEM(subjectPem); | ||
const isLeafCert = i === 0; | ||
const isRootCert = i + 1 >= certificates.length; | ||
let issuerPem = ''; | ||
if (i + 1 >= certificates.length) { | ||
if (isRootCert) { | ||
issuerPem = subjectPem; | ||
@@ -81,3 +92,11 @@ } | ||
if (notBefore > now || notAfter < now) { | ||
throw new Error('Intermediate certificate is not yet valid or expired'); | ||
if (isLeafCert) { | ||
throw new CertificateNotYetValidOrExpired(`Leaf certificate is not yet valid or expired: ${issuerPem}`); | ||
} | ||
else if (isRootCert) { | ||
throw new CertificateNotYetValidOrExpired(`Root certificate is not yet valid or expired: ${issuerPem}`); | ||
} | ||
else { | ||
throw new CertificateNotYetValidOrExpired(`Intermediate certificate is not yet valid or expired: ${issuerPem}`); | ||
} | ||
} | ||
@@ -107,2 +126,8 @@ if (subjectCert.getIssuerString() !== issuerCert.getSubjectString()) { | ||
} | ||
class CertificateNotYetValidOrExpired extends Error { | ||
constructor(message) { | ||
super(message); | ||
this.name = 'CertificateNotYetValidOrExpired'; | ||
} | ||
} | ||
//# sourceMappingURL=validateCertificatePath.js.map |
@@ -8,2 +8,2 @@ /// <reference types="node" /> | ||
*/ | ||
export default function verifyAttestationWithMetadata(statement: MetadataStatement, alg: number, x5c: Buffer[] | Base64URLString[]): Promise<boolean>; | ||
export default function verifyAttestationWithMetadata(statement: MetadataStatement, credentialPublicKey: Buffer, x5c: Buffer[] | Base64URLString[]): Promise<boolean>; |
@@ -8,2 +8,4 @@ "use strict"; | ||
const validateCertificatePath_1 = __importDefault(require("../helpers/validateCertificatePath")); | ||
const decodeCredentialPublicKey_1 = __importDefault(require("../helpers/decodeCredentialPublicKey")); | ||
const convertCOSEtoPKCS_1 = require("../helpers/convertCOSEtoPKCS"); | ||
/** | ||
@@ -13,5 +15,5 @@ * Match properties of the authenticator's attestation statement against expected values as | ||
*/ | ||
async function verifyAttestationWithMetadata(statement, alg, x5c) { | ||
async function verifyAttestationWithMetadata(statement, credentialPublicKey, x5c) { | ||
// Make sure the alg in the attestation statement matches one of the ones specified in metadata | ||
const statementCOSEAlgs = new Set(); | ||
const keypairCOSEAlgs = new Set(); | ||
statement.authenticationAlgorithms.forEach(algSign => { | ||
@@ -21,9 +23,43 @@ // Convert algSign string to { kty, alg, crv } | ||
if (algSignCOSEINFO) { | ||
statementCOSEAlgs.add(algSignCOSEINFO.alg); | ||
keypairCOSEAlgs.add(algSignCOSEINFO); | ||
} | ||
}); | ||
if (!statementCOSEAlgs.has(alg)) { | ||
const debugAlgs = Array.from(statementCOSEAlgs).join(', '); | ||
throw new Error(`Attestation alg "${alg}" did not match metadata auth algs [${debugAlgs}]`); | ||
// Extract the public key's COSE info for comparison | ||
const decodedPublicKey = decodeCredentialPublicKey_1.default(credentialPublicKey); | ||
// Assume everything is a number because these values should be | ||
const publicKeyCOSEInfo = { | ||
kty: decodedPublicKey.get(convertCOSEtoPKCS_1.COSEKEYS.kty), | ||
alg: decodedPublicKey.get(convertCOSEtoPKCS_1.COSEKEYS.alg), | ||
crv: decodedPublicKey.get(convertCOSEtoPKCS_1.COSEKEYS.crv), | ||
}; | ||
if (!publicKeyCOSEInfo.crv) { | ||
delete publicKeyCOSEInfo.crv; | ||
} | ||
/** | ||
* Attempt to match the credential public key's algorithm to one specified in the device's | ||
* metadata | ||
*/ | ||
let foundMatch = false; | ||
for (const keypairAlg of keypairCOSEAlgs) { | ||
// Make sure algorithm and key type match | ||
if (keypairAlg.alg === publicKeyCOSEInfo.alg && keypairAlg.kty === publicKeyCOSEInfo.kty) { | ||
// If not an RSA keypair then make sure curve numbers match too | ||
if ((keypairAlg.kty === convertCOSEtoPKCS_1.COSEKTY.EC2 || keypairAlg.kty === convertCOSEtoPKCS_1.COSEKTY.OKP) | ||
&& keypairAlg.crv === publicKeyCOSEInfo.crv) { | ||
foundMatch = true; | ||
} | ||
else { | ||
// We've matched an RSA public key's properties | ||
foundMatch = true; | ||
} | ||
} | ||
if (foundMatch) { | ||
break; | ||
} | ||
} | ||
// Make sure the public key is one of the allowed algorithms | ||
if (!foundMatch) { | ||
const debugAlgs = Array.from(keypairCOSEAlgs).join(', '); | ||
throw new Error(`Public key algorithm ${publicKeyCOSEInfo} did not match any metadata algorithms [${debugAlgs}]`); | ||
} | ||
try { | ||
@@ -33,3 +69,3 @@ await validateCertificatePath_1.default(x5c.map(convertCertBufferToPEM_1.default), statement.attestationRootCertificates.map(convertCertBufferToPEM_1.default)); | ||
catch (err) { | ||
throw new Error(`Could not validate certificate path with any metadata root certificates`); | ||
throw new Error(`Could not validate certificate path with any metadata root certificates: ${err.message}`); | ||
} | ||
@@ -36,0 +72,0 @@ return true; |
@@ -209,3 +209,3 @@ "use strict"; | ||
try { | ||
await verifyAttestationWithMetadata_1.default(statement, alg, x5c); | ||
await verifyAttestationWithMetadata_1.default(statement, credentialPublicKey, x5c); | ||
} | ||
@@ -212,0 +212,0 @@ catch (err) { |
@@ -81,3 +81,3 @@ "use strict"; | ||
try { | ||
await verifyAttestationWithMetadata_1.default(statement, alg, x5c); | ||
await verifyAttestationWithMetadata_1.default(statement, credentialPublicKey, x5c); | ||
} | ||
@@ -84,0 +84,0 @@ catch (err) { |
@@ -18,3 +18,3 @@ "use strict"; | ||
async function verifyAttestationAndroidSafetyNet(options) { | ||
const { attStmt, clientDataHash, authData, aaguid, rootCertificates, verifyTimestampMS = true, } = options; | ||
const { attStmt, clientDataHash, authData, aaguid, rootCertificates, verifyTimestampMS = true, credentialPublicKey, } = options; | ||
const { response, ver } = attStmt; | ||
@@ -76,5 +76,3 @@ if (!ver) { | ||
try { | ||
// Convert from alg in JWT header to a number in the metadata | ||
const alg = HEADER.alg === 'RS256' ? -257 : -99999; | ||
await verifyAttestationWithMetadata_1.default(statement, alg, HEADER.x5c); | ||
await verifyAttestationWithMetadata_1.default(statement, credentialPublicKey, HEADER.x5c); | ||
} | ||
@@ -81,0 +79,0 @@ catch (err) { |
@@ -92,3 +92,3 @@ "use strict"; | ||
try { | ||
await verifyAttestationWithMetadata_1.default(statement, alg, x5c); | ||
await verifyAttestationWithMetadata_1.default(statement, credentialPublicKey, x5c); | ||
} | ||
@@ -95,0 +95,0 @@ catch (err) { |
@@ -6,3 +6,3 @@ /// <reference types="node" /> | ||
credential: RegistrationCredentialJSON; | ||
expectedChallenge: string | ChallengeVerifier; | ||
expectedChallenge: string | ((challenge: string) => boolean); | ||
expectedOrigin: string | string[]; | ||
@@ -13,3 +13,2 @@ expectedRPID?: string | string[]; | ||
}; | ||
export declare type ChallengeVerifier = (challenge: string) => boolean; | ||
/** | ||
@@ -16,0 +15,0 @@ * Verify that the user has legitimately completed the registration process |
@@ -12,12 +12,1 @@ /** | ||
export declare const GlobalSign_Root_CA = "-----BEGIN CERTIFICATE-----\nMIIDdTCCAl2gAwIBAgILBAAAAAABFUtaw5QwDQYJKoZIhvcNAQEFBQAwVzELMAkG\nA1UEBhMCQkUxGTAXBgNVBAoTEEdsb2JhbFNpZ24gbnYtc2ExEDAOBgNVBAsTB1Jv\nb3QgQ0ExGzAZBgNVBAMTEkdsb2JhbFNpZ24gUm9vdCBDQTAeFw05ODA5MDExMjAw\nMDBaFw0yODAxMjgxMjAwMDBaMFcxCzAJBgNVBAYTAkJFMRkwFwYDVQQKExBHbG9i\nYWxTaWduIG52LXNhMRAwDgYDVQQLEwdSb290IENBMRswGQYDVQQDExJHbG9iYWxT\naWduIFJvb3QgQ0EwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQDaDuaZ\njc6j40+Kfvvxi4Mla+pIH/EqsLmVEQS98GPR4mdmzxzdzxtIK+6NiY6arymAZavp\nxy0Sy6scTHAHoT0KMM0VjU/43dSMUBUc71DuxC73/OlS8pF94G3VNTCOXkNz8kHp\n1Wrjsok6Vjk4bwY8iGlbKk3Fp1S4bInMm/k8yuX9ifUSPJJ4ltbcdG6TRGHRjcdG\nsnUOhugZitVtbNV4FpWi6cgKOOvyJBNPc1STE4U6G7weNLWLBYy5d4ux2x8gkasJ\nU26Qzns3dLlwR5EiUWMWea6xrkEmCMgZK9FGqkjWZCrXgzT/LCrBbBlDSgeF59N8\n9iFo7+ryUp9/k5DPAgMBAAGjQjBAMA4GA1UdDwEB/wQEAwIBBjAPBgNVHRMBAf8E\nBTADAQH/MB0GA1UdDgQWBBRge2YaRQ2XyolQL30EzTSo//z9SzANBgkqhkiG9w0B\nAQUFAAOCAQEA1nPnfE920I2/7LqivjTFKDK1fPxsnCwrvQmeU79rXqoRSLblCKOz\nyj1hTdNGCbM+w6DjY1Ub8rrvrTnhQ7k4o+YviiY776BQVvnGCv04zcQLcFGUl5gE\n38NflNUVyRRBnMRddWQVDf9VMOyGj/8N7yy5Y0b2qvzfvGn9LhJIZJrglfCm7ymP\nAbEVtQwdpf5pLGkkeB6zpxxxYu7KyJesF12KwvhHhm4qxFYxldBniYUr+WymXUad\nDKqC5JlR3XC321Y9YeRq4VzW9v493kHMB65jUr9TU/Qr6cf9tveCX4XSQRjbgbME\nHMUfpIBvFSDJ3gyICh3WZlXi/EjJKSZp4A==\n-----END CERTIFICATE-----\n"; | ||
/** | ||
* GlobalSign R2 | ||
* | ||
* Downloaded from https://pki.goog/repo/certs/gsr2.pem | ||
* | ||
* Valid until 2021-12-15 @ 00:00 PST | ||
* | ||
* SHA256 Fingerprint | ||
* 69:E2:D0:6C:30:F3:66:16:61:65:E9:1D:68:D1:CE:E5:CC:47:58:4A:80:22:7E:76:66:60:86:C0:10:72:41:EB | ||
*/ | ||
export declare const GlobalSign_R2 = "-----BEGIN CERTIFICATE-----\nMIIDvDCCAqSgAwIBAgINAgPk9GHsmdnVeWbKejANBgkqhkiG9w0BAQUFADBMMSAw\nHgYDVQQLExdHbG9iYWxTaWduIFJvb3QgQ0EgLSBSMjETMBEGA1UEChMKR2xvYmFs\nU2lnbjETMBEGA1UEAxMKR2xvYmFsU2lnbjAeFw0wNjEyMTUwODAwMDBaFw0yMTEy\nMTUwODAwMDBaMEwxIDAeBgNVBAsTF0dsb2JhbFNpZ24gUm9vdCBDQSAtIFIyMRMw\nEQYDVQQKEwpHbG9iYWxTaWduMRMwEQYDVQQDEwpHbG9iYWxTaWduMIIBIjANBgkq\nhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAps8kDr4ubyiZRULEqz4hVJsL03+EcPoS\ns8u/h1/Gf4bTsjBc1v2t8Xvc5fhglgmSEPXQU977e35ziKxSiHtKpspJpl6op4xa\nEbx6guu+jOmzrJYlB5dKmSoHL7Qed7+KD7UCfBuWuMW5Oiy81hK561l94tAGhl9e\nSWq1OV6INOy8eAwImIRsqM1LtKB9DHlN8LgtyyHK1WxbfeGgKYSh+dOUScskYpEg\nvN0L1dnM+eonCitzkcadG6zIy+jgoPQvkItN+7A2G/YZeoXgbfJhE4hcn+CTClGX\nilrOr6vV96oJqmC93Nlf33KpYBNeAAHJSvo/pOoHAyECjoLKA8KbjwIDAQABo4Gc\nMIGZMA4GA1UdDwEB/wQEAwIBhjAPBgNVHRMBAf8EBTADAQH/MB0GA1UdDgQWBBSb\n4gdXZxwewGoG3lm0mi3f3BmGLjAfBgNVHSMEGDAWgBSb4gdXZxwewGoG3lm0mi3f\n3BmGLjA2BgNVHR8ELzAtMCugKaAnhiVodHRwOi8vY3JsLmdsb2JhbHNpZ24ubmV0\nL3Jvb3QtcjIuY3JsMA0GCSqGSIb3DQEBBQUAA4IBAQANeX81Z1YqDIs4EaLjG0qP\nOxIzaJI/y4kiRj3a+y3KOx74clIkLuMgi/9/5iv/n+1LyhGU9g7174slbzJOPbSp\np1eT19ST2mYbdgTLx/hm3tTLoHIY/w4ZbnQYwfnPwAG4RefnEFYPQJmpD+Wh8BJw\nBgtm2drTale/T6NBwmwnEFunfaMfMX3g6IBrx7VKnxIkJh/3p190WveLKgl9n7i5\nSWce/4woPimEn9WfEQWRvp6wKhaCKFjuCMuulEZusoOUJ4LfJnXxcuQTgIrSnwI7\nKfSSjsd42w3lX1fbgJp7vPmLM6OBRvAXuYRKTFqMAWbb7OaGIEE+cbxY6PDepnva\n-----END CERTIFICATE-----\n"; |
"use strict"; | ||
Object.defineProperty(exports, "__esModule", { value: true }); | ||
exports.GlobalSign_R2 = exports.GlobalSign_Root_CA = void 0; | ||
exports.GlobalSign_Root_CA = void 0; | ||
/** | ||
@@ -36,35 +36,2 @@ * GlobalSign Root CA | ||
`; | ||
/** | ||
* GlobalSign R2 | ||
* | ||
* Downloaded from https://pki.goog/repo/certs/gsr2.pem | ||
* | ||
* Valid until 2021-12-15 @ 00:00 PST | ||
* | ||
* SHA256 Fingerprint | ||
* 69:E2:D0:6C:30:F3:66:16:61:65:E9:1D:68:D1:CE:E5:CC:47:58:4A:80:22:7E:76:66:60:86:C0:10:72:41:EB | ||
*/ | ||
exports.GlobalSign_R2 = `-----BEGIN CERTIFICATE----- | ||
MIIDvDCCAqSgAwIBAgINAgPk9GHsmdnVeWbKejANBgkqhkiG9w0BAQUFADBMMSAw | ||
HgYDVQQLExdHbG9iYWxTaWduIFJvb3QgQ0EgLSBSMjETMBEGA1UEChMKR2xvYmFs | ||
U2lnbjETMBEGA1UEAxMKR2xvYmFsU2lnbjAeFw0wNjEyMTUwODAwMDBaFw0yMTEy | ||
MTUwODAwMDBaMEwxIDAeBgNVBAsTF0dsb2JhbFNpZ24gUm9vdCBDQSAtIFIyMRMw | ||
EQYDVQQKEwpHbG9iYWxTaWduMRMwEQYDVQQDEwpHbG9iYWxTaWduMIIBIjANBgkq | ||
hkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAps8kDr4ubyiZRULEqz4hVJsL03+EcPoS | ||
s8u/h1/Gf4bTsjBc1v2t8Xvc5fhglgmSEPXQU977e35ziKxSiHtKpspJpl6op4xa | ||
Ebx6guu+jOmzrJYlB5dKmSoHL7Qed7+KD7UCfBuWuMW5Oiy81hK561l94tAGhl9e | ||
SWq1OV6INOy8eAwImIRsqM1LtKB9DHlN8LgtyyHK1WxbfeGgKYSh+dOUScskYpEg | ||
vN0L1dnM+eonCitzkcadG6zIy+jgoPQvkItN+7A2G/YZeoXgbfJhE4hcn+CTClGX | ||
ilrOr6vV96oJqmC93Nlf33KpYBNeAAHJSvo/pOoHAyECjoLKA8KbjwIDAQABo4Gc | ||
MIGZMA4GA1UdDwEB/wQEAwIBhjAPBgNVHRMBAf8EBTADAQH/MB0GA1UdDgQWBBSb | ||
4gdXZxwewGoG3lm0mi3f3BmGLjAfBgNVHSMEGDAWgBSb4gdXZxwewGoG3lm0mi3f | ||
3BmGLjA2BgNVHR8ELzAtMCugKaAnhiVodHRwOi8vY3JsLmdsb2JhbHNpZ24ubmV0 | ||
L3Jvb3QtcjIuY3JsMA0GCSqGSIb3DQEBBQUAA4IBAQANeX81Z1YqDIs4EaLjG0qP | ||
OxIzaJI/y4kiRj3a+y3KOx74clIkLuMgi/9/5iv/n+1LyhGU9g7174slbzJOPbSp | ||
p1eT19ST2mYbdgTLx/hm3tTLoHIY/w4ZbnQYwfnPwAG4RefnEFYPQJmpD+Wh8BJw | ||
Bgtm2drTale/T6NBwmwnEFunfaMfMX3g6IBrx7VKnxIkJh/3p190WveLKgl9n7i5 | ||
SWce/4woPimEn9WfEQWRvp6wKhaCKFjuCMuulEZusoOUJ4LfJnXxcuQTgIrSnwI7 | ||
KfSSjsd42w3lX1fbgJp7vPmLM6OBRvAXuYRKTFqMAWbb7OaGIEE+cbxY6PDepnva | ||
-----END CERTIFICATE----- | ||
`; | ||
//# sourceMappingURL=android-safetynet.js.map |
@@ -52,3 +52,3 @@ "use strict"; | ||
identifier: 'android-safetynet', | ||
certificates: [android_safetynet_1.GlobalSign_R2, android_safetynet_1.GlobalSign_Root_CA], | ||
certificates: [android_safetynet_1.GlobalSign_Root_CA], | ||
}); | ||
@@ -55,0 +55,0 @@ settingsService.setRootCertificates({ |
{ | ||
"name": "@simplewebauthn/server", | ||
"version": "4.3.0", | ||
"version": "4.4.0", | ||
"description": "SimpleWebAuthn for Servers", | ||
@@ -62,3 +62,3 @@ "main": "dist/index.js", | ||
}, | ||
"gitHead": "717b7037f58ff18003309e2c1f05f5a2f7036c2d", | ||
"gitHead": "4a8fb255d4fd6fbc146dedf0a2efc938b99f5973", | ||
"devDependencies": { | ||
@@ -65,0 +65,0 @@ "@types/cbor": "^5.0.1", |
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
243781
3802