diff --git a/client/src/builtin/E2EE.js b/client/src/builtin/E2EE.js index 4c0fe99f..f390c406 100644 --- a/client/src/builtin/E2EE.js +++ b/client/src/builtin/E2EE.js @@ -10,7 +10,7 @@ import { Settings } from 'modules'; import BuiltinModule from './BuiltinModule'; -import { WebpackModules, ReactComponents, MonkeyPatch, Patcher, DiscordApi } from 'modules'; +import { WebpackModules, ReactComponents, MonkeyPatch, Patcher, DiscordApi, Security } from 'modules'; import { VueInjector, Reflection } from 'ui'; import { ClientLogger as Logger } from 'common'; import { request } from 'vendor'; @@ -20,7 +20,7 @@ import E2EEMessageButton from './E2EEMessageButton.vue'; import aes256 from 'aes256'; let seed = Math.random().toString(36).replace(/[^a-z]+/g, ''); -const decryptCache = []; +const imageCache = []; export default new class E2EE extends BuiltinModule { @@ -46,6 +46,12 @@ export default new class E2EE extends BuiltinModule { } encrypt(key, content, prefix = '') { + if (!content) { + // Get key for current channel and encrypt + const haveKey = this.getKey(DiscordApi.currentChannel.id); + if (!haveKey) return 'nokey'; + return this.encrypt(this.decrypt(this.decrypt(seed, this.master), haveKey), key); + } return prefix + aes256.encrypt(key, content); } @@ -60,6 +66,7 @@ export default new class E2EE extends BuiltinModule { } async enabled(e) { + window.sec = Security; this.patchMessageContent(); const selector = '.' + WebpackModules.getClassName('channelTextArea', 'emojiButton'); const cta = await ReactComponents.getComponent('ChannelTextArea', { selector }); @@ -106,52 +113,49 @@ export default new class E2EE extends BuiltinModule { renderMessageContent(component, args, retVal) { if (!component.props.message.bd_encrypted) return; - - retVal.props.children[0].props.children.props.children.props.children.unshift(VueInjector.createReactElement(E2EEMessageButton, { - message: component.props.message - })); + retVal.props.children[0].props.children.props.children.props.children.unshift(VueInjector.createReactElement(E2EEMessageButton)); } beforeRenderImageWrapper(component, args, retVal) { if (!component.props || !component.props.src) return; if (component.props.decrypting) return; + component.props.decrypting = true; - const src = component.props.src; + const src = component.props.original || component.props.src.split('?')[0]; if (!src.includes('bde2ee')) return; + component.props.className = 'bd-encryptedImage'; - const alreadyDecrypted = decryptCache.find(item => item.src === component.props.src); - if (alreadyDecrypted) { - component.props.className = 'bd-decryptedImage'; - component.props.src = component.props.original = alreadyDecrypted.encodedImage; - component.props.width = alreadyDecrypted.width; - component.props.height = alreadyDecrypted.height; + const haveKey = this.getKey(DiscordApi.currentChannel.id); + if (!haveKey) return; + + const cached = imageCache.find(item => item.src === src); + if (cached) { + Logger.info('E2EE', 'Returning encrypted image from cache'); + try { + const decrypt = this.decrypt(this.decrypt(this.decrypt(seed, this.master), haveKey), cached.image); + component.props.className = 'bd-decryptedImage'; + component.props.src = component.props.original = decrypt; + } catch (err) { return } finally { component.props.readyState = 'READY' } return; } - let resolution = null; - try { - resolution = src.match(/_(.*?)\./)[1].split('x'); - } catch (err) { } - - component.props.className = 'bd-encryptedImage'; - component.props.decrypting = true; - - request.get(component.props.src, { encoding: 'binary' }).then(res => { + component.props.readyState = 'LOADING'; + Logger.info('E2EE', 'Decrypting image: ' + src); + request.get(src, { encoding: 'binary' }).then(res => { const arr = new Uint8Array(new ArrayBuffer(res.length)); for (let i = 0; i < res.length; i++) arr[i] = res.charCodeAt(i); + const aobindex = Utils.aobscan(arr, [73, 69, 78, 68]) + 8; + const sliced = arr.slice(aobindex); + const image = new TextDecoder().decode(sliced); - const sliced = arr.slice(aobindex, arr.length - aobindex); - const encoded = Utils.arrayBufferToBase64(sliced); - const base64enc = 'data:image/png;base64,' + encoded; + imageCache.push({ src, image }); - if (!component || !component.props) return; - if (resolution && resolution.length >= 2) { - component.props.width = parseInt(resolution[0]); - component.props.height = parseInt(resolution[1]); + if (!component || !component.props) { + Logger.warn('E2EE', 'Component seems to be gone'); + return; } - decryptCache.push({ src, width: component.props.width, height: component.props.height, encodedImage: base64enc }); component.props.decrypting = false; component.forceUpdate(); }).catch(err => { diff --git a/client/src/builtin/E2EEComponent.vue b/client/src/builtin/E2EEComponent.vue index 589d5341..e094927b 100644 --- a/client/src/builtin/E2EEComponent.vue +++ b/client/src/builtin/E2EEComponent.vue @@ -10,34 +10,40 @@ + +