Initial Commit

This commit is contained in:
2026-05-27 23:47:53 +01:00
parent fbc70f96e6
commit 9b28db724f
18103 changed files with 1600554 additions and 0 deletions

View File

@@ -0,0 +1,145 @@
import { describe, expect, it } from "@suaveplan/testing/runner";
import { VAULT_CODES } from "../errors/codes.js";
import { VaultError } from "../errors/vault-error.js";
import {
createManifest,
hashVaultId,
MANIFEST_AEAD_XCHACHA20_POLY1305,
MANIFEST_BYTE_SIZE,
MANIFEST_FORMAT_VERSION,
MANIFEST_KDF_ARGON2ID_2SKD,
packManifest,
touchManifest,
unpackManifest,
} from "./manifest.js";
const baseInput = {
vaultId: "personal",
argon2idMemoryKB: 65536,
argon2idIterations: 3,
argon2idParallelism: 4,
schemaVersion: 1,
now: 1_700_000_000_000,
};
describe("vault manifest", () => {
it("createManifest populates current versions and timestamps", {
timeout: 5000,
}, () => {
const m = createManifest(baseInput);
expect(m.formatVersion).toBe(MANIFEST_FORMAT_VERSION);
expect(m.kdfId).toBe(MANIFEST_KDF_ARGON2ID_2SKD);
expect(m.aeadAlgorithm).toBe(MANIFEST_AEAD_XCHACHA20_POLY1305);
expect(m.argon2idMemoryKB).toBe(65536);
expect(m.argon2idIterations).toBe(3);
expect(m.argon2idParallelism).toBe(4);
expect(m.createdAt).toBe(baseInput.now);
expect(m.lastModifiedAt).toBe(baseInput.now);
expect(m.schemaVersion).toBe(1);
expect(m.vaultIdHash.length).toBe(32);
});
it("hashVaultId is deterministic and 32 bytes", { timeout: 5000 }, () => {
const a = hashVaultId("personal");
const b = hashVaultId("personal");
expect(a).toEqual(b);
expect(a.length).toBe(32);
const c = hashVaultId("work");
expect(c).not.toEqual(a);
});
it("packManifest + unpackManifest round-trip preserves all fields", {
timeout: 5000,
}, () => {
const m = createManifest(baseInput);
const packed = packManifest(m);
expect(packed.length).toBe(MANIFEST_BYTE_SIZE);
const round = unpackManifest(packed);
expect(round).toEqual(m);
});
it("unpackManifest ignores trailing bytes", { timeout: 5000 }, () => {
const m = createManifest(baseInput);
const packed = packManifest(m);
const padded = new Uint8Array(packed.length + 100);
padded.set(packed, 0);
const round = unpackManifest(padded);
expect(round).toEqual(m);
});
it("unpackManifest rejects too-short buffers with MANIFEST_INVALID", {
timeout: 5000,
}, () => {
try {
unpackManifest(new Uint8Array(MANIFEST_BYTE_SIZE - 1));
throw new Error("should have thrown");
} catch (err) {
expect(VaultError.is(err)).toBe(true);
expect((err as VaultError).code).toBe(VAULT_CODES.MANIFEST_INVALID);
}
});
it("packManifest rejects non-conforming manifests", {
timeout: 5000,
}, () => {
const bad = createManifest(baseInput);
try {
packManifest({
...bad,
vaultIdHash: new Uint8Array(31),
});
throw new Error("should have thrown");
} catch (err) {
expect(VaultError.is(err)).toBe(true);
expect((err as VaultError).code).toBe(VAULT_CODES.MANIFEST_INVALID);
}
});
it("unpackManifest rejects unsupported format versions", {
timeout: 5000,
}, () => {
const m = createManifest(baseInput);
const packed = packManifest(m);
// Mutate the formatVersion field (first 2 bytes — big-endian).
const tampered = new Uint8Array(packed);
tampered[0] = 0xff;
tampered[1] = 0xff;
try {
unpackManifest(tampered);
throw new Error("should have thrown");
} catch (err) {
expect(VaultError.is(err)).toBe(true);
expect((err as VaultError).code).toBe(
VAULT_CODES.MANIFEST_VERSION_UNSUPPORTED
);
}
});
it("touchManifest updates only lastModifiedAt", { timeout: 5000 }, () => {
const m = createManifest(baseInput);
const next = touchManifest(m, baseInput.now + 1000);
expect(next.lastModifiedAt).toBe(baseInput.now + 1000);
expect(next.createdAt).toBe(m.createdAt);
expect(next.formatVersion).toBe(m.formatVersion);
expect(next.vaultIdHash).toEqual(m.vaultIdHash);
});
it("unpackManifest rejects manifests with corrupt internal numerics", {
timeout: 5000,
}, () => {
const m = createManifest(baseInput);
const packed = packManifest(m);
const tampered = new Uint8Array(packed);
// Zero out argon2idMemoryKB which violates min(1) on the schema.
// formatVersion u16 (2) + kdfId u8 (1) + aeadAlgorithm u8 (1) = 4
// argon2idMemoryKB is at offset 4..8.
for (let i = 4; i < 8; i++) tampered[i] = 0;
try {
unpackManifest(tampered);
throw new Error("should have thrown");
} catch (err) {
expect(VaultError.is(err)).toBe(true);
expect((err as VaultError).code).toBe(VAULT_CODES.MANIFEST_INVALID);
}
});
});