mirror of
https://github.com/keepassxreboot/keepassxc-browser.git
synced 2026-03-11 08:54:43 +00:00
140 lines
5.3 KiB
JavaScript
140 lines
5.3 KiB
JavaScript
'use strict';
|
|
|
|
const MINIMUM_TIMEOUT = 15000;
|
|
const DEFAULT_TIMEOUT = 30000;
|
|
const DISCOURAGED_TIMEOUT = 120000;
|
|
|
|
// From ArrayBuffer to URL encoded base64 string
|
|
const kpxcArrayBufferToBase64 = function(buf) {
|
|
const str = [ ...new Uint8Array(buf) ].map(c => String.fromCharCode(c)).join('');
|
|
return window.btoa(str).replaceAll('+', '-').replaceAll('/', '_').replaceAll('=', '');
|
|
};
|
|
|
|
const checkErrors = function(pkOptions, sameOriginWithAncestors) {
|
|
if (!pkOptions) {
|
|
throw new Error('No publicKey configuration options were provided');
|
|
}
|
|
|
|
if (pkOptions.signal && pkOptions.signal.aborted) {
|
|
throw new DOMException('Abort signalled', DOMException.AbortError);
|
|
}
|
|
|
|
if (!sameOriginWithAncestors) {
|
|
throw new DOMException('Cross-origin register or authentication is not allowed.', DOMException.NotAllowedError);
|
|
}
|
|
|
|
if (pkOptions.challenge.length < 16) {
|
|
throw new TypeError('challenge is shorter than required minimum length.');
|
|
}
|
|
};
|
|
|
|
const getTimeout = function(userVerification, timeout) {
|
|
if (!timeout || Number(timeout) === 0 || isNaN(Number(timeout))) {
|
|
return userVerification === 'discouraged' ? DISCOURAGED_TIMEOUT : DEFAULT_TIMEOUT;
|
|
}
|
|
|
|
// Note: A suggested reasonable range for the timeout member of options is 15 seconds to 120 seconds.
|
|
if (Number(timeout) < MINIMUM_TIMEOUT || Number(timeout) > DISCOURAGED_TIMEOUT) {
|
|
return DEFAULT_TIMEOUT;
|
|
}
|
|
|
|
return Number(timeout);
|
|
};
|
|
|
|
const kpxcPasskeysUtils = {};
|
|
|
|
// Sends response from KeePassXC back to the injected script
|
|
kpxcPasskeysUtils.sendPasskeysResponse = function(publicKey, errorCode, errorMessage) {
|
|
const response = errorCode
|
|
? { errorCode: errorCode, errorMessage: errorMessage, fallback: kpxc.settings.passkeysFallback }
|
|
: { publicKey: publicKey, fallback: kpxc.settings.passkeysFallback };
|
|
const details = isFirefox() ? cloneInto(response, document.defaultView) : response;
|
|
document.dispatchEvent(new CustomEvent('kpxc-passkeys-response', { detail: details }));
|
|
};
|
|
|
|
// Create a new object with base64 strings for KeePassXC
|
|
kpxcPasskeysUtils.buildCredentialCreationOptions = function(pkOptions, sameOriginWithAncestors) {
|
|
try {
|
|
checkErrors(pkOptions, sameOriginWithAncestors);
|
|
|
|
const publicKey = {};
|
|
publicKey.attestation = pkOptions?.attestation;
|
|
publicKey.authenticatorSelection = pkOptions?.authenticatorSelection;
|
|
publicKey.challenge = kpxcArrayBufferToBase64(pkOptions.challenge);
|
|
publicKey.extensions = pkOptions?.extensions;
|
|
|
|
// Make sure integers are used for "alg". Set to reserved if not found.
|
|
// https://www.iana.org/assignments/cose/cose.xhtml#algorithms
|
|
publicKey.pubKeyCredParams = [];
|
|
if (pkOptions.pubKeyCredParams) {
|
|
for (const credParam of pkOptions.pubKeyCredParams) {
|
|
publicKey.pubKeyCredParams.push({
|
|
type: credParam?.type,
|
|
alg: credParam.alg ? Number(credParam.alg) : 0
|
|
});
|
|
}
|
|
}
|
|
|
|
publicKey.rp = pkOptions?.rp;
|
|
publicKey.timeout = getTimeout(publicKey?.authenticatorSelection?.userVerification, pkOptions?.timeout);
|
|
|
|
publicKey.excludeCredentials = [];
|
|
if (pkOptions.excludeCredentials && pkOptions.excludeCredentials.length > 0) {
|
|
for (const cred of pkOptions.excludeCredentials) {
|
|
publicKey.excludeCredentials.push({
|
|
id: kpxcArrayBufferToBase64(cred.id),
|
|
transports: cred.transports,
|
|
type: cred.type
|
|
});
|
|
}
|
|
}
|
|
|
|
publicKey.user = {};
|
|
publicKey.user.displayName = pkOptions.user.displayName;
|
|
publicKey.user.id = kpxcArrayBufferToBase64(pkOptions.user.id);
|
|
publicKey.user.name = pkOptions.user.name;
|
|
|
|
return publicKey;
|
|
} catch (e) {
|
|
console.log(e);
|
|
}
|
|
};
|
|
|
|
// Create a new object with base64 strings for KeePassXC
|
|
kpxcPasskeysUtils.buildCredentialRequestOptions = function(pkOptions, sameOriginWithAncestors) {
|
|
try {
|
|
checkErrors(pkOptions, sameOriginWithAncestors);
|
|
|
|
const publicKey = {};
|
|
publicKey.challenge = kpxcArrayBufferToBase64(pkOptions.challenge);
|
|
publicKey.enterpriseAttestationPossible = false;
|
|
publicKey.extensions = pkOptions?.extensions;
|
|
publicKey.rpId = pkOptions?.rpId;
|
|
publicKey.timeout = getTimeout(publicKey?.userVerification, pkOptions?.timeout);
|
|
publicKey.userVerification = pkOptions?.userVerification;
|
|
|
|
publicKey.allowCredentials = [];
|
|
if (pkOptions.allowCredentials && pkOptions.allowCredentials.length > 0) {
|
|
for (const cred of pkOptions.allowCredentials) {
|
|
const transports = [];
|
|
if (cred.transports) {
|
|
for (const tp of cred.transports) {
|
|
transports.push(tp);
|
|
}
|
|
}
|
|
|
|
const arr = {
|
|
id: kpxcArrayBufferToBase64(cred.id),
|
|
transports: [ ...transports, 'internal' ],
|
|
type: cred.type
|
|
};
|
|
|
|
publicKey.allowCredentials.push(arr);
|
|
}
|
|
}
|
|
|
|
return publicKey;
|
|
} catch (e) {
|
|
console.log(e);
|
|
}
|
|
};
|