@cryptforge/cryptography 0.2.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/README.md +331 -0
- package/dist/index.d.mts +125 -0
- package/dist/index.d.ts +125 -0
- package/dist/index.js +119 -0
- package/dist/index.mjs +91 -0
- package/dist/server.d.mts +172 -0
- package/dist/server.d.ts +172 -0
- package/dist/server.js +213 -0
- package/dist/server.mjs +174 -0
- package/package.json +50 -0
package/README.md
ADDED
|
@@ -0,0 +1,331 @@
|
|
|
1
|
+
# @cryptforge/cryptography
|
|
2
|
+
|
|
3
|
+
Cryptographic operations for CryptForge applications with support for both client (browser) and server (Node.js) environments.
|
|
4
|
+
|
|
5
|
+
## Features
|
|
6
|
+
|
|
7
|
+
- 🔐 **Encryption & Decryption**: Symmetric and asymmetric encryption operations
|
|
8
|
+
- 🌐 **Cross-Platform**: Separate implementations for browser and Node.js environments
|
|
9
|
+
- 🔑 **Key Management**: Utilities for key generation, derivation, and storage
|
|
10
|
+
- 🛡️ **Type-Safe**: Full TypeScript support with comprehensive type definitions
|
|
11
|
+
- 📦 **Tree-Shakeable**: Optimized bundle sizes with separate entry points
|
|
12
|
+
|
|
13
|
+
## Installation
|
|
14
|
+
|
|
15
|
+
```bash
|
|
16
|
+
npm install @cryptforge/cryptography
|
|
17
|
+
```
|
|
18
|
+
|
|
19
|
+
## Usage
|
|
20
|
+
|
|
21
|
+
### Client/Browser Usage
|
|
22
|
+
|
|
23
|
+
The client export provides `CryptoBrowser`, which uses the Web Crypto API for encryption/decryption:
|
|
24
|
+
|
|
25
|
+
```typescript
|
|
26
|
+
import { CryptoBrowser } from '@cryptforge/cryptography';
|
|
27
|
+
import { createAuthClient } from '@cryptforge/auth';
|
|
28
|
+
|
|
29
|
+
// Create auth client and unlock wallet
|
|
30
|
+
const auth = createAuthClient();
|
|
31
|
+
await auth.unlock({ password: 'your-password', chainId: 'ethereum' });
|
|
32
|
+
|
|
33
|
+
// Derive encryption key from wallet
|
|
34
|
+
const encryptionKey = await auth.deriveDataEncryptionKey({
|
|
35
|
+
purpose: 'my-app-data',
|
|
36
|
+
version: 1,
|
|
37
|
+
});
|
|
38
|
+
|
|
39
|
+
// Create CryptoBrowser instance
|
|
40
|
+
const crypto = new CryptoBrowser();
|
|
41
|
+
crypto.setEncryptionKey(encryptionKey);
|
|
42
|
+
|
|
43
|
+
// Encrypt data
|
|
44
|
+
const encrypted = await crypto.encrypt()('Hello, World!');
|
|
45
|
+
console.log('Encrypted:', encrypted); // Base64-encoded string
|
|
46
|
+
|
|
47
|
+
// Decrypt data
|
|
48
|
+
const decrypted = await crypto.decrypt()(encrypted);
|
|
49
|
+
console.log('Decrypted:', decrypted); // "Hello, World!"
|
|
50
|
+
```
|
|
51
|
+
|
|
52
|
+
### Using with Callback Pattern
|
|
53
|
+
|
|
54
|
+
```typescript
|
|
55
|
+
import { CryptoBrowser } from '@cryptforge/cryptography';
|
|
56
|
+
|
|
57
|
+
// Create instance with dynamic key retrieval
|
|
58
|
+
const crypto = new CryptoBrowser();
|
|
59
|
+
|
|
60
|
+
// Set key when wallet is unlocked
|
|
61
|
+
const unlockWallet = async () => {
|
|
62
|
+
await auth.unlock({ password: 'password', chainId: 'ethereum' });
|
|
63
|
+
const key = await auth.deriveDataEncryptionKey({
|
|
64
|
+
purpose: 'my-app-data',
|
|
65
|
+
});
|
|
66
|
+
crypto.setEncryptionKey(key);
|
|
67
|
+
};
|
|
68
|
+
|
|
69
|
+
// Use encryption after unlocking
|
|
70
|
+
await unlockWallet();
|
|
71
|
+
const encrypted = await crypto.encrypt()('Secret message');
|
|
72
|
+
```
|
|
73
|
+
|
|
74
|
+
### Encryption Details
|
|
75
|
+
|
|
76
|
+
`CryptoBrowser` uses **AES-256-GCM** encryption:
|
|
77
|
+
|
|
78
|
+
- **Algorithm**: AES-GCM (Galois/Counter Mode)
|
|
79
|
+
- **Key Size**: 256 bits
|
|
80
|
+
- **IV Size**: 12 bytes (96 bits)
|
|
81
|
+
- **Output Format**: Base64-encoded (IV + ciphertext)
|
|
82
|
+
|
|
83
|
+
The initialization vector (IV) is randomly generated for each encryption operation and prepended to the ciphertext. This ensures each encryption produces a unique result, even for the same plaintext.
|
|
84
|
+
|
|
85
|
+
### Server/Node.js Usage
|
|
86
|
+
|
|
87
|
+
The server export provides both browser and server-specific functionality:
|
|
88
|
+
|
|
89
|
+
```typescript
|
|
90
|
+
import { CryptoServer } from '@cryptforge/cryptography/server';
|
|
91
|
+
|
|
92
|
+
// CryptoServer provides Node.js-specific crypto operations
|
|
93
|
+
const crypto = new CryptoServer();
|
|
94
|
+
|
|
95
|
+
// Server-side encryption/decryption
|
|
96
|
+
// (Implementation similar to CryptoBrowser but optimized for Node.js)
|
|
97
|
+
```
|
|
98
|
+
|
|
99
|
+
**Note**: The server implementation is currently under development. For now, you can use `CryptoBrowser` in Node.js environments as the Web Crypto API is available in Node.js v15+.
|
|
100
|
+
|
|
101
|
+
## Architecture
|
|
102
|
+
|
|
103
|
+
This package maintains strict separation between client and server environments:
|
|
104
|
+
|
|
105
|
+
- **`src/client/`**: Browser-compatible implementations using Web Crypto API
|
|
106
|
+
- **`src/server/`**: Node.js-specific implementations using Node's crypto module
|
|
107
|
+
- **`src/shared/`**: Environment-agnostic utilities and helpers
|
|
108
|
+
- **`src/types/`**: Shared TypeScript type definitions
|
|
109
|
+
|
|
110
|
+
## Entry Points
|
|
111
|
+
|
|
112
|
+
- **`@cryptforge/cryptography`**: Client/browser-safe exports (default)
|
|
113
|
+
- **`@cryptforge/cryptography/server`**: Server-specific exports + client exports
|
|
114
|
+
|
|
115
|
+
## API Reference
|
|
116
|
+
|
|
117
|
+
### CryptoBrowser
|
|
118
|
+
|
|
119
|
+
Browser-compatible cryptographic operations using Web Crypto API.
|
|
120
|
+
|
|
121
|
+
#### Constructor
|
|
122
|
+
|
|
123
|
+
```typescript
|
|
124
|
+
new CryptoBrowser()
|
|
125
|
+
```
|
|
126
|
+
|
|
127
|
+
Creates a new CryptoBrowser instance. The encryption key must be set before using encrypt/decrypt methods.
|
|
128
|
+
|
|
129
|
+
#### Methods
|
|
130
|
+
|
|
131
|
+
##### `setEncryptionKey(key: CryptoKey): void`
|
|
132
|
+
|
|
133
|
+
Sets the encryption key for subsequent operations. Key must be:
|
|
134
|
+
- Algorithm: AES-GCM
|
|
135
|
+
- Length: 256 bits
|
|
136
|
+
- Usages: `['encrypt', 'decrypt']`
|
|
137
|
+
|
|
138
|
+
```typescript
|
|
139
|
+
const key = await auth.deriveDataEncryptionKey({
|
|
140
|
+
purpose: 'my-data',
|
|
141
|
+
algorithm: 'AES-GCM',
|
|
142
|
+
length: 256,
|
|
143
|
+
});
|
|
144
|
+
|
|
145
|
+
crypto.setEncryptionKey(key);
|
|
146
|
+
```
|
|
147
|
+
|
|
148
|
+
##### `encrypt(): (data: string) => Promise<string>`
|
|
149
|
+
|
|
150
|
+
Returns a curried function that encrypts string data.
|
|
151
|
+
|
|
152
|
+
```typescript
|
|
153
|
+
const encryptFn = crypto.encrypt();
|
|
154
|
+
const encrypted = await encryptFn('Hello, World!');
|
|
155
|
+
```
|
|
156
|
+
|
|
157
|
+
**Returns**: Base64-encoded string containing IV + ciphertext
|
|
158
|
+
|
|
159
|
+
##### `decrypt(): (encryptedData: string) => Promise<string>`
|
|
160
|
+
|
|
161
|
+
Returns a curried function that decrypts encrypted string data.
|
|
162
|
+
|
|
163
|
+
```typescript
|
|
164
|
+
const decryptFn = crypto.decrypt();
|
|
165
|
+
const decrypted = await decryptFn(encrypted);
|
|
166
|
+
```
|
|
167
|
+
|
|
168
|
+
**Throws**: Error if decryption fails (wrong key, corrupted data, etc.)
|
|
169
|
+
|
|
170
|
+
## Examples
|
|
171
|
+
|
|
172
|
+
### Basic Encryption/Decryption
|
|
173
|
+
|
|
174
|
+
```typescript
|
|
175
|
+
import { CryptoBrowser } from '@cryptforge/cryptography';
|
|
176
|
+
import { createAuthClient } from '@cryptforge/auth';
|
|
177
|
+
|
|
178
|
+
// Setup
|
|
179
|
+
const auth = createAuthClient();
|
|
180
|
+
await auth.unlock({ password: 'password', chainId: 'ethereum' });
|
|
181
|
+
|
|
182
|
+
const key = await auth.deriveDataEncryptionKey({
|
|
183
|
+
purpose: 'user-documents',
|
|
184
|
+
});
|
|
185
|
+
|
|
186
|
+
const crypto = new CryptoBrowser();
|
|
187
|
+
crypto.setEncryptionKey(key);
|
|
188
|
+
|
|
189
|
+
// Encrypt
|
|
190
|
+
const data = JSON.stringify({ title: 'My Document', content: 'Secret data' });
|
|
191
|
+
const encrypted = await crypto.encrypt()(data);
|
|
192
|
+
|
|
193
|
+
// Store encrypted data
|
|
194
|
+
localStorage.setItem('myDocument', encrypted);
|
|
195
|
+
|
|
196
|
+
// Later... decrypt
|
|
197
|
+
const stored = localStorage.getItem('myDocument');
|
|
198
|
+
const decrypted = await crypto.decrypt()(stored!);
|
|
199
|
+
const document = JSON.parse(decrypted);
|
|
200
|
+
```
|
|
201
|
+
|
|
202
|
+
### Encrypting Multiple Items
|
|
203
|
+
|
|
204
|
+
```typescript
|
|
205
|
+
const encrypt = crypto.encrypt();
|
|
206
|
+
const decrypt = crypto.decrypt();
|
|
207
|
+
|
|
208
|
+
// Encrypt multiple items
|
|
209
|
+
const items = ['Item 1', 'Item 2', 'Item 3'];
|
|
210
|
+
const encryptedItems = await Promise.all(
|
|
211
|
+
items.map(item => encrypt(item))
|
|
212
|
+
);
|
|
213
|
+
|
|
214
|
+
// Decrypt multiple items
|
|
215
|
+
const decryptedItems = await Promise.all(
|
|
216
|
+
encryptedItems.map(item => decrypt(item))
|
|
217
|
+
);
|
|
218
|
+
```
|
|
219
|
+
|
|
220
|
+
### Error Handling
|
|
221
|
+
|
|
222
|
+
```typescript
|
|
223
|
+
try {
|
|
224
|
+
const encrypted = await crypto.encrypt()('Secret data');
|
|
225
|
+
const decrypted = await crypto.decrypt()(encrypted);
|
|
226
|
+
} catch (error) {
|
|
227
|
+
if (error.message.includes('Encryption key not available')) {
|
|
228
|
+
// Key not set - unlock wallet first
|
|
229
|
+
await auth.unlock({ password: 'password' });
|
|
230
|
+
const key = await auth.deriveDataEncryptionKey({ purpose: 'data' });
|
|
231
|
+
crypto.setEncryptionKey(key);
|
|
232
|
+
} else {
|
|
233
|
+
// Decryption failed - wrong key or corrupted data
|
|
234
|
+
console.error('Decryption failed:', error);
|
|
235
|
+
}
|
|
236
|
+
}
|
|
237
|
+
```
|
|
238
|
+
|
|
239
|
+
### With Automerge Documents
|
|
240
|
+
|
|
241
|
+
Perfect for encrypting Automerge documents:
|
|
242
|
+
|
|
243
|
+
```typescript
|
|
244
|
+
import { CryptoBrowser } from '@cryptforge/cryptography';
|
|
245
|
+
import * as Automerge from '@automerge/automerge';
|
|
246
|
+
|
|
247
|
+
// Create Automerge document
|
|
248
|
+
let doc = Automerge.init();
|
|
249
|
+
doc = Automerge.change(doc, doc => {
|
|
250
|
+
doc.notes = 'Secret notes';
|
|
251
|
+
});
|
|
252
|
+
|
|
253
|
+
// Encrypt document
|
|
254
|
+
const docBytes = Automerge.save(doc);
|
|
255
|
+
const docString = btoa(String.fromCharCode(...docBytes));
|
|
256
|
+
const encrypted = await crypto.encrypt()(docString);
|
|
257
|
+
|
|
258
|
+
// Store encrypted document
|
|
259
|
+
await storage.save('doc_id', encrypted);
|
|
260
|
+
|
|
261
|
+
// Later... decrypt and load
|
|
262
|
+
const stored = await storage.load('doc_id');
|
|
263
|
+
const decrypted = await crypto.decrypt()(stored);
|
|
264
|
+
const docBytesRestored = Uint8Array.from(atob(decrypted), c => c.charCodeAt(0));
|
|
265
|
+
const restoredDoc = Automerge.load(docBytesRestored);
|
|
266
|
+
```
|
|
267
|
+
|
|
268
|
+
## Security Considerations
|
|
269
|
+
|
|
270
|
+
### Best Practices
|
|
271
|
+
|
|
272
|
+
1. **Key Management**: Never hardcode encryption keys. Always derive them from user credentials or secure key storage.
|
|
273
|
+
|
|
274
|
+
2. **Key Rotation**: Use the `version` parameter in `deriveDataEncryptionKey` to support key rotation:
|
|
275
|
+
|
|
276
|
+
```typescript
|
|
277
|
+
// Old data encrypted with v1
|
|
278
|
+
const keyV1 = await auth.deriveDataEncryptionKey({ purpose: 'data', version: 1 });
|
|
279
|
+
|
|
280
|
+
// New data encrypted with v2
|
|
281
|
+
const keyV2 = await auth.deriveDataEncryptionKey({ purpose: 'data', version: 2 });
|
|
282
|
+
```
|
|
283
|
+
|
|
284
|
+
3. **Secure Key Storage**: The encryption key is kept in memory only. When the user locks their wallet, the key is cleared.
|
|
285
|
+
|
|
286
|
+
4. **No Key Export**: Keys are created with `extractable: false` by default, preventing them from being exported.
|
|
287
|
+
|
|
288
|
+
### What's Protected
|
|
289
|
+
|
|
290
|
+
✅ **Data at Rest** - Encrypted data stored in IndexedDB, localStorage, or disk
|
|
291
|
+
✅ **Data in Transit** - Can encrypt before sending over network
|
|
292
|
+
✅ **Memory Safety** - Keys cleared when wallet locks
|
|
293
|
+
|
|
294
|
+
### What's NOT Protected
|
|
295
|
+
|
|
296
|
+
❌ **Data in Use** - Decrypted data in application memory
|
|
297
|
+
❌ **Side Channels** - Timing attacks, power analysis
|
|
298
|
+
❌ **Compromised Device** - Malware, keyloggers
|
|
299
|
+
|
|
300
|
+
Always follow security best practices for your specific use case.
|
|
301
|
+
|
|
302
|
+
## Cross-Platform Compatibility
|
|
303
|
+
|
|
304
|
+
This package uses **AES-256-GCM**, which is supported across all platforms:
|
|
305
|
+
|
|
306
|
+
| Platform | Support | Notes |
|
|
307
|
+
|----------|---------|-------|
|
|
308
|
+
| **Browser** | ✅ | Web Crypto API (all modern browsers) |
|
|
309
|
+
| **Node.js** | ✅ | Native crypto module (v15+) |
|
|
310
|
+
| **Electron** | ✅ | Both main and renderer processes |
|
|
311
|
+
| **React Native** | ✅ | Via polyfills |
|
|
312
|
+
| **Web Workers** | ✅ | Web Crypto API available |
|
|
313
|
+
|
|
314
|
+
## Development
|
|
315
|
+
|
|
316
|
+
```bash
|
|
317
|
+
# Build the package
|
|
318
|
+
npm run build
|
|
319
|
+
|
|
320
|
+
# Run tests
|
|
321
|
+
npm test
|
|
322
|
+
```
|
|
323
|
+
|
|
324
|
+
## Related Packages
|
|
325
|
+
|
|
326
|
+
- **[@cryptforge/auth](../auth)** - Key derivation using `deriveDataEncryptionKey()`
|
|
327
|
+
- **[@cryptforge/core](../core)** - Core types and interfaces
|
|
328
|
+
|
|
329
|
+
## License
|
|
330
|
+
|
|
331
|
+
MIT
|
package/dist/index.d.mts
ADDED
|
@@ -0,0 +1,125 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Generic interface for cryptographic operations.
|
|
3
|
+
*
|
|
4
|
+
* @template TKey - The encryption key type (CryptoKey for browser, Buffer for Node.js)
|
|
5
|
+
*/
|
|
6
|
+
interface CryptoOperations<TKey = unknown> {
|
|
7
|
+
/**
|
|
8
|
+
* Encryption key for all document operations.
|
|
9
|
+
* All documents are encrypted at rest using this key.
|
|
10
|
+
* Browser implementations use CryptoKey, Node.js implementations use Buffer.
|
|
11
|
+
*/
|
|
12
|
+
setEncryptionKey?: (key: TKey) => void;
|
|
13
|
+
/**
|
|
14
|
+
* Creates an encryption function that encrypts string data using AES-GCM encryption.
|
|
15
|
+
* Returns a curried function that uses the instance's encryption key from `setEncryptionKey`.
|
|
16
|
+
*
|
|
17
|
+
* The returned function signature:
|
|
18
|
+
* - `data`: The plaintext string to encrypt
|
|
19
|
+
* - Returns: A promise resolving to the encrypted data as a string
|
|
20
|
+
*/
|
|
21
|
+
encrypt: () => (data: string) => Promise<string>;
|
|
22
|
+
/**
|
|
23
|
+
* Creates a decryption function that decrypts encrypted string data using AES-GCM decryption.
|
|
24
|
+
* Returns a curried function that uses the instance's encryption key from `setEncryptionKey`.
|
|
25
|
+
*
|
|
26
|
+
* The returned function signature:
|
|
27
|
+
* - `encryptedData`: The encrypted string data to decrypt
|
|
28
|
+
* - Returns: A promise resolving to the decrypted plaintext string
|
|
29
|
+
*/
|
|
30
|
+
decrypt: () => (encryptedData: string) => Promise<string>;
|
|
31
|
+
}
|
|
32
|
+
/**
|
|
33
|
+
* Configuration options for cryptographic operations
|
|
34
|
+
*/
|
|
35
|
+
interface CryptoConfig {
|
|
36
|
+
/**
|
|
37
|
+
* Algorithm to use for encryption/decryption
|
|
38
|
+
*/
|
|
39
|
+
algorithm?: string;
|
|
40
|
+
/**
|
|
41
|
+
* Key derivation iterations (if applicable)
|
|
42
|
+
*/
|
|
43
|
+
iterations?: number;
|
|
44
|
+
}
|
|
45
|
+
/**
|
|
46
|
+
* Result of an encryption operation
|
|
47
|
+
*/
|
|
48
|
+
interface EncryptionResult {
|
|
49
|
+
/**
|
|
50
|
+
* Encrypted data
|
|
51
|
+
*/
|
|
52
|
+
ciphertext: Uint8Array;
|
|
53
|
+
/**
|
|
54
|
+
* Initialization vector or nonce
|
|
55
|
+
*/
|
|
56
|
+
iv: Uint8Array;
|
|
57
|
+
/**
|
|
58
|
+
* Authentication tag (for authenticated encryption)
|
|
59
|
+
*/
|
|
60
|
+
tag?: Uint8Array;
|
|
61
|
+
}
|
|
62
|
+
/**
|
|
63
|
+
* Result of a decryption operation
|
|
64
|
+
*/
|
|
65
|
+
interface DecryptionResult {
|
|
66
|
+
/**
|
|
67
|
+
* Decrypted plaintext data
|
|
68
|
+
*/
|
|
69
|
+
plaintext: Uint8Array;
|
|
70
|
+
}
|
|
71
|
+
/**
|
|
72
|
+
* Key pair for asymmetric cryptography
|
|
73
|
+
*/
|
|
74
|
+
interface KeyPair {
|
|
75
|
+
/**
|
|
76
|
+
* Public key
|
|
77
|
+
*/
|
|
78
|
+
publicKey: Uint8Array;
|
|
79
|
+
/**
|
|
80
|
+
* Private key
|
|
81
|
+
*/
|
|
82
|
+
privateKey: Uint8Array;
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
/**
|
|
86
|
+
* Browser implementation of cryptographic operations using Web Crypto API.
|
|
87
|
+
* Uses CryptoKey for encryption/decryption operations.
|
|
88
|
+
*/
|
|
89
|
+
declare class CryptoBrowser implements CryptoOperations<CryptoKey> {
|
|
90
|
+
private encryptionKey?;
|
|
91
|
+
/**
|
|
92
|
+
* Creates a new CryptoBrowser instance.
|
|
93
|
+
* @param getEncryptionKey - Function that returns the encryption key for this instance
|
|
94
|
+
*/
|
|
95
|
+
constructor();
|
|
96
|
+
/**
|
|
97
|
+
* Sets the encryption key for the encrypt and decrypt operations.
|
|
98
|
+
* Validates that the key is AES-GCM with 256-bit length for cross-platform compatibility.
|
|
99
|
+
*
|
|
100
|
+
* @param key - The encryption key to set (must be AES-GCM 256-bit)
|
|
101
|
+
* @throws {Error} If key is not AES-GCM or not 256-bit
|
|
102
|
+
*/
|
|
103
|
+
setEncryptionKey: (key: CryptoKey) => void;
|
|
104
|
+
/**
|
|
105
|
+
* Creates an encryption function that encrypts string data using AES-GCM encryption.
|
|
106
|
+
* Uses a random initialization vector (IV) for each encryption operation.
|
|
107
|
+
* The IV is prepended to the ciphertext and the result is base64-encoded.
|
|
108
|
+
* Uses the encryption key provided via the `setEncryptionKey` method.
|
|
109
|
+
*
|
|
110
|
+
* @returns A curried function that encrypts data
|
|
111
|
+
*/
|
|
112
|
+
encrypt: () => (data: string) => Promise<string>;
|
|
113
|
+
/**
|
|
114
|
+
* Creates a decryption function that decrypts encrypted string data using AES-GCM decryption.
|
|
115
|
+
* Extracts the IV from the beginning of the encrypted data and uses it to decrypt the ciphertext.
|
|
116
|
+
* Uses the encryption key provided via the constructor.
|
|
117
|
+
*
|
|
118
|
+
* @returns A curried function that decrypts data
|
|
119
|
+
*/
|
|
120
|
+
decrypt: () => (encryptedData: string) => Promise<string>;
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
declare const version = "0.1.0";
|
|
124
|
+
|
|
125
|
+
export { CryptoBrowser, type CryptoConfig, type CryptoOperations, type DecryptionResult, type EncryptionResult, type KeyPair, version };
|
package/dist/index.d.ts
ADDED
|
@@ -0,0 +1,125 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Generic interface for cryptographic operations.
|
|
3
|
+
*
|
|
4
|
+
* @template TKey - The encryption key type (CryptoKey for browser, Buffer for Node.js)
|
|
5
|
+
*/
|
|
6
|
+
interface CryptoOperations<TKey = unknown> {
|
|
7
|
+
/**
|
|
8
|
+
* Encryption key for all document operations.
|
|
9
|
+
* All documents are encrypted at rest using this key.
|
|
10
|
+
* Browser implementations use CryptoKey, Node.js implementations use Buffer.
|
|
11
|
+
*/
|
|
12
|
+
setEncryptionKey?: (key: TKey) => void;
|
|
13
|
+
/**
|
|
14
|
+
* Creates an encryption function that encrypts string data using AES-GCM encryption.
|
|
15
|
+
* Returns a curried function that uses the instance's encryption key from `setEncryptionKey`.
|
|
16
|
+
*
|
|
17
|
+
* The returned function signature:
|
|
18
|
+
* - `data`: The plaintext string to encrypt
|
|
19
|
+
* - Returns: A promise resolving to the encrypted data as a string
|
|
20
|
+
*/
|
|
21
|
+
encrypt: () => (data: string) => Promise<string>;
|
|
22
|
+
/**
|
|
23
|
+
* Creates a decryption function that decrypts encrypted string data using AES-GCM decryption.
|
|
24
|
+
* Returns a curried function that uses the instance's encryption key from `setEncryptionKey`.
|
|
25
|
+
*
|
|
26
|
+
* The returned function signature:
|
|
27
|
+
* - `encryptedData`: The encrypted string data to decrypt
|
|
28
|
+
* - Returns: A promise resolving to the decrypted plaintext string
|
|
29
|
+
*/
|
|
30
|
+
decrypt: () => (encryptedData: string) => Promise<string>;
|
|
31
|
+
}
|
|
32
|
+
/**
|
|
33
|
+
* Configuration options for cryptographic operations
|
|
34
|
+
*/
|
|
35
|
+
interface CryptoConfig {
|
|
36
|
+
/**
|
|
37
|
+
* Algorithm to use for encryption/decryption
|
|
38
|
+
*/
|
|
39
|
+
algorithm?: string;
|
|
40
|
+
/**
|
|
41
|
+
* Key derivation iterations (if applicable)
|
|
42
|
+
*/
|
|
43
|
+
iterations?: number;
|
|
44
|
+
}
|
|
45
|
+
/**
|
|
46
|
+
* Result of an encryption operation
|
|
47
|
+
*/
|
|
48
|
+
interface EncryptionResult {
|
|
49
|
+
/**
|
|
50
|
+
* Encrypted data
|
|
51
|
+
*/
|
|
52
|
+
ciphertext: Uint8Array;
|
|
53
|
+
/**
|
|
54
|
+
* Initialization vector or nonce
|
|
55
|
+
*/
|
|
56
|
+
iv: Uint8Array;
|
|
57
|
+
/**
|
|
58
|
+
* Authentication tag (for authenticated encryption)
|
|
59
|
+
*/
|
|
60
|
+
tag?: Uint8Array;
|
|
61
|
+
}
|
|
62
|
+
/**
|
|
63
|
+
* Result of a decryption operation
|
|
64
|
+
*/
|
|
65
|
+
interface DecryptionResult {
|
|
66
|
+
/**
|
|
67
|
+
* Decrypted plaintext data
|
|
68
|
+
*/
|
|
69
|
+
plaintext: Uint8Array;
|
|
70
|
+
}
|
|
71
|
+
/**
|
|
72
|
+
* Key pair for asymmetric cryptography
|
|
73
|
+
*/
|
|
74
|
+
interface KeyPair {
|
|
75
|
+
/**
|
|
76
|
+
* Public key
|
|
77
|
+
*/
|
|
78
|
+
publicKey: Uint8Array;
|
|
79
|
+
/**
|
|
80
|
+
* Private key
|
|
81
|
+
*/
|
|
82
|
+
privateKey: Uint8Array;
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
/**
|
|
86
|
+
* Browser implementation of cryptographic operations using Web Crypto API.
|
|
87
|
+
* Uses CryptoKey for encryption/decryption operations.
|
|
88
|
+
*/
|
|
89
|
+
declare class CryptoBrowser implements CryptoOperations<CryptoKey> {
|
|
90
|
+
private encryptionKey?;
|
|
91
|
+
/**
|
|
92
|
+
* Creates a new CryptoBrowser instance.
|
|
93
|
+
* @param getEncryptionKey - Function that returns the encryption key for this instance
|
|
94
|
+
*/
|
|
95
|
+
constructor();
|
|
96
|
+
/**
|
|
97
|
+
* Sets the encryption key for the encrypt and decrypt operations.
|
|
98
|
+
* Validates that the key is AES-GCM with 256-bit length for cross-platform compatibility.
|
|
99
|
+
*
|
|
100
|
+
* @param key - The encryption key to set (must be AES-GCM 256-bit)
|
|
101
|
+
* @throws {Error} If key is not AES-GCM or not 256-bit
|
|
102
|
+
*/
|
|
103
|
+
setEncryptionKey: (key: CryptoKey) => void;
|
|
104
|
+
/**
|
|
105
|
+
* Creates an encryption function that encrypts string data using AES-GCM encryption.
|
|
106
|
+
* Uses a random initialization vector (IV) for each encryption operation.
|
|
107
|
+
* The IV is prepended to the ciphertext and the result is base64-encoded.
|
|
108
|
+
* Uses the encryption key provided via the `setEncryptionKey` method.
|
|
109
|
+
*
|
|
110
|
+
* @returns A curried function that encrypts data
|
|
111
|
+
*/
|
|
112
|
+
encrypt: () => (data: string) => Promise<string>;
|
|
113
|
+
/**
|
|
114
|
+
* Creates a decryption function that decrypts encrypted string data using AES-GCM decryption.
|
|
115
|
+
* Extracts the IV from the beginning of the encrypted data and uses it to decrypt the ciphertext.
|
|
116
|
+
* Uses the encryption key provided via the constructor.
|
|
117
|
+
*
|
|
118
|
+
* @returns A curried function that decrypts data
|
|
119
|
+
*/
|
|
120
|
+
decrypt: () => (encryptedData: string) => Promise<string>;
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
declare const version = "0.1.0";
|
|
124
|
+
|
|
125
|
+
export { CryptoBrowser, type CryptoConfig, type CryptoOperations, type DecryptionResult, type EncryptionResult, type KeyPair, version };
|
package/dist/index.js
ADDED
|
@@ -0,0 +1,119 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __defProp = Object.defineProperty;
|
|
3
|
+
var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
|
|
4
|
+
var __getOwnPropNames = Object.getOwnPropertyNames;
|
|
5
|
+
var __hasOwnProp = Object.prototype.hasOwnProperty;
|
|
6
|
+
var __export = (target, all) => {
|
|
7
|
+
for (var name in all)
|
|
8
|
+
__defProp(target, name, { get: all[name], enumerable: true });
|
|
9
|
+
};
|
|
10
|
+
var __copyProps = (to, from, except, desc) => {
|
|
11
|
+
if (from && typeof from === "object" || typeof from === "function") {
|
|
12
|
+
for (let key of __getOwnPropNames(from))
|
|
13
|
+
if (!__hasOwnProp.call(to, key) && key !== except)
|
|
14
|
+
__defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
|
|
15
|
+
}
|
|
16
|
+
return to;
|
|
17
|
+
};
|
|
18
|
+
var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
|
|
19
|
+
|
|
20
|
+
// src/index.ts
|
|
21
|
+
var index_exports = {};
|
|
22
|
+
__export(index_exports, {
|
|
23
|
+
CryptoBrowser: () => CryptoBrowser,
|
|
24
|
+
version: () => version
|
|
25
|
+
});
|
|
26
|
+
module.exports = __toCommonJS(index_exports);
|
|
27
|
+
|
|
28
|
+
// src/client/CryptoBrowser.ts
|
|
29
|
+
var CryptoBrowser = class {
|
|
30
|
+
encryptionKey;
|
|
31
|
+
/**
|
|
32
|
+
* Creates a new CryptoBrowser instance.
|
|
33
|
+
* @param getEncryptionKey - Function that returns the encryption key for this instance
|
|
34
|
+
*/
|
|
35
|
+
constructor() {
|
|
36
|
+
}
|
|
37
|
+
/**
|
|
38
|
+
* Sets the encryption key for the encrypt and decrypt operations.
|
|
39
|
+
* Validates that the key is AES-GCM with 256-bit length for cross-platform compatibility.
|
|
40
|
+
*
|
|
41
|
+
* @param key - The encryption key to set (must be AES-GCM 256-bit)
|
|
42
|
+
* @throws {Error} If key is not AES-GCM or not 256-bit
|
|
43
|
+
*/
|
|
44
|
+
setEncryptionKey = (key) => {
|
|
45
|
+
if (key.algorithm.name !== "AES-GCM") {
|
|
46
|
+
throw new Error(
|
|
47
|
+
`Invalid key algorithm: ${key.algorithm.name}. Key must be AES-GCM for cross-platform compatibility with CryptoServer.`
|
|
48
|
+
);
|
|
49
|
+
}
|
|
50
|
+
const keyLength = key.algorithm.length;
|
|
51
|
+
if (keyLength !== 256) {
|
|
52
|
+
throw new Error(
|
|
53
|
+
`Invalid key length: ${keyLength} bits. Key must be 256-bit for cross-platform compatibility with CryptoServer (AES-256-GCM).`
|
|
54
|
+
);
|
|
55
|
+
}
|
|
56
|
+
this.encryptionKey = key;
|
|
57
|
+
};
|
|
58
|
+
/**
|
|
59
|
+
* Creates an encryption function that encrypts string data using AES-GCM encryption.
|
|
60
|
+
* Uses a random initialization vector (IV) for each encryption operation.
|
|
61
|
+
* The IV is prepended to the ciphertext and the result is base64-encoded.
|
|
62
|
+
* Uses the encryption key provided via the `setEncryptionKey` method.
|
|
63
|
+
*
|
|
64
|
+
* @returns A curried function that encrypts data
|
|
65
|
+
*/
|
|
66
|
+
encrypt = () => async (data) => {
|
|
67
|
+
const key = this.encryptionKey;
|
|
68
|
+
if (!key) {
|
|
69
|
+
throw new Error(
|
|
70
|
+
"Encryption key not available. You must call `setEncryptionKey` before using this instance."
|
|
71
|
+
);
|
|
72
|
+
}
|
|
73
|
+
const iv = crypto.getRandomValues(new Uint8Array(12));
|
|
74
|
+
const encrypted = await crypto.subtle.encrypt(
|
|
75
|
+
{ name: "AES-GCM", iv },
|
|
76
|
+
key,
|
|
77
|
+
new TextEncoder().encode(data)
|
|
78
|
+
);
|
|
79
|
+
const combined = new Uint8Array(iv.length + encrypted.byteLength);
|
|
80
|
+
combined.set(iv);
|
|
81
|
+
combined.set(new Uint8Array(encrypted), iv.length);
|
|
82
|
+
return btoa(String.fromCharCode(...combined));
|
|
83
|
+
};
|
|
84
|
+
/**
|
|
85
|
+
* Creates a decryption function that decrypts encrypted string data using AES-GCM decryption.
|
|
86
|
+
* Extracts the IV from the beginning of the encrypted data and uses it to decrypt the ciphertext.
|
|
87
|
+
* Uses the encryption key provided via the constructor.
|
|
88
|
+
*
|
|
89
|
+
* @returns A curried function that decrypts data
|
|
90
|
+
*/
|
|
91
|
+
decrypt = () => async (encryptedData) => {
|
|
92
|
+
const key = this.encryptionKey;
|
|
93
|
+
if (!key) {
|
|
94
|
+
throw new Error(
|
|
95
|
+
"Encryption key not available. You must call `setEncryptionKey` before using this instance."
|
|
96
|
+
);
|
|
97
|
+
}
|
|
98
|
+
const combined = Uint8Array.from(
|
|
99
|
+
atob(encryptedData),
|
|
100
|
+
(c) => c.charCodeAt(0)
|
|
101
|
+
);
|
|
102
|
+
const iv = combined.slice(0, 12);
|
|
103
|
+
const ciphertext = combined.slice(12);
|
|
104
|
+
const decrypted = await crypto.subtle.decrypt(
|
|
105
|
+
{ name: "AES-GCM", iv },
|
|
106
|
+
key,
|
|
107
|
+
ciphertext
|
|
108
|
+
);
|
|
109
|
+
return new TextDecoder().decode(decrypted);
|
|
110
|
+
};
|
|
111
|
+
};
|
|
112
|
+
|
|
113
|
+
// src/index.ts
|
|
114
|
+
var version = "0.1.0";
|
|
115
|
+
// Annotate the CommonJS export names for ESM import in node:
|
|
116
|
+
0 && (module.exports = {
|
|
117
|
+
CryptoBrowser,
|
|
118
|
+
version
|
|
119
|
+
});
|