diff options
Diffstat (limited to 'packages/server/src')
11 files changed, 44 insertions, 16 deletions
diff --git a/packages/server/src/authentication/generateAuthenticationOptions.ts b/packages/server/src/authentication/generateAuthenticationOptions.ts index 1eab513..4c841d9 100644 --- a/packages/server/src/authentication/generateAuthenticationOptions.ts +++ b/packages/server/src/authentication/generateAuthenticationOptions.ts @@ -5,6 +5,7 @@ import type { UserVerificationRequirement, } from '@simplewebauthn/typescript-types'; import base64url from '../helpers/base64url' +import uint8Array from '../helpers/uint8Array'; import { generateChallenge } from '../helpers/generateChallenge'; @@ -42,6 +43,14 @@ export function generateAuthenticationOptions( rpID, } = options; + /** + * Preserve ability to specify `string` values for challenges + */ + let _challenge = challenge; + if (typeof _challenge === 'string') { + _challenge = uint8Array.fromString(_challenge); + } + return { challenge: base64url.fromBuffer(_challenge), allowCredentials: allowCredentials?.map(cred => ({ diff --git a/packages/server/src/authentication/verifyAuthenticationResponse.ts b/packages/server/src/authentication/verifyAuthenticationResponse.ts index 7ae002e..9200758 100644 --- a/packages/server/src/authentication/verifyAuthenticationResponse.ts +++ b/packages/server/src/authentication/verifyAuthenticationResponse.ts @@ -12,6 +12,7 @@ import { parseAuthenticatorData } from '../helpers/parseAuthenticatorData'; import { isBase64URLString } from '../helpers/isBase64URLString'; import { parseBackupFlags } from '../helpers/parseBackupFlags'; import { AuthenticationExtensionsAuthenticatorOutputs } from '../helpers/decodeAuthenticatorExtensions'; +import uint8Array from '../helpers/uint8Array'; import base64url from '../helpers/base64url'; export type VerifyAuthenticationResponseOpts = { @@ -149,14 +150,14 @@ export async function verifyAuthenticationResponse( // Make sure the response's RP ID is ours if (typeof expectedRPID === 'string') { const expectedRPIDHash = toHash(Buffer.from(expectedRPID, 'ascii')); - if (!rpIdHash.equals(expectedRPIDHash)) { + if (!uint8Array.areEqual(rpIdHash, expectedRPIDHash)) { throw new Error(`Unexpected RP ID hash`); } } else { // Go through each expected RP ID and try to find one that matches const foundMatch = expectedRPID.some(expected => { const expectedRPIDHash = toHash(Buffer.from(expected, 'ascii')); - return rpIdHash.equals(expectedRPIDHash); + return uint8Array.areEqual(rpIdHash, expectedRPIDHash); }); if (!foundMatch) { diff --git a/packages/server/src/helpers/convertAAGUIDToString.ts b/packages/server/src/helpers/convertAAGUIDToString.ts index 3dc837c..80dc63c 100644 --- a/packages/server/src/helpers/convertAAGUIDToString.ts +++ b/packages/server/src/helpers/convertAAGUIDToString.ts @@ -1,9 +1,11 @@ +import uint8Array from './uint8Array'; + /** * Convert the aaguid buffer in authData into a UUID string */ export function convertAAGUIDToString(aaguid: Uint8Array): string { // Raw Hex: adce000235bcc60a648b0b25f1f05503 - const hex = aaguid.toString('hex'); + const hex = uint8Array.toHex(aaguid); const segments: string[] = [ hex.slice(0, 8), // 8 diff --git a/packages/server/src/helpers/convertCOSEtoPKCS.ts b/packages/server/src/helpers/convertCOSEtoPKCS.ts index 158fb36..0ee6a97 100644 --- a/packages/server/src/helpers/convertCOSEtoPKCS.ts +++ b/packages/server/src/helpers/convertCOSEtoPKCS.ts @@ -1,5 +1,6 @@ import { COSEAlgorithmIdentifier } from '@simplewebauthn/typescript-types'; import { decodeCborFirst } from './decodeCbor'; +import uint8Array from './uint8array'; /** * Takes COSE-encoded public key and converts it to PKCS key @@ -7,19 +8,19 @@ import { decodeCborFirst } from './decodeCbor'; export function convertCOSEtoPKCS(cosePublicKey: Uint8Array): Uint8Array { const struct: COSEPublicKey = decodeCborFirst(cosePublicKey); - const tag = Buffer.from([0x04]); const x = struct.get(COSEKEYS.x); const y = struct.get(COSEKEYS.y); + const tag = Uint8Array.from([0x04]); if (!x) { throw new Error('COSE public key was missing x'); } if (y) { - return Buffer.concat([tag, x as Buffer, y as Buffer]); + return uint8Array.concat([tag, x as Uint8Array, y as Uint8Array]); } - return Buffer.concat([tag, x as Buffer]); + return uint8Array.concat([tag, x as Uint8Array]); } export type COSEPublicKey = Map<COSEAlgorithmIdentifier, number | Buffer>; diff --git a/packages/server/src/helpers/convertCertBufferToPEM.ts b/packages/server/src/helpers/convertCertBufferToPEM.ts index 5339282..faf9001 100644 --- a/packages/server/src/helpers/convertCertBufferToPEM.ts +++ b/packages/server/src/helpers/convertCertBufferToPEM.ts @@ -1,5 +1,6 @@ import type { Base64URLString } from '@simplewebauthn/typescript-types'; +import uint8Array from './uint8Array'; import base64url from './base64url'; /** @@ -14,7 +15,7 @@ export function convertCertBufferToPEM(certBuffer: Uint8Array | Base64URLString) if (typeof certBuffer === 'string') { b64cert = base64url.toBase64(certBuffer); } else { - b64cert = certBuffer.toString('base64'); + b64cert = uint8Array.toBase64(certBuffer); } let PEMKey = ''; diff --git a/packages/server/src/registration/generateRegistrationOptions.ts b/packages/server/src/registration/generateRegistrationOptions.ts index 12138b0..6273246 100644 --- a/packages/server/src/registration/generateRegistrationOptions.ts +++ b/packages/server/src/registration/generateRegistrationOptions.ts @@ -10,6 +10,7 @@ import type { import { generateChallenge } from '../helpers/generateChallenge'; import base64url from '../helpers/base64url'; +import uint8Array from '../helpers/uint8Array'; export type GenerateRegistrationOptionsOpts = { rpName: string; @@ -151,6 +152,14 @@ export function generateRegistrationOptions( authenticatorSelection.requireResidentKey = authenticatorSelection.residentKey === 'required'; } + /** + * Preserve ability to specify `string` values for challenges + */ + let _challenge = challenge; + if (typeof _challenge === 'string') { + _challenge = uint8Array.fromString(_challenge); + } + return { challenge: base64url.fromBuffer(_challenge), rp: { diff --git a/packages/server/src/registration/verifications/verifyAttestationAndroidKey.ts b/packages/server/src/registration/verifications/verifyAttestationAndroidKey.ts index 55a0612..6c89df8 100644 --- a/packages/server/src/registration/verifications/verifyAttestationAndroidKey.ts +++ b/packages/server/src/registration/verifications/verifyAttestationAndroidKey.ts @@ -8,6 +8,7 @@ import { convertCertBufferToPEM } from '../../helpers/convertCertBufferToPEM'; import { validateCertificatePath } from '../../helpers/validateCertificatePath'; import { verifySignature } from '../../helpers/verifySignature'; import { COSEALGHASH, convertCOSEtoPKCS } from '../../helpers/convertCOSEtoPKCS'; +import uint8Array from '../../helpers/uint8Array'; import { MetadataService } from '../../services/metadataService'; import { verifyAttestationWithMetadata } from '../../metadata/verifyAttestationWithMetadata'; @@ -36,14 +37,14 @@ export async function verifyAttestationAndroidKey( // 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); - const parsedCertPubKey = Buffer.from( + const parsedCertPubKey = new Uint8Array( parsedCert.tbsCertificate.subjectPublicKeyInfo.subjectPublicKey, ); // Convert the credentialPublicKey to PKCS const credPubKeyPKCS = convertCOSEtoPKCS(credentialPublicKey); - if (!credPubKeyPKCS.equals(parsedCertPubKey)) { + if (!uint8Array.areEqual(credPubKeyPKCS, parsedCertPubKey)) { throw new Error('Credential public key does not equal leaf cert public key (AndroidKey)'); } diff --git a/packages/server/src/registration/verifications/verifyAttestationAndroidSafetyNet.ts b/packages/server/src/registration/verifications/verifyAttestationAndroidSafetyNet.ts index 4c1e685..65e5c00 100644 --- a/packages/server/src/registration/verifications/verifyAttestationAndroidSafetyNet.ts +++ b/packages/server/src/registration/verifications/verifyAttestationAndroidSafetyNet.ts @@ -7,6 +7,7 @@ import { verifySignature } from '../../helpers/verifySignature'; import { getCertificateInfo } from '../../helpers/getCertificateInfo'; import { validateCertificatePath } from '../../helpers/validateCertificatePath'; import { convertCertBufferToPEM } from '../../helpers/convertCertBufferToPEM'; +import uint8Array from '../../helpers/uint8Array'; import { MetadataService } from '../../services/metadataService'; import { verifyAttestationWithMetadata } from '../../metadata/verifyAttestationWithMetadata'; @@ -65,7 +66,7 @@ export async function verifyAttestationAndroidSafetyNet( const nonceBase = Buffer.concat([authData, clientDataHash]); const nonceBuffer = toHash(nonceBase); - const expectedNonce = nonceBuffer.toString('base64'); + const expectedNonce = uint8Array.toBase64(nonceBuffer); if (nonce !== expectedNonce) { throw new Error('Could not verify payload nonce (SafetyNet)'); diff --git a/packages/server/src/registration/verifications/verifyAttestationApple.ts b/packages/server/src/registration/verifications/verifyAttestationApple.ts index e0c7890..fc12f9d 100644 --- a/packages/server/src/registration/verifications/verifyAttestationApple.ts +++ b/packages/server/src/registration/verifications/verifyAttestationApple.ts @@ -7,6 +7,7 @@ import { validateCertificatePath } from '../../helpers/validateCertificatePath'; import { convertCertBufferToPEM } from '../../helpers/convertCertBufferToPEM'; import { toHash } from '../../helpers/toHash'; import { convertCOSEtoPKCS } from '../../helpers/convertCOSEtoPKCS'; +import uint8Array from '../../helpers/uint8Array'; export async function verifyAttestationApple( options: AttestationFormatVerifierOpts, @@ -55,7 +56,7 @@ export async function verifyAttestationApple( */ const extNonce = Buffer.from(extCertNonce.extnValue.buffer).slice(6); - if (!nonce.equals(extNonce)) { + if (!uint8Array.areEqual(nonce, extNonce)) { throw new Error(`credCert nonce was not expected value (Apple)`); } @@ -65,7 +66,7 @@ export async function verifyAttestationApple( const credPubKeyPKCS = convertCOSEtoPKCS(credentialPublicKey); const credCertSubjectPublicKey = Buffer.from(subjectPublicKeyInfo.subjectPublicKey); - if (!credPubKeyPKCS.equals(credCertSubjectPublicKey)) { + if (!uint8Array.areEqual(credPubKeyPKCS, credCertSubjectPublicKey)) { throw new Error('Credential public key does not equal credCert public key (Apple)'); } diff --git a/packages/server/src/registration/verifications/verifyAttestationFIDOU2F.ts b/packages/server/src/registration/verifications/verifyAttestationFIDOU2F.ts index 3c79b9e..e73c22f 100644 --- a/packages/server/src/registration/verifications/verifyAttestationFIDOU2F.ts +++ b/packages/server/src/registration/verifications/verifyAttestationFIDOU2F.ts @@ -4,6 +4,7 @@ import { convertCOSEtoPKCS } from '../../helpers/convertCOSEtoPKCS'; import { convertCertBufferToPEM } from '../../helpers/convertCertBufferToPEM'; import { validateCertificatePath } from '../../helpers/validateCertificatePath'; import { verifySignature } from '../../helpers/verifySignature'; +import uint8Array from '../../helpers/uint8Array'; /** * Verify an attestation response with fmt 'fido-u2f' @@ -17,7 +18,7 @@ export async function verifyAttestationFIDOU2F( rpIdHash, credentialID, credentialPublicKey, - aaguid = '', + aaguid, rootCertificates, } = options; @@ -43,7 +44,7 @@ export async function verifyAttestationFIDOU2F( } // FIDO spec says that aaguid _must_ equal 0x00 here to be legit - const aaguidToHex = Number.parseInt(aaguid.toString('hex'), 16); + const aaguidToHex = Number.parseInt(uint8Array.toHex(aaguid), 16); if (aaguidToHex !== 0x00) { throw new Error(`AAGUID "${aaguidToHex}" was not expected value`); } diff --git a/packages/server/src/registration/verifyRegistrationResponse.ts b/packages/server/src/registration/verifyRegistrationResponse.ts index efe7531..60140a4 100644 --- a/packages/server/src/registration/verifyRegistrationResponse.ts +++ b/packages/server/src/registration/verifyRegistrationResponse.ts @@ -18,6 +18,7 @@ import { decodeCredentialPublicKey } from '../helpers/decodeCredentialPublicKey' import { COSEKEYS } from '../helpers/convertCOSEtoPKCS'; import { convertAAGUIDToString } from '../helpers/convertAAGUIDToString'; import { parseBackupFlags } from '../helpers/parseBackupFlags'; +import uint8Array from '../helpers/uint8Array'; import { SettingsService } from '../services/settingsService'; import { supportedCOSEAlgorithmIdentifiers } from './generateRegistrationOptions'; @@ -141,14 +142,14 @@ export async function verifyRegistrationResponse( if (expectedRPID) { if (typeof expectedRPID === 'string') { const expectedRPIDHash = toHash(Buffer.from(expectedRPID, 'ascii')); - if (!rpIdHash.equals(expectedRPIDHash)) { + if (!uint8Array.areEqual(rpIdHash, expectedRPIDHash)) { throw new Error(`Unexpected RP ID hash`); } } else { // Go through each expected RP ID and try to find one that matches const foundMatch = expectedRPID.some(expected => { const expectedRPIDHash = toHash(Buffer.from(expected, 'ascii')); - return rpIdHash.equals(expectedRPIDHash); + return uint8Array.areEqual(rpIdHash, expectedRPIDHash); }); if (!foundMatch) { |