1
0
mirror of https://github.com/bitwarden/browser.git synced 2025-01-31 22:51:28 +01:00

[EC-598] feat: fully working credential creation

This commit is contained in:
Andreas Coroiu 2022-12-16 11:42:32 +01:00
parent e4bbb173b4
commit 91daba5991
No known key found for this signature in database
GPG Key ID: E70B5FFC81DFEC1A
6 changed files with 56 additions and 19 deletions

View File

@ -1,5 +1,8 @@
import { Fido2Utils } from "@bitwarden/common/abstractions/fido2/fido2-utils"; import { Fido2Utils } from "@bitwarden/common/abstractions/fido2/fido2-utils";
import { CredentialRegistrationParams } from "@bitwarden/common/abstractions/fido2/fido2.service.abstraction"; import {
CredentialRegistrationParams,
CredentialRegistrationResult,
} from "@bitwarden/common/abstractions/fido2/fido2.service.abstraction";
export class WebauthnUtils { export class WebauthnUtils {
static mapCredentialCreationOptions( static mapCredentialCreationOptions(
@ -44,4 +47,19 @@ export class WebauthnUtils {
}, },
}; };
} }
static mapCredentialRegistrationResult(
result: CredentialRegistrationResult
): PublicKeyCredential {
return {
id: result.credentialId,
rawId: Fido2Utils.stringToBuffer(result.credentialId),
type: "public-key",
response: {
clientDataJSON: Fido2Utils.stringToBuffer(result.clientDataJSON),
attestationObject: Fido2Utils.stringToBuffer(result.attestationObject),
} as AuthenticatorAttestationResponse,
getClientExtensionResults: () => ({}),
};
}
} }

View File

@ -22,6 +22,7 @@ messenger.addHandler(async (message) => {
resolve({ resolve({
type: MessageType.CredentialCreationResponse, type: MessageType.CredentialCreationResponse,
approved: true, approved: true,
result: response,
}); });
} }
); );

View File

@ -1,4 +1,7 @@
import { CredentialRegistrationParams } from "@bitwarden/common/abstractions/fido2/fido2.service.abstraction"; import {
CredentialRegistrationParams,
CredentialRegistrationResult,
} from "@bitwarden/common/abstractions/fido2/fido2.service.abstraction";
export enum MessageType { export enum MessageType {
CredentialCreationRequest, CredentialCreationRequest,
@ -17,6 +20,7 @@ export type CredentialCreationRequest = {
export type CredentialCreationResponse = { export type CredentialCreationResponse = {
type: MessageType.CredentialCreationResponse; type: MessageType.CredentialCreationResponse;
approved: boolean; approved: boolean;
result?: CredentialRegistrationResult;
}; };
export type CredentialGetRequest = { export type CredentialGetRequest = {

View File

@ -14,12 +14,16 @@ const browserCredentials = {
const messenger = Messenger.forDOMCommunication(window); const messenger = Messenger.forDOMCommunication(window);
navigator.credentials.create = async (options?: CredentialCreationOptions): Promise<Credential> => { navigator.credentials.create = async (options?: CredentialCreationOptions): Promise<Credential> => {
await messenger.request({ const response = await messenger.request({
type: MessageType.CredentialCreationRequest, type: MessageType.CredentialCreationRequest,
data: WebauthnUtils.mapCredentialCreationOptions(options, window.location.origin), data: WebauthnUtils.mapCredentialCreationOptions(options, window.location.origin),
}); });
if (response.type !== MessageType.CredentialCreationResponse || !response.approved) {
return await browserCredentials.create(options); return await browserCredentials.create(options);
}
return WebauthnUtils.mapCredentialRegistrationResult(response.result);
}; };
navigator.credentials.get = async (options?: CredentialRequestOptions): Promise<Credential> => { navigator.credentials.get = async (options?: CredentialRequestOptions): Promise<Credential> => {

View File

@ -33,7 +33,13 @@ export interface CredentialRegistrationParams {
}; };
} }
export interface CredentialRegistrationResult {
credentialId: string;
clientDataJSON: string;
attestationObject: string;
}
export abstract class Fido2Service { export abstract class Fido2Service {
createCredential: (params: CredentialRegistrationParams) => Promise<unknown>; createCredential: (params: CredentialRegistrationParams) => Promise<CredentialRegistrationResult>;
assertCredential: () => unknown; assertCredential: () => unknown;
} }

View File

@ -4,6 +4,7 @@ import { Fido2UserInterfaceService } from "../../abstractions/fido2/fido2-user-i
import { Fido2Utils } from "../../abstractions/fido2/fido2-utils"; import { Fido2Utils } from "../../abstractions/fido2/fido2-utils";
import { import {
CredentialRegistrationParams, CredentialRegistrationParams,
CredentialRegistrationResult,
Fido2Service as Fido2ServiceAbstraction, Fido2Service as Fido2ServiceAbstraction,
} from "../../abstractions/fido2/fido2.service.abstraction"; } from "../../abstractions/fido2/fido2.service.abstraction";
import { Utils } from "../../misc/utils"; import { Utils } from "../../misc/utils";
@ -26,10 +27,12 @@ export class Fido2Service implements Fido2ServiceAbstraction {
constructor(private fido2UserInterfaceService: Fido2UserInterfaceService) {} constructor(private fido2UserInterfaceService: Fido2UserInterfaceService) {}
async createCredential(params: CredentialRegistrationParams): Promise<unknown> { async createCredential(
params: CredentialRegistrationParams
): Promise<CredentialRegistrationResult> {
await this.fido2UserInterfaceService.confirmNewCredential(); await this.fido2UserInterfaceService.confirmNewCredential();
// eslint-disable-next-line no-console // eslint-disable-next-line no-console
console.log("Fido2Service.registerCredential", params); console.log("Fido2Service.createCredential", params);
const attestationFormat = STANDARD_ATTESTATION_FORMAT; const attestationFormat = STANDARD_ATTESTATION_FORMAT;
const encoder = new TextEncoder(); const encoder = new TextEncoder();
@ -39,7 +42,7 @@ export class Fido2Service implements Fido2ServiceAbstraction {
JSON.stringify({ JSON.stringify({
type: "webauthn.create", type: "webauthn.create",
challenge: params.challenge, challenge: params.challenge,
origin, origin: params.origin,
}) })
); );
const keyPair = await crypto.subtle.generateKey( const keyPair = await crypto.subtle.generateKey(
@ -80,20 +83,22 @@ export class Fido2Service implements Fido2ServiceAbstraction {
this.credentials.set(credentialId.encoded, { this.credentials.set(credentialId.encoded, {
credentialId, credentialId,
keyPair, keyPair,
origin, origin: params.origin,
rpId: params.rp.id, rpId: params.rp.id,
userHandle: Fido2Utils.stringToBuffer(params.user.id), userHandle: Fido2Utils.stringToBuffer(params.user.id),
}); });
// eslint-disable-next-line no-console
console.log("Fido2Service.createCredential => result", {
credentialId: Fido2Utils.bufferToString(credentialId.raw),
clientDataJSON: Fido2Utils.bufferToString(clientData),
attestationObject: Fido2Utils.bufferToString(attestationObject),
});
return { return {
id: credentialId.encoded, credentialId: Fido2Utils.bufferToString(credentialId.raw),
rawId: credentialId.raw, clientDataJSON: Fido2Utils.bufferToString(clientData),
type: "public-key", attestationObject: Fido2Utils.bufferToString(attestationObject),
response: {
clientDataJSON: clientData,
attestationObject: attestationObject,
} as AuthenticatorAttestationResponse,
getClientExtensionResults: () => ({}),
}; };
} }
@ -174,8 +179,7 @@ async function generateAuthData(params: AuthDataParams) {
coseBytes.set(keyY, 10 + 32 + 3); coseBytes.set(keyY, 10 + 32 + 3);
// credential public key - convert to array from CBOR encoded COSE key // credential public key - convert to array from CBOR encoded COSE key
const credPublicKeyBytes = coseBytes.subarray(0, -1); attestedCredentialData.push(...coseBytes);
attestedCredentialData.push(...credPublicKeyBytes);
authData.push(...attestedCredentialData); authData.push(...attestedCredentialData);
} }