Node.js SDK for format-preserving and AES-GCM encryption via HashiCorp Vault or built-in key management
A comprehensive Node.js SDK for format-preserving encryption (FF1) and AES-GCM encryption via HashiCorp Vault or built-in key management. This SDK provides multiple encryption modes including REST, Transit, and Dynamic Transit encryption with support for both single operations and batch processing.
- Format-Preserving Encryption (FF1): Preserves data format while encrypting
- AES-256-CBC Encryption: Standard encryption for transit and rest data
- Multiple Key Types: Support for rest, transit, and dynamictransit encryption modes
- Dynamic Key Management: Automatic key rotation with trace ID tracking
- Batch Processing: Efficient encryption/decryption of multiple data items
- Template Detection: Automatic detection of numeric vs alphanumeric data
- Key Caching: Automatic 5-minute key caching for improved performance
- Automatic Retry: Built-in retry logic for transient network errors (ECONNRESET, etc.)
- TypeScript Support: Full type definitions and IntelliSense support
- Multiple Build Formats: CommonJS, ESM, and UMD support
- ES5 Compatibility: Full support for older browsers and Node.js versions (0.12+)
- Comprehensive Error Handling: Detailed error messages and timeout support
``bash`
npm install sevola-sdk-js
This library is fully compatible with ES5 environments and older browsers. It includes built-in polyfills for:
- Promise: Full Promise implementation for environments without native support
- Object.assign: Object property copying utility
- Array.from: Array creation from array-like objects
- Array.prototype.find: Array search functionality
- String.prototype.startsWith/endsWith/includes: String search methods
The library automatically detects and polyfills missing features, ensuring compatibility with:
- Internet Explorer 9+
- Node.js 0.12+
- Older mobile browsers
- Legacy enterprise environments
Note: For production use in ES5 environments, consider installing core-js as a peer dependency for additional polyfills:
`bash`
npm install core-js
This library cannot be used in browser environments due to several Node.js-specific dependencies and security requirements:
#### Why Browser Usage is Not Supported:
1. Node.js Crypto Module: The SDK relies on Node.js built-in crypto module for:
- AES-256-CBC encryption/decryption
- PBKDF2 key derivation
- Random byte generation
- Hash functions
2. File System Access: Some operations require access to Node.js file system APIs
3. Network Security: The SDK makes HTTP requests to encryption servers, which may have CORS restrictions in browsers
4. Security Considerations: Running encryption libraries in browsers can expose sensitive operations to client-side code
#### What This Means:
- ✅ Supported: Node.js applications, server-side scripts, CLI tools
- ❌ Not Supported: Web browsers, React Native, Electron renderer process
- ⚠️ Limited Support: Electron main process (if properly configured)
#### Example of What Won't Work:
`javascript`
// ❌ This will fail in browsers
import { EncryptClient } from 'sevola-sdk-js';
const client = new EncryptClient('api-key'); // Error: crypto module not found
#### Example of What Will Work:
`javascript`
// ✅ This works in Node.js
import { EncryptClient } from 'sevola-sdk-js';
const client = new EncryptClient('api-key');
const encrypted = await client.encryptData('sensitive data');
`typescript
import { EncryptClient } from 'sevola-sdk-js';
// Initialize with API key only
const client = new EncryptClient('your-api-key');
// Or with full configuration
const client = new EncryptClient({
apiKey: 'your-api-key',
baseUrl: 'http://localhost:8082', // Optional, defaults to localhost:8082
timeout: 30000, // Optional, defaults to 30 seconds
logger: true, // Optional, enables debug logging (default: false)
retryAttempts: 3, // Optional, number of retries for network errors (default: 3)
retryDelay: 1000 // Optional, base delay between retries in ms (default: 1000)
});
`
`typescript
// Single data encryption with automatic template detection
const plaintext = 'Hello123World';
const encrypted = await client.encryptData(plaintext);
console.log('Encrypted:', encrypted); // Returns array with encrypted data and template
// Single data decryption
const decrypted = await client.decryptData(encrypted[0].encrypted);
console.log('Decrypted:', decrypted); // Returns array with decrypted data and template
// Batch encryption with explicit templates
const requests = [
{ template: 'numeric', data: '12345' },
{ template: 'alphanumeric', data: 'ABC123' },
{ template: 'numeric', data: '67890' }
];
const batchEncrypted = await client.encryptDataFF1(requests);
console.log('Batch Encrypted:', batchEncrypted);
// Batch decryption
const batchDecrypted = await client.decryptDataFF1(batchEncrypted);
console.log('Batch Decrypted:', batchDecrypted);
`
`typescript
// Standard transit encryption
const plaintext = 'Sensitive transit data';
const encrypted = await client.encryptTransit(plaintext);
console.log('Transit Encrypted:', encrypted);
// Transit decryption
const decrypted = await client.decryptTransit(encrypted);
console.log('Transit Decrypted:', decrypted);
// Dynamic transit encryption (with automatic key rotation)
const dynamicEncrypted = await client.encryptTransitDynamic(plaintext);
console.log('Dynamic Encrypted:', dynamicEncrypted);
// Dynamic transit decryption (uses embedded trace ID)
const dynamicDecrypted = await client.decryptTransitDynamic(dynamicEncrypted);
console.log('Dynamic Decrypted:', dynamicDecrypted);
// High-Performance Batch Processing
const batchData = ['item1', 'item2', 'item3', ...moreItems];
// Encrypt multiple items in parallel (optimized with caching and concurrency)
const batchEncrypted = await client.encryptTransitBatch(batchData);
console.log('Batch Encrypted:', batchEncrypted.length);
// Decrypt multiple items in parallel
const batchDecrypted = await client.decryptTransitBatch(batchEncrypted);
console.log('Batch Verified:', batchDecrypted[0] === batchData[0]);
`
The main client class for all encryption operations.
#### Constructor
`typescript`
new EncryptClient(apiKey: string)
new EncryptClient(options: EncryptClientOptions)
#### Configuration Options
`typescript`
interface EncryptClientOptions {
apiKey: string; // Required: Your API key for authentication
baseUrl?: string; // Optional: Base URL for the API (default: 'http://localhost:8082')
timeout?: number; // Optional: Request timeout in milliseconds (default: 30000)
logger?: boolean; // Optional: Enable debug logging (default: false)
retryAttempts?: number; // Optional: Number of retry attempts for network errors (default: 3)
retryDelay?: number; // Optional: Base delay between retries in ms (default: 1000)
}
| Option | Type | Default | Description |
|--------|------|---------|-------------|
| apiKey | string | required | Your API key for authentication |baseUrl
| | string | 'http://localhost:8082' | Base URL for the encryption API |timeout
| | number | 30000 | Request timeout in milliseconds |logger
| | boolean | false | Enable debug logging to console |retryAttempts
| | number | 3 | Number of retry attempts for transient network errors |retryDelay
| | number | 1000 | Base delay between retries in ms (uses exponential backoff) |
#### Format-Preserving Encryption (FF1)
`typescript
// Legacy method with automatic template detection
encryptData(plaintext: string): Promise
decryptData(cipherText: string): Promise
// Modern batch methods with explicit templates
encryptDataFF1(requests: { template: string; data: string }[]): Promise<{ template: string; encrypted: string }[]>
decryptDataFF1(requests: { template: string; data: string }[]): Promise<{ template: string; decrypted: string }[]>
`
#### AES Transit Encryption
`typescript
// Standard transit encryption
encryptTransit(plaintext: string): Promise
decryptTransit(cipherText: string): Promise
// Modern batch methods with concurrency support (up to 20 batches in parallel)
encryptTransitBatch(plaintexts: string[]): Promise
decryptTransitBatch(ciphertexts: string[]): Promise
// Dynamic transit encryption with key rotation
encryptTransitDynamic(plaintext: string): Promise
decryptTransitDynamic(cipherText: string): Promise
`
#### Cache Management
`typescript
// Clear all cached encryption keys (force fresh fetch from server)
clearCache(): void
// Get number of cached keys (for debugging/monitoring)
getCacheSize(): number
`
#### Encryption Results
`typescript
interface EncryptionResult {
encrypted: string;
template: 'numeric' | 'alphanumeric';
}
interface DecryptionResult {
data: string;
template: 'numeric' | 'alphanumeric';
}
`
#### API Responses
`typescript
interface ApiResponse {
status: string;
message: string;
data: string;
}
interface DynamicKeyResponse {
status: string;
message?: string;
data: {
key: string;
'x-sevola-traceid': string;
};
}
`
#### Key Types and Operations
`typescript`
type KeyType = 'rest' | 'transit' | 'dynamictransit';
type EncryptionOperation = 'enc' | 'dec';
`typescript
const client = new EncryptClient('your-api-key');
// Encrypt credit card number (preserves numeric format)
const cardNumber = '4111111111111111';
const encrypted = await client.encryptDataFF1([
{ template: 'numeric', data: cardNumber }
]);
console.log('Original:', cardNumber);
console.log('Encrypted:', encrypted[0].encrypted); // Still numeric format
// Decrypt
const decrypted = await client.decryptDataFF1(encrypted);
console.log('Decrypted:', decrypted[0].decrypted); // Original card number
`
`typescript
const client = new EncryptClient('your-api-key');
// Encrypt email address (preserves alphanumeric format)
const email = 'user@example.com';
const encrypted = await client.encryptDataFF1([
{ template: 'alphanumeric', data: email }
]);
console.log('Original:', email);
console.log('Encrypted:', encrypted[0].encrypted); // Preserves @ symbol and format
// Decrypt
const decrypted = await client.decryptDataFF1(encrypted);
console.log('Decrypted:', decrypted[0].decrypted); // Original email
`
`typescript
const client = new EncryptClient('your-api-key');
const dataToEncrypt = [
{ template: 'numeric', data: '1234567890' }, // Phone number
{ template: 'alphanumeric', data: 'ABC123XYZ' }, // Product code
{ template: 'numeric', data: '9876543210' }, // Another phone number
{ template: 'alphanumeric', data: 'user@domain.com' } // Email
];
// Encrypt all data in one batch
const encrypted = await client.encryptDataFF1(dataToEncrypt);
// Process encrypted data
encrypted.forEach((item, index) => {
console.log(Item ${index + 1}:, {
template: item.template,
encrypted: item.encrypted
});
});
// Decrypt all data in one batch
const decrypted = await client.decryptDataFF1(encrypted);
`
`typescript
const client = new EncryptClient('your-api-key');
// Encrypt with dynamic key (each encryption uses a new key)
const sensitiveData = 'highly-sensitive-information';
const encrypted = await client.encryptTransitDynamic(sensitiveData);
console.log('Dynamic Encrypted:', encrypted);
// Output includes trace ID: "base64encrypteddatax-sevola-traceid=abc123..."
// Decrypt using the embedded trace ID
const decrypted = await client.decryptTransitDynamic(encrypted);
console.log('Decrypted:', decrypted); // Original sensitive data
`
`typescript
const client = new EncryptClient({
apiKey: 'your-api-key',
logger: true // Enable to see retry attempts in console
});
try {
// Attempt encryption (will auto-retry on network errors)
const encrypted = await client.encryptData('test data');
console.log('Success:', encrypted);
} catch (error) {
if (error.message.includes('timeout')) {
console.error('Request timed out - check network connection');
} else if (error.message.includes('Invalid API Key')) {
console.error('Authentication failed - check API key');
} else if (error.message.includes('Network error after')) {
console.error('Network error persisted after all retries');
} else if (error.message.includes('HTTP 404')) {
console.error('API endpoint not found - check base URL');
} else {
console.error('Encryption failed:', error.message);
}
}
`
The SDK supports two main template types for format-preserving encryption:
- Use Case: Phone numbers, credit card numbers, SSNs, PINs
- Example: "1234567890" → "9876543210" (same format, different values)$3
- Alphabet: 0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ@
- Use Case: Email addresses, product codes, usernames, alphanumeric IDs
- Example: "user@example.com" → "x7k9#m2n4p8q" (preserves @ symbol)Performance & Reliability
$3
The SDK is optimized for high-throughput scenarios using intelligent batching and concurrency control:
- Parallel Execution:
encryptTransitBatch and decryptTransitBatch process requests in parallel (up to 20 concurrent batches).
- Derived Key Caching: CPU-intensive PBKDF2 operations are cached, making decryption essentially instantaneous (millisec range) after the first fetch.
- Smart Queueing: Large datasets are automatically split into manageable chunks to prevent API overloading.$3
The SDK automatically caches encryption keys to reduce API calls and improve performance:
- Keys are cached for 5 minutes by default
- Subsequent encrypt/decrypt operations reuse cached keys
- Use
clearCache() to force fetching fresh keys from the server`typescript
const client = new EncryptClient('your-api-key');// Perform multiple operations (key is fetched once, then cached)
await client.encryptData('data1');
await client.encryptData('data2'); // Uses cached key
await client.encryptData('data3'); // Uses cached key
// Force fresh key fetch
client.clearCache();
// Check cache state
console.log('Cached keys:', client.getCacheSize());
`$3
The SDK automatically retries requests when transient network errors occur:
| Error Code | Description |
|------------|-------------|
|
ECONNRESET | Connection reset by peer |
| ECONNREFUSED | Connection refused |
| ETIMEDOUT | Connection timed out |
| ENOTFOUND | DNS lookup failed |
| EAI_AGAIN | DNS temporary failure |
| EPIPE | Broken pipe |
| EHOSTUNREACH | Host unreachable |
| ENETUNREACH | Network unreachable |Retry behavior uses exponential backoff: 1s → 2s → 3s (with default settings).
`typescript
// Configure retry behavior
const client = new EncryptClient({
apiKey: 'your-api-key',
retryAttempts: 5, // Try up to 5 times
retryDelay: 2000, // Start with 2s delay (then 4s, 6s, 8s, 10s)
logger: true // Enable to see retry logs
});
`Security Features
$3
- Automatic Key Retrieval: Keys are fetched securely from the server
- Key Caching: Keys are cached locally for 5 minutes to improve performance
- Key Rotation: Dynamic transit mode supports automatic key rotation
- Trace ID Tracking: Each dynamic encryption includes a unique trace ID
- Secure Communication: All API calls use HTTPS and proper authentication$3
- FF1 Algorithm: NIST-approved format-preserving encryption
- AES-256-CBC: Industry-standard symmetric encryption
- PBKDF2 Key Derivation: Secure key derivation with salt
- Random IV Generation: Unique initialization vectors for each encryptionRequirements
- Node.js: 14.0.0 or higher
- Runtime Dependencies:
-
node-fetch: ^3.3.2 (HTTP client)
- ubiq-security: ^2.2.3 (FF1 encryption library)
- Development Dependencies: See package.json` for full listMIT © PT Ifabula Digital Kreasi
- Repository: https://gitlab.com/kasfi.tamiya/sevola_sdk_js
- Issues: https://gitlab.com/kasfi.tamiya/sevola_sdk_js/issues
- Author: Kasfi Tamiya (kasfi.tamiya@ifabula.com)
- Company: PT Ifabula Digital Kreasi (https://ifabula.com)