commit
0ea5bc2dcc
|
@ -90,6 +90,32 @@ export default new class E2EE extends BuiltinModule {
|
||||||
Settings.getSetting('security', 'e2eedb', 'e2ekvps').addItem({ value: { key: channelId, value: key } });
|
Settings.getSetting('security', 'e2eedb', 'e2ekvps').addItem({ value: { key: channelId, value: key } });
|
||||||
}
|
}
|
||||||
|
|
||||||
|
<<<<<<< Updated upstream
|
||||||
|
=======
|
||||||
|
get ecdhStorage() {
|
||||||
|
return this._ecdhStorage || (this._ecdhStorage = {});
|
||||||
|
}
|
||||||
|
|
||||||
|
createKeyExchange(dmChannelID) {
|
||||||
|
this.ecdhStorage[dmChannelID] = Security.createECDH();
|
||||||
|
return Security.generateECDHKeys(this.ecdhStorage[dmChannelID]);
|
||||||
|
}
|
||||||
|
|
||||||
|
publicKeyFor(dmChannelID) {
|
||||||
|
return Security.getECDHPublicKey(this.ecdhStorage[dmChannelID]);
|
||||||
|
}
|
||||||
|
|
||||||
|
computeSecret(dmChannelID, otherKey) {
|
||||||
|
try {
|
||||||
|
const secret = Security.computeECDHSecret(this.ecdhStorage[dmChannelID], otherKey);
|
||||||
|
delete this.ecdhStorage[dmChannelID];
|
||||||
|
return Security.sha256(secret);
|
||||||
|
} catch (e) {
|
||||||
|
throw e;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
>>>>>>> Stashed changes
|
||||||
async enabled(e) {
|
async enabled(e) {
|
||||||
seed = Security.randomBytes();
|
seed = Security.randomBytes();
|
||||||
// TODO Input modal for key
|
// TODO Input modal for key
|
||||||
|
|
|
@ -9,7 +9,11 @@
|
||||||
*/
|
*/
|
||||||
|
|
||||||
<template>
|
<template>
|
||||||
|
<<<<<<< Updated upstream
|
||||||
<div class="bd-e2eeTaContainer" @contextmenu.prevent="channelType === 'DM' && $refs.ee2eLockContextMenu.open()">
|
<div class="bd-e2eeTaContainer" @contextmenu.prevent="channelType === 'DM' && $refs.ee2eLockContextMenu.open()">
|
||||||
|
=======
|
||||||
|
<div class="bd-e2eeTaContainer" @contextmenu.prevent="currentChannel.type === 'DM' && $refs.ee2eLockContextMenu.open()">
|
||||||
|
>>>>>>> Stashed changes
|
||||||
<v-popover popoverClass="bd-popover bd-e2eePopover" placement="top">
|
<v-popover popoverClass="bd-popover bd-e2eePopover" placement="top">
|
||||||
<div v-if="error" class="bd-e2eeTaBtn bd-e2eeLock bd-error">
|
<div v-if="error" class="bd-e2eeTaBtn bd-e2eeLock bd-error">
|
||||||
<MiLock v-tooltip="error" />
|
<MiLock v-tooltip="error" />
|
||||||
|
@ -28,6 +32,9 @@
|
||||||
<template slot="popover">
|
<template slot="popover">
|
||||||
<div @click="toggleEncrypt" :class="{'bd-warn': !E2EE.encryptNewMessages, 'bd-ok': E2EE.encryptNewMessages}"><MiLock size="16" v-tooltip="'Toggle Encryption'" /></div>
|
<div @click="toggleEncrypt" :class="{'bd-warn': !E2EE.encryptNewMessages, 'bd-ok': E2EE.encryptNewMessages}"><MiLock size="16" v-tooltip="'Toggle Encryption'" /></div>
|
||||||
<div v-close-popover @click="showUploadDialog" v-if="!error"><MiImagePlus size="16" v-tooltip="'Upload Encrypted Image'" /></div>
|
<div v-close-popover @click="showUploadDialog" v-if="!error"><MiImagePlus size="16" v-tooltip="'Upload Encrypted Image'" /></div>
|
||||||
|
<!-- Using these icons for now -->
|
||||||
|
<div v-close-popover @click="generatePublicKey" v-if="currentChannel.type === 'DM'"><MiPencil size="16" v-tooltip="'Generate Public Key'" /></div>
|
||||||
|
<div v-close-popover @click="receivePublicKey" v-if="currentChannel.type === 'DM' && E2EE.ecdhStorage[currentChannel.id]"><MiRefresh size="16" v-tooltip="'Receive Public Key'" /></div>
|
||||||
</template>
|
</template>
|
||||||
</v-popover>
|
</v-popover>
|
||||||
<div class="bd-taDivider"></div>
|
<div class="bd-taDivider"></div>
|
||||||
|
@ -46,18 +53,26 @@
|
||||||
import { remote } from 'electron';
|
import { remote } from 'electron';
|
||||||
import { E2EE } from 'builtin';
|
import { E2EE } from 'builtin';
|
||||||
import { DiscordApi, Security } from 'modules';
|
import { DiscordApi, Security } from 'modules';
|
||||||
|
<<<<<<< Updated upstream
|
||||||
import { MiLock, MiPlus, MiImagePlus } from '../ui/components/common/MaterialIcon';
|
import { MiLock, MiPlus, MiImagePlus } from '../ui/components/common/MaterialIcon';
|
||||||
import contextMenu from 'vue-context-menu';
|
import contextMenu from 'vue-context-menu';
|
||||||
|
=======
|
||||||
|
import { MiLock, MiPlus, MiImagePlus, MiPencil, MiRefresh } from '../ui/components/common/MaterialIcon';
|
||||||
|
>>>>>>> Stashed changes
|
||||||
import { Toasts } from 'ui';
|
import { Toasts } from 'ui';
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
components: { MiLock, MiPlus, MiImagePlus },
|
components: { MiLock, MiPlus, MiImagePlus, MiPencil, MiRefresh },
|
||||||
data() {
|
data() {
|
||||||
return {
|
return {
|
||||||
E2EE,
|
E2EE,
|
||||||
state: 'loading',
|
state: 'loading',
|
||||||
error: null,
|
error: null,
|
||||||
|
<<<<<<< Updated upstream
|
||||||
channelType: DiscordApi.currentChannel.type
|
channelType: DiscordApi.currentChannel.type
|
||||||
|
=======
|
||||||
|
currentChannel: DiscordApi.currentChannel
|
||||||
|
>>>>>>> Stashed changes
|
||||||
};
|
};
|
||||||
},
|
},
|
||||||
methods: {
|
methods: {
|
||||||
|
@ -90,12 +105,17 @@
|
||||||
Toasts.success('New messages will be encrypted');
|
Toasts.success('New messages will be encrypted');
|
||||||
},
|
},
|
||||||
generatePublicKey() {
|
generatePublicKey() {
|
||||||
|
<<<<<<< Updated upstream
|
||||||
const dmChannelID = location.pathname.split("/")[3];
|
const dmChannelID = location.pathname.split("/")[3];
|
||||||
|
=======
|
||||||
|
const dmChannelID = DiscordApi.currentChannel.id;
|
||||||
|
>>>>>>> Stashed changes
|
||||||
const publicKeyMessage = `My public key is: \`${E2EE.createKeyExchange(dmChannelID)}\`. Please give me your public key if you haven't done so and add my public key by pasting it in the chat textbox, right clicking the lock icon, and selecting \`Receive Public Key\`.`;
|
const publicKeyMessage = `My public key is: \`${E2EE.createKeyExchange(dmChannelID)}\`. Please give me your public key if you haven't done so and add my public key by pasting it in the chat textbox, right clicking the lock icon, and selecting \`Receive Public Key\`.`;
|
||||||
const chatInput = document.getElementsByClassName('da-textArea')[0];
|
const chatInput = document.getElementsByClassName('da-textArea')[0];
|
||||||
chatInput.value = publicKeyMessage;
|
chatInput.value = publicKeyMessage;
|
||||||
const evt = { currentTarget: chatInput };
|
const evt = { currentTarget: chatInput };
|
||||||
chatInput[Object.keys(chatInput).find(k => k.startsWith('__reactEventHandlers'))].onChange.call(chatInput, evt);
|
chatInput[Object.keys(chatInput).find(k => k.startsWith('__reactEventHandlers'))].onChange.call(chatInput, evt);
|
||||||
|
<<<<<<< Updated upstream
|
||||||
},
|
},
|
||||||
computeSharedSecret() {
|
computeSharedSecret() {
|
||||||
try {
|
try {
|
||||||
|
@ -111,6 +131,25 @@
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
Toasts.error("Invalid public key. Please set up a new key exchange.");
|
Toasts.error("Invalid public key. Please set up a new key exchange.");
|
||||||
console.error(e);
|
console.error(e);
|
||||||
|
=======
|
||||||
|
this.$forceUpdate();
|
||||||
|
},
|
||||||
|
receivePublicKey() {
|
||||||
|
try {
|
||||||
|
const dmChannelID = DiscordApi.currentChannel.id;
|
||||||
|
const chatInput = document.getElementsByClassName('da-textArea')[0];
|
||||||
|
const otherPublicKey = chatInput.value;
|
||||||
|
const secret = E2EE.computeSecret(dmChannelID, otherPublicKey);
|
||||||
|
E2EE.setKey(dmChannelID, secret);
|
||||||
|
chatInput.value = "";
|
||||||
|
const evt = { currentTarget: chatInput };
|
||||||
|
chatInput[Object.keys(chatInput).find(k => k.startsWith('__reactEventHandlers'))].onChange.call(chatInput, evt);
|
||||||
|
Toasts.success("Encryption key has been set for this DM channel.");
|
||||||
|
this.$forceUpdate();
|
||||||
|
} catch (e) {
|
||||||
|
Toasts.error("Invalid public key. Please set up a new key exchange.");
|
||||||
|
console.error(e);
|
||||||
|
>>>>>>> Stashed changes
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
|
@ -80,4 +80,26 @@ export default class Security {
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static createECDH() {
|
||||||
|
return nodecrypto.createECDH('secp521r1');
|
||||||
|
}
|
||||||
|
|
||||||
|
static sha256(text) {
|
||||||
|
const hash = nodecrypto.createHash('sha256');
|
||||||
|
hash.update(text);
|
||||||
|
return hash.digest('base64');
|
||||||
|
}
|
||||||
|
|
||||||
|
static generateECDHKeys(ecdh) {
|
||||||
|
return ecdh.generateKeys('base64');
|
||||||
|
}
|
||||||
|
|
||||||
|
static getECDHPublicKey(ecdh) {
|
||||||
|
return ecdh.getPublicKey('base64');
|
||||||
|
}
|
||||||
|
|
||||||
|
static computeECDHSecret(ecdh, otherPublicKey) {
|
||||||
|
return ecdh.computeSecret(otherPublicKey, 'base64', 'base64');
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -4797,9 +4797,9 @@
|
||||||
"dev": true,
|
"dev": true,
|
||||||
"optional": true,
|
"optional": true,
|
||||||
"requires": {
|
"requires": {
|
||||||
"asynckit": "0.4.0",
|
"asynckit": "^0.4.0",
|
||||||
"combined-stream": "1.0.5",
|
"combined-stream": "^1.0.5",
|
||||||
"mime-types": "2.1.15"
|
"mime-types": "^2.1.12"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"fs.realpath": {
|
"fs.realpath": {
|
||||||
|
@ -4902,8 +4902,8 @@
|
||||||
"dev": true,
|
"dev": true,
|
||||||
"optional": true,
|
"optional": true,
|
||||||
"requires": {
|
"requires": {
|
||||||
"ajv": "4.11.8",
|
"ajv": "^4.9.1",
|
||||||
"har-schema": "1.0.5"
|
"har-schema": "^1.0.5"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"has-unicode": {
|
"has-unicode": {
|
||||||
|
@ -4972,7 +4972,7 @@
|
||||||
"integrity": "sha1-754xOG8DGn8NZDr4L95QxFfvAMs=",
|
"integrity": "sha1-754xOG8DGn8NZDr4L95QxFfvAMs=",
|
||||||
"dev": true,
|
"dev": true,
|
||||||
"requires": {
|
"requires": {
|
||||||
"number-is-nan": "1.0.1"
|
"number-is-nan": "^1.0.0"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"is-typedarray": {
|
"is-typedarray": {
|
||||||
|
@ -5077,7 +5077,7 @@
|
||||||
"integrity": "sha1-pOv1BkCUVpI3uM9wBGd20J/JKu0=",
|
"integrity": "sha1-pOv1BkCUVpI3uM9wBGd20J/JKu0=",
|
||||||
"dev": true,
|
"dev": true,
|
||||||
"requires": {
|
"requires": {
|
||||||
"mime-db": "1.27.0"
|
"mime-db": "~1.27.0"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"minimatch": {
|
"minimatch": {
|
||||||
|
@ -5086,7 +5086,7 @@
|
||||||
"integrity": "sha512-yJHVQEhyqPLUTgt9B83PXu6W3rx4MvvHvSUvToogpwoGDOUQ+yDrR0HRot+yOCdCO7u4hX3pWft6kWBBcqh0UA==",
|
"integrity": "sha512-yJHVQEhyqPLUTgt9B83PXu6W3rx4MvvHvSUvToogpwoGDOUQ+yDrR0HRot+yOCdCO7u4hX3pWft6kWBBcqh0UA==",
|
||||||
"dev": true,
|
"dev": true,
|
||||||
"requires": {
|
"requires": {
|
||||||
"brace-expansion": "1.1.7"
|
"brace-expansion": "^1.1.7"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"minimist": {
|
"minimist": {
|
||||||
|
|
Loading…
Reference in New Issue