@kya-os/mcp-i 0.1.0-alpha.2.2 → 0.1.0-alpha.2.4
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 +258 -58
- package/dist/auto.d.ts +0 -12
- package/dist/auto.js +3 -14
- package/dist/crypto.d.ts +10 -26
- package/dist/crypto.js +117 -37
- package/dist/dev-helper.d.ts +3 -0
- package/dist/dev-helper.js +54 -0
- package/dist/encrypted-storage.d.ts +11 -0
- package/dist/encrypted-storage.js +73 -0
- package/dist/index.d.ts +33 -48
- package/dist/index.js +267 -191
- package/dist/logger.d.ts +32 -0
- package/dist/logger.js +66 -0
- package/dist/registry/index.d.ts +12 -0
- package/dist/registry/index.js +56 -0
- package/dist/registry/knowthat.d.ts +13 -0
- package/dist/registry/knowthat.js +88 -0
- package/dist/rotation.d.ts +35 -0
- package/dist/rotation.js +102 -0
- package/dist/storage.d.ts +41 -0
- package/dist/storage.js +163 -0
- package/dist/transport.d.ts +34 -0
- package/dist/transport.js +207 -0
- package/dist/types.d.ts +80 -17
- package/dist/types.js +0 -4
- package/package.json +36 -8
- package/dist/__tests__/challenge-response.test.d.ts +0 -5
- package/dist/__tests__/challenge-response.test.d.ts.map +0 -1
- package/dist/__tests__/challenge-response.test.js +0 -218
- package/dist/__tests__/challenge-response.test.js.map +0 -1
- package/dist/__tests__/crypto.test.d.ts +0 -5
- package/dist/__tests__/crypto.test.d.ts.map +0 -1
- package/dist/__tests__/crypto.test.js +0 -153
- package/dist/__tests__/crypto.test.js.map +0 -1
- package/dist/auto-enhance.d.ts +0 -41
- package/dist/auto-enhance.d.ts.map +0 -1
- package/dist/auto-enhance.js +0 -193
- package/dist/auto-enhance.js.map +0 -1
- package/dist/auto-init.d.ts +0 -12
- package/dist/auto-init.d.ts.map +0 -1
- package/dist/auto-init.js +0 -166
- package/dist/auto-init.js.map +0 -1
- package/dist/auto.d.ts.map +0 -1
- package/dist/auto.js.map +0 -1
- package/dist/crypto.d.ts.map +0 -1
- package/dist/crypto.js.map +0 -1
- package/dist/index.d.ts.map +0 -1
- package/dist/index.js.map +0 -1
- package/dist/patch.d.ts +0 -22
- package/dist/patch.d.ts.map +0 -1
- package/dist/patch.js +0 -164
- package/dist/patch.js.map +0 -1
- package/dist/transparent.d.ts +0 -40
- package/dist/transparent.d.ts.map +0 -1
- package/dist/transparent.js +0 -167
- package/dist/transparent.js.map +0 -1
- package/dist/types.d.ts.map +0 -1
- package/dist/types.js.map +0 -1
package/README.md
CHANGED
|
@@ -1,10 +1,34 @@
|
|
|
1
1
|
# @kya-os/mcp-i
|
|
2
2
|
|
|
3
|
-
|
|
3
|
+
The SEO package for AI agents. Register your MCP server and get automatic directory listings in 2 lines of code.
|
|
4
4
|
|
|
5
|
-
|
|
5
|
+
[](https://www.npmjs.com/package/@kya-os/mcp-i)
|
|
6
|
+
[](https://opensource.org/licenses/MIT)
|
|
7
|
+
[](https://modelcontextprotocol-identity.io)
|
|
6
8
|
|
|
7
|
-
|
|
9
|
+
## Why Your MCP Server Needs This
|
|
10
|
+
|
|
11
|
+
**For MCP Server Developers:**
|
|
12
|
+
|
|
13
|
+
- **Get a DID** - Your agent gets a permanent, cryptographic identity from knowthat.ai
|
|
14
|
+
- **Automatic Directory Listings** - Submit to multiple directories with zero extra work
|
|
15
|
+
- **Build Reputation** - Every interaction is signed and verifiable
|
|
16
|
+
- **Future-Proof** - Ready for the decentralized agent ecosystem
|
|
17
|
+
- **Production-Ready** - Optimized for Lambda, Edge, Next.js, and traditional deployments
|
|
18
|
+
|
|
19
|
+
**For Directory Maintainers:**
|
|
20
|
+
|
|
21
|
+
- **Easy Integration** - List MCP-I compliant agents automatically
|
|
22
|
+
- **Verified Agents** - Only list agents with cryptographic proof
|
|
23
|
+
- **Join the Network** - Tap into the growing MCP-I ecosystem
|
|
24
|
+
|
|
25
|
+
## How It Works
|
|
26
|
+
|
|
27
|
+
1. **Identity Registration**: Your agent is registered with knowthat.ai (the MCP-I registry)
|
|
28
|
+
2. **DID Generation**: You get a `did:web:knowthat.ai:agents:your-agent` identifier
|
|
29
|
+
3. **Directory Submission**: Based on your preferences, knowthat.ai submits your agent to directories
|
|
30
|
+
4. **Cryptographic Signing**: All agent responses are signed with your private key
|
|
31
|
+
5. **Verification**: Anyone can verify your agent's identity and authenticity
|
|
8
32
|
|
|
9
33
|
## Installation
|
|
10
34
|
|
|
@@ -12,86 +36,262 @@ Make MCP-I adoption a no-brainer for the hundreds of thousands of MCP servers. Z
|
|
|
12
36
|
npm install @kya-os/mcp-i
|
|
13
37
|
```
|
|
14
38
|
|
|
15
|
-
##
|
|
39
|
+
## Quick Start
|
|
16
40
|
|
|
17
|
-
###
|
|
41
|
+
### 1. Zero Configuration (Recommended)
|
|
18
42
|
|
|
19
43
|
```typescript
|
|
20
|
-
import "@kya-os/mcp-i/auto";
|
|
44
|
+
import "@kya-os/mcp-i/auto";
|
|
45
|
+
// That's it! Your server now has cryptographic identity
|
|
21
46
|
```
|
|
22
47
|
|
|
23
|
-
###
|
|
48
|
+
### 2. Production Configuration
|
|
24
49
|
|
|
25
50
|
```typescript
|
|
26
51
|
import { enableMCPIdentity } from "@kya-os/mcp-i";
|
|
27
52
|
|
|
53
|
+
const identity = await enableMCPIdentity({
|
|
54
|
+
name: "Production Agent",
|
|
55
|
+
|
|
56
|
+
// Auto-detect runtime (Lambda, Edge, Node.js)
|
|
57
|
+
storage: "auto",
|
|
58
|
+
transport: "auto",
|
|
59
|
+
|
|
60
|
+
// Encrypt private keys at rest
|
|
61
|
+
encryptionPassword: process.env.AGENT_KEY_PASSWORD,
|
|
62
|
+
|
|
63
|
+
// Professional logging
|
|
64
|
+
logLevel: "error", // or 'silent' for production
|
|
65
|
+
|
|
66
|
+
// Directory preferences (optional)
|
|
67
|
+
directories: "verified", // List on all verified directories
|
|
68
|
+
// OR directories: ["smithery", "glama"] // Specific directories
|
|
69
|
+
// OR directories: "none" // No directory listings
|
|
70
|
+
});
|
|
71
|
+
|
|
72
|
+
// Enable automatic key rotation
|
|
73
|
+
await identity.enableAutoRotation({
|
|
74
|
+
maxAge: 90 * 24 * 60 * 60 * 1000, // 90 days
|
|
75
|
+
maxSignatures: 1_000_000, // 1M signatures
|
|
76
|
+
});
|
|
77
|
+
```
|
|
78
|
+
|
|
79
|
+
### 3. Lambda/Edge Runtime
|
|
80
|
+
|
|
81
|
+
```typescript
|
|
82
|
+
// Automatic configuration for serverless
|
|
83
|
+
const identity = await enableMCPIdentity({
|
|
84
|
+
name: "Serverless Agent",
|
|
85
|
+
storage: "memory", // No file system needed
|
|
86
|
+
transport: "fetch", // Native fetch for edge
|
|
87
|
+
logLevel: "silent", // No console output
|
|
88
|
+
});
|
|
89
|
+
```
|
|
90
|
+
|
|
91
|
+
## Production Features
|
|
92
|
+
|
|
93
|
+
### Performance Optimizations
|
|
94
|
+
|
|
95
|
+
- **Lazy Loading**: Crypto libraries load only when needed
|
|
96
|
+
- **Signature Caching**: Repeated signatures are 10x faster
|
|
97
|
+
- **Precomputed Values**: DIDs and keys cached in memory
|
|
98
|
+
- **Optimized Transport**: Auto-selects axios vs fetch
|
|
99
|
+
|
|
100
|
+
### Security Features
|
|
101
|
+
|
|
102
|
+
- **Key Encryption**: Private keys encrypted with AES-256-GCM
|
|
103
|
+
- **Key Rotation**: Automatic rotation based on age/usage
|
|
104
|
+
- **Nonce Tracking**: Prevents replay attacks
|
|
105
|
+
- **Timestamp Validation**: Configurable tolerance windows
|
|
106
|
+
|
|
107
|
+
### Runtime Support
|
|
108
|
+
|
|
109
|
+
- **AWS Lambda**: Automatic memory storage
|
|
110
|
+
- **Vercel Edge**: Native fetch transport
|
|
111
|
+
- **Cloudflare Workers**: Full compatibility
|
|
112
|
+
- **Node.js**: Traditional file storage
|
|
113
|
+
|
|
114
|
+
### Directory Listings
|
|
115
|
+
|
|
116
|
+
```typescript
|
|
117
|
+
// Configure directory listings
|
|
28
118
|
await enableMCPIdentity({
|
|
29
|
-
name: "
|
|
30
|
-
|
|
31
|
-
|
|
119
|
+
name: "My Agent",
|
|
120
|
+
|
|
121
|
+
// Option 1: List on all verified directories
|
|
122
|
+
directories: "verified",
|
|
123
|
+
|
|
124
|
+
// Option 2: List on specific directories
|
|
125
|
+
directories: ["smithery", "glama"],
|
|
126
|
+
|
|
127
|
+
// Option 3: No directory listings (registry only)
|
|
128
|
+
directories: "none",
|
|
32
129
|
});
|
|
130
|
+
|
|
131
|
+
// Directory preferences are sent to knowthat.ai
|
|
132
|
+
// The registry handles submissions to your chosen directories
|
|
33
133
|
```
|
|
34
134
|
|
|
35
|
-
##
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
}],
|
|
51
|
-
"_mcp_identity": {
|
|
52
|
-
"did": "did:web:knowthat.ai:agents:calendar-booker",
|
|
53
|
-
"signature": "0x3045...",
|
|
54
|
-
"timestamp": "2025-01-31T10:00:00Z",
|
|
55
|
-
"conformanceLevel": 2
|
|
56
|
-
}
|
|
135
|
+
## Advanced Usage
|
|
136
|
+
|
|
137
|
+
### Key Rotation
|
|
138
|
+
|
|
139
|
+
```typescript
|
|
140
|
+
// Check key health
|
|
141
|
+
const health = identity.checkKeyHealth();
|
|
142
|
+
console.log(`Key age: ${health.age}ms`);
|
|
143
|
+
console.log(`Signatures: ${health.signatureCount}`);
|
|
144
|
+
console.log(`Should rotate: ${health.shouldRotate}`);
|
|
145
|
+
|
|
146
|
+
// Manual rotation
|
|
147
|
+
const result = await identity.rotateKeys("security-policy");
|
|
148
|
+
if (result.success) {
|
|
149
|
+
console.log(`Grace period ends: ${result.gracePeriodEnd}`);
|
|
57
150
|
}
|
|
151
|
+
|
|
152
|
+
// Automatic rotation
|
|
153
|
+
await identity.enableAutoRotation({
|
|
154
|
+
maxAge: 30 * 24 * 60 * 60 * 1000, // 30 days
|
|
155
|
+
maxSignatures: 500_000, // 500k signatures
|
|
156
|
+
});
|
|
58
157
|
```
|
|
59
158
|
|
|
60
|
-
|
|
159
|
+
### Edit/Claim URLs
|
|
61
160
|
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
- 📊 **Analytics & Reputation** tracking
|
|
66
|
-
- 🛡️ **Protection Against Impersonation**
|
|
161
|
+
```typescript
|
|
162
|
+
// Get signed URLs for editing
|
|
163
|
+
const { editUrl, claimUrl } = await identity.requestEditAccess();
|
|
67
164
|
|
|
68
|
-
|
|
165
|
+
// Edit URL - for existing agents
|
|
166
|
+
console.log("Edit your agent:", editUrl);
|
|
69
167
|
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
3. Advertises capabilities in server info
|
|
74
|
-
4. Works with all transports (STDIO, SSE, HTTP)
|
|
168
|
+
// Claim URL - for draft/unclaimed agents
|
|
169
|
+
console.log("Claim your agent:", claimUrl);
|
|
170
|
+
```
|
|
75
171
|
|
|
76
|
-
|
|
172
|
+
### Custom Storage
|
|
77
173
|
|
|
78
|
-
|
|
174
|
+
```typescript
|
|
175
|
+
// Encrypted file storage
|
|
176
|
+
await enableMCPIdentity({
|
|
177
|
+
storage: "file",
|
|
178
|
+
persistencePath: "/secure/location/.identity",
|
|
179
|
+
encryptionPassword: "strong-password",
|
|
180
|
+
});
|
|
181
|
+
|
|
182
|
+
// Memory storage with custom key
|
|
183
|
+
await enableMCPIdentity({
|
|
184
|
+
storage: "memory",
|
|
185
|
+
memoryKey: "agent-123", // Useful for multiple agents
|
|
186
|
+
});
|
|
187
|
+
```
|
|
188
|
+
|
|
189
|
+
### Custom Logger
|
|
190
|
+
|
|
191
|
+
```typescript
|
|
192
|
+
await enableMCPIdentity({
|
|
193
|
+
logger: {
|
|
194
|
+
debug: (msg, ...args) => myLogger.debug(msg, args),
|
|
195
|
+
info: (msg, ...args) => myLogger.info(msg, args),
|
|
196
|
+
warn: (msg, ...args) => myLogger.warn(msg, args),
|
|
197
|
+
error: (msg, ...args) => myLogger.error(msg, args),
|
|
198
|
+
},
|
|
199
|
+
});
|
|
200
|
+
```
|
|
201
|
+
|
|
202
|
+
## API Reference
|
|
203
|
+
|
|
204
|
+
### `enableMCPIdentity(options?)`
|
|
205
|
+
|
|
206
|
+
Main function to enable identity for your MCP server.
|
|
207
|
+
|
|
208
|
+
**Options:**
|
|
209
|
+
|
|
210
|
+
```typescript
|
|
211
|
+
interface MCPIdentityOptions {
|
|
212
|
+
// Basic info
|
|
213
|
+
name?: string;
|
|
214
|
+
description?: string;
|
|
215
|
+
repository?: string;
|
|
216
|
+
|
|
217
|
+
// Storage
|
|
218
|
+
storage?: "file" | "memory" | "auto";
|
|
219
|
+
persistencePath?: string;
|
|
220
|
+
memoryKey?: string;
|
|
221
|
+
encryptionPassword?: string;
|
|
222
|
+
|
|
223
|
+
// Transport
|
|
224
|
+
transport?: "axios" | "fetch" | "auto";
|
|
225
|
+
|
|
226
|
+
// Security
|
|
227
|
+
timestampTolerance?: number; // Default: 60000ms
|
|
228
|
+
enableNonceTracking?: boolean; // Default: true
|
|
229
|
+
|
|
230
|
+
// Directory listings
|
|
231
|
+
directories?: string[] | "verified" | "none"; // Default: "verified"
|
|
232
|
+
|
|
233
|
+
// Development
|
|
234
|
+
mode?: "development" | "production";
|
|
235
|
+
|
|
236
|
+
// Logging
|
|
237
|
+
logger?: Logger;
|
|
238
|
+
logLevel?: "debug" | "info" | "warn" | "error" | "silent";
|
|
239
|
+
}
|
|
240
|
+
```
|
|
241
|
+
|
|
242
|
+
### `MCPIdentity` Methods
|
|
243
|
+
|
|
244
|
+
- `sign(message)`: Sign with caching
|
|
245
|
+
- `verify(message, signature, publicKey?)`: Verify signatures
|
|
246
|
+
- `respondToChallenge(challenge)`: MCP-I authentication
|
|
247
|
+
- `signResponse(response)`: Add identity to responses
|
|
248
|
+
- `requestEditAccess()`: Get edit/claim URLs
|
|
249
|
+
- `rotateKeys(reason?)`: Manual key rotation
|
|
250
|
+
- `enableAutoRotation(policy?)`: Automatic rotation
|
|
251
|
+
- `checkKeyHealth()`: Key rotation status
|
|
252
|
+
|
|
253
|
+
## Files Created
|
|
79
254
|
|
|
80
|
-
```bash
|
|
81
|
-
AGENT_DID="did:web:knowthat.ai:agents:your-agent"
|
|
82
|
-
AGENT_PUBLIC_KEY="base64-encoded-public-key"
|
|
83
|
-
AGENT_PRIVATE_KEY="base64-encoded-private-key"
|
|
84
|
-
AGENT_ID="uuid"
|
|
85
|
-
AGENT_SLUG="your-agent-slug"
|
|
86
255
|
```
|
|
256
|
+
.mcp-identity.json # Your agent's identity (encrypted if password set)
|
|
257
|
+
```
|
|
258
|
+
|
|
259
|
+
## Security Best Practices
|
|
260
|
+
|
|
261
|
+
1. **Use encryption in production**: Always set `encryptionPassword`
|
|
262
|
+
2. **Enable key rotation**: Set up automatic rotation policies
|
|
263
|
+
3. **Secure storage**: Use appropriate file permissions
|
|
264
|
+
4. **Monitor key health**: Check rotation status regularly
|
|
265
|
+
5. **Add to .gitignore**: Never commit identity files
|
|
266
|
+
|
|
267
|
+
## Performance Tips
|
|
268
|
+
|
|
269
|
+
1. **Use memory storage** for Lambda/Edge runtimes
|
|
270
|
+
2. **Enable signature caching** (automatic)
|
|
271
|
+
3. **Use 'silent' log level** in production
|
|
272
|
+
4. **Let transport auto-select** based on runtime
|
|
273
|
+
5. **Preload identity** during cold starts
|
|
274
|
+
|
|
275
|
+
## Troubleshooting
|
|
276
|
+
|
|
277
|
+
### Lambda/Edge Issues
|
|
278
|
+
|
|
279
|
+
- Ensure `storage: 'memory'` or `'auto'`
|
|
280
|
+
- Use `transport: 'fetch'` for edge runtimes
|
|
281
|
+
- Set `logLevel: 'silent'` to avoid console issues
|
|
282
|
+
|
|
283
|
+
### Key Rotation Failures
|
|
284
|
+
|
|
285
|
+
- Check network connectivity to knowthat.ai
|
|
286
|
+
- Verify current keys are not corrupted
|
|
287
|
+
- Manual rotation: `await identity.rotateKeys('recovery')`
|
|
87
288
|
|
|
88
|
-
|
|
289
|
+
### Performance Issues
|
|
89
290
|
|
|
90
|
-
-
|
|
91
|
-
-
|
|
92
|
-
-
|
|
93
|
-
- Nonce tracking prevents reuse
|
|
291
|
+
- Verify signature caching is working
|
|
292
|
+
- Check lazy loading (should see delayed first signature)
|
|
293
|
+
- Use memory storage when possible
|
|
94
294
|
|
|
95
295
|
## License
|
|
96
296
|
|
|
97
|
-
MIT
|
|
297
|
+
MIT
|
package/dist/auto.d.ts
CHANGED
|
@@ -1,13 +1 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Auto-initialization for MCP Identity
|
|
3
|
-
*
|
|
4
|
-
* Just import this file to automatically enable MCP-I for any MCP server:
|
|
5
|
-
*
|
|
6
|
-
* ```typescript
|
|
7
|
-
* import "@kya-os/mcp-i/auto";
|
|
8
|
-
* ```
|
|
9
|
-
*
|
|
10
|
-
* That's it! Your MCP server now has identity.
|
|
11
|
-
*/
|
|
12
1
|
export {};
|
|
13
|
-
//# sourceMappingURL=auto.d.ts.map
|
package/dist/auto.js
CHANGED
|
@@ -1,24 +1,13 @@
|
|
|
1
1
|
"use strict";
|
|
2
|
-
/**
|
|
3
|
-
* Auto-initialization for MCP Identity
|
|
4
|
-
*
|
|
5
|
-
* Just import this file to automatically enable MCP-I for any MCP server:
|
|
6
|
-
*
|
|
7
|
-
* ```typescript
|
|
8
|
-
* import "@kya-os/mcp-i/auto";
|
|
9
|
-
* ```
|
|
10
|
-
*
|
|
11
|
-
* That's it! Your MCP server now has identity.
|
|
12
|
-
*/
|
|
13
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
14
3
|
const index_1 = require("./index");
|
|
15
|
-
|
|
4
|
+
const logger_1 = require("./logger");
|
|
16
5
|
(async () => {
|
|
17
6
|
try {
|
|
18
7
|
await (0, index_1.enableMCPIdentity)();
|
|
19
8
|
}
|
|
20
9
|
catch (error) {
|
|
21
|
-
|
|
10
|
+
const logger = (0, logger_1.getLogger)();
|
|
11
|
+
logger.error('[MCP-I] Failed to auto-initialize:', error);
|
|
22
12
|
}
|
|
23
13
|
})();
|
|
24
|
-
//# sourceMappingURL=auto.js.map
|
package/dist/crypto.d.ts
CHANGED
|
@@ -1,32 +1,16 @@
|
|
|
1
|
-
|
|
2
|
-
* Cryptographic utilities for MCP-I
|
|
3
|
-
* Implements Ed25519 signing and verification for challenge-response authentication
|
|
4
|
-
*/
|
|
5
|
-
/**
|
|
6
|
-
* Generate a new Ed25519 key pair
|
|
7
|
-
*/
|
|
8
|
-
export declare function generateKeyPair(): Promise<{
|
|
1
|
+
export interface PrecomputedKeyPair {
|
|
9
2
|
publicKey: string;
|
|
10
3
|
privateKey: string;
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
4
|
+
publicKeyBytes?: Uint8Array;
|
|
5
|
+
privateKeyBytes?: Uint8Array;
|
|
6
|
+
}
|
|
7
|
+
export declare function generateKeyPair(): Promise<PrecomputedKeyPair>;
|
|
15
8
|
export declare function sign(message: string | Buffer, privateKeyBase64: string): Promise<string>;
|
|
16
|
-
/**
|
|
17
|
-
* Verify an Ed25519 signature
|
|
18
|
-
*/
|
|
19
9
|
export declare function verify(message: string | Buffer, signatureBase64: string, publicKeyBase64: string): Promise<boolean>;
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
*/
|
|
23
|
-
export declare function generateNonce(length?: number): string;
|
|
24
|
-
/**
|
|
25
|
-
* Constant-time string comparison to prevent timing attacks
|
|
26
|
-
*/
|
|
10
|
+
export declare function generateNonce(length?: number): Promise<string>;
|
|
11
|
+
export declare function generateNonceSync(length?: number): string;
|
|
27
12
|
export declare function constantTimeEqual(a: string, b: string): boolean;
|
|
28
|
-
/**
|
|
29
|
-
* Convert Ed25519 public key to did:key format
|
|
30
|
-
*/
|
|
31
13
|
export declare function publicKeyToDid(publicKeyBase64: string): string;
|
|
32
|
-
|
|
14
|
+
export declare function encrypt(data: string, password: string): Promise<string>;
|
|
15
|
+
export declare function decrypt(encryptedData: string, password: string): Promise<string>;
|
|
16
|
+
export declare function clearCache(): void;
|
package/dist/crypto.js
CHANGED
|
@@ -1,8 +1,4 @@
|
|
|
1
1
|
"use strict";
|
|
2
|
-
/**
|
|
3
|
-
* Cryptographic utilities for MCP-I
|
|
4
|
-
* Implements Ed25519 signing and verification for challenge-response authentication
|
|
5
|
-
*/
|
|
6
2
|
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
|
|
7
3
|
if (k2 === undefined) k2 = k;
|
|
8
4
|
var desc = Object.getOwnPropertyDescriptor(m, k);
|
|
@@ -41,57 +37,93 @@ exports.generateKeyPair = generateKeyPair;
|
|
|
41
37
|
exports.sign = sign;
|
|
42
38
|
exports.verify = verify;
|
|
43
39
|
exports.generateNonce = generateNonce;
|
|
40
|
+
exports.generateNonceSync = generateNonceSync;
|
|
44
41
|
exports.constantTimeEqual = constantTimeEqual;
|
|
45
42
|
exports.publicKeyToDid = publicKeyToDid;
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
43
|
+
exports.encrypt = encrypt;
|
|
44
|
+
exports.decrypt = decrypt;
|
|
45
|
+
exports.clearCache = clearCache;
|
|
46
|
+
let ed25519 = null;
|
|
47
|
+
let cryptoModule = null;
|
|
48
|
+
const signatureCache = new Map();
|
|
49
|
+
const MAX_CACHE_SIZE = 100;
|
|
50
|
+
async function loadEd25519() {
|
|
51
|
+
if (!ed25519) {
|
|
52
|
+
ed25519 = await Promise.resolve().then(() => __importStar(require('@noble/ed25519')));
|
|
53
|
+
}
|
|
54
|
+
return ed25519;
|
|
55
|
+
}
|
|
56
|
+
async function loadCrypto() {
|
|
57
|
+
if (!cryptoModule) {
|
|
58
|
+
cryptoModule = await Promise.resolve().then(() => __importStar(require('crypto')));
|
|
59
|
+
}
|
|
60
|
+
return cryptoModule;
|
|
61
|
+
}
|
|
51
62
|
async function generateKeyPair() {
|
|
52
|
-
const
|
|
53
|
-
const
|
|
63
|
+
const ed = await loadEd25519();
|
|
64
|
+
const privateKeyBytes = ed.utils.randomPrivateKey();
|
|
65
|
+
const publicKeyBytes = await ed.getPublicKeyAsync(privateKeyBytes);
|
|
66
|
+
const publicKey = Buffer.from(publicKeyBytes).toString('base64');
|
|
67
|
+
const privateKey = Buffer.from(privateKeyBytes).toString('base64');
|
|
54
68
|
return {
|
|
55
|
-
publicKey
|
|
56
|
-
privateKey
|
|
69
|
+
publicKey,
|
|
70
|
+
privateKey,
|
|
71
|
+
publicKeyBytes,
|
|
72
|
+
privateKeyBytes
|
|
57
73
|
};
|
|
58
74
|
}
|
|
59
|
-
/**
|
|
60
|
-
* Sign a message with Ed25519
|
|
61
|
-
*/
|
|
62
75
|
async function sign(message, privateKeyBase64) {
|
|
76
|
+
const messageStr = typeof message === 'string' ? message : message.toString('base64');
|
|
77
|
+
const cacheKey = `${privateKeyBase64}:${messageStr}`;
|
|
78
|
+
const cached = signatureCache.get(cacheKey);
|
|
79
|
+
if (cached) {
|
|
80
|
+
return cached;
|
|
81
|
+
}
|
|
82
|
+
const ed = await loadEd25519();
|
|
63
83
|
const messageBuffer = typeof message === 'string'
|
|
64
84
|
? Buffer.from(message, 'utf-8')
|
|
65
85
|
: message;
|
|
66
86
|
const privateKey = Buffer.from(privateKeyBase64, 'base64');
|
|
67
|
-
const signature = await
|
|
68
|
-
|
|
87
|
+
const signature = await ed.signAsync(messageBuffer, privateKey);
|
|
88
|
+
const signatureBase64 = Buffer.from(signature).toString('base64');
|
|
89
|
+
if (signatureCache.size >= MAX_CACHE_SIZE) {
|
|
90
|
+
const firstKey = signatureCache.keys().next().value;
|
|
91
|
+
if (firstKey) {
|
|
92
|
+
signatureCache.delete(firstKey);
|
|
93
|
+
}
|
|
94
|
+
}
|
|
95
|
+
signatureCache.set(cacheKey, signatureBase64);
|
|
96
|
+
return signatureBase64;
|
|
69
97
|
}
|
|
70
|
-
/**
|
|
71
|
-
* Verify an Ed25519 signature
|
|
72
|
-
*/
|
|
73
98
|
async function verify(message, signatureBase64, publicKeyBase64) {
|
|
74
99
|
try {
|
|
100
|
+
const ed = await loadEd25519();
|
|
75
101
|
const messageBuffer = typeof message === 'string'
|
|
76
102
|
? Buffer.from(message, 'utf-8')
|
|
77
103
|
: message;
|
|
78
104
|
const signature = Buffer.from(signatureBase64, 'base64');
|
|
79
105
|
const publicKey = Buffer.from(publicKeyBase64, 'base64');
|
|
80
|
-
return await
|
|
106
|
+
return await ed.verifyAsync(signature, messageBuffer, publicKey);
|
|
81
107
|
}
|
|
82
108
|
catch {
|
|
83
109
|
return false;
|
|
84
110
|
}
|
|
85
111
|
}
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
112
|
+
async function generateNonce(length = 32) {
|
|
113
|
+
const crypto = await loadCrypto();
|
|
114
|
+
return crypto.randomBytes(length).toString('hex');
|
|
115
|
+
}
|
|
116
|
+
function generateNonceSync(length = 32) {
|
|
117
|
+
if (typeof globalThis.crypto !== 'undefined' && globalThis.crypto.getRandomValues) {
|
|
118
|
+
const bytes = new Uint8Array(length);
|
|
119
|
+
globalThis.crypto.getRandomValues(bytes);
|
|
120
|
+
return Buffer.from(bytes).toString('hex');
|
|
121
|
+
}
|
|
122
|
+
else {
|
|
123
|
+
const crypto = require('crypto');
|
|
124
|
+
return crypto.randomBytes(length).toString('hex');
|
|
125
|
+
}
|
|
91
126
|
}
|
|
92
|
-
/**
|
|
93
|
-
* Constant-time string comparison to prevent timing attacks
|
|
94
|
-
*/
|
|
95
127
|
function constantTimeEqual(a, b) {
|
|
96
128
|
if (a.length !== b.length) {
|
|
97
129
|
return false;
|
|
@@ -102,16 +134,64 @@ function constantTimeEqual(a, b) {
|
|
|
102
134
|
}
|
|
103
135
|
return result === 0;
|
|
104
136
|
}
|
|
105
|
-
/**
|
|
106
|
-
* Convert Ed25519 public key to did:key format
|
|
107
|
-
*/
|
|
108
137
|
function publicKeyToDid(publicKeyBase64) {
|
|
109
138
|
const publicKey = Buffer.from(publicKeyBase64, 'base64');
|
|
110
|
-
// Multicodec ed25519-pub header (0xed 0x01)
|
|
111
139
|
const multicodec = Buffer.from([0xed, 0x01]);
|
|
112
140
|
const multikey = Buffer.concat([multicodec, publicKey]);
|
|
113
|
-
// Base58 encode (simplified - in production use a proper base58 library)
|
|
114
|
-
// For now, just return a placeholder
|
|
115
141
|
return `did:key:z${multikey.toString('base64').replace(/\+/g, '-').replace(/\//g, '_').replace(/=/g, '')}`;
|
|
116
142
|
}
|
|
117
|
-
|
|
143
|
+
async function encrypt(data, password) {
|
|
144
|
+
const encoder = new TextEncoder();
|
|
145
|
+
const salt = new Uint8Array(16);
|
|
146
|
+
globalThis.crypto.getRandomValues(salt);
|
|
147
|
+
const keyMaterial = await globalThis.crypto.subtle.importKey('raw', encoder.encode(password), 'PBKDF2', false, ['deriveKey']);
|
|
148
|
+
const key = await globalThis.crypto.subtle.deriveKey({
|
|
149
|
+
name: 'PBKDF2',
|
|
150
|
+
salt,
|
|
151
|
+
iterations: 100000,
|
|
152
|
+
hash: 'SHA-256'
|
|
153
|
+
}, keyMaterial, { name: 'AES-GCM', length: 256 }, false, ['encrypt']);
|
|
154
|
+
const iv = new Uint8Array(12);
|
|
155
|
+
globalThis.crypto.getRandomValues(iv);
|
|
156
|
+
const encrypted = await globalThis.crypto.subtle.encrypt({ name: 'AES-GCM', iv }, key, encoder.encode(data));
|
|
157
|
+
const combined = new Uint8Array(salt.length + iv.length + encrypted.byteLength);
|
|
158
|
+
combined.set(salt);
|
|
159
|
+
combined.set(iv, salt.length);
|
|
160
|
+
combined.set(new Uint8Array(encrypted), salt.length + iv.length);
|
|
161
|
+
return 'enc:' + Buffer.from(combined).toString('base64');
|
|
162
|
+
}
|
|
163
|
+
async function decrypt(encryptedData, password) {
|
|
164
|
+
let dataToDecrypt = encryptedData;
|
|
165
|
+
if (encryptedData.startsWith('enc:')) {
|
|
166
|
+
dataToDecrypt = encryptedData.slice(4);
|
|
167
|
+
}
|
|
168
|
+
if (!dataToDecrypt || dataToDecrypt.length < 44) {
|
|
169
|
+
return encryptedData;
|
|
170
|
+
}
|
|
171
|
+
const encoder = new TextEncoder();
|
|
172
|
+
const decoder = new TextDecoder();
|
|
173
|
+
try {
|
|
174
|
+
const combined = Buffer.from(dataToDecrypt, 'base64');
|
|
175
|
+
if (combined.length < 29) {
|
|
176
|
+
return encryptedData;
|
|
177
|
+
}
|
|
178
|
+
const salt = combined.slice(0, 16);
|
|
179
|
+
const iv = combined.slice(16, 28);
|
|
180
|
+
const encrypted = combined.slice(28);
|
|
181
|
+
const keyMaterial = await globalThis.crypto.subtle.importKey('raw', encoder.encode(password), 'PBKDF2', false, ['deriveKey']);
|
|
182
|
+
const key = await globalThis.crypto.subtle.deriveKey({
|
|
183
|
+
name: 'PBKDF2',
|
|
184
|
+
salt: new Uint8Array(salt),
|
|
185
|
+
iterations: 100000,
|
|
186
|
+
hash: 'SHA-256'
|
|
187
|
+
}, keyMaterial, { name: 'AES-GCM', length: 256 }, false, ['decrypt']);
|
|
188
|
+
const decrypted = await globalThis.crypto.subtle.decrypt({ name: 'AES-GCM', iv: new Uint8Array(iv) }, key, new Uint8Array(encrypted));
|
|
189
|
+
return decoder.decode(decrypted);
|
|
190
|
+
}
|
|
191
|
+
catch (error) {
|
|
192
|
+
throw new Error('Failed to decrypt data: invalid password or corrupted data');
|
|
193
|
+
}
|
|
194
|
+
}
|
|
195
|
+
function clearCache() {
|
|
196
|
+
signatureCache.clear();
|
|
197
|
+
}
|