@simplewebauthn/server
Advanced tools
Comparing version 5.4.5 to 6.0.0
@@ -34,3 +34,3 @@ /// <reference types="node" /> | ||
*/ | ||
export declare function verifyAuthenticationResponse(options: VerifyAuthenticationResponseOpts): VerifiedAuthenticationResponse; | ||
export declare function verifyAuthenticationResponse(options: VerifyAuthenticationResponseOpts): Promise<VerifiedAuthenticationResponse>; | ||
/** | ||
@@ -37,0 +37,0 @@ * Result of authentication verification |
@@ -10,3 +10,2 @@ "use strict"; | ||
const toHash_1 = require("../helpers/toHash"); | ||
const convertPublicKeyToPEM_1 = require("../helpers/convertPublicKeyToPEM"); | ||
const verifySignature_1 = require("../helpers/verifySignature"); | ||
@@ -35,3 +34,3 @@ const parseAuthenticatorData_1 = require("../helpers/parseAuthenticatorData"); | ||
*/ | ||
function verifyAuthenticationResponse(options) { | ||
async function verifyAuthenticationResponse(options) { | ||
const { credential, expectedChallenge, expectedOrigin, expectedRPID, authenticator, requireUserVerification, advancedFIDOConfig, } = options; | ||
@@ -122,3 +121,3 @@ const { id, rawId, type: credentialType, response } = credential; | ||
if (advancedFIDOConfig !== undefined) { | ||
const { userVerification: fidoUserVerification, } = advancedFIDOConfig; | ||
const { userVerification: fidoUserVerification } = advancedFIDOConfig; | ||
/** | ||
@@ -152,3 +151,2 @@ * Use FIDO Conformance-defined rules for verifying UP and UV flags | ||
const signatureBase = Buffer.concat([authDataBuffer, clientDataHash]); | ||
const publicKey = (0, convertPublicKeyToPEM_1.convertPublicKeyToPEM)(authenticator.credentialPublicKey); | ||
const signature = base64url_1.default.toBuffer(response.signature); | ||
@@ -164,3 +162,7 @@ if ((counter > 0 || authenticator.counter > 0) && counter <= authenticator.counter) { | ||
const toReturn = { | ||
verified: (0, verifySignature_1.verifySignature)(signature, signatureBase, publicKey), | ||
verified: await (0, verifySignature_1.verifySignature)({ | ||
signature, | ||
signatureBase, | ||
credentialPublicKey: authenticator.credentialPublicKey, | ||
}), | ||
authenticationInfo: { | ||
@@ -167,0 +169,0 @@ newCounter: counter, |
/// <reference types="node" /> | ||
import type { SigningSchemeHash } from 'node-rsa'; | ||
import { COSEAlgorithmIdentifier } from '@simplewebauthn/typescript-types'; | ||
@@ -32,1 +31,6 @@ /** | ||
}; | ||
/** | ||
* Imported from node-rsa's types | ||
*/ | ||
declare type SigningSchemeHash = 'pkcs1-ripemd160' | 'pkcs1-md4' | 'pkcs1-md5' | 'pkcs1-sha' | 'pkcs1-sha1' | 'pkcs1-sha224' | 'pkcs1-sha256' | 'pkcs1-sha384' | 'pkcs1-sha512' | 'pss-ripemd160' | 'pss-md4' | 'pss-md5' | 'pss-sha' | 'pss-sha1' | 'pss-sha224' | 'pss-sha256' | 'pss-sha384' | 'pss-sha512'; | ||
export {}; |
@@ -59,13 +59,14 @@ "use strict"; | ||
exports.COSEALGHASH = { | ||
'-65535': 'sha1', | ||
'-259': 'sha512', | ||
'-258': 'sha384', | ||
'-257': 'sha256', | ||
'-258': 'sha384', | ||
'-259': 'sha512', | ||
'-65535': 'sha1', | ||
'-39': 'sha512', | ||
'-38': 'sha384', | ||
'-37': 'sha256', | ||
'-36': 'sha512', | ||
'-35': 'sha384', | ||
'-8': 'sha512', | ||
'-7': 'sha256', | ||
'-8': 'sha512', | ||
'-36': 'sha512', | ||
}; | ||
//# sourceMappingURL=convertCOSEtoPKCS.js.map |
/// <reference types="node" /> | ||
declare type VerifySignatureOptsLeafCert = { | ||
signature: Buffer; | ||
signatureBase: Buffer; | ||
leafCert: Buffer; | ||
hashAlgorithm?: string; | ||
}; | ||
declare type VerifySignatureOptsCredentialPublicKey = { | ||
signature: Buffer; | ||
signatureBase: Buffer; | ||
credentialPublicKey: Buffer; | ||
hashAlgorithm?: string; | ||
}; | ||
/** | ||
@@ -10,2 +22,3 @@ * Verify an authenticator's signature | ||
*/ | ||
export declare function verifySignature(signature: Buffer, signatureBase: Buffer, publicKey: string, algo?: string): boolean; | ||
export declare function verifySignature(opts: VerifySignatureOptsLeafCert | VerifySignatureOptsCredentialPublicKey): Promise<boolean>; | ||
export {}; |
@@ -8,2 +8,7 @@ "use strict"; | ||
const crypto_1 = __importDefault(require("crypto")); | ||
const cbor_1 = __importDefault(require("cbor")); | ||
const ed25519_1 = require("@noble/ed25519"); | ||
const convertCOSEtoPKCS_1 = require("./convertCOSEtoPKCS"); | ||
const convertCertBufferToPEM_1 = require("./convertCertBufferToPEM"); | ||
const convertPublicKeyToPEM_1 = require("./convertPublicKeyToPEM"); | ||
/** | ||
@@ -17,6 +22,55 @@ * Verify an authenticator's signature | ||
*/ | ||
function verifySignature(signature, signatureBase, publicKey, algo = 'sha256') { | ||
return crypto_1.default.createVerify(algo).update(signatureBase).verify(publicKey, signature); | ||
async function verifySignature(opts) { | ||
const { signature, signatureBase, hashAlgorithm = 'sha256' } = opts; | ||
const _isLeafcertOpts = isLeafCertOpts(opts); | ||
const _isCredPubKeyOpts = isCredPubKeyOpts(opts); | ||
if (!_isLeafcertOpts && !_isCredPubKeyOpts) { | ||
throw new Error('Must declare either "leafCert" or "credentialPublicKey"'); | ||
} | ||
if (_isLeafcertOpts && _isCredPubKeyOpts) { | ||
throw new Error('Must not declare both "leafCert" and "credentialPublicKey"'); | ||
} | ||
let publicKeyPEM = ''; | ||
if (_isCredPubKeyOpts) { | ||
const { credentialPublicKey } = opts; | ||
// Decode CBOR to COSE | ||
let struct; | ||
try { | ||
struct = cbor_1.default.decodeAllSync(credentialPublicKey)[0]; | ||
} | ||
catch (err) { | ||
const _err = err; | ||
throw new Error(`Error decoding public key while converting to PEM: ${_err.message}`); | ||
} | ||
const kty = struct.get(convertCOSEtoPKCS_1.COSEKEYS.kty); | ||
if (!kty) { | ||
throw new Error('Public key was missing kty'); | ||
} | ||
// Check key type | ||
if (kty === convertCOSEtoPKCS_1.COSEKTY.OKP) { | ||
// Verify Ed25519 slightly differently | ||
const x = struct.get(convertCOSEtoPKCS_1.COSEKEYS.x); | ||
if (!x) { | ||
throw new Error('Public key was missing x (OKP)'); | ||
} | ||
return (0, ed25519_1.verify)(signature, signatureBase, x); | ||
} | ||
else { | ||
// Convert pubKey to PEM for ECC and RSA | ||
publicKeyPEM = (0, convertPublicKeyToPEM_1.convertPublicKeyToPEM)(credentialPublicKey); | ||
} | ||
} | ||
if (_isLeafcertOpts) { | ||
const { leafCert } = opts; | ||
publicKeyPEM = (0, convertCertBufferToPEM_1.convertCertBufferToPEM)(leafCert); | ||
} | ||
return crypto_1.default.createVerify(hashAlgorithm).update(signatureBase).verify(publicKeyPEM, signature); | ||
} | ||
exports.verifySignature = verifySignature; | ||
function isLeafCertOpts(opts) { | ||
return Object.keys(opts).indexOf('leafCert') >= 0; | ||
} | ||
function isCredPubKeyOpts(opts) { | ||
return (Object.keys(opts).indexOf('credentialPublicKey') >= 0); | ||
} | ||
//# sourceMappingURL=verifySignature.js.map |
@@ -227,4 +227,8 @@ "use strict"; | ||
// In the wise words of Yuriy Ackermann: "Get Martini friend, you are done!" | ||
const leafCertPEM = (0, convertCertBufferToPEM_1.convertCertBufferToPEM)(x5c[0]); | ||
return (0, verifySignature_1.verifySignature)(sig, certInfo, leafCertPEM, hashAlg); | ||
return (0, verifySignature_1.verifySignature)({ | ||
signature: sig, | ||
signatureBase: certInfo, | ||
leafCert: x5c[0], | ||
hashAlgorithm: hashAlg | ||
}); | ||
} | ||
@@ -231,0 +235,0 @@ exports.verifyAttestationTPM = verifyAttestationTPM; |
@@ -83,7 +83,11 @@ "use strict"; | ||
const signatureBase = Buffer.concat([authData, clientDataHash]); | ||
const leafCertPEM = (0, convertCertBufferToPEM_1.convertCertBufferToPEM)(x5c[0]); | ||
const hashAlg = convertCOSEtoPKCS_1.COSEALGHASH[alg]; | ||
return (0, verifySignature_1.verifySignature)(sig, signatureBase, leafCertPEM, hashAlg); | ||
return (0, verifySignature_1.verifySignature)({ | ||
signature: sig, | ||
signatureBase, | ||
leafCert: x5c[0], | ||
hashAlgorithm: hashAlg | ||
}); | ||
} | ||
exports.verifyAttestationAndroidKey = verifyAttestationAndroidKey; | ||
//# sourceMappingURL=verifyAttestationAndroidKey.js.map |
@@ -106,4 +106,7 @@ "use strict"; | ||
const signatureBuffer = base64url_1.default.toBuffer(SIGNATURE); | ||
const leafCertPEM = (0, convertCertBufferToPEM_1.convertCertBufferToPEM)(leafCertBuffer); | ||
const verified = (0, verifySignature_1.verifySignature)(signatureBuffer, signatureBaseBuffer, leafCertPEM); | ||
const verified = await (0, verifySignature_1.verifySignature)({ | ||
signature: signatureBuffer, | ||
signatureBase: signatureBaseBuffer, | ||
leafCert: leafCertBuffer, | ||
}); | ||
/** | ||
@@ -110,0 +113,0 @@ * END Verify Signature |
@@ -42,6 +42,9 @@ "use strict"; | ||
} | ||
const leafCertPEM = (0, convertCertBufferToPEM_1.convertCertBufferToPEM)(x5c[0]); | ||
return (0, verifySignature_1.verifySignature)(sig, signatureBase, leafCertPEM); | ||
return (0, verifySignature_1.verifySignature)({ | ||
signature: sig, | ||
signatureBase, | ||
leafCert: x5c[0], | ||
}); | ||
} | ||
exports.verifyAttestationFIDOU2F = verifyAttestationFIDOU2F; | ||
//# sourceMappingURL=verifyAttestationFIDOU2F.js.map |
"use strict"; | ||
var __importDefault = (this && this.__importDefault) || function (mod) { | ||
return (mod && mod.__esModule) ? mod : { "default": mod }; | ||
}; | ||
Object.defineProperty(exports, "__esModule", { value: true }); | ||
exports.verifyAttestationPacked = void 0; | ||
const elliptic_1 = __importDefault(require("elliptic")); | ||
const node_rsa_1 = __importDefault(require("node-rsa")); | ||
const convertCOSEtoPKCS_1 = require("../../helpers/convertCOSEtoPKCS"); | ||
const toHash_1 = require("../../helpers/toHash"); | ||
const convertCertBufferToPEM_1 = require("../../helpers/convertCertBufferToPEM"); | ||
@@ -15,3 +9,2 @@ const validateCertificatePath_1 = require("../../helpers/validateCertificatePath"); | ||
const verifySignature_1 = require("../../helpers/verifySignature"); | ||
const decodeCredentialPublicKey_1 = require("../../helpers/decodeCredentialPublicKey"); | ||
const metadataService_1 = require("../../services/metadataService"); | ||
@@ -33,5 +26,3 @@ const verifyAttestationWithMetadata_1 = require("../../metadata/verifyAttestationWithMetadata"); | ||
let verified = false; | ||
const pkcsPublicKey = (0, convertCOSEtoPKCS_1.convertCOSEtoPKCS)(credentialPublicKey); | ||
if (x5c) { | ||
const leafCert = (0, convertCertBufferToPEM_1.convertCertBufferToPEM)(x5c[0]); | ||
const { subject, basicConstraintsCA, version, notBefore, notAfter } = (0, getCertificateInfo_1.getCertificateInfo)(x5c[0]); | ||
@@ -98,56 +89,16 @@ const { OU, CN, O, C } = subject; | ||
} | ||
verified = (0, verifySignature_1.verifySignature)(sig, signatureBase, leafCert); | ||
verified = await (0, verifySignature_1.verifySignature)({ | ||
signature: sig, | ||
signatureBase, | ||
leafCert: x5c[0], | ||
}); | ||
} | ||
else { | ||
const cosePublicKey = (0, decodeCredentialPublicKey_1.decodeCredentialPublicKey)(credentialPublicKey); | ||
const kty = cosePublicKey.get(convertCOSEtoPKCS_1.COSEKEYS.kty); | ||
if (!kty) { | ||
throw new Error('COSE public key was missing kty (Packed|Self)'); | ||
} | ||
const hashAlg = convertCOSEtoPKCS_1.COSEALGHASH[alg]; | ||
if (kty === convertCOSEtoPKCS_1.COSEKTY.EC2) { | ||
const crv = cosePublicKey.get(convertCOSEtoPKCS_1.COSEKEYS.crv); | ||
if (!crv) { | ||
throw new Error('COSE public key was missing kty crv (Packed|EC2)'); | ||
} | ||
const signatureBaseHash = (0, toHash_1.toHash)(signatureBase, hashAlg); | ||
/** | ||
* Instantiating the curve here is _very_ computationally heavy - a bit of profiling | ||
* (in compiled JS, not TS) reported an average of ~125ms to execute this line. The elliptic | ||
* README states, "better do it once and reuse it", so maybe there's a better way to handle | ||
* this in a server context, when we can re-use an existing instance. | ||
* | ||
* For now, it's worth noting that this line is probably the reason why it can take | ||
* 5-6 seconds to run tests. | ||
*/ | ||
const ec = new elliptic_1.default.ec(convertCOSEtoPKCS_1.COSECRV[crv]); | ||
const key = ec.keyFromPublic(pkcsPublicKey); | ||
verified = key.verify(signatureBaseHash, sig); | ||
} | ||
else if (kty === convertCOSEtoPKCS_1.COSEKTY.RSA) { | ||
const n = cosePublicKey.get(convertCOSEtoPKCS_1.COSEKEYS.n); | ||
if (!n) { | ||
throw new Error('COSE public key was missing n (Packed|RSA)'); | ||
} | ||
const signingScheme = convertCOSEtoPKCS_1.COSERSASCHEME[alg]; | ||
// TODO: Verify this works | ||
const key = new node_rsa_1.default(); | ||
key.setOptions({ signingScheme }); | ||
key.importKey({ | ||
n: n, | ||
e: 65537, | ||
}, 'components-public'); | ||
verified = key.verify(signatureBase, sig); | ||
} | ||
else if (kty === convertCOSEtoPKCS_1.COSEKTY.OKP) { | ||
const x = cosePublicKey.get(convertCOSEtoPKCS_1.COSEKEYS.x); | ||
if (!x) { | ||
throw new Error('COSE public key was missing x (Packed|OKP)'); | ||
} | ||
const signatureBaseHash = (0, toHash_1.toHash)(signatureBase, hashAlg); | ||
const key = new elliptic_1.default.eddsa('ed25519'); | ||
key.keyFromPublic(x); | ||
// TODO: is `publicKey` right here? | ||
verified = key.verify(signatureBaseHash, sig, pkcsPublicKey); | ||
} | ||
verified = await (0, verifySignature_1.verifySignature)({ | ||
signature: sig, | ||
signatureBase, | ||
credentialPublicKey, | ||
hashAlgorithm: hashAlg | ||
}); | ||
} | ||
@@ -154,0 +105,0 @@ return verified; |
{ | ||
"name": "@simplewebauthn/server", | ||
"version": "5.4.5", | ||
"version": "6.0.0", | ||
"description": "SimpleWebAuthn for Servers", | ||
@@ -33,3 +33,3 @@ "main": "dist/index.js", | ||
"engines": { | ||
"node": ">=10.0.0" | ||
"node": ">=14.0.0" | ||
}, | ||
@@ -50,25 +50,22 @@ "scripts": { | ||
"dependencies": { | ||
"@noble/ed25519": "^1.6.1", | ||
"@peculiar/asn1-android": "^2.1.7", | ||
"@peculiar/asn1-schema": "^2.1.7", | ||
"@peculiar/asn1-x509": "^2.1.7", | ||
"@simplewebauthn/typescript-types": "^5.4.0", | ||
"@simplewebauthn/typescript-types": "^6.0.0", | ||
"base64url": "^3.0.1", | ||
"cbor": "^5.1.0", | ||
"debug": "^4.3.2", | ||
"elliptic": "^6.5.3", | ||
"jsrsasign": "^10.4.0", | ||
"jwk-to-pem": "^2.0.4", | ||
"node-fetch": "^2.6.0", | ||
"node-rsa": "^1.1.1" | ||
"node-fetch": "^2.6.0" | ||
}, | ||
"gitHead": "d5fbeb340fb2610c86f102f184aa75a0d27cc243", | ||
"gitHead": "95cb2107d15ae15994367cc99040720ae186c9bd", | ||
"devDependencies": { | ||
"@types/cbor": "^5.0.1", | ||
"@types/debug": "^4.1.7", | ||
"@types/elliptic": "^6.4.13", | ||
"@types/jsrsasign": "^8.0.13", | ||
"@types/jwk-to-pem": "^2.0.1", | ||
"@types/node-fetch": "^2.5.12", | ||
"@types/node-rsa": "^1.1.1" | ||
"@types/node-fetch": "^2.5.12" | ||
} | ||
} |
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
11
5
4170
263768
+ Added@noble/ed25519@^1.6.1
+ Added@noble/ed25519@1.7.3(transitive)
+ Added@simplewebauthn/typescript-types@6.2.1(transitive)
- Removedelliptic@^6.5.3
- Removednode-rsa@^1.1.1
- Removed@simplewebauthn/typescript-types@5.4.0(transitive)
- Removedasn1@0.2.6(transitive)
- Removednode-rsa@1.1.1(transitive)