CH-J Toolsspráva serverů přes SSH
Menu
Vydané zdroje

CH-J Tools

Procházejte adresáře a soubory konkrétního vydání aplikace.

Stáhnout zdroje ZIP
CH-J / Server Manager 406 souborů
core-20261024T135205Z-7c487d719de2 Přidáno: · Sestaveno:
keyGeneratorService.test.js Přidáno: Datum není evidováno 7,7 KB · 124 řádků
Kód Stáhnout soubor
1"use strict";
3const test = require("node:test");
4const assert = require("node:assert/strict");
5const fs = require("node:fs");
6const os = require("node:os");
7const path = require("node:path");
8const { utils: sshUtils } = require("ssh2");
9const { KeyGeneratorService, PASSWORD_SETS, generateDsaNumbers } = require("../src/main/keys/keyGeneratorService");
11test("key generator creates modern and explicitly confirmed legacy key pairs", () => {
12 const service = new KeyGeneratorService();
13 for (const input of [
14 { type: "ed25519" }, { type: "rsa", bits: 2048 }, { type: "ecdsa", bits: 256 },
15 { type: "dsa", bits: 1024, legacyConfirmed: true }, { type: "rsa1", bits: 1024, legacyConfirmed: true }
16 ]) {
17 const generated = service.generate({ ...input, comment: "test@core" });
18 if (input.type === "rsa1") assert.match(generated.privateKey, /^PuTTY-User-Key-File-2: ssh1-rsa/);
19 else assert.match(generated.privateKey, /BEGIN PRIVATE KEY/);
20 assert.ok(generated.publicKey.length > 40);
21 assert.equal(Object.prototype.hasOwnProperty.call(generated, "passphrase"), false);
22 }
23 assert.throws(() => service.generate({ type: "dsa" }), /explicit confirmation/);
24 assert.throws(() => service.generate({ type: "rsa1" }), /explicit confirmation/);
25});
27test("key generator exports OpenSSH, PEM and PPK formats and reports safe passphrase fallback", () => {
28 const service = new KeyGeneratorService();
29 for (const input of [
30 { type: "ed25519", privateKeyFormat: "openssh" },
31 { type: "rsa", bits: 2048, privateKeyFormat: "openssh" },
32 { type: "ecdsa", bits: 256, privateKeyFormat: "openssh" },
33 { type: "dsa", bits: 1024, privateKeyFormat: "openssh", legacyConfirmed: true }
34 ]) {
35 const generated = service.generate(input);
36 assert.equal(generated.privateKeyFormat, "openssh");
37 assert.match(generated.privateKey, /^-----BEGIN OPENSSH PRIVATE KEY-----/);
38 assert.match(generated.publicKeyOpenSsh, /^(ssh-|ecdsa-)/);
39 const parsed = sshUtils.parseKey(generated.privateKey);
40 assert.equal(parsed instanceof Error, false, parsed?.message);
41 }
42 for (const input of [
43 { type: "ed25519" }, { type: "rsa", bits: 2048 }, { type: "ecdsa", bits: 256 },
44 { type: "dsa", legacyConfirmed: true }, { type: "rsa1", legacyConfirmed: true }
45 ]) {
46 const generated = service.generate({ ...input, privateKeyFormat: "ppk" });
47 assert.equal(generated.privateKeyFormat, "ppk");
48 assert.match(generated.privateKey, /^PuTTY-User-Key-File-2: /);
49 assert.match(generated.privateKey, /\nPrivate-MAC: [a-f0-9]{40}\n$/);
50 }
51 const fallback = service.generate({ type: "ed25519", privateKeyFormat: "openssh", passphrase: "secret" });
52 assert.equal(fallback.privateKeyFormat, "pem");
53 assert.match(fallback.privateKey, /BEGIN ENCRYPTED PRIVATE KEY/);
54 assert.match(fallback.notice, /šifrovaný PEM/);
55});
57test("key generator saves only a selected generated pair with restrictive private mode", async (t) => {
58 const root = fs.mkdtempSync(path.join(os.tmpdir(), "chj-keys-"));
59 t.after(() => fs.rmSync(root, { recursive: true, force: true }));
60 const target = path.join(root, "id_ed25519");
61 const service = new KeyGeneratorService({ selectSavePath: async () => ({ canceled: false, path: target }) });
62 const generated = service.generate({ type: "ed25519" });
63 const saved = await service.save(generated.generationId);
64 assert.equal(saved.privatePath, target);
65 assert.match(fs.readFileSync(target, "utf8"), /BEGIN PRIVATE KEY/);
66 assert.match(fs.readFileSync(`${target}.pub`, "utf8"), /^ssh-ed25519 /);
67 if (process.platform !== "win32") assert.equal(fs.statSync(target).mode & 0o777, 0o600);
68});
70test("DSA works without platform DSA key generation (Electron/BoringSSL) and yields a valid 1024/160 key", () => {
71 const nodeCrypto = require("node:crypto");
72 const original = nodeCrypto.generateKeyPairSync;
73 nodeCrypto.generateKeyPairSync = (type, ...rest) => { if (type === "dsa") throw new Error("Key generation job failed"); return original(type, ...rest); };
74 try {
75 const service = new KeyGeneratorService({});
76 for (const privateKeyFormat of ["openssh", "pem", "ppk"]) {
77 const result = service.generate({ type: "dsa", legacyConfirmed: true, privateKeyFormat, comment: "t" });
78 assert.equal(result.type, "dsa"); assert.match(result.publicKey, /^ssh-dss AAAAB3NzaC1kc3M/);
79 const key = nodeCrypto.createPrivateKey(result.privateKeyPem);
80 assert.deepEqual(key.asymmetricKeyDetails, { modulusLength: 1024, divisorLength: 160 });
81 const signature = nodeCrypto.sign("sha1", Buffer.from("chj"), key);
82 assert.equal(nodeCrypto.verify("sha1", Buffer.from("chj"), nodeCrypto.createPublicKey(key), signature), true);
83 if (privateKeyFormat === "openssh") assert.match(result.privateKey, /^-----BEGIN OPENSSH PRIVATE KEY-----/);
84 if (privateKeyFormat === "ppk") assert.match(result.privateKey, /^PuTTY-User-Key-File-2: ssh-dss/);
85 }
86 assert.throws(() => service.generate({ type: "dsa" }), /explicit confirmation/);
87 } finally { nodeCrypto.generateKeyPairSync = original; }
88});
90test("DSA domain parameters satisfy q | p-1 and g^q = 1 mod p", () => {
91 const { p, q, g, x, y } = generateDsaNumbers();
92 const pow = (b, e, m) => { let r = 1n; b %= m; while (e > 0n) { if (e & 1n) r = r * b % m; b = b * b % m; e >>= 1n; } return r; };
93 assert.equal(p.toString(2).length, 1024); assert.equal(q.toString(2).length, 160);
94 assert.equal((p - 1n) % q, 0n); assert.ok(g > 1n && g < p); assert.equal(pow(g, q, p), 1n);
95 assert.ok(x > 0n && x < q); assert.equal(pow(g, x, p), y);
96});
98test("password generator: length 4-70, selected sets only, grouping every 5th character", () => {
99 const service = new KeyGeneratorService({});
100 const allowed = (options) => new Set(Object.keys(PASSWORD_SETS).filter((key) => options[key]).flatMap((key) => [...PASSWORD_SETS[key]]));
101 for (const options of [{ length: 4, upper: true }, { length: 70, upper: true, lower: true, digits: true, special: true }, { length: 33, digits: true, special: true }]) {
102 for (let run = 0; run < 50; run += 1) {
103 const { password } = service.generatePassword(options);
104 assert.equal([...password].length, options.length);
105 for (const char of password) assert.ok(allowed(options).has(char), `unexpected ${char}`);
106 for (const key of Object.keys(PASSWORD_SETS).filter((name) => options[name])) assert.ok([...password].some((char) => PASSWORD_SETS[key].includes(char)), `${key} present`);
107 }
108 }
109 assert.equal(PASSWORD_SETS.digits, "123456789"); assert.equal(PASSWORD_SETS.special, "-@)¨§'=+");
110 for (let run = 0; run < 50; run += 1) {
111 const { password, randomCharacters } = service.generatePassword({ length: 20, upper: true, digits: true, special: true, grouped: true });
112 assert.match(password, /^[^-]{4}-[^-]{4}-[^-]{4}-[^-]{4}-$/u, password);
113 assert.equal(randomCharacters, 16);
114 }
115 assert.match(service.generatePassword({ length: 12, lower: true, grouped: true }).password, /^[a-z]{4}-[a-z]{4}-[a-z]{2}$/);
116 assert.match(service.generatePassword({ length: 4, lower: true, grouped: true }).password, /^[a-z]{4}$/, "no separator before the 5th character");
117 for (const bad of [3, 71, 12.5, "x"]) assert.throws(() => service.generatePassword({ length: bad, upper: true }), /4-70/);
118 assert.throws(() => service.generatePassword({ length: 12 }), /at least one character set/);
119 // Even the shortest grouped password keeps 4 random characters, so all four sets always fit.
120 const all = service.generatePassword({ length: 5, upper: true, lower: true, digits: true, special: true, grouped: true });
121 assert.match(all.password, /^[^-]{4}-$/u); assert.equal(all.randomCharacters, 4);
122 const seen = new Set(); for (let run = 0; run < 200; run += 1) for (const char of service.generatePassword({ length: 20, digits: true }).password) seen.add(char);
123 assert.equal(seen.size, 9, "every digit 1-9 occurs, 0 never");
124});
SHA-256ea760fd76d48ef54836a8bb9d9c41a64c1b30a7f83e6a5fd10965a6aa7d63a30

CH-J Proprietary Software License 1.14 · Zdroje jsou zveřejněny pod CH-J Proprietary Software License 1.14. Jejich dostupnost nemění licenční podmínky ani neposkytuje další oprávnění.

SHA-256 archivu476ed8aef0f1e96a5acdf223127b6692acdf4492eeaf5d0816b6e2a634fbbda6
Soubory