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,148 @@
import { promises as fs } from "node:fs";
import { tmpdir } from "node:os";
import path from "node:path";
import { ed25519 } from "@suaveplan/crypto-curve";
import { type ChainLevel, TlvChainCodec } from "@suaveplan/crypto-keys";
import {
afterAll,
beforeAll,
describe,
expect,
it,
} from "@suaveplan/testing/runner";
import fc from "fast-check";
import { prepareFileCommand } from "../src/commands/files/files.js";
import { serveFileCommand } from "../src/commands/files/serve-file.js";
import { createRootKeysCommand } from "../src/commands/keys/keys.js";
function arbBytes(min: number, max: number): fc.Arbitrary<Uint8Array> {
return fc
.array(fc.integer({ min: 0, max: 255 }), {
minLength: min,
maxLength: max,
})
.map((arr) => Uint8Array.from(arr));
}
describe("property: TLV chain codec round-trip", () => {
const codec = new TlvChainCodec();
// Build a syntactically-valid ChainLevel by deriving an ed25519
// signerPublicKey from a random seed; signature/payload are arbitrary
// byte arrays — the codec does not validate the cryptographic
// relationship, only the byte layout.
function arbChainLevel(): fc.Arbitrary<ChainLevel> {
return fc
.record({
seed: arbBytes(32, 32),
payload: arbBytes(0, 256),
signature: arbBytes(0, 64),
})
.map(({ seed, payload, signature }) => {
const signerPublicKey = ed25519.getPublicKey(seed);
return {
algorithm: "ed25519",
signerPublicKey,
payload,
signature,
} satisfies ChainLevel;
});
}
it("decode(encode(levels)) is byte-equal to the input for any depth in [1, 5]", {
timeout: 30000,
}, () => {
fc.assert(
fc.property(
fc.array(arbChainLevel(), { minLength: 1, maxLength: 5 }),
(levels) => {
const encoded = codec.encode(levels);
const decoded = codec.decode(encoded);
expect(decoded.length).toBe(levels.length);
for (let i = 0; i < levels.length; i++) {
const a = levels[i];
const b = decoded[i];
if (!a || !b) throw new Error("missing level");
expect(b.algorithm).toBe(a.algorithm);
expect(Array.from(b.signerPublicKey)).toEqual(
Array.from(a.signerPublicKey)
);
expect(Array.from(b.payload)).toEqual(
Array.from(a.payload)
);
expect(Array.from(b.signature)).toEqual(
Array.from(a.signature)
);
}
}
),
{ numRuns: 200 }
);
});
});
describe("property: packet prepare → serve round-trip", () => {
let dir = "";
let vaultPath = "";
const PW = "proppw1234";
beforeAll(async () => {
dir = path.join(
tmpdir(),
`sk-prop-${Date.now()}-${Math.random().toString(36).slice(2)}`
);
await fs.mkdir(dir, { recursive: true });
await createRootKeysCommand(PW, { output: dir });
vaultPath = path.join(dir, "root.vault");
});
afterAll(async () => {
await fs.rm(dir, { recursive: true, force: true });
});
// Each fast-check run decrypts the vault (Argon2id) inside
// prepareFileCommand, which dominates wall-clock time at ~5s/run.
// The chain codec property above runs at 200 numRuns; here we use a
// smaller `numRuns` so the suite stays under the package's 30s
// testTimeout while still exercising a meaningful spread of file
// sizes and packet sizes.
it("serve(prepare(file)) is byte-identical for arbitrary file bytes (1..10 KiB) and packet sizes", {
timeout: 300000,
}, async () => {
let runIndex = 0;
await fc.assert(
fc.asyncProperty(
arbBytes(1, 10 * 1024),
fc.integer({ min: 64, max: 4096 }),
async (fileBytes, packetSize) => {
const label = `run-${runIndex++}`;
const inputPath = path.join(dir, `${label}.bin`);
const preparedDir = path.join(dir, `${label}-prepared`);
const outputPath = path.join(dir, `${label}-out.bin`);
await fs.writeFile(inputPath, fileBytes);
await prepareFileCommand(inputPath, vaultPath, PW, {
output: preparedDir,
packetSize,
});
const result = await serveFileCommand(preparedDir, {
output: outputPath,
});
expect(result.bytesWritten).toBe(fileBytes.byteLength);
const reassembled = await fs.readFile(outputPath);
expect(reassembled.byteLength).toBe(fileBytes.byteLength);
for (let i = 0; i < fileBytes.byteLength; i++) {
if (reassembled[i] !== fileBytes[i]) {
throw new Error(
`byte mismatch at offset ${i} (run=${label})`
);
}
}
await fs.rm(inputPath, { force: true });
await fs.rm(preparedDir, { recursive: true, force: true });
await fs.rm(outputPath, { force: true });
}
),
{ numRuns: 10 }
);
});
});