@smartledger/keys 1.3.0 → 1.5.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.
- package/LICENSE +23 -23
- package/README.md +535 -492
- package/dist/cjs/browser.d.ts.map +1 -1
- package/dist/cjs/browser.js +5 -2
- package/dist/cjs/browser.js.map +1 -1
- package/dist/cjs/sdk.d.ts +30 -1
- package/dist/cjs/sdk.d.ts.map +1 -1
- package/dist/cjs/sdk.js +127 -4
- package/dist/cjs/sdk.js.map +1 -1
- package/dist/cjs/types.d.ts +47 -0
- package/dist/cjs/types.d.ts.map +1 -1
- package/dist/esm/browser.d.ts.map +1 -1
- package/dist/esm/browser.js +6 -3
- package/dist/esm/browser.js.map +1 -1
- package/dist/esm/package.json +1 -1
- package/dist/esm/sdk.d.ts +30 -1
- package/dist/esm/sdk.d.ts.map +1 -1
- package/dist/esm/sdk.js +127 -4
- package/dist/esm/sdk.js.map +1 -1
- package/dist/esm/types.d.ts +47 -0
- package/dist/esm/types.d.ts.map +1 -1
- package/dist/lumen-keys.js +157 -34
- package/dist/lumen-keys.js.map +3 -3
- package/dist/lumen-keys.min.js +3 -3
- package/dist/lumen-keys.min.js.map +3 -3
- package/dist/tsconfig.esm.tsbuildinfo +1 -1
- package/dist/tsconfig.tsbuildinfo +1 -1
- package/package.json +76 -76
package/README.md
CHANGED
|
@@ -1,492 +1,535 @@
|
|
|
1
|
-
# @smartledger/keys SDK Documentation
|
|
2
|
-
|
|
3
|
-
**Single source of truth for cryptographic key operations.**
|
|
4
|
-
|
|
5
|
-
Version: 1.
|
|
6
|
-
Status: ✅ Production Ready
|
|
7
|
-
Tests:
|
|
8
|
-
|
|
9
|
-
---
|
|
10
|
-
|
|
11
|
-
## Why This SDK?
|
|
12
|
-
|
|
13
|
-
Keys are the **root of everything** in your crypto architecture:
|
|
14
|
-
- Identity
|
|
15
|
-
- Signatures
|
|
16
|
-
- PQ migration
|
|
17
|
-
- Regulatory/audit story
|
|
18
|
-
|
|
19
|
-
Without a clean SDK layer, key handling becomes **ad hoc** and **brittle**:
|
|
20
|
-
- ❌ Random `generateKeypair()` implementations scattered across repos
|
|
21
|
-
- ❌ Slightly different formats (hex here, base64 there)
|
|
22
|
-
- ❌ Surprise refactors when adding PQ suites
|
|
23
|
-
- ❌ Private keys exposed to calling code
|
|
24
|
-
|
|
25
|
-
**With `@smartledger/keys`**, all your agents/modules/bridge code simply ask:
|
|
26
|
-
```typescript
|
|
27
|
-
"Give me a signing key of suite X"
|
|
28
|
-
"Sign this payload with keyId Y"
|
|
29
|
-
```
|
|
30
|
-
|
|
31
|
-
And **don't care** how keys are stored, rotated, or implemented.
|
|
32
|
-
|
|
33
|
-
---
|
|
34
|
-
|
|
35
|
-
## Design Principles
|
|
36
|
-
|
|
37
|
-
1. **Private keys hidden** - Never exposed to most callers
|
|
38
|
-
2. **Crypto agility** - Suite is config, not code
|
|
39
|
-
3. **Single entry point** - Easy to audit
|
|
40
|
-
4. **Idempotent operations** - Safe for retries
|
|
41
|
-
5. **Minimal API surface** - Small, focused, easy to learn
|
|
42
|
-
|
|
43
|
-
---
|
|
44
|
-
|
|
45
|
-
## Installation
|
|
46
|
-
|
|
47
|
-
```bash
|
|
48
|
-
npm install @smartledger/keys
|
|
49
|
-
```
|
|
50
|
-
|
|
51
|
-
---
|
|
52
|
-
|
|
53
|
-
## Quick Start
|
|
54
|
-
|
|
55
|
-
```typescript
|
|
56
|
-
import {
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
}
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
**
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
|
|
277
|
-
|
|
278
|
-
|
|
279
|
-
|
|
280
|
-
|
|
281
|
-
|
|
282
|
-
|
|
283
|
-
|
|
284
|
-
|
|
285
|
-
|
|
286
|
-
|
|
287
|
-
|
|
288
|
-
|
|
289
|
-
|
|
290
|
-
}
|
|
291
|
-
|
|
292
|
-
|
|
293
|
-
|
|
294
|
-
|
|
295
|
-
|
|
296
|
-
|
|
297
|
-
|
|
298
|
-
|
|
299
|
-
|
|
300
|
-
|
|
301
|
-
|
|
302
|
-
|
|
303
|
-
|
|
304
|
-
|
|
305
|
-
|
|
306
|
-
|
|
307
|
-
|
|
308
|
-
|
|
309
|
-
|
|
310
|
-
|
|
311
|
-
|
|
312
|
-
|
|
313
|
-
|
|
314
|
-
|
|
315
|
-
```
|
|
316
|
-
|
|
317
|
-
|
|
318
|
-
|
|
319
|
-
|
|
320
|
-
|
|
321
|
-
|
|
322
|
-
|
|
323
|
-
|
|
324
|
-
|
|
325
|
-
|
|
326
|
-
|
|
327
|
-
|
|
328
|
-
|
|
329
|
-
|
|
330
|
-
|
|
331
|
-
|
|
332
|
-
|
|
333
|
-
|
|
334
|
-
|
|
335
|
-
|
|
336
|
-
|
|
337
|
-
|
|
338
|
-
|
|
339
|
-
|
|
340
|
-
|
|
341
|
-
|
|
342
|
-
|
|
343
|
-
|
|
344
|
-
|
|
345
|
-
|
|
346
|
-
|
|
347
|
-
|
|
348
|
-
|
|
349
|
-
|
|
350
|
-
|
|
351
|
-
|
|
352
|
-
|
|
353
|
-
|
|
354
|
-
//
|
|
355
|
-
|
|
356
|
-
|
|
357
|
-
|
|
358
|
-
|
|
359
|
-
|
|
360
|
-
|
|
361
|
-
|
|
362
|
-
|
|
363
|
-
|
|
364
|
-
|
|
365
|
-
|
|
366
|
-
|
|
367
|
-
|
|
368
|
-
|
|
369
|
-
|
|
370
|
-
|
|
371
|
-
|
|
372
|
-
|
|
373
|
-
|
|
374
|
-
|
|
375
|
-
|
|
376
|
-
|
|
377
|
-
|
|
378
|
-
```
|
|
379
|
-
|
|
380
|
-
|
|
381
|
-
|
|
382
|
-
|
|
383
|
-
|
|
384
|
-
|
|
385
|
-
|
|
386
|
-
|
|
387
|
-
|
|
388
|
-
|
|
389
|
-
|
|
390
|
-
|
|
391
|
-
|
|
392
|
-
|
|
393
|
-
|
|
394
|
-
|
|
395
|
-
|
|
396
|
-
|
|
397
|
-
|
|
398
|
-
|
|
399
|
-
|
|
400
|
-
|
|
401
|
-
|
|
402
|
-
|
|
403
|
-
|
|
404
|
-
|
|
405
|
-
|
|
406
|
-
|
|
407
|
-
|
|
408
|
-
|
|
409
|
-
|
|
410
|
-
|
|
411
|
-
|
|
412
|
-
|
|
413
|
-
|
|
414
|
-
|
|
415
|
-
|
|
416
|
-
|
|
417
|
-
|
|
418
|
-
|
|
419
|
-
|
|
420
|
-
|
|
421
|
-
|
|
422
|
-
|
|
423
|
-
|
|
424
|
-
|
|
425
|
-
|
|
426
|
-
|
|
427
|
-
|
|
428
|
-
|
|
429
|
-
|
|
430
|
-
|
|
431
|
-
|
|
432
|
-
|
|
433
|
-
|
|
434
|
-
###
|
|
435
|
-
|
|
436
|
-
|
|
437
|
-
|
|
438
|
-
|
|
439
|
-
|
|
440
|
-
|
|
441
|
-
|
|
442
|
-
|
|
443
|
-
|
|
444
|
-
|
|
445
|
-
|
|
446
|
-
|
|
447
|
-
|
|
448
|
-
|
|
449
|
-
|
|
450
|
-
|
|
451
|
-
|
|
452
|
-
|
|
453
|
-
|
|
454
|
-
|
|
455
|
-
|
|
456
|
-
|
|
457
|
-
|
|
458
|
-
|
|
459
|
-
|
|
460
|
-
|
|
461
|
-
-
|
|
462
|
-
-
|
|
463
|
-
-
|
|
464
|
-
-
|
|
465
|
-
|
|
466
|
-
|
|
467
|
-
|
|
468
|
-
|
|
469
|
-
|
|
470
|
-
|
|
471
|
-
|
|
472
|
-
|
|
473
|
-
|
|
474
|
-
|
|
475
|
-
|
|
476
|
-
|
|
477
|
-
|
|
478
|
-
|
|
479
|
-
|
|
480
|
-
|
|
481
|
-
|
|
482
|
-
|
|
483
|
-
|
|
484
|
-
|
|
485
|
-
|
|
486
|
-
|
|
487
|
-
|
|
488
|
-
|
|
489
|
-
|
|
490
|
-
|
|
491
|
-
|
|
492
|
-
|
|
1
|
+
# @smartledger/keys SDK Documentation
|
|
2
|
+
|
|
3
|
+
**Single source of truth for cryptographic key operations.**
|
|
4
|
+
|
|
5
|
+
Version: 1.4.0
|
|
6
|
+
Status: ✅ Production Ready
|
|
7
|
+
Tests: 52/52 passing
|
|
8
|
+
|
|
9
|
+
---
|
|
10
|
+
|
|
11
|
+
## Why This SDK?
|
|
12
|
+
|
|
13
|
+
Keys are the **root of everything** in your crypto architecture:
|
|
14
|
+
- Identity
|
|
15
|
+
- Signatures
|
|
16
|
+
- PQ migration
|
|
17
|
+
- Regulatory/audit story
|
|
18
|
+
|
|
19
|
+
Without a clean SDK layer, key handling becomes **ad hoc** and **brittle**:
|
|
20
|
+
- ❌ Random `generateKeypair()` implementations scattered across repos
|
|
21
|
+
- ❌ Slightly different formats (hex here, base64 there)
|
|
22
|
+
- ❌ Surprise refactors when adding PQ suites
|
|
23
|
+
- ❌ Private keys exposed to calling code
|
|
24
|
+
|
|
25
|
+
**With `@smartledger/keys`**, all your agents/modules/bridge code simply ask:
|
|
26
|
+
```typescript
|
|
27
|
+
"Give me a signing key of suite X"
|
|
28
|
+
"Sign this payload with keyId Y"
|
|
29
|
+
```
|
|
30
|
+
|
|
31
|
+
And **don't care** how keys are stored, rotated, or implemented.
|
|
32
|
+
|
|
33
|
+
---
|
|
34
|
+
|
|
35
|
+
## Design Principles
|
|
36
|
+
|
|
37
|
+
1. **Private keys hidden** - Never exposed to most callers
|
|
38
|
+
2. **Crypto agility** - Suite is config, not code
|
|
39
|
+
3. **Single entry point** - Easy to audit
|
|
40
|
+
4. **Idempotent operations** - Safe for retries
|
|
41
|
+
5. **Minimal API surface** - Small, focused, easy to learn
|
|
42
|
+
|
|
43
|
+
---
|
|
44
|
+
|
|
45
|
+
## Installation
|
|
46
|
+
|
|
47
|
+
```bash
|
|
48
|
+
npm install @smartledger/keys
|
|
49
|
+
```
|
|
50
|
+
|
|
51
|
+
---
|
|
52
|
+
|
|
53
|
+
## Quick Start
|
|
54
|
+
|
|
55
|
+
```typescript
|
|
56
|
+
import { createKeySDK } from '@smartledger/keys';
|
|
57
|
+
|
|
58
|
+
// One line to get a ready-to-use SDK with ML-DSA + ECDSA suites registered
|
|
59
|
+
const sdk = createKeySDK();
|
|
60
|
+
|
|
61
|
+
// Create both PQ + ECDSA keys for an agent
|
|
62
|
+
const { primaryKey, secondaryKey } = await sdk.createDualSignatureKeys(
|
|
63
|
+
'agent-resonance',
|
|
64
|
+
{
|
|
65
|
+
primarySignatureSuite: 'ml-dsa-87',
|
|
66
|
+
secondarySignatureSuite: 'bsv-ecdsa-secp256k1',
|
|
67
|
+
}
|
|
68
|
+
);
|
|
69
|
+
|
|
70
|
+
// Sign with both suites (private keys stay hidden)
|
|
71
|
+
const message = new TextEncoder().encode('agent output');
|
|
72
|
+
const { signatures } = await sdk.signWithSuites('agent-resonance', message);
|
|
73
|
+
|
|
74
|
+
// Verify both signatures
|
|
75
|
+
const verify = await sdk.verifyWithSuites('agent-resonance', message, signatures);
|
|
76
|
+
console.log(verify.allValid); // true
|
|
77
|
+
```
|
|
78
|
+
|
|
79
|
+
---
|
|
80
|
+
|
|
81
|
+
## API Reference
|
|
82
|
+
|
|
83
|
+
### Factory Helper
|
|
84
|
+
|
|
85
|
+
`createKeySDK(config?)` returns a ready-to-use SDK wired with in-memory storage and the default ML-DSA + ECDSA suites. Pass your own `keyRegistry` or `suiteRegistry` to override defaults.
|
|
86
|
+
|
|
87
|
+
### KeySDK Interface
|
|
88
|
+
|
|
89
|
+
#### `createKey(agentId, profile, options?)`
|
|
90
|
+
|
|
91
|
+
Create a new key for an agent/module.
|
|
92
|
+
|
|
93
|
+
**Parameters:**
|
|
94
|
+
- `agentId` (string) - Agent identifier (e.g. `'agent-resonance'`)
|
|
95
|
+
- `profile` (CryptoProfile) - Which signature suites to use
|
|
96
|
+
- `options` (CreateKeyOptions) - Optional expiration, usage, etc.
|
|
97
|
+
|
|
98
|
+
**Returns:** `Promise<KeyRecord>` - KeyRecord with metadata and public key
|
|
99
|
+
|
|
100
|
+
**Example:**
|
|
101
|
+
```typescript
|
|
102
|
+
const key = await sdk.createKey(
|
|
103
|
+
'agent-schema',
|
|
104
|
+
{ primarySignatureSuite: 'ml-dsa-87' },
|
|
105
|
+
{
|
|
106
|
+
expiresAt: new Date(Date.now() + 365 * 86400000).toISOString(), // 1 year
|
|
107
|
+
usage: ['signing'],
|
|
108
|
+
}
|
|
109
|
+
);
|
|
110
|
+
```
|
|
111
|
+
|
|
112
|
+
---
|
|
113
|
+
|
|
114
|
+
#### `getKey(keyId)`
|
|
115
|
+
|
|
116
|
+
Get an existing key by ID.
|
|
117
|
+
|
|
118
|
+
**Parameters:**
|
|
119
|
+
- `keyId` (string) - Key identifier
|
|
120
|
+
|
|
121
|
+
**Returns:** `Promise<KeyRecord | null>` - KeyRecord or null if not found
|
|
122
|
+
|
|
123
|
+
**Example:**
|
|
124
|
+
```typescript
|
|
125
|
+
const key = await sdk.getKey('agent-resonance:pk-ml-1');
|
|
126
|
+
if (key) {
|
|
127
|
+
console.log(key.meta.suiteId); // 'ml-dsa-87'
|
|
128
|
+
console.log(key.publicKey); // Uint8Array
|
|
129
|
+
}
|
|
130
|
+
```
|
|
131
|
+
|
|
132
|
+
---
|
|
133
|
+
|
|
134
|
+
#### `signWithKey(keyId, message)`
|
|
135
|
+
|
|
136
|
+
Sign a message with a key. **Private key never leaves the SDK.**
|
|
137
|
+
|
|
138
|
+
**Parameters:**
|
|
139
|
+
- `keyId` (string) - Which key to sign with
|
|
140
|
+
- `message` (Uint8Array) - Message to sign (will be hashed internally)
|
|
141
|
+
|
|
142
|
+
**Returns:** `Promise<Uint8Array>` - Signature bytes
|
|
143
|
+
|
|
144
|
+
**Throws:** Error if key not found or not active
|
|
145
|
+
|
|
146
|
+
**Example:**
|
|
147
|
+
```typescript
|
|
148
|
+
const message = new TextEncoder().encode('agent output');
|
|
149
|
+
const signature = await sdk.signWithKey('agent-resonance:pk-ml-1', message);
|
|
150
|
+
```
|
|
151
|
+
|
|
152
|
+
---
|
|
153
|
+
|
|
154
|
+
#### `verifySignature(keyId, message, signature)`
|
|
155
|
+
|
|
156
|
+
Verify a signature against a public key.
|
|
157
|
+
|
|
158
|
+
**Parameters:**
|
|
159
|
+
- `keyId` (string) - Which key to verify with
|
|
160
|
+
- `message` (Uint8Array) - Original message
|
|
161
|
+
- `signature` (Uint8Array) - Signature to verify
|
|
162
|
+
|
|
163
|
+
**Returns:** `Promise<boolean>` - true if valid
|
|
164
|
+
|
|
165
|
+
**Example:**
|
|
166
|
+
```typescript
|
|
167
|
+
const valid = await sdk.verifySignature(
|
|
168
|
+
'agent-resonance:pk-ml-1',
|
|
169
|
+
message,
|
|
170
|
+
signature
|
|
171
|
+
);
|
|
172
|
+
```
|
|
173
|
+
|
|
174
|
+
---
|
|
175
|
+
|
|
176
|
+
#### `listKeysForAgent(agentId, activeOnly?)`
|
|
177
|
+
|
|
178
|
+
List all keys for an agent.
|
|
179
|
+
|
|
180
|
+
**Parameters:**
|
|
181
|
+
- `agentId` (string) - Agent identifier
|
|
182
|
+
- `activeOnly` (boolean) - If true, only return active keys (default: true)
|
|
183
|
+
|
|
184
|
+
**Returns:** `Promise<KeyRecord[]>` - Array of KeyRecords
|
|
185
|
+
|
|
186
|
+
**Example:**
|
|
187
|
+
```typescript
|
|
188
|
+
const keys = await sdk.listKeysForAgent('agent-schema');
|
|
189
|
+
for (const key of keys) {
|
|
190
|
+
console.log(`${key.meta.keyId} (${key.meta.suiteId})`);
|
|
191
|
+
}
|
|
192
|
+
```
|
|
193
|
+
|
|
194
|
+
---
|
|
195
|
+
|
|
196
|
+
#### `getActiveCryptoProfile(agentId)`
|
|
197
|
+
|
|
198
|
+
Get the active crypto profile for an agent.
|
|
199
|
+
|
|
200
|
+
Looks up the most recent active keys and infers the profile.
|
|
201
|
+
|
|
202
|
+
**Parameters:**
|
|
203
|
+
- `agentId` (string) - Agent identifier
|
|
204
|
+
|
|
205
|
+
**Returns:** `Promise<CryptoProfile | null>` - CryptoProfile or null if no keys found
|
|
206
|
+
|
|
207
|
+
**Example:**
|
|
208
|
+
```typescript
|
|
209
|
+
const profile = await sdk.getActiveCryptoProfile('agent-schema');
|
|
210
|
+
console.log(profile.primarySignatureSuite); // 'ml-dsa-87'
|
|
211
|
+
console.log(profile.secondarySignatureSuite); // 'bsv-ecdsa-secp256k1'
|
|
212
|
+
```
|
|
213
|
+
|
|
214
|
+
---
|
|
215
|
+
|
|
216
|
+
#### `rotateAgentKeys(agentId)`
|
|
217
|
+
|
|
218
|
+
Rotate an agent's keys.
|
|
219
|
+
|
|
220
|
+
Generates new keypairs for all active keys, marks old ones as rotated.
|
|
221
|
+
|
|
222
|
+
**Parameters:**
|
|
223
|
+
- `agentId` (string) - Agent identifier
|
|
224
|
+
|
|
225
|
+
**Returns:** `Promise<KeyRecord[]>` - Array of new KeyRecords
|
|
226
|
+
|
|
227
|
+
**Example:**
|
|
228
|
+
```typescript
|
|
229
|
+
const newKeys = await sdk.rotateAgentKeys('agent-schema');
|
|
230
|
+
for (const key of newKeys) {
|
|
231
|
+
console.log(`New: ${key.meta.keyId}`);
|
|
232
|
+
console.log(`Rotated from: ${key.meta.rotatedFrom}`);
|
|
233
|
+
}
|
|
234
|
+
```
|
|
235
|
+
|
|
236
|
+
---
|
|
237
|
+
|
|
238
|
+
#### `getOrCreateKey(agentId, profile, options?)`
|
|
239
|
+
|
|
240
|
+
Create key if it doesn't exist, otherwise return existing.
|
|
241
|
+
|
|
242
|
+
**Idempotent** key creation for agent setup.
|
|
243
|
+
|
|
244
|
+
**Parameters:**
|
|
245
|
+
- `agentId` (string) - Agent identifier
|
|
246
|
+
- `profile` (CryptoProfile) - Crypto configuration
|
|
247
|
+
- `options` (CreateKeyOptions) - Creation options
|
|
248
|
+
|
|
249
|
+
**Returns:** `Promise<KeyRecord>` - KeyRecord (new or existing)
|
|
250
|
+
|
|
251
|
+
**Example:**
|
|
252
|
+
```typescript
|
|
253
|
+
// Safe to call multiple times
|
|
254
|
+
const key1 = await sdk.getOrCreateKey('agent-validator', {
|
|
255
|
+
primarySignatureSuite: 'ml-dsa-87',
|
|
256
|
+
});
|
|
257
|
+
|
|
258
|
+
const key2 = await sdk.getOrCreateKey('agent-validator', {
|
|
259
|
+
primarySignatureSuite: 'ml-dsa-87',
|
|
260
|
+
});
|
|
261
|
+
|
|
262
|
+
console.log(key1.meta.keyId === key2.meta.keyId); // true
|
|
263
|
+
```
|
|
264
|
+
|
|
265
|
+
---
|
|
266
|
+
|
|
267
|
+
#### `createDualSignatureKeys(agentId, profile, options?)`
|
|
268
|
+
|
|
269
|
+
Create both primary and secondary keys in one call. Requires `profile.secondarySignatureSuite`.
|
|
270
|
+
|
|
271
|
+
**Returns:** `{ primaryKey, secondaryKey }`
|
|
272
|
+
|
|
273
|
+
**Example:**
|
|
274
|
+
```typescript
|
|
275
|
+
const { primaryKey, secondaryKey } = await sdk.createDualSignatureKeys(
|
|
276
|
+
'agent-bridge',
|
|
277
|
+
{
|
|
278
|
+
primarySignatureSuite: 'ml-dsa-87',
|
|
279
|
+
secondarySignatureSuite: 'bsv-ecdsa-secp256k1',
|
|
280
|
+
}
|
|
281
|
+
);
|
|
282
|
+
```
|
|
283
|
+
|
|
284
|
+
---
|
|
285
|
+
|
|
286
|
+
#### `getOrCreateDualSignatureKeys(agentId, profile, options?)`
|
|
287
|
+
|
|
288
|
+
Idempotent version of the above; reuses active keys when present.
|
|
289
|
+
|
|
290
|
+
**Returns:** `{ primaryKey, secondaryKey }`
|
|
291
|
+
|
|
292
|
+
---
|
|
293
|
+
|
|
294
|
+
#### `signWithSuites(agentId, message, suites?)`
|
|
295
|
+
|
|
296
|
+
Sign once per suite. When `suites` is omitted, active primary + secondary are used.
|
|
297
|
+
|
|
298
|
+
**Returns:** `{ signatures: Array<{ suiteId, keyId, signature }> }`
|
|
299
|
+
|
|
300
|
+
**Example:**
|
|
301
|
+
```typescript
|
|
302
|
+
const message = new TextEncoder().encode('hybrid payload');
|
|
303
|
+
const { signatures } = await sdk.signWithSuites('agent-bridge', message);
|
|
304
|
+
```
|
|
305
|
+
|
|
306
|
+
---
|
|
307
|
+
|
|
308
|
+
#### `verifyWithSuites(agentId, message, signatures)`
|
|
309
|
+
|
|
310
|
+
Verify multiple signatures and get per-suite results.
|
|
311
|
+
|
|
312
|
+
**Returns:** `{ results: Array<{ suiteId, keyId, valid }>, allValid: boolean }`
|
|
313
|
+
|
|
314
|
+
**Example:**
|
|
315
|
+
```typescript
|
|
316
|
+
const verify = await sdk.verifyWithSuites('agent-bridge', message, signatures);
|
|
317
|
+
console.log(verify.allValid); // true when every suite verifies
|
|
318
|
+
```
|
|
319
|
+
---
|
|
320
|
+
|
|
321
|
+
---
|
|
322
|
+
|
|
323
|
+
## Types
|
|
324
|
+
|
|
325
|
+
### KeyRecord
|
|
326
|
+
|
|
327
|
+
Public key record (safe to expose, no private key).
|
|
328
|
+
|
|
329
|
+
```typescript
|
|
330
|
+
interface KeyRecord {
|
|
331
|
+
meta: KeyMeta; // Key metadata
|
|
332
|
+
publicKey: Uint8Array; // Public key bytes
|
|
333
|
+
}
|
|
334
|
+
```
|
|
335
|
+
|
|
336
|
+
### CreateKeyOptions
|
|
337
|
+
|
|
338
|
+
Options for key creation.
|
|
339
|
+
|
|
340
|
+
```typescript
|
|
341
|
+
interface CreateKeyOptions {
|
|
342
|
+
expiresAt?: string; // ISO 8601
|
|
343
|
+
usage?: Array<'signing' | 'encryption' | 'both'>; // Default: ['signing']
|
|
344
|
+
cryptoProfileVersion?: string; // Default: '1.0.0'
|
|
345
|
+
}
|
|
346
|
+
```
|
|
347
|
+
|
|
348
|
+
### CryptoProfile
|
|
349
|
+
|
|
350
|
+
Per-agent/module crypto configuration.
|
|
351
|
+
|
|
352
|
+
```typescript
|
|
353
|
+
interface CryptoProfile {
|
|
354
|
+
primarySignatureSuite: string; // e.g. 'ml-dsa-87'
|
|
355
|
+
secondarySignatureSuite?: string; // e.g. 'bsv-ecdsa-secp256k1' (hybrid)
|
|
356
|
+
keyEncapsulationSuite?: string; // e.g. 'ml-kem-768' (future)
|
|
357
|
+
}
|
|
358
|
+
```
|
|
359
|
+
|
|
360
|
+
---
|
|
361
|
+
|
|
362
|
+
## Usage Patterns
|
|
363
|
+
|
|
364
|
+
### Single Signature (PQ-only)
|
|
365
|
+
|
|
366
|
+
```typescript
|
|
367
|
+
// Agent uses only ML-DSA
|
|
368
|
+
const key = await sdk.createKey('agent-research', {
|
|
369
|
+
primarySignatureSuite: 'ml-dsa-87',
|
|
370
|
+
});
|
|
371
|
+
|
|
372
|
+
const message = new TextEncoder().encode('research results');
|
|
373
|
+
const signature = await sdk.signWithKey(key.meta.keyId, message);
|
|
374
|
+
```
|
|
375
|
+
|
|
376
|
+
### Hybrid Signature (ECDSA + PQ)
|
|
377
|
+
|
|
378
|
+
```typescript
|
|
379
|
+
// One call to create both keys
|
|
380
|
+
await sdk.getOrCreateDualSignatureKeys('agent-bridge', {
|
|
381
|
+
primarySignatureSuite: 'ml-dsa-87',
|
|
382
|
+
secondarySignatureSuite: 'bsv-ecdsa-secp256k1',
|
|
383
|
+
});
|
|
384
|
+
|
|
385
|
+
// Sign with both suites
|
|
386
|
+
const message = new TextEncoder().encode('bridge output');
|
|
387
|
+
const { signatures } = await sdk.signWithSuites('agent-bridge', message);
|
|
388
|
+
|
|
389
|
+
// Verify both signatures
|
|
390
|
+
const verified = await sdk.verifyWithSuites('agent-bridge', message, signatures);
|
|
391
|
+
console.log(verified.allValid); // true
|
|
392
|
+
```
|
|
393
|
+
|
|
394
|
+
### Agent Setup (Idempotent)
|
|
395
|
+
|
|
396
|
+
```typescript
|
|
397
|
+
// Safe to call on every startup
|
|
398
|
+
async function setupAgent(agentId: string) {
|
|
399
|
+
const key = await sdk.getOrCreateKey(agentId, {
|
|
400
|
+
primarySignatureSuite: 'ml-dsa-87',
|
|
401
|
+
}, {
|
|
402
|
+
expiresAt: new Date(Date.now() + 365 * 86400000).toISOString(),
|
|
403
|
+
});
|
|
404
|
+
|
|
405
|
+
return key;
|
|
406
|
+
}
|
|
407
|
+
```
|
|
408
|
+
|
|
409
|
+
### Monthly Key Rotation
|
|
410
|
+
|
|
411
|
+
```typescript
|
|
412
|
+
// Automated rotation job
|
|
413
|
+
async function monthlyRotation() {
|
|
414
|
+
const agents = ['agent-resonance', 'agent-schema', 'agent-validator'];
|
|
415
|
+
|
|
416
|
+
for (const agentId of agents) {
|
|
417
|
+
const newKeys = await sdk.rotateAgentKeys(agentId);
|
|
418
|
+
console.log(`✓ Rotated ${agentId}: ${newKeys.length} keys`);
|
|
419
|
+
}
|
|
420
|
+
}
|
|
421
|
+
```
|
|
422
|
+
|
|
423
|
+
---
|
|
424
|
+
|
|
425
|
+
## Benefits
|
|
426
|
+
|
|
427
|
+
### 🔒 Security
|
|
428
|
+
|
|
429
|
+
- **Private keys hidden** - Never exposed to calling code
|
|
430
|
+
- **Single audit point** - All key operations go through SDK
|
|
431
|
+
- **No key leakage** - Keys stay in KeyRegistry storage
|
|
432
|
+
- **Consistent hashing** - Suite-appropriate algorithms
|
|
433
|
+
|
|
434
|
+
### 🔄 Crypto Agility
|
|
435
|
+
|
|
436
|
+
- **Suite is config** - Change ML-DSA → Falcon without code changes
|
|
437
|
+
- **Profile-driven** - CryptoProfile defines agent's crypto setup
|
|
438
|
+
- **Easy migration** - Hybrid mode for ECDSA → PQ transition
|
|
439
|
+
|
|
440
|
+
### 🛠️ Developer Experience
|
|
441
|
+
|
|
442
|
+
- **Clean API** - 8 methods, easy to learn
|
|
443
|
+
- **Idempotent** - `getOrCreateKey` safe for retries
|
|
444
|
+
- **TypeScript** - Full type safety
|
|
445
|
+
- **Well-tested** - 23 tests covering all scenarios
|
|
446
|
+
|
|
447
|
+
### 📊 Operations
|
|
448
|
+
|
|
449
|
+
- **Centralized** - One SDK for all agents
|
|
450
|
+
- **Auditable** - Easy to track key operations
|
|
451
|
+
- **Scalable** - Storage backends (in-memory, file, KMS)
|
|
452
|
+
- **Rotation-friendly** - Built-in rotation support
|
|
453
|
+
|
|
454
|
+
---
|
|
455
|
+
|
|
456
|
+
## Best Practices
|
|
457
|
+
|
|
458
|
+
### ✅ Do
|
|
459
|
+
|
|
460
|
+
- Use SDK for **all** key operations
|
|
461
|
+
- Call `getOrCreateKey` for idempotent setup
|
|
462
|
+
- Set expiration dates on keys
|
|
463
|
+
- Rotate keys regularly (monthly/quarterly)
|
|
464
|
+
- Use `signWithKey` (not direct registry access)
|
|
465
|
+
|
|
466
|
+
### ❌ Don't
|
|
467
|
+
|
|
468
|
+
- Expose private keys to calling code
|
|
469
|
+
- Create keys manually outside SDK
|
|
470
|
+
- Hard-code suite IDs in business logic
|
|
471
|
+
- Skip key rotation
|
|
472
|
+
|
|
473
|
+
---
|
|
474
|
+
|
|
475
|
+
## Integration with Existing Code
|
|
476
|
+
|
|
477
|
+
### Before (Direct Registry)
|
|
478
|
+
|
|
479
|
+
```typescript
|
|
480
|
+
// Scattered key operations
|
|
481
|
+
const suite = globalRegistry.getSuite('ml-dsa-87');
|
|
482
|
+
const keypair = await suite.generateKeypair();
|
|
483
|
+
await keyRegistry.registerKey('agent-1:pk-1', keypair, 'ml-dsa-87');
|
|
484
|
+
|
|
485
|
+
const key = await keyRegistry.getKey('agent-1:pk-1');
|
|
486
|
+
const sig = await suite.sign(key.keypair.privateKey, message);
|
|
487
|
+
```
|
|
488
|
+
|
|
489
|
+
### After (SDK)
|
|
490
|
+
|
|
491
|
+
```typescript
|
|
492
|
+
// Clean, centralized
|
|
493
|
+
const key = await sdk.createKey('agent-1', {
|
|
494
|
+
primarySignatureSuite: 'ml-dsa-87',
|
|
495
|
+
});
|
|
496
|
+
|
|
497
|
+
const sig = await sdk.signWithKey(key.meta.keyId, message);
|
|
498
|
+
```
|
|
499
|
+
|
|
500
|
+
---
|
|
501
|
+
|
|
502
|
+
## What's Next?
|
|
503
|
+
|
|
504
|
+
- **File Storage** - Persistent key storage (not just in-memory)
|
|
505
|
+
- **KMS Integration** - AWS KMS, Azure Key Vault, HSM
|
|
506
|
+
- **Key Backup** - Encrypted backup/recovery procedures
|
|
507
|
+
- **Multi-tenant** - Isolate keys per tenant
|
|
508
|
+
- **Hardware Keys** - Support for non-exportable hardware keys
|
|
509
|
+
|
|
510
|
+
---
|
|
511
|
+
|
|
512
|
+
## Summary
|
|
513
|
+
|
|
514
|
+
**`@smartledger/keys` is the single source of truth for cryptographic keys:**
|
|
515
|
+
|
|
516
|
+
✅ 23 tests passing
|
|
517
|
+
✅ Private keys hidden from callers
|
|
518
|
+
✅ Crypto-agnostic API (suite is config)
|
|
519
|
+
✅ Idempotent operations
|
|
520
|
+
✅ Easy to audit
|
|
521
|
+
✅ Production-ready
|
|
522
|
+
|
|
523
|
+
**Use it for all key operations. Make crypto agility a reality.**
|
|
524
|
+
|
|
525
|
+
---
|
|
526
|
+
|
|
527
|
+
**See Also:**
|
|
528
|
+
- [Key Management Guide](../docs/KEY_MANAGEMENT_GUIDE.md) - Lower-level KeyRegistry details
|
|
529
|
+
- [Algorithm Selection Guide](../docs/ALGORITHM_SELECTION_GUIDE.md) - Which suite to use?
|
|
530
|
+
- [ECDSA Implementation Strategy](../docs/ECDSA_IMPLEMENTATION_STRATEGY.md) - Why Noble
|
|
531
|
+
|
|
532
|
+
---
|
|
533
|
+
|
|
534
|
+
**Version**: 1.0.0
|
|
535
|
+
**Last Updated**: November 28, 2025
|