123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422 |
- import { CustomError } from "@ente/shared/error";
- import sodium, { StateAddress } from "libsodium-wrappers";
- import { ENCRYPTION_CHUNK_SIZE } from "../constants";
- import { B64EncryptionResult } from "../types";
- export async function decryptChaChaOneShot(
- data: Uint8Array,
- header: Uint8Array,
- key: string,
- ) {
- await sodium.ready;
- const pullState = sodium.crypto_secretstream_xchacha20poly1305_init_pull(
- header,
- await fromB64(key),
- );
- const pullResult = sodium.crypto_secretstream_xchacha20poly1305_pull(
- pullState,
- data,
- null,
- );
- return pullResult.message;
- }
- export async function decryptChaCha(
- data: Uint8Array,
- header: Uint8Array,
- key: string,
- ) {
- await sodium.ready;
- const pullState = sodium.crypto_secretstream_xchacha20poly1305_init_pull(
- header,
- await fromB64(key),
- );
- const decryptionChunkSize =
- ENCRYPTION_CHUNK_SIZE +
- sodium.crypto_secretstream_xchacha20poly1305_ABYTES;
- let bytesRead = 0;
- const decryptedData = [];
- let tag = sodium.crypto_secretstream_xchacha20poly1305_TAG_MESSAGE;
- while (tag !== sodium.crypto_secretstream_xchacha20poly1305_TAG_FINAL) {
- let chunkSize = decryptionChunkSize;
- if (bytesRead + chunkSize > data.length) {
- chunkSize = data.length - bytesRead;
- }
- const buffer = data.slice(bytesRead, bytesRead + chunkSize);
- const pullResult = sodium.crypto_secretstream_xchacha20poly1305_pull(
- pullState,
- buffer,
- );
- if (!pullResult.message) {
- throw new Error(CustomError.PROCESSING_FAILED);
- }
- for (let index = 0; index < pullResult.message.length; index++) {
- decryptedData.push(pullResult.message[index]);
- }
- tag = pullResult.tag;
- bytesRead += chunkSize;
- }
- return Uint8Array.from(decryptedData);
- }
- export async function initChunkDecryption(header: Uint8Array, key: Uint8Array) {
- await sodium.ready;
- const pullState = sodium.crypto_secretstream_xchacha20poly1305_init_pull(
- header,
- key,
- );
- const decryptionChunkSize =
- ENCRYPTION_CHUNK_SIZE +
- sodium.crypto_secretstream_xchacha20poly1305_ABYTES;
- const tag = sodium.crypto_secretstream_xchacha20poly1305_TAG_MESSAGE;
- return { pullState, decryptionChunkSize, tag };
- }
- export async function decryptFileChunk(
- data: Uint8Array,
- pullState: StateAddress,
- ) {
- await sodium.ready;
- const pullResult = sodium.crypto_secretstream_xchacha20poly1305_pull(
- pullState,
- data,
- );
- if (!pullResult.message) {
- throw new Error(CustomError.PROCESSING_FAILED);
- }
- const newTag = pullResult.tag;
- return { decryptedData: pullResult.message, newTag };
- }
- export async function encryptChaChaOneShot(data: Uint8Array, key: string) {
- await sodium.ready;
- const uintkey: Uint8Array = await fromB64(key);
- const initPushResult =
- sodium.crypto_secretstream_xchacha20poly1305_init_push(uintkey);
- const [pushState, header] = [initPushResult.state, initPushResult.header];
- const pushResult = sodium.crypto_secretstream_xchacha20poly1305_push(
- pushState,
- data,
- null,
- sodium.crypto_secretstream_xchacha20poly1305_TAG_FINAL,
- );
- return {
- key: await toB64(uintkey),
- file: {
- encryptedData: pushResult,
- decryptionHeader: await toB64(header),
- },
- };
- }
- export async function encryptChaCha(data: Uint8Array) {
- await sodium.ready;
- const uintkey: Uint8Array =
- sodium.crypto_secretstream_xchacha20poly1305_keygen();
- const initPushResult =
- sodium.crypto_secretstream_xchacha20poly1305_init_push(uintkey);
- const [pushState, header] = [initPushResult.state, initPushResult.header];
- let bytesRead = 0;
- let tag = sodium.crypto_secretstream_xchacha20poly1305_TAG_MESSAGE;
- const encryptedData = [];
- while (tag !== sodium.crypto_secretstream_xchacha20poly1305_TAG_FINAL) {
- let chunkSize = ENCRYPTION_CHUNK_SIZE;
- if (bytesRead + chunkSize >= data.length) {
- chunkSize = data.length - bytesRead;
- tag = sodium.crypto_secretstream_xchacha20poly1305_TAG_FINAL;
- }
- const buffer = data.slice(bytesRead, bytesRead + chunkSize);
- bytesRead += chunkSize;
- const pushResult = sodium.crypto_secretstream_xchacha20poly1305_push(
- pushState,
- buffer,
- null,
- tag,
- );
- for (let index = 0; index < pushResult.length; index++) {
- encryptedData.push(pushResult[index]);
- }
- }
- return {
- key: await toB64(uintkey),
- file: {
- encryptedData: new Uint8Array(encryptedData),
- decryptionHeader: await toB64(header),
- },
- };
- }
- export async function initChunkEncryption() {
- await sodium.ready;
- const key = sodium.crypto_secretstream_xchacha20poly1305_keygen();
- const initPushResult =
- sodium.crypto_secretstream_xchacha20poly1305_init_push(key);
- const [pushState, header] = [initPushResult.state, initPushResult.header];
- return {
- key: await toB64(key),
- decryptionHeader: await toB64(header),
- pushState,
- };
- }
- export async function encryptFileChunk(
- data: Uint8Array,
- pushState: sodium.StateAddress,
- isFinalChunk: boolean,
- ) {
- await sodium.ready;
- const tag = isFinalChunk
- ? sodium.crypto_secretstream_xchacha20poly1305_TAG_FINAL
- : sodium.crypto_secretstream_xchacha20poly1305_TAG_MESSAGE;
- const pushResult = sodium.crypto_secretstream_xchacha20poly1305_push(
- pushState,
- data,
- null,
- tag,
- );
- return pushResult;
- }
- export async function encryptToB64(data: string, key: string) {
- await sodium.ready;
- const encrypted = await encrypt(await fromB64(data), await fromB64(key));
- return {
- encryptedData: await toB64(encrypted.encryptedData),
- key: await toB64(encrypted.key),
- nonce: await toB64(encrypted.nonce),
- } as B64EncryptionResult;
- }
- export async function generateKeyAndEncryptToB64(data: string) {
- await sodium.ready;
- 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 fromUTF8(data));
- return await encryptToB64(b64Data, key);
- }
- export async function decryptB64(data: string, nonce: string, key: string) {
- await sodium.ready;
- const decrypted = await decrypt(
- await fromB64(data),
- await fromB64(nonce),
- await fromB64(key),
- );
- return await toB64(decrypted);
- }
- export async function decryptToUTF8(data: string, nonce: string, key: string) {
- await sodium.ready;
- const decrypted = await decrypt(
- await fromB64(data),
- await fromB64(nonce),
- await fromB64(key),
- );
- return sodium.to_string(decrypted);
- }
- async function encrypt(data: Uint8Array, key: Uint8Array) {
- await sodium.ready;
- const nonce = sodium.randombytes_buf(sodium.crypto_secretbox_NONCEBYTES);
- const encryptedData = sodium.crypto_secretbox_easy(data, nonce, key);
- return {
- encryptedData,
- key,
- nonce,
- };
- }
- async function decrypt(data: Uint8Array, nonce: Uint8Array, key: Uint8Array) {
- await sodium.ready;
- return sodium.crypto_secretbox_open_easy(data, nonce, key);
- }
- export async function initChunkHashing() {
- await sodium.ready;
- const hashState = sodium.crypto_generichash_init(
- null,
- sodium.crypto_generichash_BYTES_MAX,
- );
- return hashState;
- }
- export async function hashFileChunk(
- hashState: sodium.StateAddress,
- chunk: Uint8Array,
- ) {
- await sodium.ready;
- sodium.crypto_generichash_update(hashState, chunk);
- }
- export async function completeChunkHashing(hashState: sodium.StateAddress) {
- await sodium.ready;
- const hash = sodium.crypto_generichash_final(
- hashState,
- sodium.crypto_generichash_BYTES_MAX,
- );
- const hashString = toB64(hash);
- return hashString;
- }
- export async function deriveKey(
- passphrase: string,
- salt: string,
- opsLimit: number,
- memLimit: number,
- ) {
- await sodium.ready;
- return await toB64(
- sodium.crypto_pwhash(
- sodium.crypto_secretbox_KEYBYTES,
- await fromUTF8(passphrase),
- await fromB64(salt),
- opsLimit,
- memLimit,
- sodium.crypto_pwhash_ALG_ARGON2ID13,
- ),
- );
- }
- export async function deriveSensitiveKey(passphrase: string, salt: string) {
- await sodium.ready;
- const minMemLimit = sodium.crypto_pwhash_MEMLIMIT_MIN;
- let opsLimit = sodium.crypto_pwhash_OPSLIMIT_SENSITIVE;
- let memLimit = sodium.crypto_pwhash_MEMLIMIT_SENSITIVE;
- while (memLimit > minMemLimit) {
- try {
- const key = await deriveKey(passphrase, salt, opsLimit, memLimit);
- return {
- key,
- opsLimit,
- memLimit,
- };
- } catch (e) {
- opsLimit *= 2;
- memLimit /= 2;
- }
- }
- }
- export async function deriveInteractiveKey(passphrase: string, salt: string) {
- await sodium.ready;
- const key = await toB64(
- sodium.crypto_pwhash(
- sodium.crypto_secretbox_KEYBYTES,
- await fromUTF8(passphrase),
- await fromB64(salt),
- sodium.crypto_pwhash_OPSLIMIT_INTERACTIVE,
- sodium.crypto_pwhash_MEMLIMIT_INTERACTIVE,
- sodium.crypto_pwhash_ALG_ARGON2ID13,
- ),
- );
- return {
- key,
- opsLimit: sodium.crypto_pwhash_OPSLIMIT_INTERACTIVE,
- memLimit: sodium.crypto_pwhash_MEMLIMIT_INTERACTIVE,
- };
- }
- export async function generateEncryptionKey() {
- await sodium.ready;
- return await toB64(sodium.crypto_kdf_keygen());
- }
- export async function generateSaltToDeriveKey() {
- await sodium.ready;
- return await toB64(sodium.randombytes_buf(sodium.crypto_pwhash_SALTBYTES));
- }
- export async function generateKeyPair() {
- await sodium.ready;
- const keyPair: sodium.KeyPair = sodium.crypto_box_keypair();
- return {
- privateKey: await toB64(keyPair.privateKey),
- publicKey: await toB64(keyPair.publicKey),
- };
- }
- export async function boxSealOpen(
- input: string,
- publicKey: string,
- secretKey: string,
- ) {
- await sodium.ready;
- return await toB64(
- sodium.crypto_box_seal_open(
- await fromB64(input),
- await fromB64(publicKey),
- await fromB64(secretKey),
- ),
- );
- }
- export async function boxSeal(input: string, publicKey: string) {
- await sodium.ready;
- return await toB64(
- sodium.crypto_box_seal(await fromB64(input), await fromB64(publicKey)),
- );
- }
- export async function generateSubKey(
- key: string,
- subKeyLength: number,
- subKeyID: number,
- context: string,
- ) {
- await sodium.ready;
- return await toB64(
- sodium.crypto_kdf_derive_from_key(
- subKeyLength,
- subKeyID,
- context,
- await fromB64(key),
- ),
- );
- }
- export async function fromB64(input: string) {
- await sodium.ready;
- return sodium.from_base64(input, sodium.base64_variants.ORIGINAL);
- }
- export async function toB64(input: Uint8Array) {
- await sodium.ready;
- return sodium.to_base64(input, sodium.base64_variants.ORIGINAL);
- }
- export async function toURLSafeB64(input: Uint8Array) {
- await sodium.ready;
- return sodium.to_base64(input, sodium.base64_variants.URLSAFE);
- }
- export async function fromUTF8(input: string) {
- await sodium.ready;
- return sodium.from_string(input);
- }
- export async function toUTF8(input: string) {
- await sodium.ready;
- return sodium.to_string(await fromB64(input));
- }
- export async function toHex(input: string) {
- await sodium.ready;
- return sodium.to_hex(await fromB64(input));
- }
- export async function fromHex(input: string) {
- await sodium.ready;
- return await toB64(sodium.from_hex(input));
- }
|