1
0
mirror of https://github.com/bitwarden/browser synced 2025-12-15 15:53:27 +00:00

[EC-598] feat: increment counter during assertion

This commit is contained in:
Andreas Coroiu
2023-03-29 16:55:09 +02:00
parent 597bc0b197
commit 5bf4156fc6
2 changed files with 70 additions and 5 deletions

View File

@@ -588,7 +588,7 @@ describe("FidoAuthenticatorService", () => {
}); });
}); });
describe("assertion of non-discoverable credential", () => { describe("vault contains credential", () => {
let credentialIds: string[]; let credentialIds: string[];
let ciphers: CipherView[]; let ciphers: CipherView[];
let params: Fido2AuthenticatorGetAssertionParams; let params: Fido2AuthenticatorGetAssertionParams;
@@ -612,10 +612,66 @@ describe("FidoAuthenticatorService", () => {
/** Spec: Prompt the user to select a public key credential source selectedCredential from credentialOptions. */ /** Spec: Prompt the user to select a public key credential source selectedCredential from credentialOptions. */
it("should request confirmation from the user", async () => { it("should request confirmation from the user", async () => {
userInterface.pickCredential.mockResolvedValue(ciphers[0].id);
await authenticator.getAssertion(params); await authenticator.getAssertion(params);
expect(userInterface.pickCredential).toHaveBeenCalledWith(ciphers.map((c) => c.id)); expect(userInterface.pickCredential).toHaveBeenCalledWith(ciphers.map((c) => c.id));
}); });
/** Spec: If the user does not consent, return an error code equivalent to "NotAllowedError" and terminate the operation. */
it("should throw error", async () => {
userInterface.pickCredential.mockResolvedValue(undefined);
const result = async () => await authenticator.getAssertion(params);
await expect(result).rejects.toThrowError(Fido2AutenticatorErrorCode.NotAllowed);
});
});
describe("assertion of non-discoverable credential", () => {
let credentialIds: string[];
let ciphers: CipherView[];
let params: Fido2AuthenticatorGetAssertionParams;
beforeEach(async () => {
credentialIds = [Utils.newGuid(), Utils.newGuid()];
ciphers = await Promise.all(
credentialIds.map((id) =>
createCipherView(
{ type: CipherType.Login },
{ nonDiscoverableId: id, rpId: RpId, counter: 9000 }
)
)
);
params = await createParams({
allowCredentialDescriptorList: credentialIds.map((credentialId) => ({
id: Utils.guidToRawFormat(credentialId),
type: "public-key",
})),
rpId: RpId,
});
cipherService.getAllDecrypted.mockResolvedValue(ciphers);
userInterface.pickCredential.mockResolvedValue(ciphers[0].id);
});
/** Spec: Increment the credential associated signature counter */
it("should increment counter", async () => {
const encrypted = Symbol();
cipherService.encrypt.mockResolvedValue(encrypted as any);
await authenticator.getAssertion(params);
expect(cipherService.encrypt).toHaveBeenCalledWith(
expect.objectContaining({
id: ciphers[0].id,
fido2Key: expect.objectContaining({
counter: 9001,
}),
})
);
expect(cipherService.updateWithServer).toHaveBeenCalledWith(encrypted);
});
}); });
async function createParams( async function createParams(
@@ -651,10 +707,12 @@ function createCipherView(
const cipher = new CipherView(); const cipher = new CipherView();
cipher.id = data.id ?? Utils.newGuid(); cipher.id = data.id ?? Utils.newGuid();
cipher.type = data.type ?? CipherType.Fido2Key; cipher.type = data.type ?? CipherType.Fido2Key;
cipher.localData = {};
cipher.login = data.type ?? data.type === CipherType.Login ? new LoginView() : null; cipher.login = data.type ?? data.type === CipherType.Login ? new LoginView() : null;
cipher.fido2Key = new Fido2KeyView(); cipher.fido2Key = new Fido2KeyView();
cipher.fido2Key.nonDiscoverableId = fido2Key.nonDiscoverableId; cipher.fido2Key.nonDiscoverableId = fido2Key.nonDiscoverableId;
cipher.fido2Key.rpId = fido2Key.rpId; cipher.fido2Key.rpId = fido2Key.rpId ?? RpId;
cipher.fido2Key.counter = fido2Key.counter ?? 0;
return cipher; return cipher;
} }

View File

@@ -162,12 +162,19 @@ export class Fido2AuthenticatorService implements Fido2AuthenticatorServiceAbstr
throw new Fido2AutenticatorError(Fido2AutenticatorErrorCode.NotAllowed); throw new Fido2AutenticatorError(Fido2AutenticatorErrorCode.NotAllowed);
} }
// eslint-disable-next-line @typescript-eslint/no-unused-vars const selectedCredentialId = await this.userInterface.pickCredential(
const selectedCredential = await this.userInterface.pickCredential(
credentialOptions.map((cipher) => cipher.id) credentialOptions.map((cipher) => cipher.id)
); );
const selectedCredential = credentialOptions.find((c) => c.id === selectedCredentialId);
return null; if (selectedCredential === undefined) {
throw new Fido2AutenticatorError(Fido2AutenticatorErrorCode.NotAllowed);
}
++selectedCredential.fido2Key.counter;
selectedCredential.localData.lastUsedDate = new Date().getTime();
const encrypted = await this.cipherService.encrypt(selectedCredential);
await this.cipherService.updateWithServer(encrypted);
} }
private async vaultContainsCredentials( private async vaultContainsCredentials(