Use security module

This commit is contained in:
Jiiks 2018-08-13 12:33:44 +03:00
parent 0c0ebb2ebb
commit 5592a8d376
3 changed files with 46 additions and 15 deletions

View File

@ -17,7 +17,6 @@ import { request } from 'vendor';
import { Utils } from 'common'; import { Utils } from 'common';
import E2EEComponent from './E2EEComponent.vue'; import E2EEComponent from './E2EEComponent.vue';
import E2EEMessageButton from './E2EEMessageButton.vue'; import E2EEMessageButton from './E2EEMessageButton.vue';
import aes256 from 'aes256';
const TEMP_KEY = 'temporarymasterkey'; const TEMP_KEY = 'temporarymasterkey';
let seed; let seed;
@ -31,7 +30,7 @@ export default new class E2EE extends BuiltinModule {
setMaster(key) { setMaster(key) {
seed = Security.randomBytes(); seed = Security.randomBytes();
const newMaster = this.encrypt(seed, key); const newMaster = Security.encrypt(seed, key);
// TODO re-encrypt everything with new master // TODO re-encrypt everything with new master
return (this.master = newMaster); return (this.master = newMaster);
} }
@ -45,17 +44,22 @@ export default new class E2EE extends BuiltinModule {
} }
encrypt(key, content, prefix = '') { encrypt(key, content, prefix = '') {
if (!key) {
// Encrypt something with master
return Security.encrypt(Security.decrypt(seed, this.master), content);
}
if (!content) { if (!content) {
// Get key for current channel and encrypt // Get key for current channel and encrypt
const haveKey = this.getKey(DiscordApi.currentChannel.id); const haveKey = this.getKey(DiscordApi.currentChannel.id);
if (!haveKey) return 'nokey'; if (!haveKey) return 'nokey';
return this.encrypt(this.decrypt(this.decrypt(seed, this.master), haveKey), key); return Security.encrypt(Security.decrypt(seed, [this.master, haveKey]), key);
} }
return prefix + aes256.encrypt(key, content); return prefix + Security.encrypt(key, content);
} }
decrypt(key, content, prefix = '') { decrypt(key, content, prefix = '') {
return aes256.decrypt(key, content.replace(prefix, '')); return Security.decrypt(key, content, prefix);
} }
async createHmac(data) { async createHmac(data) {
@ -71,9 +75,10 @@ export default new class E2EE extends BuiltinModule {
} }
async enabled(e) { async enabled(e) {
window._sec = Security;
seed = Security.randomBytes(); seed = Security.randomBytes();
// TODO Input modal for key // TODO Input modal for key
this.master = this.encrypt(seed, TEMP_KEY); this.master = Security.encrypt(seed, TEMP_KEY);
this.patchMessageContent(); this.patchMessageContent();
const selector = '.' + WebpackModules.getClassName('channelTextArea', 'emojiButton'); const selector = '.' + WebpackModules.getClassName('channelTextArea', 'emojiButton');
@ -106,7 +111,7 @@ export default new class E2EE extends BuiltinModule {
if (!content.startsWith('$:')) return; // Not an encrypted string if (!content.startsWith('$:')) return; // Not an encrypted string
let decrypt; let decrypt;
try { try {
decrypt = this.decrypt(this.decrypt(this.decrypt(seed, this.master), key), component.props.message.content); decrypt = Security.decrypt(seed, [this.master, key, component.props.message.content]);
} catch (err) { return } // Ignore errors such as non empty } catch (err) { return } // Ignore errors such as non empty
component.props.message.bd_encrypted = true; component.props.message.bd_encrypted = true;
@ -121,7 +126,11 @@ export default new class E2EE extends BuiltinModule {
renderMessageContent(component, args, retVal) { renderMessageContent(component, args, retVal) {
if (!component.props.message.bd_encrypted) return; if (!component.props.message.bd_encrypted) return;
try {
retVal.props.children[0].props.children.props.children.props.children.unshift(VueInjector.createReactElement(E2EEMessageButton)); retVal.props.children[0].props.children.props.children.props.children.unshift(VueInjector.createReactElement(E2EEMessageButton));
} catch (err) {
Logger.err('E2EE', err.message);
}
} }
beforeRenderImageWrapper(component, args, retVal) { beforeRenderImageWrapper(component, args, retVal) {
@ -145,7 +154,7 @@ export default new class E2EE extends BuiltinModule {
} }
Logger.info('E2EE', 'Returning encrypted image from cache'); Logger.info('E2EE', 'Returning encrypted image from cache');
try { try {
const decrypt = this.decrypt(this.decrypt(this.decrypt(seed, this.master), haveKey), cached.image); const decrypt = Security.decrypt(seed, [this.master, haveKey, cached.image]);
component.props.className = 'bd-decryptedImage'; component.props.className = 'bd-decryptedImage';
component.props.src = component.props.original = 'data:;base64,' + decrypt; component.props.src = component.props.original = 'data:;base64,' + decrypt;
} catch (err) { return } finally { component.props.readyState = 'READY' } } catch (err) { return } finally { component.props.readyState = 'READY' }
@ -207,7 +216,7 @@ export default new class E2EE extends BuiltinModule {
handleChannelTextAreaSubmit(component, args, retVal) { handleChannelTextAreaSubmit(component, args, retVal) {
const key = this.getKey(DiscordApi.currentChannel.id); const key = this.getKey(DiscordApi.currentChannel.id);
if (!this.encryptNewMessages || !key) return; if (!this.encryptNewMessages || !key) return;
component.props.value = this.encrypt(this.decrypt(this.decrypt(seed, this.master), key), component.props.value, '$:'); component.props.value = Security.encrypt(Security.decrypt(seed, [this.master, key]), component.props.value, '$:');
} }
async disabled(e) { async disabled(e) {

View File

@ -14,16 +14,20 @@ import aes256 from 'aes256';
export default class Security { export default class Security {
static encrypt(key, content, prefix = '') { static encrypt(key, content, prefix = '') {
if (key instanceof Array) return this.deepEncrypt(key, content, prefix); if (key instanceof Array || content instanceof Array) return this.deepEncrypt(key, content, prefix);
return `${prefix}${aes256.encrypt(key, content)}`; return `${prefix}${aes256.encrypt(key, content)}`;
} }
static decrypt(key, content, prefix = '') { static decrypt(key, content, prefix = '') {
if (key instanceof Array) return this.deepDecrypt(key, content, prefix); if (key instanceof Array || content instanceof Array) {
console.log('deep decrypting');
return this.deepDecrypt(key, content, prefix);
}
return aes256.decrypt(key, content.replace(prefix, '')); return aes256.decrypt(key, content.replace(prefix, ''));
} }
static deepEncrypt(keys, content, prefix = '') { static deepEncrypt(keys, content, prefix = '') {
if (content && content instanceof Array) return this.deepEncryptContent(keys, content, prefix);
let encrypt = null; let encrypt = null;
for (const key of keys) { for (const key of keys) {
if (encrypt === null) encrypt = this.encrypt(key, content, prefix); if (encrypt === null) encrypt = this.encrypt(key, content, prefix);
@ -32,7 +36,17 @@ export default class Security {
return encrypt; return encrypt;
} }
static deepEncryptContent(key, contents, prefix = '') {
let encrypt = null;
for (const content of contents) {
if (encrypt === null) encrypt = this.encrypt(key, content, prefix);
else encrypt = this.encrypt(encrypt, content, prefix);
}
return encrypt;
}
static deepDecrypt(keys, content, prefix = '') { static deepDecrypt(keys, content, prefix = '') {
if (content && content instanceof Array) return this.deepDecryptContent(keys, content, prefix);
let decrypt = null; let decrypt = null;
for (const key of keys.reverse()) { for (const key of keys.reverse()) {
if (decrypt === null) decrypt = this.decrypt(key, content, prefix); if (decrypt === null) decrypt = this.decrypt(key, content, prefix);
@ -41,6 +55,15 @@ export default class Security {
return decrypt; return decrypt;
} }
static deepDecryptContent(key, contents, prefix = '') {
let decrypt = null;
for (const content of contents) {
if (decrypt === null) decrypt = this.decrypt(key, content, prefix);
else decrypt = this.decrypt(decrypt, content, prefix);
}
return decrypt;
}
static randomBytes(length = 64, to = 'hex') { static randomBytes(length = 64, to = 'hex') {
return nodecrypto.randomBytes(length).toString(to); return nodecrypto.randomBytes(length).toString(to);
} }

View File

@ -22,13 +22,12 @@
</template> </template>
<script> <script>
import aes256 from 'aes256';
import { DiscordApi } from 'modules'; import { DiscordApi } from 'modules';
import { E2EE } from 'builtin';
export default { export default {
data() { data() {
return { return {
masterKey: 'temporarymasterkey',
valueChanged: false valueChanged: false
} }
}, },
@ -42,7 +41,7 @@
}, },
valueBlur(e) { valueBlur(e) {
if (!this.valueChanged) return; if (!this.valueChanged) return;
const value = aes256.encrypt(this.masterKey, e.target.value); const value = E2EE.encrypt(null, e.target.value);
this.setting.value = { key: this.setting.value.key, value } this.setting.value = { key: this.setting.value.key, value }
this.valueChanged = false; this.valueChanged = false;
}, },