mirror of
https://github.com/bitwarden/browser.git
synced 2025-01-31 22:51:28 +01:00
[EC-598] feat: first tested rule in new authentitcator
This commit is contained in:
parent
e70d6cdcd4
commit
c8ab590086
@ -189,6 +189,15 @@ export class BrowserFido2UserInterfaceService implements Fido2UserInterfaceServi
|
||||
return false;
|
||||
}
|
||||
|
||||
async confirmDuplicateCredential(
|
||||
existingCipherIds: string[],
|
||||
newCredential: NewCredentialParams,
|
||||
abortController?: AbortController
|
||||
) {
|
||||
// Not Implemented
|
||||
return false;
|
||||
}
|
||||
|
||||
private setAbortTimeout(abortController: AbortController) {
|
||||
return setTimeout(() => abortController.abort());
|
||||
}
|
||||
|
@ -18,18 +18,19 @@ export interface Fido2AuthenticatorMakeCredentialsParams {
|
||||
id?: string;
|
||||
};
|
||||
user: {
|
||||
name: string;
|
||||
displayName: string;
|
||||
id: BufferSource;
|
||||
name?: string;
|
||||
displayName?: string;
|
||||
icon?: string;
|
||||
};
|
||||
pubKeyCredParams: {
|
||||
alg: number;
|
||||
// type: "public-key"; // not used
|
||||
type: "public-key"; // not used
|
||||
}[];
|
||||
excludeList?: {
|
||||
id: BufferSource;
|
||||
transports?: ("ble" | "internal" | "nfc" | "usb")[];
|
||||
// type: "public-key"; // not used
|
||||
type: "public-key"; // not used
|
||||
}[];
|
||||
extensions?: {
|
||||
appid?: string;
|
||||
|
@ -10,4 +10,9 @@ export abstract class Fido2UserInterfaceService {
|
||||
params: NewCredentialParams,
|
||||
abortController?: AbortController
|
||||
) => 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", () => {
|
||||
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", () => {
|
||||
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 {
|
||||
Fido2AuthenticatorMakeCredentialsParams,
|
||||
Fido2AuthenticatorService as Fido2AuthenticatorServiceAbstraction,
|
||||
} 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
|
||||
* https://fidoalliance.org/specs/fido-v2.0-ps-20190130/fido-client-to-authenticator-protocol-v2.0-ps-20190130.html
|
||||
*/
|
||||
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";
|
||||
|
||||
export class Fido2UserInterfaceService implements Fido2UserInterfaceServiceAbstraction {
|
||||
async confirmCredential(cipherId: string): Promise<boolean> {
|
||||
async confirmCredential(): Promise<boolean> {
|
||||
return false;
|
||||
}
|
||||
|
||||
pickCredential(cipherIds: string[]): Promise<string> {
|
||||
pickCredential(): Promise<string> {
|
||||
throw new RequestAbortedError();
|
||||
}
|
||||
|
||||
async confirmNewCredential(): Promise<boolean> {
|
||||
return false;
|
||||
}
|
||||
|
||||
async confirmDuplicateCredential() {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user