Skip to content

Commit

Permalink
Things are progressing
Browse files Browse the repository at this point in the history
  • Loading branch information
varjolintu committed Oct 21, 2022
1 parent c44a80f commit f2e2d8f
Show file tree
Hide file tree
Showing 2 changed files with 94 additions and 30 deletions.
5 changes: 3 additions & 2 deletions keepassxc-browser/content/keepassxc-browser.js
Original file line number Diff line number Diff line change
Expand Up @@ -845,6 +845,7 @@ browser.runtime.onMessage.addListener(async function(req, sender) {
});

// Webauthn
// TODO: Add an option for this. Don't do it for every page.
const webauthn = document.createElement('script');
webauthn.src = browser.runtime.getURL('content/webauthn.js');
document.documentElement.appendChild(webauthn);
Expand All @@ -855,8 +856,8 @@ window.addEventListener('message', async (ev) => {
console.log(ev.data.publicKey);

const ret = await sendMessage('webauthn-register', [ ev.data.publicKey, ev.origin ]);
if (ret.response) {
window.postMessage({ action: 'webauthn-create-response', data: ret.response }, window.location.origin);
if (ret) {
window.postMessage({ action: 'webauthn-create-response', response: ret }, window.location.origin);
}
}
});
119 changes: 91 additions & 28 deletions keepassxc-browser/content/webauthn.js
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ const postMessageToExtension = function(request) {
const handler = (msg) => {
if (msg && msg.data && msg.data.action === messageRequest + '-response') {
messageEvent.removeEventListener('message', listener);
resolve(msg.data.data);
resolve(msg.data.response);
return;
}
};
Expand All @@ -30,47 +30,110 @@ const stringToArrayBuffer = function(str) {
const arrayToArrayBuffer = function(arr) {
const buf = Uint8Array.from(arr);
return buf.buffer;
}
};

// URL encoding needs some characters replaced
const base64ToArrayBuffer = function(str) {
return stringToArrayBuffer(atob(str));
return stringToArrayBuffer(window.atob(str.replaceAll('-', '+').replaceAll('_', '/')));
};

// URL encoding needs some characters replaced
const arrayBufferToBase64 = function(arr) {
var clonedArr = new ArrayBuffer(arr.byteLength);
new Uint8Array(clonedArr).set(new Uint8Array(arr));

return window.btoa(clonedArr).replaceAll('\\', '-').replaceAll('/', '_').replaceAll('=', '');
};

/*const arrayBufferToBase64HexString = function(arr) {
return window.btoa(arrayBufferToHexString(arr));
};*/

const arrayBufferToHexString = function(arr) {
return [ ...new Uint8Array(arr) ].map(c => c.toString(16).padStart(2, '0')).join('');
};

const buildCredentialCreationOptions = function(options) {
const publicKey = {};
publicKey.attestation = options.publicKey.attestation;
publicKey.authenticatorSelection = options.publicKey.authenticatorSelection;
//publicKey.challenge = arrayBufferToBase64(options.publicKey.challenge);
publicKey.challenge = arrayBufferToHexString(options.publicKey.challenge);
publicKey.extensions = options.publicKey.extensions;
publicKey.pubKeyCredParams = options.publicKey.pubKeyCredParams;
publicKey.rp = options.publicKey.rp;
publicKey.timeout = options.publicKey.timeout;

publicKey.excludeCredentials = [];
for (const cred of options.publicKey.excludeCredentials) {
const arr = {
//id: arrayBufferToBase64(cred.id),
id: arrayBufferToHexString(cred.id),
transports: cred.transports,
type: cred.type
};

publicKey.excludeCredentials.push(arr);
}

publicKey.user = {};
publicKey.user.displayName = options.publicKey.user.displayName;
//publicKey.user.id = arrayBufferToBase64(options.publicKey.user.id);
publicKey.user.id = arrayBufferToHexString(options.publicKey.user.id);
publicKey.user.name = options.publicKey.user.name;

return publicKey;
};

const buildCredentialRequestOptions = function(options) {
const publicKey = {};

return publicKey;
};

(async () => {
const originalCredentials = navigator.credentials;

const webauthnCredentials = {
async create(options) {
console.log('Overridden create');

const publicKey = {};
publicKey.attestation = options.publicKey.attestation;
publicKey.authenticatorSelection = options.publicKey.authenticatorSelection;
publicKey.challenge = window.btoa(options.publicKey.challenge); // Use b64 instead
publicKey.pubKeyCredParams = options.publicKey.pubKeyCredParams;
publicKey.rp = options.publicKey.rp;
publicKey.timeout = options.publicKey.timeout;
publicKey.user = options.publicKey.user;
publicKey.user.id = window.btoa(options.publicKey.user.id); // Use b64 instead

const publicKeyCredential = await postMessageToExtension({ action: 'webauthn-create', publicKey: publicKey });
console.log('Response: ', publicKeyCredential);

// Parse the response and change needed variables from b64 to ArrayBuffer/UInt8Array etc.
publicKeyCredential.rawId = base64ToArrayBuffer(publicKeyCredential.id);
publicKeyCredential.response.attestationObject.authData = arrayToArrayBuffer(publicKeyCredential.response.attestationObject.authData);
publicKeyCredential.response.clientDataJSON = stringToArrayBuffer(JSON.stringify(publicKeyCredential.response.clientDataJSON));

console.log('Final response: ', publicKeyCredential);
return publicKeyCredential;
if (options.publicKey) {
console.log(options.publicKey);
const publicKey = buildCredentialCreationOptions(options);
console.log(publicKey);
const response = await postMessageToExtension({ action: 'webauthn-create', publicKey: publicKey });
if (response.length === 0 || response.response === 'canceled') {
return;
}

// Parse
const publicKeyCredential = response.response;
publicKeyCredential.rawId = base64ToArrayBuffer(publicKeyCredential.id);
publicKeyCredential.response.attestationObject.authData = arrayToArrayBuffer(publicKeyCredential.response.attestationObject.authData);
publicKeyCredential.response.clientDataJSON = stringToArrayBuffer(JSON.stringify(publicKeyCredential.response.clientDataJSON));
publicKeyCredential.getClientExtensionResults = () => {};
return publicKeyCredential;
}

//const createResponse = await originalCredentials.create(options);
//console.log(createResponse);
//return createResponse;
return originalCredentials.create(options);
},
async get(options) {
console.log('Overridden get');
if (options.publicKey) {
console.log(options.publicKey);

const publicKey = buildCredentialRequestOptions(options);
}

return originalCredentials.get(options);
}
};

// Overwrite navigator.credentials
try {
Object.assign(navigator.credentials, webauthnCredentials);
Object.defineProperty(navigator, 'credentials', { value: webauthnCredentials });
} catch (err) {
console.log(err);
console.log('Cannot override navigator.credentials: ', err);
}
})();

0 comments on commit f2e2d8f

Please sign in to comment.