diff --git a/src/services/downloadManager.ts b/src/services/downloadManager.ts index 2729f794d..2ff5e329c 100644 --- a/src/services/downloadManager.ts +++ b/src/services/downloadManager.ts @@ -207,7 +207,7 @@ class DownloadManager { ); const fileKey = await cryptoWorker.fromB64(file.key); const { pullState, decryptionChunkSize } = - await cryptoWorker.initDecryption( + await cryptoWorker.initChunkDecryption( decryptionHeader, fileKey ); @@ -229,7 +229,7 @@ class DownloadManager { decryptionChunkSize ); const { decryptedData } = - await cryptoWorker.decryptChunk( + await cryptoWorker.decryptFileChunk( fileData, pullState ); @@ -242,7 +242,7 @@ class DownloadManager { } else { if (data) { const { decryptedData } = - await cryptoWorker.decryptChunk( + await cryptoWorker.decryptFileChunk( data, pullState ); diff --git a/src/services/publicCollectionDownloadManager.ts b/src/services/publicCollectionDownloadManager.ts index 6b130e029..45e3eaaf9 100644 --- a/src/services/publicCollectionDownloadManager.ts +++ b/src/services/publicCollectionDownloadManager.ts @@ -214,7 +214,7 @@ class PublicCollectionDownloadManager { ); const fileKey = await cryptoWorker.fromB64(file.key); const { pullState, decryptionChunkSize } = - await cryptoWorker.initDecryption( + await cryptoWorker.initChunkDecryption( decryptionHeader, fileKey ); @@ -236,7 +236,7 @@ class PublicCollectionDownloadManager { decryptionChunkSize ); const { decryptedData } = - await cryptoWorker.decryptChunk( + await cryptoWorker.decryptFileChunk( fileData, pullState ); @@ -249,7 +249,7 @@ class PublicCollectionDownloadManager { } else { if (data) { const { decryptedData } = - await cryptoWorker.decryptChunk( + await cryptoWorker.decryptFileChunk( data, pullState ); diff --git a/src/services/upload/encryptionService.ts b/src/services/upload/encryptionService.ts index 0779b958c..389deddaa 100644 --- a/src/services/upload/encryptionService.ts +++ b/src/services/upload/encryptionService.ts @@ -1,7 +1,12 @@ +import { Remote } from 'comlink'; import { EncryptionResult } from 'types/crypto'; import { DataStream, isDataStream } from 'types/upload'; +import { DedicatedCryptoWorker } from 'worker/crypto.worker'; -async function encryptFileStream(worker, fileData: DataStream) { +async function encryptFileStream( + worker: Remote, + fileData: DataStream +) { const { stream, chunkCount } = fileData; const fileStreamReader = stream.getReader(); const { key, decryptionHeader, pushState } = @@ -32,7 +37,7 @@ async function encryptFileStream(worker, fileData: DataStream) { } export async function encryptFiledata( - worker, + worker: Remote, filedata: Uint8Array | DataStream ): Promise> { return isDataStream(filedata) diff --git a/src/services/upload/fileService.ts b/src/services/upload/fileService.ts index 965fcadcb..38a32455e 100644 --- a/src/services/upload/fileService.ts +++ b/src/services/upload/fileService.ts @@ -69,7 +69,7 @@ export async function readFile( } export async function extractFileMetadata( - worker, + worker: Remote, parsedMetadataJSONMap: ParsedMetadataJSONMap, collectionID: number, fileTypeInfo: FileTypeInfo, diff --git a/src/services/upload/hashService.tsx b/src/services/upload/hashService.tsx index e0a4766c0..1c943312d 100644 --- a/src/services/upload/hashService.tsx +++ b/src/services/upload/hashService.tsx @@ -1,11 +1,16 @@ +import { Remote } from 'comlink'; import { FILE_READER_CHUNK_SIZE } from 'constants/upload'; import { getFileStream, getElectronFileStream } from 'services/readerService'; import { ElectronFile, DataStream } from 'types/upload'; import { CustomError } from 'utils/error'; import { addLogLine, getFileNameSize } from 'utils/logging'; import { logError } from 'utils/sentry'; +import { DedicatedCryptoWorker } from 'worker/crypto.worker'; -export async function getFileHash(worker, file: File | ElectronFile) { +export async function getFileHash( + worker: Remote, + file: File | ElectronFile +) { try { addLogLine(`getFileHash called for ${getFileNameSize(file)}`); let filedata: DataStream; diff --git a/src/services/upload/livePhotoService.ts b/src/services/upload/livePhotoService.ts index f530692cf..ec602c8a5 100644 --- a/src/services/upload/livePhotoService.ts +++ b/src/services/upload/livePhotoService.ts @@ -18,6 +18,8 @@ import { extractFileMetadata } from './fileService'; import { getFileHash } from './hashService'; import { generateThumbnail } from './thumbnailService'; import uploadCancelService from './uploadCancelService'; +import { Remote } from 'comlink'; +import { DedicatedCryptoWorker } from 'worker/crypto.worker'; interface LivePhotoIdentifier { collectionID: number; @@ -44,7 +46,7 @@ export async function getLivePhotoFileType( } export async function extractLivePhotoMetadata( - worker, + worker: Remote, parsedMetadataJSONMap: ParsedMetadataJSONMap, collectionID: number, fileTypeInfo: FileTypeInfo, diff --git a/src/services/upload/metadataService.ts b/src/services/upload/metadataService.ts index a49a383db..eab20dd45 100644 --- a/src/services/upload/metadataService.ts +++ b/src/services/upload/metadataService.ts @@ -17,6 +17,8 @@ import { tryToParseDateTime, } from 'utils/time'; import { getFileHash } from './hashService'; +import { Remote } from 'comlink'; +import { DedicatedCryptoWorker } from 'worker/crypto.worker'; interface ParsedMetadataJSONWithTitle { title: string; @@ -30,7 +32,7 @@ const NULL_PARSED_METADATA_JSON: ParsedMetadataJSON = { }; export async function extractMetadata( - worker, + worker: Remote, receivedFile: File | ElectronFile, fileTypeInfo: FileTypeInfo ) { diff --git a/src/services/upload/uploadService.ts b/src/services/upload/uploadService.ts index 76d05e6e5..dafe4c2ab 100644 --- a/src/services/upload/uploadService.ts +++ b/src/services/upload/uploadService.ts @@ -105,7 +105,7 @@ class UploadService { } async extractAssetMetadata( - worker, + worker: Remote, { isLivePhoto, file, livePhotoAssets }: UploadAsset, collectionID: number, fileTypeInfo: FileTypeInfo diff --git a/src/utils/crypto/index.ts b/src/utils/crypto/index.ts index 99ac12dec..30fc1d9de 100644 --- a/src/utils/crypto/index.ts +++ b/src/utils/crypto/index.ts @@ -88,8 +88,11 @@ export const saveKeyInSessionStore = async ( key: string, fromDesktop?: boolean ) => { + // the key is encrypted before saving in session storage, to obfuscate it from the browser const cryptoWorker = await ComlinkCryptoWorker.getInstance(); - const sessionKeyAttributes = await cryptoWorker.encryptToB64(key); + const sessionKeyAttributes = await cryptoWorker.generateKeyAndEncryptToB64( + key + ); setKey(keyType, sessionKeyAttributes); if (isElectron() && !fromDesktop) { safeStorageService.setEncryptionKey(key); diff --git a/src/utils/crypto/libsodium.ts b/src/utils/crypto/libsodium.ts index e942c1c7b..769a8bb22 100644 --- a/src/utils/crypto/libsodium.ts +++ b/src/utils/crypto/libsodium.ts @@ -69,7 +69,10 @@ export async function initChunkDecryption(header: Uint8Array, key: Uint8Array) { return { pullState, decryptionChunkSize, tag }; } -export async function decryptChunk(data: Uint8Array, pullState: StateAddress) { +export async function decryptFileChunk( + data: Uint8Array, + pullState: StateAddress +) { await sodium.ready; const pullResult = sodium.crypto_secretstream_xchacha20poly1305_pull( pullState, @@ -79,12 +82,10 @@ export async function decryptChunk(data: Uint8Array, pullState: StateAddress) { return { decryptedData: pullResult.message, newTag }; } -export async function encryptChaChaOneShot(data: Uint8Array, key?: string) { +export async function encryptChaChaOneShot(data: Uint8Array, key: string) { await sodium.ready; - const uintkey: Uint8Array = key - ? await fromB64(key) - : sodium.crypto_secretstream_xchacha20poly1305_keygen(); + const uintkey: Uint8Array = await fromB64(key); const initPushResult = sodium.crypto_secretstream_xchacha20poly1305_init_push(uintkey); const [pushState, header] = [initPushResult.state, initPushResult.header]; @@ -104,12 +105,11 @@ export async function encryptChaChaOneShot(data: Uint8Array, key?: string) { }; } -export async function encryptChaCha(data: Uint8Array, key?: string) { +export async function encryptChaCha(data: Uint8Array) { await sodium.ready; - const uintkey: Uint8Array = key - ? await fromB64(key) - : sodium.crypto_secretstream_xchacha20poly1305_keygen(); + const uintkey: Uint8Array = + sodium.crypto_secretstream_xchacha20poly1305_keygen(); const initPushResult = sodium.crypto_secretstream_xchacha20poly1305_init_push(uintkey); @@ -159,14 +159,14 @@ export async function initChunkEncryption() { pushState, }; } + export async function encryptFileChunk( data: Uint8Array, pushState: sodium.StateAddress, - finalChunk?: boolean + isFinalChunk: boolean ) { await sodium.ready; - - const tag = finalChunk + const tag = isFinalChunk ? sodium.crypto_secretstream_xchacha20poly1305_TAG_FINAL : sodium.crypto_secretstream_xchacha20poly1305_TAG_MESSAGE; const pushResult = sodium.crypto_secretstream_xchacha20poly1305_push( @@ -178,12 +178,9 @@ export async function encryptFileChunk( return pushResult; } -export async function encryptToB64(data: string, key?: string) { +export async function encryptToB64(data: string, key: string) { await sodium.ready; - const encrypted = await encrypt( - await fromB64(data), - key ? await fromB64(key) : null - ); + const encrypted = await encrypt(await fromB64(data), await fromB64(key)); return { encryptedData: await toB64(encrypted.encryptedData), @@ -191,7 +188,13 @@ export async function encryptToB64(data: string, key?: string) { nonce: await toB64(encrypted.nonce), } as B64EncryptionResult; } -export async function encryptUTF8(data: string, key?: string) { + +export async function generateKeyAndEncryptToB64(data: string) { + const key = sodium.crypto_secretbox_keygen(); + return await encryptToB64(data, await toB64(key)); +} + +export async function encryptUTF8(data: string, key: string) { const b64Data = await toB64(await fromString(data)); return await encryptToB64(b64Data, key); } @@ -218,41 +221,22 @@ export async function decryptToUTF8(data: string, nonce: string, key: string) { return sodium.to_string(decrypted); } -export async function encrypt(data: Uint8Array, key?: Uint8Array) { +async function encrypt(data: Uint8Array, key: Uint8Array) { await sodium.ready; - const uintkey: Uint8Array = key || sodium.crypto_secretbox_keygen(); const nonce = sodium.randombytes_buf(sodium.crypto_secretbox_NONCEBYTES); - const encryptedData = sodium.crypto_secretbox_easy(data, nonce, uintkey); + const encryptedData = sodium.crypto_secretbox_easy(data, nonce, key); return { encryptedData, - key: uintkey, + key, nonce, }; } -export async function decrypt( - data: Uint8Array, - nonce: Uint8Array, - key: Uint8Array -) { +async function decrypt(data: Uint8Array, nonce: Uint8Array, key: Uint8Array) { await sodium.ready; return sodium.crypto_secretbox_open_easy(data, nonce, key); } -export async function verifyHash(hash: string, input: string) { - await sodium.ready; - return sodium.crypto_pwhash_str_verify(hash, await fromB64(input)); -} - -export async function hash(input: string) { - await sodium.ready; - return sodium.crypto_pwhash_str( - await fromB64(input), - sodium.crypto_pwhash_OPSLIMIT_SENSITIVE, - sodium.crypto_pwhash_MEMLIMIT_MODERATE - ); -} - export async function initChunkHashing() { await sodium.ready; const hashState = sodium.crypto_generichash_init( diff --git a/src/worker/crypto.worker.ts b/src/worker/crypto.worker.ts index d5d193279..4f318316c 100644 --- a/src/worker/crypto.worker.ts +++ b/src/worker/crypto.worker.ts @@ -49,44 +49,28 @@ export class DedicatedCryptoWorker { return libsodium.encryptChaChaOneShot(fileData, key); } - async encryptFile(fileData: Uint8Array, key: string) { - return libsodium.encryptChaCha(fileData, key); + async encryptFile(fileData: Uint8Array) { + return libsodium.encryptChaCha(fileData); } async encryptFileChunk( data: Uint8Array, pushState: StateAddress, - finalChunk: boolean + isFinalChunk: boolean ) { - return libsodium.encryptFileChunk(data, pushState, finalChunk); + return libsodium.encryptFileChunk(data, pushState, isFinalChunk); } async initChunkEncryption() { return libsodium.initChunkEncryption(); } - async initDecryption(header: Uint8Array, key: Uint8Array) { + async initChunkDecryption(header: Uint8Array, key: Uint8Array) { return libsodium.initChunkDecryption(header, key); } - async decryptChunk(fileData: Uint8Array, pullState: StateAddress) { - return libsodium.decryptChunk(fileData, pullState); - } - - async encrypt(data: Uint8Array, key: Uint8Array) { - return libsodium.encrypt(data, key); - } - - async decrypt(data: Uint8Array, nonce: Uint8Array, key: Uint8Array) { - return libsodium.decrypt(data, nonce, key); - } - - async hash(input: string) { - return libsodium.hash(input); - } - - async verifyHash(hash: string, input: string) { - return libsodium.verifyHash(hash, input); + async decryptFileChunk(fileData: Uint8Array, pullState: StateAddress) { + return libsodium.decryptFileChunk(fileData, pullState); } async initChunkHashing() { @@ -126,10 +110,14 @@ export class DedicatedCryptoWorker { return libsodium.decryptToUTF8(data, nonce, key); } - async encryptToB64(data: string, key?: string) { + async encryptToB64(data: string, key: string) { return libsodium.encryptToB64(data, key); } + async generateKeyAndEncryptToB64(data: string) { + return libsodium.generateKeyAndEncryptToB64(data); + } + async encryptUTF8(data: string, key: string) { return libsodium.encryptUTF8(data, key); }