### Install @boringnode/encryption Source: https://github.com/boringnode/encryption/blob/1.x/README.md Install the library using npm. This is the first step before using any of its features. ```bash npm install @boringnode/encryption ``` -------------------------------- ### Encryption Class Initialization Source: https://context7.com/boringnode/encryption/llms.txt Demonstrates how to initialize the Encryption class with a driver configuration, including key rotation support where the new key is used for encryption and all keys are tried for decryption. ```APIDOC ## `Encryption` — Core Class The primary encryption class. It wraps a driver config that specifies the cipher factory plus an array of keys. The **first key** is used for all new encryptions; **all keys** are tried during decryption to support seamless key rotation. ```typescript import { Encryption } from '@boringnode/encryption' import { chacha20poly1305 } from '@boringnode/encryption/drivers/chacha20_poly1305' // Key rotation: provide the new key first, keep the old key for decryption const encryption = new Encryption( chacha20poly1305({ id: 'app', keys: [ process.env.NEW_APP_KEY!, // used for new encryptions process.env.OLD_APP_KEY!, // still accepted for decryption ], }) ) // Encrypts with NEW_APP_KEY const freshToken = encryption.encrypt({ userId: 1 }) // Decrypts tokens made with either key const fromNew = encryption.decrypt(freshToken) // works const fromOld = encryption.decrypt(oldTokenEncryptedBefore) // also works ``` ``` -------------------------------- ### Configure Encryption with ChaCha20-Poly1305 Source: https://github.com/boringnode/encryption/blob/1.x/README.md Initialize the Encryption class with the ChaCha20-Poly1305 driver. Ensure the 'id' is a non-empty string and 'keys' are provided. ```typescript 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], }) ) ``` -------------------------------- ### Initialize Encryption with Key Rotation Source: https://context7.com/boringnode/encryption/llms.txt Instantiate the Encryption class with a driver configuration that includes multiple keys. The first key is used for new encryptions, while all provided keys are used for decryption to support seamless key rotation. ```typescript import { Encryption } from '@boringnode/encryption' import { chacha20poly1305 } from '@boringnode/encryption/drivers/chacha20_poly1305' // Key rotation: provide the new key first, keep the old key for decryption const encryption = new Encryption( chacha20poly1305({ id: 'app', keys: [ process.env.NEW_APP_KEY!, // used for new encryptions process.env.OLD_APP_KEY!, // still accepted for decryption ], }) ) // Encrypts with NEW_APP_KEY const freshToken = encryption.encrypt({ userId: 1 }) // Decrypts tokens made with either key const fromNew = encryption.decrypt(freshToken) // works const fromOld = encryption.decrypt(oldTokenEncryptedBefore) // also works ``` -------------------------------- ### Initialize and Use Hmac Utility Source: https://context7.com/boringnode/encryption/llms.txt Generate and compare URL-safe base64-encoded HMAC-SHA-256 digests using a timing-safe comparison. Useful for low-level HMAC operations. ```typescript import { Hmac } from '@boringnode/encryption' import { createHash } from 'node:crypto' // Derive a Buffer key from a passphrase const key = createHash('sha256').update(process.env.APP_KEY!).digest() const hmac = new Hmac(key) const digest = hmac.generate('my-data-to-sign') // => URL-safe base64 string // Timing-safe comparison hmac.compare('my-data-to-sign', digest) // => true hmac.compare('tampered-data', digest) // => false ``` -------------------------------- ### encrypt() - Encrypt with Expiration and Purpose Source: https://context7.com/boringnode/encryption/llms.txt Explains how to use the `encrypt` method, which accepts JSON-serializable values, an optional TTL string/number for expiration, and an optional purpose tag for enhanced security. ```APIDOC ## `encrypt()` — Encrypt with Expiration and Purpose `Encryption.encrypt()` accepts any JSON-serialisable value plus an optional TTL string/number and an optional purpose tag. Purpose-bound ciphertexts can only be decrypted when the same purpose string is provided, preventing misuse across contexts (e.g., using a password-reset token as a login token). ```typescript 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!] }) ) // --- With expiration (string durations: ms, s, m, h, d) --- const expiringToken = encryption.encrypt({ userId: 1 }, '15m') // Decrypts successfully within 15 minutes; returns null after // --- With purpose --- const resetToken = encryption.encrypt({ userId: 1 }, undefined, 'password-reset') encryption.decrypt(resetToken, 'password-reset') // => { userId: 1 } encryption.decrypt(resetToken, 'email-verify') // => null (wrong purpose) encryption.decrypt(resetToken) // => null (missing purpose) // --- Object-options overload --- const token = encryption.encrypt( { userId: 1 }, { expiresIn: '1h', purpose: 'api-access' } ) encryption.decrypt(token, 'api-access') // => { userId: 1 } ``` ``` -------------------------------- ### Configure AES-SIV Driver (Deterministic) Source: https://github.com/boringnode/encryption/blob/1.x/README.md Configure the AES-SIV deterministic encryption driver. Requires a unique 'id' and a single 'key'. Note that 'expiresIn' is not supported and key rotation is not automatic. ```typescript import { aessiv } from '@boringnode/encryption/drivers/aes_siv' const config = aessiv({ id: 'app', key: 'your-32-character-secret-key-here', }) ``` -------------------------------- ### Configure AES-256-CBC Driver Source: https://github.com/boringnode/encryption/blob/1.x/README.md Configure the AES-256-CBC encryption driver. Requires a unique 'id' and an array of 'keys'. ```typescript import { aes256cbc } from '@boringnode/encryption/drivers/aes_256_cbc' const config = aes256cbc({ id: 'app', keys: ['your-32-character-secret-key-here'], }) ``` -------------------------------- ### Initialize and Use EncryptionManager Source: https://context7.com/boringnode/encryption/llms.txt Register multiple encryption drivers with EncryptionManager and delegate encryption/decryption tasks. The default encrypter is used unless specified. ```typescript import { EncryptionManager } from '@boringnode/encryption' import { chacha20poly1305 } from '@boringnode/encryption/drivers/chacha20_poly1305' import { aes256gcm } from '@boringnode/encryption/drivers/aes_256_gcm' import { aessiv } from '@boringnode/encryption/drivers/aes_siv' const manager = new EncryptionManager({ default: 'primary', list: { primary: chacha20poly1305({ id: 'primary', keys: [process.env.PRIMARY_KEY!] }), session: aes256gcm({ id: 'session', keys: [process.env.SESSION_KEY!] }), pii: aessiv({ id: 'pii', key: process.env.PII_KEY! }), }, }) // Uses the 'primary' encrypter (default) const token = manager.encrypt({ userId: 1 }, '1h', 'auth') manager.decrypt(token, 'auth') // => { userId: 1 } // Use a specific encrypter by name const sessionToken = manager.use('session').encrypt({ sessionId: 'xyz' }) manager.use('session').decrypt(sessionToken) // => { sessionId: 'xyz' } // Deterministic PII via named encrypter const piiIndex = manager.use('pii').blindIndex('user@example.com', 'users.email') ``` -------------------------------- ### aessiv() — AES-SIV Deterministic Driver Factory Source: https://context7.com/boringnode/encryption/llms.txt Creates an EncryptionConfig using AES-SIV (RFC 5297), which produces a synthetic IV derived from the plaintext, making encryption fully deterministic. The same plaintext always produces the same ciphertext, enabling direct equality lookups on encrypted database columns. Due to this property, `expiresIn` is not supported and key rotation must be handled via an explicit migration strategy. ```APIDOC ## aessiv() — AES-SIV Deterministic Driver Factory ### Description Creates an `EncryptionConfig` using AES-SIV (RFC 5297), which produces a synthetic IV derived from the plaintext — making encryption fully deterministic. The same plaintext always produces the same ciphertext, enabling direct equality lookups on encrypted database columns. Because of this property, `expiresIn` is not supported and key rotation must be handled via an explicit migration strategy. ### Usage ```typescript import { Encryption } from '@boringnode/encryption' import { aessiv } from '@boringnode/encryption/drivers/aes_siv' const encryption = new Encryption( aessiv({ id: 'pii', key: process.env.PII_KEY!, // single key — no rotation array }) ) const email = 'user@example.com' // Same plaintext always yields the same ciphertext const enc1 = encryption.encrypt(email) const enc2 = encryption.encrypt(email) // enc1 === enc2 => true const decrypted = encryption.decrypt(enc1) // => "user@example.com" // Use for WHERE queries: store enc1 and search with enc2 // SELECT * FROM users WHERE encrypted_email = $1 ``` ### Parameters #### Driver Factory Parameters - **id** (string) - Required - A unique identifier for this encryption configuration, embedded in the ciphertext. - **key** (string) - Required - The single encryption key. Must be at least 16 characters long. ### Methods #### `encrypt(value: any): string` Encrypts the given value and returns a base64url encoded string. The ciphertext is deterministic. #### `decrypt(encryptedValue: string): T | null` Decrypts the given string and returns the original value. Returns `null` if decryption fails or the input is invalid. ``` -------------------------------- ### blindIndex() / blindIndexes() - Deterministic Equality Indexes Source: https://context7.com/boringnode/encryption/llms.txt Introduces `blindIndex` and `blindIndexes` for generating deterministic, purpose-namespaced HMAC-SHA-256 digests (blind indexes) using HKDF. These allow for equality queries on encrypted fields without decryption, supporting key rotation. ```APIDOC ## `blindIndex()` / `blindIndexes()` — Deterministic Equality Indexes Blind indexes are HKDF-derived, purpose-namespaced HMAC-SHA-256 digests that let you run equality queries on encrypted fields without decrypting them. `blindIndex()` computes the index for the current (first) key; `blindIndexes()` computes indexes across all keys for use during key rotation. ```typescript import { Encryption } from '@boringnode/encryption' import { chacha20poly1305 } from '@boringnode/encryption/drivers/chacha20_poly1305' const encryption = new Encryption( chacha20poly1305({ id: 'app', keys: [process.env.NEW_KEY!, process.env.OLD_KEY!], }) ) // Always normalise input before indexing const email = 'User@Example.COM'.trim().toLowerCase() // "user@example.com" // Single index for write path (uses first key only) const idx = encryption.blindIndex(email, 'users.email') // Store idx alongside the encrypted email column // INSERT INTO users (email_encrypted, email_index) VALUES ($1, $2) // During key rotation: query with ALL known indexes const indexes = encryption.blindIndexes(email, 'users.email') // SELECT * FROM users WHERE email_index = ANY($1::text[]) // Then re-encrypt matched rows with the new key and update to a single index ``` ``` -------------------------------- ### Configure Encryption with Multiple Keys for Rotation Source: https://github.com/boringnode/encryption/blob/1.x/README.md Initialize the Encryption class with multiple keys to support key rotation. The first key is used for encryption, while all keys are available for decryption. ```typescript 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) // Works ``` -------------------------------- ### base64UrlEncode() / base64UrlDecode() Source: https://context7.com/boringnode/encryption/llms.txt Standalone helper functions for URL-safe (RFC 4648 §5) base64 encoding and decoding. These functions automatically handle padding and return `null` for invalid input instead of throwing errors. ```APIDOC ## base64UrlEncode() / base64UrlDecode() — URL-Safe Base64 Utilities Standalone helpers for URL-safe (RFC 4648 §5) base64 encoding and decoding. Automatically strips `=` padding on encode and restores it on decode; returns `null` on invalid input instead of throwing. ### Functions - **`base64UrlEncode(data: string | Buffer)`**: Encodes the given data into a URL-safe base64 string. - **`base64UrlDecode(encoded: string, encoding?: BufferEncoding)`**: Decodes a URL-safe base64 string. If an encoding is specified (e.g., 'utf8'), it returns a string; otherwise, it returns a Buffer. Returns `null` if the input is invalid. ### Example Usage ```typescript import { base64UrlEncode, base64UrlDecode } from '@boringnode/encryption' // Encode a string const encoded = base64UrlEncode('Hello, World!') // => "SGVsbG8sIFdvcmxkIQ" (no +, /, or = characters) // Decode to a UTF-8 string const str = base64UrlDecode(encoded, 'utf8') // => "Hello, World!" // Decode to a Buffer (e.g., for binary data) const buf = base64UrlDecode(encoded) // => // Returns null on invalid input — never throws const bad = base64UrlDecode('!!!invalid!!!') // => null ``` ``` -------------------------------- ### Configure AES-256-GCM Driver Source: https://github.com/boringnode/encryption/blob/1.x/README.md Configure the AES-256-GCM encryption driver. Requires a unique 'id' and an array of 'keys'. ```typescript import { aes256gcm } from '@boringnode/encryption/drivers/aes_256_gcm' const config = aes256gcm({ id: 'app', keys: ['your-32-character-secret-key-here'], }) ``` -------------------------------- ### Configure ChaCha20-Poly1305 Driver Source: https://github.com/boringnode/encryption/blob/1.x/README.md Configure the ChaCha20-Poly1305 encryption driver. Requires a unique 'id' and an array of 'keys'. ```typescript import { chacha20poly1305 } from '@boringnode/encryption/drivers/chacha20_poly1305' const config = chacha20poly1305({ id: 'app', keys: ['your-32-character-secret-key-here'], }) ``` -------------------------------- ### Create Encryption Instance with EncryptionFactory Source: https://context7.com/boringnode/encryption/llms.txt Use EncryptionFactory to create a default or custom configured Encryption instance. The default configuration is suitable for testing only. ```typescript import { EncryptionFactory } from '@boringnode/encryption/factories' // Uses a built-in default key and id — suitable for tests only const encryption = new EncryptionFactory().create() const token = encryption.encrypt({ test: true }) const payload = encryption.decrypt<{ test: boolean }>(token) // => { test: true } ``` ```typescript import { aes256gcm } from '@boringnode/encryption/drivers/aes_256_gcm' const custom = new EncryptionFactory( aes256gcm({ id: 'test', keys: ['a-secure-test-key-32chars-long!!'] }) ).create() ``` -------------------------------- ### Initialize Message Verifier Source: https://github.com/boringnode/encryption/blob/1.x/README.md Instantiate the MessageVerifier with an array of secret keys. This verifier is used for signing and verifying data integrity without encryption. ```typescript import { MessageVerifier } from '@boringnode/encryption/message_verifier' const verifier = new MessageVerifier(['your-secret-key']) ``` -------------------------------- ### Initialize and Use MessageVerifier Source: https://context7.com/boringnode/encryption/llms.txt Create a MessageVerifier for HMAC-signed tokens. Supports key rotation by providing multiple secrets for verification. ```typescript import { MessageVerifier } from '@boringnode/encryption/message_verifier' const verifier = new MessageVerifier([process.env.APP_SECRET!]) // Sign with optional expiration and purpose const signed = verifier.sign({ userId: 1 }, '30m', 'csrf') // => "base64Payload.base64Hmac" // Verify — returns typed value or null const result = verifier.unsign<{ userId: number }>(signed, 'csrf') // => { userId: 1 } verifier.unsign(signed, 'wrong-purpose') // => null verifier.unsign('tampered.sig') // => null // Key rotation: list multiple secrets; first is used for signing, all tried for verification const rotatingVerifier = new MessageVerifier([process.env.NEW_SECRET!, process.env.OLD_SECRET!]) rotatingVerifier.unsign(oldSignedValue) // still works ``` -------------------------------- ### Blind Index Generation Source: https://github.com/boringnode/encryption/blob/1.x/README.md Generates a deterministic hash (blind index) for equality queries. It's recommended to use normalized primitive values and specify a purpose for the index. ```APIDOC ## blindIndex ### Description Generates a deterministic hash for equality queries. It's recommended to use normalized primitive values and specify a purpose for the index. ### Method Signature `encryption.blindIndex(value: string | number | boolean | Date, purpose: string): string` ### Parameters - **value** (string | number | boolean | Date) - The primitive value to index. Should be normalized. - **purpose** (string) - Required. Identifies the field or context (e.g., 'users.email', 'users.ssn'). ### Example ```typescript const index = encryption.blindIndex('foo@example.com', 'users.email') const emailIndex = encryption.blindIndex(email.trim().toLowerCase(), 'users.email') ``` ``` -------------------------------- ### aes256cbc() — AES-256-CBC Driver Factory Source: https://context7.com/boringnode/encryption/llms.txt Creates an EncryptionConfig using AES-256-CBC with per-message HKDF-derived keys and an HMAC-SHA-256 authentication tag. This driver is provided for legacy compatibility with systems that require CBC mode. The ciphertext format is `id.base64Cipher.base64Iv.base64Hmac`. ```APIDOC ## aes256cbc() — AES-256-CBC Driver Factory ### Description Creates an `EncryptionConfig` using AES-256-CBC with per-message HKDF-derived keys and an HMAC-SHA-256 authentication tag. Provided for legacy compatibility with systems that require CBC mode. The ciphertext format is `id.base64Cipher.base64Iv.base64Hmac`. ### Usage ```typescript import { Encryption } from '@boringnode/encryption' import { aes256cbc } from '@boringnode/encryption/drivers/aes_256_cbc' const encryption = new Encryption( aes256cbc({ id: 'legacy', keys: [process.env.LEGACY_KEY!], }) ) const encrypted = encryption.encrypt('sensitive data') // => "legacy.base64CipherText.base64Iv.base64Hmac" const decrypted = encryption.decrypt(encrypted) // => "sensitive data" ``` ### Parameters #### Driver Factory Parameters - **id** (string) - Required - A unique identifier for this encryption configuration, embedded in the ciphertext. - **keys** (string[]) - Required - An array of keys. The first key is used for encryption, and all keys are used for decryption. Keys must be at least 16 characters long. ### Methods #### `encrypt(value: any): string` Encrypts the given value and returns a base64url encoded string. #### `decrypt(encryptedValue: string): T | null` Decrypts the given string and returns the original value. Returns `null` if decryption fails or the input is invalid. ``` -------------------------------- ### Generate a Blind Index Source: https://github.com/boringnode/encryption/blob/1.x/README.md Use this to create a deterministic hash for equality queries on sensitive fields. Ensure the purpose string accurately identifies the field. ```typescript const index = encryption.blindIndex('foo@example.com', 'users.email') ``` -------------------------------- ### Blind Index Generation (Multiple) Source: https://github.com/boringnode/encryption/blob/1.x/README.md Generates all applicable blind indexes for a given value and purpose. Useful when rotating keys. ```APIDOC ## blindIndexes ### Description Generates all applicable blind indexes for a given value and purpose. Useful when rotating keys. ### Method Signature `encryption.blindIndexes(value: string | number | boolean | Date, purpose: string): string[]` ### Parameters - **value** (string | number | boolean | Date) - The primitive value to index. Should be normalized. - **purpose** (string) - Required. Identifies the field or context (e.g., 'users.email', 'users.ssn'). ### Example ```typescript const indexes = encryption.blindIndexes('foo@example.com', 'users.email') // Use SQL: WHERE email_index IN (...) ``` ``` -------------------------------- ### HMAC Compare Source: https://github.com/boringnode/encryption/blob/1.x/README.md Compares a data string against a pre-generated HMAC hash in a timing-safe manner. ```APIDOC ## Hmac.compare ### Description Compares a data string against a pre-generated HMAC hash in a timing-safe manner. ### Method Signature `hmac.compare(data: string, hash: string): boolean` ### Parameters - **data** (string) - The data to compare. - **hash** (string) - The HMAC hash to compare against. ### Returns - (boolean) - True if the data matches the hash, false otherwise. ### Example ```typescript const isValid = hmac.compare('data to sign', hash) ``` ``` -------------------------------- ### URL-Safe Base64 Encoding and Decoding Source: https://context7.com/boringnode/encryption/llms.txt Utilities for URL-safe base64 encoding and decoding. Handles padding automatically and returns null for invalid input. ```typescript import { base64UrlEncode, base64UrlDecode } from '@boringnode/encryption' // Encode a string const encoded = base64UrlEncode('Hello, World!') // => "SGVsbG8sIFdvcmxkIQ" (no +, /, or = characters) // Decode to a UTF-8 string const str = base64UrlDecode(encoded, 'utf8') // => "Hello, World!" // Decode to a Buffer (e.g., for binary data) const buf = base64UrlDecode(encoded) // => // Returns null on invalid input — never throws const bad = base64UrlDecode('!!!invalid!!!') // => null ``` -------------------------------- ### chacha20poly1305() — ChaCha20-Poly1305 Driver Factory Source: https://context7.com/boringnode/encryption/llms.txt Creates an EncryptionConfig using the ChaCha20-Poly1305 AEAD cipher. This is the recommended driver for most use cases due to its speed and native authentication. The `id` prefix ensures the correct driver is identified on decryption. ```APIDOC ## chacha20poly1305() — ChaCha20-Poly1305 Driver Factory ### Description Creates an `EncryptionConfig` using the ChaCha20-Poly1305 AEAD cipher. This is the recommended driver for most use cases: it is modern, fast, and natively authenticated (no separate HMAC step). The `id` prefix is embedded in every ciphertext so the correct driver is always identified on decryption. ### Usage ```typescript 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!], // must be ≥ 16 characters, cannot contain "." }) ) // Encrypt any supported type const token = encryption.encrypt({ userId: 42, role: 'admin' }) // => "app.base64CipherText.base64Iv.base64Tag" // Decrypt — returns typed value or null on failure const payload = encryption.decrypt<{ userId: number; role: string }>(token) // => { userId: 42, role: 'admin' } // Invalid / tampered input returns null — never throws const bad = encryption.decrypt('tampered.garbage.data.here') // => null ``` ### Parameters #### Driver Factory Parameters - **id** (string) - Required - A unique identifier for this encryption configuration, embedded in the ciphertext. - **keys** (string[]) - Required - An array of keys. The first key is used for encryption, and all keys are used for decryption. Keys must be at least 16 characters long and cannot contain the '.' character. ### Methods #### `encrypt(value: any): string` Encrypts the given value and returns a base64url encoded string. #### `decrypt(encryptedValue: string): T | null` Decrypts the given string and returns the original value. Returns `null` if decryption fails or the input is invalid. ``` -------------------------------- ### Generate Deterministic Blind Indexes Source: https://context7.com/boringnode/encryption/llms.txt Use blindIndex() to create a purpose-namespaced HMAC-SHA-256 digest for the current encryption key, suitable for equality queries on encrypted fields. Use blindIndexes() to generate indexes across all keys, essential during key rotation. ```typescript import { Encryption } from '@boringnode/encryption' import { chacha20poly1305 } from '@boringnode/encryption/drivers/chacha20_poly1305' const encryption = new Encryption( chacha20poly1305({ id: 'app', keys: [process.env.NEW_KEY!, process.env.OLD_KEY!], }) ) // Always normalise input before indexing const email = 'User@Example.COM'.trim().toLowerCase() // "user@example.com" // Single index for write path (uses first key only) const idx = encryption.blindIndex(email, 'users.email') // Store idx alongside the encrypted email column // INSERT INTO users (email_encrypted, email_index) VALUES ($1, $2) // During key rotation: query with ALL known indexes const indexes = encryption.blindIndexes(email, 'users.email') // SELECT * FROM users WHERE email_index = ANY($1::text[]) // Then re-encrypt matched rows with the new key and update to a single index ``` -------------------------------- ### AES-SIV Deterministic Encryption Source: https://context7.com/boringnode/encryption/llms.txt Use AES-SIV for fully deterministic encryption where the same plaintext always produces the same ciphertext. This is useful for database equality queries. Note that `expiresIn` is not supported, and key rotation requires an explicit migration strategy. This driver uses a single key. ```typescript import { Encryption } from '@boringnode/encryption' import { aessiv } from '@boringnode/encryption/drivers/aes_siv' const encryption = new Encryption( aessiv({ id: 'pii', key: process.env.PII_KEY!, }) ) const email = 'user@example.com' // Same plaintext always yields the same ciphertext const enc1 = encryption.encrypt(email) const enc2 = encryption.encrypt(email) // enc1 === enc2 => true const decrypted = encryption.decrypt(enc1) // => "user@example.com" // Use for WHERE queries: store enc1 and search with enc2 // SELECT * FROM users WHERE encrypted_email = $1 ``` -------------------------------- ### Generate Blind Index with Normalization Source: https://github.com/boringnode/encryption/blob/1.x/README.md Before generating a blind index, normalize primitive values like strings by trimming whitespace and converting to lowercase to ensure consistent indexing. ```typescript const emailIndex = encryption.blindIndex(email.trim().toLowerCase(), 'users.email') ``` -------------------------------- ### Generate Multiple Blind Indexes Source: https://github.com/boringnode/encryption/blob/1.x/README.md When rotating keys, generate all possible blind indexes for a given value to ensure backward compatibility during queries. Use the SQL `IN` clause for efficient querying. ```typescript const indexes = encryption.blindIndexes('foo@example.com', 'users.email') // Use SQL: WHERE email_index IN (...) ``` -------------------------------- ### Encrypt with Expiration Support Source: https://github.com/boringnode/encryption/blob/1.x/README.md Encrypt data with a time-to-live (TTL). Decryption will return null if the token has expired. ```typescript // 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) // => null ``` -------------------------------- ### AES-256-CBC Encryption Source: https://context7.com/boringnode/encryption/llms.txt Employ AES-256-CBC for legacy compatibility, using HKDF-derived keys and an HMAC-SHA-256 tag. The ciphertext format is `id.base64Cipher.base64Iv.base64Hmac`. ```typescript import { Encryption } from '@boringnode/encryption' import { aes256cbc } from '@boringnode/encryption/drivers/aes_256_cbc' const encryption = new Encryption( aes256cbc({ id: 'legacy', keys: [process.env.LEGACY_KEY!], }) ) const encrypted = encryption.encrypt('sensitive data') // => "legacy.base64CipherText.base64Iv.base64Hmac" const decrypted = encryption.decrypt(encrypted) // => "sensitive data" ``` -------------------------------- ### Base64 URL-Safe Encoding and Decoding Source: https://github.com/boringnode/encryption/blob/1.x/README.md Utilize URL-safe base64 encoding and decoding functions. Specify the desired output encoding (e.g., 'utf8') when decoding. ```typescript import { base64UrlEncode, base64UrlDecode } from '@boringnode/encryption/base64' const encoded = base64UrlEncode('Hello World') const decoded = base64UrlDecode(encoded, 'utf8') ``` -------------------------------- ### aes256gcm() — AES-256-GCM Driver Factory Source: https://context7.com/boringnode/encryption/llms.txt Creates an EncryptionConfig using AES-256-GCM, an industry-standard authenticated encryption algorithm. This driver is suitable when NIST-standard AES is a compliance requirement. The API is identical to `chacha20poly1305`. ```APIDOC ## aes256gcm() — AES-256-GCM Driver Factory ### Description Creates an `EncryptionConfig` using AES-256-GCM, the industry-standard authenticated encryption algorithm. Suitable when NIST-standard AES is a compliance requirement. The API is identical to `chacha20poly1305`. ### Usage ```typescript import { Encryption } from '@boringnode/encryption' import { aes256gcm } from '@boringnode/encryption/drivers/aes_256_gcm' const encryption = new Encryption( aes256gcm({ id: 'session', keys: [process.env.SESSION_KEY!], }) ) const encrypted = encryption.encrypt({ sessionId: 'abc123', expiresAt: new Date('2025-12-31') }) // => "session.base64CipherText.base64Iv.base64Tag" const decrypted = encryption.decrypt<{ sessionId: string; expiresAt: string }>(encrypted) // => { sessionId: 'abc123', expiresAt: '2025-12-31T00:00:00.000Z' } ``` ### Parameters #### Driver Factory Parameters - **id** (string) - Required - A unique identifier for this encryption configuration, embedded in the ciphertext. - **keys** (string[]) - Required - An array of keys. The first key is used for encryption, and all keys are used for decryption. Keys must be at least 16 characters long. ### Methods #### `encrypt(value: any): string` Encrypts the given value and returns a base64url encoded string. #### `decrypt(encryptedValue: string): T | null` Decrypts the given string and returns the original value. Returns `null` if decryption fails or the input is invalid. ``` -------------------------------- ### Handle Decryption Failures Source: https://github.com/boringnode/encryption/blob/1.x/README.md The library returns `null` for decryption failures instead of throwing exceptions. This simplifies error handling and mitigates timing attacks. ```typescript const result = encryption.decrypt(maybeInvalidValue) if (result === null) { // Invalid, expired, wrong purpose, or tampered } ``` -------------------------------- ### MessageVerifier Source: https://context7.com/boringnode/encryption/llms.txt Creates HMAC-signed tokens that are tamper-evident but not encrypted. Useful for signed URLs, CSRF tokens, or any data where integrity is important but confidentiality is not. ```APIDOC ## MessageVerifier — HMAC-Signed Tokens (No Encryption) `MessageVerifier` base64url-encodes the payload and appends an HMAC-SHA-256 signature. The content is **not encrypted** — it is readable but tamper-evident. This is useful for signed URLs, CSRF tokens, or any data where confidentiality is not required but integrity is. ### Methods - **`sign(payload, ttl?, purpose?)`**: Base64url-encodes the payload and appends an HMAC-SHA-256 signature. Optionally takes a time-to-live and a purpose string. - **`unsign(signedString, purpose?)`**: Verifies the signature of the signed string. Returns the typed payload if valid, otherwise returns `null`. ### Key Rotation Provide multiple secrets in the constructor. The first secret is used for signing, and all secrets are tried for verification, allowing for seamless key rotation. ### Example Usage ```typescript import { MessageVerifier } from '@boringnode/encryption/message_verifier' const verifier = new MessageVerifier([process.env.APP_SECRET!]) // Sign with optional expiration and purpose const signed = verifier.sign({ userId: 1 }, '30m', 'csrf') // => "base64Payload.base64Hmac" // Verify — returns typed value or null const result = verifier.unsign<{ userId: number }>(signed, 'csrf') // => { userId: 1 } verifier.unsign(signed, 'wrong-purpose') // => null verifier.unsign('tampered.sig') // => null // Key rotation: list multiple secrets; first is used for signing, all tried for verification const rotatingVerifier = new MessageVerifier([process.env.NEW_SECRET!, process.env.OLD_SECRET!]) rotatingVerifier.unsign(oldSignedValue) // still works ``` ``` -------------------------------- ### Base64 URL Encode Source: https://github.com/boringnode/encryption/blob/1.x/README.md Encodes a string into a URL-safe base64 format. ```APIDOC ## base64UrlEncode ### Description Encodes a string into a URL-safe base64 format. ### Method Signature `base64UrlEncode(input: string): string` ### Parameters - **input** (string) - The string to encode. ### Returns - (string) - The URL-safe base64 encoded string. ### Example ```typescript const encoded = base64UrlEncode('Hello World') ``` ``` -------------------------------- ### Encrypt with Purpose Source: https://github.com/boringnode/encryption/blob/1.x/README.md Encrypt data with an optional purpose string. The same purpose must be provided during decryption to prevent misuse. ```typescript // 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) // => null ``` -------------------------------- ### ChaCha20-Poly1305 Encryption Source: https://context7.com/boringnode/encryption/llms.txt Use the ChaCha20-Poly1305 driver for modern, fast, and authenticated encryption. The `id` is embedded in the ciphertext for automatic driver selection during decryption. Ensure keys are at least 16 characters long and do not contain '.'. ```typescript 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 supported type const token = encryption.encrypt({ userId: 42, role: 'admin' }) // => "app.base64CipherText.base64Iv.base64Tag" // Decrypt — returns typed value or null on failure const payload = encryption.decrypt<{ userId: number; role: string }>(token) // => { userId: 42, role: 'admin' } // Invalid / tampered input returns null — never throws const bad = encryption.decrypt('tampered.garbage.data.here') // => null ``` -------------------------------- ### decrypt() - Decrypt Typed Values Source: https://context7.com/boringnode/encryption/llms.txt Details the `decrypt` method, which attempts decryption using all registered keys and returns `null` for invalid, expired, tampered, or purpose-mismatched data, ensuring it never throws exceptions. ```APIDOC ## `decrypt()` — Decrypt Typed Values `Encryption.decrypt()` tries all registered keys in order and returns `null` if none produce a valid result (wrong key, tampered data, expired, wrong purpose). It never throws. ```typescript 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!] }) ) interface UserPayload { userId: number; role: string } function getUserFromToken(raw: string): UserPayload | null { const payload = encryption.decrypt(raw, 'auth') if (payload === null) { // Covers: invalid format, tampered data, expired token, wrong purpose return null } return payload } const token = encryption.encrypt({ userId: 7, role: 'editor' }, '2h', 'auth') getUserFromToken(token) // => { userId: 7, role: 'editor' } getUserFromToken('garbage') // => null ``` ``` -------------------------------- ### HMAC Generate Source: https://github.com/boringnode/encryption/blob/1.x/README.md Generates an HMAC signature for a given data string using a secret key. ```APIDOC ## Hmac.generate ### Description Generates an HMAC signature for a given data string using a secret key. ### Method Signature `hmac.generate(data: string): string` ### Parameters - **data** (string) - The data to sign. ### Returns - (string) - The generated HMAC hash. ### Example ```typescript const hash = hmac.generate('data to sign') ``` ``` -------------------------------- ### Encrypt Data with Expiration and Purpose Source: https://context7.com/boringnode/encryption/llms.txt Use the encrypt method to add an expiration time (as a string duration or number of milliseconds) and an optional purpose tag to the encrypted data. Purpose-bound ciphertexts require the same purpose string for decryption. ```typescript 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!] }) ) // --- With expiration (string durations: ms, s, m, h, d) --- const expiringToken = encryption.encrypt({ userId: 1 }, '15m') // Decrypts successfully within 15 minutes; returns null after // --- With purpose --- const resetToken = encryption.encrypt({ userId: 1 }, undefined, 'password-reset') encryption.decrypt(resetToken, 'password-reset') // => { userId: 1 } encryption.decrypt(resetToken, 'email-verify') // => null (wrong purpose) encryption.decrypt(resetToken) // => null (missing purpose) // --- Object-options overload --- const token = encryption.encrypt( { userId: 1 }, { expiresIn: '1h', purpose: 'api-access' } ) encryption.decrypt(token, 'api-access') // => { userId: 1 } ``` -------------------------------- ### EncryptionManager Source: https://context7.com/boringnode/encryption/llms.txt Manages multiple named encryption configurations and delegates encryption/decryption operations. It supports using a default encrypter or specifying one by name. ```APIDOC ## EncryptionManager Manages multiple named `EncryptionConfig` entries and resolves them on demand with caching. It exposes the same `encrypt`, `decrypt`, `blindIndex`, and `blindIndexes` methods, delegating to the `default` encrypter unless told otherwise. ### Methods - **`encrypt(payload, ttl?, purpose?)`**: Encrypts the given payload. Optionally takes a time-to-live and a purpose string. - **`decrypt(token, purpose?)`**: Decrypts the given token. Optionally takes a purpose string to verify against. - **`use(name)`**: Returns a specific encrypter instance by its registered name. - **`blindIndex(value, purpose)`**: Generates a deterministic blind index for a given value and purpose. - **`blindIndexes(values, purpose)`**: Generates deterministic blind indexes for multiple values. ### Example Usage ```typescript import { EncryptionManager } from '@boringnode/encryption' import { chacha20poly1305 } from '@boringnode/encryption/drivers/chacha20_poly1305' import { aes256gcm } from '@boringnode/encryption/drivers/aes_256_gcm' import { aessiv } from '@boringnode/encryption/drivers/aes_siv' const manager = new EncryptionManager({ default: 'primary', list: { primary: chacha20poly1305({ id: 'primary', keys: [process.env.PRIMARY_KEY!] }), session: aes256gcm({ id: 'session', keys: [process.env.SESSION_KEY!] }), pii: aessiv({ id: 'pii', key: process.env.PII_KEY! }), }, }) // Uses the 'primary' encrypter (default) const token = manager.encrypt({ userId: 1 }, '1h', 'auth') manager.decrypt(token, 'auth') // => { userId: 1 } // Use a specific encrypter by name const sessionToken = manager.use('session').encrypt({ sessionId: 'xyz' }) manager.use('session').decrypt(sessionToken) // => { sessionId: 'xyz' } // Deterministic PII via named encrypter const piiIndex = manager.use('pii').blindIndex('user@example.com', 'users.email') ``` ``` -------------------------------- ### Handle Encryption Errors with Error Types Source: https://context7.com/boringnode/encryption/llms.txt Catch specific error types from the '@boringnode/encryption' namespace when constructing an Encryption instance with invalid configurations, such as insecure keys. ```typescript import * as errors from '@boringnode/encryption' // or individually: // import { errors } from '@boringnode/encryption' // E_MISSING_ENCRYPTER_KEYS — Encryption constructed with no keys // E_MISSING_ENCRYPTER_KEY — Driver constructed with no key // E_INSECURE_ENCRYPTER_KEY — Key shorter than 16 characters // E_MISSING_ENCRYPTER_ID — Driver constructed without an id // E_INVALID_ENCRYPTER_ID — id is empty or contains "." // E_DETERMINISTIC_DRIVER_EXPIRES_IN_NOT_SUPPORTED — expiresIn used with AES-SIV // E_BLIND_INDEX_PURPOSE_REQUIRED — blindIndex called with empty purpose import { Encryption } from '@boringnode/encryption' import { chacha20poly1305 } from '@boringnode/encryption/drivers/chacha20_poly1305' try { new Encryption( chacha20poly1305({ id: 'app', keys: ['short'] }) // < 16 chars ) } catch (err) { if (err instanceof errors.E_INSECURE_ENCRYPTER_KEY) { console.error('Key too short:', err.message) // => "The value of your key should be at least 16 characters long" } } ``` -------------------------------- ### Hmac Source: https://context7.com/boringnode/encryption/llms.txt A low-level utility for generating and comparing URL-safe base64-encoded HMAC-SHA-256 digests using a timing-safe comparison. It is used internally by `MessageVerifier` and `AES-256-CBC`. ```APIDOC ## Hmac — Low-Level HMAC-SHA-256 Utility `Hmac` generates and compares URL-safe base64-encoded HMAC-SHA-256 digests using a timing-safe comparison. It is used internally by `MessageVerifier` and `AES-256-CBC` but is also exported for direct use. ### Constructor - **`new Hmac(key: Buffer)`**: Initializes the Hmac utility with a secret key as a Buffer. ### Methods - **`generate(data: string | Buffer)`**: Generates a URL-safe base64-encoded HMAC-SHA-256 digest for the given data. - **`compare(data: string | Buffer, digest: string)`**: Compares the HMAC-SHA-256 digest of the given data against the provided digest in a timing-safe manner. Returns `true` if they match, `false` otherwise. ### Example Usage ```typescript import { Hmac } from '@boringnode/encryption' import { createHash } from 'node:crypto' // Derive a Buffer key from a passphrase const key = createHash('sha256').update(process.env.APP_KEY!).digest() const hmac = new Hmac(key) const digest = hmac.generate('my-data-to-sign') // => URL-safe base64 string // Timing-safe comparison hmac.compare('my-data-to-sign', digest) // => true hmac.compare('tampered-data', digest) // => false ``` ``` -------------------------------- ### Base64 URL Decode Source: https://github.com/boringnode/encryption/blob/1.x/README.md Decodes a URL-safe base64 encoded string into its original format. ```APIDOC ## base64UrlDecode ### Description Decodes a URL-safe base64 encoded string into its original format. ### Method Signature `base64UrlDecode(encodedString: string, encoding?: BufferEncoding): string` ### Parameters - **encodedString** (string) - The URL-safe base64 encoded string to decode. - **encoding** (BufferEncoding, optional) - The character encoding to use for the decoded string (e.g., 'utf8'). Defaults to 'utf8'. ### Returns - (string) - The decoded string. ### Example ```typescript const decoded = base64UrlDecode(encoded, 'utf8') ``` ``` -------------------------------- ### Encrypt and Decrypt Data Source: https://github.com/boringnode/encryption/blob/1.x/README.md Encrypt arbitrary JavaScript objects and decrypt them back. The encrypted string includes the ID, ciphertext, IV, and tag. ```typescript // 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' } ``` -------------------------------- ### HMAC Signature Generation Source: https://github.com/boringnode/encryption/blob/1.x/README.md Generate HMAC signatures for data using a secret key. The `Hmac` class ensures secure signature generation. ```typescript import { Hmac } from '@boringnode/encryption' const hmac = new Hmac(secretKey) // Generate HMAC const hash = hmac.generate('data to sign') ```