diff --git a/keepassxc-browser/content/passkeys.js b/keepassxc-browser/content/passkeys.js index 4ca7e9c0b..289c5fac4 100644 --- a/keepassxc-browser/content/passkeys.js +++ b/keepassxc-browser/content/passkeys.js @@ -26,6 +26,68 @@ return kpxcStringToArrayBuffer(window.atob(str?.replaceAll('-', '+').replaceAll('_', '/'))); }; + const kpxcArrayBufferToBase64Url = function (buff) { + const str = new Uint8Array(buff).reduce((acc, x) => (acc += String.fromCharCode(x)), ""); + return window.btoa(str).replace(/\+/g, "-").replace(/\//g, "_").replace(/=*$/g, ""); + }; + + const kpxcAuthenticatorAttestationResponseJson = function(response) { + const authData = response.getAuthenticatorData(); + const responsePk = response.getPublicKey(); + + let publicKey; + if (responsePk === null) { + publicKey = null; + } else { + publicKey = kpxcArrayBufferToBase64Url(responsePk); + } + + return { + clientDataJSON: kpxcArrayBufferToBase64Url(response.clientDataJSON), + authenticatorData: kpxcArrayBufferToBase64Url(authData), + transports: response.getTransports(), + publicKey, + publicKeyAlgorithm: response.getPublicKeyAlgorithm(), + attestationObject: kpxcArrayBufferToBase64Url(response.attestationObject), + }; + } + + const kpxcAuthenticatorAssertionResponseJson = function(response) { + const userHandle = response.userHandle + ? kpxcArrayBufferToBase64Url(response.userHandle) + : undefined; + return { + clientDataJSON: kpxcArrayBufferToBase64Url(response.clientDataJSON), + authenticatorData: kpxcArrayBufferToBase64Url(response.authenticatorData), + signature: kpxcArrayBufferToBase64Url(response.signature), + userHandle, + }; + } + + const kpxcPublicKeyCredentialJson = function (credential) { + const clientExtensionResults = credential.getClientExtensionResults(); + const type = credential.type; + const authenticatorAttachment = credential.authenticatorAttachment; + let response; + + if (credential.response instanceof AuthenticatorAttestationResponse) { + response = kpxcAuthenticatorAttestationResponseJson(credential.response); + } + + if (credential.response instanceof AuthenticatorAssertionResponse) { + response = kpxcAuthenticatorAssertionResponseJson(credential.response); + } + + return { + id: kpxcArrayBufferToBase64Url(credential.rawId), + rawId: kpxcArrayBufferToBase64Url(credential.rawId), + response, + authenticatorAttachment, + clientExtensionResults, + type, + }; + } + // Wraps response to AuthenticatorAttestationResponse object const createAttestationResponse = function(publicKey) { const response = { @@ -63,7 +125,8 @@ response: authenticatorResponse, type: publicKey.type, clientExtensionResults: () => publicKey?.response?.clientExtensionResults || {}, - getClientExtensionResults: () => publicKey?.response?.clientExtensionResults || {} + getClientExtensionResults: () => publicKey?.response?.clientExtensionResults || {}, + toJSON: () => kpxcPublicKeyCredentialJson(publicKeyCredential) }; return Object.setPrototypeOf(publicKeyCredential, PublicKeyCredential.prototype);