Skip to content

Commit

Permalink
feat: change how the authenticator parses credential ids to support b64
Browse files Browse the repository at this point in the history
  • Loading branch information
coroiu committed Nov 14, 2024
1 parent 3d1e744 commit 8906495
Show file tree
Hide file tree
Showing 4 changed files with 67 additions and 24 deletions.
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { parseCredentialId } from "./credential-id-utils";
import { compareCredentialIds, parseCredentialId } from "./credential-id-utils";

describe("credential-id-utils", () => {
describe("parseCredentialId", () => {
Expand Down Expand Up @@ -36,4 +36,33 @@ describe("credential-id-utils", () => {
expect(result).toBeUndefined();
});
});

describe("compareCredentialIds", () => {
it("returns true when the two credential IDs are equal", () => {
const a = new Uint8Array([0x01, 0x02, 0x03]);
const b = new Uint8Array([0x01, 0x02, 0x03]);

const result = compareCredentialIds(a, b);

expect(result).toBe(true);
});

it("returns false when the two credential IDs are not equal", () => {
const a = new Uint8Array([0x01, 0x02, 0x03]);
const b = new Uint8Array([0x01, 0x02, 0x04]);

const result = compareCredentialIds(a, b);

expect(result).toBe(false);
});

it("returns false when the two credential IDs have different lengths", () => {
const a = new Uint8Array([0x01, 0x02, 0x03]);
const b = new Uint8Array([0x01, 0x02, 0x03, 0x04]);

const result = compareCredentialIds(a, b);

expect(result).toBe(false);
});
});
});
17 changes: 17 additions & 0 deletions libs/common/src/platform/services/fido2/credential-id-utils.ts
Original file line number Diff line number Diff line change
Expand Up @@ -12,3 +12,20 @@ export function parseCredentialId(encodedCredentialId: string): Uint8Array {
return undefined;
}
}

/**
* Compares two credential IDs for equality.
*/
export function compareCredentialIds(a: Uint8Array, b: Uint8Array): boolean {
if (a.length !== b.length) {
return false;
}

for (let i = 0; i < a.length; i++) {
if (a[i] !== b[i]) {
return false;
}
}

return true;
}
Original file line number Diff line number Diff line change
Expand Up @@ -26,9 +26,9 @@ import {
import { Utils } from "../../misc/utils";

import { CBOR } from "./cbor";
import { parseCredentialId } from "./credential-id-utils";
import { AAGUID, Fido2AuthenticatorService } from "./fido2-authenticator.service";
import { Fido2Utils } from "./fido2-utils";
import { guidToRawFormat } from "./guid-utils";

const RpId = "bitwarden.com";

Expand Down Expand Up @@ -139,7 +139,7 @@ describe("FidoAuthenticatorService", () => {
params = await createParams({
excludeCredentialDescriptorList: [
{
id: guidToRawFormat(excludedCipher.login.fido2Credentials[0].credentialId),
id: parseCredentialId(excludedCipher.login.fido2Credentials[0].credentialId),
type: "public-key",
},
],
Expand Down Expand Up @@ -482,7 +482,7 @@ describe("FidoAuthenticatorService", () => {
credentialId = Utils.newGuid();
params = await createParams({
allowCredentialDescriptorList: [
{ id: guidToRawFormat(credentialId), type: "public-key" },
{ id: parseCredentialId(credentialId), type: "public-key" },
],
rpId: RpId,
});
Expand Down Expand Up @@ -546,7 +546,7 @@ describe("FidoAuthenticatorService", () => {
let params: Fido2AuthenticatorGetAssertionParams;

beforeEach(async () => {
credentialIds = [Utils.newGuid(), Utils.newGuid()];
credentialIds = [Utils.newGuid(), "b64.Lb5SVTumSV6gYJpeWh3laA"];
ciphers = [
await createCipherView(
{ type: CipherType.Login },
Expand All @@ -559,7 +559,7 @@ describe("FidoAuthenticatorService", () => {
];
params = await createParams({
allowCredentialDescriptorList: credentialIds.map((credentialId) => ({
id: guidToRawFormat(credentialId),
id: parseCredentialId(credentialId),
type: "public-key",
})),
rpId: RpId,
Expand Down Expand Up @@ -667,7 +667,7 @@ describe("FidoAuthenticatorService", () => {
selectedCredentialId = credentialIds[0];
params = await createParams({
allowCredentialDescriptorList: credentialIds.map((credentialId) => ({
id: guidToRawFormat(credentialId),
id: parseCredentialId(credentialId),
type: "public-key",
})),
rpId: RpId,
Expand Down Expand Up @@ -723,7 +723,7 @@ describe("FidoAuthenticatorService", () => {
const flags = encAuthData.slice(32, 33);
const counter = encAuthData.slice(33, 37);

expect(result.selectedCredential.id).toEqual(guidToRawFormat(selectedCredentialId));
expect(result.selectedCredential.id).toEqual(parseCredentialId(selectedCredentialId));
expect(result.selectedCredential.userHandle).toEqual(
Fido2Utils.stringToBuffer(fido2Credentials[0].userHandle),
);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -23,9 +23,10 @@ import { LogService } from "../../abstractions/log.service";
import { Utils } from "../../misc/utils";

import { CBOR } from "./cbor";
import { compareCredentialIds, parseCredentialId } from "./credential-id-utils";
import { p1363ToDer } from "./ecdsa-utils";
import { Fido2Utils } from "./fido2-utils";
import { guidToRawFormat, guidToStandardFormat } from "./guid-utils";
import { guidToStandardFormat } from "./guid-utils";

// AAGUID: d548826e-79b4-db40-a3d8-11116f7e8349
export const AAGUID = new Uint8Array([
Expand Down Expand Up @@ -178,7 +179,7 @@ export class Fido2AuthenticatorService implements Fido2AuthenticatorServiceAbstr

const authData = await generateAuthData({
rpId: params.rpEntity.id,
credentialId: guidToRawFormat(credentialId),
credentialId: parseCredentialId(credentialId),
counter: fido2Credential.counter,
userPresence: true,
userVerification: userVerified,
Expand All @@ -193,7 +194,7 @@ export class Fido2AuthenticatorService implements Fido2AuthenticatorServiceAbstr
);

return {
credentialId: guidToRawFormat(credentialId),
credentialId: parseCredentialId(credentialId),
attestationObject,
authData,
publicKey: pubKeyDer,
Expand Down Expand Up @@ -313,7 +314,7 @@ export class Fido2AuthenticatorService implements Fido2AuthenticatorServiceAbstr

const authenticatorData = await generateAuthData({
rpId: selectedFido2Credential.rpId,
credentialId: guidToRawFormat(selectedCredentialId),
credentialId: parseCredentialId(selectedCredentialId),
counter: selectedFido2Credential.counter,
userPresence: true,
userVerification: userVerified,
Expand All @@ -328,7 +329,7 @@ export class Fido2AuthenticatorService implements Fido2AuthenticatorServiceAbstr
return {
authenticatorData,
selectedCredential: {
id: guidToRawFormat(selectedCredentialId),
id: parseCredentialId(selectedCredentialId),
userHandle: Fido2Utils.stringToBuffer(selectedFido2Credential.userHandle),
},
signature,
Expand Down Expand Up @@ -412,16 +413,7 @@ export class Fido2AuthenticatorService implements Fido2AuthenticatorServiceAbstr
credentials: PublicKeyCredentialDescriptor[],
rpId: string,
): Promise<CipherView[]> {
const ids: string[] = [];

for (const credential of credentials) {
try {
ids.push(guidToStandardFormat(credential.id));
// eslint-disable-next-line no-empty
} catch {}
}

if (ids.length === 0) {
if (credentials.length === 0) {
return [];
}

Expand All @@ -432,7 +424,12 @@ export class Fido2AuthenticatorService implements Fido2AuthenticatorServiceAbstr
cipher.type === CipherType.Login &&
cipher.login.hasFido2Credentials &&
cipher.login.fido2Credentials[0].rpId === rpId &&
ids.includes(cipher.login.fido2Credentials[0].credentialId),
credentials.some((credential) =>
compareCredentialIds(
credential.id,
parseCredentialId(cipher.login.fido2Credentials[0].credentialId),
),
),
);
}

Expand Down

0 comments on commit 8906495

Please sign in to comment.