mirror of
https://github.com/bitwarden/browser
synced 2025-12-16 16:23:44 +00:00
[EC-598] feat: first tested rule in new authentitcator
This commit is contained in:
@@ -189,6 +189,15 @@ export class BrowserFido2UserInterfaceService implements Fido2UserInterfaceServi
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
async confirmDuplicateCredential(
|
||||||
|
existingCipherIds: string[],
|
||||||
|
newCredential: NewCredentialParams,
|
||||||
|
abortController?: AbortController
|
||||||
|
) {
|
||||||
|
// Not Implemented
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
private setAbortTimeout(abortController: AbortController) {
|
private setAbortTimeout(abortController: AbortController) {
|
||||||
return setTimeout(() => abortController.abort());
|
return setTimeout(() => abortController.abort());
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -18,18 +18,19 @@ export interface Fido2AuthenticatorMakeCredentialsParams {
|
|||||||
id?: string;
|
id?: string;
|
||||||
};
|
};
|
||||||
user: {
|
user: {
|
||||||
name: string;
|
|
||||||
displayName: string;
|
|
||||||
id: BufferSource;
|
id: BufferSource;
|
||||||
|
name?: string;
|
||||||
|
displayName?: string;
|
||||||
|
icon?: string;
|
||||||
};
|
};
|
||||||
pubKeyCredParams: {
|
pubKeyCredParams: {
|
||||||
alg: number;
|
alg: number;
|
||||||
// type: "public-key"; // not used
|
type: "public-key"; // not used
|
||||||
}[];
|
}[];
|
||||||
excludeList?: {
|
excludeList?: {
|
||||||
id: BufferSource;
|
id: BufferSource;
|
||||||
transports?: ("ble" | "internal" | "nfc" | "usb")[];
|
transports?: ("ble" | "internal" | "nfc" | "usb")[];
|
||||||
// type: "public-key"; // not used
|
type: "public-key"; // not used
|
||||||
}[];
|
}[];
|
||||||
extensions?: {
|
extensions?: {
|
||||||
appid?: string;
|
appid?: string;
|
||||||
|
|||||||
@@ -10,4 +10,9 @@ export abstract class Fido2UserInterfaceService {
|
|||||||
params: NewCredentialParams,
|
params: NewCredentialParams,
|
||||||
abortController?: AbortController
|
abortController?: AbortController
|
||||||
) => Promise<boolean>;
|
) => Promise<boolean>;
|
||||||
|
confirmDuplicateCredential: (
|
||||||
|
existingCipherIds: string[],
|
||||||
|
newCredential: NewCredentialParams,
|
||||||
|
abortController?: AbortController
|
||||||
|
) => Promise<boolean>;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,5 +1,118 @@
|
|||||||
|
import { TextEncoder } from "util";
|
||||||
|
|
||||||
|
import { mock, MockProxy } from "jest-mock-extended";
|
||||||
|
|
||||||
|
import { Utils } from "../../misc/utils";
|
||||||
|
import { CipherService } from "../../vault/abstractions/cipher.service";
|
||||||
|
import { CipherType } from "../../vault/enums/cipher-type";
|
||||||
|
import { CipherView } from "../../vault/models/view/cipher.view";
|
||||||
|
import { Fido2AuthenticatorMakeCredentialsParams } from "../abstractions/fido2-authenticator.service.abstraction";
|
||||||
|
import { Fido2UserInterfaceService } from "../abstractions/fido2-user-interface.service.abstraction";
|
||||||
|
import { Fido2Utils } from "../abstractions/fido2-utils";
|
||||||
|
import { Fido2KeyView } from "../models/view/fido2-key.view";
|
||||||
|
|
||||||
|
import { Fido2AuthenticatorService } from "./fido2-authenticator.service";
|
||||||
|
|
||||||
|
const RpId = "bitwarden.com";
|
||||||
|
|
||||||
describe("FidoAuthenticatorService", () => {
|
describe("FidoAuthenticatorService", () => {
|
||||||
|
let cipherService!: MockProxy<CipherService>;
|
||||||
|
let userInterface!: MockProxy<Fido2UserInterfaceService>;
|
||||||
|
let authenticator!: Fido2AuthenticatorService;
|
||||||
|
|
||||||
|
beforeEach(() => {
|
||||||
|
cipherService = mock<CipherService>();
|
||||||
|
userInterface = mock<Fido2UserInterfaceService>();
|
||||||
|
authenticator = new Fido2AuthenticatorService(cipherService, userInterface);
|
||||||
|
});
|
||||||
|
|
||||||
describe("authenticatorMakeCredential", () => {
|
describe("authenticatorMakeCredential", () => {
|
||||||
test.skip("To be implemented");
|
describe("when vault contains excluded credential", () => {
|
||||||
|
let excludedCipher: CipherView;
|
||||||
|
let params: Fido2AuthenticatorMakeCredentialsParams;
|
||||||
|
|
||||||
|
beforeEach(async () => {
|
||||||
|
excludedCipher = createCipherView();
|
||||||
|
params = await createCredentialParams({
|
||||||
|
excludeList: [{ id: Fido2Utils.stringToBuffer(excludedCipher.id), type: "public-key" }],
|
||||||
|
});
|
||||||
|
cipherService.getAllDecrypted.mockResolvedValue([excludedCipher]);
|
||||||
|
});
|
||||||
|
|
||||||
|
/** Spec: wait for user presence */
|
||||||
|
it("should wait for confirmation from user", async () => {
|
||||||
|
userInterface.confirmDuplicateCredential.mockResolvedValue(true);
|
||||||
|
|
||||||
|
await authenticator.makeCredential(params);
|
||||||
|
|
||||||
|
expect(userInterface.confirmDuplicateCredential).toHaveBeenCalled();
|
||||||
|
});
|
||||||
|
});
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
async function createCredentialParams(
|
||||||
|
params: Partial<Fido2AuthenticatorMakeCredentialsParams> = {}
|
||||||
|
): Promise<Fido2AuthenticatorMakeCredentialsParams> {
|
||||||
|
return {
|
||||||
|
clientDataHash: params.clientDataHash ?? (await createClientDataHash()),
|
||||||
|
rp: params.rp ?? {
|
||||||
|
name: "Bitwarden",
|
||||||
|
id: RpId,
|
||||||
|
},
|
||||||
|
user: params.user ?? {
|
||||||
|
id: randomBytes(64),
|
||||||
|
name: "jane.doe@bitwarden.com",
|
||||||
|
displayName: "Jane Doe",
|
||||||
|
icon: " data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAAAGXRFWHRTb2Z0d2FyZQBBZG9iZSBJbWFnZVJlYWR5ccllPAAAAOhJREFUeNpiFI+9E8DAwDAfiAUYSAMfgDiQBVmzlSYnUTqPXf/OANWzngVZ87pKKaIMCGp/BjeEhRjFMKAjx8bQFC2CIs9CpHNxAiYGCsEQM4Cfiwm3AY9f/yZogIcRN4ZahAFv/jAcu4E7xMNtecEYpAakFqsX8me9Yvj07R+G5jR3foaJqWJgOZAaZMAIzAv/kQV05NgZ5hdIMMiKQJIIyEYrDU6wrYkTXjBcefQTvwGwwCoJFGJIBdoMArN3fmToWf+O4SMW14EMeI8rJ8Jcgexn9BwJCoNEaNbEACCN+DSDsjNAgAEAri9Zii/uDMsAAAAASUVORK5CYII=",
|
||||||
|
},
|
||||||
|
pubKeyCredParams: params.pubKeyCredParams ?? [
|
||||||
|
{
|
||||||
|
alg: -1, // ES256
|
||||||
|
type: "public-key",
|
||||||
|
},
|
||||||
|
],
|
||||||
|
excludeList: params.excludeList ?? [
|
||||||
|
{
|
||||||
|
id: randomBytes(16),
|
||||||
|
transports: ["internal"],
|
||||||
|
type: "public-key",
|
||||||
|
},
|
||||||
|
],
|
||||||
|
extensions: params.extensions ?? {
|
||||||
|
appid: undefined,
|
||||||
|
appidExclude: undefined,
|
||||||
|
credProps: undefined,
|
||||||
|
uvm: false as boolean,
|
||||||
|
},
|
||||||
|
options: params.options ?? {
|
||||||
|
rk: false as boolean,
|
||||||
|
uv: false as boolean,
|
||||||
|
},
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
function createCipherView(id = Utils.newGuid()): CipherView {
|
||||||
|
const cipher = new CipherView();
|
||||||
|
cipher.id = id;
|
||||||
|
cipher.type = CipherType.Fido2Key;
|
||||||
|
cipher.fido2Key = new Fido2KeyView();
|
||||||
|
return cipher;
|
||||||
|
}
|
||||||
|
|
||||||
|
async function createClientDataHash() {
|
||||||
|
const encoder = new TextEncoder();
|
||||||
|
const clientData = encoder.encode(
|
||||||
|
JSON.stringify({
|
||||||
|
type: "webauthn.create",
|
||||||
|
challenge: Fido2Utils.bufferToString(randomBytes(16)),
|
||||||
|
origin: RpId,
|
||||||
|
crossOrigin: false,
|
||||||
|
})
|
||||||
|
);
|
||||||
|
return await crypto.subtle.digest({ name: "SHA-256" }, clientData);
|
||||||
|
}
|
||||||
|
|
||||||
|
function randomBytes(length: number) {
|
||||||
|
return new Uint8Array(Array.from({ length }, () => Math.floor(Math.random() * 255)));
|
||||||
|
}
|
||||||
|
|||||||
@@ -1,12 +1,28 @@
|
|||||||
|
import { CipherService } from "../../vault/services/cipher.service";
|
||||||
import {
|
import {
|
||||||
Fido2AuthenticatorMakeCredentialsParams,
|
Fido2AuthenticatorMakeCredentialsParams,
|
||||||
Fido2AuthenticatorService as Fido2AuthenticatorServiceAbstraction,
|
Fido2AuthenticatorService as Fido2AuthenticatorServiceAbstraction,
|
||||||
} from "../abstractions/fido2-authenticator.service.abstraction";
|
} from "../abstractions/fido2-authenticator.service.abstraction";
|
||||||
|
import { Fido2UserInterfaceService } from "../abstractions/fido2-user-interface.service.abstraction";
|
||||||
|
import { Fido2Utils } from "../abstractions/fido2-utils";
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Bitwarden implementation of the Authenticator API described by the FIDO Alliance
|
* Bitwarden implementation of the Authenticator API described by the FIDO Alliance
|
||||||
* https://fidoalliance.org/specs/fido-v2.0-ps-20190130/fido-client-to-authenticator-protocol-v2.0-ps-20190130.html
|
* https://fidoalliance.org/specs/fido-v2.0-ps-20190130/fido-client-to-authenticator-protocol-v2.0-ps-20190130.html
|
||||||
*/
|
*/
|
||||||
export class Fido2AuthenticatorService implements Fido2AuthenticatorServiceAbstraction {
|
export class Fido2AuthenticatorService implements Fido2AuthenticatorServiceAbstraction {
|
||||||
makeCredential: (params: Fido2AuthenticatorMakeCredentialsParams) => void;
|
constructor(
|
||||||
|
private cipherService: CipherService,
|
||||||
|
private userInterface: Fido2UserInterfaceService
|
||||||
|
) {}
|
||||||
|
|
||||||
|
async makeCredential(params: Fido2AuthenticatorMakeCredentialsParams): Promise<void> {
|
||||||
|
this.userInterface.confirmDuplicateCredential(
|
||||||
|
[Fido2Utils.bufferToString(params.excludeList[0].id)],
|
||||||
|
{
|
||||||
|
credentialName: params.rp.name,
|
||||||
|
userName: params.user.name,
|
||||||
|
}
|
||||||
|
);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -2,15 +2,19 @@ import { Fido2UserInterfaceService as Fido2UserInterfaceServiceAbstraction } fro
|
|||||||
import { RequestAbortedError } from "../abstractions/fido2.service.abstraction";
|
import { RequestAbortedError } from "../abstractions/fido2.service.abstraction";
|
||||||
|
|
||||||
export class Fido2UserInterfaceService implements Fido2UserInterfaceServiceAbstraction {
|
export class Fido2UserInterfaceService implements Fido2UserInterfaceServiceAbstraction {
|
||||||
async confirmCredential(cipherId: string): Promise<boolean> {
|
async confirmCredential(): Promise<boolean> {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
pickCredential(cipherIds: string[]): Promise<string> {
|
pickCredential(): Promise<string> {
|
||||||
throw new RequestAbortedError();
|
throw new RequestAbortedError();
|
||||||
}
|
}
|
||||||
|
|
||||||
async confirmNewCredential(): Promise<boolean> {
|
async confirmNewCredential(): Promise<boolean> {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
async confirmDuplicateCredential() {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user