import { test, expect } from "bun:test"; import crypto from "node:crypto"; import { decodeVncPassword, vncChallengeResponse, vncPortForDisplay, resolveVncPassword, } from "./vnc-keepalive.ts"; // Independent reference for VNC's bit-reversed-key DES, to cross-check the module. const rev = (b: number) => { let r = 0; for (let i = 0; i < 8; i++) r = (r << 1) | ((b >> i) & 1); return r & 0xff; }; const vncKey = (buf: Buffer) => Buffer.from([...buf.subarray(0, 8)].map(rev)); const desEnc = (key: Buffer, data: Buffer) => { const c = crypto.createCipheriv("des-ecb", key, null); c.setAutoPadding(false); return Buffer.concat([c.update(data), c.final()]); }; const FIXED_KEY = Buffer.from([23, 82, 107, 6, 35, 78, 88, 7]); test("decodeVncPassword inverts the fixed-key obfuscation", () => { const pw = Buffer.from("s3cr3t\0\0", "binary"); // 8 bytes, trailing nulls const obf = desEnc(vncKey(FIXED_KEY), pw); // how vncpasswd stores it expect(decodeVncPassword(obf).toString()).toBe("s3cr3t"); }); test("vncChallengeResponse encrypts both challenge blocks with the bit-reversed password key", () => { const pw = Buffer.from("hunter12"); const challenge = crypto.randomBytes(16); const expected = desEnc(vncKey(pw), challenge); const got = vncChallengeResponse(pw, challenge); expect(got.length).toBe(16); expect(got.equals(expected)).toBe(true); }); test("vncPortForDisplay maps an X display to its RFB port", () => { expect(vncPortForDisplay(":1")).toBe(5901); expect(vncPortForDisplay(":0")).toBe(5900); expect(vncPortForDisplay(":5")).toBe(5905); }); test("resolveVncPassword prefers the VNC_PASSWORD env var", () => { const pw = resolveVncPassword({ VNC_PASSWORD: "letmein9" } as NodeJS.ProcessEnv); expect(pw?.toString()).toBe("letmein9"); }); test("resolveVncPassword returns null when nothing is available", () => { const pw = resolveVncPassword({ VNC_PASSWD_FILE: "/nonexistent/path/xyz" } as NodeJS.ProcessEnv); expect(pw).toBeNull(); });