More docs etc

This commit is contained in:
Manav Rathi
2024-08-05 13:28:02 +05:30
parent 13f31a7d09
commit bd42650b9e
2 changed files with 92 additions and 61 deletions

View File

@@ -26,43 +26,14 @@ import * as libsodium from "@ente/shared/crypto/internal/libsodium";
*
* See {@link encryptChaChaOneShot} for the implementation details.
*
* @param data The data (bytes) to encrypt.
* @param data A {@link Uint8Array} containing the bytes to encrypt.
*
* @param keyB64 Base64 encoded string containing the encryption key. This is
* expected to the key of the file with which {@link data} is associated.
*
* @returns The encrypted data and the (Base64 encoded) decryption header.
*/
export const encryptFileAssociatedData = (data: Uint8Array, keyB64: string) =>
libsodium.encryptChaChaOneShot(data, keyB64);
/**
* Decrypt arbitrary data associated with a file using the file's key.
*
* This is the sibling of {@link encryptFileAssociatedData}.
*
* @param encryptedData A {@link Base64 encoded string containing the encrypted data.
*
* @param headerB64 Base64 encoded string containing the decryption header
* produced during encryption.
*
* @param keyB64 Base64 encoded string containing the encryption key. This is
* expected to be the key of the file with which {@link encryptedDataB64} is
* associated.
*
* @returns The decrypted metadata bytes.
*/
export const decryptFileAssociatedDataFromB64 = async (
encryptedDataB64: string,
decryptionHeaderB64: string,
keyB64: string,
) =>
libsodium.decryptChaChaOneShot(
await libsodium.fromB64(encryptedDataB64),
await libsodium.fromB64(decryptionHeaderB64),
keyB64,
);
export const encryptFileAssociatedData = libsodium.encryptChaChaOneShot;
/**
* A variant of {@link encryptFileAssociatedData} that returns the Base64
@@ -85,6 +56,51 @@ export const encryptFileAssociatedDataToB64 = async (
};
};
/**
* A variant of {@link encryptFileAssociatedData} tailored for encrypting the
* various metadata fields associated with a file.
*
* Instead of raw bytes, it takes as input an arbitrary JSON object which it
* encodes into a string, and encrypts that. Also, instead of returning the raw
* encrypted bytes, it returns their Base64 encoded string representation.
*/
export const encryptFileMetadata = async (
// Arbitrary JSON really, but since TypeScript doesn't have a native JSON
// type use a string Record as a standin replacement.
metadata: Record<string, unknown>,
keyB64: string,
) => {
const textEncoder = new TextEncoder();
const encodedMetadata = textEncoder.encode(JSON.stringify(metadata));
const { encryptedData, decryptionHeaderB64 } =
await encryptFileAssociatedData(encodedMetadata, keyB64);
return {
encryptedData: await libsodium.toB64(encryptedData),
decryptionHeaderB64,
};
};
/**
* Decrypt arbitrary data associated with a file using the file's key.
*
* This is the sibling of {@link encryptFileAssociatedData}.
*
* See {@link decryptChaChaOneShot2} for the implementation details.
*
* @param encryptedData A {@link Uint8Array} containing the bytes to decrypt.
*
* @param header A Base64 string containing the bytes of the decryption header
* that was produced during encryption.
*
* @param keyB64 Base64 encoded string containing the encryption key. This is
* expected to be the key of the file with which {@link encryptedDataB64} is
* associated.
*
* @returns The decrypted bytes.
*/
export const decryptFileAssociatedData = libsodium.decryptChaChaOneShot2;
/**
* Decrypt arbitrary data associated with a file using the file's key.
*
@@ -111,28 +127,3 @@ export const decryptFileAssociatedDataFromB64 = async (
await libsodium.fromB64(decryptionHeaderB64),
keyB64,
);
/**
* A variant of {@link encryptFileAssociatedData} tailored for encrypting the
* various metadata fields associated with a file.
*
* Instead of raw bytes, it takes as input an arbitrary JSON object which it
* encodes into a string, and encrypts that. Also, instead of returning the raw
* encrypted bytes, it returns their Base64 encoded string representation.
*/
export const encryptFileMetadata = async (
// Arbitrary JSON really, but since TypeScript doesn't have a native JSON
// type use a string Record as a standin replacement.
metadata: Record<string, unknown>,
keyB64: string,
) => {
const textEncoder = new TextEncoder();
const encodedMetadata = textEncoder.encode(JSON.stringify(metadata));
const { encryptedData, decryptionHeaderB64 } =
await encryptFileAssociatedData(encodedMetadata, keyB64);
return {
encryptedData: await libsodium.toB64(encryptedData),
decryptionHeaderB64,
};
};

View File

@@ -113,6 +113,10 @@ export async function fromHex(input: string) {
/**
* Encrypt the given {@link data} using the given (Base64 encoded) key.
*
* Use {@link decryptChaChaOneShot} to decrypt the result.
*
* [Note: Salsa and ChaCha]
*
* This uses the same stream encryption algorithm (XChaCha20 stream cipher with
* Poly1305 MAC authentication) that we use for encrypting other streams, in
* particular the actual file's contents.
@@ -127,6 +131,9 @@ export async function fromHex(input: string) {
* encryption, which we do use in other places where we need to one shot
* encryption of independent bits of data.
*
* These secretbox APIs use XSalsa20 with Poly1305. XSalsa20 is a minor variant
* (predecessor in fact) of XChaCha20.
*
* See: https://doc.libsodium.org/secret-key_cryptography/secretbox
*
* The difference here is that this function is meant to used for data
@@ -242,15 +249,48 @@ export async function encryptFileChunk(
return pushResult;
}
export async function decryptChaChaOneShot(
/**
* Decrypt the result of {@link encryptChaChaOneShot}.
*
* @param encryptedData A {@link Uint8Array} containing the bytes to decrypt.
*
* @param header A Base64 string containing the bytes of the decryption header
* that was produced during encryption.
*
* @param keyB64 The Base64 string containing the key that was used to encrypt
* the data.
*
* @returns The decrypted bytes.
*
* @returns The decrypted metadata bytes.
*/
export const decryptChaChaOneShot2 = async (
encryptedData: Uint8Array,
headerB64: string,
keyB64: string,
) => {
await sodium.ready;
const pullState = sodium.crypto_secretstream_xchacha20poly1305_init_pull(
await fromB64(headerB64),
await fromB64(keyB64),
);
const pullResult = sodium.crypto_secretstream_xchacha20poly1305_pull(
pullState,
encryptedData,
null,
);
return pullResult.message;
};
export const decryptChaChaOneShot = async (
data: Uint8Array,
header: Uint8Array,
key: string,
) {
keyB64: string,
) => {
await sodium.ready;
const pullState = sodium.crypto_secretstream_xchacha20poly1305_init_pull(
header,
await fromB64(key),
await fromB64(keyB64),
);
const pullResult = sodium.crypto_secretstream_xchacha20poly1305_pull(
pullState,
@@ -258,7 +298,7 @@ export async function decryptChaChaOneShot(
null,
);
return pullResult.message;
}
};
export const decryptChaCha = async (
data: Uint8Array,