summaryrefslogtreecommitdiffhomepage
diff options
context:
space:
mode:
authorMatthew Miller <matthew@millerti.me>2022-11-11 19:47:25 -0800
committerMatthew Miller <matthew@millerti.me>2022-11-11 19:47:25 -0800
commit790dd59591dd6cba154ff34bbb6496d91f3be14a (patch)
treeb4195ba4713cbc25f051ab65487d3cafa783dc71
parenta42cd6ab88bc3081e86a10d920c5a3949ee48f95 (diff)
Refactor RP ID hash matching
-rw-r--r--packages/server/src/authentication/verifyAuthenticationResponse.ts19
-rw-r--r--packages/server/src/helpers/matchExpectedRPID.ts39
-rw-r--r--packages/server/src/registration/verifyRegistrationResponse.ts19
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