diff --git a/Gruntfile.js b/Gruntfile.js index 697d23011..a90f4a447 100644 --- a/Gruntfile.js +++ b/Gruntfile.js @@ -29,6 +29,9 @@ module.exports = grunt => { 'node_modules/bytebuffer/dist/bytebuffer.js', 'js/curve/curve25519_compiled.js', 'js/curve/curve25519_wrapper.js', + 'node_modules/libsodium/dist/modules/libsodium.js', + 'node_modules/libsodium-wrappers/dist/modules/libsodium-wrappers.js', + 'libtextsecure/libsignal-protocol.js', 'js/util_worker_tasks.js', ]; diff --git a/js/modules/crypto.js b/js/modules/crypto.js index 9b470947d..e7b543372 100644 --- a/js/modules/crypto.js +++ b/js/modules/crypto.js @@ -1,5 +1,5 @@ /* eslint-env browser */ -/* global dcodeIO, libsignal */ +/* global dcodeIO */ /* eslint-disable camelcase, no-bitwise */ diff --git a/js/util_worker_tasks.js b/js/util_worker_tasks.js index 7a6e386e4..b5659a5b7 100644 --- a/js/util_worker_tasks.js +++ b/js/util_worker_tasks.js @@ -1,15 +1,18 @@ -/* global dcodeIO, Internal */ +/* global dcodeIO, Internal, libsignal, sodium */ /* eslint-disable no-console */ /* eslint-disable strict */ const functions = { arrayBufferToStringBase64, fromBase64ToArrayBuffer, + fromHexToArrayBuffer, verifySignature, DecryptAESGCM, deriveSymmetricKey, encryptForPubkey, generateEphemeralKeyPair, + decryptAttachmentBuffer, + encryptAttachmentBuffer, }; onmessage = async e => { @@ -196,3 +199,65 @@ async function DecryptAESGCM(symmetricKey, ivAndCiphertext) { return crypto.subtle.decrypt({ name: 'AES-GCM', iv: nonce }, key, ciphertext); } + +async function getSodium() { + await sodium.ready; + return sodium; +} + +// Uint8Array, ArrayBuffer +async function decryptAttachmentBuffer(encryptingKey, bufferIn) { + const sodium = await getSodium(); + + const header = new Uint8Array( + bufferIn.slice(0, sodium.crypto_secretstream_xchacha20poly1305_HEADERBYTES) + ); + + const encryptedBuffer = new Uint8Array( + bufferIn.slice(sodium.crypto_secretstream_xchacha20poly1305_HEADERBYTES) + ); + try { + /* Decrypt the stream: initializes the state, using the key and a header */ + const state = sodium.crypto_secretstream_xchacha20poly1305_init_pull(header, encryptingKey); + // what if ^ this call fail (? try to load as a unencrypted attachment?) + + const messageTag = sodium.crypto_secretstream_xchacha20poly1305_pull(state, encryptedBuffer); + // we expect the final tag to be there. If not, we might have an issue with this file + // maybe not encrypted locally? + if (messageTag.tag === sodium.crypto_secretstream_xchacha20poly1305_TAG_FINAL) { + return messageTag.message; + } + } catch (e) { + console.warn('Failed to load the file as an encrypted one', e); + } + return new Uint8Array(); +} + +// Uint8Array, ArrayBuffer +async function encryptAttachmentBuffer(encryptingKey, bufferIn) { + const sodium = await getSodium(); + + try { + const uintArrayIn = new Uint8Array(bufferIn); + + /* Set up a new stream: initialize the state and create the header */ + const { state, header } = sodium.crypto_secretstream_xchacha20poly1305_init_push(encryptingKey); + /* Now, encrypt the buffer. */ + const bufferOut = sodium.crypto_secretstream_xchacha20poly1305_push( + state, + uintArrayIn, + null, + sodium.crypto_secretstream_xchacha20poly1305_TAG_FINAL + ); + + const encryptedBufferWithHeader = new Uint8Array(bufferOut.length + header.length); + encryptedBufferWithHeader.set(header); + encryptedBufferWithHeader.set(bufferOut, header.length); + + return { encryptedBufferWithHeader, header }; + } catch (e) { + console.warn('encryptAttachmentBuffer error: ', e); + + return null; + } +} diff --git a/main.js b/main.js index 8d66447ce..c4b84fd7c 100644 --- a/main.js +++ b/main.js @@ -75,13 +75,6 @@ const { createTemplate } = require('./app/menu'); const { installFileHandler, installWebHandler } = require('./app/protocol_filter'); const { installPermissionsHandler } = require('./app/permissions'); -const _sodium = require('libsodium-wrappers'); - -async function getSodium() { - await _sodium.ready; - return _sodium; -} - let appStartInitialSpellcheckSetting = true; async function getSpellCheckSetting() { diff --git a/ts/types/Attachment.ts b/ts/types/Attachment.ts index 3fdfb4bfe..6a0f1cc04 100644 --- a/ts/types/Attachment.ts +++ b/ts/types/Attachment.ts @@ -400,60 +400,18 @@ export const encryptAttachmentBuffer = async (bufferIn: ArrayBuffer) => { if (!isArrayBuffer(bufferIn)) { throw new TypeError("'bufferIn' must be an array buffer"); } - - const uintArrayIn = new Uint8Array(bufferIn); - const sodium = await getSodium(); - const encryptingKey = window.textsecure.storage.get('local_attachment_encrypted_key'); - - /* Set up a new stream: initialize the state and create the header */ - const { state, header } = sodium.crypto_secretstream_xchacha20poly1305_init_push( - fromHexToArray(encryptingKey) - ); - /* Now, encrypt the buffer. */ - const bufferOut = sodium.crypto_secretstream_xchacha20poly1305_push( - state, - uintArrayIn, - null, - sodium.crypto_secretstream_xchacha20poly1305_TAG_FINAL + const encryptingKey = fromHexToArray( + window.textsecure.storage.get('local_attachment_encrypted_key') ); - - const encryptedBufferWithHeader = new Uint8Array(bufferOut.length + header.length); - encryptedBufferWithHeader.set(header); - encryptedBufferWithHeader.set(bufferOut, header.length); - - return { encryptedBufferWithHeader, header }; + return window.callWorker('encryptAttachmentBuffer', encryptingKey, bufferIn); }; export const decryptAttachmentBuffer = async (bufferIn: ArrayBuffer): Promise => { if (!isArrayBuffer(bufferIn)) { throw new TypeError("'bufferIn' must be an array buffer"); } - const sodium = await getSodium(); - const encryptingKey = window.textsecure.storage.get('local_attachment_encrypted_key'); - - const header = new Uint8Array( - bufferIn.slice(0, sodium.crypto_secretstream_xchacha20poly1305_HEADERBYTES) - ); - - const encryptedBuffer = new Uint8Array( - bufferIn.slice(sodium.crypto_secretstream_xchacha20poly1305_HEADERBYTES) + const encryptingKey = fromHexToArray( + window.textsecure.storage.get('local_attachment_encrypted_key') ); - try { - /* Decrypt the stream: initializes the state, using the key and a header */ - const state = sodium.crypto_secretstream_xchacha20poly1305_init_pull( - header, - fromHexToArray(encryptingKey) - ); - // what if ^ this call fail (? try to load as a unencrypted attachment?) - - const messageTag = sodium.crypto_secretstream_xchacha20poly1305_pull(state, encryptedBuffer); - // we expect the final tag to be there. If not, we might have an issue with this file - // maybe not encrypted locally? - if (messageTag.tag === sodium.crypto_secretstream_xchacha20poly1305_TAG_FINAL) { - return messageTag.message; - } - } catch (e) { - window?.log?.warn('Failed to load the file as an encrypted one', e); - } - return new Uint8Array(); + return window.callWorker('decryptAttachmentBuffer', encryptingKey, bufferIn); };