From 1fdc623ebcbc5383ffcd473393ef06370fec979d Mon Sep 17 00:00:00 2001 From: Elian Doran Date: Sun, 12 Apr 2026 20:03:41 +0300 Subject: [PATCH] fix(core): corruption caused by encryption --- .../src/services/data_encryption.spec.ts | 67 +++++++++++++++++++ .../services/encryption/data_encryption.ts | 4 +- 2 files changed, 69 insertions(+), 2 deletions(-) create mode 100644 apps/client-standalone/src/services/data_encryption.spec.ts diff --git a/apps/client-standalone/src/services/data_encryption.spec.ts b/apps/client-standalone/src/services/data_encryption.spec.ts new file mode 100644 index 0000000000..c4b9e07d96 --- /dev/null +++ b/apps/client-standalone/src/services/data_encryption.spec.ts @@ -0,0 +1,67 @@ +import { describe, it, expect } from "vitest"; +import { data_encryption } from "@triliumnext/core"; + +// Note: BrowserCryptoProvider is already initialized via test_setup.ts + +describe("data_encryption with BrowserCryptoProvider", () => { + it("should encrypt and decrypt ASCII text correctly", () => { + const key = new Uint8Array(16).fill(42); + const plainText = "Hello, World!"; + + const encrypted = data_encryption.encrypt(key, plainText); + expect(typeof encrypted).toBe("string"); + expect(encrypted.length).toBeGreaterThan(0); + + const decrypted = data_encryption.decryptString(key, encrypted); + expect(decrypted).toBe(plainText); + }); + + it("should encrypt and decrypt UTF-8 text correctly", () => { + const key = new Uint8Array(16).fill(42); + const plainText = "Привет мир! 你好世界! 🎉"; + + const encrypted = data_encryption.encrypt(key, plainText); + const decrypted = data_encryption.decryptString(key, encrypted); + expect(decrypted).toBe(plainText); + }); + + it("should encrypt and decrypt empty string", () => { + const key = new Uint8Array(16).fill(42); + const plainText = ""; + + const encrypted = data_encryption.encrypt(key, plainText); + const decrypted = data_encryption.decryptString(key, encrypted); + expect(decrypted).toBe(plainText); + }); + + it("should encrypt and decrypt binary data", () => { + const key = new Uint8Array(16).fill(42); + const plainData = new Uint8Array([0, 1, 2, 255, 128, 64]); + + const encrypted = data_encryption.encrypt(key, plainData); + const decrypted = data_encryption.decrypt(key, encrypted); + expect(decrypted).toBeInstanceOf(Uint8Array); + expect(Array.from(decrypted as Uint8Array)).toEqual(Array.from(plainData)); + }); + + it("should fail decryption with wrong key", () => { + const key1 = new Uint8Array(16).fill(42); + const key2 = new Uint8Array(16).fill(43); + const plainText = "Secret message"; + + const encrypted = data_encryption.encrypt(key1, plainText); + + // decrypt returns false when digest doesn't match + const result = data_encryption.decrypt(key2, encrypted); + expect(result).toBe(false); + }); + + it("should handle large content", () => { + const key = new Uint8Array(16).fill(42); + const plainText = "x".repeat(100000); + + const encrypted = data_encryption.encrypt(key, plainText); + const decrypted = data_encryption.decryptString(key, encrypted); + expect(decrypted).toBe(plainText); + }); +}); diff --git a/packages/trilium-core/src/services/encryption/data_encryption.ts b/packages/trilium-core/src/services/encryption/data_encryption.ts index ffae2c1e9d..bacaa2c420 100644 --- a/packages/trilium-core/src/services/encryption/data_encryption.ts +++ b/packages/trilium-core/src/services/encryption/data_encryption.ts @@ -33,7 +33,7 @@ function encrypt(key: Uint8Array, plainText: Uint8Array | string) { throw new Error("No data key!"); } - const plainTextUint8Array = ArrayBuffer.isView(plainText) ? plainText : Uint8Array.from(plainText); + const plainTextUint8Array = ArrayBuffer.isView(plainText) ? plainText : encodeUtf8(plainText); const iv = getCrypto().randomBytes(16); const cipher = getCrypto().createCipheriv("aes-128-cbc", pad(key), pad(iv)); @@ -88,7 +88,7 @@ function decrypt(key: Uint8Array, cipherText: string | Uint8Array): Uint8Array | 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)); + return (ArrayBuffer.isView(cipherText) ? cipherText : encodeUtf8(cipherText)); } throw e; }