@sebastienrousseau/crypto-mcp 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.
Files changed (54) hide show
  1. package/README.md +75 -22
  2. package/dist/protocol.d.ts +18 -0
  3. package/dist/protocol.d.ts.map +1 -0
  4. package/dist/protocol.js +63 -0
  5. package/dist/server.d.ts +1 -0
  6. package/dist/server.d.ts.map +1 -1
  7. package/dist/server.js +16 -14
  8. package/dist/tools/annotations.d.ts +6 -0
  9. package/dist/tools/annotations.d.ts.map +1 -0
  10. package/dist/tools/annotations.js +68 -0
  11. package/dist/tools/definitions.d.ts +3 -0
  12. package/dist/tools/definitions.d.ts.map +1 -1
  13. package/dist/tools/definitions.js +220 -128
  14. package/dist/tools/index.d.ts +1 -0
  15. package/dist/tools/index.d.ts.map +1 -1
  16. package/dist/tools/index.js +33 -6
  17. package/dist/tools/kem.d.ts +8 -0
  18. package/dist/tools/kem.d.ts.map +1 -0
  19. package/dist/tools/kem.js +156 -0
  20. package/dist/tools/keyimport.d.ts +5 -0
  21. package/dist/tools/keyimport.d.ts.map +1 -0
  22. package/dist/tools/keyimport.js +207 -0
  23. package/dist/tools/keys.d.ts +9 -0
  24. package/dist/tools/keys.d.ts.map +1 -1
  25. package/dist/tools/keys.js +92 -42
  26. package/dist/tools/keystore.d.ts +25 -0
  27. package/dist/tools/keystore.d.ts.map +1 -0
  28. package/dist/tools/keystore.js +84 -0
  29. package/dist/tools/kms.d.ts +1 -0
  30. package/dist/tools/kms.d.ts.map +1 -1
  31. package/dist/tools/kms.js +28 -18
  32. package/dist/tools/result.d.ts +0 -1
  33. package/dist/tools/result.d.ts.map +1 -1
  34. package/dist/tools/result.js +0 -8
  35. package/dist/tools/schema-helpers.d.ts +12 -0
  36. package/dist/tools/schema-helpers.d.ts.map +1 -0
  37. package/dist/tools/schema-helpers.js +38 -0
  38. package/dist/tools/signing.d.ts.map +1 -1
  39. package/dist/tools/signing.js +118 -32
  40. package/dist/tools/stream-definitions.d.ts +3 -0
  41. package/dist/tools/stream-definitions.d.ts.map +1 -0
  42. package/dist/tools/stream-definitions.js +122 -0
  43. package/dist/tools/stream.d.ts +6 -0
  44. package/dist/tools/stream.d.ts.map +1 -0
  45. package/dist/tools/stream.js +97 -0
  46. package/dist/tools/symmetric.d.ts +1 -0
  47. package/dist/tools/symmetric.d.ts.map +1 -1
  48. package/dist/tools/symmetric.js +22 -8
  49. package/dist/tools/validate.d.ts +3 -0
  50. package/dist/tools/validate.d.ts.map +1 -0
  51. package/dist/tools/validate.js +76 -0
  52. package/dist/types.d.ts +19 -2
  53. package/dist/types.d.ts.map +1 -1
  54. package/package.json +19 -17
@@ -0,0 +1,156 @@
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.hybridKemMultiDecapsulateHandler = exports.hybridKemMultiEncapsulateHandler = exports.hybridKemDecapsulateHandler = exports.hybridKemEncapsulateHandler = exports.kemDecapsulate = exports.kemEncapsulate = void 0;
7
+ const node_crypto_1 = __importDefault(require("node:crypto"));
8
+ const crypto_lib_1 = require("@sebastienrousseau/crypto-lib");
9
+ const keystore_1 = require("./keystore");
10
+ const result_1 = require("./result");
11
+ function resolveKemLevel(levelArg, defaultLevel = 768) {
12
+ const num = Number(levelArg);
13
+ if (num === 512 || num === 768 || num === 1024) {
14
+ return num;
15
+ }
16
+ return defaultLevel;
17
+ }
18
+ function inferLevelFromKey(keyKind) {
19
+ if (keyKind === "ml-kem-512")
20
+ return 512;
21
+ if (keyKind === "ml-kem-1024")
22
+ return 1024;
23
+ return 768;
24
+ }
25
+ const kemEncapsulate = async (args) => {
26
+ const level = resolveKemLevel(args.level, 768);
27
+ const { ciphertext, sharedSecret, algorithm } = (0, crypto_lib_1.mlKemEncap)(level, String(args.publicKey));
28
+ const keyHandle = keystore_1.keyStore.add((0, keystore_1.symmetricKey)(Buffer.from(sharedSecret, "hex"), `${algorithm} encapsulation`));
29
+ return (0, result_1.jsonResult)({ algorithm, ciphertext, keyHandle });
30
+ };
31
+ exports.kemEncapsulate = kemEncapsulate;
32
+ const kemDecapsulate = async (args) => {
33
+ const key = keystore_1.keyStore.use(String(args.keyHandle), [
34
+ "ml-kem-512",
35
+ "ml-kem-768",
36
+ "ml-kem-1024",
37
+ ]);
38
+ const defaultLevel = inferLevelFromKey(key.kind);
39
+ const level = resolveKemLevel(args.level, defaultLevel);
40
+ const { sharedSecret, algorithm } = (0, crypto_lib_1.mlKemDecap)(level, key.secret.toString("hex"), String(args.ciphertext));
41
+ const keyHandle = keystore_1.keyStore.add((0, keystore_1.symmetricKey)(Buffer.from(sharedSecret, "hex"), `${algorithm} decapsulation`));
42
+ return (0, result_1.jsonResult)({ algorithm, keyHandle });
43
+ };
44
+ exports.kemDecapsulate = kemDecapsulate;
45
+ const hybridKemEncapsulateHandler = async (args) => {
46
+ const level = resolveKemLevel(args.level, 768);
47
+ const { x25519EphemeralPublic, mlKemCiphertext, sharedSecret, algorithm } = (0, crypto_lib_1.hybridKemEncapsulate)(level, String(args.x25519PublicKey), String(args.mlKemPublicKey));
48
+ const keyHandle = keystore_1.keyStore.add((0, keystore_1.symmetricKey)(Buffer.from(sharedSecret, "hex"), `${algorithm} encapsulation`));
49
+ return (0, result_1.jsonResult)({
50
+ algorithm,
51
+ x25519EphemeralPublic,
52
+ mlKemCiphertext,
53
+ keyHandle,
54
+ });
55
+ };
56
+ exports.hybridKemEncapsulateHandler = hybridKemEncapsulateHandler;
57
+ const hybridKemDecapsulateHandler = async (args) => {
58
+ const xKey = keystore_1.keyStore.use(String(args.x25519KeyHandle), ["x25519"]);
59
+ const kemKey = keystore_1.keyStore.use(String(args.mlKemKeyHandle), [
60
+ "ml-kem-512",
61
+ "ml-kem-768",
62
+ "ml-kem-1024",
63
+ ]);
64
+ const defaultLevel = inferLevelFromKey(kemKey.kind);
65
+ const level = resolveKemLevel(args.level, defaultLevel);
66
+ const { sharedSecret, algorithm } = (0, crypto_lib_1.hybridKemDecapsulate)(level, xKey.secret.toString("hex"), kemKey.secret.toString("hex"), String(args.x25519EphemeralPublic), String(args.mlKemCiphertext));
67
+ const keyHandle = keystore_1.keyStore.add((0, keystore_1.symmetricKey)(Buffer.from(sharedSecret, "hex"), `${algorithm} decapsulation`));
68
+ return (0, result_1.jsonResult)({ algorithm, keyHandle });
69
+ };
70
+ exports.hybridKemDecapsulateHandler = hybridKemDecapsulateHandler;
71
+ function parseHybridRecipients(raw) {
72
+ const parsed = JSON.parse(String(raw));
73
+ if (!Array.isArray(parsed) || parsed.length === 0) {
74
+ throw new Error("recipients must be a non-empty array of recipient objects");
75
+ }
76
+ return parsed;
77
+ }
78
+ const hybridKemMultiEncapsulateHandler = async (args) => {
79
+ const recipients = parseHybridRecipients(args.recipients);
80
+ let dek;
81
+ let keyHandle;
82
+ if (args.keyHandle !== undefined && args.keyHandle !== null) {
83
+ keyHandle = String(args.keyHandle);
84
+ const stored = keystore_1.keyStore.use(keyHandle, ["symmetric-256"]);
85
+ dek = Buffer.from(stored.secret);
86
+ }
87
+ else {
88
+ dek = node_crypto_1.default.randomBytes(32);
89
+ keyHandle = keystore_1.keyStore.add((0, keystore_1.symmetricKey)(dek, "multi-recipient hybrid KEM DEK"));
90
+ }
91
+ const encryptedRecipients = recipients.map((r) => {
92
+ const wrapped = (0, crypto_lib_1.wrapDekHybrid)(dek, String(r.x25519PublicKey), String(r.mlKemPublicKey));
93
+ return {
94
+ ...(r.id !== undefined ? { id: String(r.id) } : {}),
95
+ ephemeralPublicKey: wrapped.ephemeralPublicKey,
96
+ mlKemCiphertext: wrapped.mlKemCiphertext,
97
+ wrappedKey: wrapped.wrappedKey,
98
+ encryptedKey: wrapped.wrappedKey,
99
+ };
100
+ });
101
+ return (0, result_1.jsonResult)({
102
+ algorithm: "multi-hybrid-kem-768-aes-256-gcm",
103
+ keyHandle,
104
+ recipients: encryptedRecipients,
105
+ });
106
+ };
107
+ exports.hybridKemMultiEncapsulateHandler = hybridKemMultiEncapsulateHandler;
108
+ function resolveDirectRecipient(args) {
109
+ const encKey = args.wrappedKey ?? args.encryptedKey;
110
+ if (!args.ephemeralPublicKey || !args.mlKemCiphertext || !encKey) {
111
+ return undefined;
112
+ }
113
+ return {
114
+ ephemeralPublicKey: String(args.ephemeralPublicKey),
115
+ mlKemCiphertext: String(args.mlKemCiphertext),
116
+ wrappedKey: String(encKey),
117
+ };
118
+ }
119
+ function resolveListRecipient(args) {
120
+ if (args.recipients === undefined)
121
+ return undefined;
122
+ const list = JSON.parse(String(args.recipients));
123
+ const item = args.recipientId !== undefined
124
+ ? list.find((e) => e.id === String(args.recipientId))
125
+ : list[args.recipientIndex !== undefined ? Number(args.recipientIndex) : 0];
126
+ if (!item)
127
+ return undefined;
128
+ const key = item.wrappedKey ?? item.encryptedKey;
129
+ if (!key)
130
+ return undefined;
131
+ return {
132
+ ephemeralPublicKey: item.ephemeralPublicKey,
133
+ mlKemCiphertext: item.mlKemCiphertext,
134
+ wrappedKey: String(key),
135
+ };
136
+ }
137
+ function resolveRecipientEntry(args) {
138
+ const resolved = resolveDirectRecipient(args) ?? resolveListRecipient(args);
139
+ if (!resolved) {
140
+ throw new Error("Missing recipient encryption details (ephemeralPublicKey, mlKemCiphertext, wrappedKey)");
141
+ }
142
+ return resolved;
143
+ }
144
+ const hybridKemMultiDecapsulateHandler = async (args) => {
145
+ const xKey = keystore_1.keyStore.use(String(args.x25519KeyHandle), ["x25519"]);
146
+ const kemKey = keystore_1.keyStore.use(String(args.mlKemKeyHandle), ["ml-kem-768"]);
147
+ const entry = resolveRecipientEntry(args);
148
+ const dek = (0, crypto_lib_1.unwrapDekHybrid)(xKey.secret, kemKey.secret, entry.ephemeralPublicKey, entry.mlKemCiphertext, entry.wrappedKey);
149
+ const keyHandle = keystore_1.keyStore.add((0, keystore_1.symmetricKey)(Buffer.from(dek), "multi-recipient hybrid KEM DEK"));
150
+ return (0, result_1.jsonResult)({
151
+ algorithm: "multi-hybrid-kem-768-aes-256-gcm",
152
+ keyHandle,
153
+ });
154
+ };
155
+ exports.hybridKemMultiDecapsulateHandler = hybridKemMultiDecapsulateHandler;
156
+ //# sourceMappingURL=kem.js.map
@@ -0,0 +1,5 @@
1
+ import fs from "node:fs";
2
+ import { ToolHandler } from "./result";
3
+ export declare function readAtMost(handle: Pick<fs.promises.FileHandle, "read">, max: number): Promise<Buffer>;
4
+ export declare const importKey: ToolHandler;
5
+ //# sourceMappingURL=keyimport.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"keyimport.d.ts","sourceRoot":"","sources":["../../src/tools/keyimport.ts"],"names":[],"mappings":"AAGA,OAAO,EAAE,MAAM,SAAS,CAAC;AAUzB,OAAO,EAAY,WAAW,EAAc,MAAM,UAAU,CAAC;AA4F7D,wBAAsB,UAAU,CAC9B,MAAM,EAAE,IAAI,CAAC,EAAE,CAAC,QAAQ,CAAC,UAAU,EAAE,MAAM,CAAC,EAC5C,GAAG,EAAE,MAAM,GACV,OAAO,CAAC,MAAM,CAAC,CAajB;AAuKD,eAAO,MAAM,SAAS,EAAE,WAUvB,CAAC"}
@@ -0,0 +1,207 @@
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.importKey = void 0;
7
+ exports.readAtMost = readAtMost;
8
+ const node_crypto_1 = __importDefault(require("node:crypto"));
9
+ const node_fs_1 = __importDefault(require("node:fs"));
10
+ const node_path_1 = __importDefault(require("node:path"));
11
+ const definitions_1 = require("./definitions");
12
+ const keys_1 = require("./keys");
13
+ const keystore_1 = require("./keystore");
14
+ const result_1 = require("./result");
15
+ const crypto_lib_1 = require("@sebastienrousseau/crypto-lib");
16
+ const RAW_KEY_BYTES = 32;
17
+ const SOURCE = "crypto_key_import";
18
+ const UNREADABLE = `Key file not found or not readable in ${definitions_1.KEY_DIR_ENV}`;
19
+ const OPEN_FLAGS = node_fs_1.default.constants.O_RDONLY | (node_fs_1.default.constants.O_NOFOLLOW | 0);
20
+ async function keyDirectory() {
21
+ const dir = process.env[definitions_1.KEY_DIR_ENV];
22
+ if (!dir) {
23
+ throw new Error(`Key import is disabled: set ${definitions_1.KEY_DIR_ENV} to the directory holding the key files`);
24
+ }
25
+ try {
26
+ return await node_fs_1.default.promises.realpath(dir);
27
+ }
28
+ catch {
29
+ throw new Error(`${definitions_1.KEY_DIR_ENV} does not exist or cannot be read`);
30
+ }
31
+ }
32
+ function checkRelative(file) {
33
+ if (node_path_1.default.isAbsolute(file)) {
34
+ throw new Error(`path must be relative to ${definitions_1.KEY_DIR_ENV}`);
35
+ }
36
+ if (Array.from(file).some((char) => char < " ")) {
37
+ throw new Error("path must not contain control characters");
38
+ }
39
+ if (file.split(/[\\/]/).includes("..")) {
40
+ throw new Error("path must not contain '..' segments");
41
+ }
42
+ }
43
+ async function resolveKeyFile(dir, file) {
44
+ checkRelative(file);
45
+ const resolved = await fsStep(node_fs_1.default.promises.realpath(node_path_1.default.resolve(dir, file)));
46
+ if (!resolved.startsWith(dir + node_path_1.default.sep)) {
47
+ throw new Error(`path resolves outside ${definitions_1.KEY_DIR_ENV}`);
48
+ }
49
+ return resolved;
50
+ }
51
+ async function fsStep(operation) {
52
+ try {
53
+ return await operation;
54
+ }
55
+ catch {
56
+ throw new Error(UNREADABLE);
57
+ }
58
+ }
59
+ function checkFile(stats) {
60
+ if (!stats.isFile())
61
+ throw new Error("path is not a regular file");
62
+ if (stats.size > definitions_1.MAX_KEY_FILE_BYTES) {
63
+ throw new Error(`Key file is larger than ${definitions_1.MAX_KEY_FILE_BYTES} bytes`);
64
+ }
65
+ }
66
+ async function readAtMost(handle, max) {
67
+ const buf = Buffer.alloc(max + 1);
68
+ let length = 0;
69
+ for (;;) {
70
+ const { bytesRead } = await handle.read(buf, length, buf.length - length);
71
+ if (bytesRead === 0)
72
+ break;
73
+ length += bytesRead;
74
+ if (length > max) {
75
+ buf.fill(0);
76
+ throw new Error(`Key file is larger than ${max} bytes`);
77
+ }
78
+ }
79
+ return buf.subarray(0, length);
80
+ }
81
+ async function readKeyFile(file) {
82
+ checkFile(await fsStep(node_fs_1.default.promises.stat(file)));
83
+ const handle = await fsStep(node_fs_1.default.promises.open(file, OPEN_FLAGS));
84
+ try {
85
+ checkFile(await fsStep(handle.stat()));
86
+ return await readAtMost(handle, definitions_1.MAX_KEY_FILE_BYTES);
87
+ }
88
+ finally {
89
+ await handle.close();
90
+ }
91
+ }
92
+ function hexDigit(byte) {
93
+ if (byte >= 0x30 && byte <= 0x39)
94
+ return byte - 0x30;
95
+ const lower = byte | 0x20;
96
+ if (lower >= 0x61 && lower <= 0x66)
97
+ return lower - 0x57;
98
+ return -1;
99
+ }
100
+ function decodeHex(text) {
101
+ const out = Buffer.alloc(text.length / 2);
102
+ for (let i = 0; i < out.length; i++) {
103
+ const high = hexDigit(text[2 * i]);
104
+ const low = hexDigit(text[2 * i + 1]);
105
+ if (high < 0 || low < 0) {
106
+ (0, crypto_lib_1.wipeMemory)(out);
107
+ return undefined;
108
+ }
109
+ out[i] = high * 16 + low;
110
+ }
111
+ return out;
112
+ }
113
+ function trimEnd(bytes) {
114
+ let end = bytes.length;
115
+ while (end > 0 && " \t\r\n".includes(String.fromCharCode(bytes[end - 1]))) {
116
+ end--;
117
+ }
118
+ return bytes.subarray(0, end);
119
+ }
120
+ function rawSecret(bytes) {
121
+ if (bytes.length === RAW_KEY_BYTES)
122
+ return Buffer.from(bytes);
123
+ const text = trimEnd(bytes);
124
+ const secret = text.length === 2 * RAW_KEY_BYTES ? decodeHex(text) : undefined;
125
+ if (!secret) {
126
+ throw new Error("Unrecognised key file: expected a PKCS#8 PEM private key, 32 raw bytes, or 64 hex digits");
127
+ }
128
+ return secret;
129
+ }
130
+ function rawKey(bytes, kind) {
131
+ const secret = rawSecret(bytes);
132
+ if (kind === "hmac-sha256") {
133
+ return {
134
+ kind: "hmac-sha256",
135
+ secret,
136
+ info: { bits: 256, source: SOURCE, usage: "crypto_sign, crypto_verify" },
137
+ };
138
+ }
139
+ return (0, keystore_1.symmetricKey)(secret, SOURCE);
140
+ }
141
+ function privateKeyKind(key) {
142
+ const details = key.asymmetricKeyDetails;
143
+ switch (key.asymmetricKeyType) {
144
+ case "ed25519":
145
+ return { kind: "ed25519", info: {} };
146
+ case "rsa":
147
+ if (!definitions_1.RSA_MODULUS_LENGTHS.includes(Number(details.modulusLength))) {
148
+ throw new Error(`Unsupported RSA modulus length (allowed: ${definitions_1.RSA_MODULUS_LENGTHS.join(", ")})`);
149
+ }
150
+ return { kind: "rsa", info: { bits: details.modulusLength } };
151
+ case "ec":
152
+ if (!definitions_1.EC_CURVES.includes(String(details.namedCurve))) {
153
+ throw new Error(`Unsupported EC curve (allowed: ${definitions_1.EC_CURVES.join(", ")})`);
154
+ }
155
+ return { kind: "ecc", info: { curve: details.namedCurve } };
156
+ default:
157
+ throw new Error(`Unsupported private key type: ${key.asymmetricKeyType} (allowed: ed25519, rsa, ec)`);
158
+ }
159
+ }
160
+ function parsePrivateKey(bytes) {
161
+ try {
162
+ return node_crypto_1.default.createPrivateKey({ key: bytes, format: "pem" });
163
+ }
164
+ catch {
165
+ throw new Error("Key file is not a valid PKCS#8 PEM private key");
166
+ }
167
+ }
168
+ function pkcs8Key(bytes) {
169
+ const privateKey = parsePrivateKey(bytes);
170
+ const { kind, info } = privateKeyKind(privateKey);
171
+ const publicKey = node_crypto_1.default.createPublicKey(privateKey);
172
+ return (0, keys_1.asymmetric)(kind, { privateKey, publicKey }, { ...info, source: SOURCE });
173
+ }
174
+ function pemKey(bytes, kind) {
175
+ if (kind !== undefined) {
176
+ throw new Error("kind applies only to raw keys; a PEM key's type comes from the key");
177
+ }
178
+ if (bytes.includes("-----BEGIN ENCRYPTED PRIVATE KEY-----")) {
179
+ throw new Error("Encrypted PKCS#8 keys are not supported: decrypt the key file first (openssl pkcs8 -nocrypt)");
180
+ }
181
+ if (bytes.includes("-----BEGIN PRIVATE KEY-----"))
182
+ return pkcs8Key(bytes);
183
+ if (bytes.includes("PUBLIC KEY-----")) {
184
+ throw new Error("Public keys are not imported: pass the PEM to crypto_verify as publicKey instead");
185
+ }
186
+ throw new Error("Unsupported PEM block: only unencrypted PKCS#8 private keys ('BEGIN PRIVATE KEY') are imported");
187
+ }
188
+ function parseKey(bytes, kind) {
189
+ if (bytes.length !== RAW_KEY_BYTES && bytes.includes("-----BEGIN ")) {
190
+ return pemKey(bytes, kind);
191
+ }
192
+ return rawKey(bytes, kind);
193
+ }
194
+ const importKey = async (args) => {
195
+ const dir = await keyDirectory();
196
+ const file = await resolveKeyFile(dir, String(args.path));
197
+ const bytes = await readKeyFile(file);
198
+ try {
199
+ const material = parseKey(bytes, args.kind);
200
+ return (0, result_1.jsonResult)(keystore_1.keyStore.describe(keystore_1.keyStore.add(material)));
201
+ }
202
+ finally {
203
+ (0, crypto_lib_1.wipeMemory)(bytes);
204
+ }
205
+ };
206
+ exports.importKey = importKey;
207
+ //# sourceMappingURL=keyimport.js.map
@@ -1,4 +1,13 @@
1
+ import crypto from "node:crypto";
2
+ import { KeyMaterial } from "./keystore";
1
3
  import { ToolHandler } from "./result";
4
+ export declare function publicPem(key: crypto.KeyObject): string;
5
+ export declare function asymmetric(kind: KeyMaterial["kind"], pair: {
6
+ privateKey: crypto.KeyObject;
7
+ publicKey: crypto.KeyObject;
8
+ }, info: Record<string, unknown>): KeyMaterial;
2
9
  export declare const generateKey: ToolHandler;
10
+ export declare const listKeys: ToolHandler;
11
+ export declare const destroyKey: ToolHandler;
3
12
  export declare const inspectKey: ToolHandler;
4
13
  //# sourceMappingURL=keys.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"keys.d.ts","sourceRoot":"","sources":["../../src/tools/keys.ts"],"names":[],"mappings":"AAKA,OAAO,EAAY,WAAW,EAA2B,MAAM,UAAU,CAAC;AA2E1E,eAAO,MAAM,WAAW,EAAE,WAOzB,CAAC;AAmBF,eAAO,MAAM,UAAU,EAAE,WAaxB,CAAC"}
1
+ {"version":3,"file":"keys.d.ts","sourceRoot":"","sources":["../../src/tools/keys.ts"],"names":[],"mappings":"AAEA,OAAO,MAAM,MAAM,aAAa,CAAC;AAOjC,OAAO,EAAE,WAAW,EAA0B,MAAM,YAAY,CAAC;AACjE,OAAO,EAAY,WAAW,EAA2B,MAAM,UAAU,CAAC;AAG1E,wBAAgB,SAAS,CAAC,GAAG,EAAE,MAAM,CAAC,SAAS,GAAG,MAAM,CAEvD;AAGD,wBAAgB,UAAU,CACxB,IAAI,EAAE,WAAW,CAAC,MAAM,CAAC,EACzB,IAAI,EAAE;IAAE,UAAU,EAAE,MAAM,CAAC,SAAS,CAAC;IAAC,SAAS,EAAE,MAAM,CAAC,SAAS,CAAA;CAAE,EACnE,IAAI,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAC5B,WAAW,CAOb;AA2GD,eAAO,MAAM,WAAW,EAAE,WAOzB,CAAC;AAGF,eAAO,MAAM,QAAQ,EAAE,WAC6C,CAAC;AAGrE,eAAO,MAAM,UAAU,EAAE,WAMxB,CAAC;AAmBF,eAAO,MAAM,UAAU,EAAE,WAaxB,CAAC"}
@@ -3,81 +3,131 @@ var __importDefault = (this && this.__importDefault) || function (mod) {
3
3
  return (mod && mod.__esModule) ? mod : { "default": mod };
4
4
  };
5
5
  Object.defineProperty(exports, "__esModule", { value: true });
6
- exports.inspectKey = exports.generateKey = void 0;
6
+ exports.inspectKey = exports.destroyKey = exports.listKeys = exports.generateKey = void 0;
7
+ exports.publicPem = publicPem;
8
+ exports.asymmetric = asymmetric;
7
9
  const node_crypto_1 = __importDefault(require("node:crypto"));
8
10
  const crypto_lib_1 = require("@sebastienrousseau/crypto-lib");
9
11
  const definitions_1 = require("./definitions");
12
+ const keystore_1 = require("./keystore");
10
13
  const result_1 = require("./result");
11
- const PEM = {
12
- publicKeyEncoding: { type: "spki", format: "pem" },
13
- privateKeyEncoding: { type: "pkcs8", format: "pem" },
14
- };
14
+ function publicPem(key) {
15
+ return key.export({ type: "spki", format: "pem" });
16
+ }
17
+ function asymmetric(kind, pair, info) {
18
+ return {
19
+ kind,
20
+ privateKey: pair.privateKey,
21
+ publicKey: pair.publicKey,
22
+ info: { ...info, quantumSafe: false, publicKey: publicPem(pair.publicKey) },
23
+ };
24
+ }
15
25
  function generateEd25519() {
16
- const { publicKey, privateKey } = node_crypto_1.default.generateKeyPairSync("ed25519", PEM);
17
- return { type: "ed25519", quantumSafe: false, publicKey, privateKey };
26
+ return asymmetric("ed25519", node_crypto_1.default.generateKeyPairSync("ed25519"), {});
18
27
  }
19
28
  function generateRsa(args) {
20
- const modulusLength = Number(args.modulusLength) || 2048;
29
+ const modulusLength = Number(args.modulusLength ?? 2048);
21
30
  if (!definitions_1.RSA_MODULUS_LENGTHS.includes(modulusLength)) {
22
31
  throw new Error(`Unsupported RSA modulus length: ${modulusLength} (allowed: ${definitions_1.RSA_MODULUS_LENGTHS.join(", ")})`);
23
32
  }
24
- const { publicKey, privateKey } = node_crypto_1.default.generateKeyPairSync("rsa", {
25
- modulusLength,
26
- ...PEM,
27
- });
28
- return {
29
- type: "rsa",
30
- bits: modulusLength,
31
- quantumSafe: false,
32
- publicKey,
33
- privateKey,
34
- };
33
+ const pair = node_crypto_1.default.generateKeyPairSync("rsa", { modulusLength });
34
+ return asymmetric("rsa", pair, { bits: modulusLength });
35
35
  }
36
36
  function generateEcc(args) {
37
- const namedCurve = args.curve || "prime256v1";
37
+ const namedCurve = String(args.curve ?? "prime256v1");
38
38
  if (!definitions_1.EC_CURVES.includes(namedCurve)) {
39
39
  throw new Error(`Unsupported curve: ${namedCurve} (allowed: ${definitions_1.EC_CURVES.join(", ")})`);
40
40
  }
41
- const { publicKey, privateKey } = node_crypto_1.default.generateKeyPairSync("ec", {
42
- namedCurve,
43
- ...PEM,
44
- });
41
+ const pair = node_crypto_1.default.generateKeyPairSync("ec", { namedCurve });
42
+ return asymmetric("ecc", pair, { curve: namedCurve });
43
+ }
44
+ function generateMlKem(level) {
45
+ const { publicKey, secretKey } = (0, crypto_lib_1.mlKemKeygen)(level);
46
+ const securityCategory = level === 512 ? 1 : level === 768 ? 3 : 5;
45
47
  return {
46
- type: "ecc",
47
- curve: namedCurve,
48
- quantumSafe: false,
49
- publicKey,
50
- privateKey,
48
+ kind: `ml-kem-${level}`,
49
+ secret: Buffer.from(secretKey, "hex"),
50
+ info: {
51
+ standard: "NIST FIPS 203",
52
+ securityCategory,
53
+ quantumSafe: true,
54
+ encoding: "hex",
55
+ publicKey,
56
+ },
51
57
  };
52
58
  }
53
- function generateMlKem768() {
54
- const { publicKey, secretKey } = (0, crypto_lib_1.mlKemKeygen)(768);
59
+ function generateMlDsa(level) {
60
+ const { publicKey, secretKey } = (0, crypto_lib_1.mlDsaKeygen)(level);
61
+ const securityCategory = level === 44 ? 2 : level === 65 ? 3 : 5;
55
62
  return {
56
- type: "ml-kem-768",
57
- standard: "NIST FIPS 203",
58
- securityCategory: 3,
59
- quantumSafe: true,
60
- encoding: "hex",
61
- publicKey,
62
- privateKey: secretKey,
63
+ kind: `ml-dsa-${level}`,
64
+ secret: Buffer.from(secretKey, "hex"),
65
+ info: {
66
+ standard: "NIST FIPS 204",
67
+ securityCategory,
68
+ quantumSafe: true,
69
+ encoding: "hex",
70
+ publicKey,
71
+ },
72
+ };
73
+ }
74
+ function generateSymmetric() {
75
+ return (0, keystore_1.symmetricKey)(node_crypto_1.default.randomBytes(32), "generated");
76
+ }
77
+ function generateX25519() {
78
+ const pair = (0, crypto_lib_1.generateX25519KeyPair)();
79
+ return {
80
+ kind: "x25519",
81
+ secret: Buffer.from(pair.privateKey, "hex"),
82
+ info: {
83
+ curve: "x25519",
84
+ quantumSafe: false,
85
+ encoding: "hex",
86
+ publicKey: pair.publicKey,
87
+ },
88
+ };
89
+ }
90
+ function generateHmac() {
91
+ return {
92
+ kind: "hmac-sha256",
93
+ secret: node_crypto_1.default.randomBytes(32),
94
+ info: { bits: 256, usage: "crypto_sign, crypto_verify" },
63
95
  };
64
96
  }
65
97
  const GENERATORS = {
66
98
  ed25519: generateEd25519,
99
+ x25519: generateX25519,
67
100
  rsa: generateRsa,
68
101
  ecc: generateEcc,
69
- "ml-kem-768": generateMlKem768,
102
+ "ml-kem-512": () => generateMlKem(512),
103
+ "ml-kem-768": () => generateMlKem(768),
104
+ "ml-kem-1024": () => generateMlKem(1024),
105
+ "ml-dsa-44": () => generateMlDsa(44),
106
+ "ml-dsa-65": () => generateMlDsa(65),
107
+ "ml-dsa-87": () => generateMlDsa(87),
108
+ "symmetric-256": generateSymmetric,
109
+ "hmac-sha256": generateHmac,
70
110
  };
71
111
  const generateKey = async (args) => {
72
- const type = args.type || "ed25519";
112
+ const type = String(args.type);
73
113
  const generator = Object.hasOwn(GENERATORS, type)
74
114
  ? GENERATORS[type]
75
115
  : undefined;
76
116
  if (!generator)
77
117
  return (0, result_1.errorResult)(`Unsupported key type: ${type}`);
78
- return (0, result_1.jsonResult)(generator(args));
118
+ return (0, result_1.jsonResult)(keystore_1.keyStore.describe(keystore_1.keyStore.add(generator(args))));
79
119
  };
80
120
  exports.generateKey = generateKey;
121
+ const listKeys = async () => (0, result_1.jsonResult)({ keys: keystore_1.keyStore.list(), capacity: keystore_1.keyStore.capacity });
122
+ exports.listKeys = listKeys;
123
+ const destroyKey = async (args) => {
124
+ const keyHandle = String(args.keyHandle);
125
+ if (!keystore_1.keyStore.remove(keyHandle)) {
126
+ return (0, result_1.errorResult)("Unknown key handle: nothing was destroyed");
127
+ }
128
+ return (0, result_1.jsonResult)({ keyHandle, destroyed: true });
129
+ };
130
+ exports.destroyKey = destroyKey;
81
131
  function classifyKey(keyData) {
82
132
  if (keyData.includes("-----BEGIN PGP"))
83
133
  return "OpenPGP Key Block";
@@ -97,7 +147,7 @@ function keyFormat(keyData) {
97
147
  return keyData.includes("-----BEGIN") ? "PEM" : "Raw";
98
148
  }
99
149
  const inspectKey = async (args) => {
100
- const keyData = args.keyData ? String(args.keyData).trim() : "";
150
+ const keyData = String(args.keyData).trim();
101
151
  const fingerprint = node_crypto_1.default
102
152
  .createHash("sha256")
103
153
  .update(keyData)
@@ -0,0 +1,25 @@
1
+ import crypto from "node:crypto";
2
+ export type KeyKind = "ed25519" | "rsa" | "ecc" | "x25519" | "ml-kem-512" | "ml-kem-768" | "ml-kem-1024" | "ml-dsa-44" | "ml-dsa-65" | "ml-dsa-87" | "symmetric-256" | "hmac-sha256";
3
+ export interface KeyMaterial {
4
+ kind: KeyKind;
5
+ info: Record<string, unknown>;
6
+ privateKey?: crypto.KeyObject;
7
+ publicKey?: crypto.KeyObject;
8
+ secret?: Buffer;
9
+ }
10
+ export declare const MAX_KEYS = 64;
11
+ export declare const KEY_HANDLE_PATTERN = "^kh_[0-9a-f]{32}$";
12
+ export declare function symmetricKey(secret: Buffer, source: string): KeyMaterial;
13
+ export declare class KeyStore {
14
+ readonly capacity: number;
15
+ private readonly keys;
16
+ constructor(capacity?: number);
17
+ get size(): number;
18
+ add(material: KeyMaterial): string;
19
+ use(handle: string, kinds: readonly KeyKind[]): KeyMaterial;
20
+ describe(handle: string): Record<string, unknown>;
21
+ list(): Array<Record<string, unknown>>;
22
+ remove(handle: string): boolean;
23
+ }
24
+ export declare const keyStore: KeyStore;
25
+ //# sourceMappingURL=keystore.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"keystore.d.ts","sourceRoot":"","sources":["../../src/tools/keystore.ts"],"names":[],"mappings":"AAEA,OAAO,MAAM,MAAM,aAAa,CAAC;AAajC,MAAM,MAAM,OAAO,GACf,SAAS,GACT,KAAK,GACL,KAAK,GACL,QAAQ,GACR,YAAY,GACZ,YAAY,GACZ,aAAa,GACb,WAAW,GACX,WAAW,GACX,WAAW,GACX,eAAe,GACf,aAAa,CAAC;AAGlB,MAAM,WAAW,WAAW;IAC1B,IAAI,EAAE,OAAO,CAAC;IAEd,IAAI,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;IAE9B,UAAU,CAAC,EAAE,MAAM,CAAC,SAAS,CAAC;IAE9B,SAAS,CAAC,EAAE,MAAM,CAAC,SAAS,CAAC;IAE7B,MAAM,CAAC,EAAE,MAAM,CAAC;CACjB;AAOD,eAAO,MAAM,QAAQ,KAAK,CAAC;AAG3B,eAAO,MAAM,kBAAkB,sBAAsB,CAAC;AAMtD,wBAAgB,YAAY,CAAC,MAAM,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,GAAG,WAAW,CAUxE;AAUD,qBAAa,QAAQ;aAIgB,QAAQ,EAAE,MAAM;IAHnD,OAAO,CAAC,QAAQ,CAAC,IAAI,CAAgC;gBAGlB,QAAQ,GAAE,MAAiB;IAG9D,IAAW,IAAI,IAAI,MAAM,CAExB;IAGM,GAAG,CAAC,QAAQ,EAAE,WAAW,GAAG,MAAM;IAiBlC,GAAG,CAAC,MAAM,EAAE,MAAM,EAAE,KAAK,EAAE,SAAS,OAAO,EAAE,GAAG,WAAW;IAkB3D,QAAQ,CAAC,MAAM,EAAE,MAAM,GAAG,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC;IAWjD,IAAI,IAAI,KAAK,CAAC,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;IAKtC,MAAM,CAAC,MAAM,EAAE,MAAM,GAAG,OAAO;CAOvC;AAGD,eAAO,MAAM,QAAQ,UAAiB,CAAC"}