@cryptforge/cryptography
v0.2.1
Published
Cryptographic operations for CryptForge applications - supports both client and server environments
Maintainers
Readme
@cryptforge/cryptography
Cryptographic operations for CryptForge applications with support for both client (browser) and server (Node.js) environments.
Features
- 🔐 Encryption & Decryption: Symmetric and asymmetric encryption operations
- 🌐 Cross-Platform: Separate implementations for browser and Node.js environments
- 🔑 Key Management: Utilities for key generation, derivation, and storage
- 🛡️ Type-Safe: Full TypeScript support with comprehensive type definitions
- 📦 Tree-Shakeable: Optimized bundle sizes with separate entry points
Installation
npm install @cryptforge/cryptographyUsage
Client/Browser Usage
The client export provides CryptoBrowser, which uses the Web Crypto API for encryption/decryption:
import { CryptoBrowser } from '@cryptforge/cryptography';
import { createAuthClient } from '@cryptforge/auth';
// Create auth client and unlock wallet
const auth = createAuthClient();
await auth.unlock({ password: 'your-password', chainId: 'ethereum' });
// Derive encryption key from wallet
const encryptionKey = await auth.deriveDataEncryptionKey({
purpose: 'my-app-data',
version: 1,
});
// Create CryptoBrowser instance
const crypto = new CryptoBrowser();
crypto.setEncryptionKey(encryptionKey);
// Encrypt data
const encrypted = await crypto.encrypt()('Hello, World!');
console.log('Encrypted:', encrypted); // Base64-encoded string
// Decrypt data
const decrypted = await crypto.decrypt()(encrypted);
console.log('Decrypted:', decrypted); // "Hello, World!"Using with Callback Pattern
import { CryptoBrowser } from '@cryptforge/cryptography';
// Create instance with dynamic key retrieval
const crypto = new CryptoBrowser();
// Set key when wallet is unlocked
const unlockWallet = async () => {
await auth.unlock({ password: 'password', chainId: 'ethereum' });
const key = await auth.deriveDataEncryptionKey({
purpose: 'my-app-data',
});
crypto.setEncryptionKey(key);
};
// Use encryption after unlocking
await unlockWallet();
const encrypted = await crypto.encrypt()('Secret message');Encryption Details
CryptoBrowser uses AES-256-GCM encryption:
- Algorithm: AES-GCM (Galois/Counter Mode)
- Key Size: 256 bits
- IV Size: 12 bytes (96 bits)
- Output Format: Base64-encoded (IV + ciphertext)
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.
Server/Node.js Usage
The server export provides both browser and server-specific functionality:
import { CryptoServer } from '@cryptforge/cryptography/server';
// CryptoServer provides Node.js-specific crypto operations
const crypto = new CryptoServer();
// Server-side encryption/decryption
// (Implementation similar to CryptoBrowser but optimized for Node.js)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+.
Architecture
This package maintains strict separation between client and server environments:
src/client/: Browser-compatible implementations using Web Crypto APIsrc/server/: Node.js-specific implementations using Node's crypto modulesrc/shared/: Environment-agnostic utilities and helperssrc/types/: Shared TypeScript type definitions
Entry Points
@cryptforge/cryptography: Client/browser-safe exports (default)@cryptforge/cryptography/server: Server-specific exports + client exports
API Reference
CryptoBrowser
Browser-compatible cryptographic operations using Web Crypto API.
Constructor
new CryptoBrowser()Creates a new CryptoBrowser instance. The encryption key must be set before using encrypt/decrypt methods.
Methods
setEncryptionKey(key: CryptoKey): void
Sets the encryption key for subsequent operations. Key must be:
- Algorithm: AES-GCM
- Length: 256 bits
- Usages:
['encrypt', 'decrypt']
const key = await auth.deriveDataEncryptionKey({
purpose: 'my-data',
algorithm: 'AES-GCM',
length: 256,
});
crypto.setEncryptionKey(key);encrypt(): (data: string) => Promise<string>
Returns a curried function that encrypts string data.
const encryptFn = crypto.encrypt();
const encrypted = await encryptFn('Hello, World!');Returns: Base64-encoded string containing IV + ciphertext
decrypt(): (encryptedData: string) => Promise<string>
Returns a curried function that decrypts encrypted string data.
const decryptFn = crypto.decrypt();
const decrypted = await decryptFn(encrypted);Throws: Error if decryption fails (wrong key, corrupted data, etc.)
Examples
Basic Encryption/Decryption
import { CryptoBrowser } from '@cryptforge/cryptography';
import { createAuthClient } from '@cryptforge/auth';
// Setup
const auth = createAuthClient();
await auth.unlock({ password: 'password', chainId: 'ethereum' });
const key = await auth.deriveDataEncryptionKey({
purpose: 'user-documents',
});
const crypto = new CryptoBrowser();
crypto.setEncryptionKey(key);
// Encrypt
const data = JSON.stringify({ title: 'My Document', content: 'Secret data' });
const encrypted = await crypto.encrypt()(data);
// Store encrypted data
localStorage.setItem('myDocument', encrypted);
// Later... decrypt
const stored = localStorage.getItem('myDocument');
const decrypted = await crypto.decrypt()(stored!);
const document = JSON.parse(decrypted);Encrypting Multiple Items
const encrypt = crypto.encrypt();
const decrypt = crypto.decrypt();
// Encrypt multiple items
const items = ['Item 1', 'Item 2', 'Item 3'];
const encryptedItems = await Promise.all(
items.map(item => encrypt(item))
);
// Decrypt multiple items
const decryptedItems = await Promise.all(
encryptedItems.map(item => decrypt(item))
);Error Handling
try {
const encrypted = await crypto.encrypt()('Secret data');
const decrypted = await crypto.decrypt()(encrypted);
} catch (error) {
if (error.message.includes('Encryption key not available')) {
// Key not set - unlock wallet first
await auth.unlock({ password: 'password' });
const key = await auth.deriveDataEncryptionKey({ purpose: 'data' });
crypto.setEncryptionKey(key);
} else {
// Decryption failed - wrong key or corrupted data
console.error('Decryption failed:', error);
}
}With Automerge Documents
Perfect for encrypting Automerge documents:
import { CryptoBrowser } from '@cryptforge/cryptography';
import * as Automerge from '@automerge/automerge';
// Create Automerge document
let doc = Automerge.init();
doc = Automerge.change(doc, doc => {
doc.notes = 'Secret notes';
});
// Encrypt document
const docBytes = Automerge.save(doc);
const docString = btoa(String.fromCharCode(...docBytes));
const encrypted = await crypto.encrypt()(docString);
// Store encrypted document
await storage.save('doc_id', encrypted);
// Later... decrypt and load
const stored = await storage.load('doc_id');
const decrypted = await crypto.decrypt()(stored);
const docBytesRestored = Uint8Array.from(atob(decrypted), c => c.charCodeAt(0));
const restoredDoc = Automerge.load(docBytesRestored);Security Considerations
Best Practices
Key Management: Never hardcode encryption keys. Always derive them from user credentials or secure key storage.
Key Rotation: Use the
versionparameter inderiveDataEncryptionKeyto support key rotation:
// Old data encrypted with v1
const keyV1 = await auth.deriveDataEncryptionKey({ purpose: 'data', version: 1 });
// New data encrypted with v2
const keyV2 = await auth.deriveDataEncryptionKey({ purpose: 'data', version: 2 });Secure Key Storage: The encryption key is kept in memory only. When the user locks their wallet, the key is cleared.
No Key Export: Keys are created with
extractable: falseby default, preventing them from being exported.
What's Protected
✅ Data at Rest - Encrypted data stored in IndexedDB, localStorage, or disk
✅ Data in Transit - Can encrypt before sending over network
✅ Memory Safety - Keys cleared when wallet locks
What's NOT Protected
❌ Data in Use - Decrypted data in application memory
❌ Side Channels - Timing attacks, power analysis
❌ Compromised Device - Malware, keyloggers
Always follow security best practices for your specific use case.
Cross-Platform Compatibility
This package uses AES-256-GCM, which is supported across all platforms:
| Platform | Support | Notes | |----------|---------|-------| | Browser | ✅ | Web Crypto API (all modern browsers) | | Node.js | ✅ | Native crypto module (v15+) | | Electron | ✅ | Both main and renderer processes | | React Native | ✅ | Via polyfills | | Web Workers | ✅ | Web Crypto API available |
Development
# Build the package
npm run build
# Run tests
npm testRelated Packages
- @cryptforge/auth - Key derivation using
deriveDataEncryptionKey() - @cryptforge/core - Core types and interfaces
License
MIT
