@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,84 @@
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.keyStore = exports.KeyStore = exports.KEY_HANDLE_PATTERN = exports.MAX_KEYS = void 0;
7
+ exports.symmetricKey = symmetricKey;
8
+ const node_crypto_1 = __importDefault(require("node:crypto"));
9
+ const crypto_lib_1 = require("@sebastienrousseau/crypto-lib");
10
+ exports.MAX_KEYS = 64;
11
+ exports.KEY_HANDLE_PATTERN = "^kh_[0-9a-f]{32}$";
12
+ function symmetricKey(secret, source) {
13
+ return {
14
+ kind: "symmetric-256",
15
+ secret,
16
+ info: {
17
+ bits: 256,
18
+ source,
19
+ usage: "crypto_encrypt, crypto_decrypt, crypto_kms_wrap",
20
+ },
21
+ };
22
+ }
23
+ function wipe(key) {
24
+ if (key.secret) {
25
+ (0, crypto_lib_1.wipeMemory)(key.secret);
26
+ }
27
+ }
28
+ class KeyStore {
29
+ capacity;
30
+ keys = new Map();
31
+ constructor(capacity = exports.MAX_KEYS) {
32
+ this.capacity = capacity;
33
+ }
34
+ get size() {
35
+ return this.keys.size;
36
+ }
37
+ add(material) {
38
+ const handle = `kh_${node_crypto_1.default.randomBytes(16).toString("hex")}`;
39
+ this.keys.set(handle, {
40
+ ...material,
41
+ createdAt: new Date().toISOString(),
42
+ });
43
+ while (this.keys.size > this.capacity) {
44
+ const oldest = this.keys.keys().next().value;
45
+ this.remove(oldest);
46
+ }
47
+ return handle;
48
+ }
49
+ use(handle, kinds) {
50
+ const key = this.keys.get(handle);
51
+ if (!key) {
52
+ throw new Error("Unknown key handle: it was never issued, or the key was destroyed or evicted");
53
+ }
54
+ if (!kinds.includes(key.kind)) {
55
+ throw new Error(`Key handle refers to a ${key.kind} key; this tool needs ${kinds.join(" or ")}`);
56
+ }
57
+ this.keys.delete(handle);
58
+ this.keys.set(handle, key);
59
+ return key;
60
+ }
61
+ describe(handle) {
62
+ const key = this.keys.get(handle);
63
+ return {
64
+ keyHandle: handle,
65
+ type: key.kind,
66
+ createdAt: key.createdAt,
67
+ ...key.info,
68
+ };
69
+ }
70
+ list() {
71
+ return [...this.keys.keys()].map((handle) => this.describe(handle));
72
+ }
73
+ remove(handle) {
74
+ const key = this.keys.get(handle);
75
+ if (!key)
76
+ return false;
77
+ wipe(key);
78
+ this.keys.delete(handle);
79
+ return true;
80
+ }
81
+ }
82
+ exports.KeyStore = KeyStore;
83
+ exports.keyStore = new KeyStore();
84
+ //# sourceMappingURL=keystore.js.map
@@ -1,4 +1,5 @@
1
1
  import { ToolHandler } from "./result";
2
+ export declare const MAX_KEK_LABELS = 64;
2
3
  export declare const kmsWrap: ToolHandler;
3
4
  export declare const kmsUnwrap: ToolHandler;
4
5
  //# sourceMappingURL=kms.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"kms.d.ts","sourceRoot":"","sources":["../../src/tools/kms.ts"],"names":[],"mappings":"AAGA,OAAO,EAAY,WAAW,EAA2B,MAAM,UAAU,CAAC;AA4C1E,eAAO,MAAM,OAAO,EAAE,WAerB,CAAC;AAGF,eAAO,MAAM,SAAS,EAAE,WAkBvB,CAAC"}
1
+ {"version":3,"file":"kms.d.ts","sourceRoot":"","sources":["../../src/tools/kms.ts"],"names":[],"mappings":"AAKA,OAAO,EAAY,WAAW,EAAc,MAAM,UAAU,CAAC;AAe7D,eAAO,MAAM,cAAc,KAAK,CAAC;AA2CjC,eAAO,MAAM,OAAO,EAAE,WAmBrB,CAAC;AAMF,eAAO,MAAM,SAAS,EAAE,WAgBvB,CAAC"}
package/dist/tools/kms.js CHANGED
@@ -1,16 +1,20 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.kmsUnwrap = exports.kmsWrap = void 0;
3
+ exports.kmsUnwrap = exports.kmsWrap = exports.MAX_KEK_LABELS = void 0;
4
4
  const crypto_kms_1 = require("@sebastienrousseau/crypto-kms");
5
+ const crypto_lib_1 = require("@sebastienrousseau/crypto-lib");
6
+ const keystore_1 = require("./keystore");
5
7
  const result_1 = require("./result");
8
+ const symmetric_1 = require("./symmetric");
6
9
  const localKms = new crypto_kms_1.LocalKmsProvider();
7
10
  const localKeyIds = new Map();
11
+ exports.MAX_KEK_LABELS = 64;
8
12
  function kmsRequest(args) {
9
- const provider = String(args.provider || "local");
13
+ const provider = String(args.provider);
10
14
  if (provider !== "local") {
11
15
  throw new Error(`KMS provider '${provider}' is not configured in this server; only 'local' is available`);
12
16
  }
13
- return { provider, keyId: String(args.keyId || "kms-key-default") };
17
+ return { provider, keyId: String(args.keyId) };
14
18
  }
15
19
  function wrapContext(keyId) {
16
20
  return { purpose: "crypto-mcp-dek-wrap", keyId };
@@ -19,6 +23,9 @@ async function localKeyFor(label) {
19
23
  const existing = localKeyIds.get(label);
20
24
  if (existing)
21
25
  return existing;
26
+ if (localKeyIds.size >= exports.MAX_KEK_LABELS) {
27
+ throw new Error(`At most ${exports.MAX_KEK_LABELS} KEK labels can be created; reuse an existing keyId`);
28
+ }
22
29
  const created = await localKms.createKey("aes-256-gcm", "wrap", {
23
30
  label,
24
31
  });
@@ -27,15 +34,20 @@ async function localKeyFor(label) {
27
34
  }
28
35
  const kmsWrap = async (args) => {
29
36
  const { provider, keyId } = kmsRequest(args);
30
- const dek = (0, result_1.parseKey256)(args.dek, "dek");
31
- const kmsKeyId = await localKeyFor(keyId);
32
- const { ciphertext } = await localKms.encrypt(kmsKeyId, dek, wrapContext(keyId));
33
- return (0, result_1.jsonResult)({
34
- provider,
35
- keyId,
36
- status: "wrapped",
37
- wrappedKey: Buffer.from(ciphertext, "base64").toString("hex"),
38
- });
37
+ const dek = Buffer.from((0, symmetric_1.symmetricSecret)(args.keyHandle));
38
+ try {
39
+ const kmsKeyId = await localKeyFor(keyId);
40
+ const { ciphertext } = await localKms.encrypt(kmsKeyId, dek, wrapContext(keyId));
41
+ return (0, result_1.jsonResult)({
42
+ provider,
43
+ keyId,
44
+ status: "wrapped",
45
+ wrappedKey: Buffer.from(ciphertext, "base64").toString("hex"),
46
+ });
47
+ }
48
+ finally {
49
+ (0, crypto_lib_1.wipeMemory)(dek);
50
+ }
39
51
  };
40
52
  exports.kmsWrap = kmsWrap;
41
53
  const kmsUnwrap = async (args) => {
@@ -46,12 +58,10 @@ const kmsUnwrap = async (args) => {
46
58
  }
47
59
  const wrapped = Buffer.from(String(args.wrappedKey), "hex");
48
60
  const { plaintext } = await localKms.decrypt(kmsKeyId, wrapped.toString("base64"), wrapContext(keyId));
49
- return (0, result_1.jsonResult)({
50
- provider,
51
- keyId,
52
- status: "unwrapped",
53
- dek: Buffer.from(plaintext).toString("hex"),
54
- });
61
+ const dek = Buffer.from(plaintext);
62
+ (0, crypto_lib_1.wipeMemory)(plaintext);
63
+ const keyHandle = keystore_1.keyStore.add((0, keystore_1.symmetricKey)(dek, "crypto_kms_unwrap"));
64
+ return (0, result_1.jsonResult)({ provider, keyId, status: "unwrapped", keyHandle });
55
65
  };
56
66
  exports.kmsUnwrap = kmsUnwrap;
57
67
  //# sourceMappingURL=kms.js.map
@@ -3,5 +3,4 @@ export type ToolArgs = Record<string, unknown>;
3
3
  export type ToolHandler = (args: ToolArgs) => Promise<MCPCallToolResult>;
4
4
  export declare function jsonResult(value: unknown): MCPCallToolResult;
5
5
  export declare function errorResult(text: string): MCPCallToolResult;
6
- export declare function parseKey256(value: unknown, label?: string): Buffer;
7
6
  //# sourceMappingURL=result.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"result.d.ts","sourceRoot":"","sources":["../../src/tools/result.ts"],"names":[],"mappings":"AAEA,OAAO,EAAE,iBAAiB,EAAE,MAAM,UAAU,CAAC;AAG7C,MAAM,MAAM,QAAQ,GAAG,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;AAG/C,MAAM,MAAM,WAAW,GAAG,CAAC,IAAI,EAAE,QAAQ,KAAK,OAAO,CAAC,iBAAiB,CAAC,CAAC;AAGzE,wBAAgB,UAAU,CAAC,KAAK,EAAE,OAAO,GAAG,iBAAiB,CAI5D;AAGD,wBAAgB,WAAW,CAAC,IAAI,EAAE,MAAM,GAAG,iBAAiB,CAE3D;AAOD,wBAAgB,WAAW,CAAC,KAAK,EAAE,OAAO,EAAE,KAAK,SAAQ,GAAG,MAAM,CAQjE"}
1
+ {"version":3,"file":"result.d.ts","sourceRoot":"","sources":["../../src/tools/result.ts"],"names":[],"mappings":"AAEA,OAAO,EAAE,iBAAiB,EAAE,MAAM,UAAU,CAAC;AAG7C,MAAM,MAAM,QAAQ,GAAG,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;AAG/C,MAAM,MAAM,WAAW,GAAG,CAAC,IAAI,EAAE,QAAQ,KAAK,OAAO,CAAC,iBAAiB,CAAC,CAAC;AAGzE,wBAAgB,UAAU,CAAC,KAAK,EAAE,OAAO,GAAG,iBAAiB,CAI5D;AAGD,wBAAgB,WAAW,CAAC,IAAI,EAAE,MAAM,GAAG,iBAAiB,CAE3D"}
@@ -2,7 +2,6 @@
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
3
  exports.jsonResult = jsonResult;
4
4
  exports.errorResult = errorResult;
5
- exports.parseKey256 = parseKey256;
6
5
  function jsonResult(value) {
7
6
  return {
8
7
  content: [{ type: "text", text: JSON.stringify(value, null, 2) }],
@@ -11,11 +10,4 @@ function jsonResult(value) {
11
10
  function errorResult(text) {
12
11
  return { isError: true, content: [{ type: "text", text }] };
13
12
  }
14
- function parseKey256(value, label = "key") {
15
- const hex = String(value ?? "");
16
- if (!/^[0-9a-fA-F]{64}$/.test(hex)) {
17
- throw new Error(`${label} must be a 256-bit key encoded as 64 hex characters`);
18
- }
19
- return Buffer.from(hex, "hex");
20
- }
21
13
  //# sourceMappingURL=result.js.map
@@ -0,0 +1,12 @@
1
+ import { MCPToolParameterProperty } from "../types";
2
+ export declare const MAX_TEXT_LENGTH = 1048576;
3
+ export declare const MAX_KEY_LENGTH = 16384;
4
+ export declare const HEX_BYTES = "^(?:[0-9a-fA-F]{2})*$";
5
+ export declare const X25519_PUBLIC_KEY_HEX = 64;
6
+ export declare const ML_KEM_768_PUBLIC_KEY_HEX = 2368;
7
+ export declare const HANDLE_NOTE = "Secret key material stays inside the server; only an opaque keyHandle is returned.";
8
+ export declare function keyHandle(description: string): MCPToolParameterProperty;
9
+ export declare function text(description: string): MCPToolParameterProperty;
10
+ export declare function hex(description: string, length: number, upTo?: boolean): MCPToolParameterProperty;
11
+ export declare function oneOf(description: string, values: string[]): MCPToolParameterProperty;
12
+ //# sourceMappingURL=schema-helpers.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"schema-helpers.d.ts","sourceRoot":"","sources":["../../src/tools/schema-helpers.ts"],"names":[],"mappings":"AAEA,OAAO,EAAE,wBAAwB,EAAE,MAAM,UAAU,CAAC;AAIpD,eAAO,MAAM,eAAe,UAAY,CAAC;AAGzC,eAAO,MAAM,cAAc,QAAS,CAAC;AAGrC,eAAO,MAAM,SAAS,0BAA0B,CAAC;AAGjD,eAAO,MAAM,qBAAqB,KAAK,CAAC;AAGxC,eAAO,MAAM,yBAAyB,OAAO,CAAC;AAG9C,eAAO,MAAM,WAAW,uFAC8D,CAAC;AAGvF,wBAAgB,SAAS,CAAC,WAAW,EAAE,MAAM,GAAG,wBAAwB,CAOvE;AAGD,wBAAgB,IAAI,CAAC,WAAW,EAAE,MAAM,GAAG,wBAAwB,CAElE;AAGD,wBAAgB,GAAG,CACjB,WAAW,EAAE,MAAM,EACnB,MAAM,EAAE,MAAM,EACd,IAAI,UAAQ,GACX,wBAAwB,CAQ1B;AAGD,wBAAgB,KAAK,CACnB,WAAW,EAAE,MAAM,EACnB,MAAM,EAAE,MAAM,EAAE,GACf,wBAAwB,CAE1B"}
@@ -0,0 +1,38 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.HANDLE_NOTE = exports.ML_KEM_768_PUBLIC_KEY_HEX = exports.X25519_PUBLIC_KEY_HEX = exports.HEX_BYTES = exports.MAX_KEY_LENGTH = exports.MAX_TEXT_LENGTH = void 0;
4
+ exports.keyHandle = keyHandle;
5
+ exports.text = text;
6
+ exports.hex = hex;
7
+ exports.oneOf = oneOf;
8
+ const keystore_1 = require("./keystore");
9
+ exports.MAX_TEXT_LENGTH = 1_048_576;
10
+ exports.MAX_KEY_LENGTH = 16_384;
11
+ exports.HEX_BYTES = "^(?:[0-9a-fA-F]{2})*$";
12
+ exports.X25519_PUBLIC_KEY_HEX = 64;
13
+ exports.ML_KEM_768_PUBLIC_KEY_HEX = 2368;
14
+ exports.HANDLE_NOTE = "Secret key material stays inside the server; only an opaque keyHandle is returned.";
15
+ function keyHandle(description) {
16
+ return {
17
+ type: "string",
18
+ description: `${description} Key handles look like 'kh_' followed by 32 hex characters.`,
19
+ maxLength: 35,
20
+ pattern: keystore_1.KEY_HANDLE_PATTERN,
21
+ };
22
+ }
23
+ function text(description) {
24
+ return { type: "string", description, maxLength: exports.MAX_TEXT_LENGTH };
25
+ }
26
+ function hex(description, length, upTo = false) {
27
+ return {
28
+ type: "string",
29
+ description,
30
+ minLength: upTo ? 2 : length,
31
+ maxLength: length,
32
+ pattern: exports.HEX_BYTES,
33
+ };
34
+ }
35
+ function oneOf(description, values) {
36
+ return { type: "string", description, enum: values };
37
+ }
38
+ //# sourceMappingURL=schema-helpers.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"signing.d.ts","sourceRoot":"","sources":["../../src/tools/signing.ts"],"names":[],"mappings":"AAIA,OAAO,EAAE,WAAW,EAAc,MAAM,UAAU,CAAC;AAGnD,eAAO,MAAM,IAAI,EAAE,WAwBlB,CAAC;AAiCF,eAAO,MAAM,MAAM,EAAE,WASpB,CAAC;AAGF,eAAO,MAAM,IAAI,EAAE,WAUlB,CAAC"}
1
+ {"version":3,"file":"signing.d.ts","sourceRoot":"","sources":["../../src/tools/signing.ts"],"names":[],"mappings":"AAUA,OAAO,EAAY,WAAW,EAAc,MAAM,UAAU,CAAC;AA6D7D,eAAO,MAAM,IAAI,EAAE,WAmBlB,CAAC;AAmFF,eAAO,MAAM,MAAM,EAAE,WAsBpB,CAAC;AAGF,eAAO,MAAM,IAAI,EAAE,WAUlB,CAAC"}
@@ -5,51 +5,137 @@ var __importDefault = (this && this.__importDefault) || function (mod) {
5
5
  Object.defineProperty(exports, "__esModule", { value: true });
6
6
  exports.hash = exports.verify = exports.sign = void 0;
7
7
  const node_crypto_1 = __importDefault(require("node:crypto"));
8
+ const crypto_lib_1 = require("@sebastienrousseau/crypto-lib");
8
9
  const definitions_1 = require("./definitions");
10
+ const keystore_1 = require("./keystore");
9
11
  const result_1 = require("./result");
12
+ const SIGNING_KINDS = [
13
+ "ed25519",
14
+ "rsa",
15
+ "ecc",
16
+ "ml-dsa-44",
17
+ "ml-dsa-65",
18
+ "ml-dsa-87",
19
+ "hmac-sha256",
20
+ ];
21
+ function ecdsaScheme(key) {
22
+ const curve = String(key.asymmetricKeyDetails?.namedCurve);
23
+ if (!definitions_1.EC_CURVES.includes(curve)) {
24
+ throw new Error(`Unsupported EC curve: ${curve}`);
25
+ }
26
+ const digest = curve === "secp384r1" ? "sha384" : "sha256";
27
+ return { algorithm: `ecdsa-${digest}`, digest, pss: false };
28
+ }
29
+ function schemeFor(key) {
30
+ switch (key.asymmetricKeyType) {
31
+ case "ed25519":
32
+ return { algorithm: "ed25519", digest: null, pss: false };
33
+ case "rsa":
34
+ return { algorithm: "rsa-pss", digest: "sha256", pss: true };
35
+ case "ec":
36
+ return ecdsaScheme(key);
37
+ default:
38
+ throw new Error(`Unsupported key type: ${key.asymmetricKeyType}`);
39
+ }
40
+ }
41
+ function keyInput(key, scheme) {
42
+ if (!scheme.pss)
43
+ return key;
44
+ return {
45
+ key,
46
+ padding: node_crypto_1.default.constants.RSA_PKCS1_PSS_PADDING,
47
+ saltLength: node_crypto_1.default.constants.RSA_PSS_SALTLEN_DIGEST,
48
+ };
49
+ }
50
+ function hmac(secret, data) {
51
+ return node_crypto_1.default.createHmac("sha256", secret).update(data).digest();
52
+ }
10
53
  const sign = async (args) => {
11
- const data = String(args.data);
12
- const algorithm = args.algorithm || "ed25519";
13
- const privateKey = String(args.privateKey);
14
- if (algorithm === "hmac-sha256") {
15
- const signature = node_crypto_1.default
16
- .createHmac("sha256", privateKey)
17
- .update(data)
18
- .digest("hex");
54
+ const key = keystore_1.keyStore.use(String(args.keyHandle), SIGNING_KINDS);
55
+ const data = Buffer.from(String(args.data), "utf8");
56
+ if (key.kind === "hmac-sha256") {
57
+ const signature = hmac(key.secret, data).toString("hex");
19
58
  return (0, result_1.jsonResult)({ algorithm: "hmac-sha256", signature });
20
59
  }
21
- if (algorithm === "ed25519") {
22
- const signature = node_crypto_1.default
23
- .sign(null, Buffer.from(data), privateKey)
24
- .toString("hex");
60
+ if (key.kind.startsWith("ml-dsa-")) {
61
+ const level = Number(key.kind.slice(7));
62
+ const secretKeyHex = key.secret.toString("hex");
63
+ const { signature, algorithm } = (0, crypto_lib_1.mlDsaSign)(level, secretKeyHex, data);
25
64
  return (0, result_1.jsonResult)({ algorithm, signature });
26
65
  }
27
- const signer = node_crypto_1.default.createSign("SHA256");
28
- signer.update(data);
29
- signer.end();
30
- return (0, result_1.jsonResult)({ algorithm, signature: signer.sign(privateKey, "hex") });
66
+ const privateKey = key.privateKey;
67
+ const scheme = schemeFor(privateKey);
68
+ const signature = node_crypto_1.default
69
+ .sign(scheme.digest, data, keyInput(privateKey, scheme))
70
+ .toString("hex");
71
+ return (0, result_1.jsonResult)({ algorithm: scheme.algorithm, signature });
31
72
  };
32
73
  exports.sign = sign;
33
- function verifySignature(algorithm, data, signature, publicKey) {
34
- if (algorithm === "hmac-sha256") {
35
- const expected = node_crypto_1.default
36
- .createHmac("sha256", publicKey)
37
- .update(data)
38
- .digest("hex");
39
- return node_crypto_1.default.timingSafeEqual(Buffer.from(signature, "hex"), Buffer.from(expected, "hex"));
74
+ function verifyAsymmetric(publicKey, data, signature) {
75
+ const scheme = schemeFor(publicKey);
76
+ const valid = node_crypto_1.default.verify(scheme.digest, data, keyInput(publicKey, scheme), signature);
77
+ return { algorithm: scheme.algorithm, valid };
78
+ }
79
+ function verifyHmac(secret, data, signature) {
80
+ const expected = hmac(secret, data);
81
+ const valid = signature.length === expected.length &&
82
+ node_crypto_1.default.timingSafeEqual(signature, expected);
83
+ return { algorithm: "hmac-sha256", valid };
84
+ }
85
+ const ML_DSA_LEVELS_BY_LEN = {
86
+ 2624: 44,
87
+ 3904: 65,
88
+ 5184: 87,
89
+ };
90
+ function verifierFromPublic(raw) {
91
+ const pub = raw.trim();
92
+ if (pub.includes("PRIVATE KEY")) {
93
+ throw new Error("publicKey must be a public key, not a private key");
94
+ }
95
+ const mlDsaLevel = /^[0-9a-fA-F]+$/.test(pub)
96
+ ? ML_DSA_LEVELS_BY_LEN[pub.length]
97
+ : undefined;
98
+ if (mlDsaLevel) {
99
+ return { kind: "ml-dsa", level: mlDsaLevel, publicKeyHex: pub };
100
+ }
101
+ return { kind: "asymmetric", publicKey: node_crypto_1.default.createPublicKey(pub) };
102
+ }
103
+ function verifierFromKey(handle) {
104
+ const key = keystore_1.keyStore.use(handle, SIGNING_KINDS);
105
+ if (key.kind === "hmac-sha256") {
106
+ return { kind: "hmac", secret: key.secret };
107
+ }
108
+ if (key.kind.startsWith("ml-dsa-")) {
109
+ const level = Number(key.kind.slice(7));
110
+ return { kind: "ml-dsa", level, publicKeyHex: String(key.info.publicKey) };
40
111
  }
41
- if (algorithm === "ed25519") {
42
- return node_crypto_1.default.verify(null, Buffer.from(data), publicKey, Buffer.from(signature, "hex"));
112
+ return { kind: "asymmetric", publicKey: key.publicKey };
113
+ }
114
+ function verifierFor(args) {
115
+ const hasPub = args.publicKey !== undefined;
116
+ if (hasPub === (args.keyHandle !== undefined)) {
117
+ throw new Error("Pass exactly one of publicKey or keyHandle");
43
118
  }
44
- const verifier = node_crypto_1.default.createVerify("SHA256");
45
- verifier.update(data);
46
- verifier.end();
47
- return verifier.verify(publicKey, Buffer.from(signature, "hex"));
119
+ return hasPub
120
+ ? verifierFromPublic(String(args.publicKey))
121
+ : verifierFromKey(String(args.keyHandle));
48
122
  }
49
123
  const verify = async (args) => {
50
- const algorithm = args.algorithm || "ed25519";
51
- const valid = verifySignature(algorithm, String(args.data), String(args.signature), String(args.publicKey));
52
- return (0, result_1.jsonResult)({ algorithm, valid });
124
+ const verifier = verifierFor(args);
125
+ const data = Buffer.from(String(args.data), "utf8");
126
+ const signatureHex = String(args.signature);
127
+ if (verifier.kind === "ml-dsa") {
128
+ const res = (0, crypto_lib_1.mlDsaVerify)(verifier.level, verifier.publicKeyHex, data, signatureHex);
129
+ return (0, result_1.jsonResult)({
130
+ algorithm: `ml-dsa-${verifier.level}`,
131
+ valid: res.valid,
132
+ });
133
+ }
134
+ const signature = Buffer.from(signatureHex, "hex");
135
+ const result = verifier.kind === "hmac"
136
+ ? verifyHmac(verifier.secret, data, signature)
137
+ : verifyAsymmetric(verifier.publicKey, data, signature);
138
+ return (0, result_1.jsonResult)(result);
53
139
  };
54
140
  exports.verify = verify;
55
141
  const hash = async (args) => {
@@ -0,0 +1,3 @@
1
+ import { MCPTool } from "../types";
2
+ export declare const STREAM_TOOLS: MCPTool[];
3
+ //# sourceMappingURL=stream-definitions.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"stream-definitions.d.ts","sourceRoot":"","sources":["../../src/tools/stream-definitions.ts"],"names":[],"mappings":"AAEA,OAAO,EAAE,OAAO,EAAE,MAAM,UAAU,CAAC;AASnC,eAAO,MAAM,YAAY,EAAE,OAAO,EA0JjC,CAAC"}
@@ -0,0 +1,122 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.STREAM_TOOLS = void 0;
4
+ const schema_helpers_1 = require("./schema-helpers");
5
+ exports.STREAM_TOOLS = [
6
+ {
7
+ name: "crypto_stream_encrypt",
8
+ description: "Encrypt plaintext using post-quantum hybrid STREAM AEAD (X25519 + ML-KEM-768 + XChaCha20-Poly1305) with anti-truncation protection. Returns base64 ciphertext.",
9
+ inputSchema: {
10
+ type: "object",
11
+ additionalProperties: false,
12
+ properties: {
13
+ plaintext: (0, schema_helpers_1.text)("Plaintext string to encrypt."),
14
+ x25519PublicKey: (0, schema_helpers_1.hex)("Hex-encoded X25519 public key (64 hex characters).", schema_helpers_1.X25519_PUBLIC_KEY_HEX),
15
+ mlKemPublicKey: (0, schema_helpers_1.hex)("Hex-encoded ML-KEM-768 public key (2368 hex characters).", schema_helpers_1.ML_KEM_768_PUBLIC_KEY_HEX),
16
+ chunkSize: {
17
+ type: "integer",
18
+ description: "Chunk size in bytes (minimum 64, default 65536).",
19
+ minimum: 64,
20
+ },
21
+ },
22
+ required: ["plaintext", "x25519PublicKey", "mlKemPublicKey"],
23
+ },
24
+ },
25
+ {
26
+ name: "crypto_stream_decrypt",
27
+ description: "Decrypt a post-quantum hybrid STREAM AEAD ciphertext using server-held x25519 and ml-kem-768 key handles. Plaintext memory is wiped after decoding.",
28
+ inputSchema: {
29
+ type: "object",
30
+ additionalProperties: false,
31
+ properties: {
32
+ ciphertext: (0, schema_helpers_1.text)("Base64-encoded STREAM ciphertext."),
33
+ x25519KeyHandle: (0, schema_helpers_1.keyHandle)("Handle of the recipient X25519 key (or 32-byte symmetric-256 key)."),
34
+ mlKemKeyHandle: (0, schema_helpers_1.keyHandle)("Handle of the recipient ML-KEM-768 key."),
35
+ chunkSize: {
36
+ type: "integer",
37
+ description: "Chunk size used during encryption (default 65536).",
38
+ minimum: 64,
39
+ },
40
+ },
41
+ required: ["ciphertext", "x25519KeyHandle", "mlKemKeyHandle"],
42
+ },
43
+ },
44
+ {
45
+ name: "crypto_stream_multi_encrypt",
46
+ description: "Encrypt plaintext for multiple recipients using post-quantum hybrid STREAM AEAD (X25519 + ML-KEM-768 + XChaCha20-Poly1305) with anti-truncation framing. Returns base64 ciphertext.",
47
+ inputSchema: {
48
+ type: "object",
49
+ additionalProperties: false,
50
+ properties: {
51
+ plaintext: (0, schema_helpers_1.text)("Plaintext string to encrypt."),
52
+ recipients: (0, schema_helpers_1.text)("JSON-serialized array of recipient public key descriptors: [{ recipientId, recipientX25519Public, recipientMlKemPublic }]."),
53
+ chunkSize: {
54
+ type: "integer",
55
+ description: "Chunk size in bytes (minimum 1024, maximum 16777216, default 65536).",
56
+ minimum: 1024,
57
+ maximum: 16777216,
58
+ },
59
+ },
60
+ required: ["plaintext", "recipients"],
61
+ },
62
+ },
63
+ {
64
+ name: "crypto_stream_multi_decrypt",
65
+ description: "Decrypt a multi-recipient post-quantum hybrid STREAM AEAD ciphertext using server-held x25519 and ml-kem-768 key handles. Plaintext memory is wiped after decoding.",
66
+ inputSchema: {
67
+ type: "object",
68
+ additionalProperties: false,
69
+ properties: {
70
+ ciphertext: (0, schema_helpers_1.text)("Base64-encoded multi-recipient STREAM ciphertext."),
71
+ x25519KeyHandle: (0, schema_helpers_1.keyHandle)("Handle of the recipient X25519 key (or 32-byte symmetric-256 key)."),
72
+ mlKemKeyHandle: (0, schema_helpers_1.keyHandle)("Handle of the recipient ML-KEM-768 key."),
73
+ recipientId: (0, schema_helpers_1.text)("Optional recipient identifier for direct slot lookup."),
74
+ chunkSize: {
75
+ type: "integer",
76
+ description: "Chunk size in bytes (minimum 1024, maximum 16777216, default 65536).",
77
+ minimum: 1024,
78
+ maximum: 16777216,
79
+ },
80
+ },
81
+ required: ["ciphertext", "x25519KeyHandle", "mlKemKeyHandle"],
82
+ },
83
+ },
84
+ {
85
+ name: "crypto_hybrid_kem_multi_encapsulate",
86
+ description: "Encapsulate a symmetric key for multiple recipients using post-quantum hybrid KEM (X25519 + ML-KEM-768 + AES-256-GCM key wrapping). Returns recipient ciphertexts and a symmetric-256 keyHandle.",
87
+ inputSchema: {
88
+ type: "object",
89
+ additionalProperties: false,
90
+ properties: {
91
+ recipients: (0, schema_helpers_1.text)("JSON-serialized array of recipient public key objects: [{ id?, x25519PublicKey, mlKemPublicKey }]."),
92
+ keyHandle: (0, schema_helpers_1.keyHandle)("Optional handle of an existing symmetric-256 key to encapsulate. If omitted, a fresh 256-bit DEK is generated."),
93
+ },
94
+ required: ["recipients"],
95
+ },
96
+ },
97
+ {
98
+ name: "crypto_hybrid_kem_multi_decapsulate",
99
+ description: "Decapsulate a multi-recipient post-quantum hybrid KEM key using server-held x25519 and ml-kem-768 key handles. Returns a symmetric-256 keyHandle for the recovered DEK.",
100
+ inputSchema: {
101
+ type: "object",
102
+ additionalProperties: false,
103
+ properties: {
104
+ x25519KeyHandle: (0, schema_helpers_1.keyHandle)("Handle of recipient X25519 key."),
105
+ mlKemKeyHandle: (0, schema_helpers_1.keyHandle)("Handle of recipient ML-KEM-768 key."),
106
+ ephemeralPublicKey: (0, schema_helpers_1.hex)("Hex-encoded sender ephemeral X25519 public key (64 hex characters).", schema_helpers_1.X25519_PUBLIC_KEY_HEX),
107
+ mlKemCiphertext: (0, schema_helpers_1.hex)("Hex-encoded ML-KEM-768 ciphertext (2176 hex characters).", 2176, true),
108
+ wrappedKey: (0, schema_helpers_1.text)("Base64-encoded wrapped DEK (RFC 3394 AES Key Wrap)."),
109
+ encryptedKey: (0, schema_helpers_1.text)("Alias for wrappedKey."),
110
+ recipients: (0, schema_helpers_1.text)("Optional JSON-serialized array of encrypted recipient objects: [{ id?, ephemeralPublicKey, mlKemCiphertext, encryptedKey }]."),
111
+ recipientId: (0, schema_helpers_1.text)("Optional recipient ID to select from recipients array."),
112
+ recipientIndex: {
113
+ type: "integer",
114
+ description: "Optional index to select from recipients array (default 0).",
115
+ minimum: 0,
116
+ },
117
+ },
118
+ required: ["x25519KeyHandle", "mlKemKeyHandle"],
119
+ },
120
+ },
121
+ ];
122
+ //# sourceMappingURL=stream-definitions.js.map
@@ -0,0 +1,6 @@
1
+ import { ToolHandler } from "./result";
2
+ export declare const streamEncrypt: ToolHandler;
3
+ export declare const streamDecrypt: ToolHandler;
4
+ export declare const streamMultiEncrypt: ToolHandler;
5
+ export declare const streamMultiDecrypt: ToolHandler;
6
+ //# sourceMappingURL=stream.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"stream.d.ts","sourceRoot":"","sources":["../../src/tools/stream.ts"],"names":[],"mappings":"AAWA,OAAO,EAAE,WAAW,EAAc,MAAM,UAAU,CAAC;AAMnD,eAAO,MAAM,aAAa,EAAE,WAmB3B,CAAC;AAOF,eAAO,MAAM,aAAa,EAAE,WAwB3B,CAAC;AAiBF,eAAO,MAAM,kBAAkB,EAAE,WAkBhC,CAAC;AAOF,eAAO,MAAM,kBAAkB,EAAE,WA4BhC,CAAC"}