This commit is contained in:
yinjun.chen1
2026-09-14 13:45:17 +08:00
commit 1eef16a332
18 changed files with 3964 additions and 0 deletions
+154
View File
@@ -0,0 +1,154 @@
// tests/crypto-store.test.js
// 测试 crypto-store.js:主口令派生、AES-GCM 加解密、设置/解锁/锁定
// 运行:node --test tests/crypto-store.test.js
const { describe, it, beforeEach } = require("node:test");
const assert = require("node:assert/strict");
function createMemoryStorage() {
const local = {};
const session = {};
const makeArea = (store) => ({
async get(keys) {
const result = {};
if (Array.isArray(keys)) keys.forEach((k) => { if (k in store) result[k] = store[k]; });
else if (typeof keys === "string") { if (keys in store) result[keys] = store[keys]; }
else Object.assign(result, store);
return result;
},
async set(items) { Object.assign(store, items); },
async remove(keys) {
if (Array.isArray(keys)) keys.forEach((k) => delete store[k]);
else delete store[keys];
},
});
return { local: makeArea(local), session: makeArea(session), localStore: local, sessionStore: session };
}
function loadCryptoStore(storage) {
global.chrome = { storage };
delete require.cache[require.resolve("../crypto-store.js")];
return require("../crypto-store.js");
}
describe("crypto-store - 基础加解密", () => {
it("encryptJson + decryptJson 往返一致", async () => {
const storage = createMemoryStorage();
const C = loadCryptoStore(storage);
const salt = C.randomBytes(16);
const key = await C.deriveKey("mypassword", salt, C.PBKDF2_ITERATIONS);
const data = [{ id: "e1", name: "个人", configs: [{ domain: "a.com" }] }];
const cipher = await C.encryptJson(data, key);
const decrypted = await C.decryptJson(cipher, key);
assert.deepEqual(decrypted, data);
});
it("不同口令派生的密钥无法解密对方数据", async () => {
const storage = createMemoryStorage();
const C = loadCryptoStore(storage);
const salt = C.randomBytes(16);
const key1 = await C.deriveKey("password1", salt, C.PBKDF2_ITERATIONS);
const key2 = await C.deriveKey("password2", salt, C.PBKDF2_ITERATIONS);
const cipher = await C.encryptJson({ secret: "hello" }, key1);
await assert.rejects(() => C.decryptJson(cipher, key2));
});
it("b64encode / b64decode 往返一致", () => {
const C = loadCryptoStore(createMemoryStorage());
const buf = new Uint8Array([0, 1, 2, 253, 254, 255]);
const encoded = C.b64encode(buf);
const decoded = C.b64decode(encoded);
assert.deepEqual(Array.from(decoded), Array.from(buf));
});
});
describe("crypto-store - 设置/解锁/锁定", () => {
let C, storage;
beforeEach(() => {
storage = createMemoryStorage();
C = loadCryptoStore(storage);
});
it("hasVault 初始为 false", async () => {
assert.equal(await C.hasVault(), false);
assert.equal(await C.isUnlocked(), false);
});
it("setupVault 加密并解锁", async () => {
const envs = [{ id: "e1", name: "个人", configs: [] }];
const key = await C.setupVault("mypass123", envs);
assert.ok(key);
assert.equal(await C.hasVault(), true);
assert.equal(await C.isUnlocked(), true);
// local 中是密文,不是明文
assert.ok(storage.localStore.vaultMeta);
assert.ok(storage.localStore.encryptedVault);
assert.equal(storage.localStore.environments, undefined);
// session 中是明文
assert.ok(storage.sessionStore.environments);
});
it("setupVault 口令不能为空", async () => {
await assert.rejects(() => C.setupVault("", []), /口令不能为空/);
});
it("unlock 正确口令可解密", async () => {
await C.setupVault("correct horse", [{ id: "e1", name: "x", configs: [] }]);
// 先锁定
await C.lock();
assert.equal(await C.isUnlocked(), false);
const { environments, key } = await C.unlock("correct horse");
assert.ok(key);
assert.equal(environments.length, 1);
assert.equal(environments[0].name, "x");
assert.equal(await C.isUnlocked(), true);
});
it("unlock 错误口令抛错", async () => {
await C.setupVault("correct", []);
await C.lock();
await assert.rejects(() => C.unlock("wrong"), /口令错误/);
// 错误口令后仍未解锁
assert.equal(await C.isUnlocked(), false);
});
it("lock 清除 session 明文但保留 local 密文", async () => {
await C.setupVault("pw", [{ id: "e1", name: "x", configs: [] }]);
assert.ok(storage.sessionStore.environments);
await C.lock();
assert.equal(storage.sessionStore.environments, undefined);
// 密文还在
assert.ok(storage.localStore.encryptedVault);
});
it("unlock 未设置口令时抛错", async () => {
await assert.rejects(() => C.unlock("anything"), /尚未设置口令/);
});
it("lock 后可再次 unlock", async () => {
await C.setupVault("pw", [{ id: "e1", name: "x", configs: [] }]);
await C.lock();
await C.unlock("pw");
assert.equal(await C.isUnlocked(), true);
const envs = (await C.unlock("pw")).environments;
assert.equal(envs[0].name, "x");
});
});
describe("crypto-store - persistEncrypted", () => {
it("用密钥重新加密 environments 写回 local", async () => {
const storage = createMemoryStorage();
const C = loadCryptoStore(storage);
const key = await C.setupVault("pw", [{ id: "e1", name: "x", configs: [] }]);
// 修改环境
const newEnvs = [{ id: "e1", name: "x", configs: [{ domain: "a.com" }] }];
await C.persistEncrypted(newEnvs, key);
// 锁定后重新解锁,验证持久化生效
await C.lock();
const { environments } = await C.unlock("pw");
assert.equal(environments[0].configs.length, 1);
assert.equal(environments[0].configs[0].domain, "a.com");
});
});