@sebastienrousseau/crypto-cli 0.0.8 → 0.0.24
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +685 -3
- package/dist/cli.js +37 -196
- package/dist/commands/decrypt.command.js +2 -2
- package/dist/commands/encrypt.command.js +2 -2
- package/dist/commands/generate.command.js +2 -2
- package/dist/commands/modern/cbom.command.d.ts +2 -1
- package/dist/commands/modern/cbom.command.d.ts.map +1 -1
- package/dist/commands/modern/cbom.command.js +99 -146
- package/dist/commands/modern/cbom.core.d.ts +5 -0
- package/dist/commands/modern/cbom.core.d.ts.map +1 -0
- package/dist/commands/modern/cbom.core.js +57 -0
- package/dist/commands/modern/encrypt.command.d.ts.map +1 -1
- package/dist/commands/modern/encrypt.command.js +42 -63
- package/dist/commands/modern/hash.command.d.ts.map +1 -1
- package/dist/commands/modern/hash.command.js +1 -1
- package/dist/commands/modern/keygen.command.js +1 -1
- package/dist/commands/modern/password-hash.command.d.ts.map +1 -1
- package/dist/commands/modern/password-hash.command.js +28 -27
- package/dist/commands/modern/sign.command.d.ts.map +1 -1
- package/dist/commands/modern/sign.command.js +83 -73
- package/dist/commands/reformat.command.js +2 -2
- package/dist/commands/revoke.command.js +2 -2
- package/dist/commands/session.command.js +2 -2
- package/dist/commands/sign.command.js +2 -2
- package/dist/commands/verify.command.js +2 -2
- package/dist/menu.d.ts +12 -0
- package/dist/menu.d.ts.map +1 -0
- package/dist/menu.js +107 -0
- package/dist/program/cbom.d.ts +4 -0
- package/dist/program/cbom.d.ts.map +1 -0
- package/dist/program/cbom.js +49 -0
- package/dist/program/encrypt.d.ts +6 -0
- package/dist/program/encrypt.d.ts.map +1 -0
- package/dist/program/encrypt.js +73 -0
- package/dist/program/hash.d.ts +4 -0
- package/dist/program/hash.d.ts.map +1 -0
- package/dist/program/hash.js +21 -0
- package/dist/program/hpke.d.ts +4 -0
- package/dist/program/hpke.d.ts.map +1 -0
- package/dist/program/hpke.js +111 -0
- package/dist/program/index.d.ts +7 -0
- package/dist/program/index.d.ts.map +1 -0
- package/dist/program/index.js +77 -0
- package/dist/program/io.d.ts +35 -0
- package/dist/program/io.d.ts.map +1 -0
- package/dist/program/io.js +65 -0
- package/dist/program/keygen.d.ts +4 -0
- package/dist/program/keygen.d.ts.map +1 -0
- package/dist/program/keygen.js +32 -0
- package/dist/program/kms.d.ts +8 -0
- package/dist/program/kms.d.ts.map +1 -0
- package/dist/program/kms.js +247 -0
- package/dist/program/opaque.d.ts +4 -0
- package/dist/program/opaque.d.ts.map +1 -0
- package/dist/program/opaque.js +209 -0
- package/dist/program/password.d.ts +4 -0
- package/dist/program/password.d.ts.map +1 -0
- package/dist/program/password.js +73 -0
- package/dist/program/secrets.d.ts +30 -0
- package/dist/program/secrets.d.ts.map +1 -0
- package/dist/program/secrets.js +70 -0
- package/dist/program/sign.d.ts +18 -0
- package/dist/program/sign.d.ts.map +1 -0
- package/dist/program/sign.js +130 -0
- package/dist/program/stream-multi.d.ts +7 -0
- package/dist/program/stream-multi.d.ts.map +1 -0
- package/dist/program/stream-multi.js +163 -0
- package/dist/program/stream.d.ts +6 -0
- package/dist/program/stream.d.ts.map +1 -0
- package/dist/program/stream.js +102 -0
- package/dist/program/usage.d.ts +7 -0
- package/dist/program/usage.d.ts.map +1 -0
- package/dist/program/usage.js +36 -0
- package/package.json +21 -34
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.registerCbom = void 0;
|
|
4
|
+
const promises_1 = require("node:fs/promises");
|
|
5
|
+
const commander_1 = require("commander");
|
|
6
|
+
const cbom_core_1 = require("../commands/modern/cbom.core");
|
|
7
|
+
const io_1 = require("./io");
|
|
8
|
+
const deliver = async (ctx, document, opts, label) => {
|
|
9
|
+
const pretty = JSON.stringify(document, null, 2);
|
|
10
|
+
if (opts.output === undefined) {
|
|
11
|
+
(0, io_1.emit)(ctx.io, Boolean(opts.json), document, pretty);
|
|
12
|
+
return;
|
|
13
|
+
}
|
|
14
|
+
await (0, promises_1.writeFile)(opts.output, `${pretty}\n`, "utf8");
|
|
15
|
+
ctx.io.stderr(`${label} written to ${opts.output}\n`);
|
|
16
|
+
};
|
|
17
|
+
const withOutputOptions = (command, what) => command
|
|
18
|
+
.option("-o, --output <file>", `write the ${what} to a file, not stdout`)
|
|
19
|
+
.option("--json", `print the ${what} as one line of JSON`);
|
|
20
|
+
const registerScan = (cbom, ctx) => withOutputOptions(cbom
|
|
21
|
+
.command("scan")
|
|
22
|
+
.description("Scan source code and print its CBOM")
|
|
23
|
+
.argument("[directory]", "directory or file to scan", ".")
|
|
24
|
+
.addOption(new commander_1.Option("-f, --format <format>", "CBOM standard")
|
|
25
|
+
.choices(cbom_core_1.CBOM_FORMATS)
|
|
26
|
+
.default("cyclonedx")), "CBOM").action(async (directory, opts) => {
|
|
27
|
+
await deliver(ctx, await (0, cbom_core_1.buildCbom)(directory, opts.format), opts, "CBOM");
|
|
28
|
+
});
|
|
29
|
+
const registerAudit = (cbom, ctx) => withOutputOptions(cbom
|
|
30
|
+
.command("audit")
|
|
31
|
+
.description("Validate and audit a CBOM; exits 1 when the audit status is FAIL")
|
|
32
|
+
.argument("[file]", "CBOM JSON file; '-' or omitted reads standard input"), "audit report").action(async (file, opts) => {
|
|
33
|
+
const raw = (await (0, io_1.readInput)(file, ctx.io)).toString("utf8");
|
|
34
|
+
const result = await (0, cbom_core_1.auditCbomJson)(raw);
|
|
35
|
+
await deliver(ctx, result, opts, "Audit report");
|
|
36
|
+
if (result.status === "FAIL")
|
|
37
|
+
ctx.exitCode = io_1.EXIT.FAILURE;
|
|
38
|
+
});
|
|
39
|
+
const registerCbom = (program, ctx) => {
|
|
40
|
+
const cbom = program
|
|
41
|
+
.command("cbom")
|
|
42
|
+
.description("Generate or audit a Cryptographic Bill of Materials")
|
|
43
|
+
.helpCommand(false);
|
|
44
|
+
registerScan(cbom, ctx);
|
|
45
|
+
registerAudit(cbom, ctx);
|
|
46
|
+
return cbom;
|
|
47
|
+
};
|
|
48
|
+
exports.registerCbom = registerCbom;
|
|
49
|
+
//# sourceMappingURL=cbom.js.map
|
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
import { Command } from "commander";
|
|
2
|
+
import { type RunContext } from "./io";
|
|
3
|
+
export declare const parseSymmetricKey: (raw: Buffer) => Uint8Array;
|
|
4
|
+
export declare const registerEncrypt: (program: Command, ctx: RunContext) => Command;
|
|
5
|
+
export declare const registerDecrypt: (program: Command, ctx: RunContext) => Command;
|
|
6
|
+
//# sourceMappingURL=encrypt.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"encrypt.d.ts","sourceRoot":"","sources":["../../src/program/encrypt.ts"],"names":[],"mappings":"AAKA,OAAO,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;AAEpC,OAAO,EAKL,KAAK,UAAU,EAChB,MAAM,MAAM,CAAC;AA6Bd,eAAO,MAAM,iBAAiB,GAAI,KAAK,MAAM,KAAG,UAQ/C,CAAC;AAgEF,eAAO,MAAM,eAAe,GAAI,SAAS,OAAO,EAAE,KAAK,UAAU,YAU7D,CAAC;AASL,eAAO,MAAM,eAAe,GAAI,SAAS,OAAO,EAAE,KAAK,UAAU,YA+B7D,CAAC"}
|
|
@@ -0,0 +1,73 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.registerDecrypt = exports.registerEncrypt = exports.parseSymmetricKey = void 0;
|
|
4
|
+
const high_level_1 = require("@sebastienrousseau/crypto-lib/high-level");
|
|
5
|
+
const io_1 = require("./io");
|
|
6
|
+
const secrets_1 = require("./secrets");
|
|
7
|
+
const KEY_LENGTH = 32;
|
|
8
|
+
const HEX_KEY = /^[0-9a-fA-F]{64}$/;
|
|
9
|
+
const BASE64 = /^[A-Za-z0-9+/]+={0,2}$/;
|
|
10
|
+
const parseSymmetricKey = (raw) => {
|
|
11
|
+
const text = raw.toString("latin1").trim();
|
|
12
|
+
if (HEX_KEY.test(text))
|
|
13
|
+
return Buffer.from(text, "hex");
|
|
14
|
+
if (raw.length === KEY_LENGTH)
|
|
15
|
+
return raw;
|
|
16
|
+
throw new Error(`the key must be ${KEY_LENGTH} bytes: 64 hexadecimal characters ` +
|
|
17
|
+
`or ${KEY_LENGTH} raw bytes`);
|
|
18
|
+
};
|
|
19
|
+
exports.parseSymmetricKey = parseSymmetricKey;
|
|
20
|
+
const readKey = async (ctx, opts, file) => {
|
|
21
|
+
const source = { file: opts.keyFile, stdin: opts.keyStdin };
|
|
22
|
+
const raw = await (0, secrets_1.requireSecret)({
|
|
23
|
+
io: ctx.io,
|
|
24
|
+
source,
|
|
25
|
+
flags: secrets_1.KEY_FLAGS,
|
|
26
|
+
dataOnStdin: (0, io_1.isStdin)(file),
|
|
27
|
+
checkMode: true,
|
|
28
|
+
}, "key");
|
|
29
|
+
return (0, exports.parseSymmetricKey)(raw);
|
|
30
|
+
};
|
|
31
|
+
const parseSealed = (input) => {
|
|
32
|
+
const sealed = (0, io_1.valueOrJsonField)(input, "sealed");
|
|
33
|
+
if (!BASE64.test(sealed)) {
|
|
34
|
+
throw new Error("the input is not base64 from crypto-cli encrypt");
|
|
35
|
+
}
|
|
36
|
+
return Buffer.from(sealed, "base64");
|
|
37
|
+
};
|
|
38
|
+
const secretboxCommand = (program, name, description, input) => (0, secrets_1.withSecretOptions)(program
|
|
39
|
+
.command(name)
|
|
40
|
+
.description(description)
|
|
41
|
+
.argument("[file]", `${input}; '-' or omitted reads standard input`), secrets_1.KEY_FLAGS, {
|
|
42
|
+
what: "key",
|
|
43
|
+
format: "32 bytes, as 64 hex characters or raw",
|
|
44
|
+
takesData: true,
|
|
45
|
+
}).option("--json", "print the result as one line of JSON");
|
|
46
|
+
const registerEncrypt = (program, ctx) => secretboxCommand(program, "encrypt", "Encrypt with XChaCha20-Poly1305 and print the sealed box as base64", "file to encrypt").action(async (file, opts) => {
|
|
47
|
+
const key = await readKey(ctx, opts, file);
|
|
48
|
+
const result = high_level_1.secretbox.seal(key, await (0, io_1.readInput)(file, ctx.io));
|
|
49
|
+
(0, io_1.emit)(ctx.io, Boolean(opts.json), result, result.sealed);
|
|
50
|
+
});
|
|
51
|
+
exports.registerEncrypt = registerEncrypt;
|
|
52
|
+
const registerDecrypt = (program, ctx) => secretboxCommand(program, "decrypt", "Decrypt the output of encrypt; exits 1 on a wrong key or modified input", "output of encrypt (base64 or its --json line)").action(async (file, opts) => {
|
|
53
|
+
const key = await readKey(ctx, opts, file);
|
|
54
|
+
const sealed = parseSealed(await (0, io_1.readInput)(file, ctx.io));
|
|
55
|
+
let plaintext;
|
|
56
|
+
try {
|
|
57
|
+
plaintext = high_level_1.secretbox.open(key, sealed);
|
|
58
|
+
}
|
|
59
|
+
catch {
|
|
60
|
+
throw new Error("decryption failed: wrong key, or the input was modified or truncated");
|
|
61
|
+
}
|
|
62
|
+
if (!opts.json) {
|
|
63
|
+
ctx.io.stdout(plaintext);
|
|
64
|
+
return;
|
|
65
|
+
}
|
|
66
|
+
(0, io_1.emit)(ctx.io, true, {
|
|
67
|
+
plaintext: Buffer.from(plaintext).toString("base64"),
|
|
68
|
+
encoding: "base64",
|
|
69
|
+
algorithm: "xchacha20-poly1305",
|
|
70
|
+
}, "");
|
|
71
|
+
});
|
|
72
|
+
exports.registerDecrypt = registerDecrypt;
|
|
73
|
+
//# sourceMappingURL=encrypt.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"hash.d.ts","sourceRoot":"","sources":["../../src/program/hash.ts"],"names":[],"mappings":"AAKA,OAAO,EAAE,OAAO,EAAU,MAAM,WAAW,CAAC;AAM5C,OAAO,EAAmB,KAAK,UAAU,EAAE,MAAM,MAAM,CAAC;AAcxD,eAAO,MAAM,YAAY,GAAI,SAAS,OAAO,EAAE,KAAK,UAAU,YAexD,CAAC"}
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.registerHash = void 0;
|
|
4
|
+
const commander_1 = require("commander");
|
|
5
|
+
const modern_1 = require("@sebastienrousseau/crypto-lib/modern");
|
|
6
|
+
const io_1 = require("./io");
|
|
7
|
+
const registerHash = (program, ctx) => program
|
|
8
|
+
.command("hash")
|
|
9
|
+
.description("Hash a file, or standard input when no file is given")
|
|
10
|
+
.argument("[file]", "file to hash; '-' or omitted reads standard input")
|
|
11
|
+
.addOption(new commander_1.Option("-a, --algorithm <name>", "hash algorithm")
|
|
12
|
+
.choices(modern_1.HASH_ALGORITHMS)
|
|
13
|
+
.default("sha256"))
|
|
14
|
+
.option("--json", "print the result as one line of JSON")
|
|
15
|
+
.action(async (file, opts) => {
|
|
16
|
+
const data = await (0, io_1.readInput)(file, ctx.io);
|
|
17
|
+
const result = (0, modern_1.hash)({ algorithm: opts.algorithm, data });
|
|
18
|
+
(0, io_1.emit)(ctx.io, Boolean(opts.json), result, result.digest);
|
|
19
|
+
});
|
|
20
|
+
exports.registerHash = registerHash;
|
|
21
|
+
//# sourceMappingURL=hash.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"hpke.d.ts","sourceRoot":"","sources":["../../src/program/hpke.ts"],"names":[],"mappings":"AAKA,OAAO,EAAE,OAAO,EAAU,MAAM,WAAW,CAAC;AAM5C,OAAO,EAAqC,KAAK,UAAU,EAAE,MAAM,MAAM,CAAC;AAyG1E,wBAAgB,YAAY,CAAC,OAAO,EAAE,OAAO,EAAE,GAAG,EAAE,UAAU,GAAG,IAAI,CA0DpE"}
|
|
@@ -0,0 +1,111 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.registerHpke = registerHpke;
|
|
4
|
+
const commander_1 = require("commander");
|
|
5
|
+
const modern_1 = require("@sebastienrousseau/crypto-lib/modern");
|
|
6
|
+
const io_1 = require("./io");
|
|
7
|
+
const KEM_CHOICES = [
|
|
8
|
+
"x25519-ml-kem-768",
|
|
9
|
+
"x25519",
|
|
10
|
+
"p256",
|
|
11
|
+
];
|
|
12
|
+
const AEAD_CHOICES = [
|
|
13
|
+
"chacha20-poly1305",
|
|
14
|
+
"aes-128-gcm",
|
|
15
|
+
];
|
|
16
|
+
function stringToHex(text) {
|
|
17
|
+
const isHex = /^[0-9a-fA-F]*$/.test(text) && text.length % 2 === 0;
|
|
18
|
+
return isHex ? text : Buffer.from(text, "utf8").toString("hex");
|
|
19
|
+
}
|
|
20
|
+
function executeKeygen(opts, ctx) {
|
|
21
|
+
const kp = (0, modern_1.hpkeGenerateKeyPair)(opts.kem);
|
|
22
|
+
const data = {
|
|
23
|
+
kem: opts.kem,
|
|
24
|
+
publicKey: kp.publicKey,
|
|
25
|
+
privateKey: kp.privateKey,
|
|
26
|
+
};
|
|
27
|
+
const desc = [
|
|
28
|
+
`Algorithm: ${opts.kem}`,
|
|
29
|
+
`Public Key: ${kp.publicKey}`,
|
|
30
|
+
`Private Key: ${kp.privateKey}`,
|
|
31
|
+
].join("\n");
|
|
32
|
+
(0, io_1.emit)(ctx.io, Boolean(opts.json), data, desc);
|
|
33
|
+
}
|
|
34
|
+
async function executeSeal(file, opts, ctx) {
|
|
35
|
+
const raw = await (0, io_1.readInput)(file, ctx.io);
|
|
36
|
+
const plaintextHex = raw.toString("hex");
|
|
37
|
+
const res = (0, modern_1.hpkeSeal)({
|
|
38
|
+
recipientPublicKey: opts.publicKey,
|
|
39
|
+
plaintext: plaintextHex,
|
|
40
|
+
suite: { kem: opts.kem, aead: opts.aead },
|
|
41
|
+
...(opts.info !== undefined ? { info: stringToHex(opts.info) } : {}),
|
|
42
|
+
...(opts.aad !== undefined ? { aad: stringToHex(opts.aad) } : {}),
|
|
43
|
+
});
|
|
44
|
+
const data = {
|
|
45
|
+
encapsulatedKey: res.encapsulatedKey,
|
|
46
|
+
ciphertext: res.ciphertext,
|
|
47
|
+
};
|
|
48
|
+
const desc = [
|
|
49
|
+
`Encapsulated: ${res.encapsulatedKey}`,
|
|
50
|
+
`Ciphertext: ${res.ciphertext}`,
|
|
51
|
+
].join("\n");
|
|
52
|
+
(0, io_1.emit)(ctx.io, Boolean(opts.json), data, desc);
|
|
53
|
+
}
|
|
54
|
+
async function executeOpen(file, opts, ctx) {
|
|
55
|
+
const raw = await (0, io_1.readInput)(file, ctx.io);
|
|
56
|
+
const ciphertext = (0, io_1.valueOrJsonField)(raw, "ciphertext").trim();
|
|
57
|
+
const res = (0, modern_1.hpkeOpen)({
|
|
58
|
+
recipientPrivateKey: opts.secretKey,
|
|
59
|
+
encapsulatedKey: opts.encapsulatedKey,
|
|
60
|
+
ciphertext,
|
|
61
|
+
suite: { kem: opts.kem, aead: opts.aead },
|
|
62
|
+
...(opts.info !== undefined ? { info: stringToHex(opts.info) } : {}),
|
|
63
|
+
...(opts.aad !== undefined ? { aad: stringToHex(opts.aad) } : {}),
|
|
64
|
+
});
|
|
65
|
+
const plaintext = Buffer.from(res.plaintext, "hex").toString("utf8");
|
|
66
|
+
const data = { plaintext, hex: res.plaintext };
|
|
67
|
+
(0, io_1.emit)(ctx.io, Boolean(opts.json), data, plaintext);
|
|
68
|
+
}
|
|
69
|
+
function registerHpke(program, ctx) {
|
|
70
|
+
const hpke = program
|
|
71
|
+
.command("hpke")
|
|
72
|
+
.description("Hybrid Public Key Encryption (HPKE RFC 9180 and PQ hybrid)");
|
|
73
|
+
hpke
|
|
74
|
+
.command("keygen")
|
|
75
|
+
.description("Generate an HPKE key pair")
|
|
76
|
+
.addOption(new commander_1.Option("-k, --kem <kem>", "key encapsulation mechanism")
|
|
77
|
+
.choices(KEM_CHOICES)
|
|
78
|
+
.default("x25519-ml-kem-768"))
|
|
79
|
+
.option("--json", "print key pair as JSON")
|
|
80
|
+
.action((opts) => executeKeygen(opts, ctx));
|
|
81
|
+
hpke
|
|
82
|
+
.command("seal [file]")
|
|
83
|
+
.description("Encrypt a message using HPKE")
|
|
84
|
+
.requiredOption("-p, --public-key <hex>", "recipient public key (hex)")
|
|
85
|
+
.addOption(new commander_1.Option("-k, --kem <kem>", "key encapsulation mechanism")
|
|
86
|
+
.choices(KEM_CHOICES)
|
|
87
|
+
.default("x25519-ml-kem-768"))
|
|
88
|
+
.addOption(new commander_1.Option("-a, --aead <aead>", "authenticated encryption algorithm")
|
|
89
|
+
.choices(AEAD_CHOICES)
|
|
90
|
+
.default("chacha20-poly1305"))
|
|
91
|
+
.option("--info <info>", "application-supplied info string")
|
|
92
|
+
.option("--aad <aad>", "additional authenticated data")
|
|
93
|
+
.option("--json", "print ciphertext and encapsulated key as JSON")
|
|
94
|
+
.action((file, opts) => executeSeal(file, opts, ctx));
|
|
95
|
+
hpke
|
|
96
|
+
.command("open [file]")
|
|
97
|
+
.description("Decrypt an HPKE ciphertext")
|
|
98
|
+
.requiredOption("-s, --secret-key <hex>", "recipient private key (hex)")
|
|
99
|
+
.requiredOption("-e, --encapsulated-key <hex>", "encapsulated key (hex)")
|
|
100
|
+
.addOption(new commander_1.Option("-k, --kem <kem>", "key encapsulation mechanism")
|
|
101
|
+
.choices(KEM_CHOICES)
|
|
102
|
+
.default("x25519-ml-kem-768"))
|
|
103
|
+
.addOption(new commander_1.Option("-a, --aead <aead>", "authenticated encryption algorithm")
|
|
104
|
+
.choices(AEAD_CHOICES)
|
|
105
|
+
.default("chacha20-poly1305"))
|
|
106
|
+
.option("--info <info>", "application-supplied info string")
|
|
107
|
+
.option("--aad <aad>", "additional authenticated data")
|
|
108
|
+
.option("--json", "print plaintext and hex as JSON")
|
|
109
|
+
.action((file, opts) => executeOpen(file, opts, ctx));
|
|
110
|
+
}
|
|
111
|
+
//# sourceMappingURL=hpke.js.map
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
import { Command } from "commander";
|
|
2
|
+
import { type CliIO, type RunContext } from "./io";
|
|
3
|
+
export { EXIT, exitOnClosedPipe, processIO, type CliIO, type RunContext, } from "./io";
|
|
4
|
+
export declare const buildProgram: (ctx: RunContext, version: string) => Command;
|
|
5
|
+
export declare const run: (args: string[], io: CliIO) => Promise<number>;
|
|
6
|
+
export declare const main: (args: string[], io: CliIO, menu: () => Promise<void>) => Promise<number>;
|
|
7
|
+
//# sourceMappingURL=index.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/program/index.ts"],"names":[],"mappings":"AAKA,OAAO,EAAE,OAAO,EAAkB,MAAM,WAAW,CAAC;AAKpD,OAAO,EAAoB,KAAK,KAAK,EAAE,KAAK,UAAU,EAAE,MAAM,MAAM,CAAC;AASrE,OAAO,EACL,IAAI,EACJ,gBAAgB,EAChB,SAAS,EACT,KAAK,KAAK,EACV,KAAK,UAAU,GAChB,MAAM,MAAM,CAAC;AASd,eAAO,MAAM,YAAY,GAAI,KAAK,UAAU,EAAE,SAAS,MAAM,KAAG,OAyB/D,CAAC;AAmBF,eAAO,MAAM,GAAG,GAAU,MAAM,MAAM,EAAE,EAAE,IAAI,KAAK,KAAG,OAAO,CAAC,MAAM,CASnE,CAAC;AAWF,eAAO,MAAM,IAAI,GACf,MAAM,MAAM,EAAE,EACd,IAAI,KAAK,EACT,MAAM,MAAM,OAAO,CAAC,IAAI,CAAC,KACxB,OAAO,CAAC,MAAM,CAShB,CAAC"}
|
|
@@ -0,0 +1,77 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.main = exports.run = exports.buildProgram = exports.processIO = exports.exitOnClosedPipe = exports.EXIT = void 0;
|
|
4
|
+
const commander_1 = require("commander");
|
|
5
|
+
const version_utils_1 = require("../utils/version.utils");
|
|
6
|
+
const cbom_1 = require("./cbom");
|
|
7
|
+
const hash_1 = require("./hash");
|
|
8
|
+
const encrypt_1 = require("./encrypt");
|
|
9
|
+
const io_1 = require("./io");
|
|
10
|
+
const keygen_1 = require("./keygen");
|
|
11
|
+
const password_1 = require("./password");
|
|
12
|
+
const sign_1 = require("./sign");
|
|
13
|
+
const stream_1 = require("./stream");
|
|
14
|
+
const opaque_1 = require("./opaque");
|
|
15
|
+
const hpke_1 = require("./hpke");
|
|
16
|
+
const kms_1 = require("./kms");
|
|
17
|
+
var io_2 = require("./io");
|
|
18
|
+
Object.defineProperty(exports, "EXIT", { enumerable: true, get: function () { return io_2.EXIT; } });
|
|
19
|
+
Object.defineProperty(exports, "exitOnClosedPipe", { enumerable: true, get: function () { return io_2.exitOnClosedPipe; } });
|
|
20
|
+
Object.defineProperty(exports, "processIO", { enumerable: true, get: function () { return io_2.processIO; } });
|
|
21
|
+
const buildProgram = (ctx, version) => {
|
|
22
|
+
const program = new commander_1.Command("crypto-cli")
|
|
23
|
+
.description("Cryptographic operations from the command line. Run without " +
|
|
24
|
+
"arguments in a terminal for the interactive menu.")
|
|
25
|
+
.version(version, "-V, --version", "print the version")
|
|
26
|
+
.helpOption("-h, --help", "print help")
|
|
27
|
+
.helpCommand(false)
|
|
28
|
+
.exitOverride()
|
|
29
|
+
.configureOutput({ writeOut: ctx.io.stdout, writeErr: ctx.io.stderr })
|
|
30
|
+
.showSuggestionAfterError();
|
|
31
|
+
(0, hash_1.registerHash)(program, ctx);
|
|
32
|
+
(0, keygen_1.registerKeygen)(program, ctx);
|
|
33
|
+
(0, encrypt_1.registerEncrypt)(program, ctx);
|
|
34
|
+
(0, encrypt_1.registerDecrypt)(program, ctx);
|
|
35
|
+
(0, sign_1.registerSign)(program, ctx);
|
|
36
|
+
(0, sign_1.registerVerify)(program, ctx);
|
|
37
|
+
(0, password_1.registerPassword)(program, ctx);
|
|
38
|
+
(0, cbom_1.registerCbom)(program, ctx);
|
|
39
|
+
(0, stream_1.registerStream)(program, ctx);
|
|
40
|
+
(0, opaque_1.registerOpaque)(program, ctx);
|
|
41
|
+
(0, hpke_1.registerHpke)(program, ctx);
|
|
42
|
+
(0, kms_1.registerKms)(program, ctx);
|
|
43
|
+
return program;
|
|
44
|
+
};
|
|
45
|
+
exports.buildProgram = buildProgram;
|
|
46
|
+
const exitCodeFor = (err, io) => {
|
|
47
|
+
if (err instanceof commander_1.CommanderError) {
|
|
48
|
+
return err.exitCode === 0 ? io_1.EXIT.OK : io_1.EXIT.USAGE;
|
|
49
|
+
}
|
|
50
|
+
io.stderr(`crypto-cli: ${err.message}\n`);
|
|
51
|
+
return err instanceof io_1.UsageError ? io_1.EXIT.USAGE : io_1.EXIT.FAILURE;
|
|
52
|
+
};
|
|
53
|
+
const run = async (args, io) => {
|
|
54
|
+
const ctx = { io, exitCode: io_1.EXIT.OK };
|
|
55
|
+
const program = (0, exports.buildProgram)(ctx, await (0, version_utils_1.getVersion)());
|
|
56
|
+
try {
|
|
57
|
+
await program.parseAsync(args, { from: "user" });
|
|
58
|
+
return ctx.exitCode;
|
|
59
|
+
}
|
|
60
|
+
catch (err) {
|
|
61
|
+
return exitCodeFor(err, io);
|
|
62
|
+
}
|
|
63
|
+
};
|
|
64
|
+
exports.run = run;
|
|
65
|
+
const main = async (args, io, menu) => {
|
|
66
|
+
if (args.length > 0)
|
|
67
|
+
return (0, exports.run)(args, io);
|
|
68
|
+
if (io.isTTY) {
|
|
69
|
+
await menu();
|
|
70
|
+
return io_1.EXIT.OK;
|
|
71
|
+
}
|
|
72
|
+
const ctx = { io, exitCode: io_1.EXIT.OK };
|
|
73
|
+
io.stderr((0, exports.buildProgram)(ctx, await (0, version_utils_1.getVersion)()).helpInformation());
|
|
74
|
+
return io_1.EXIT.USAGE;
|
|
75
|
+
};
|
|
76
|
+
exports.main = main;
|
|
77
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
export declare const EXIT: {
|
|
2
|
+
readonly OK: 0;
|
|
3
|
+
readonly FAILURE: 1;
|
|
4
|
+
readonly USAGE: 2;
|
|
5
|
+
};
|
|
6
|
+
export type InputSource = AsyncIterable<Buffer | string> | Iterable<Buffer | string>;
|
|
7
|
+
export interface CliIO {
|
|
8
|
+
stdin: InputSource;
|
|
9
|
+
stdout: (data: string | Uint8Array) => void;
|
|
10
|
+
stderr: (text: string) => void;
|
|
11
|
+
isTTY: boolean;
|
|
12
|
+
promptSecret?: ((message: string) => Promise<string | undefined>) | undefined;
|
|
13
|
+
}
|
|
14
|
+
export declare class UsageError extends Error {
|
|
15
|
+
}
|
|
16
|
+
export interface RunContext {
|
|
17
|
+
io: CliIO;
|
|
18
|
+
exitCode: number;
|
|
19
|
+
}
|
|
20
|
+
export declare const isInteractive: (input: {
|
|
21
|
+
isTTY?: boolean;
|
|
22
|
+
}, output: {
|
|
23
|
+
isTTY?: boolean;
|
|
24
|
+
}) => boolean;
|
|
25
|
+
export declare const exitOnClosedPipe: (stream: NodeJS.EventEmitter, exit: (code: number) => void) => NodeJS.EventEmitter<any>;
|
|
26
|
+
export declare const promptHidden: (message: string) => Promise<string | undefined>;
|
|
27
|
+
export declare const secretPrompt: (stdin: {
|
|
28
|
+
isTTY?: boolean;
|
|
29
|
+
}) => ((message: string) => Promise<string | undefined>) | undefined;
|
|
30
|
+
export declare const processIO: () => CliIO;
|
|
31
|
+
export declare const isStdin: (file: string | undefined) => file is undefined | "-";
|
|
32
|
+
export declare const readInput: (file: string | undefined, io: CliIO) => Promise<Buffer<ArrayBuffer>>;
|
|
33
|
+
export declare const valueOrJsonField: (input: Buffer, field: string) => string;
|
|
34
|
+
export declare const emit: (io: CliIO, json: boolean, data: unknown, text: string) => void;
|
|
35
|
+
//# sourceMappingURL=io.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"io.d.ts","sourceRoot":"","sources":["../../src/program/io.ts"],"names":[],"mappings":"AAoBA,eAAO,MAAM,IAAI;;;;CAA2C,CAAC;AAG7D,MAAM,MAAM,WAAW,GACrB,aAAa,CAAC,MAAM,GAAG,MAAM,CAAC,GAAG,QAAQ,CAAC,MAAM,GAAG,MAAM,CAAC,CAAC;AAG7D,MAAM,WAAW,KAAK;IAEpB,KAAK,EAAE,WAAW,CAAC;IAEnB,MAAM,EAAE,CAAC,IAAI,EAAE,MAAM,GAAG,UAAU,KAAK,IAAI,CAAC;IAE5C,MAAM,EAAE,CAAC,IAAI,EAAE,MAAM,KAAK,IAAI,CAAC;IAE/B,KAAK,EAAE,OAAO,CAAC;IAKf,YAAY,CAAC,EAAE,CAAC,CAAC,OAAO,EAAE,MAAM,KAAK,OAAO,CAAC,MAAM,GAAG,SAAS,CAAC,CAAC,GAAG,SAAS,CAAC;CAC/E;AAMD,qBAAa,UAAW,SAAQ,KAAK;CAAG;AAGxC,MAAM,WAAW,UAAU;IAEzB,EAAE,EAAE,KAAK,CAAC;IAEV,QAAQ,EAAE,MAAM,CAAC;CAClB;AASD,eAAO,MAAM,aAAa,GACxB,OAAO;IAAE,KAAK,CAAC,EAAE,OAAO,CAAA;CAAE,EAC1B,QAAQ;IAAE,KAAK,CAAC,EAAE,OAAO,CAAA;CAAE,YACY,CAAC;AAO1C,eAAO,MAAM,gBAAgB,GAC3B,QAAQ,MAAM,CAAC,YAAY,EAC3B,MAAM,CAAC,IAAI,EAAE,MAAM,KAAK,IAAI,6BAK1B,CAAC;AASL,eAAO,MAAM,YAAY,GAAU,SAAS,MAAM,gCAQjD,CAAC;AAQF,eAAO,MAAM,YAAY,GAAI,OAAO;IAAE,KAAK,CAAC,EAAE,OAAO,CAAA;CAAE,gBAhBX,MAAM,6CAiBV,CAAC;AAGzC,eAAO,MAAM,SAAS,QAAO,KAM3B,CAAC;AAgBH,eAAO,MAAM,OAAO,GAAI,MAAM,MAAM,GAAG,SAAS,KAAG,IAAI,IAAI,SAAS,GAAG,GACnC,CAAC;AASrC,eAAO,MAAM,SAAS,GAAI,MAAM,MAAM,GAAG,SAAS,EAAE,IAAI,KAAK,iCACT,CAAC;AAUrD,eAAO,MAAM,gBAAgB,GAAI,OAAO,MAAM,EAAE,OAAO,MAAM,KAAG,MAQ/D,CAAC;AAWF,eAAO,MAAM,IAAI,GAAI,IAAI,KAAK,EAAE,MAAM,OAAO,EAAE,MAAM,OAAO,EAAE,MAAM,MAAM,SACpB,CAAC"}
|
|
@@ -0,0 +1,65 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
3
|
+
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
4
|
+
};
|
|
5
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
|
+
exports.emit = exports.valueOrJsonField = exports.readInput = exports.isStdin = exports.processIO = exports.secretPrompt = exports.promptHidden = exports.exitOnClosedPipe = exports.isInteractive = exports.UsageError = exports.EXIT = void 0;
|
|
7
|
+
const promises_1 = require("node:fs/promises");
|
|
8
|
+
const prompts_1 = __importDefault(require("prompts"));
|
|
9
|
+
exports.EXIT = { OK: 0, FAILURE: 1, USAGE: 2 };
|
|
10
|
+
class UsageError extends Error {
|
|
11
|
+
}
|
|
12
|
+
exports.UsageError = UsageError;
|
|
13
|
+
const isInteractive = (input, output) => Boolean(input.isTTY && output.isTTY);
|
|
14
|
+
exports.isInteractive = isInteractive;
|
|
15
|
+
const exitOnClosedPipe = (stream, exit) => stream.on("error", (err) => {
|
|
16
|
+
if (err.code !== "EPIPE")
|
|
17
|
+
throw err;
|
|
18
|
+
exit(exports.EXIT.OK);
|
|
19
|
+
});
|
|
20
|
+
exports.exitOnClosedPipe = exitOnClosedPipe;
|
|
21
|
+
const promptHidden = async (message) => {
|
|
22
|
+
const answer = await (0, prompts_1.default)({
|
|
23
|
+
type: "password",
|
|
24
|
+
name: "secret",
|
|
25
|
+
message,
|
|
26
|
+
stdout: process.stderr,
|
|
27
|
+
});
|
|
28
|
+
return answer.secret;
|
|
29
|
+
};
|
|
30
|
+
exports.promptHidden = promptHidden;
|
|
31
|
+
const secretPrompt = (stdin) => stdin.isTTY ? exports.promptHidden : undefined;
|
|
32
|
+
exports.secretPrompt = secretPrompt;
|
|
33
|
+
const processIO = () => ({
|
|
34
|
+
stdin: process.stdin,
|
|
35
|
+
stdout: (data) => process.stdout.write(data),
|
|
36
|
+
stderr: (text) => process.stderr.write(text),
|
|
37
|
+
isTTY: (0, exports.isInteractive)(process.stdin, process.stdout),
|
|
38
|
+
promptSecret: (0, exports.secretPrompt)(process.stdin),
|
|
39
|
+
});
|
|
40
|
+
exports.processIO = processIO;
|
|
41
|
+
const readAll = async (stream) => {
|
|
42
|
+
const chunks = [];
|
|
43
|
+
for await (const chunk of stream) {
|
|
44
|
+
chunks.push(Buffer.isBuffer(chunk) ? chunk : Buffer.from(chunk));
|
|
45
|
+
}
|
|
46
|
+
return Buffer.concat(chunks);
|
|
47
|
+
};
|
|
48
|
+
const isStdin = (file) => file === undefined || file === "-";
|
|
49
|
+
exports.isStdin = isStdin;
|
|
50
|
+
const readInput = (file, io) => (0, exports.isStdin)(file) ? readAll(io.stdin) : (0, promises_1.readFile)(file);
|
|
51
|
+
exports.readInput = readInput;
|
|
52
|
+
const valueOrJsonField = (input, field) => {
|
|
53
|
+
const text = input.toString("utf8").trim();
|
|
54
|
+
if (!text.startsWith("{"))
|
|
55
|
+
return text;
|
|
56
|
+
const value = JSON.parse(text)[field];
|
|
57
|
+
if (typeof value !== "string") {
|
|
58
|
+
throw new Error(`the JSON input has no "${field}" string`);
|
|
59
|
+
}
|
|
60
|
+
return value;
|
|
61
|
+
};
|
|
62
|
+
exports.valueOrJsonField = valueOrJsonField;
|
|
63
|
+
const emit = (io, json, data, text) => io.stdout(`${json ? JSON.stringify(data) : text}\n`);
|
|
64
|
+
exports.emit = emit;
|
|
65
|
+
//# sourceMappingURL=io.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"keygen.d.ts","sourceRoot":"","sources":["../../src/program/keygen.ts"],"names":[],"mappings":"AAKA,OAAO,EAAE,OAAO,EAAU,MAAM,WAAW,CAAC;AAQ5C,OAAO,EAAQ,KAAK,UAAU,EAAE,MAAM,MAAM,CAAC;AA0B7C,eAAO,MAAM,cAAc,GAAI,SAAS,OAAO,EAAE,KAAK,UAAU,YAoB1D,CAAC"}
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.registerKeygen = void 0;
|
|
4
|
+
const commander_1 = require("commander");
|
|
5
|
+
const keys_1 = require("@sebastienrousseau/crypto-lib/keys");
|
|
6
|
+
const io_1 = require("./io");
|
|
7
|
+
const describe = (key) => [
|
|
8
|
+
`Algorithm: ${key.algorithm}`,
|
|
9
|
+
`Key ID: ${key.kid}`,
|
|
10
|
+
`Public: ${key.publicKey}`,
|
|
11
|
+
`Private: ${key.privateKey}`,
|
|
12
|
+
].join("\n");
|
|
13
|
+
const registerKeygen = (program, ctx) => program
|
|
14
|
+
.command("keygen")
|
|
15
|
+
.description("Generate a key pair (the private key is printed to stdout)")
|
|
16
|
+
.addOption(new commander_1.Option("-a, --algorithm <name>", "key algorithm")
|
|
17
|
+
.choices(keys_1.KEY_ALGORITHMS)
|
|
18
|
+
.makeOptionMandatory())
|
|
19
|
+
.option("--kid <id>", "key ID (default: thumbprint of the public key)")
|
|
20
|
+
.addOption(new commander_1.Option("--use <use>", "intended key usage").choices(["sig", "enc"]))
|
|
21
|
+
.option("--json", "print the key pair as one line of JSON")
|
|
22
|
+
.action((opts) => {
|
|
23
|
+
const metadata = {};
|
|
24
|
+
if (opts.kid !== undefined)
|
|
25
|
+
metadata.kid = opts.kid;
|
|
26
|
+
if (opts.use !== undefined)
|
|
27
|
+
metadata.use = opts.use;
|
|
28
|
+
const key = (0, keys_1.generateKeyPair)(opts.algorithm, metadata);
|
|
29
|
+
(0, io_1.emit)(ctx.io, Boolean(opts.json), key, describe(key));
|
|
30
|
+
});
|
|
31
|
+
exports.registerKeygen = registerKeygen;
|
|
32
|
+
//# sourceMappingURL=keygen.js.map
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
import { Command } from "commander";
|
|
2
|
+
import { type KmsProvider } from "@sebastienrousseau/crypto-kms";
|
|
3
|
+
import { type RunContext } from "./io";
|
|
4
|
+
export declare function registerCliKmsProvider(name: string, provider: KmsProvider): void;
|
|
5
|
+
export declare function resetCliKmsProviders(): void;
|
|
6
|
+
export declare function resolveCliKmsProvider(name?: string): KmsProvider;
|
|
7
|
+
export declare function registerKms(program: Command, ctx: RunContext): void;
|
|
8
|
+
//# sourceMappingURL=kms.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"kms.d.ts","sourceRoot":"","sources":["../../src/program/kms.ts"],"names":[],"mappings":"AAKA,OAAO,EAAE,OAAO,EAAU,MAAM,WAAW,CAAC;AAC5C,OAAO,EACL,KAAK,WAAW,EAMjB,MAAM,+BAA+B,CAAC;AACvC,OAAO,EAAqC,KAAK,UAAU,EAAE,MAAM,MAAM,CAAC;AAM1E,wBAAgB,sBAAsB,CACpC,IAAI,EAAE,MAAM,EACZ,QAAQ,EAAE,WAAW,GACpB,IAAI,CAEN;AAGD,wBAAgB,oBAAoB,IAAI,IAAI,CAG3C;AA+CD,wBAAgB,qBAAqB,CAAC,IAAI,CAAC,EAAE,MAAM,GAAG,WAAW,CAQhE;AAkQD,wBAAgB,WAAW,CAAC,OAAO,EAAE,OAAO,EAAE,GAAG,EAAE,UAAU,GAAG,IAAI,CASnE"}
|