mirror of
https://github.com/zadam/trilium.git
synced 2026-05-07 07:56:24 +02:00
chore(standalone): remove async encryption
This commit is contained in:
@@ -1,8 +1,6 @@
|
||||
export interface Cipher {
|
||||
update(data: Uint8Array): Uint8Array;
|
||||
final(): Uint8Array;
|
||||
/** Async finalization for browser environments where Web Crypto API is async-only */
|
||||
finalizeAsync?(): Promise<Uint8Array>;
|
||||
}
|
||||
|
||||
export interface ScryptOptions {
|
||||
|
||||
@@ -108,118 +108,8 @@ function decryptString(dataKey: Uint8Array, cipherText: string) {
|
||||
return decodeUtf8(buffer);
|
||||
}
|
||||
|
||||
/**
|
||||
* Async version of encrypt that works in both Node.js and browser environments.
|
||||
* Uses finalizeAsync() for browser compatibility when available.
|
||||
*/
|
||||
async function encryptAsync(key: Uint8Array, plainText: Uint8Array | string): Promise<string> {
|
||||
if (!key) {
|
||||
throw new Error("No data key!");
|
||||
}
|
||||
|
||||
const plainTextUint8Array = ArrayBuffer.isView(plainText) ? plainText : encodeUtf8(plainText);
|
||||
|
||||
const iv = getCrypto().randomBytes(16);
|
||||
const cipher = getCrypto().createCipheriv("aes-128-cbc", pad(key), pad(iv));
|
||||
|
||||
const digest = shaArray(plainTextUint8Array).slice(0, 4);
|
||||
const digestWithPayload = concat2(digest, plainTextUint8Array);
|
||||
|
||||
// Use async finalization if available (browser), otherwise sync (Node.js)
|
||||
let encryptedData: Uint8Array;
|
||||
if (cipher.finalizeAsync) {
|
||||
// Browser: update() buffers data, finalizeAsync() encrypts and returns all
|
||||
cipher.update(digestWithPayload);
|
||||
encryptedData = await cipher.finalizeAsync();
|
||||
} else {
|
||||
// Node.js: update() and final() both return encrypted chunks
|
||||
encryptedData = concat2(cipher.update(digestWithPayload), cipher.final());
|
||||
}
|
||||
|
||||
const encryptedDataWithIv = concat2(iv, encryptedData);
|
||||
|
||||
return encodeBase64(encryptedDataWithIv);
|
||||
}
|
||||
|
||||
/**
|
||||
* Async version of decrypt that works in both Node.js and browser environments.
|
||||
* Uses finalizeAsync() for browser compatibility when available.
|
||||
*/
|
||||
async function decryptAsync(key: Uint8Array, cipherText: string | Uint8Array): Promise<Uint8Array | false | null> {
|
||||
if (cipherText === null) {
|
||||
return null;
|
||||
}
|
||||
|
||||
if (!key) {
|
||||
return encodeUtf8("[protected]");
|
||||
}
|
||||
|
||||
try {
|
||||
const cipherTextStr = typeof cipherText === "string" ? cipherText : decodeUtf8(cipherText);
|
||||
const cipherTextUint8ArrayWithIv = decodeBase64(cipherTextStr);
|
||||
|
||||
// old encrypted data can have IV of length 13, see some details here: https://github.com/zadam/trilium/issues/3017
|
||||
const ivLength = cipherTextUint8ArrayWithIv.length % 16 === 0 ? 16 : 13;
|
||||
|
||||
const iv = cipherTextUint8ArrayWithIv.slice(0, ivLength);
|
||||
const cipherTextUint8Array = cipherTextUint8ArrayWithIv.slice(ivLength);
|
||||
|
||||
const decipher = getCrypto().createDecipheriv("aes-128-cbc", pad(key), pad(iv));
|
||||
|
||||
// Use async finalization if available (browser), otherwise sync (Node.js)
|
||||
let decryptedBytes: Uint8Array;
|
||||
if (decipher.finalizeAsync) {
|
||||
// Browser: update() buffers data, finalizeAsync() decrypts and returns all
|
||||
decipher.update(cipherTextUint8Array);
|
||||
decryptedBytes = await decipher.finalizeAsync();
|
||||
} else {
|
||||
// Node.js: update() and final() both return decrypted chunks
|
||||
decryptedBytes = concat2(decipher.update(cipherTextUint8Array), decipher.final());
|
||||
}
|
||||
|
||||
const digest = decryptedBytes.slice(0, 4);
|
||||
const payload = decryptedBytes.slice(4);
|
||||
|
||||
const computedDigest = shaArray(payload).slice(0, 4);
|
||||
|
||||
if (!arraysIdentical(digest, computedDigest)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return payload;
|
||||
} catch (e: any) {
|
||||
// recovery from https://github.com/zadam/trilium/issues/510
|
||||
if (e.message?.includes("WRONG_FINAL_BLOCK_LENGTH") || e.message?.includes("wrong final block length")) {
|
||||
getLog().info("Caught WRONG_FINAL_BLOCK_LENGTH, returning cipherText instead");
|
||||
|
||||
return (ArrayBuffer.isView(cipherText) ? cipherText : Uint8Array.from(cipherText));
|
||||
}
|
||||
throw e;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Async version of decryptString that works in both Node.js and browser environments.
|
||||
*/
|
||||
async function decryptStringAsync(dataKey: Uint8Array, cipherText: string): Promise<string | null> {
|
||||
const buffer = await decryptAsync(dataKey, cipherText);
|
||||
|
||||
if (buffer === null) {
|
||||
return null;
|
||||
} else if (buffer === false) {
|
||||
getLog().error(`Could not decrypt string. Uint8Array: ${buffer}`);
|
||||
|
||||
throw new Error("Could not decrypt string.");
|
||||
}
|
||||
|
||||
return decodeUtf8(buffer);
|
||||
}
|
||||
|
||||
export default {
|
||||
encrypt,
|
||||
decrypt,
|
||||
decryptString,
|
||||
encryptAsync,
|
||||
decryptAsync,
|
||||
decryptStringAsync
|
||||
decryptString
|
||||
};
|
||||
|
||||
@@ -31,7 +31,7 @@ export async function setDataKey(
|
||||
plainTextDataKey: string | Uint8Array
|
||||
): Promise<void> {
|
||||
const passwordDerivedKey = await scryptService.getPasswordDerivedKey(password);
|
||||
const newEncryptedDataKey = await data_encryption.encryptAsync(passwordDerivedKey, plainTextDataKey);
|
||||
const newEncryptedDataKey = data_encryption.encrypt(passwordDerivedKey, plainTextDataKey);
|
||||
options.setOption("encryptedDataKey", newEncryptedDataKey);
|
||||
}
|
||||
|
||||
@@ -41,7 +41,7 @@ export async function setDataKey(
|
||||
export async function getDataKey(password: string): Promise<Uint8Array | false | null> {
|
||||
const passwordDerivedKey = await scryptService.getPasswordDerivedKey(password);
|
||||
const encryptedDataKey = options.getOption("encryptedDataKey");
|
||||
return data_encryption.decryptAsync(passwordDerivedKey, encryptedDataKey);
|
||||
return data_encryption.decrypt(passwordDerivedKey, encryptedDataKey);
|
||||
}
|
||||
|
||||
export default {
|
||||
|
||||
@@ -29,15 +29,6 @@ function encrypt(plainText: string | Uint8Array) {
|
||||
return dataEncryptionService.encrypt(dataKey, plainText);
|
||||
}
|
||||
|
||||
async function encryptAsync(plainText: string | Uint8Array): Promise<string | null> {
|
||||
const dataKey = getDataKey();
|
||||
if (plainText === null || dataKey === null) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return dataEncryptionService.encryptAsync(dataKey, plainText);
|
||||
}
|
||||
|
||||
function decrypt(cipherText: string | Uint8Array): Uint8Array | null {
|
||||
const dataKey = getDataKey();
|
||||
if (cipherText === null || dataKey === null) {
|
||||
@@ -47,15 +38,6 @@ function decrypt(cipherText: string | Uint8Array): Uint8Array | null {
|
||||
return dataEncryptionService.decrypt(dataKey, cipherText) || null;
|
||||
}
|
||||
|
||||
async function decryptAsync(cipherText: string | Uint8Array): Promise<Uint8Array | null> {
|
||||
const dataKey = getDataKey();
|
||||
if (cipherText === null || dataKey === null) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return (await dataEncryptionService.decryptAsync(dataKey, cipherText)) || null;
|
||||
}
|
||||
|
||||
function decryptString(cipherText: string): string | null {
|
||||
const dataKey = getDataKey();
|
||||
if (dataKey === null) {
|
||||
@@ -64,14 +46,6 @@ function decryptString(cipherText: string): string | null {
|
||||
return dataEncryptionService.decryptString(dataKey, cipherText);
|
||||
}
|
||||
|
||||
async function decryptStringAsync(cipherText: string): Promise<string | null> {
|
||||
const dataKey = getDataKey();
|
||||
if (dataKey === null) {
|
||||
return null;
|
||||
}
|
||||
return dataEncryptionService.decryptStringAsync(dataKey, cipherText);
|
||||
}
|
||||
|
||||
let lastProtectedSessionOperationDate: number | null = null;
|
||||
|
||||
function touchProtectedSession() {
|
||||
@@ -89,11 +63,8 @@ export default {
|
||||
resetDataKey,
|
||||
isProtectedSessionAvailable,
|
||||
encrypt,
|
||||
encryptAsync,
|
||||
decrypt,
|
||||
decryptAsync,
|
||||
decryptString,
|
||||
decryptStringAsync,
|
||||
touchProtectedSession,
|
||||
getLastProtectedSessionOperationDate
|
||||
};
|
||||
|
||||
Reference in New Issue
Block a user