@pqc-sdk/cli 0.1.0 → 0.1.2
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 +8 -8
- package/dist/index.js +49 -49
- package/dist/index.js.map +1 -1
- package/package.json +4 -4
package/README.md
CHANGED
|
@@ -3,23 +3,23 @@
|
|
|
3
3
|
[](https://github.com/jeloercc/pqc-sdk/actions/workflows/ci.yml)
|
|
4
4
|
[](https://www.npmjs.com/package/@pqc-sdk/cli)
|
|
5
5
|
|
|
6
|
-
CLI
|
|
7
|
-
post-
|
|
6
|
+
CLI for [@pqc-sdk/core](https://www.npmjs.com/package/@pqc-sdk/core):
|
|
7
|
+
post-quantum projects in one command.
|
|
8
8
|
|
|
9
9
|
```bash
|
|
10
|
-
#
|
|
10
|
+
# Initialize a project: config + development keys + working example
|
|
11
11
|
npx @pqc-sdk/cli init
|
|
12
12
|
|
|
13
|
-
#
|
|
13
|
+
# Generate keys serialized as base64url
|
|
14
14
|
npx @pqc-sdk/cli keygen --algorithm ml-dsa-65 --out keys/
|
|
15
15
|
|
|
16
|
-
#
|
|
16
|
+
# Detect pre-quantum crypto (RSA/ECDSA/ECDH) and what to migrate to PQC
|
|
17
17
|
npx @pqc-sdk/cli audit
|
|
18
18
|
```
|
|
19
19
|
|
|
20
|
-
`audit`
|
|
21
|
-
|
|
20
|
+
`audit` exits with code 1 when it finds crypto to migrate — usable as a CI
|
|
21
|
+
gate. Output uses colors only when there is a TTY: readable in logs and pipes.
|
|
22
22
|
|
|
23
|
-
##
|
|
23
|
+
## License
|
|
24
24
|
|
|
25
25
|
[MIT](./LICENSE)
|
package/dist/index.js
CHANGED
|
@@ -23,7 +23,7 @@ var item = (message) => {
|
|
|
23
23
|
};
|
|
24
24
|
var finding = (location, what, migrateTo) => {
|
|
25
25
|
console.log(` ${pc.red("\u25CF")} ${pc.bold(location)} \u2014 ${what}`);
|
|
26
|
-
console.log(` ${pc.dim("
|
|
26
|
+
console.log(` ${pc.dim("migrate to:")} ${pc.cyan(migrateTo)}`);
|
|
27
27
|
};
|
|
28
28
|
var heading = (message) => {
|
|
29
29
|
console.log(pc.bold(message));
|
|
@@ -33,44 +33,44 @@ var heading = (message) => {
|
|
|
33
33
|
var ML_DSA = "ML-DSA-65 (pqc.sign / pqc.verify)";
|
|
34
34
|
var ML_KEM = "ML-KEM-768 + AES-256-GCM (pqc.encrypt / pqc.decrypt)";
|
|
35
35
|
var RISKY_PACKAGES = {
|
|
36
|
-
jsonwebtoken: { what: "JWTs
|
|
37
|
-
jose: { what: "JOSE/JWT
|
|
38
|
-
elliptic: { what: "
|
|
39
|
-
secp256k1: { what: "
|
|
40
|
-
"node-rsa": { what: "
|
|
41
|
-
"node-forge": { what: "RSA/X.509
|
|
42
|
-
tweetnacl: { what: "Ed25519/X25519 (pre-
|
|
36
|
+
jsonwebtoken: { what: "JWTs signed with RSA/ECDSA (RS256/ES256)", migrateTo: ML_DSA },
|
|
37
|
+
jose: { what: "JOSE/JWT with RSA/ECDSA algorithms", migrateTo: ML_DSA },
|
|
38
|
+
elliptic: { what: "Classic elliptic curves (ECDSA/ECDH)", migrateTo: `${ML_DSA} and ${ML_KEM}` },
|
|
39
|
+
secp256k1: { what: "ECDSA signatures over secp256k1", migrateTo: ML_DSA },
|
|
40
|
+
"node-rsa": { what: "RSA encryption and signatures", migrateTo: `${ML_KEM} and ${ML_DSA}` },
|
|
41
|
+
"node-forge": { what: "Classic RSA/X.509", migrateTo: `${ML_KEM} and ${ML_DSA}` },
|
|
42
|
+
tweetnacl: { what: "Ed25519/X25519 (pre-quantum)", migrateTo: `${ML_DSA} and ${ML_KEM}` }
|
|
43
43
|
};
|
|
44
44
|
var CODE_PATTERNS = [
|
|
45
45
|
{
|
|
46
46
|
re: /create(?:Sign|Verify)\s*\(/,
|
|
47
|
-
what: "
|
|
47
|
+
what: "RSA/ECDSA signing via node:crypto (createSign/createVerify)",
|
|
48
48
|
migrateTo: ML_DSA
|
|
49
49
|
},
|
|
50
50
|
{
|
|
51
51
|
re: /createECDH\s*\(|\.diffieHellman\s*\(/,
|
|
52
|
-
what: "
|
|
52
|
+
what: "ECDH/DH key exchange",
|
|
53
53
|
migrateTo: ML_KEM
|
|
54
54
|
},
|
|
55
55
|
{
|
|
56
56
|
re: /publicEncrypt\s*\(|privateDecrypt\s*\(/,
|
|
57
|
-
what: "
|
|
57
|
+
what: "RSA encryption (publicEncrypt/privateDecrypt)",
|
|
58
58
|
migrateTo: ML_KEM
|
|
59
59
|
},
|
|
60
60
|
{
|
|
61
61
|
re: /generateKeyPair(?:Sync)?\s*\(\s*['"](?:rsa|rsa-pss|dsa|ec|ed25519|ed448|x25519|x448)['"]/,
|
|
62
|
-
what: "
|
|
63
|
-
migrateTo: "pqc.keys.generate (ML-KEM-768
|
|
62
|
+
what: "pre-quantum keypair generation",
|
|
63
|
+
migrateTo: "pqc.keys.generate (ML-KEM-768 for encryption, ML-DSA-65 for signatures)"
|
|
64
64
|
},
|
|
65
65
|
{
|
|
66
66
|
re: /['"`](?:RS|ES|PS)(?:256|384|512)['"`]/,
|
|
67
|
-
what: "JWT
|
|
67
|
+
what: "JWT with an RSA/ECDSA signing algorithm",
|
|
68
68
|
migrateTo: ML_DSA
|
|
69
69
|
},
|
|
70
70
|
{
|
|
71
71
|
re: /['"`](?:RSA-OAEP|ECDH|ECDSA)['"`]/,
|
|
72
|
-
what: "WebCrypto
|
|
73
|
-
migrateTo: `${ML_KEM}
|
|
72
|
+
what: "WebCrypto with a pre-quantum algorithm",
|
|
73
|
+
migrateTo: `${ML_KEM} or ${ML_DSA}`
|
|
74
74
|
}
|
|
75
75
|
];
|
|
76
76
|
var SOURCE_EXTENSIONS = /* @__PURE__ */ new Set([".js", ".mjs", ".cjs", ".ts", ".mts", ".cts", ".jsx", ".tsx"]);
|
|
@@ -121,23 +121,23 @@ async function auditSources(cwd) {
|
|
|
121
121
|
var audit = defineCommand({
|
|
122
122
|
meta: {
|
|
123
123
|
name: "audit",
|
|
124
|
-
description: "
|
|
124
|
+
description: "Detect pre-quantum crypto and suggest the PQC equivalent"
|
|
125
125
|
},
|
|
126
126
|
async run() {
|
|
127
127
|
const cwd = process.cwd();
|
|
128
128
|
const findings = [...await auditPackageJson(cwd), ...await auditSources(cwd)];
|
|
129
129
|
if (findings.length === 0) {
|
|
130
|
-
ok("
|
|
130
|
+
ok("No pre-quantum crypto detected.");
|
|
131
131
|
return;
|
|
132
132
|
}
|
|
133
|
-
heading(`
|
|
133
|
+
heading(`Pre-quantum crypto detected (${findings.length} findings):`);
|
|
134
134
|
for (const f of findings) {
|
|
135
135
|
finding(f.location, f.what, f.migrateTo);
|
|
136
136
|
}
|
|
137
137
|
console.log();
|
|
138
138
|
console.log(
|
|
139
139
|
pc2.yellow(
|
|
140
|
-
`${findings.length}
|
|
140
|
+
`${findings.length} usages to migrate. Algorithm guide: FIPS 203 (ML-KEM) encryption, FIPS 204 (ML-DSA) signatures.`
|
|
141
141
|
)
|
|
142
142
|
);
|
|
143
143
|
process.exitCode = 1;
|
|
@@ -157,7 +157,7 @@ import { SUPPORTED_ALGORITHMS, pqc } from "@pqc-sdk/core";
|
|
|
157
157
|
function assertSupportedAlgorithm(value) {
|
|
158
158
|
if (!SUPPORTED_ALGORITHMS.includes(value)) {
|
|
159
159
|
throw new Error(
|
|
160
|
-
`
|
|
160
|
+
`Unsupported algorithm: ${value} (supported: ${SUPPORTED_ALGORITHMS.join(", ")})`
|
|
161
161
|
);
|
|
162
162
|
}
|
|
163
163
|
return value;
|
|
@@ -168,7 +168,7 @@ async function writeKeyPair(directory, baseName, algorithm, force) {
|
|
|
168
168
|
if (!force) {
|
|
169
169
|
for (const path of [publicPath, secretPath]) {
|
|
170
170
|
if (existsSync2(path)) {
|
|
171
|
-
throw new Error(`${path}
|
|
171
|
+
throw new Error(`${path} already exists. Use --force to overwrite it.`);
|
|
172
172
|
}
|
|
173
173
|
}
|
|
174
174
|
}
|
|
@@ -190,22 +190,22 @@ var CONFIG = {
|
|
|
190
190
|
};
|
|
191
191
|
var EXAMPLE = `import { pqc } from '@pqc-sdk/core';
|
|
192
192
|
|
|
193
|
-
//
|
|
194
|
-
const pair = await pqc.keys.generate(); // ML-KEM-768
|
|
195
|
-
const ciphertext = await pqc.encrypt('
|
|
193
|
+
// Full roundtrip: generate keys, encrypt and decrypt.
|
|
194
|
+
const pair = await pqc.keys.generate(); // ML-KEM-768 by default
|
|
195
|
+
const ciphertext = await pqc.encrypt('hello post-quantum', pair.publicKey);
|
|
196
196
|
const plaintext = await pqc.decrypt(ciphertext, pair.secretKey);
|
|
197
|
-
console.log(new TextDecoder().decode(plaintext)); // "
|
|
197
|
+
console.log(new TextDecoder().decode(plaintext)); // "hello post-quantum"
|
|
198
198
|
|
|
199
|
-
//
|
|
199
|
+
// Digital signatures (ML-DSA-65):
|
|
200
200
|
const signer = await pqc.keys.generate({ algorithm: 'ml-dsa-65' });
|
|
201
|
-
const signature = await pqc.sign('
|
|
202
|
-
console.log(await pqc.verify('
|
|
201
|
+
const signature = await pqc.sign('document', signer.secretKey);
|
|
202
|
+
console.log(await pqc.verify('document', signature, signer.publicKey)); // true
|
|
203
203
|
|
|
204
|
-
//
|
|
204
|
+
// Keys serialize to base64url with metadata, ready to persist:
|
|
205
205
|
const token = pqc.keys.serialize(pair.publicKey);
|
|
206
206
|
console.log(token.slice(0, 48) + '\u2026');
|
|
207
207
|
|
|
208
|
-
//
|
|
208
|
+
// To load the development keys generated by \`pqc init\`:
|
|
209
209
|
// import { readFile } from 'node:fs/promises';
|
|
210
210
|
// const publicKey = pqc.keys.deserialize(
|
|
211
211
|
// (await readFile('keys/dev.public.pqc', 'utf8')).trim(),
|
|
@@ -214,23 +214,23 @@ console.log(token.slice(0, 48) + '\u2026');
|
|
|
214
214
|
var init = defineCommand2({
|
|
215
215
|
meta: {
|
|
216
216
|
name: "init",
|
|
217
|
-
description: "
|
|
217
|
+
description: "Initialize a project: config, development keys and example"
|
|
218
218
|
},
|
|
219
219
|
async run() {
|
|
220
220
|
if (existsSync3(CONFIG_FILE)) {
|
|
221
|
-
throw new Error(`${CONFIG_FILE}
|
|
221
|
+
throw new Error(`${CONFIG_FILE} already exists: the project is already initialized.`);
|
|
222
222
|
}
|
|
223
223
|
await writeFile2(CONFIG_FILE, `${JSON.stringify(CONFIG, null, 2)}
|
|
224
224
|
`);
|
|
225
225
|
const keys = await writeKeyPair(CONFIG.keysDir, "dev", CONFIG.defaultAlgorithm, false);
|
|
226
226
|
await writeFile2("example.ts", EXAMPLE);
|
|
227
|
-
heading("
|
|
228
|
-
item(`${CONFIG_FILE} \u2014
|
|
229
|
-
item(`${keys.publicPath} / ${keys.secretPath} \u2014
|
|
230
|
-
item("example.ts \u2014 roundtrip
|
|
227
|
+
heading("PQC project initialized:");
|
|
228
|
+
item(`${CONFIG_FILE} \u2014 configuration with safe defaults`);
|
|
229
|
+
item(`${keys.publicPath} / ${keys.secretPath} \u2014 development ${keys.algorithm} pair`);
|
|
230
|
+
item("example.ts \u2014 full roundtrip ready to run");
|
|
231
231
|
console.log();
|
|
232
|
-
warn("
|
|
233
|
-
ok(`
|
|
232
|
+
warn("The keys/dev.* keys are for development ONLY \u2014 do NOT use them in production.");
|
|
233
|
+
ok(`Next step: run example.ts (node --experimental-strip-types example.ts or tsx)`);
|
|
234
234
|
}
|
|
235
235
|
});
|
|
236
236
|
|
|
@@ -239,32 +239,32 @@ import { defineCommand as defineCommand3 } from "citty";
|
|
|
239
239
|
var keygen = defineCommand3({
|
|
240
240
|
meta: {
|
|
241
241
|
name: "keygen",
|
|
242
|
-
description: "
|
|
242
|
+
description: "Generate a PQC key pair serialized as base64url"
|
|
243
243
|
},
|
|
244
244
|
args: {
|
|
245
245
|
algorithm: {
|
|
246
246
|
type: "string",
|
|
247
|
-
description: "
|
|
247
|
+
description: "Algorithm of the pair (ml-kem-768 or ml-dsa-65)",
|
|
248
248
|
default: "ml-kem-768"
|
|
249
249
|
},
|
|
250
250
|
out: {
|
|
251
251
|
type: "string",
|
|
252
|
-
description: "
|
|
252
|
+
description: "Output directory",
|
|
253
253
|
default: "keys"
|
|
254
254
|
},
|
|
255
255
|
force: {
|
|
256
256
|
type: "boolean",
|
|
257
|
-
description: "
|
|
257
|
+
description: "Overwrite existing keys",
|
|
258
258
|
default: false
|
|
259
259
|
}
|
|
260
260
|
},
|
|
261
261
|
async run({ args }) {
|
|
262
262
|
const algorithm = assertSupportedAlgorithm(args.algorithm);
|
|
263
263
|
const keys = await writeKeyPair(args.out, algorithm, algorithm, args.force);
|
|
264
|
-
ok(
|
|
265
|
-
item(`
|
|
266
|
-
item(`
|
|
267
|
-
warn("
|
|
264
|
+
ok(`${algorithm} pair generated:`);
|
|
265
|
+
item(`public: ${keys.publicPath}`);
|
|
266
|
+
item(`secret: ${keys.secretPath} (mode 0600)`);
|
|
267
|
+
warn("The secret key must not be committed or leave this environment.");
|
|
268
268
|
}
|
|
269
269
|
});
|
|
270
270
|
|
|
@@ -272,8 +272,8 @@ var keygen = defineCommand3({
|
|
|
272
272
|
var main = defineCommand4({
|
|
273
273
|
meta: {
|
|
274
274
|
name: "pqc",
|
|
275
|
-
version: "0.
|
|
276
|
-
description: "CLI
|
|
275
|
+
version: "0.1.2",
|
|
276
|
+
description: "CLI for the post-quantum cryptography SDK (@pqc-sdk/core)"
|
|
277
277
|
},
|
|
278
278
|
subCommands: {
|
|
279
279
|
init,
|
package/dist/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/index.ts","../src/commands/audit.ts","../src/ui.ts","../src/commands/init.ts","../src/keyfiles.ts","../src/commands/keygen.ts"],"sourcesContent":["import { defineCommand, runMain } from 'citty';\n\nimport { audit } from './commands/audit.js';\nimport { init } from './commands/init.js';\nimport { keygen } from './commands/keygen.js';\n\nconst main = defineCommand({\n meta: {\n name: 'pqc',\n version: '0.0.1',\n description: 'CLI del SDK de criptografía post-cuántica (@pqc-sdk/core)',\n },\n subCommands: {\n init,\n keygen,\n audit,\n },\n});\n\nawait runMain(main);\n","import { readFile, readdir } from 'node:fs/promises';\nimport { existsSync } from 'node:fs';\nimport { join, relative } from 'node:path';\n\nimport { defineCommand } from 'citty';\nimport pc from 'picocolors';\n\nimport { finding, heading, ok } from '../ui.js';\n\ninterface Finding {\n location: string;\n what: string;\n migrateTo: string;\n}\n\nconst ML_DSA = 'ML-DSA-65 (pqc.sign / pqc.verify)';\nconst ML_KEM = 'ML-KEM-768 + AES-256-GCM (pqc.encrypt / pqc.decrypt)';\n\nconst RISKY_PACKAGES: Record<string, { what: string; migrateTo: string }> = {\n jsonwebtoken: { what: 'JWTs firmados con RSA/ECDSA (RS256/ES256)', migrateTo: ML_DSA },\n jose: { what: 'JOSE/JWT con algoritmos RSA/ECDSA', migrateTo: ML_DSA },\n elliptic: { what: 'Curvas elípticas clásicas (ECDSA/ECDH)', migrateTo: `${ML_DSA} y ${ML_KEM}` },\n secp256k1: { what: 'Firmas ECDSA sobre secp256k1', migrateTo: ML_DSA },\n 'node-rsa': { what: 'Cifrado y firmas RSA', migrateTo: `${ML_KEM} y ${ML_DSA}` },\n 'node-forge': { what: 'RSA/X.509 clásico', migrateTo: `${ML_KEM} y ${ML_DSA}` },\n tweetnacl: { what: 'Ed25519/X25519 (pre-cuántico)', migrateTo: `${ML_DSA} y ${ML_KEM}` },\n};\n\nconst CODE_PATTERNS: ReadonlyArray<{ re: RegExp; what: string; migrateTo: string }> = [\n {\n re: /create(?:Sign|Verify)\\s*\\(/,\n what: 'firma RSA/ECDSA vía node:crypto (createSign/createVerify)',\n migrateTo: ML_DSA,\n },\n {\n re: /createECDH\\s*\\(|\\.diffieHellman\\s*\\(/,\n what: 'intercambio de claves ECDH/DH',\n migrateTo: ML_KEM,\n },\n {\n re: /publicEncrypt\\s*\\(|privateDecrypt\\s*\\(/,\n what: 'cifrado RSA (publicEncrypt/privateDecrypt)',\n migrateTo: ML_KEM,\n },\n {\n re: /generateKeyPair(?:Sync)?\\s*\\(\\s*['\"](?:rsa|rsa-pss|dsa|ec|ed25519|ed448|x25519|x448)['\"]/,\n what: 'generación de keypair pre-cuántico',\n migrateTo: 'pqc.keys.generate (ML-KEM-768 para cifrado, ML-DSA-65 para firmas)',\n },\n {\n re: /['\"`](?:RS|ES|PS)(?:256|384|512)['\"`]/,\n what: 'JWT con algoritmo de firma RSA/ECDSA',\n migrateTo: ML_DSA,\n },\n {\n re: /['\"`](?:RSA-OAEP|ECDH|ECDSA)['\"`]/,\n what: 'WebCrypto con algoritmo pre-cuántico',\n migrateTo: `${ML_KEM} o ${ML_DSA}`,\n },\n];\n\nconst SOURCE_EXTENSIONS = new Set(['.js', '.mjs', '.cjs', '.ts', '.mts', '.cts', '.jsx', '.tsx']);\nconst IGNORED_DIRS = new Set(['node_modules', 'dist', 'build', 'coverage', '.git', '.next']);\n\nasync function collectSourceFiles(root: string): Promise<string[]> {\n const files: string[] = [];\n const pending = [root];\n while (pending.length > 0) {\n const dir = pending.pop()!;\n for (const entry of await readdir(dir, { withFileTypes: true })) {\n if (entry.isDirectory()) {\n if (!IGNORED_DIRS.has(entry.name)) pending.push(join(dir, entry.name));\n continue;\n }\n const dot = entry.name.lastIndexOf('.');\n if (dot !== -1 && SOURCE_EXTENSIONS.has(entry.name.slice(dot))) {\n files.push(join(dir, entry.name));\n }\n }\n }\n return files.sort();\n}\n\nasync function auditPackageJson(cwd: string): Promise<Finding[]> {\n const path = join(cwd, 'package.json');\n if (!existsSync(path)) return [];\n const manifest = JSON.parse(await readFile(path, 'utf8')) as Record<\n string,\n Record<string, string> | undefined\n >;\n const declared = {\n ...manifest.dependencies,\n ...manifest.devDependencies,\n ...manifest.peerDependencies,\n };\n return Object.keys(declared)\n .filter((name) => name in RISKY_PACKAGES)\n .map((name) => ({ location: `package.json (${name})`, ...RISKY_PACKAGES[name]! }));\n}\n\nasync function auditSources(cwd: string): Promise<Finding[]> {\n const findings: Finding[] = [];\n for (const file of await collectSourceFiles(cwd)) {\n const lines = (await readFile(file, 'utf8')).split('\\n');\n lines.forEach((line, index) => {\n for (const { re, what, migrateTo } of CODE_PATTERNS) {\n if (re.test(line)) {\n findings.push({ location: `${relative(cwd, file)}:${index + 1}`, what, migrateTo });\n }\n }\n });\n }\n return findings;\n}\n\nexport const audit = defineCommand({\n meta: {\n name: 'audit',\n description: 'Detecta crypto pre-cuántico y sugiere el equivalente PQC',\n },\n async run() {\n const cwd = process.cwd();\n const findings = [...(await auditPackageJson(cwd)), ...(await auditSources(cwd))];\n\n if (findings.length === 0) {\n ok('Sin crypto pre-cuántico detectado.');\n return;\n }\n\n heading(`Crypto pre-cuántico detectado (${findings.length} hallazgos):`);\n for (const f of findings) {\n finding(f.location, f.what, f.migrateTo);\n }\n console.log();\n console.log(\n pc.yellow(\n `${findings.length} usos a migrar. Guía de algoritmos: FIPS 203 (ML-KEM) cifrado, FIPS 204 (ML-DSA) firmas.`,\n ),\n );\n process.exitCode = 1;\n },\n});\n","import pc from 'picocolors';\n\n/**\n * Helpers de salida. picocolors desactiva los colores automáticamente cuando\n * no hay TTY (y NO_COLOR/FORCE_COLOR se respetan), así el output queda legible\n * en pipes y logs de CI sin códigos ANSI.\n */\nexport const ok = (message: string): void => {\n console.log(`${pc.green('✓')} ${message}`);\n};\n\nexport const warn = (message: string): void => {\n console.log(`${pc.yellow('⚠')} ${pc.yellow(message)}`);\n};\n\nexport const item = (message: string): void => {\n console.log(` ${message}`);\n};\n\nexport const finding = (location: string, what: string, migrateTo: string): void => {\n console.log(` ${pc.red('●')} ${pc.bold(location)} — ${what}`);\n console.log(` ${pc.dim('migrar a:')} ${pc.cyan(migrateTo)}`);\n};\n\nexport const heading = (message: string): void => {\n console.log(pc.bold(message));\n};\n","import { writeFile } from 'node:fs/promises';\nimport { existsSync } from 'node:fs';\n\nimport { defineCommand } from 'citty';\n\nimport { writeKeyPair } from '../keyfiles.js';\nimport { heading, item, ok, warn } from '../ui.js';\n\nconst CONFIG_FILE = 'pqc.config.json';\n\nconst CONFIG = {\n defaultAlgorithm: 'ml-kem-768',\n keysDir: 'keys',\n} as const;\n\nconst EXAMPLE = `import { pqc } from '@pqc-sdk/core';\n\n// Roundtrip completo: generar keys, cifrar y descifrar.\nconst pair = await pqc.keys.generate(); // ML-KEM-768 por defecto\nconst ciphertext = await pqc.encrypt('hola post-quantum', pair.publicKey);\nconst plaintext = await pqc.decrypt(ciphertext, pair.secretKey);\nconsole.log(new TextDecoder().decode(plaintext)); // \"hola post-quantum\"\n\n// Firmas digitales (ML-DSA-65):\nconst signer = await pqc.keys.generate({ algorithm: 'ml-dsa-65' });\nconst signature = await pqc.sign('documento', signer.secretKey);\nconsole.log(await pqc.verify('documento', signature, signer.publicKey)); // true\n\n// Las keys se serializan a base64url con metadata, listas para persistir:\nconst token = pqc.keys.serialize(pair.publicKey);\nconsole.log(token.slice(0, 48) + '…');\n\n// Para cargar las keys de desarrollo que generó \\`pqc init\\`:\n// import { readFile } from 'node:fs/promises';\n// const publicKey = pqc.keys.deserialize(\n// (await readFile('keys/dev.public.pqc', 'utf8')).trim(),\n// );\n`;\n\nexport const init = defineCommand({\n meta: {\n name: 'init',\n description: 'Inicializa un proyecto: config, keys de desarrollo y ejemplo',\n },\n async run() {\n if (existsSync(CONFIG_FILE)) {\n throw new Error(`${CONFIG_FILE} ya existe: el proyecto ya está inicializado.`);\n }\n\n await writeFile(CONFIG_FILE, `${JSON.stringify(CONFIG, null, 2)}\\n`);\n const keys = await writeKeyPair(CONFIG.keysDir, 'dev', CONFIG.defaultAlgorithm, false);\n await writeFile('example.ts', EXAMPLE);\n\n heading('Proyecto PQC inicializado:');\n item(`${CONFIG_FILE} — configuración con defaults seguros`);\n item(`${keys.publicPath} / ${keys.secretPath} — par ${keys.algorithm} de desarrollo`);\n item('example.ts — roundtrip completo listo para ejecutar');\n console.log();\n warn('Las keys de keys/dev.* son SOLO de desarrollo — NO usarlas en producción.');\n ok(`Siguiente paso: ejecutá example.ts (node --experimental-strip-types example.ts o tsx)`);\n },\n});\n","import { chmod, mkdir, writeFile } from 'node:fs/promises';\nimport { existsSync } from 'node:fs';\nimport { join } from 'node:path';\n\nimport { SUPPORTED_ALGORITHMS, pqc, type SupportedAlgorithm } from '@pqc-sdk/core';\n\nexport interface WrittenKeyPair {\n algorithm: SupportedAlgorithm;\n publicPath: string;\n secretPath: string;\n}\n\nexport function assertSupportedAlgorithm(value: string): SupportedAlgorithm {\n if (!(SUPPORTED_ALGORITHMS as readonly string[]).includes(value)) {\n throw new Error(\n `Algoritmo no soportado: ${value} (soportados: ${SUPPORTED_ALGORITHMS.join(', ')})`,\n );\n }\n return value as SupportedAlgorithm;\n}\n\n/** Genera un par y lo escribe serializado en base64url, un archivo por key. */\nexport async function writeKeyPair(\n directory: string,\n baseName: string,\n algorithm: SupportedAlgorithm,\n force: boolean,\n): Promise<WrittenKeyPair> {\n const publicPath = join(directory, `${baseName}.public.pqc`);\n const secretPath = join(directory, `${baseName}.secret.pqc`);\n\n if (!force) {\n for (const path of [publicPath, secretPath]) {\n if (existsSync(path)) {\n throw new Error(`${path} ya existe. Usá --force para sobreescribirla.`);\n }\n }\n }\n\n const pair = await pqc.keys.generate({ algorithm });\n await mkdir(directory, { recursive: true });\n await writeFile(publicPath, `${pqc.keys.serialize(pair.publicKey)}\\n`);\n await writeFile(secretPath, `${pqc.keys.serialize(pair.secretKey)}\\n`, { mode: 0o600 });\n await chmod(secretPath, 0o600);\n\n return { algorithm, publicPath, secretPath };\n}\n","import { defineCommand } from 'citty';\n\nimport { assertSupportedAlgorithm, writeKeyPair } from '../keyfiles.js';\nimport { item, ok, warn } from '../ui.js';\n\nexport const keygen = defineCommand({\n meta: {\n name: 'keygen',\n description: 'Genera un par de keys PQC serializadas en base64url',\n },\n args: {\n algorithm: {\n type: 'string',\n description: 'Algoritmo del par (ml-kem-768 o ml-dsa-65)',\n default: 'ml-kem-768',\n },\n out: {\n type: 'string',\n description: 'Directorio de salida',\n default: 'keys',\n },\n force: {\n type: 'boolean',\n description: 'Sobreescribir keys existentes',\n default: false,\n },\n },\n async run({ args }) {\n const algorithm = assertSupportedAlgorithm(args.algorithm);\n const keys = await writeKeyPair(args.out, algorithm, algorithm, args.force);\n\n ok(`Par ${algorithm} generado:`);\n item(`pública: ${keys.publicPath}`);\n item(`secreta: ${keys.secretPath} (modo 0600)`);\n warn('La key secreta no debe commitearse ni salir de este entorno.');\n },\n});\n"],"mappings":"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","names":["defineCommand","pc","pc","writeFile","existsSync","defineCommand","existsSync","join","defineCommand","existsSync","writeFile","defineCommand","defineCommand","defineCommand"]}
|
|
1
|
+
{"version":3,"sources":["../src/index.ts","../src/commands/audit.ts","../src/ui.ts","../src/commands/init.ts","../src/keyfiles.ts","../src/commands/keygen.ts"],"sourcesContent":["import { defineCommand, runMain } from 'citty';\n\nimport { audit } from './commands/audit.js';\nimport { init } from './commands/init.js';\nimport { keygen } from './commands/keygen.js';\n\n// Injected by tsup (`define` in tsup.config.ts) from package.json at build time.\ndeclare const __PQC_CLI_VERSION__: string;\n\nconst main = defineCommand({\n meta: {\n name: 'pqc',\n version: __PQC_CLI_VERSION__,\n description: 'CLI for the post-quantum cryptography SDK (@pqc-sdk/core)',\n },\n subCommands: {\n init,\n keygen,\n audit,\n },\n});\n\nawait runMain(main);\n","import { readFile, readdir } from 'node:fs/promises';\nimport { existsSync } from 'node:fs';\nimport { join, relative } from 'node:path';\n\nimport { defineCommand } from 'citty';\nimport pc from 'picocolors';\n\nimport { finding, heading, ok } from '../ui.js';\n\ninterface Finding {\n location: string;\n what: string;\n migrateTo: string;\n}\n\nconst ML_DSA = 'ML-DSA-65 (pqc.sign / pqc.verify)';\nconst ML_KEM = 'ML-KEM-768 + AES-256-GCM (pqc.encrypt / pqc.decrypt)';\n\nconst RISKY_PACKAGES: Record<string, { what: string; migrateTo: string }> = {\n jsonwebtoken: { what: 'JWTs signed with RSA/ECDSA (RS256/ES256)', migrateTo: ML_DSA },\n jose: { what: 'JOSE/JWT with RSA/ECDSA algorithms', migrateTo: ML_DSA },\n elliptic: { what: 'Classic elliptic curves (ECDSA/ECDH)', migrateTo: `${ML_DSA} and ${ML_KEM}` },\n secp256k1: { what: 'ECDSA signatures over secp256k1', migrateTo: ML_DSA },\n 'node-rsa': { what: 'RSA encryption and signatures', migrateTo: `${ML_KEM} and ${ML_DSA}` },\n 'node-forge': { what: 'Classic RSA/X.509', migrateTo: `${ML_KEM} and ${ML_DSA}` },\n tweetnacl: { what: 'Ed25519/X25519 (pre-quantum)', migrateTo: `${ML_DSA} and ${ML_KEM}` },\n};\n\nconst CODE_PATTERNS: ReadonlyArray<{ re: RegExp; what: string; migrateTo: string }> = [\n {\n re: /create(?:Sign|Verify)\\s*\\(/,\n what: 'RSA/ECDSA signing via node:crypto (createSign/createVerify)',\n migrateTo: ML_DSA,\n },\n {\n re: /createECDH\\s*\\(|\\.diffieHellman\\s*\\(/,\n what: 'ECDH/DH key exchange',\n migrateTo: ML_KEM,\n },\n {\n re: /publicEncrypt\\s*\\(|privateDecrypt\\s*\\(/,\n what: 'RSA encryption (publicEncrypt/privateDecrypt)',\n migrateTo: ML_KEM,\n },\n {\n re: /generateKeyPair(?:Sync)?\\s*\\(\\s*['\"](?:rsa|rsa-pss|dsa|ec|ed25519|ed448|x25519|x448)['\"]/,\n what: 'pre-quantum keypair generation',\n migrateTo: 'pqc.keys.generate (ML-KEM-768 for encryption, ML-DSA-65 for signatures)',\n },\n {\n re: /['\"`](?:RS|ES|PS)(?:256|384|512)['\"`]/,\n what: 'JWT with an RSA/ECDSA signing algorithm',\n migrateTo: ML_DSA,\n },\n {\n re: /['\"`](?:RSA-OAEP|ECDH|ECDSA)['\"`]/,\n what: 'WebCrypto with a pre-quantum algorithm',\n migrateTo: `${ML_KEM} or ${ML_DSA}`,\n },\n];\n\nconst SOURCE_EXTENSIONS = new Set(['.js', '.mjs', '.cjs', '.ts', '.mts', '.cts', '.jsx', '.tsx']);\nconst IGNORED_DIRS = new Set(['node_modules', 'dist', 'build', 'coverage', '.git', '.next']);\n\nasync function collectSourceFiles(root: string): Promise<string[]> {\n const files: string[] = [];\n const pending = [root];\n while (pending.length > 0) {\n const dir = pending.pop()!;\n for (const entry of await readdir(dir, { withFileTypes: true })) {\n if (entry.isDirectory()) {\n if (!IGNORED_DIRS.has(entry.name)) pending.push(join(dir, entry.name));\n continue;\n }\n const dot = entry.name.lastIndexOf('.');\n if (dot !== -1 && SOURCE_EXTENSIONS.has(entry.name.slice(dot))) {\n files.push(join(dir, entry.name));\n }\n }\n }\n return files.sort();\n}\n\nasync function auditPackageJson(cwd: string): Promise<Finding[]> {\n const path = join(cwd, 'package.json');\n if (!existsSync(path)) return [];\n const manifest = JSON.parse(await readFile(path, 'utf8')) as Record<\n string,\n Record<string, string> | undefined\n >;\n const declared = {\n ...manifest.dependencies,\n ...manifest.devDependencies,\n ...manifest.peerDependencies,\n };\n return Object.keys(declared)\n .filter((name) => name in RISKY_PACKAGES)\n .map((name) => ({ location: `package.json (${name})`, ...RISKY_PACKAGES[name]! }));\n}\n\nasync function auditSources(cwd: string): Promise<Finding[]> {\n const findings: Finding[] = [];\n for (const file of await collectSourceFiles(cwd)) {\n const lines = (await readFile(file, 'utf8')).split('\\n');\n lines.forEach((line, index) => {\n for (const { re, what, migrateTo } of CODE_PATTERNS) {\n if (re.test(line)) {\n findings.push({ location: `${relative(cwd, file)}:${index + 1}`, what, migrateTo });\n }\n }\n });\n }\n return findings;\n}\n\nexport const audit = defineCommand({\n meta: {\n name: 'audit',\n description: 'Detect pre-quantum crypto and suggest the PQC equivalent',\n },\n async run() {\n const cwd = process.cwd();\n const findings = [...(await auditPackageJson(cwd)), ...(await auditSources(cwd))];\n\n if (findings.length === 0) {\n ok('No pre-quantum crypto detected.');\n return;\n }\n\n heading(`Pre-quantum crypto detected (${findings.length} findings):`);\n for (const f of findings) {\n finding(f.location, f.what, f.migrateTo);\n }\n console.log();\n console.log(\n pc.yellow(\n `${findings.length} usages to migrate. Algorithm guide: FIPS 203 (ML-KEM) encryption, FIPS 204 (ML-DSA) signatures.`,\n ),\n );\n process.exitCode = 1;\n },\n});\n","import pc from 'picocolors';\n\n/**\n * Output helpers. picocolors disables colors automatically when there is no\n * TTY (and NO_COLOR/FORCE_COLOR are honored), so output stays readable in\n * pipes and CI logs without ANSI codes.\n */\nexport const ok = (message: string): void => {\n console.log(`${pc.green('✓')} ${message}`);\n};\n\nexport const warn = (message: string): void => {\n console.log(`${pc.yellow('⚠')} ${pc.yellow(message)}`);\n};\n\nexport const item = (message: string): void => {\n console.log(` ${message}`);\n};\n\nexport const finding = (location: string, what: string, migrateTo: string): void => {\n console.log(` ${pc.red('●')} ${pc.bold(location)} — ${what}`);\n console.log(` ${pc.dim('migrate to:')} ${pc.cyan(migrateTo)}`);\n};\n\nexport const heading = (message: string): void => {\n console.log(pc.bold(message));\n};\n","import { writeFile } from 'node:fs/promises';\nimport { existsSync } from 'node:fs';\n\nimport { defineCommand } from 'citty';\n\nimport { writeKeyPair } from '../keyfiles.js';\nimport { heading, item, ok, warn } from '../ui.js';\n\nconst CONFIG_FILE = 'pqc.config.json';\n\nconst CONFIG = {\n defaultAlgorithm: 'ml-kem-768',\n keysDir: 'keys',\n} as const;\n\nconst EXAMPLE = `import { pqc } from '@pqc-sdk/core';\n\n// Full roundtrip: generate keys, encrypt and decrypt.\nconst pair = await pqc.keys.generate(); // ML-KEM-768 by default\nconst ciphertext = await pqc.encrypt('hello post-quantum', pair.publicKey);\nconst plaintext = await pqc.decrypt(ciphertext, pair.secretKey);\nconsole.log(new TextDecoder().decode(plaintext)); // \"hello post-quantum\"\n\n// Digital signatures (ML-DSA-65):\nconst signer = await pqc.keys.generate({ algorithm: 'ml-dsa-65' });\nconst signature = await pqc.sign('document', signer.secretKey);\nconsole.log(await pqc.verify('document', signature, signer.publicKey)); // true\n\n// Keys serialize to base64url with metadata, ready to persist:\nconst token = pqc.keys.serialize(pair.publicKey);\nconsole.log(token.slice(0, 48) + '…');\n\n// To load the development keys generated by \\`pqc init\\`:\n// import { readFile } from 'node:fs/promises';\n// const publicKey = pqc.keys.deserialize(\n// (await readFile('keys/dev.public.pqc', 'utf8')).trim(),\n// );\n`;\n\nexport const init = defineCommand({\n meta: {\n name: 'init',\n description: 'Initialize a project: config, development keys and example',\n },\n async run() {\n if (existsSync(CONFIG_FILE)) {\n throw new Error(`${CONFIG_FILE} already exists: the project is already initialized.`);\n }\n\n await writeFile(CONFIG_FILE, `${JSON.stringify(CONFIG, null, 2)}\\n`);\n const keys = await writeKeyPair(CONFIG.keysDir, 'dev', CONFIG.defaultAlgorithm, false);\n await writeFile('example.ts', EXAMPLE);\n\n heading('PQC project initialized:');\n item(`${CONFIG_FILE} — configuration with safe defaults`);\n item(`${keys.publicPath} / ${keys.secretPath} — development ${keys.algorithm} pair`);\n item('example.ts — full roundtrip ready to run');\n console.log();\n warn('The keys/dev.* keys are for development ONLY — do NOT use them in production.');\n ok(`Next step: run example.ts (node --experimental-strip-types example.ts or tsx)`);\n },\n});\n","import { chmod, mkdir, writeFile } from 'node:fs/promises';\nimport { existsSync } from 'node:fs';\nimport { join } from 'node:path';\n\nimport { SUPPORTED_ALGORITHMS, pqc, type SupportedAlgorithm } from '@pqc-sdk/core';\n\nexport interface WrittenKeyPair {\n algorithm: SupportedAlgorithm;\n publicPath: string;\n secretPath: string;\n}\n\nexport function assertSupportedAlgorithm(value: string): SupportedAlgorithm {\n if (!(SUPPORTED_ALGORITHMS as readonly string[]).includes(value)) {\n throw new Error(\n `Unsupported algorithm: ${value} (supported: ${SUPPORTED_ALGORITHMS.join(', ')})`,\n );\n }\n return value as SupportedAlgorithm;\n}\n\n/** Generates a pair and writes it serialized as base64url, one file per key. */\nexport async function writeKeyPair(\n directory: string,\n baseName: string,\n algorithm: SupportedAlgorithm,\n force: boolean,\n): Promise<WrittenKeyPair> {\n const publicPath = join(directory, `${baseName}.public.pqc`);\n const secretPath = join(directory, `${baseName}.secret.pqc`);\n\n if (!force) {\n for (const path of [publicPath, secretPath]) {\n if (existsSync(path)) {\n throw new Error(`${path} already exists. Use --force to overwrite it.`);\n }\n }\n }\n\n const pair = await pqc.keys.generate({ algorithm });\n await mkdir(directory, { recursive: true });\n await writeFile(publicPath, `${pqc.keys.serialize(pair.publicKey)}\\n`);\n await writeFile(secretPath, `${pqc.keys.serialize(pair.secretKey)}\\n`, { mode: 0o600 });\n await chmod(secretPath, 0o600);\n\n return { algorithm, publicPath, secretPath };\n}\n","import { defineCommand } from 'citty';\n\nimport { assertSupportedAlgorithm, writeKeyPair } from '../keyfiles.js';\nimport { item, ok, warn } from '../ui.js';\n\nexport const keygen = defineCommand({\n meta: {\n name: 'keygen',\n description: 'Generate a PQC key pair serialized as base64url',\n },\n args: {\n algorithm: {\n type: 'string',\n description: 'Algorithm of the pair (ml-kem-768 or ml-dsa-65)',\n default: 'ml-kem-768',\n },\n out: {\n type: 'string',\n description: 'Output directory',\n default: 'keys',\n },\n force: {\n type: 'boolean',\n description: 'Overwrite existing keys',\n default: false,\n },\n },\n async run({ args }) {\n const algorithm = assertSupportedAlgorithm(args.algorithm);\n const keys = await writeKeyPair(args.out, algorithm, algorithm, args.force);\n\n ok(`${algorithm} pair generated:`);\n item(`public: ${keys.publicPath}`);\n item(`secret: ${keys.secretPath} (mode 0600)`);\n warn('The secret key must not be committed or leave this environment.');\n },\n});\n"],"mappings":"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","names":["defineCommand","pc","pc","writeFile","existsSync","defineCommand","existsSync","join","defineCommand","existsSync","writeFile","defineCommand","defineCommand","defineCommand"]}
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@pqc-sdk/cli",
|
|
3
|
-
"version": "0.1.
|
|
4
|
-
"description": "CLI
|
|
3
|
+
"version": "0.1.2",
|
|
4
|
+
"description": "CLI for the post-quantum cryptography SDK",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"bin": {
|
|
7
7
|
"pqc": "./dist/index.js"
|
|
@@ -15,7 +15,7 @@
|
|
|
15
15
|
"dependencies": {
|
|
16
16
|
"citty": "^0.2.2",
|
|
17
17
|
"picocolors": "^1.1.1",
|
|
18
|
-
"@pqc-sdk/core": "0.1.
|
|
18
|
+
"@pqc-sdk/core": "0.1.2"
|
|
19
19
|
},
|
|
20
20
|
"devDependencies": {
|
|
21
21
|
"@types/node": "^22.15.29",
|
|
@@ -23,7 +23,7 @@
|
|
|
23
23
|
"vitest": "^3.2.6"
|
|
24
24
|
},
|
|
25
25
|
"license": "MIT",
|
|
26
|
-
"homepage": "https://github.
|
|
26
|
+
"homepage": "https://jeloercc.github.io/pqc-sdk/",
|
|
27
27
|
"bugs": "https://github.com/jeloercc/pqc-sdk/issues",
|
|
28
28
|
"publishConfig": {
|
|
29
29
|
"access": "public"
|