mirror of https://github.com/keeweb/keeweb.git
browser extension backend 101
This commit is contained in:
parent
43f6a4f499
commit
4b17ac19dc
|
@ -6,6 +6,7 @@ import { ExportApi } from 'comp/app/export-api';
|
|||
import { SingleInstanceChecker } from 'comp/app/single-instance-checker';
|
||||
import { Updater } from 'comp/app/updater';
|
||||
import { UsbListener } from 'comp/app/usb-listener';
|
||||
import { BrowserExtensionConnector } from 'comp/app/browser-extension-connector';
|
||||
import { FeatureTester } from 'comp/browser/feature-tester';
|
||||
import { FocusDetector } from 'comp/browser/focus-detector';
|
||||
import { IdleTracker } from 'comp/browser/idle-tracker';
|
||||
|
@ -177,6 +178,7 @@ ready(() => {
|
|||
AppRightsChecker.init();
|
||||
IdleTracker.init();
|
||||
UsbListener.init();
|
||||
BrowserExtensionConnector.init();
|
||||
setTimeout(() => {
|
||||
PluginManager.runAutoUpdate();
|
||||
}, Timeouts.AutoUpdatePluginsAfterStart);
|
||||
|
|
|
@ -0,0 +1,167 @@
|
|||
import kdbxweb from 'kdbxweb';
|
||||
import { box as tweetnaclBox } from 'tweetnacl';
|
||||
import { RuntimeInfo } from 'const/runtime-info';
|
||||
import { Launcher } from 'comp/launcher';
|
||||
import { AppSettingsModel } from 'models/app-settings-model';
|
||||
|
||||
const connectedClients = {};
|
||||
|
||||
function incrementNonce(nonce) {
|
||||
// from libsodium/utils.c, like it is in KeePassXC
|
||||
let i = 0;
|
||||
let c = 1;
|
||||
for (; i < nonce.length; ++i) {
|
||||
c += nonce[i];
|
||||
nonce[i] = c;
|
||||
c >>= 8;
|
||||
}
|
||||
}
|
||||
|
||||
function getClient(request) {
|
||||
if (!request.clientID) {
|
||||
throw new Error('Empty clientID');
|
||||
}
|
||||
const client = connectedClients[request.clientID];
|
||||
if (!client) {
|
||||
throw new Error(`Client not connected: ${request.clientID}`);
|
||||
}
|
||||
return client;
|
||||
}
|
||||
|
||||
function decryptRequest(request) {
|
||||
const client = getClient(request);
|
||||
|
||||
if (!request.nonce) {
|
||||
throw new Error('Empty nonce');
|
||||
}
|
||||
if (!request.message) {
|
||||
throw new Error('Empty message');
|
||||
}
|
||||
|
||||
const nonce = kdbxweb.ByteUtils.base64ToBytes(request.nonce);
|
||||
const message = kdbxweb.ByteUtils.base64ToBytes(request.message);
|
||||
|
||||
const data = tweetnaclBox.open(message, nonce, client.publicKey, client.keys.secretKey);
|
||||
|
||||
const json = new TextDecoder().decode(data);
|
||||
const payload = JSON.parse(json);
|
||||
|
||||
if (payload?.action !== request.action) {
|
||||
throw new Error(`Bad action in decrypted payload`);
|
||||
}
|
||||
|
||||
return payload;
|
||||
}
|
||||
|
||||
function encryptResponse(request, payload) {
|
||||
const client = getClient(request);
|
||||
|
||||
const json = JSON.stringify(payload);
|
||||
const data = new TextEncoder().encode(json);
|
||||
|
||||
let nonce = kdbxweb.ByteUtils.base64ToBytes(request.nonce);
|
||||
incrementNonce(nonce);
|
||||
|
||||
const encrypted = tweetnaclBox(data, nonce, client.publicKey, client.keys.secretKey);
|
||||
|
||||
const message = kdbxweb.ByteUtils.bytesToBase64(encrypted);
|
||||
nonce = kdbxweb.ByteUtils.bytesToBase64(nonce);
|
||||
|
||||
return {
|
||||
action: request.action,
|
||||
message,
|
||||
nonce
|
||||
};
|
||||
}
|
||||
|
||||
const ProtocolHandlers = {
|
||||
'ping'({ data }) {
|
||||
return { data };
|
||||
},
|
||||
|
||||
'change-public-keys'({ publicKey, clientID: clientId }) {
|
||||
const keys = tweetnaclBox.keyPair();
|
||||
publicKey = kdbxweb.ByteUtils.base64ToBytes(publicKey);
|
||||
|
||||
connectedClients[clientId] = { publicKey, keys };
|
||||
|
||||
return {
|
||||
action: 'change-public-keys',
|
||||
version: RuntimeInfo.version,
|
||||
publicKey: kdbxweb.ByteUtils.bytesToBase64(keys.publicKey),
|
||||
success: 'true'
|
||||
};
|
||||
},
|
||||
|
||||
'get-databasehash'(request) {
|
||||
decryptRequest(request);
|
||||
return encryptResponse(request, {
|
||||
action: 'hash',
|
||||
version: RuntimeInfo.version,
|
||||
hash: 'TODO'
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
const BrowserExtensionConnector = {
|
||||
init() {
|
||||
AppSettingsModel.on('change:browserExtension', (model, enabled) => {
|
||||
if (enabled) {
|
||||
this.start();
|
||||
} else {
|
||||
this.stop();
|
||||
}
|
||||
});
|
||||
if (AppSettingsModel.browserExtension) {
|
||||
this.start();
|
||||
}
|
||||
},
|
||||
|
||||
start() {
|
||||
if (!Launcher) {
|
||||
this.startWebMessageListener();
|
||||
}
|
||||
},
|
||||
|
||||
stop() {
|
||||
if (!Launcher) {
|
||||
this.stopWebMessageListener();
|
||||
}
|
||||
},
|
||||
|
||||
startWebMessageListener() {
|
||||
window.addEventListener('message', this.browserWindowMessage);
|
||||
},
|
||||
|
||||
stopWebMessageListener() {
|
||||
window.removeEventListener('message', this.browserWindowMessage);
|
||||
},
|
||||
|
||||
browserWindowMessage(e) {
|
||||
if (e.origin !== location.origin) {
|
||||
return;
|
||||
}
|
||||
if (e.source !== window) {
|
||||
return;
|
||||
}
|
||||
if (e?.data?.kwConnect !== 'request') {
|
||||
return;
|
||||
}
|
||||
let response;
|
||||
try {
|
||||
const handler = ProtocolHandlers[e.data.action];
|
||||
if (!handler) {
|
||||
throw new Error(`Handler not found: ${e.data.action}`);
|
||||
}
|
||||
response = handler(e.data) || {};
|
||||
} catch (e) {
|
||||
response = { error: e.message || 'Unknown error' };
|
||||
}
|
||||
if (response) {
|
||||
response.kwConnect = 'response';
|
||||
postMessage(response, window.location.origin);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
export { BrowserExtensionConnector };
|
|
@ -47,6 +47,7 @@ const DefaultAppSettings = {
|
|||
deviceOwnerAuthTimeoutMinutes: 0, // how often master password is required with Touch ID
|
||||
disableOfflineStorage: false, // don't cache loaded files in offline storage
|
||||
shortLivedStorageToken: false, // short-lived sessions in cloud storage providers
|
||||
browserExtension: false, // support browser extension interaction
|
||||
|
||||
yubiKeyShowIcon: true, // show an icon to open OTP codes from YubiKey
|
||||
yubiKeyAutoOpen: false, // auto-load one-time codes when there are open files
|
||||
|
|
|
@ -14,10 +14,7 @@
|
|||
<li><a href="https://github.com/patrick-steele-idem/morphdom" target="_blank">morphdom</a><span class="muted-color">, fast and lightweight DOM diffing/patching, © Patrick Steele-Idem <pnidem@gmail.com> (psteeleidem.com)</span></li>
|
||||
<li><a href="https://lodash.com/" target="_blank">lodash</a><span class="muted-color">, a modern JavaScript utility library delivering modularity, performance & extras, © OpenJS Foundation and other contributors <https://openjsf.org/></span></li>
|
||||
<li><a href="https://jquery.com/" target="_blank">jQuery</a><span class="muted-color">, fast, small, and feature-rich JavaScript library, © OpenJS Foundation and other contributors, https://openjsf.org/</span></li>
|
||||
<li><a href="https://marked.js.org/" target="_blank">marked</a><span class="muted-color">, a markdown parser and compiler, © 2018+, MarkedJS (https://github.com/markedjs/) © 2011-2018, Christopher Jeffrey (https://github.com/chjj/)</span></li>
|
||||
<li><a href="https://github.com/cure53/DOMPurify" target="_blank">dompurify</a><span class="muted-color">, a DOM-only, super-fast, uber-tolerant XSS sanitizer, © 2015 Mario Heiderich, </span>
|
||||
<a href="{{licenseLinkApache}}" class="muted-color" target="_blank">Apache-2.0 license</a></li>
|
||||
<li><a href="https://github.com/TomFrost/node-phonetic" target="_blank">node-phonetic</a><span class="muted-color">, generates unique, pronounceable names, © 2013 Tom Frost</span></li>
|
||||
|
||||
</ul>
|
||||
|
||||
<h3>Core components</h3>
|
||||
|
@ -48,8 +45,13 @@
|
|||
|
||||
<h3>Utils</h3>
|
||||
<ul>
|
||||
<li><a href="https://marked.js.org/" target="_blank">marked</a><span class="muted-color">, a markdown parser and compiler, © 2018+, MarkedJS (https://github.com/markedjs/) © 2011-2018, Christopher Jeffrey (https://github.com/chjj/)</span></li>
|
||||
<li><a href="https://github.com/cure53/DOMPurify" target="_blank">dompurify</a><span class="muted-color">, a DOM-only, super-fast, uber-tolerant XSS sanitizer, © 2015 Mario Heiderich, </span>
|
||||
<a href="{{licenseLinkApache}}" class="muted-color" target="_blank">Apache-2.0 license</a></li>
|
||||
<li><a href="https://github.com/TomFrost/node-phonetic" target="_blank">node-phonetic</a><span class="muted-color">, generates unique, pronounceable names, © 2013 Tom Frost</span></li>
|
||||
<li><a href="https://github.com/LazarSoft/jsqrcode" target="_blank">jsqrcode</a><span class="muted-color">, javascript QR code scanner,
|
||||
<a href="{{licenseLinkApache}}" class="muted-color" target="_blank">Apache-2.0 license</a></span></li>
|
||||
<li><a href="https://tweetnacl.js.org/" target="_blank">tweetnacl.js</a><span class="muted-color">, port of TweetNaCl cryptographic library to JavaScript, public domain</span></li>
|
||||
</ul>
|
||||
|
||||
<h3>Styles</h3>
|
||||
|
|
|
@ -56,6 +56,7 @@ function config(options) {
|
|||
argon2: 'argon2-browser/dist/argon2.js',
|
||||
marked: devMode ? 'marked/lib/marked.js' : 'marked/marked.min.js',
|
||||
dompurify: `dompurify/dist/purify${devMode ? '' : '.min'}.js`,
|
||||
tweetnacl: `tweetnacl/nacl${devMode ? '' : '.min'}.js`,
|
||||
hbs: 'handlebars/runtime.js',
|
||||
'argon2-wasm': 'argon2-browser/dist/argon2.wasm',
|
||||
templates: path.join(rootDir, 'app/templates'),
|
||||
|
|
|
@ -1,11 +1,11 @@
|
|||
{
|
||||
"name": "keeweb",
|
||||
"version": "1.17.4",
|
||||
"version": "1.17.5",
|
||||
"lockfileVersion": 2,
|
||||
"requires": true,
|
||||
"packages": {
|
||||
"": {
|
||||
"version": "1.17.4",
|
||||
"version": "1.17.5",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@babel/core": "^7.13.8",
|
||||
|
@ -94,6 +94,7 @@
|
|||
"svgicons2svgfont": "^9.1.1",
|
||||
"terser-webpack-plugin": "^5.1.1",
|
||||
"time-grunt": "2.0.0",
|
||||
"tweetnacl": "^0.14.5",
|
||||
"url-loader": "^4.1.1",
|
||||
"wawoff2": "^1.0.2",
|
||||
"webpack": "^5.24.3",
|
||||
|
|
|
@ -96,6 +96,7 @@
|
|||
"svgicons2svgfont": "^9.1.1",
|
||||
"terser-webpack-plugin": "^5.1.1",
|
||||
"time-grunt": "2.0.0",
|
||||
"tweetnacl": "^0.14.5",
|
||||
"url-loader": "^4.1.1",
|
||||
"wawoff2": "^1.0.2",
|
||||
"webpack": "^5.24.3",
|
||||
|
|
Loading…
Reference in New Issue