mirror of
https://github.com/streetwriters/notesnook.git
synced 2026-02-23 19:49:56 +01:00
crypto: add support for bulk decryption
This commit is contained in:
committed by
Abdullah Atta
parent
84004c4d73
commit
f8ce23aa11
@@ -23,7 +23,8 @@ import {
|
||||
OutputFormat,
|
||||
Cipher,
|
||||
EncryptionKey,
|
||||
INNCrypto
|
||||
INNCrypto,
|
||||
Output
|
||||
} from "@notesnook/crypto";
|
||||
import { NNCryptoWorkerModule } from "./src/worker";
|
||||
import { wrap } from "comlink";
|
||||
@@ -38,6 +39,8 @@ export class NNCryptoWorker implements INNCrypto {
|
||||
if (!this.worker) throw new Error("worker cannot be undefined.");
|
||||
if (this.isReady) return;
|
||||
|
||||
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
|
||||
// @ts-ignore
|
||||
this.workermodule = wrap<NNCryptoWorkerModule>(this.worker);
|
||||
// this.workermodule = await spawn<NNCryptoWorkerModule>(this.worker);
|
||||
this.isReady = true;
|
||||
@@ -45,7 +48,7 @@ export class NNCryptoWorker implements INNCrypto {
|
||||
|
||||
async encrypt(
|
||||
key: SerializedKey,
|
||||
plaintext: Plaintext,
|
||||
plaintext: Plaintext<OutputFormat>,
|
||||
outputFormat: OutputFormat = "uint8array"
|
||||
): Promise<Cipher> {
|
||||
await this.init();
|
||||
@@ -54,17 +57,28 @@ export class NNCryptoWorker implements INNCrypto {
|
||||
return this.workermodule.encrypt(key, plaintext, outputFormat);
|
||||
}
|
||||
|
||||
async decrypt(
|
||||
async decrypt<TOutputFormat extends OutputFormat>(
|
||||
key: SerializedKey,
|
||||
cipherData: Cipher,
|
||||
outputFormat: OutputFormat = "text"
|
||||
): Promise<Plaintext> {
|
||||
outputFormat: TOutputFormat = "text" as TOutputFormat
|
||||
): Promise<Output<TOutputFormat>> {
|
||||
await this.init();
|
||||
if (!this.workermodule) throw new Error("Worker module is not ready.");
|
||||
|
||||
return this.workermodule.decrypt(key, cipherData, outputFormat);
|
||||
}
|
||||
|
||||
async decryptMulti<TOutputFormat extends OutputFormat>(
|
||||
key: SerializedKey,
|
||||
items: Cipher[],
|
||||
outputFormat: TOutputFormat = "text" as TOutputFormat
|
||||
): Promise<Output<TOutputFormat>[]> {
|
||||
await this.init();
|
||||
if (!this.workermodule) throw new Error("Worker module is not ready.");
|
||||
|
||||
return this.workermodule.decryptMulti(key, items, outputFormat);
|
||||
}
|
||||
|
||||
async hash(password: string, salt: string): Promise<string> {
|
||||
await this.init();
|
||||
if (!this.workermodule) throw new Error("Worker module is not ready.");
|
||||
|
||||
@@ -24,15 +24,23 @@ import {
|
||||
SerializedKey
|
||||
} from "@notesnook/crypto/dist/src/types";
|
||||
import { expose, transfer } from "comlink";
|
||||
import { NNCrypto } from "@notesnook/crypto";
|
||||
import type { Decryption, NNCrypto, Output } from "@notesnook/crypto";
|
||||
|
||||
let crypto: NNCrypto | null = null;
|
||||
let decryption: typeof Decryption | null = null;
|
||||
|
||||
async function loadNNCrypto(): Promise<NNCrypto> {
|
||||
if (crypto) return crypto;
|
||||
const { NNCrypto } = await import("@notesnook/crypto");
|
||||
return (crypto = new NNCrypto());
|
||||
}
|
||||
|
||||
async function loadDecryptionModule(): Promise<typeof Decryption> {
|
||||
if (decryption) return decryption;
|
||||
const { Decryption } = await import("@notesnook/crypto");
|
||||
return (decryption = Decryption);
|
||||
}
|
||||
|
||||
const module = {
|
||||
exportKey: async function (password: string, salt?: string) {
|
||||
const crypto = await loadNNCrypto();
|
||||
@@ -48,19 +56,31 @@ const module = {
|
||||
},
|
||||
encrypt: async function (
|
||||
key: SerializedKey,
|
||||
plaintext: Plaintext,
|
||||
plaintext: Plaintext<OutputFormat>,
|
||||
outputFormat?: OutputFormat
|
||||
) {
|
||||
const crypto = await loadNNCrypto();
|
||||
return crypto.encrypt(key, plaintext, outputFormat);
|
||||
},
|
||||
decrypt: async function (
|
||||
decrypt: async function <TOutputFormat extends OutputFormat>(
|
||||
key: SerializedKey,
|
||||
cipherData: Cipher,
|
||||
outputFormat?: OutputFormat
|
||||
outputFormat: TOutputFormat = "text" as TOutputFormat
|
||||
) {
|
||||
const crypto = await loadNNCrypto();
|
||||
return crypto.decrypt(key, cipherData, outputFormat);
|
||||
const decryption = await loadDecryptionModule();
|
||||
return decryption.decrypt(key, cipherData, outputFormat);
|
||||
},
|
||||
decryptMulti: async function <TOutputFormat extends OutputFormat>(
|
||||
key: SerializedKey,
|
||||
items: Cipher[],
|
||||
outputFormat: TOutputFormat = "text" as TOutputFormat
|
||||
) {
|
||||
const decryption = await loadDecryptionModule();
|
||||
const decryptedItems: Output<TOutputFormat>[] = [];
|
||||
for (const cipherData of items) {
|
||||
decryptedItems.push(decryption.decrypt(key, cipherData, outputFormat));
|
||||
}
|
||||
return decryptedItems;
|
||||
},
|
||||
createEncryptionStream: async function (key: SerializedKey) {
|
||||
const crypto = await loadNNCrypto();
|
||||
|
||||
@@ -26,6 +26,7 @@ import Password from "./src/password";
|
||||
import {
|
||||
Cipher,
|
||||
EncryptionKey,
|
||||
Output,
|
||||
OutputFormat,
|
||||
Plaintext,
|
||||
SerializedKey
|
||||
@@ -40,24 +41,37 @@ export class NNCrypto implements INNCrypto {
|
||||
this.isReady = true;
|
||||
}
|
||||
|
||||
async encrypt(
|
||||
async encrypt<TFormat extends OutputFormat>(
|
||||
key: SerializedKey,
|
||||
plaintext: Plaintext,
|
||||
plaintext: Plaintext<TFormat>,
|
||||
outputFormat: OutputFormat = "uint8array"
|
||||
): Promise<Cipher> {
|
||||
await this.init();
|
||||
return Encryption.encrypt(key, plaintext, outputFormat);
|
||||
}
|
||||
|
||||
async decrypt(
|
||||
async decrypt<TOutputFormat extends OutputFormat>(
|
||||
key: SerializedKey,
|
||||
cipherData: Cipher,
|
||||
outputFormat: OutputFormat = "text"
|
||||
): Promise<Plaintext> {
|
||||
outputFormat: TOutputFormat = "text" as TOutputFormat
|
||||
): Promise<Output<TOutputFormat>> {
|
||||
await this.init();
|
||||
return Decryption.decrypt(key, cipherData, outputFormat);
|
||||
}
|
||||
|
||||
async decryptMulti<TOutputFormat extends OutputFormat>(
|
||||
key: SerializedKey,
|
||||
items: Cipher[],
|
||||
outputFormat: TOutputFormat = "text" as TOutputFormat
|
||||
): Promise<Output<TOutputFormat>[]> {
|
||||
await this.init();
|
||||
const decryptedItems: Output<TOutputFormat>[] = [];
|
||||
for (const cipherData of items) {
|
||||
decryptedItems.push(Decryption.decrypt(key, cipherData, outputFormat));
|
||||
}
|
||||
return decryptedItems;
|
||||
}
|
||||
|
||||
async hash(password: string, salt: string): Promise<string> {
|
||||
await this.init();
|
||||
return Password.hash(password, salt);
|
||||
@@ -139,3 +153,4 @@ export class NNCrypto implements INNCrypto {
|
||||
|
||||
export * from "./src/types";
|
||||
export * from "./src/interfaces";
|
||||
export { Decryption };
|
||||
|
||||
@@ -29,7 +29,7 @@ import {
|
||||
from_hex
|
||||
} from "@notesnook/sodium";
|
||||
import KeyUtils from "./keyutils";
|
||||
import { Cipher, OutputFormat, Plaintext, SerializedKey } from "./types";
|
||||
import { Cipher, Output, OutputFormat, SerializedKey } from "./types";
|
||||
|
||||
export default class Decryption {
|
||||
private static transformInput(cipherData: Cipher): Uint8Array {
|
||||
@@ -54,11 +54,11 @@ export default class Decryption {
|
||||
return input;
|
||||
}
|
||||
|
||||
static decrypt(
|
||||
static decrypt<TOutputFormat extends OutputFormat>(
|
||||
key: SerializedKey,
|
||||
cipherData: Cipher,
|
||||
outputFormat: OutputFormat = "text"
|
||||
): Plaintext {
|
||||
outputFormat: TOutputFormat = "text" as TOutputFormat
|
||||
): Output<TOutputFormat> {
|
||||
if (!key.salt && cipherData.salt) key.salt = cipherData.salt;
|
||||
const encryptionKey = KeyUtils.transform(key);
|
||||
|
||||
@@ -71,15 +71,13 @@ export default class Decryption {
|
||||
encryptionKey.key
|
||||
);
|
||||
|
||||
return {
|
||||
format: outputFormat,
|
||||
data:
|
||||
outputFormat === "base64"
|
||||
? to_base64(plaintext, base64_variants.ORIGINAL)
|
||||
: outputFormat === "text"
|
||||
? to_string(plaintext)
|
||||
: plaintext
|
||||
};
|
||||
return (
|
||||
outputFormat === "base64"
|
||||
? to_base64(plaintext, base64_variants.ORIGINAL)
|
||||
: outputFormat === "text"
|
||||
? to_string(plaintext)
|
||||
: plaintext
|
||||
) as Output<TOutputFormat>;
|
||||
}
|
||||
|
||||
static createStream(
|
||||
|
||||
@@ -34,7 +34,9 @@ import { Chunk, Cipher, OutputFormat, Plaintext, SerializedKey } from "./types";
|
||||
|
||||
const encoder = new TextEncoder();
|
||||
export default class Encryption {
|
||||
private static transformInput(plaintext: Plaintext): Uint8Array {
|
||||
private static transformInput(
|
||||
plaintext: Plaintext<OutputFormat>
|
||||
): Uint8Array {
|
||||
let data: Uint8Array | null = null;
|
||||
if (typeof plaintext.data === "string" && plaintext.format === "base64") {
|
||||
data = from_base64(plaintext.data, base64_variants.ORIGINAL);
|
||||
@@ -49,7 +51,7 @@ export default class Encryption {
|
||||
|
||||
static encrypt(
|
||||
key: SerializedKey,
|
||||
plaintext: Plaintext,
|
||||
plaintext: Plaintext<OutputFormat>,
|
||||
outputFormat: OutputFormat = "uint8array"
|
||||
): Cipher {
|
||||
const encryptionKey = KeyUtils.transform(key);
|
||||
|
||||
@@ -23,7 +23,8 @@ import {
|
||||
OutputFormat,
|
||||
Plaintext,
|
||||
SerializedKey,
|
||||
Chunk
|
||||
Chunk,
|
||||
Output
|
||||
} from "./types";
|
||||
|
||||
export interface IStreamable {
|
||||
@@ -34,15 +35,21 @@ export interface IStreamable {
|
||||
export interface INNCrypto {
|
||||
encrypt(
|
||||
key: SerializedKey,
|
||||
plaintext: Plaintext,
|
||||
plaintext: Plaintext<OutputFormat>,
|
||||
outputFormat?: OutputFormat
|
||||
): Promise<Cipher>;
|
||||
|
||||
decrypt(
|
||||
decrypt<TOutputFormat extends OutputFormat>(
|
||||
key: SerializedKey,
|
||||
cipherData: Cipher,
|
||||
outputFormat?: OutputFormat
|
||||
): Promise<Plaintext>;
|
||||
outputFormat?: TOutputFormat
|
||||
): Promise<Output<TOutputFormat>>;
|
||||
|
||||
decryptMulti<TOutputFormat extends OutputFormat>(
|
||||
key: SerializedKey,
|
||||
cipherData: Cipher[],
|
||||
outputFormat?: TOutputFormat
|
||||
): Promise<Output<TOutputFormat>[]>;
|
||||
|
||||
hash(password: string, salt: string): Promise<string>;
|
||||
|
||||
|
||||
@@ -30,10 +30,12 @@ export type Cipher = {
|
||||
length: number;
|
||||
};
|
||||
|
||||
export type Plaintext = {
|
||||
format: OutputFormat;
|
||||
data: string | Uint8Array;
|
||||
export type Plaintext<TFormat extends OutputFormat> = {
|
||||
format: TFormat;
|
||||
data: TFormat extends StringOutputFormat ? string : Uint8Array;
|
||||
};
|
||||
export type Output<TFormat extends OutputFormat> =
|
||||
TFormat extends StringOutputFormat ? string : Uint8Array;
|
||||
|
||||
export type SerializedKey = {
|
||||
password?: string;
|
||||
|
||||
Reference in New Issue
Block a user