@quantsafe/mcp 0.0.0-stage → 0.1.0

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.
@@ -0,0 +1,60 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.EVM_ADDRESS_RE = exports.QSAFE_DERIVATION_MESSAGE = void 0;
4
+ exports.canonicalRecoveryByte = canonicalRecoveryByte;
5
+ exports.recoverPersonalSigner = recoverPersonalSigner;
6
+ const secp256k1_1 = require("@noble/curves/secp256k1");
7
+ const sha3_1 = require("@noble/hashes/sha3");
8
+ const utils_1 = require("@noble/hashes/utils");
9
+ // Byte for byte the message the browser asks a wallet to sign (QSAFE_SIGN_MESSAGE in
10
+ // src/lib/wallet/keypair-context.tsx). That module loads React and wagmi, which the stdio server
11
+ // must not, so the text lives here too and a unit test holds the two equal. Any other text
12
+ // derives a different identity, one no browser session can reproduce.
13
+ exports.QSAFE_DERIVATION_MESSAGE = 'QuantSafe keypair derivation v1\n' +
14
+ '\n' +
15
+ 'This signature derives your personal post-quantum encryption keypair.\n' +
16
+ 'It grants no permissions and costs no gas.\n' +
17
+ '\n' +
18
+ 'Domain: quantsafe.tech';
19
+ exports.EVM_ADDRESS_RE = /^0x[0-9a-fA-F]{40}$/;
20
+ function personalSignDigest(message) {
21
+ const body = (0, utils_1.utf8ToBytes)(message);
22
+ return (0, sha3_1.keccak_256)((0, utils_1.concatBytes)((0, utils_1.utf8ToBytes)(`\x19Ethereum Signed Message:\n${body.length}`), body));
23
+ }
24
+ /**
25
+ * The signature lowercase and 0x-prefixed, its 65 bytes untouched. The browser derives keys from
26
+ * the bytes exactly as the wallet returned them, recovery byte included, and has done so since
27
+ * v1; rewriting v here (0/1 to 27/28) would give a wallet that returns 0/1 one identity in the
28
+ * browser and another over MCP, and changing the browser instead would move every such user's
29
+ * keys. So the bytes stay as signed, and recoverPersonalSigner refuses any v other than 0, 1, 27
30
+ * or 28.
31
+ */
32
+ function canonicalRecoveryByte(signatureHex) {
33
+ return `0x${(signatureHex.startsWith('0x') ? signatureHex.slice(2) : signatureHex).toLowerCase()}`;
34
+ }
35
+ /**
36
+ * The lowercase address whose key produced this EIP-191 (personal_sign) signature over message,
37
+ * or null when the 65 bytes are not a recoverable signature. Both recovery byte forms recover,
38
+ * and keys are derived from canonicalRecoveryByte(signature), the same bytes the browser uses. A high-s
39
+ * signature is refused even though it recovers: wallets only emit low-s, so it is a rewritten
40
+ * copy whose bytes would derive keys the wallet itself never reproduces.
41
+ */
42
+ function recoverPersonalSigner(message, signatureHex) {
43
+ const raw = signatureHex.startsWith('0x') ? signatureHex.slice(2) : signatureHex;
44
+ if (!/^[0-9a-fA-F]{130}$/.test(raw))
45
+ return null;
46
+ const v = Number.parseInt(raw.slice(128), 16);
47
+ const recovery = v >= 27 ? v - 27 : v;
48
+ if (recovery !== 0 && recovery !== 1)
49
+ return null;
50
+ try {
51
+ const sig = secp256k1_1.secp256k1.Signature.fromCompact(raw.slice(0, 128)).addRecoveryBit(recovery);
52
+ if (sig.hasHighS())
53
+ return null;
54
+ const publicKey = sig.recoverPublicKey(personalSignDigest(message)).toRawBytes(false);
55
+ return '0x' + (0, utils_1.bytesToHex)((0, sha3_1.keccak_256)(publicKey.subarray(1)).subarray(-20));
56
+ }
57
+ catch {
58
+ return null;
59
+ }
60
+ }
package/package.json CHANGED
@@ -1,6 +1,36 @@
1
1
  {
2
2
  "name": "@quantsafe/mcp",
3
- "version": "0.0.0-stage",
4
- "stub": true,
5
- "description": "Temporary package placeholder for staged publishing"
6
- }
3
+ "version": "0.1.0",
4
+ "description": "Post-quantum encryption for AI agents. MCP server with ML-KEM-768 + X25519 hybrid encryption and ML-DSA-65 signatures.",
5
+ "keywords": [
6
+ "mcp",
7
+ "model-context-protocol",
8
+ "post-quantum",
9
+ "ml-kem",
10
+ "ml-dsa",
11
+ "encryption",
12
+ "x402",
13
+ "quantsafe"
14
+ ],
15
+ "homepage": "https://quantsafe.tech/mcp",
16
+ "license": "MIT",
17
+ "bin": {
18
+ "qsafe-mcp": "dist/mcp/server.js"
19
+ },
20
+ "files": [
21
+ "dist"
22
+ ],
23
+ "engines": {
24
+ "node": ">=20"
25
+ },
26
+ "publishConfig": {
27
+ "access": "public"
28
+ },
29
+ "dependencies": {
30
+ "@modelcontextprotocol/sdk": "^1.32.1",
31
+ "@noble/curves": "^1.8.1",
32
+ "@noble/hashes": "^1.7.1",
33
+ "@noble/post-quantum": "^0.4.0",
34
+ "zod": "^4.4.3"
35
+ }
36
+ }