A framework-agnostic encryption library for Node.js. Built with simplicity and security in mind, @boringnode/encryption provides a unified API for encrypting and signing data with support for multiple encryption algorithms and key rotation.
npm install @boringnode/encryption- Multiple Algorithms: ChaCha20-Poly1305, AES-256-GCM, AES-256-CBC
- Key Rotation: Encrypt with new keys, decrypt with old ones
- Purpose-Bound Encryption: Ensure encrypted values are used for their intended purpose
- Expiration Support: Set time-to-live on encrypted values
- Message Verification: Sign data without encrypting (HMAC-based)
- Type-Safe: Full TypeScript support with typed payloads
import { Encryption } from '@boringnode/encryption'
import { chacha20poly1305 } from '@boringnode/encryption/drivers/chacha20_poly1305'
const encryption = new Encryption(
chacha20poly1305({
id: 'app',
keys: [process.env.APP_KEY],
})
)// Encrypt any value
const encrypted = encryption.encrypt({ userId: 1, role: 'admin' })
// => "app.base64EncodedCipherText.base64EncodedIv.base64EncodedTag"
// Decrypt the value
const decrypted = encryption.decrypt(encrypted)
// => { userId: 1, role: 'admin' }The library supports encrypting a wide range of data types:
- Strings
- Numbers
- Booleans
- Arrays
- Objects
- Dates
Modern, fast, and secure. Recommended for most use cases.
import { chacha20poly1305 } from '@boringnode/encryption/drivers/chacha20_poly1305'
const config = chacha20poly1305({
id: 'app',
keys: ['your-32-character-secret-key-here'],
})Industry-standard authenticated encryption.
import { aes256gcm } from '@boringnode/encryption/drivers/aes_256_gcm'
const config = aes256gcm({
id: 'app',
keys: ['your-32-character-secret-key-here'],
})Legacy support with HMAC authentication.
import { aes256cbc } from '@boringnode/encryption/drivers/aes_256_cbc'
const config = aes256cbc({
id: 'app',
keys: ['your-32-character-secret-key-here'],
})The library supports multiple keys for seamless key rotation. The first key is used for encryption, while all keys are tried during decryption.
const encryption = new Encryption(
chacha20poly1305({
id: 'app',
keys: [
process.env.NEW_APP_KEY, // Used for encryption
process.env.OLD_APP_KEY, // Still valid for decryption
],
})
)
// New encryptions use NEW_APP_KEY
const encrypted = encryption.encrypt('secret')
// Decryption works with both keys
encryption.decrypt(encryptedWithOldKey) // Works
encryption.decrypt(encryptedWithNewKey) // WorksEnsure encrypted values are only used for their intended purpose:
// Encrypt with a purpose
const token = encryption.encrypt({ userId: 1 }, undefined, 'password-reset')
// Must provide same purpose to decrypt
encryption.decrypt(token, 'password-reset') // => { userId: 1 }
encryption.decrypt(token, 'email-verify') // => null
encryption.decrypt(token) // => nullSet a time-to-live on encrypted values:
// Expires in 1 hour
const token = encryption.encrypt({ userId: 1 }, '1h')
// Expires in 30 minutes
const token = encryption.encrypt({ userId: 1 }, '30m')
// Expires in 7 days
const token = encryption.encrypt({ userId: 1 }, '7d')
// After expiration, decrypt returns null
encryption.decrypt(expiredToken) // => nullWhen you need to ensure data integrity without hiding the content, use the MessageVerifier. The payload is base64-encoded (not encrypted) and signed with HMAC.
import { MessageVerifier } from '@boringnode/encryption/message_verifier'
const verifier = new MessageVerifier(['your-secret-key'])
// Sign a value
const signed = verifier.sign({ userId: 1 })
// Verify and retrieve the value
const payload = verifier.unsign(signed)
// => { userId: 1 }
// Tampered values return null
verifier.unsign('tampered.value') // => nullThe verifier also supports purpose and expiration:
// With expiration
const signed = verifier.sign({ userId: 1 }, '1h')
// With purpose
const signed = verifier.sign({ userId: 1 }, undefined, 'api-token')
const payload = verifier.unsign(signed, 'api-token')URL-safe base64 encoding/decoding utilities are available:
import { base64UrlEncode, base64UrlDecode } from '@boringnode/encryption/base64'
const encoded = base64UrlEncode('Hello World')
const decoded = base64UrlDecode(encoded, 'utf8')Generate and verify HMAC signatures:
import { Hmac } from '@boringnode/encryption'
const hmac = new Hmac(secretKey)
// Generate HMAC
const hash = hmac.generate('data to sign')
// Verify HMAC (timing-safe comparison)
const isValid = hmac.compare('data to sign', hash)The library is designed to return null on decryption failures rather than throwing exceptions. This prevents timing attacks and simplifies error handling:
const result = encryption.decrypt(maybeInvalidValue)
if (result === null) {
// Invalid, expired, wrong purpose, or tampered
}