mirror of
https://github.com/bitwarden/browser.git
synced 2025-01-21 21:11:35 +01:00
[PM-4756] [PM-4755] Add BE and BS flags, and credProps (#7947)
* [PM-4756] feat: set BE and BS flags * [PM-4755] feat: add support for credProps.rk * [PM-4755] feat: add extension support to page-script object mapping
This commit is contained in:
parent
111c102018
commit
b0dd64bab4
@ -33,7 +33,9 @@ export class WebauthnUtils {
|
||||
transports: credential.transports,
|
||||
type: credential.type,
|
||||
})),
|
||||
extensions: undefined, // extensions not currently supported
|
||||
extensions: {
|
||||
credProps: keyOptions.extensions?.credProps,
|
||||
},
|
||||
pubKeyCredParams: keyOptions.pubKeyCredParams.map((params) => ({
|
||||
alg: params.alg,
|
||||
type: params.type,
|
||||
@ -78,7 +80,9 @@ export class WebauthnUtils {
|
||||
return result.transports;
|
||||
},
|
||||
} as AuthenticatorAttestationResponse,
|
||||
getClientExtensionResults: () => ({}),
|
||||
getClientExtensionResults: () => ({
|
||||
credProps: result.extensions.credProps,
|
||||
}),
|
||||
} as PublicKeyCredential;
|
||||
|
||||
// Modify prototype chains to fix `instanceof` calls.
|
||||
|
@ -80,13 +80,12 @@ export interface CreateCredentialParams {
|
||||
}[];
|
||||
/**
|
||||
* This member contains additional parameters requesting additional processing by the client and authenticator.
|
||||
* Not currently supported.
|
||||
**/
|
||||
extensions?: {
|
||||
appid?: string;
|
||||
appidExclude?: string;
|
||||
appid?: string; // Not supported
|
||||
appidExclude?: string; // Not supported
|
||||
uvm?: boolean; // Not supported
|
||||
credProps?: boolean;
|
||||
uvm?: boolean;
|
||||
};
|
||||
/**
|
||||
* This member contains information about the desired properties of the credential to be created.
|
||||
@ -125,6 +124,11 @@ export interface CreateCredentialResult {
|
||||
publicKey: string;
|
||||
publicKeyAlgorithm: number;
|
||||
transports: string[];
|
||||
extensions: {
|
||||
credProps?: {
|
||||
rk: boolean;
|
||||
};
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -362,7 +362,7 @@ describe("FidoAuthenticatorService", () => {
|
||||
0xd0, 0x5c, 0x3d, 0xc3,
|
||||
]),
|
||||
);
|
||||
expect(flags).toEqual(new Uint8Array([0b01000001])); // UP = true, AD = true
|
||||
expect(flags).toEqual(new Uint8Array([0b01011001])); // UP = true, AT = true, BE = true, BS = true
|
||||
expect(counter).toEqual(new Uint8Array([0, 0, 0, 0])); // 0 because of new counter
|
||||
expect(aaguid).toEqual(AAGUID);
|
||||
expect(credentialIdLength).toEqual(new Uint8Array([0, 16])); // 16 bytes because we're using GUIDs
|
||||
@ -697,7 +697,7 @@ describe("FidoAuthenticatorService", () => {
|
||||
0xd0, 0x5c, 0x3d, 0xc3,
|
||||
]),
|
||||
);
|
||||
expect(flags).toEqual(new Uint8Array([0b00000001])); // UP = true
|
||||
expect(flags).toEqual(new Uint8Array([0b00011001])); // UP = true, BE = true, BS = true
|
||||
expect(counter).toEqual(new Uint8Array([0, 0, 0x23, 0x29])); // 9001 in hex
|
||||
|
||||
// Verify signature
|
||||
|
@ -444,6 +444,8 @@ async function generateAuthData(params: AuthDataParams) {
|
||||
const flags = authDataFlags({
|
||||
extensionData: false,
|
||||
attestationData: params.keyPair != undefined,
|
||||
backupEligibility: true,
|
||||
backupState: true, // Credentials are always synced
|
||||
userVerification: params.userVerification,
|
||||
userPresence: params.userPresence,
|
||||
});
|
||||
@ -522,6 +524,8 @@ async function generateSignature(params: SignatureParams) {
|
||||
interface Flags {
|
||||
extensionData: boolean;
|
||||
attestationData: boolean;
|
||||
backupEligibility: boolean;
|
||||
backupState: boolean;
|
||||
userVerification: boolean;
|
||||
userPresence: boolean;
|
||||
}
|
||||
@ -537,6 +541,14 @@ function authDataFlags(options: Flags): number {
|
||||
flags |= 0b01000000;
|
||||
}
|
||||
|
||||
if (options.backupEligibility) {
|
||||
flags |= 0b00001000;
|
||||
}
|
||||
|
||||
if (options.backupState) {
|
||||
flags |= 0b00010000;
|
||||
}
|
||||
|
||||
if (options.userVerification) {
|
||||
flags |= 0b00000100;
|
||||
}
|
||||
|
@ -207,6 +207,42 @@ describe("FidoAuthenticatorService", () => {
|
||||
);
|
||||
});
|
||||
|
||||
it("should return credProps.rk = true when creating a discoverable credential", async () => {
|
||||
const params = createParams({
|
||||
authenticatorSelection: { residentKey: "required", userVerification: "required" },
|
||||
extensions: { credProps: true },
|
||||
});
|
||||
authenticator.makeCredential.mockResolvedValue(createAuthenticatorMakeResult());
|
||||
|
||||
const result = await client.createCredential(params, tab);
|
||||
|
||||
expect(result.extensions.credProps?.rk).toBe(true);
|
||||
});
|
||||
|
||||
it("should return credProps.rk = false when creating a non-discoverable credential", async () => {
|
||||
const params = createParams({
|
||||
authenticatorSelection: { residentKey: "discouraged", userVerification: "required" },
|
||||
extensions: { credProps: true },
|
||||
});
|
||||
authenticator.makeCredential.mockResolvedValue(createAuthenticatorMakeResult());
|
||||
|
||||
const result = await client.createCredential(params, tab);
|
||||
|
||||
expect(result.extensions.credProps?.rk).toBe(false);
|
||||
});
|
||||
|
||||
it("should return credProps = undefiend when the extension is not requested", async () => {
|
||||
const params = createParams({
|
||||
authenticatorSelection: { residentKey: "required", userVerification: "required" },
|
||||
extensions: {},
|
||||
});
|
||||
authenticator.makeCredential.mockResolvedValue(createAuthenticatorMakeResult());
|
||||
|
||||
const result = await client.createCredential(params, tab);
|
||||
|
||||
expect(result.extensions.credProps).toBeUndefined();
|
||||
});
|
||||
|
||||
// Spec: If any authenticator returns an error status equivalent to "InvalidStateError", Return a DOMException whose name is "InvalidStateError" and terminate this algorithm.
|
||||
it("should throw error if authenticator throws InvalidState", async () => {
|
||||
const params = createParams();
|
||||
|
@ -192,6 +192,13 @@ export class Fido2ClientService implements Fido2ClientServiceAbstraction {
|
||||
throw new DOMException("The operation either timed out or was not allowed.", "AbortError");
|
||||
}
|
||||
|
||||
let credProps;
|
||||
if (params.extensions?.credProps) {
|
||||
credProps = {
|
||||
rk: makeCredentialParams.requireResidentKey,
|
||||
};
|
||||
}
|
||||
|
||||
clearTimeout(timeout);
|
||||
return {
|
||||
credentialId: Fido2Utils.bufferToString(makeCredentialResult.credentialId),
|
||||
@ -201,6 +208,7 @@ export class Fido2ClientService implements Fido2ClientServiceAbstraction {
|
||||
publicKey: Fido2Utils.bufferToString(makeCredentialResult.publicKey),
|
||||
publicKeyAlgorithm: makeCredentialResult.publicKeyAlgorithm,
|
||||
transports: params.rp.id === "google.com" ? ["internal", "usb"] : ["internal"],
|
||||
extensions: { credProps },
|
||||
};
|
||||
}
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user