diff --git a/web/packages/base/crypto/ente-impl.ts b/web/packages/base/crypto/ente-impl.ts new file mode 100644 index 0000000000..e8580ae008 --- /dev/null +++ b/web/packages/base/crypto/ente-impl.ts @@ -0,0 +1,74 @@ +/** Careful when adding add other imports! */ +import * as libsodium from "./libsodium"; + +export const encryptAssociatedDataI = libsodium.encryptChaChaOneShot; + +export const encryptThumbnailI = encryptAssociatedDataI; + +export const encryptFileEmbeddingI = async ( + data: Uint8Array, + keyB64: string, +) => { + const { encryptedData, decryptionHeaderB64 } = await encryptAssociatedDataI( + data, + keyB64, + ); + return { + encryptedDataB64: await libsodium.toB64(encryptedData), + decryptionHeaderB64, + }; +}; + +export const encryptMetadataI = async (metadata: unknown, keyB64: string) => { + const encodedMetadata = new TextEncoder().encode(JSON.stringify(metadata)); + + const { encryptedData, decryptionHeaderB64 } = await encryptAssociatedDataI( + encodedMetadata, + keyB64, + ); + return { + encryptedDataB64: await libsodium.toB64(encryptedData), + decryptionHeaderB64, + }; +}; + +export const decryptAssociatedDataI = libsodium.decryptChaChaOneShot; + +export const decryptThumbnailI = decryptAssociatedDataI; + +export const decryptFileEmbeddingI = async ( + encryptedDataB64: string, + decryptionHeaderB64: string, + keyB64: string, +) => + decryptAssociatedDataI( + await libsodium.fromB64(encryptedDataB64), + decryptionHeaderB64, + keyB64, + ); + +export const decryptMetadataI = async ( + encryptedDataB64: string, + decryptionHeaderB64: string, + keyB64: string, +) => + JSON.parse( + new TextDecoder().decode( + await decryptMetadataBytesI( + encryptedDataB64, + decryptionHeaderB64, + keyB64, + ), + ), + ) as unknown; + +export const decryptMetadataBytesI = async ( + encryptedDataB64: string, + decryptionHeaderB64: string, + keyB64: string, +) => + await decryptAssociatedDataI( + await libsodium.fromB64(encryptedDataB64), + decryptionHeaderB64, + keyB64, + ); diff --git a/web/packages/base/crypto/ente.ts b/web/packages/base/crypto/ente.ts index 74737db1db..c5ebca7eab 100644 --- a/web/packages/base/crypto/ente.ts +++ b/web/packages/base/crypto/ente.ts @@ -36,13 +36,20 @@ * function in a shared "crypto" web worker (which then invokes the * implementation, but this time in the context of a web worker). * + * To avoid a circular dependency during webpack imports, we need to keep the + * implementation functions in a separate file (ente-impl.ts). This is a bit + * unfortunate, since it makes them harder to read and reason about (since their + * documentation and parameter names are all in ente.ts). + * * Some older code directly calls the functions in the shared crypto.worker.ts, * but that should be avoided since it makes the code not behave the way we want * when we're already in a web worker. There are exceptions to this * recommendation though (in circumstances where we create more crypto workers * instead of using the shared one). */ +import { assertionFailed } from "../assert"; import { inWorker } from "../env"; +import * as ei from "./ente-impl"; import * as libsodium from "./libsodium"; import { sharedCryptoWorker } from "./worker"; @@ -182,12 +189,14 @@ export const decryptFileEmbedding = async ( encryptedDataB64: string, decryptionHeaderB64: string, keyB64: string, -) => - decryptAssociatedData( - await libsodium.fromB64(encryptedDataB64), +) => { + if (!inWorker()) assertionFailed("Only implemented for use in web workers"); + return ei.decryptFileEmbeddingI( + encryptedDataB64, decryptionHeaderB64, keyB64, ); +}; /** * Decrypt the metadata associated with an Ente object (file, collection or @@ -212,15 +221,11 @@ export const decryptMetadata = async ( decryptionHeaderB64: string, keyB64: string, ) => - JSON.parse( - new TextDecoder().decode( - await decryptMetadataBytes( - encryptedDataB64, - decryptionHeaderB64, - keyB64, - ), - ), - ) as unknown; + inWorker() + ? ei.decryptMetadataI(encryptedDataB64, decryptionHeaderB64, keyB64) + : sharedCryptoWorker().then((w) => + w.decryptMetadata(encryptedDataB64, decryptionHeaderB64, keyB64), + ); /** * A variant of {@link decryptMetadata} that does not attempt to parse the @@ -233,22 +238,15 @@ export const decryptMetadataBytes = ( keyB64: string, ) => inWorker() - ? decryptMetadataBytesI(encryptedDataB64, decryptionHeaderB64, keyB64) - : sharedCryptoWorker().then((cw) => - cw.decryptMetadataBytes( + ? ei.decryptMetadataBytesI( + encryptedDataB64, + decryptionHeaderB64, + keyB64, + ) + : sharedCryptoWorker().then((w) => + w.decryptMetadataBytes( encryptedDataB64, decryptionHeaderB64, keyB64, ), ); - -export const decryptMetadataBytesI = async ( - encryptedDataB64: string, - decryptionHeaderB64: string, - keyB64: string, -) => - await decryptAssociatedData( - await libsodium.fromB64(encryptedDataB64), - decryptionHeaderB64, - keyB64, - ); diff --git a/web/packages/base/crypto/worker/worker.ts b/web/packages/base/crypto/worker/worker.ts index 298c7150d0..ab775b6a61 100644 --- a/web/packages/base/crypto/worker/worker.ts +++ b/web/packages/base/crypto/worker/worker.ts @@ -1,6 +1,6 @@ import { expose } from "comlink"; import type { StateAddress } from "libsodium-wrappers"; -import * as ente from "../ente"; +import * as ei from "../ente-impl"; import * as libsodium from "../libsodium"; /** @@ -12,44 +12,32 @@ import * as libsodium from "../libsodium"; * Note: Keep these methods logic free. They are meant to be trivial proxies. */ export class CryptoWorker { - // TODO: -- AUDIT BELOW -- - - async decryptThumbnail( - encryptedData: Uint8Array, - headerB64: string, - keyB64: string, - ) { - return ente.decryptThumbnail(encryptedData, headerB64, keyB64); + async encryptThumbnail(a: Uint8Array, b: string) { + return ei.encryptThumbnailI(a, b); } - async decryptMetadata( - encryptedDataB64: string, - decryptionHeaderB64: string, - keyB64: string, - ) { - return ente.decryptMetadata( - encryptedDataB64, - decryptionHeaderB64, - keyB64, - ); + async encryptMetadata(a: unknown, b: string) { + return ei.encryptMetadataI(a, b); + } + + async decryptThumbnail(a: Uint8Array, b: string, c: string) { + return ei.decryptThumbnailI(a, b, c); + } + + async decryptMetadata(a: string, b: string, c: string) { + return ei.decryptMetadataI(a, b, c); } async decryptMetadataBytes(a: string, b: string, c: string) { - return ente.decryptMetadataBytesI(a, b, c); + return ei.decryptMetadataBytesI(a, b, c); } + // TODO: -- AUDIT BELOW -- + async decryptFile(fileData: Uint8Array, header: Uint8Array, key: string) { return libsodium.decryptChaCha(fileData, header, key); } - async encryptThumbnail(data: Uint8Array, keyB64: string) { - return ente.encryptThumbnail(data, keyB64); - } - - async encryptMetadata(metadata: unknown, keyB64: string) { - return ente.encryptMetadata(metadata, keyB64); - } - async encryptFile(fileData: Uint8Array) { return libsodium.encryptChaCha(fileData); }