diff options
Diffstat (limited to 'packages/server/src')
8 files changed, 171 insertions, 67 deletions
diff --git a/packages/server/src/helpers/convertCOSEtoPKCS.ts b/packages/server/src/helpers/convertCOSEtoPKCS.ts index b9b9d09..d388d98 100644 --- a/packages/server/src/helpers/convertCOSEtoPKCS.ts +++ b/packages/server/src/helpers/convertCOSEtoPKCS.ts @@ -1,12 +1,13 @@ -import { COSEAlgorithmIdentifier } from '@simplewebauthn/typescript-types'; - import { isoCBOR, isoUint8Array } from './iso'; /** * Takes COSE-encoded public key and converts it to PKCS key */ export function convertCOSEtoPKCS(cosePublicKey: Uint8Array): Uint8Array { - const struct = isoCBOR.decodeFirst<COSEPublicKey>(cosePublicKey); + // This is a little sloppy, I'm using COSEPublicKeyEC2 since it could have both x and y, but when + // there's no y it means it's probably better typed as COSEPublicKeyOKP. I'll leave this for now + // and revisit it later if it ever becomes an actual problem. + const struct = isoCBOR.decodeFirst<COSEPublicKeyEC2>(cosePublicKey); const tag = Uint8Array.from([0x04]); const x = struct.get(COSEKEYS.x); @@ -17,13 +18,62 @@ export function convertCOSEtoPKCS(cosePublicKey: Uint8Array): Uint8Array { } if (y) { - return isoUint8Array.concat([tag, x as Uint8Array, y as Uint8Array]); + return isoUint8Array.concat([tag, x, y]); } - return isoUint8Array.concat([tag, x as Uint8Array]); + return isoUint8Array.concat([tag, x]); } -export type COSEPublicKey = Map<COSEAlgorithmIdentifier, number | Uint8Array>; +/** + * Fundamental values that are needed to discern the more specific COSE public key types below + */ +export type COSEPublicKey = { + // Getters + get(key: COSEKEYS.kty): COSEKTY | undefined; + get(key: COSEKEYS.alg): COSEALG | undefined; + // Setters + set(key: COSEKEYS.kty, value: COSEKTY): void; + set(key: COSEKEYS.alg, value: COSEALG): void; +}; + +export type COSEPublicKeyOKP = COSEPublicKey & { + // Getters + get(key: COSEKEYS.x): Uint8Array | undefined; + // Setters + set(key: COSEKEYS.x, value: Uint8Array): void; +}; + +export type COSEPublicKeyEC2 = COSEPublicKey & { + // Getters + get(key: COSEKEYS.crv): number | undefined; + get(key: COSEKEYS.x): Uint8Array | undefined; + get(key: COSEKEYS.y): Uint8Array | undefined; + // Setters + set(key: COSEKEYS.crv, value: number): void; + set(key: COSEKEYS.x, value: Uint8Array): void; + set(key: COSEKEYS.y, value: Uint8Array): void; +}; + +export type COSEPublicKeyRSA = COSEPublicKey & { + // Getters + get(key: COSEKEYS.n): Uint8Array | undefined; + get(key: COSEKEYS.e): Uint8Array | undefined; + // Setters + set(key: COSEKEYS.n, value: Uint8Array): void; + set(key: COSEKEYS.e, value: Uint8Array): void; +}; + +export function isCOSEPublicKeyOKP(publicKey: COSEPublicKey): publicKey is COSEPublicKeyOKP { + return publicKey.get(COSEKEYS.kty) === COSEKTY.OKP; +} + +export function isCOSEPublicKeyEC2(publicKey: COSEPublicKey): publicKey is COSEPublicKeyEC2 { + return publicKey.get(COSEKEYS.kty) === COSEKTY.EC2; +} + +export function isCOSEPublicKeyRSA(publicKey: COSEPublicKey): publicKey is COSEPublicKeyRSA { + return publicKey.get(COSEKEYS.kty) === COSEKTY.RSA; +} export enum COSEKEYS { kty = 1, @@ -48,7 +98,14 @@ export enum COSECRV { ED25519 = 6, } -export type COSEALG = number; +export const coseAlgs = [-65535, -259, -258, -257, -47, -39, -38, -37, -36, -35, -8, -7] as const; +export type COSEALG = typeof coseAlgs[number]; +/** + * Ensure that a number is a valid COSE algorithm ID + */ +export function isCOSEAlg(alg: number): alg is COSEALG { + return coseAlgs.indexOf(alg as COSEALG) >= 0; +} export const COSERSASCHEME: { [key: string]: SigningSchemeHash } = { '-3': 'pss-sha256', @@ -72,11 +129,12 @@ export const coseCRV: { [key: number]: string } = { 6: 'ed25519', }; -export const COSEALGHASH: { [key: string]: string } = { +export const coseAlgSHAHashMap: { [K in COSEALG]: string } = { '-65535': 'sha1', '-259': 'sha512', '-258': 'sha384', '-257': 'sha256', + '-47': 'sha256', '-39': 'sha512', '-38': 'sha384', '-37': 'sha256', diff --git a/packages/server/src/helpers/convertPublicKeyToPEM.ts b/packages/server/src/helpers/convertPublicKeyToPEM.ts index 2721e3a..2aa685d 100644 --- a/packages/server/src/helpers/convertPublicKeyToPEM.ts +++ b/packages/server/src/helpers/convertPublicKeyToPEM.ts @@ -1,27 +1,27 @@ import jwkToPem from 'jwk-to-pem'; -import { COSEKEYS, COSEKTY, coseCRV, COSEPublicKey } from './convertCOSEtoPKCS'; +import { COSEKEYS, coseCRV, COSEPublicKey, isCOSEPublicKeyEC2, isCOSEPublicKeyRSA } from './convertCOSEtoPKCS'; import { isoBase64URL, isoCBOR } from './iso'; export function convertPublicKeyToPEM(publicKey: Uint8Array): string { - let struct; + let cosePublicKey; try { - struct = isoCBOR.decodeFirst<COSEPublicKey>(publicKey); + cosePublicKey = isoCBOR.decodeFirst<COSEPublicKey>(publicKey); } catch (err) { const _err = err as Error; throw new Error(`Error decoding public key while converting to PEM: ${_err.message}`); } - const kty = struct.get(COSEKEYS.kty); + const kty = cosePublicKey.get(COSEKEYS.kty); if (!kty) { throw new Error('Public key was missing kty'); } - if (kty === COSEKTY.EC2) { - const crv = struct.get(COSEKEYS.crv); - const x = struct.get(COSEKEYS.x); - const y = struct.get(COSEKEYS.y); + if (isCOSEPublicKeyEC2(cosePublicKey)) { + const crv = cosePublicKey.get(COSEKEYS.crv); + const x = cosePublicKey.get(COSEKEYS.x); + const y = cosePublicKey.get(COSEKEYS.y); if (!crv) { throw new Error('Public key was missing crv (EC2)'); @@ -39,14 +39,14 @@ export function convertPublicKeyToPEM(publicKey: Uint8Array): string { kty: 'EC', // Specify curve as "P-256" from "p256" crv: coseCRV[crv as number].replace('p', 'P-'), - x: isoBase64URL.fromBuffer(x as Uint8Array, 'base64'), - y: isoBase64URL.fromBuffer(y as Uint8Array, 'base64'), + x: isoBase64URL.fromBuffer(x, 'base64'), + y: isoBase64URL.fromBuffer(y, 'base64'), }); return ecPEM; - } else if (kty === COSEKTY.RSA) { - const n = struct.get(COSEKEYS.n); - const e = struct.get(COSEKEYS.e); + } else if (isCOSEPublicKeyRSA(cosePublicKey)) { + const n = cosePublicKey.get(COSEKEYS.n); + const e = cosePublicKey.get(COSEKEYS.e); if (!n) { throw new Error('Public key was missing n (RSA)'); @@ -58,8 +58,8 @@ export function convertPublicKeyToPEM(publicKey: Uint8Array): string { const rsaPEM = jwkToPem({ kty: 'RSA', - n: isoBase64URL.fromBuffer(n as Uint8Array, 'base64'), - e: isoBase64URL.fromBuffer(e as Uint8Array, 'base64'), + n: isoBase64URL.fromBuffer(n, 'base64'), + e: isoBase64URL.fromBuffer(e, 'base64'), }); return rsaPEM; diff --git a/packages/server/src/helpers/iso/isoCrypto.ts b/packages/server/src/helpers/iso/isoCrypto.ts index 42bd0a9..0296149 100644 --- a/packages/server/src/helpers/iso/isoCrypto.ts +++ b/packages/server/src/helpers/iso/isoCrypto.ts @@ -3,7 +3,7 @@ import { ECDSASigValue } from "@peculiar/asn1-ecc"; import { AsnParser } from '@peculiar/asn1-schema'; import { isoUint8Array, isoBase64URL } from './index'; -import { COSECRV, COSEKEYS, COSEKTY, COSEALG, COSEPublicKey } from '../convertCOSEtoPKCS'; +import { COSECRV, COSEKEYS, COSEKTY, COSEALG, COSEPublicKeyEC2, COSEPublicKeyRSA, isCOSEAlg, isCOSEPublicKeyEC2, isCOSEPublicKeyRSA } from '../convertCOSEtoPKCS'; /** * Fill up the provided bytes array with random bytes equal to its length. @@ -96,18 +96,18 @@ export async function verify({ * @param rsaHashAlgorithm A SHA hashing identifier for use when verifying signatures with the * returned RSA public key (e.g. `"sha1"`, `"sha256"`, etc...), if applicable */ -export async function importKey(publicKey: COSEPublicKey, rsaHashAlgorithm?: string): Promise<CryptoKey> { +export async function importKey(publicKey: COSEPublicKeyEC2 | COSEPublicKeyRSA, rsaHashAlgorithm?: string): Promise<CryptoKey> { const kty = publicKey.get(COSEKEYS.kty); if (!kty) { throw new Error('Public key was missing kty'); } - if (kty === COSEKTY.EC2) { + if (isCOSEPublicKeyEC2(publicKey)) { return importECKey(publicKey); } - if (kty === COSEKTY.RSA) { + if (isCOSEPublicKeyRSA(publicKey)) { return importRSAKey(publicKey, rsaHashAlgorithm); } @@ -117,7 +117,7 @@ export async function importKey(publicKey: COSEPublicKey, rsaHashAlgorithm?: str /** * Import an EC2 public key from its COSE representation */ -async function importECKey(publicKey: COSEPublicKey): Promise<CryptoKey> { +async function importECKey(publicKey: COSEPublicKeyEC2): Promise<CryptoKey> { const crv = publicKey.get(COSEKEYS.crv); const x = publicKey.get(COSEKEYS.x); const y = publicKey.get(COSEKEYS.y); @@ -151,8 +151,8 @@ async function importECKey(publicKey: COSEPublicKey): Promise<CryptoKey> { const jwk: JsonWebKey = { kty: "EC", crv: _crv, - x: isoBase64URL.fromBuffer(x as Uint8Array), - y: isoBase64URL.fromBuffer(y as Uint8Array), + x: isoBase64URL.fromBuffer(x), + y: isoBase64URL.fromBuffer(y), ext: false, }; @@ -197,35 +197,39 @@ async function verifyECSignature( /** * Import an RSA public key from its COSE representation */ -async function importRSAKey(publicKey: COSEPublicKey, hashAlgorithm?: string): Promise<CryptoKey> { +async function importRSAKey(publicKey: COSEPublicKeyRSA, hashAlgorithm?: string): Promise<CryptoKey> { const alg = publicKey.get(COSEKEYS.alg); const n = publicKey.get(COSEKEYS.n); const e = publicKey.get(COSEKEYS.e); if (!alg) { - throw new Error('RSA public key was missing alg'); + throw new Error('Public key was missing alg (RSA)'); + } + + if (!isCOSEAlg(alg)) { + throw new Error(`Public key had invalid alg ${alg} (RSA)`); } if (!n) { - throw new Error('RSA public key was missing n'); + throw new Error('Public key was missing n (RSA)'); } if (!e) { - throw new Error('RSA public key was missing e'); + throw new Error('Public key was missing e (RSA)'); } const jwk: JsonWebKey = { kty: 'RSA', alg: '', - n: isoBase64URL.fromBuffer(n as Uint8Array), - e: isoBase64URL.fromBuffer(e as Uint8Array), + n: isoBase64URL.fromBuffer(n), + e: isoBase64URL.fromBuffer(e), ext: false, }; const keyAlgorithm = { name: 'RSASSA-PKCS1-v1_5', // This is actually the digest hash that'll get used by `.verify()` - hash: { name: mapCoseAlgToWebCryptoAlg(alg as number) }, + hash: { name: mapCoseAlgToWebCryptoAlg(alg) }, }; if (hashAlgorithm) { diff --git a/packages/server/src/helpers/verifySignature.ts b/packages/server/src/helpers/verifySignature.ts index e7b4b28..4bc38e2 100644 --- a/packages/server/src/helpers/verifySignature.ts +++ b/packages/server/src/helpers/verifySignature.ts @@ -5,7 +5,7 @@ import { Certificate } from '@peculiar/asn1-x509'; import { ECParameters, id_ecPublicKey, id_secp256r1 } from '@peculiar/asn1-ecc'; import { RSAPublicKey } from '@peculiar/asn1-rsa'; -import { COSECRV, COSEKEYS, COSEKTY, COSEPublicKey } from './convertCOSEtoPKCS'; +import { COSEALG, COSECRV, COSEKEYS, COSEKTY, COSEPublicKey, COSEPublicKeyEC2, COSEPublicKeyRSA, isCOSEAlg, isCOSEPublicKeyOKP } from './convertCOSEtoPKCS'; import { isoCrypto } from './iso'; import { decodeCredentialPublicKey } from './decodeCredentialPublicKey'; @@ -43,7 +43,7 @@ export async function verifySignature( let subtlePublicKey: CryptoKey; let kty: COSEKTY; - let alg: number; + let alg: COSEALG; if (_isCredPubKeyOpts) { const { publicKey } = opts; @@ -61,21 +61,25 @@ export async function verifySignature( throw new Error('Public key was missing alg'); } + if (!isCOSEAlg(_alg)) { + throw new Error(`Public key contained invalid alg ${_alg}`); + } + // Verify Ed25519 slightly differently - if (_kty === COSEKTY.OKP) { + if (isCOSEPublicKeyOKP(cosePublicKey)) { const x = cosePublicKey.get(COSEKEYS.x); if (!x) { throw new Error('Public key was missing x (OKP)'); } - return ed25519Verify(signature, data, (x as Uint8Array)); + return ed25519Verify(signature, data, x); } // Assume we're handling COSEKTY.EC2 or COSEKTY.RSA key from here on - subtlePublicKey = await isoCrypto.importKey(cosePublicKey); + subtlePublicKey = await isoCrypto.importKey(cosePublicKey as COSEPublicKeyEC2 | COSEPublicKeyRSA); kty = _kty as COSEKTY; - alg = _alg as number; + alg = _alg; } else if (_isLeafcertOpts) { /** * Time to extract the public key from an X.509 leaf certificate @@ -130,7 +134,7 @@ export async function verifySignature( throw new Error('TODO: Figure out how to handle public keys in "compressed form"'); } - const coseEC2PubKey: COSEPublicKey = new Map(); + const coseEC2PubKey: COSEPublicKeyEC2 = new Map(); coseEC2PubKey.set(COSEKEYS.kty, COSEKTY.EC2); coseEC2PubKey.set(COSEKEYS.crv, crv); coseEC2PubKey.set(COSEKEYS.x, x); @@ -145,7 +149,7 @@ export async function verifySignature( kty = COSEKTY.RSA; const rsaPublicKey = AsnParser.parse(subjectPublicKeyInfo.subjectPublicKey, RSAPublicKey); - let _alg = -999; + let _alg: COSEALG; if (signatureAlgorithm === '1.2.840.113549.1.1.11') { _alg = -257; // RS256 } else if (signatureAlgorithm === '1.2.840.113549.1.1.12') { @@ -158,7 +162,7 @@ export async function verifySignature( ); } - const coseRSAPubKey: COSEPublicKey = new Map(); + const coseRSAPubKey: COSEPublicKeyRSA = new Map(); coseRSAPubKey.set(COSEKEYS.kty, COSEKTY.RSA); coseRSAPubKey.set(COSEKEYS.alg, _alg); coseRSAPubKey.set(COSEKEYS.n, new Uint8Array(rsaPublicKey.modulus)); diff --git a/packages/server/src/metadata/verifyAttestationWithMetadata.ts b/packages/server/src/metadata/verifyAttestationWithMetadata.ts index aa45809..80a0142 100644 --- a/packages/server/src/metadata/verifyAttestationWithMetadata.ts +++ b/packages/server/src/metadata/verifyAttestationWithMetadata.ts @@ -4,7 +4,7 @@ import type { MetadataStatement, AlgSign } from '../metadata/mdsTypes'; import { convertCertBufferToPEM } from '../helpers/convertCertBufferToPEM'; import { validateCertificatePath } from '../helpers/validateCertificatePath'; import { decodeCredentialPublicKey } from '../helpers/decodeCredentialPublicKey'; -import { COSEKEYS, COSEKTY } from '../helpers/convertCOSEtoPKCS'; +import { COSEALG, COSECRV, COSEKEYS, COSEKTY, COSEPublicKeyEC2, isCOSEPublicKeyEC2 } from '../helpers/convertCOSEtoPKCS'; /** * Match properties of the authenticator's attestation statement against expected values as @@ -41,14 +41,28 @@ export async function verifyAttestationWithMetadata({ // Extract the public key's COSE info for comparison const decodedPublicKey = decodeCredentialPublicKey(credentialPublicKey); + + const kty = decodedPublicKey.get(COSEKEYS.kty); + const alg = decodedPublicKey.get(COSEKEYS.alg); + + if (!kty) { + throw new Error('Credential public key was missing kty'); + } + + if (!alg) { + throw new Error('Credential public key was missing alg'); + } + + if (!kty) { + throw new Error('Credential public key was missing kty'); + } + // Assume everything is a number because these values should be - const publicKeyCOSEInfo: COSEInfo = { - kty: decodedPublicKey.get(COSEKEYS.kty) as number, - alg: decodedPublicKey.get(COSEKEYS.alg) as number, - crv: decodedPublicKey.get(COSEKEYS.crv) as number, - }; - if (!publicKeyCOSEInfo.crv) { - delete publicKeyCOSEInfo.crv; + const publicKeyCOSEInfo: COSEInfo = { kty, alg }; + + if (isCOSEPublicKeyEC2(decodedPublicKey)) { + const crv = decodedPublicKey.get(COSEKEYS.crv); + publicKeyCOSEInfo.crv = crv; } /** @@ -148,9 +162,9 @@ export async function verifyAttestationWithMetadata({ } type COSEInfo = { - kty: number; - alg: number; - crv?: number; + kty: COSEKTY; + alg: COSEALG; + crv?: COSECRV; }; /** diff --git a/packages/server/src/registration/verifications/tpm/verifyAttestationTPM.ts b/packages/server/src/registration/verifications/tpm/verifyAttestationTPM.ts index 45ccca8..7a147e3 100644 --- a/packages/server/src/registration/verifications/tpm/verifyAttestationTPM.ts +++ b/packages/server/src/registration/verifications/tpm/verifyAttestationTPM.ts @@ -11,7 +11,7 @@ import { import type { AttestationFormatVerifierOpts } from '../../verifyRegistrationResponse'; import { decodeCredentialPublicKey } from '../../../helpers/decodeCredentialPublicKey'; -import { COSEKEYS, COSEALGHASH } from '../../../helpers/convertCOSEtoPKCS'; +import { COSEKEYS, coseAlgSHAHashMap, isCOSEAlg, isCOSEPublicKeyEC2, isCOSEPublicKeyRSA } from '../../../helpers/convertCOSEtoPKCS'; import { toHash } from '../../../helpers/toHash'; import { convertCertBufferToPEM } from '../../../helpers/convertCertBufferToPEM'; import { validateCertificatePath } from '../../../helpers/validateCertificatePath'; @@ -50,6 +50,10 @@ export async function verifyAttestationTPM(options: AttestationFormatVerifierOpt throw new Error(`Attestation statement did not contain alg (TPM)`); } + if (!isCOSEAlg(alg)) { + throw new Error(`Attestation statement contained invalid alg ${alg} (TPM)`); + } + if (!x5c) { throw new Error('No attestation certificate provided in attestation statement (TPM)'); } @@ -70,6 +74,12 @@ export async function verifyAttestationTPM(options: AttestationFormatVerifierOpt const cosePublicKey = decodeCredentialPublicKey(credentialPublicKey); if (pubType === 'TPM_ALG_RSA') { + if (!isCOSEPublicKeyRSA(cosePublicKey)) { + throw new Error( + `Credential public key with kty ${cosePublicKey.get(COSEKEYS.kty)} did not match ${pubType}`, + ); + } + const n = cosePublicKey.get(COSEKEYS.n); const e = cosePublicKey.get(COSEKEYS.e); @@ -80,7 +90,7 @@ export async function verifyAttestationTPM(options: AttestationFormatVerifierOpt throw new Error('COSE public key missing e (TPM|RSA)'); } - if (!isoUint8Array.areEqual(unique, (n as Uint8Array))) { + if (!isoUint8Array.areEqual(unique, n)) { throw new Error('PubArea unique is not same as credentialPublicKey (TPM|RSA)'); } @@ -99,6 +109,12 @@ export async function verifyAttestationTPM(options: AttestationFormatVerifierOpt throw new Error(`Unexpected public key exp ${eSum}, expected ${pubAreaExponent} (TPM|RSA)`); } } else if (pubType === 'TPM_ALG_ECC') { + if (!isCOSEPublicKeyEC2(cosePublicKey)) { + throw new Error( + `Credential public key with kty ${cosePublicKey.get(COSEKEYS.kty)} did not match ${pubType}`, + ); + } + const crv = cosePublicKey.get(COSEKEYS.crv); const x = cosePublicKey.get(COSEKEYS.x); const y = cosePublicKey.get(COSEKEYS.y); @@ -113,7 +129,7 @@ export async function verifyAttestationTPM(options: AttestationFormatVerifierOpt throw new Error('COSE public key missing y (TPM|ECC)'); } - if (!isoUint8Array.areEqual(unique, isoUint8Array.concat([x as Uint8Array, y as Uint8Array]))) { + if (!isoUint8Array.areEqual(unique, isoUint8Array.concat([x, y]))) { throw new Error('PubArea unique is not same as public key x and y (TPM|ECC)'); } @@ -158,7 +174,7 @@ export async function verifyAttestationTPM(options: AttestationFormatVerifierOpt const attToBeSigned = isoUint8Array.concat([authData, clientDataHash]); // Hash attToBeSigned using the algorithm specified in attStmt.alg to create attToBeSignedHash - const hashAlg: string = COSEALGHASH[alg as number]; + const hashAlg: string = coseAlgSHAHashMap[alg]; const attToBeSignedHash = await toHash(attToBeSigned, hashAlg); // Check that certInfo.extraData is equals to attToBeSignedHash. diff --git a/packages/server/src/registration/verifications/verifyAttestationAndroidKey.ts b/packages/server/src/registration/verifications/verifyAttestationAndroidKey.ts index af2daf9..1287ac0 100644 --- a/packages/server/src/registration/verifications/verifyAttestationAndroidKey.ts +++ b/packages/server/src/registration/verifications/verifyAttestationAndroidKey.ts @@ -7,7 +7,7 @@ import type { AttestationFormatVerifierOpts } from '../verifyRegistrationRespons import { convertCertBufferToPEM } from '../../helpers/convertCertBufferToPEM'; import { validateCertificatePath } from '../../helpers/validateCertificatePath'; import { verifySignature } from '../../helpers/verifySignature'; -import { COSEALGHASH, convertCOSEtoPKCS } from '../../helpers/convertCOSEtoPKCS'; +import { coseAlgSHAHashMap, convertCOSEtoPKCS, isCOSEAlg } from '../../helpers/convertCOSEtoPKCS'; import { isoUint8Array } from '../../helpers/iso'; import { MetadataService } from '../../services/metadataService'; import { verifyAttestationWithMetadata } from '../../metadata/verifyAttestationWithMetadata'; @@ -36,6 +36,10 @@ export async function verifyAttestationAndroidKey( throw new Error(`Attestation statement did not contain alg (AndroidKey)`); } + if (!isCOSEAlg(alg)) { + throw new Error(`Attestation statement contained invalid alg ${alg} (AndroidKey)`); + } + // Check that credentialPublicKey matches the public key in the attestation certificate // Find the public cert in the certificate as PKCS const parsedCert = AsnParser.parse(x5c[0], Certificate); @@ -102,7 +106,7 @@ export async function verifyAttestationAndroidKey( } const signatureBase = isoUint8Array.concat([authData, clientDataHash]); - const hashAlg = COSEALGHASH[alg as number]; + const hashAlg = coseAlgSHAHashMap[alg]; return verifySignature({ signature: sig, diff --git a/packages/server/src/registration/verifications/verifyAttestationPacked.ts b/packages/server/src/registration/verifications/verifyAttestationPacked.ts index 5f00847..bf3fbc7 100644 --- a/packages/server/src/registration/verifications/verifyAttestationPacked.ts +++ b/packages/server/src/registration/verifications/verifyAttestationPacked.ts @@ -1,6 +1,6 @@ import type { AttestationFormatVerifierOpts } from '../verifyRegistrationResponse'; -import { COSEALGHASH } from '../../helpers/convertCOSEtoPKCS'; +import { coseAlgSHAHashMap, isCOSEAlg } from '../../helpers/convertCOSEtoPKCS'; import { convertCertBufferToPEM } from '../../helpers/convertCertBufferToPEM'; import { validateCertificatePath } from '../../helpers/validateCertificatePath'; import { getCertificateInfo } from '../../helpers/getCertificateInfo'; @@ -26,8 +26,12 @@ export async function verifyAttestationPacked( throw new Error('No attestation signature provided in attestation statement (Packed)'); } - if (typeof alg !== 'number') { - throw new Error(`Attestation Statement alg "${alg}" is not a number (Packed)`); + if (!alg) { + throw new Error('Attestation statement did not contain alg (Packed)'); + } + + if (!isCOSEAlg(alg)) { + throw new Error(`Attestation statement contained invalid alg ${alg} (Packed)`); } const signatureBase = isoUint8Array.concat([authData, clientDataHash]); @@ -114,7 +118,7 @@ export async function verifyAttestationPacked( leafCert: x5c[0], }); } else { - const hashAlg: string = COSEALGHASH[alg as number]; + const hashAlg: string = coseAlgSHAHashMap[alg]; verified = await verifySignature({ signature: sig, |