mirror of
https://github.com/streetwriters/notesnook.git
synced 2026-02-24 20:20:21 +01:00
119 lines
3.6 KiB
TypeScript
119 lines
3.6 KiB
TypeScript
/*
|
|
This file is part of the Notesnook project (https://notesnook.com/)
|
|
|
|
Copyright (C) 2023 Streetwriters (Private) Limited
|
|
|
|
This program is free software: you can redistribute it and/or modify
|
|
it under the terms of the GNU General Public License as published by
|
|
the Free Software Foundation, either version 3 of the License, or
|
|
(at your option) any later version.
|
|
|
|
This program is distributed in the hope that it will be useful,
|
|
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
GNU General Public License for more details.
|
|
|
|
You should have received a copy of the GNU General Public License
|
|
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
|
*/
|
|
|
|
import { ISodium, base64_variants } from "@notesnook/sodium";
|
|
import KeyUtils from "./keyutils.js";
|
|
import { Chunk, Cipher, Input, DataFormat, SerializedKey } from "./types.js";
|
|
|
|
const encoder = new TextEncoder();
|
|
export default class Encryption {
|
|
private static transformInput(
|
|
sodium: ISodium,
|
|
input: Input<DataFormat>,
|
|
format: DataFormat
|
|
): Uint8Array {
|
|
let data: Uint8Array | null = null;
|
|
if (typeof input === "string" && format === "base64") {
|
|
data = sodium.from_base64(input, base64_variants.ORIGINAL);
|
|
} else if (typeof input === "string") {
|
|
data = encoder.encode(input);
|
|
} else if (input instanceof Uint8Array) {
|
|
data = input;
|
|
}
|
|
if (!data) throw new Error("Data cannot be null.");
|
|
return data;
|
|
}
|
|
|
|
static encrypt<TOutputFormat extends DataFormat>(
|
|
sodium: ISodium,
|
|
key: SerializedKey,
|
|
input: Input<DataFormat>,
|
|
format: DataFormat,
|
|
outputFormat: TOutputFormat = "uint8array" as TOutputFormat
|
|
): Cipher<TOutputFormat> {
|
|
const encryptionKey = KeyUtils.transform(sodium, key);
|
|
const data = this.transformInput(sodium, input, format);
|
|
|
|
const nonce = sodium.randombytes_buf(
|
|
sodium.crypto_aead_xchacha20poly1305_ietf_NPUBBYTES
|
|
);
|
|
|
|
const cipher: string | Uint8Array =
|
|
sodium.crypto_aead_xchacha20poly1305_ietf_encrypt(
|
|
data,
|
|
null,
|
|
null,
|
|
nonce,
|
|
encryptionKey.key
|
|
);
|
|
|
|
let output: string | Uint8Array = cipher;
|
|
if (outputFormat === "base64") {
|
|
output = sodium.to_base64(cipher, base64_variants.URLSAFE_NO_PADDING);
|
|
}
|
|
|
|
const iv = sodium.to_base64(nonce);
|
|
return {
|
|
format: outputFormat,
|
|
alg: getAlgorithm(base64_variants.URLSAFE_NO_PADDING),
|
|
cipher: output,
|
|
iv,
|
|
salt: encryptionKey.salt,
|
|
length: data.length
|
|
} as Cipher<TOutputFormat>;
|
|
}
|
|
|
|
static createStream(
|
|
sodium: ISodium,
|
|
key: SerializedKey
|
|
): {
|
|
iv: string;
|
|
stream: TransformStream<Chunk, Uint8Array>;
|
|
} {
|
|
const { key: _key } = KeyUtils.transform(sodium, key);
|
|
const { state, header } =
|
|
sodium.crypto_secretstream_xchacha20poly1305_init_push(_key, "base64");
|
|
|
|
return {
|
|
iv: header,
|
|
stream: new TransformStream<Chunk, Uint8Array>({
|
|
start() {},
|
|
transform(chunk, controller) {
|
|
controller.enqueue(
|
|
sodium.crypto_secretstream_xchacha20poly1305_push(
|
|
state,
|
|
chunk.data,
|
|
null,
|
|
chunk.final
|
|
? sodium.crypto_secretstream_xchacha20poly1305_TAG_FINAL
|
|
: sodium.crypto_secretstream_xchacha20poly1305_TAG_MESSAGE
|
|
)
|
|
);
|
|
if (chunk.final) controller.terminate();
|
|
}
|
|
})
|
|
};
|
|
}
|
|
}
|
|
|
|
function getAlgorithm(base64Variant: base64_variants) {
|
|
//Template: encryptionAlgorithm-kdfAlgorithm-base64variant
|
|
return `xcha-argon2i13-${base64Variant}`;
|
|
}
|