secure-encryption
v1.1.3
Published
Per-user dynamic AES-256-GCM + RSA-OAEP encryption for transport and database storage
Maintainers
Readme
# secure-encryption
Enterprise hybrid encryption module for **secure data transmission** and **database field storage**. Built for both **JavaScript** and **TypeScript** environments with native type definitions.
- **Transport**: Generates a fresh AES-256-GCM key for every payload (perfect forward secrecy)
- **Storage**: Per-user Data Encryption Key (DEK) protected by RSA-OAEP
- **Key Rotation**: Supports rotating keys every 24 hours without breaking active sessions
- **AAD Support**: Additional Authenticated Data binding for tamper-proof field encryption
- **Non-blocking**: Fully async RSA operations to prevent Event Loop blocking
- **Zero external dependencies** — uses only Node.js built-in `crypto`
---
## Installation
```bash
npm install secure-encryption
Features
| Feature | Description |
| --- | --- |
| Ephemeral AES key | New AES-256-GCM key generated for every transmission |
| Per-user RSA key pair | Each user has their own RSA-3072 key pair |
| Per-user DEK | Data Encryption Key for database fields, unique per user |
| Key rotation | Rotate keys on login or every 24 hours |
| Grace period | Previous private key kept temporarily during rotation |
| Authenticated encryption | AES-GCM provides confidentiality, integrity, and optional AAD binding |
| Full TypeScript Support | Bundled with high-precision .d.ts declaration files |
Simple Usage
1. Import Module
JavaScript (CommonJS):
const CybersSecure = require('secure-encryption');
TypeScript (ES Module):
import CybersSecure, { UserKeyInfo, EncryptedPayload, EncryptedField } from 'secure-encryption';
2. Create or Rotate User Keys
// First time (registration or first login)
const keyAll = await CybersSecure.rotateUserKeyPair();
// Later (on login or scheduled job)
if (CybersSecure.shouldRotateKey(user.keyAll)) {
const newKeyAll = await CybersSecure.rotateUserKeyPair(user.keyAll);
// Save newKeyAll to database
}
Recommended keyAll structure stored in database:
{
provider: "CybersSecure",
publicKey: "...", // Can be sent to client
privateKey: "...", // Server-side only
encryptedDEK: ["..."], // DEK encrypted by RSA (Array of chunks)
createdAt: "2026-...",
expiresAt: "2026-...",
previousPrivateKey: "...", // Optional, for rotation grace period
previousExpiresAt: "..."
}
3. Encrypt / Decrypt for API (Transport)
// Encrypt payload for recipient
const encrypted = await CybersSecure.encryptPayload({ msg: "hello" }, recipientPublicKey);
// → { provider: "CybersSecure", keys: [...], data: "..." }
// Decrypt payload
const data = await CybersSecure.decryptPayload(encrypted, privateKey);
// Recommended during key rotation window
const data = await CybersSecure.decryptPayloadWithFallback(encrypted, user.keyAll);
4. Encrypt / Decrypt Database Fields (With AAD Support)
// 1. Unwrap the user's Data Encryption Key (Async)
const dek = await CybersSecure.unwrapDataKey(user.keyAll.encryptedDEK, user.keyAll.privateKey);
// 2. Encrypt before saving to DB (Optional 3rd arg: AAD for tamper protection)
const encryptedPhone = CybersSecure.encryptField("+85298765432", dek, user.id);
// → { provider: "CybersSecure", iv: "...", ciphertext: "..." }
// 3. Decrypt after reading from DB
const phone = CybersSecure.decryptField(user.phone, dek, user.id);
Important Notes
- Never send
privateKeyor raw DEK to the client. - Every call to
encryptPayloaduses a brand new AES key. - Each user has a different DEK for database encryption.
- Use
decryptPayloadWithFallbackwhen keys may have been rotated. - Always provide contextual IDs (e.g.,
userIdorrecordId) as AAD inencryptFieldto prevent ciphertext relocation attacks.
API Reference
Key Management
generateRSAKeyPair()
Generates a new RSA-3072 key pair in PEM format.
Returns: Promise<{ publicKey: string, privateKey: string }>
rotateUserKeyPair(currentKeyInfo?)
Rotates the user's RSA key pair and re-wraps the DEK.
Keeps the previous private key for a 2-hour grace period.
Returns: Promise<UserKeyInfo>
shouldRotateKey(keyInfo)
Returns true if the key has expired or does not exist.
generateDataKey(userPublicKey)
Generates a random Data Encryption Key and encrypts it with the given public key.
Returns: Promise<{ encryptedDEK: string[] }>
unwrapDataKey(encryptedDEK, privateKey)
Decrypts and returns the raw DEK (Buffer).
Returns: Promise<Buffer>
Transport Encryption
encryptPayload(data, recipientPublicKey)
Hybrid encrypts a payload. Generates a new AES key every time.
Returns: Promise<EncryptedPayload>
decryptPayload(payload, privateKey)
Decrypts a hybrid-encrypted payload.
Returns: Promise<any>
decryptPayloadWithFallback(payload, keyInfo)
Tries the current private key, then falls back to previousPrivateKey if available.
Returns: Promise<any>
Database Field Encryption
encryptField(data, dek, aad?)
Encrypts a value for storage with optional AAD.
Returns: EncryptedField ({ provider, iv, ciphertext })
decryptField(stored, dek, aad?)
Decrypts a value previously encrypted with encryptField.
Returns: any
Security Notes
- Never send or log the
privateKeyor raw DEK to the client. - Always use
decryptPayloadWithFallbackduring the key rotation window. - AES-256-GCM provides authenticated encryption (detects payload tampering).
- RSA-3072 uses OAEP with SHA-256 padding.
- All RSA encryption and decryption operations are handled asynchronously to prevent CPU starvation on Node.js Event Loop.
- The module uses only Node.js built-in
crypto(zero external dependencies).
Requirements
- Node.js >= 16.0.0
License
MIT
