Passkeys: Wrap response to PublicKeyCredential

This commit is contained in:
varjolintu 2024-04-15 06:36:18 +03:00
parent 1ccfbff792
commit cc6d9cde69
3 changed files with 61 additions and 60 deletions

View file

@ -838,10 +838,7 @@ kpxc.enablePasskeys = function() {
let errorMessage;
if (ret.response && ret.response.errorCode) {
errorMessage = await sendMessage('get_error_message', ret.response.errorCode);
// Do not create a notification for this error
if (ret?.response?.errorCode !== PASSKEYS_WAIT_FOR_LIFETIMER) {
kpxcUI.createNotification('error', errorMessage);
}
kpxcUI.createNotification('error', errorMessage);
if (kpxc.settings.passkeysFallback) {
kpxcPasskeysUtils.sendPasskeysResponse(undefined, ret.response?.errorCode, errorMessage);
@ -850,8 +847,8 @@ kpxc.enablePasskeys = function() {
}
}
const responsePublicKey = callback(ret.response);
kpxcPasskeysUtils.sendPasskeysResponse(responsePublicKey, ret.response?.errorCode, errorMessage);
logDebug('Passkey response', ret.response);
kpxcPasskeysUtils.sendPasskeysResponse(ret.response, ret.response?.errorCode, errorMessage);
stopTimer(lifetimeTimer);
}
};
@ -867,15 +864,15 @@ kpxc.enablePasskeys = function() {
ev.detail.publicKey,
ev.detail.sameOriginWithAncestors,
);
logDebug('publicKey', publicKey);
await sendResponse('passkeys_register', publicKey, kpxcPasskeysUtils.parsePublicKeyCredential);
logDebug('Passkey request', publicKey);
await sendResponse('passkeys_register', publicKey);
} else if (ev.detail.action === 'passkeys_get') {
const publicKey = kpxcPasskeysUtils.buildCredentialRequestOptions(
ev.detail.publicKey,
ev.detail.sameOriginWithAncestors,
);
logDebug('publicKey', publicKey);
await sendResponse('passkeys_get', publicKey, kpxcPasskeysUtils.parseGetPublicKeyCredential);
logDebug('Passkey request', publicKey);
await sendResponse('passkeys_get', publicKey);
}
});
};

View file

@ -4,16 +4,6 @@ const MINIMUM_TIMEOUT = 15000;
const DEFAULT_TIMEOUT = 30000;
const DISCOURAGED_TIMEOUT = 120000;
const stringToArrayBuffer = function(str) {
const arr = Uint8Array.from(str, c => c.charCodeAt(0));
return arr.buffer;
};
// From URL encoded base64 string to ArrayBuffer
const base64ToArrayBuffer = function(str) {
return stringToArrayBuffer(window.atob(str.replaceAll('-', '+').replaceAll('_', '/')));
};
// From ArrayBuffer to URL encoded base64 string
const arrayBufferToBase64 = function(buf) {
const str = [ ...new Uint8Array(buf) ].map(c => String.fromCharCode(c)).join('');
@ -158,36 +148,3 @@ kpxcPasskeysUtils.buildCredentialRequestOptions = function(pkOptions, sameOrigin
console.log(e);
}
};
// Parse register response back from base64 strings to ByteArrays
kpxcPasskeysUtils.parsePublicKeyCredential = function(publicKeyCredential) {
if (!publicKeyCredential || !publicKeyCredential.type) {
return undefined;
}
publicKeyCredential.rawId = base64ToArrayBuffer(publicKeyCredential.id);
publicKeyCredential.response.attestationObject =
base64ToArrayBuffer(publicKeyCredential.response.attestationObject);
publicKeyCredential.response.clientDataJSON = base64ToArrayBuffer(publicKeyCredential.response.clientDataJSON);
return publicKeyCredential;
};
// Parse authentication response back from base64 strings to ByteArrays
kpxcPasskeysUtils.parseGetPublicKeyCredential = function(publicKeyCredential) {
if (!publicKeyCredential || !publicKeyCredential.type) {
return undefined;
}
publicKeyCredential.rawId = base64ToArrayBuffer(publicKeyCredential.id);
publicKeyCredential.response.authenticatorData =
base64ToArrayBuffer(publicKeyCredential.response.authenticatorData);
publicKeyCredential.response.clientDataJSON = base64ToArrayBuffer(publicKeyCredential.response.clientDataJSON);
publicKeyCredential.response.signature = base64ToArrayBuffer(publicKeyCredential.response.signature);
if (publicKeyCredential.response.userHandle) {
publicKeyCredential.response.userHandle = base64ToArrayBuffer(publicKeyCredential.response.userHandle);
}
return publicKeyCredential;
};

View file

@ -15,6 +15,58 @@ const PASSKEYS_UNKNOWN_ERROR = 31;
const PASSKEYS_INVALID_CHALLENGE = 32;
const PASSKEYS_INVALID_USER_ID = 33;
const stringToArrayBuffer = function(str) {
const arr = Uint8Array.from(str, c => c.charCodeAt(0));
return arr.buffer;
};
// From URL encoded base64 string to ArrayBuffer
const base64ToArrayBuffer = function(str) {
return stringToArrayBuffer(window.atob(str?.replaceAll('-', '+').replaceAll('_', '/')));
};
// Wraps response to AuthenticatorAttestationResponse object
const createAttestationResponse = function(publicKey) {
const response = {
attestationObject: base64ToArrayBuffer(publicKey.response.attestationObject),
clientDataJSON: base64ToArrayBuffer(publicKey.response.clientDataJSON),
getAuthenticatorData: () => base64ToArrayBuffer(publicKey.response?.authenticatorData),
getTransports: () => [ 'internal' ]
};
return Object.setPrototypeOf(response, AuthenticatorAttestationResponse.prototype);
};
// Wraps response to AuthenticatorAssertionResponse object
const createAssertionResponse = function(publicKey) {
const response = {
authenticatorData: base64ToArrayBuffer(publicKey.response?.authenticatorData),
clientDataJSON: base64ToArrayBuffer(publicKey.response?.clientDataJSON),
signature: base64ToArrayBuffer(publicKey.response?.signature),
userHandle: publicKey.response?.userHandle ? base64ToArrayBuffer(publicKey.response?.userHandle) : null
};
return Object.setPrototypeOf(response, AuthenticatorAssertionResponse.prototype);
};
// Wraps public key to PublicKeyCredential object
const createPublicKeyCredential = function(publicKey) {
const authenticatorResponse = publicKey?.response?.attestationObject
? createAttestationResponse(publicKey)
: createAssertionResponse(publicKey);
const publicKeyCredential = {
authenticatorAttachment: publicKey.authenticatorAttachment,
id: publicKey.id,
rawId: base64ToArrayBuffer(publicKey.id),
response: authenticatorResponse,
type: publicKey.type,
clientExtensionResults: () => publicKey?.response?.clientExtensionResults || {},
getClientExtensionResults: () => publicKey?.response?.clientExtensionResults || {}
};
return Object.setPrototypeOf(publicKeyCredential, PublicKeyCredential.prototype);
};
// Posts a message to extension's content script and waits for response
const postMessageToExtension = function(request) {
return new Promise((resolve, reject) => {
@ -109,10 +161,7 @@ const throwError = function(errorCode, errorMessage) {
return response.fallback ? originalCredentials.create(options) : null;
}
response.publicKey.getClientExtensionResults = () => {};
response.publicKey.clientExtensionResults = () => {};
response.publicKey.response.getTransports = () => [];
return response.publicKey;
return createPublicKeyCredential(response.publicKey);
},
async get(options) {
if (!options.publicKey || options?.mediation === 'silent') {
@ -137,9 +186,7 @@ const throwError = function(errorCode, errorMessage) {
return response.fallback ? originalCredentials.get(options) : null;
}
response.publicKey.getClientExtensionResults = () => {};
response.publicKey.clientExtensionResults = () => {};
return response.publicKey;
return createPublicKeyCredential(response.publicKey);
}
};