diff options
author | Matthew Miller <matthew@millerti.me> | 2022-11-11 19:47:25 -0800 |
---|---|---|
committer | Matthew Miller <matthew@millerti.me> | 2022-11-11 19:47:25 -0800 |
commit | 790dd59591dd6cba154ff34bbb6496d91f3be14a (patch) | |
tree | b4195ba4713cbc25f051ab65487d3cafa783dc71 | |
parent | a42cd6ab88bc3081e86a10d920c5a3949ee48f95 (diff) |
Refactor RP ID hash matching
3 files changed, 51 insertions, 26 deletions
diff --git a/packages/server/src/authentication/verifyAuthenticationResponse.ts b/packages/server/src/authentication/verifyAuthenticationResponse.ts index 100cb4c..4d58078 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 { matchExpectedRPID } from '../helpers/matchExpectedRPID'; import * as uint8Array from '../helpers/uint8Array'; import * as base64url from '../helpers/base64url'; @@ -148,23 +149,15 @@ export async function verifyAuthenticationResponse( const { rpIdHash, flags, counter, extensionsData } = parsedAuthData; // Make sure the response's RP ID is ours + let expectedRPIDs: string[] = []; if (typeof expectedRPID === 'string') { - const expectedRPIDHash = toHash(uint8Array.fromASCIIString(expectedRPID)); - if (!uint8Array.areEqual(rpIdHash, expectedRPIDHash)) { - throw new Error(`Unexpected RP ID hash`); - } + expectedRPIDs = [expectedRPID]; } else { - // Go through each expected RP ID and try to find one that matches - const foundMatch = expectedRPID.some(expected => { - const expectedRPIDHash = toHash(uint8Array.fromASCIIString(expected)); - return uint8Array.areEqual(rpIdHash, expectedRPIDHash); - }); - - if (!foundMatch) { - throw new Error(`Unexpected RP ID hash`); - } + expectedRPIDs = expectedRPID; } + await matchExpectedRPID(rpIdHash, expectedRPIDs); + if (advancedFIDOConfig !== undefined) { const { userVerification: fidoUserVerification } = advancedFIDOConfig; diff --git a/packages/server/src/helpers/matchExpectedRPID.ts b/packages/server/src/helpers/matchExpectedRPID.ts new file mode 100644 index 0000000..ef5b4a9 --- /dev/null +++ b/packages/server/src/helpers/matchExpectedRPID.ts @@ -0,0 +1,39 @@ +import { toHash } from './toHash'; +import * as uint8Array from './uint8Array'; + +/** + * Go through each expected RP ID and try to find one that matches. Raises an Error if no + */ +export async function matchExpectedRPID(rpIDHash: Uint8Array, expectedRPIDs: string[]): Promise<void> { + try { + await Promise.any(expectedRPIDs.map((expected) => { + return new Promise((resolve, reject) => { + toHash(uint8Array.fromASCIIString(expected)).then((expectedRPIDHash) => { + if (uint8Array.areEqual(rpIDHash, expectedRPIDHash)) { + resolve(true); + } else { + reject(); + } + }) + }); + })); + } catch (err) { + const _err = err as Error; + + // This means no matches were found + if (_err.name === 'AggregateError') { + throw new UnexpectedRPIDHash(); + } + + // An unexpected error occurred + throw err; + } +} + +class UnexpectedRPIDHash extends Error { + constructor() { + const message = 'Unexpected RP ID hash'; + super(message); + this.name = 'UnexpectedRPIDHash'; + } +} diff --git a/packages/server/src/registration/verifyRegistrationResponse.ts b/packages/server/src/registration/verifyRegistrationResponse.ts index ae5609e..06253ad 100644 --- a/packages/server/src/registration/verifyRegistrationResponse.ts +++ b/packages/server/src/registration/verifyRegistrationResponse.ts @@ -17,6 +17,7 @@ import { decodeCredentialPublicKey } from '../helpers/decodeCredentialPublicKey' import { COSEKEYS } from '../helpers/convertCOSEtoPKCS'; import { convertAAGUIDToString } from '../helpers/convertAAGUIDToString'; import { parseBackupFlags } from '../helpers/parseBackupFlags'; +import { matchExpectedRPID } from '../helpers/matchExpectedRPID'; import * as uint8Array from '../helpers/uint8Array'; import * as base64url from '../helpers/base64url'; import { SettingsService } from '../services/settingsService'; @@ -142,22 +143,14 @@ export async function verifyRegistrationResponse( // Make sure the response's RP ID is ours if (expectedRPID) { + let expectedRPIDs: string[] = []; if (typeof expectedRPID === 'string') { - const expectedRPIDHash = toHash(uint8Array.fromASCIIString(expectedRPID)); - if (!uint8Array.areEqual(rpIdHash, expectedRPIDHash)) { - throw new Error(`Unexpected RP ID hash`); - } + expectedRPIDs = [expectedRPID]; } else { - // Go through each expected RP ID and try to find one that matches - const foundMatch = expectedRPID.some(expected => { - const expectedRPIDHash = toHash(uint8Array.fromASCIIString(expected)); - return uint8Array.areEqual(rpIdHash, expectedRPIDHash); - }); - - if (!foundMatch) { - throw new Error(`Unexpected RP ID hash`); - } + expectedRPIDs = expectedRPID; } + + await matchExpectedRPID(rpIdHash, expectedRPIDs); } // Make sure someone was physically present |