Files
ThreadNet-Web/apps/web/test/unit-tests/utils/MegolmExportEncryption-test.ts
Michael TelatynskiandGitHub 4aa1a3549e Enable oxlint restriction ruleset (#34307)
* Remove stale max-len disablements

* Remove stale camelCase & naming-convention disablements

* Remove stale ban-ts-comment disablements

* Remove stale no-var disablements

* Remove stale no-empty-property disablements

* Remove stale react rule disablements

* Remove stale no-constant-condition disablements

* Remove stale no-unused-vars disablements

* Remove stale disablements for disabled rules

* fixup camelcase

* Remove dead code

* Tidy code

* Tweak oxlint config

* Use oxlint to apply jsx/tsx extension consistently

* Fix import

* Fix imports

* Rename affected snapshots

* Update more imports

* Enable restriction ruleset

* Make code comply with new rules

* Make code comply with react/button-has-type

* Make code comply with typescript/non-nullable-type-assertion-style

* Comply with node/no-process-env

* Comply with unicorn/prefer-node-protocol

* Comply with unicorn/import-style

* Comply with unicorn/no-process-exit

* Comply with no-proto

* Comply with node/handle-callback-err

* Comply with import/no-commonjs

* Comply with node/no-path-concat

* Comply with unicorn/no-length-as-slice-end

* Comply with unicorn/no-document-cookie

* Comply with unicorn/prefer-module

* Comply with typescript/prefer-literal-enum-member

* Comply with jsx-a11y/anchor-ambiguous-text

* Tweak oxlint config

* Fix resolves

* Iterate

* Iterate

* Iterate

* Iterate

* Iterate

* Iterate

* Iterate
2026-08-04 09:15:07 +00:00

144 lines
5.4 KiB
TypeScript

/*
Copyright 2024 New Vector Ltd.
Copyright 2017 Vector Creations Ltd
SPDX-License-Identifier: AGPL-3.0-only OR GPL-3.0-only OR LicenseRef-Element-Commercial
Please see LICENSE files in the repository root for full details.
*/
import { TextEncoder } from "node:util";
import nodeCrypto from "node:crypto";
import { Crypto } from "@peculiar/webcrypto";
import type * as MegolmExportEncryptionExport from "../../../src/utils/MegolmExportEncryption";
const webCrypto = new Crypto();
function getRandomValues<T extends ArrayBufferView | null>(buf: T): T {
// @ts-ignore fussy generics
return nodeCrypto.randomFillSync(buf);
}
const TEST_VECTORS = [
[
"plain",
"password",
"-----BEGIN MEGOLM SESSION DATA-----\n" +
"AXNhbHRzYWx0c2FsdHNhbHSIiIiIiIiIiIiIiIiIiIiIAAAACmIRUW2OjZ3L2l6j9h0lHlV3M2dx\n" +
"cissyYBxjsfsAndErh065A8=\n" +
"-----END MEGOLM SESSION DATA-----",
],
[
"Hello, World",
"betterpassword",
"-----BEGIN MEGOLM SESSION DATA-----\n" +
"AW1vcmVzYWx0bW9yZXNhbHT//////////wAAAAAAAAAAAAAD6KyBpe1Niv5M5NPm4ZATsJo5nghk\n" +
"KYu63a0YQ5DRhUWEKk7CcMkrKnAUiZny\n" +
"-----END MEGOLM SESSION DATA-----",
],
[
"alphanumericallyalphanumericallyalphanumericallyalphanumerically",
"SWORDFISH",
"-----BEGIN MEGOLM SESSION DATA-----\n" +
"AXllc3NhbHR5Z29vZG5lc3P//////////wAAAAAAAAAAAAAD6OIW+Je7gwvjd4kYrb+49gKCfExw\n" +
"MgJBMD4mrhLkmgAngwR1pHjbWXaoGybtiAYr0moQ93GrBQsCzPbvl82rZhaXO3iH5uHo/RCEpOqp\n" +
"Pgg29363BGR+/Ripq/VCLKGNbw==\n" +
"-----END MEGOLM SESSION DATA-----",
],
[
"alphanumericallyalphanumericallyalphanumericallyalphanumerically",
"passwordpasswordpasswordpasswordpasswordpasswordpasswordpasswordpassword" +
"passwordpasswordpasswordpasswordpasswordpasswordpasswordpasswordpassword" +
"passwordpasswordpasswordpasswordpasswordpasswordpasswordpasswordpassword" +
"passwordpasswordpasswordpasswordpassword",
"-----BEGIN MEGOLM SESSION DATA-----\n" +
"Af//////////////////////////////////////////AAAD6IAZJy7IQ7Y0idqSw/bmpngEEVVh\n" +
"gsH+8ptgqxw6ZVWQnohr8JsuwH9SwGtiebZuBu5smPCO+RFVWH2cQYslZijXv/BEH/txvhUrrtCd\n" +
"bWnSXS9oymiqwUIGs08sXI33ZA==\n" +
"-----END MEGOLM SESSION DATA-----",
],
] as const;
function stringToArray(s: string): ArrayBuffer {
return new TextEncoder().encode(s).buffer as ArrayBuffer;
}
describe("MegolmExportEncryption", function () {
let MegolmExportEncryption: typeof MegolmExportEncryptionExport;
beforeEach(() => {
Object.defineProperty(window, "crypto", {
value: {
getRandomValues,
randomUUID: jest.fn().mockReturnValue("not-random-uuid"),
subtle: webCrypto.subtle,
},
});
MegolmExportEncryption = jest.requireActual("../../../src/utils/MegolmExportEncryption");
});
describe("decrypt", function () {
it("should handle missing header", function () {
const input = stringToArray(`-----`);
return MegolmExportEncryption.decryptMegolmKeyFile(input, "").then(
(res) => {
throw new Error("expected to throw");
},
(error) => {
expect(error.message).toEqual("Header line not found");
},
);
});
it("should handle missing trailer", function () {
const input = stringToArray(`-----BEGIN MEGOLM SESSION DATA-----
-----`);
return MegolmExportEncryption.decryptMegolmKeyFile(input, "").then(
(res) => {
throw new Error("expected to throw");
},
(error) => {
expect(error.message).toEqual("Trailer line not found");
},
);
});
it("should handle a too-short body", function () {
const input = stringToArray(`-----BEGIN MEGOLM SESSION DATA-----
AXNhbHRzYWx0c2FsdHNhbHSIiIiIiIiIiIiIiIiIiIiIAAAACmIRUW2OjZ3L2l6j9h0lHlV3M2dx
cissyYBxjsfsAn
-----END MEGOLM SESSION DATA-----
`);
return MegolmExportEncryption.decryptMegolmKeyFile(input, "").then(
(res) => {
throw new Error("expected to throw");
},
(error) => {
expect(error.message).toEqual("Invalid file: too short");
},
);
});
it.skip.each(TEST_VECTORS)("should decrypt a range of inputs", async (plain, password, input) => {
const decrypted = await MegolmExportEncryption.decryptMegolmKeyFile(stringToArray(input), password);
expect(decrypted).toEqual(plain);
});
});
describe("encrypt", function () {
it("should round-trip", function () {
const input = "words words many words in plain text here".repeat(100);
const password = "my super secret passphrase";
return MegolmExportEncryption.encryptMegolmKeyFile(input, password, { kdf_rounds: 1000 })
.then((ciphertext) => {
return MegolmExportEncryption.decryptMegolmKeyFile(ciphertext, password);
})
.then((plaintext) => {
expect(plaintext).toEqual(input);
});
});
});
});