From efc2da0635d2c31932607eb0633e4a7afc86c71e Mon Sep 17 00:00:00 2001 From: Brandon Davis Date: Fri, 9 Aug 2024 18:43:53 -0500 Subject: [PATCH] Argon2 and OTP fixes (#352) --- background/background.js | 6 ++-- package.json | 8 ++++-- scripts/manifest.ts | 4 +-- services/keepassHeader.js | 6 ++-- services/keepassReference.js | 2 +- services/keepassService.js | 47 ++++++++++++++++++-------------- services/secureCacheMemory.js | 1 - src/components/EntryDetails.vue | 12 ++++---- src/components/EntryList.vue | 2 -- src/components/EntryListItem.vue | 5 ++-- src/components/Unlock.vue | 7 +---- src/lib/argon2.js | 37 ------------------------- src/lib/argon2.wasm.js | 15 ---------- vite.config.mts | 3 +- yarn.lock | 45 +++++++++++++++++------------- 15 files changed, 77 insertions(+), 123 deletions(-) delete mode 100644 src/lib/argon2.js delete mode 100644 src/lib/argon2.wasm.js diff --git a/background/background.js b/background/background.js index 2f53e828..2ead8f04 100644 --- a/background/background.js +++ b/background/background.js @@ -12,7 +12,7 @@ import { Settings } from '$services/settings.js' import { Notifications } from "$services/notifications"; function Background(protectedMemory, settings, notifications) { - console.log('Background worker registered'); + console.log('Background worker registered.'); chrome.runtime.onInstalled.addListener(settings.upgrade); chrome.runtime.onStartup.addListener(forgetStuff); @@ -96,7 +96,7 @@ function Background(protectedMemory, settings, notifications) { files: ["/dist/contentScripts/index.global.js"], }, function(result) { //script injected - console.log("injected") + console.log("Autofill script injected.") chrome.tabs.sendMessage(message.tabId, { m: "fillPassword", u: message.u, @@ -179,7 +179,7 @@ function Background(protectedMemory, settings, notifications) { function clearClipboard() { // No longer have access to document in this context. // https://github.com/GoogleChrome/chrome-extensions-samples/tree/main/functional-samples/cookbook.offscreen-clipboard-write - console.log('Clearing clipboard'); + console.info('Clearing clipboard'); // var clearClipboard = function(e) { // e.clipboardData.setData('text/plain', ""); // e.preventDefault(); diff --git a/package.json b/package.json index cea9532a..26b06325 100644 --- a/package.json +++ b/package.json @@ -13,7 +13,7 @@ "dev:background": "run-s build:background -- --mode development", "dev:js": "run-s build:js -- --mode development", "build": "cross-env NODE_ENV=production run-s clear build:web build:prepare build:background build:js", - "build:ff": "cross-env NODE_ENV=production TARGET=firefox run-s clear build:web build:prepare build:background build:js", + "build:ff": "cross-env NODE_ENV=production TARGET=firefox run-s clear build:web build:prepare build:background build:js", "build:prepare": "esno scripts/prepare.ts", "build:background": "vite build --config vite.config.background.mts", "build:js": "vite build --config vite.config.content.mts", @@ -21,12 +21,13 @@ "pack:zip": "rimraf extension.zip && zip -r extension.zip extension/*" }, "dependencies": { + "argon2-browser": "^1.18.0", "axios": "^1.7.2", "base64-arraybuffer": "^1.0.2", "case": "^1.5.4", "font-awesome": "^4.7.0", "json-formatter-js": "^2.2.0", - "kdbxweb": "^1.2.4", + "kdbxweb": "^2.1.1", "materialize-css": "^1.0.0", "pako": "^1.0.6", "vue": "2", @@ -52,6 +53,7 @@ "npm-run-all": "^4.1.5", "rimraf": "^6.0.1", "sass": "^1.77.8", - "vite": "^5.3.5" + "vite": "^5.3.5", + "vite-plugin-wasm": "^3.3.0" } } diff --git a/scripts/manifest.ts b/scripts/manifest.ts index 14196c42..15d092e6 100644 --- a/scripts/manifest.ts +++ b/scripts/manifest.ts @@ -87,8 +87,8 @@ function chromeManifestV3(): Manifest.WebExtensionManifest { content_security_policy: { extension_pages: isDev // this is required on dev for Vite script to load - ? `script-src \'self\' http://localhost:${port}; object-src \'self\'` - : 'script-src \'self\'; object-src \'self\'' + ? `script-src \'self\' \'wasm-unsafe-eval\' http://localhost:${port}; object-src \'self\'` + : 'script-src \'self\' \'wasm-unsafe-eval\'; object-src \'self\'' }, action, "commands": { diff --git a/services/keepassHeader.js b/services/keepassHeader.js index 26c0b091..328b4e7f 100644 --- a/services/keepassHeader.js +++ b/services/keepassHeader.js @@ -29,7 +29,7 @@ function KeepassHeader() { var VERSION_KDBX = 3; if (h.sigKeePass != DBSIG_KEEPASS || (h.sigKeePassType != DBSIG_KDBX && h.sigKeePassType != DBSIG_KDBX_ALPHA && h.sigKeePassType != DBSIG_KDB && h.sigKeePassType != DBSIG_KDB_NEW)) { //fail - console.log("Signature fail. sig 1:" + h.sigKeePass.toString(16) + ", sig2:" + h.sigKeePassType.toString(16)); + console.error("Signature fail. sig 1:" + h.sigKeePass.toString(16) + ", sig2:" + h.sigKeePassType.toString(16)); throw new Error('This is not a valid KeePass file - file signature is not correct.') } @@ -39,8 +39,6 @@ function KeepassHeader() { readKdbHeader(buf, 8, h); } - //console.log(h); - //console.log("version: " + h.version.toString(16) + ", keyRounds: " + h.keyRounds); return h; } @@ -96,7 +94,7 @@ function KeepassHeader() { var len = descriptor.getUint16(1, littleEndian); var dv = new DataView(buf, position + 3, len); - //console.log("fieldid " + fieldId + " found at " + position); + position += 3; switch (fieldId) { case 0: //end of header diff --git a/services/keepassReference.js b/services/keepassReference.js index 749c885e..86b35a76 100644 --- a/services/keepassReference.js +++ b/services/keepassReference.js @@ -1,4 +1,4 @@ -import kdbxweb from 'kdbxweb'; +import * as kdbxweb from 'kdbxweb'; function KeepassReference() { "use strict"; diff --git a/services/keepassService.js b/services/keepassService.js index 59ce2519..b28294f7 100644 --- a/services/keepassService.js +++ b/services/keepassService.js @@ -6,19 +6,30 @@ import * as Base64 from 'base64-arraybuffer' import * as Case from 'case' // import pako from 'pako' import * as kdbxweb from 'kdbxweb' +import argon2 from 'argon2-browser/dist/argon2-bundled.min.js'; + +kdbxweb.CryptoEngine.setArgon2Impl(( + password, salt, + memory, iterations, length, parallelism, type, version +) => { + console.log('Using argon2 implementation', password, salt, memory, iterations, length, parallelism, type, version); + return argon2.hash({ + pass: new Uint8Array(password), + salt: new Uint8Array(salt), + time: iterations, + mem: memory, + hashLen: length, + parallelism, + type, + version, + }).then((v) => v.hash) +}); -import { argon2 } from '@/lib/argon2.js' import { parseUrl, getValidTokens } from '@/lib/utils.js' function KeepassService(keepassHeader, settings, passwordFileStoreRegistry, keepassReference) { var my = {}; - var littleEndian = (function () { - var buffer = new ArrayBuffer(2); - new DataView(buffer).setInt16(0, 256, true); - return new Int16Array(buffer)[0] === 256; - })(); - /** * return Promise(arrayBufer) */ @@ -40,7 +51,7 @@ function KeepassService(keepassHeader, settings, passwordFileStoreRegistry, keep } else if (masterPassword === "" && keyFileInfo !== undefined) { // Keyfile but empty password provided. Assume password is unused. // This extension does not support the combo empty string + keyfile. - protectedMasterPassword = null; + protectedMasterPassword = null } else { protectedMasterPassword = kdbxweb.ProtectedValue.fromString(masterPassword) } @@ -58,10 +69,8 @@ function KeepassService(keepassHeader, settings, passwordFileStoreRegistry, keep if (!h) throw new Error('Failed to read file header'); if (h.kdbx) { // KDBX - use kdbxweb library - kdbxweb.CryptoEngine.argon2 = argon2; var kdbxCreds = jsonCredentialsToKdbx(masterKey); return kdbxweb.Kdbx.load(buf, kdbxCreds).then(db => { - var psk = new Uint8Array(db.header.protectedStreamKey, 0, db.header.protectedStreamKey.length); var entries = parseKdbxDb(db.groups); majorVersion = db.header.versionMajor; return processReferences(entries, majorVersion); @@ -190,15 +199,14 @@ function KeepassService(keepassHeader, settings, passwordFileStoreRegistry, keep entry.keys.push('tags'); } if (db_entry.fields) { - var field_keys = Object.keys(db_entry.fields); - for (let k = 0; k < field_keys.length; k++) { - var field = field_keys[k]; - if (typeof db_entry.fields[field] === 'object') { + for (const [key, field] of db_entry.fields) { + const camelKey = Case.camel(key); + if (typeof field === 'object') { // type = object ? protected value - entry.protectedData[Case.camel(field)] = protectedValueToJSON(db_entry.fields[field]); + entry.protectedData[camelKey] = protectedValueToJSON(field); } else { - entry.keys.push(Case.camel(field)); - entry[Case.camel(field)] = db_entry.fields[field]; + entry.keys.push(camelKey); + entry[camelKey] = field; } } } @@ -232,10 +240,9 @@ function KeepassService(keepassHeader, settings, passwordFileStoreRegistry, keep */ function protectedValueToJSON(pv) { - console.log(pv) return { - salt: Array.from(pv._salt), - value: Array.from(pv._value) + salt: Array.from(pv.salt), + value: Array.from(pv.value) } } diff --git a/services/secureCacheMemory.js b/services/secureCacheMemory.js index a8559900..b5f35e3a 100644 --- a/services/secureCacheMemory.js +++ b/services/secureCacheMemory.js @@ -50,7 +50,6 @@ function SecureCacheMemory(protectedMemory) { //wake up the background page and get a pipe to send/receive messages: exports.get = function (key) { ready.then(function (port) { - console.log(port) port.postMessage({ action: 'get', key: key diff --git a/src/components/EntryDetails.vue b/src/components/EntryDetails.vue index aff09070..4aa47bb7 100644 --- a/src/components/EntryDetails.vue +++ b/src/components/EntryDetails.vue @@ -1,5 +1,5 @@