Vydané zdroje
Stáhnout zdroje ZIP
CH-J Tools
Procházejte adresáře a soubory konkrétního vydání aplikace.
CH-J / Server Manager
406 souborů
core-20261024T135205Z-7c487d719de2
Přidáno: · Sestaveno:
1
"use strict";3
const test = require("node:test");4
const assert = require("node:assert/strict");5
const fs = require("node:fs");6
const os = require("node:os");7
const path = require("node:path");8
const { utils: sshUtils } = require("ssh2");9
const { KeyGeneratorService, PASSWORD_SETS, generateDsaNumbers } = require("../src/main/keys/keyGeneratorService");11
test("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
});27
test("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
});57
test("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
});70
test("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
});90
test("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
});98
test("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-256
ea760fd76d48ef54836a8bb9d9c41a64c1b30a7f83e6a5fd10965a6aa7d63a30CH-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 archivu
476ed8aef0f1e96a5acdf223127b6692acdf4492eeaf5d0816b6e2a634fbbda6