@ajna-inc/vaults 0.1.3 → 0.1.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.
Files changed (2) hide show
  1. package/README.md +436 -0
  2. package/package.json +1 -1
package/README.md ADDED
@@ -0,0 +1,436 @@
1
+ # Post-Quantum Encrypted Vaults
2
+
3
+ Post-quantum encrypted vaults with DIDComm protocol for Credo. Client-side encryption using ML-KEM-768, AES-256-GCM, and Shamir secret sharing with P2P sharing via DIDComm messages.
4
+
5
+ ## Overview
6
+
7
+ The Vaults module provides secure, client-side encrypted storage with peer-to-peer sharing capabilities. All cryptographic operations happen locally on the agent - no server ever sees plaintext data. It supports multiple encryption suites including post-quantum ML-KEM-768, flexible access policies (passphrase, any-of, all-of, threshold), and optional external storage (S3) for large files.
8
+
9
+ ## Table of Contents
10
+
11
+ - [Features](#features)
12
+ - [How It Works](#how-it-works)
13
+ - [Encryption Suites](#encryption-suites)
14
+ - [Access Policies](#access-policies)
15
+ - [Protocol Flows](#protocol-flows)
16
+ - [Usage Examples](#usage-examples)
17
+ - [Installation](#installation)
18
+ - [Message Types](#message-types)
19
+ - [Event System](#event-system)
20
+ - [Security Considerations](#security-considerations)
21
+
22
+ ## Features
23
+
24
+ ### Encryption Suites
25
+ - **S3 Suite**: Passphrase-based encryption (Argon2id KDF + AES-256-GCM)
26
+ - **P1 Suite**: Post-quantum encryption (ML-KEM-768 + AES-256-GCM)
27
+ - **H1 Suite**: Hybrid mode (planned)
28
+
29
+ ### Access Policies
30
+ - **Passphrase**: Single-user encryption with passphrase-derived key
31
+ - **Any-of**: Multiple recipients, any one can decrypt independently
32
+ - **All-of**: Multiple recipients, all must cooperate to decrypt
33
+ - **Threshold**: Shamir secret sharing with configurable t-of-n threshold
34
+
35
+ ### Vault Operations
36
+ - Create, open, update, delete encrypted vaults
37
+ - List vaults and get metadata without decrypting
38
+ - KEM keypair generation and management
39
+ - Document signing vault workflows
40
+
41
+ ### DIDComm Protocol
42
+ - 15 message types for P2P vault sharing
43
+ - Request, grant, and deny access to vaults
44
+ - Threshold share request and distribution
45
+ - External storage operator support
46
+ - Problem reporting for error handling
47
+
48
+ ### Storage
49
+ - Inline storage in agent wallet
50
+ - External S3 storage for large files
51
+ - Configurable inline/external threshold
52
+ - Storage operator mode
53
+
54
+ ## How It Works
55
+
56
+ ### Architecture
57
+
58
+ ```
59
+ Application Code
60
+ |
61
+ VaultsApi
62
+ create / open / update / delete / list
63
+ KEM key mgmt / signing vaults
64
+ |
65
+ +----------------+----------------+
66
+ | | |
67
+ VaultService KEM Service SigningVaultSvc
68
+ | | |
69
+ +--------+-------+-------+--------+
70
+ | |
71
+ EncryptionSvc HPKEService
72
+ | |
73
+ WASM Crypto (30+ functions)
74
+ |
75
+ +----------+----------+
76
+ | | |
77
+ AEAD KEM Shamir
78
+ AES-256 ML-KEM-768 Secret
79
+ GCM Sharing
80
+ | | |
81
+ VaultRepository StorageService
82
+ | |
83
+ Agent Wallet S3 / External
84
+ ```
85
+
86
+ ### Encryption Flow
87
+
88
+ 1. **Passphrase vault**: Passphrase -> Argon2id KDF -> 256-bit CEK -> AES-256-GCM encrypt
89
+ 2. **Post-quantum vault**: ML-KEM-768 encapsulate -> shared secret -> HKDF -> CEK -> AES-256-GCM encrypt
90
+ 3. **Threshold vault**: CEK -> Shamir split into n shares -> wrap each share with recipient's KEM public key
91
+
92
+ ### Key Commitment
93
+
94
+ Every vault includes a key commitment (`kcmp`) in the header to verify the correct key was used during decryption, preventing key confusion attacks.
95
+
96
+ ## Encryption Suites
97
+
98
+ ### S3 Suite (Passphrase)
99
+
100
+ **KDF**: Argon2id (configurable memory and iterations)
101
+ **AEAD**: AES-256-GCM
102
+ **Use Case**: Personal vaults protected by a passphrase
103
+
104
+ - Passphrase is stretched via Argon2id with random salt
105
+ - Derived key encrypts data with AES-256-GCM
106
+ - Key commitment ensures correct passphrase verification
107
+
108
+ ### P1 Suite (Post-Quantum)
109
+
110
+ **KEM**: ML-KEM-768 (NIST PQC standard)
111
+ **AEAD**: AES-256-GCM
112
+ **Use Case**: Sharing vaults between agents with quantum-resistant security
113
+
114
+ - Recipient's ML-KEM public key encapsulates a shared secret
115
+ - Shared secret is expanded via HKDF to derive CEK
116
+ - CEK encrypts data with AES-256-GCM
117
+
118
+ ## Access Policies
119
+
120
+ ### Passphrase Policy
121
+ Single user, passphrase-derived encryption key.
122
+
123
+ ### Any-of Policy
124
+ Multiple recipients each get an independently-encrypted copy of the CEK wrapped with their KEM public key. Any single recipient can decrypt.
125
+
126
+ ### All-of Policy
127
+ All participants must cooperate. The CEK is split such that every participant's contribution is required for reconstruction.
128
+
129
+ ### Threshold Policy
130
+ Shamir secret sharing splits the CEK into `n` shares with a threshold of `t`. Any `t` shares can reconstruct the CEK. Each share is wrapped with the respective participant's KEM public key.
131
+
132
+ ## Protocol Flows
133
+
134
+ ### 1. Create and Open a Vault
135
+
136
+ ```
137
+ Agent
138
+ |-- create(passphrase, data) --> Argon2id + AES-256-GCM --> VaultRecord stored
139
+ |-- open(passphrase) --> derive key --> AES-256-GCM decrypt --> plaintext
140
+ ```
141
+
142
+ ### 2. Share Vault via DIDComm
143
+
144
+ ```
145
+ Owner Recipient
146
+ | |
147
+ | 1. GrantAccessMessage (header + CT) |
148
+ |--------------------------------------->|
149
+ | |
150
+ | 2. VaultStoredAckMessage |
151
+ |<---------------------------------------|
152
+ ```
153
+
154
+ ### 3. Request Access
155
+
156
+ ```
157
+ Requester Owner
158
+ | |
159
+ | 1. RequestAccessMessage |
160
+ |--------------------------------------->|
161
+ | |
162
+ | 2. GrantAccessMessage / DenyAccess |
163
+ |<---------------------------------------|
164
+ ```
165
+
166
+ ### 4. Threshold Reconstruction
167
+
168
+ ```
169
+ Requester ShareHolder1 ShareHolder2
170
+ | | |
171
+ | RequestShareMessage | |
172
+ |----------------------------->| |
173
+ | RequestShareMessage |
174
+ |--------------------------------------------->|
175
+ | | |
176
+ | ProvideShareMessage | |
177
+ |<-----------------------------| |
178
+ | ProvideShareMessage |
179
+ |<---------------------------------------------|
180
+ | |
181
+ | [Reconstruct CEK from t shares] |
182
+ ```
183
+
184
+ ### 5. Document Signing Vault
185
+
186
+ ```
187
+ Owner Signer
188
+ | |
189
+ | createSigningVault(doc, recipientDid) |
190
+ | shareSigningVault(connectionId) |
191
+ |--------------------------------------->|
192
+ | |
193
+ | openSigningVault()|
194
+ | sign document |
195
+ | returnSignedDocument() |
196
+ |<---------------------------------------|
197
+ ```
198
+
199
+ ## Usage Examples
200
+
201
+ ### Basic Vault Operations
202
+
203
+ ```typescript
204
+ import { Agent } from '@credo-ts/core'
205
+ import { VaultsModule } from '@ajna-inc/vaults'
206
+
207
+ const agent = new Agent({
208
+ config: { /* ... */ },
209
+ modules: {
210
+ vaults: new VaultsModule()
211
+ }
212
+ })
213
+
214
+ await agent.initialize()
215
+
216
+ // Create a vault
217
+ const vault = await agent.modules.vaults.create({
218
+ passphrase: 'my-secure-passphrase',
219
+ data: Buffer.from('sensitive document content'),
220
+ metadata: {
221
+ description: 'Contract draft',
222
+ tags: ['legal', 'draft']
223
+ }
224
+ })
225
+
226
+ // Open (decrypt) a vault
227
+ const plaintext = await agent.modules.vaults.open(vault.vaultId, {
228
+ passphrase: 'my-secure-passphrase'
229
+ })
230
+
231
+ // List all vaults
232
+ const vaults = await agent.modules.vaults.list()
233
+
234
+ // Get vault info without decrypting
235
+ const info = await agent.modules.vaults.getInfo(vault.vaultId)
236
+
237
+ // Update vault data
238
+ await agent.modules.vaults.update(vault.vaultId, {
239
+ passphrase: 'my-secure-passphrase',
240
+ data: Buffer.from('updated content')
241
+ })
242
+
243
+ // Delete a vault
244
+ await agent.modules.vaults.delete(vault.vaultId)
245
+ ```
246
+
247
+ ### KEM Key Management
248
+
249
+ ```typescript
250
+ // Generate an ML-KEM-768 keypair
251
+ const keypair = await agent.modules.vaults.generateKemKeypair()
252
+
253
+ // Store a peer's KEM public key
254
+ await agent.modules.vaults.storePeerKemKey(connectionId, peerPublicKey)
255
+
256
+ // Retrieve peer's key for encryption
257
+ const peerKey = await agent.modules.vaults.getPeerKemKey(connectionId)
258
+ ```
259
+
260
+ ### Signing Vault Workflow
261
+
262
+ ```typescript
263
+ // Owner creates a signing vault for a specific recipient
264
+ const signingVault = await agent.modules.vaults.createSigningVault({
265
+ data: documentBytes,
266
+ recipientDid: signerDid
267
+ })
268
+
269
+ // Owner shares the vault with the signer
270
+ await agent.modules.vaults.shareSigningVault(connectionId, signingVault.vaultId)
271
+
272
+ // Signer opens the vault
273
+ const doc = await agent.modules.vaults.openSigningVault(vaultId)
274
+
275
+ // Signer signs and returns the document
276
+ await agent.modules.vaults.returnSignedDocument(connectionId, {
277
+ vaultId,
278
+ signedData: signedDocBytes
279
+ })
280
+ ```
281
+
282
+ ### S3 Storage Configuration
283
+
284
+ ```typescript
285
+ const agent = new Agent({
286
+ modules: {
287
+ vaults: new VaultsModule({
288
+ storage: {
289
+ bucket: 'my-vault-bucket',
290
+ region: 'us-east-1',
291
+ accessKeyId: 'AKIA...',
292
+ secretAccessKey: '...'
293
+ },
294
+ inlineThreshold: 1024 * 1024, // 1MB - larger vaults go to S3
295
+ operatorMode: false
296
+ })
297
+ }
298
+ })
299
+ ```
300
+
301
+ ## Installation
302
+
303
+ ```bash
304
+ pnpm add @ajna-inc/vaults
305
+ ```
306
+
307
+ ## Message Types
308
+
309
+ | Message | Purpose |
310
+ |---------|---------|
311
+ | `CreateVaultMessage` | Notify of vault creation |
312
+ | `UpdateVaultMessage` | Update vault data |
313
+ | `DeleteVaultMessage` | Delete vault |
314
+ | `RequestAccessMessage` | Request access to a vault |
315
+ | `GrantAccessMessage` | Grant access with encrypted data |
316
+ | `DenyAccessMessage` | Deny access request |
317
+ | `RequestShareMessage` | Request a threshold share |
318
+ | `ProvideShareMessage` | Provide a threshold share |
319
+ | `DenyShareMessage` | Deny share request |
320
+ | `StoreVaultMessage` | Store vault at storage service |
321
+ | `VaultStoredAckMessage` | Confirm storage |
322
+ | `RetrieveVaultMessage` | Retrieve vault from storage |
323
+ | `VaultDataMessage` | Vault data transmission |
324
+ | `VaultReferenceMessage` | Storage reference |
325
+ | `VaultProblemReportMessage` | Error reporting |
326
+
327
+ ## Event System
328
+
329
+ Subscribe to vault lifecycle events:
330
+
331
+ ```typescript
332
+ import { VaultEventTypes } from '@ajna-inc/vaults'
333
+
334
+ agent.events.on(VaultEventTypes.VaultCreated, (event) => {
335
+ console.log('Vault created:', event.payload.vaultId)
336
+ })
337
+
338
+ agent.events.on(VaultEventTypes.AccessGranted, (event) => {
339
+ console.log('Access granted to vault:', event.payload.vaultId)
340
+ })
341
+
342
+ agent.events.on(VaultEventTypes.ThresholdMet, (event) => {
343
+ console.log('Threshold met, vault can be decrypted')
344
+ })
345
+ ```
346
+
347
+ ### Available Events
348
+
349
+ - `VaultCreated`, `VaultOpened`, `VaultUpdated`, `VaultDeleted`
350
+ - `VaultShared`, `VaultError`
351
+ - `AccessRequested`, `AccessGranted`, `AccessDenied`
352
+ - `ThresholdMet`, `ShareProvided`, `ShareRequested`, `ShareDenied`
353
+ - `StorageAllocated`, `StorageUploaded`, `StorageDownloaded`
354
+
355
+ ## Security Considerations
356
+
357
+ ### Cryptographic Primitives
358
+ - **ML-KEM-768**: NIST post-quantum standard for key encapsulation
359
+ - **AES-256-GCM**: Authenticated encryption with 256-bit keys
360
+ - **Argon2id**: Memory-hard KDF resistant to GPU/ASIC attacks
361
+ - **Shamir Secret Sharing**: Information-theoretically secure threshold scheme
362
+ - **Key Commitment**: Prevents key confusion and related attacks
363
+
364
+ ### Best Practices
365
+ 1. Use strong passphrases for passphrase-based vaults
366
+ 2. Configure adequate Argon2id memory (default is reasonable for most use cases)
367
+ 3. Store KEM private keys securely in the agent wallet
368
+ 4. Verify key commitments are checked on every decryption
369
+ 5. Use threshold policies for high-value data requiring multi-party authorization
370
+ 6. Consider external storage for large files to keep wallet lean
371
+
372
+ ### Client-Side Only
373
+ All encryption and decryption happens locally on the agent. No server, mediator, or storage operator ever has access to plaintext data or encryption keys.
374
+
375
+ ## Vault Header Format
376
+
377
+ ```typescript
378
+ {
379
+ v: 1, // Protocol version
380
+ suite: 'S3' | 'P1' | 'H1', // Encryption suite
381
+ aead: 'AES-256-GCM', // AEAD algorithm
382
+ docId: string, // Document identifier
383
+ vaultId: string, // Vault identifier
384
+ epoch: number, // Version counter
385
+ nonce: string, // base64url nonce
386
+ kcmp: string, // Key commitment
387
+ salt?: string, // Argon2id salt (S3 suite)
388
+ argon2?: { // KDF parameters (S3 suite)
389
+ memory: number,
390
+ iterations: number
391
+ },
392
+ policy?: { // Access policy
393
+ mode: 'passphrase' | 'any-of' | 'all-of' | 'threshold'
394
+ },
395
+ recipients?: RecipientWrap[], // Per-recipient wrapped keys
396
+ shares?: ThresholdShare[], // Threshold shares
397
+ metadata?: { // Optional metadata
398
+ description?: string,
399
+ tags?: string[]
400
+ }
401
+ }
402
+ ```
403
+
404
+ ## Error Handling
405
+
406
+ ```typescript
407
+ import { VaultError, BadSuiteError, DecryptKemError, DecryptAeadError, PolicyError } from '@ajna-inc/vaults'
408
+
409
+ try {
410
+ await agent.modules.vaults.open(vaultId, { passphrase: 'wrong' })
411
+ } catch (error) {
412
+ if (error instanceof DecryptAeadError) {
413
+ console.log('Wrong passphrase or corrupted data')
414
+ } else if (error instanceof DecryptKemError) {
415
+ console.log('KEM decapsulation failed - wrong key')
416
+ } else if (error instanceof PolicyError) {
417
+ console.log('Access policy violation')
418
+ } else if (error instanceof BadSuiteError) {
419
+ console.log('Unknown encryption suite')
420
+ }
421
+ }
422
+ ```
423
+
424
+ ## Testing
425
+
426
+ ```bash
427
+ # Run vault tests
428
+ pnpm test
429
+
430
+ # Watch mode
431
+ pnpm test:watch
432
+ ```
433
+
434
+ ## License
435
+
436
+ Apache-2.0
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@ajna-inc/vaults",
3
- "version": "0.1.3",
3
+ "version": "0.1.4",
4
4
  "description": "Post-quantum encrypted vaults with DIDComm protocol for Credo",
5
5
  "license": "Apache-2.0",
6
6
  "main": "build/index.js",