From 91daba5991446c9c8efbce58e38088e8fbaee417 Mon Sep 17 00:00:00 2001 From: Andreas Coroiu Date: Fri, 16 Dec 2022 11:42:32 +0100 Subject: [PATCH] [EC-598] feat: fully working credential creation --- apps/browser/src/browser/webauthn-utils.ts | 20 +++++++++++- .../src/content/webauthn/content-script.ts | 1 + .../src/content/webauthn/messaging/message.ts | 6 +++- .../src/content/webauthn/page-script.ts | 8 +++-- .../fido2/fido2.service.abstraction.ts | 8 ++++- .../src/services/fido2/fido2.service.ts | 32 +++++++++++-------- 6 files changed, 56 insertions(+), 19 deletions(-) diff --git a/apps/browser/src/browser/webauthn-utils.ts b/apps/browser/src/browser/webauthn-utils.ts index 5c6fb55f27..7783c8a1f7 100644 --- a/apps/browser/src/browser/webauthn-utils.ts +++ b/apps/browser/src/browser/webauthn-utils.ts @@ -1,5 +1,8 @@ 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 { 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: () => ({}), + }; + } } diff --git a/apps/browser/src/content/webauthn/content-script.ts b/apps/browser/src/content/webauthn/content-script.ts index 0bc2ac4a42..91ab565b6a 100644 --- a/apps/browser/src/content/webauthn/content-script.ts +++ b/apps/browser/src/content/webauthn/content-script.ts @@ -22,6 +22,7 @@ messenger.addHandler(async (message) => { resolve({ type: MessageType.CredentialCreationResponse, approved: true, + result: response, }); } ); diff --git a/apps/browser/src/content/webauthn/messaging/message.ts b/apps/browser/src/content/webauthn/messaging/message.ts index 0fb6c88418..92c83f9669 100644 --- a/apps/browser/src/content/webauthn/messaging/message.ts +++ b/apps/browser/src/content/webauthn/messaging/message.ts @@ -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 { CredentialCreationRequest, @@ -17,6 +20,7 @@ export type CredentialCreationRequest = { export type CredentialCreationResponse = { type: MessageType.CredentialCreationResponse; approved: boolean; + result?: CredentialRegistrationResult; }; export type CredentialGetRequest = { diff --git a/apps/browser/src/content/webauthn/page-script.ts b/apps/browser/src/content/webauthn/page-script.ts index 8155e3d5a4..1081a8db11 100644 --- a/apps/browser/src/content/webauthn/page-script.ts +++ b/apps/browser/src/content/webauthn/page-script.ts @@ -14,12 +14,16 @@ const browserCredentials = { const messenger = Messenger.forDOMCommunication(window); navigator.credentials.create = async (options?: CredentialCreationOptions): Promise => { - await messenger.request({ + const response = await messenger.request({ type: MessageType.CredentialCreationRequest, data: WebauthnUtils.mapCredentialCreationOptions(options, window.location.origin), }); - return await browserCredentials.create(options); + if (response.type !== MessageType.CredentialCreationResponse || !response.approved) { + return await browserCredentials.create(options); + } + + return WebauthnUtils.mapCredentialRegistrationResult(response.result); }; navigator.credentials.get = async (options?: CredentialRequestOptions): Promise => { diff --git a/libs/common/src/abstractions/fido2/fido2.service.abstraction.ts b/libs/common/src/abstractions/fido2/fido2.service.abstraction.ts index 3b6e4e0c54..2e43e34ee2 100644 --- a/libs/common/src/abstractions/fido2/fido2.service.abstraction.ts +++ b/libs/common/src/abstractions/fido2/fido2.service.abstraction.ts @@ -33,7 +33,13 @@ export interface CredentialRegistrationParams { }; } +export interface CredentialRegistrationResult { + credentialId: string; + clientDataJSON: string; + attestationObject: string; +} + export abstract class Fido2Service { - createCredential: (params: CredentialRegistrationParams) => Promise; + createCredential: (params: CredentialRegistrationParams) => Promise; assertCredential: () => unknown; } diff --git a/libs/common/src/services/fido2/fido2.service.ts b/libs/common/src/services/fido2/fido2.service.ts index 91d3bb76ee..37fc2d1b63 100644 --- a/libs/common/src/services/fido2/fido2.service.ts +++ b/libs/common/src/services/fido2/fido2.service.ts @@ -4,6 +4,7 @@ import { Fido2UserInterfaceService } from "../../abstractions/fido2/fido2-user-i import { Fido2Utils } from "../../abstractions/fido2/fido2-utils"; import { CredentialRegistrationParams, + CredentialRegistrationResult, Fido2Service as Fido2ServiceAbstraction, } from "../../abstractions/fido2/fido2.service.abstraction"; import { Utils } from "../../misc/utils"; @@ -26,10 +27,12 @@ export class Fido2Service implements Fido2ServiceAbstraction { constructor(private fido2UserInterfaceService: Fido2UserInterfaceService) {} - async createCredential(params: CredentialRegistrationParams): Promise { + async createCredential( + params: CredentialRegistrationParams + ): Promise { await this.fido2UserInterfaceService.confirmNewCredential(); // eslint-disable-next-line no-console - console.log("Fido2Service.registerCredential", params); + console.log("Fido2Service.createCredential", params); const attestationFormat = STANDARD_ATTESTATION_FORMAT; const encoder = new TextEncoder(); @@ -39,7 +42,7 @@ export class Fido2Service implements Fido2ServiceAbstraction { JSON.stringify({ type: "webauthn.create", challenge: params.challenge, - origin, + origin: params.origin, }) ); const keyPair = await crypto.subtle.generateKey( @@ -80,20 +83,22 @@ export class Fido2Service implements Fido2ServiceAbstraction { this.credentials.set(credentialId.encoded, { credentialId, keyPair, - origin, + origin: params.origin, rpId: params.rp.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 { - id: credentialId.encoded, - rawId: credentialId.raw, - type: "public-key", - response: { - clientDataJSON: clientData, - attestationObject: attestationObject, - } as AuthenticatorAttestationResponse, - getClientExtensionResults: () => ({}), + credentialId: Fido2Utils.bufferToString(credentialId.raw), + clientDataJSON: Fido2Utils.bufferToString(clientData), + attestationObject: Fido2Utils.bufferToString(attestationObject), }; } @@ -174,8 +179,7 @@ async function generateAuthData(params: AuthDataParams) { coseBytes.set(keyY, 10 + 32 + 3); // credential public key - convert to array from CBOR encoded COSE key - const credPublicKeyBytes = coseBytes.subarray(0, -1); - attestedCredentialData.push(...credPublicKeyBytes); + attestedCredentialData.push(...coseBytes); authData.push(...attestedCredentialData); }