39 lines
1.9 KiB
TypeScript
39 lines
1.9 KiB
TypeScript
import { randomBytes } from "node:crypto";
|
|
import { describe, expect, it } from "vitest";
|
|
import { decryptRconPassword, encryptRconPassword, rconCommandDigest } from "./rcon-credentials";
|
|
|
|
const key = randomBytes(32).toString("base64");
|
|
const otherKey = randomBytes(32).toString("base64");
|
|
const connectionId = "11111111-1111-4111-8111-111111111111";
|
|
|
|
describe("RCON credential encryption", () => {
|
|
it("round trips with randomized authenticated encryption", () => {
|
|
const first = encryptRconPassword("super-secret", connectionId, key);
|
|
const second = encryptRconPassword("super-secret", connectionId, key);
|
|
|
|
expect(first).not.toBe(second);
|
|
expect(first).not.toContain("super-secret");
|
|
expect(decryptRconPassword(first, connectionId, key)).toBe("super-secret");
|
|
expect(decryptRconPassword(second, connectionId, key)).toBe("super-secret");
|
|
});
|
|
|
|
it("fails closed for tampering, another connection, or another key", () => {
|
|
const encrypted = encryptRconPassword("super-secret", connectionId, key);
|
|
expect(() => decryptRconPassword(`${encrypted}x`, connectionId, key)).toThrow("RCON credential unavailable");
|
|
expect(() => decryptRconPassword(encrypted, "22222222-2222-4222-8222-222222222222", key)).toThrow("RCON credential unavailable");
|
|
expect(() => decryptRconPassword(encrypted, connectionId, otherKey)).toThrow("RCON credential unavailable");
|
|
});
|
|
|
|
it("requires an exact 32-byte deployment key", () => {
|
|
expect(() => encryptRconPassword("secret", connectionId, "not-base64")).toThrow("RCON credential key is not configured");
|
|
});
|
|
|
|
it("creates a keyed, versioned command digest", () => {
|
|
const digest = rconCommandDigest("say secret message", key);
|
|
expect(digest).toMatch(/^hmac-sha256:v1:[a-f0-9]{64}$/u);
|
|
expect(digest).not.toContain("secret message");
|
|
expect(rconCommandDigest("say secret message", key)).toBe(digest);
|
|
expect(rconCommandDigest("say secret message", otherKey)).not.toBe(digest);
|
|
});
|
|
});
|