From 5e67bf90c518ba129b47422740264addc657c03f Mon Sep 17 00:00:00 2001 From: Manav Rathi Date: Fri, 16 Aug 2024 10:20:19 +0530 Subject: [PATCH] Outline --- web/packages/base/crypto/libsodium.ts | 67 ++++++++++++++++++++++++++- 1 file changed, 66 insertions(+), 1 deletion(-) diff --git a/web/packages/base/crypto/libsodium.ts b/web/packages/base/crypto/libsodium.ts index f8be7c147c..935374208d 100644 --- a/web/packages/base/crypto/libsodium.ts +++ b/web/packages/base/crypto/libsodium.ts @@ -114,7 +114,72 @@ export async function fromHex(input: string) { } /** - * Encrypt the given data using the given (base64 encoded) key. + * Encrypt the given data using the provided base64 encoded key. + * + * [Note: 3 forms of encryption] + * + * libsodium provides two "high level" encryption patterns: + * + * 1. Authenticated encryption ("secretbox") + * https://doc.libsodium.org/secret-key_cryptography/secretbox + * + * 2. Encrypted streams and file encryption ("secretstream") + * https://doc.libsodium.org/secret-key_cryptography/secretstream + * + * In terms of the underlying algorithm, they are essentially the same. + * + * 1. The secretbox APIs use XSalsa20 with Poly1305 (where XSalsa20 is the + * stream cipher used for encryption, which Poly1305 is the MAC used for + * authentication). + * + * 2. The secretstream APIs use XChaCha20 with Poly1305. + * + * XSalsa20 is a minor variant (predecessor in fact) of XChaCha20. I am not + * aware why libsodium uses both the variants, but they seem to have similar + * characteristics. + * + * These two sets of APIs map functionally map to two different use cases. + * + * 1. If there is a single independent bit of data to encrypt, the secretbox + * APIs fit the bill. + * + * 2. If there is a set of related data to encrypt, e.g. the contents of a file + * where the file is too big to fit into a single message, then the + * secretstream APIs are more appropriate. + * + * However, in our code we have evolved two different use cases for the 2nd + * option. + * + * Say we have an Ente object, specifically an {@link EnteFile}. This holds the + * encryption keys for encrypting the contents of the file that a user wishes to + * upload. The secretstream APIs are the obvious fit, and indeed that's what we + * use, chunking the file if the contents are bigger than some threshold. But if + * the file is small enough, there is no need to chunk, so we also expose a + * function that does streaming encryption, but in "one-shot" mode. + * + * Later on, say we have to encrypt the public magic metadata associated with + * the {@link EnteFile}. Instead of using the secretbox APIs, we just us the + * same streaming encryption that the rest of the file uses, but since such + * metadata is well below the threshold for chunking, it invariably uses the + * "one-shot" mode. + * + * Thus, we have three scenarios: + * + * 1. Box: Using secretbox APIs to encrypt some independent blob of data. + * + * 2. File: Using secretstream APIs in one-shot mode. This is used to encrypt + * data associated to an Ente object (file, collection, entity, etc), when + * the data is small-ish (less than a few MBs). + * + * 3. Stream/Chunks: Using secretstream APIs for encrypting chunks. This is + * used to encrypt the actual content of the files associated with an + * EnteFile object. + * + * "File" is not a term of art, it is just something we use to abbreviate + * "streaming encryption in one-shot mode". + */ +/** + * Encrypt the given file data using the given (base64 encoded) key. * * Use {@link decryptChaChaOneShot} to decrypt the result. *