Semi automated key exchange process

This commit is contained in:
Jiiks 2018-08-14 10:40:33 +03:00
parent 6df9b1e019
commit 5711576a0d
2 changed files with 48 additions and 28 deletions

View File

@ -11,7 +11,7 @@
import { Settings, Cache } from 'modules';
import BuiltinModule from './BuiltinModule';
import { WebpackModules, ReactComponents, MonkeyPatch, Patcher, DiscordApi, Security } from 'modules';
import { VueInjector, Reflection } from 'ui';
import { VueInjector, Reflection, Modals, Toasts } from 'ui';
import { ClientLogger as Logger } from 'common';
import { request } from 'vendor';
import { Utils } from 'common';
@ -23,6 +23,7 @@ const userMentionPattern = new RegExp(`<@!?([0-9]{10,})>`, "g");
const roleMentionPattern = new RegExp(`<@&([0-9]{10,})>`, "g");
const everyoneMentionPattern = new RegExp(`(?:\\s+|^)@everyone(?:\\s+|$)`);
const START_DATE = new Date();
const TEMP_KEY = 'temporarymasterkey';
let seed;
@ -31,6 +32,7 @@ export default new class E2EE extends BuiltinModule {
constructor() {
super();
this.encryptNewMessages = true;
this.ecdhDate = START_DATE;
}
setMaster(key) {
@ -95,7 +97,15 @@ export default new class E2EE extends BuiltinModule {
}
createKeyExchange(dmChannelID) {
console.log(this.ecdhStorage);
if (this.ecdhStorage.hasOwnProperty(dmChannelID)) return null;
this.ecdhStorage[dmChannelID] = Security.createECDH();
setTimeout(() => {
if (this.ecdhStorage.hasOwnProperty(dmChannelID)) {
delete this.ecdhStorage[dmChannelID];
Toasts.error('Key exchange expired!');
}
}, 30000);
return Security.generateECDHKeys(this.ecdhStorage[dmChannelID]);
}
@ -113,6 +123,34 @@ export default new class E2EE extends BuiltinModule {
}
}
async handlePublicKey(component) {
if (!component.props.channel || component.props.channel.type !== 1) return;
if (component.props.message.author.id === DiscordApi.currentUser.id) return;
const channelId = component.props.channel.id;
if (component.props.message.timestamp.diff(this.ecdhDate) < 0) {
this.ecdhDate = new Date();
return;
}
this.ecdhDate = new Date();
const splitContent = component.props.message.content.split('\n');
if (splitContent.length < 5) return;
const [tagstart, begin, key, end, tagend] = splitContent;
try {
await Modals.confirm('Key Exhcange', 'Public key received. Accept?').promise;
// We already sent our key
if (!this.ecdhStorage.hasOwnProperty(channelId)) {
const publicKeyMessage = `\`\`\`\n-----BEGIN PUBLIC KEY-----\n${this.createKeyExchange(channelId)}\n-----END PUBLIC KEY-----\n\`\`\``;
WebpackModules.getModuleByName('DraftActions').saveDraft(channelId, publicKeyMessage);
}
const secret = this.computeSecret(channelId, key);
Toasts.success('Key exchange complete!');
} catch (err) {
return;
}
}
async enabled(e) {
seed = Security.randomBytes();
// TODO Input modal for key
@ -169,6 +207,7 @@ export default new class E2EE extends BuiltinModule {
beforeRenderMessageContent(component) {
if (!component.props || !component.props.message) return;
this.handlePublicKey(component);
const key = this.getKey(component.props.message.channel_id);
if (!key) return; // We don't have a key for this channel
@ -299,32 +338,6 @@ export default new class E2EE extends BuiltinModule {
MonkeyPatch('BD:E2EE', cta.component.prototype).before('handleSubmit', this.handleChannelTextAreaSubmit.bind(this));
}
get ecdh() {
if (!this._ecdh) this._ecdh = {};
return this._ecdh;
}
createKeyExchange(dmChannelID) {
this.ecdh[dmChannelID] = nodecrypto.createECDH('secp521r1');
return this.ecdh[dmChannelID].generateKeys('base64');
}
publicKeyFor(dmChannelID) {
return this.ecdh[dmChannelID].getPublicKey('base64');
}
computeSecret(dmChannelID, otherKey) {
try {
const secret = this.ecdh[dmChannelID].computeSecret(otherKey, 'base64', 'base64');
delete this.ecdh[dmChannelID];
const hash = nodecrypto.createHash('sha256');
hash.update(secret);
return hash.digest('base64');
} catch (e) {
throw e;
}
}
handleChannelTextAreaSubmit(component, args, retVal) {
const key = this.getKey(DiscordApi.currentChannel.id);
if (!this.encryptNewMessages || !key) return;

View File

@ -88,8 +88,15 @@
Toasts.success('New messages will be encrypted');
},
generatePublicKey() {
const publicKeyMessage = `\`\`\`\n-----BEGIN PUBLIC KEY-----\n${E2EE.createKeyExchange(DiscordApi.currentChannel.id)}\n-----END PUBLIC KEY-----\n\`\`\``;
const keyExchange = E2EE.createKeyExchange(DiscordApi.currentChannel.id);
if (keyExchange === null) {
Toasts.warning('Key exchange for channel already in progress!');
return;
}
const publicKeyMessage = `\`\`\`\n-----BEGIN PUBLIC KEY-----\n${keyExchange}\n-----END PUBLIC KEY-----\n\`\`\``;
WebpackModules.getModuleByName('DraftActions').saveDraft(DiscordApi.currentChannel.id, publicKeyMessage);
Toasts.info('Key exhange started. Expires in 30 seconds');
},
receivePublicKey() {
try {