@alessiofrittoli/crypto-cipher v2.2.0
Crypto Cipher đ
Node.js Cipher cryptograph utility library
Table of Contents
Getting started
The crypto-cipher
library provides AES encryption and decryption functionality, supporting both in-memory buffers and streams. It adheres to the NIST SP 800-38D standard recommendations.
â ď¸ Note that every performed operation cannot be accomplished client-side and must be executed on a back-end server.
It is part of the crypto
utility libraries and can be installed by running the following command:
npm i @alessiofrittoli/crypto-cipher
or using pnpm
pnpm i @alessiofrittoli/crypto-cipher
Key features
- Supports multiple AES algorithms (
CCM
,GCM
,OCB
,CBC
) and evenchacha20-poly1305
. - In-memory buffer encryption and decrpytion.
- Robust support for encrypting and decrypting streams (in-memory and file based), with seamless handling of key/IV extraction.
- Hybrid encryption methods for combining symmetric and asymmetric cryptography.
Options Management
- A solid options resolver mechanism ensures consistent handling of defaults and constraints.
Security Considerations
- Random
salt
andIV
generation. - Authenticated encryption modes with proper
authTag
andAdditional Authenticated Data
handling.
Readable and Modular
- Separation of concerns with clear method responsibilities.
- Comprehensive JSDoc comments enhance maintainability and readability.
API Reference
Constants
Cipher.SALT_LENGTH
Defines the minimum, maximum, and default lengths for salt.
Property | Value |
---|---|
min | 16 |
max | 64 |
default | 32 |
Cipher.IV_LENGTH
Defines the minimum, maximum, and default lengths for initialization vectors (IV).
Property | Value |
---|---|
min | 8 |
max | 32 |
default | 16 |
Cipher.AUTH_TAG_LENGTH
Defines the minimum, maximum, and default lengths for authentication tags.
Property | Value |
---|---|
min | 4 |
max | 16 |
default | 16 |
Cipher.AAD_LENGTH
Defines the minimum, maximum, and default lengths for additional authenticated data (AAD).
Property | Value |
---|---|
min | 16 |
max | 4096 |
default | 32 |
Cipher.DEFAULT_ALGORITHM
Specifies default AES algorithms for buffer and stream operations.
Operation | Algorithm | Description |
---|---|---|
buffer | aes-256-gcm | Default algorithm used for buffer data encryption/decryption |
stream | aes-256-cbc | Default algorithm used for stream encryption/decryption |
Cipher.ALGORITHMS
Supported AES algorithms:
aes-128-gcm
aes-192-gcm
aes-256-gcm
aes-128-ccm
aes-192-ccm
aes-256-ccm
aes-128-ocb
aes-192-ocb
aes-256-ocb
aes-128-cbc
aes-192-cbc
aes-256-cbc
chacha20-poly1305
Methods
Cipher.encrypt()
Encrypts an in-memory data buffer.
â ď¸ This is not suitable for large data.
Use Cipher.streamEncrypt()
or Cipher.hybridEncrypt()
methods for large data encryption.
Name | Type | Description |
---|---|---|
data | CoerceToUint8ArrayInput | Data to encrypt. |
secret | CoerceToUint8ArrayInput | Secret key for encryption. |
options | Cph.Options | (Optional) Additional encryption options. |
Type: Buffer
- The encrypted result buffer.
- See
CoerceToUint8ArrayInput
for more informations about supported input data types. - See
Cph.Options
for more informations about additional encryption options. - See In-memory data buffer encryption/decryption examples.
Cipher.decrypt()
Decrypts an in-memory data buffer.
â ď¸ This is not suitable for large data.
Use Cipher.streamDecrypt()
or Cipher.hybridDecrypt()
methods for large data decryption.
Name | Type | Description |
---|---|---|
data | CoerceToUint8ArrayInput | Data to decrypt. |
secret | CoerceToUint8ArrayInput | Secret key for decryption. |
options | Cph.Options | (Optional) Decryption options (must match encryption). |
Type: Buffer
- The decrypted result buffer.
- See
CoerceToUint8ArrayInput
for more informations about supported input data types. - See
Cph.Options
for more informations about additional decryption options. - See In-memory data buffer encryption/decryption examples.
Cipher.streamEncrypt()
Encrypts a Readable
stream to a Writable
stream.
Name | Type | Description |
---|---|---|
secret | CoerceToUint8ArrayInput | Secret key for encryption. |
options | Cph.Stream.Symmetric.EncryptOptions | Stream encryption options. |
Type: Cph.Stream.Symmetric.EncryptReturnType
- An object containing:
- a new instance of
crypto.Cipher
allowing you to add listeners to thecipher
encryption process. - the actual
encrypt
callback that must be called and awaited in order to start the encryption process.
- a new instance of
- See
CoerceToUint8ArrayInput
for more informations about supported input data types. - See
Cph.Stream.Symmetric.EncryptOptions
for more informations about encryption options. - See In-memory data stream encryption/decryption examples.
- See File based data stream encryption/decryption examples.
Cipher.streamDecrypt()
Decrypts a Readable
stream to a Writable
stream.
Name | Type | Description |
---|---|---|
secret | CoerceToUint8ArrayInput | Secret key for decryption. |
options | Cph.Stream.Symmetric.DecryptOptions | Stream decryption options. |
Type: Promise<Cph.Stream.Symmetric.DecryptReturnType>
- A new Promise that resolves when Key IV extraction completes returning an object containing:
- a new instance of
crypto.Decipher
allowing you to add listeners to thedecipher
decryption process. - the actual
decrypt
callback that must be called and awaited in order to start the decryption process.
- a new instance of
- See
CoerceToUint8ArrayInput
for more informations about supported input data types. - See
Cph.Stream.Symmetric.DecryptOptions
for more informations about decryption options. - See In-memory data stream encryption/decryption examples.
- See File based data stream encryption/decryption examples.
Cipher.hybridEncrypt()
Encrypts a stream using hybrid encryption (symmetric + RSA).
Name | Type | Description |
---|---|---|
secret | CoerceToUint8ArrayInput | Symmetric secret key. |
publicKey | crypto.RsaPublicKey \| crypto.KeyLike | RSA public key used to encrypt the generated symmetric key. |
options | Cph.Stream.Hybrid.EncryptOptions | Stream encryption options. |
Type: Cph.Stream.Hybrid.EncryptReturnType
- An object containing:
- a new instance of
cipher
allowing you to add listeners to thecipher
encryption process. - the actual
encrypt
callback that must be called and awaited to start the encryption process.
- a new instance of
- See
CoerceToUint8ArrayInput
for more informations about supported input data types. - See
Cph.Stream.Hybrid.EncryptOptions
for more informations about encryption options. - See In-memory data stream with hybrid encryption/decryption examples.
- See File based data stream with hybrid encryption/decryption examples.
Cipher.hybridDecrypt()
Decrypts a stream using hybrid decryption (symmetric + RSA).
Name | Type | Description |
---|---|---|
privateKey | crypto.RsaPrivateKey \| crypto.KeyLike | RSA private key for used to decrpyt the encrypted symmetric key. |
options | Cph.Stream.Hybrid.DecryptOptions | Stream decryption options. |
Type: Promise<Cph.Stream.Hybrid.DecryptReturnType>
- A new Promise that resolves when Key IV extraction completes returning an object containing:
- a new instance of
crypto.Decipher
allowing you to add listeners to thedecipher
decryption process. - the actual
decrypt
callback that must be called and awaited in order to start the decryption process.
- a new instance of
- See
Cph.Stream.Hybrid.DecryptOptions
for more informations about decryption options. - See In-memory data stream with hybrid encryption/decryption examples.
- See File based data stream with hybrid encryption/decryption examples.
Types
CoerceToUint8ArrayInput
This module supports different input data types and it uses the coerceToUint8Array
utility function from @alessiofrittoli/crypto-buffer
to convert it to a Uint8Array
.
- See
coerceToUint8Array
for more informations about the supported input types.
Cph.CBCTypes
Cipher CBC algorithm types.
Cph.AesAlgorithm
Supported AES algorithm types.
Cph.Options<T>
Common options in encryption/decryption processes.
Parameter | Default | Description |
---|---|---|
T | Cph.AesAlgorithm | Accepted algorithm in Cph.Options . This is usefull to constraint specifc algorithms. |
Property | Type | Default | Description |
---|---|---|---|
algorithm | T | aes-256-gcm \| aes-256-cbc | Accepted algorithms. |
salt | number | 32 | The salt length in bytes. Minimum: 16 , Maximum: 64 . |
iv | number | 16 | The Initialization Vector length in bytes. Minimum: 8 , Maximum: 32 . |
authTag | number | 16 | The authTag length in bytes. Minimum: 4 , Maximum: 16 . |
aad | CoerceToUint8ArrayInput | - | Custom Additional Authenticated Data. aadLength is then automatically resolved. If not provided, a random AAD is generated with a max length of aadLength . |
aadLength | number | 32 | The auto generated AAD length in bytes. Minimum: 16 , Maximum: 128 . |
Cph.Stream.Symmetric.EncryptOptions
Stream symmetric encryption options.
- Extends
Cph.Options<Cph.CBCTypes>
.
Property | Type | Description |
---|---|---|
input | Readable | The Readable Stream from where raw data to encrypt is read. |
output | Writable | The Writable Stream where encrypted data is written. |
Cph.Stream.Symmetric.EncryptReturnType
Returnign object from Cipher.streamEncrypt()
method.
Property | Type | Description |
---|---|---|
cipher | crypto.Cipher | The crypto.Cipher instance. |
encrypt | () => Promise<void> | The actual encrypt callback that must be called and awaited in order to start the encryption process. |
Cph.Stream.Symmetric.DecryptOptions
Stream symmetric decryption options.
- Extends
Cph.Stream.Symmetric.EncryptOptions
.
Property | Type | Description |
---|---|---|
input | Readable | The Readable Stream from where encrypted data is read. |
output | Writable | The Writable Stream where decrypted data is written. |
Cph.Stream.Symmetric.DecryptReturnType
Returnign object from awaited Cipher.streamDecrypt()
method.
Property | Type | Description |
---|---|---|
decipher | crypto.Decipher | The crypto.Decipher instance. |
decrypt | () => Promise<void> | The actual decrypt callback that must be called and awaited in order to start the decryption process. |
Cph.Stream.Hybrid.EncryptOptions
Stream hybrid encryption options.
- Alias for
Cph.Stream.Symmetric.EncryptOptions
Cph.Stream.Hybrid.EncryptReturnType
Returnign object from Cipher.hybridEncrypt()
method.
- Alias for
Cph.Stream.Symmetric.EncryptReturnType
Cph.Stream.Hybrid.DecryptOptions
Stream hybrid decryption options.
- Extends
Cph.Stream.Symmetric.DecryptOptions
.
Property | Type | Description |
---|---|---|
rsaKeyLength | number | The RSA key length in bytes used while encrypting data. This is used to properly extract the encrypted Cipher Key and Initialization Vector from the encrypted data. |
Cph.Stream.Hybrid.DecryptReturnType
Returnign object from awaited Cipher.hybridDecrypt()
method.
- Alias for
Cph.Stream.Symmetric.DecryptReturnType
Examples
Importing the library
import { Cipher } from '@alessiofrittoli/crypto-cipher'
import type { Cph as CipherTypes } from '@alessiofrittoli/crypto-cipher/types'
In-memory data buffer encryption/decryption
The simpliest way to encrypt/decrypt in-memory data buffers.
// encrypt
const data = 'my top-secret data'
const password = 'my-very-strong-password'
const encrypted = Cipher.encrypt( data, password )
// decrypt
const decrypted = Cipher.decrypt( encrypted, password )
console.log( decrypted ) // Outputs: my top-secret data
In-memory data stream encryption/decryption
The in-memory data stream comes pretty handy when, for example, we need to stream encrypted data within a Server Response or to decrypt stream data from a Server Response.
Streaming
// /api/stream-encrypt
import { Readable, Writable } from 'stream'
const routeHandler = () => {
const data = 'my top-secret data'
const password = 'my-very-strong-password'
const stream = new TransformStream()
const writer = stream.writable.getWriter()
// Create a `Readable` Stream with raw data.
const input = new Readable( {
read()
{
this.push( data )
this.push( null ) // Signal end of stream
},
} )
// `Writable` Stream where encrypted data is written
const output = new Writable( {
write( chunk, encoding, callback )
{
writer.write( chunk )
callback()
},
final( callback )
{
writer.close()
callback()
}
} )
Cipher.streamEncrypt( password, { input, output } )
.encrypt()
return (
// encrypted stream
new Response( stream.readable )
)
}
Decrypting received stream
// /api/stream-decrypt
import { Transform, Writable } from 'stream'
import { StreamReader } from '@alessiofrittoli/stream-reader'
const password = 'my-very-strong-password'
const routeHandler = () => (
fetch( '/api/stream-encrypt' )
.then( response => {
if ( ! response.body ) {
return (
new Respone( null, { status: 400 } )
)
}
const stream = new TransformStream()
const writer = stream.writable.getWriter()
const reader = new StreamReader( response.body )
const input = new Transform()
reader.read()
reader.on( 'read', chunk => {
input.push( chunk )
} )
reader.on( 'close', () => {
input.push( null )
} )
const output = new Writable( {
write( chunk, encoding, callback )
{
writer.write( chunk )
callback()
},
final( callback ) {
writer.close()
callback()
},
} )
const { decrypt } = await Cipher.streamDecrypt( password, { input, output } )
decrypt()
return (
// decrypted stream
new Response( stream.readable )
)
} )
)
In-memory data stream with hybrid encryption/decryption
Hybrid encryption offers an higher level of security by encrypting the generated symmetric key with asymmetric RSA keys.
Keypair
const password = 'my-very-strong-password'
/** RSA modulus length is required for proper key extraction during decryption process. */
const rsaKeyLength = 512 // bytes
const keyPair = crypto.generateKeyPairSync( 'rsa', {
modulusLength : rsaKeyLength * 8, // 4096 bits
publicKeyEncoding : { type: 'spki', format: 'pem' },
privateKeyEncoding : { type: 'pkcs1', format: 'pem' },
} )
// or you can optionally set a custom passphrase
const keyPair = crypto.generateKeyPairSync( 'rsa', {
modulusLength : rsaKeyLength * 8, // 4096 bits
publicKeyEncoding : { type: 'spki', format: 'pem' },
privateKeyEncoding : { type: 'pkcs1', format: 'pem', passphrase: password, cipher: 'aes-256-cbc' },
} )
Encrypt
const data = 'my top-secret data'
/** Store encrypted chunks for next example. */
const encryptedChunks: Buffer[] = []
// Create a `Readable` Stream with raw data.
const input = new Readable( {
read()
{
this.push( data ) // Push data to encrypt
this.push( null ) // Signal end of stream
},
} )
// Create a `Writable` Stream where encrypted data is written
const output = new Writable( {
write( chunk, encoding, callback )
{
// push written chunk to `encryptedChunks` for further usage.
encryptedChunks.push( chunk )
callback()
}
} )
const { encrypt } = Cipher.hybridEncrypt( password, {
key : keyPair.publicKey,
padding : crypto.constants.RSA_PKCS1_OAEP_PADDING,
oaepHash : 'SHA-256',
}, { input, output } )
await encrypt()
Decrypt
/** Store decrypted chunks. */
const chunks: Buffer[] = []
// Create a `Readable` Stream with encrypted data.
const input = new Readable( {
read()
{
this.push( Buffer.concat( encryptedChunks ) ) // Push data to decrypt
this.push( null ) // Signal end of stream
},
} )
// Create a `Writable` Stream where decrypted data is written
const output = new Writable( {
write( chunk, encoding, callback )
{
chunks.push( chunk )
callback()
},
} )
const { decrypt } = await Cipher.hybridDecrypt(
{
key : keyPair.privateKey,
passphrase: password, // optional passhrase (required if set while generating keypair).
padding : crypto.constants.RSA_PKCS1_OAEP_PADDING,
oaepHash : 'SHA-256',
}, { input, output, rsaKeyLength }
)
await decrypt()
console.log( Buffer.concat( chunks ).toString() ) // Outputs: 'my top-secret data'
File based data stream encryption/decryption
Nothig differs from the In-memory data stream encryption/decryption example, except for input
and output
streams which now comes directly from files reading/writing.
Encrypt a file
import fs from 'fs'
const password = 'my-very-strong-password'
// input where raw data to encrypt is read
const input = fs.createReadStream( 'my-very-large-top-secret-file.pdf' )
// output where encrypted data is written
const output = fs.createWriteStream( 'my-very-large-top-secret-file.encrypted' )
// encrypt
await Cipher.streamEncrypt( password, { input, output } )
.encrypt()
Decrypt a file
import fs from 'fs'
const password = 'my-very-strong-password'
// input where encrypted data is read
const input = fs.createReadStream( 'my-very-large-top-secret-file.encrypted' )
// output where decrypted data is written
const output = fs.createWriteStream( 'my-very-large-top-secret-file-decrypted.pdf' )
// decrypt
const { decrypt } = await Cipher.streamDecrypt( password, { input, output } )
await decrypt()
File based data stream with hybrid encryption/decryption
Nothig differs from the In-memory data stream with hybrid encryption/decryption example, except for input
and output
streams which now comes directly from files reading/writing.
Keypair
const password = 'my-very-strong-password'
/** RSA modulus length is required for proper key extraction during decryption process. */
const rsaKeyLength = 512 // bytes
const keyPair = crypto.generateKeyPairSync( 'rsa', {
modulusLength : rsaKeyLength * 8, // 4096 bits
publicKeyEncoding : { type: 'spki', format: 'pem' },
privateKeyEncoding : { type: 'pkcs1', format: 'pem' },
} )
// or you can optionally set a custom passphrase
const keyPair = crypto.generateKeyPairSync( 'rsa', {
modulusLength : rsaKeyLength * 8, // 4096 bits
publicKeyEncoding : { type: 'spki', format: 'pem' },
privateKeyEncoding : { type: 'pkcs1', format: 'pem', passphrase: password, cipher: 'aes-256-cbc' },
} )
Encrypt a file
import fs from 'fs'
const password = 'my-very-strong-password'
// input where raw data to encrypt is read
const input = fs.createReadStream( 'my-very-large-top-secret-file.pdf' )
// output where encrypted data is written
const output = fs.createWriteStream( 'my-very-large-top-secret-file.encrypted' )
// encrypt
const { encrypt } = Cipher.hybridEncrypt( password, {
key : keyPair.publicKey,
padding : crypto.constants.RSA_PKCS1_OAEP_PADDING,
oaepHash : 'SHA-256',
}, { input, output } )
await encrypt()
Decrypt a file
import fs from 'fs'
const password = 'my-very-strong-password'
// input where encrypted data is read
const input = fs.createReadStream( 'my-very-large-top-secret-file.encrypted' )
// output where decrypted data is written
const output = fs.createWriteStream( 'my-very-large-top-secret-file-decrypted.pdf' )
// decrypt
const { decrypt } = await Cipher.hybridDecrypt(
{
key : keyPair.privateKey,
passphrase: password, // optional passhrase (required if set while generating keypair).
padding : crypto.constants.RSA_PKCS1_OAEP_PADDING,
oaepHash : 'SHA-256',
}, { input, output, rsaKeyLength }
)
Development
Install depenendencies
npm install
or using pnpm
pnpm i
Build the source code
Run the following command to test and build code for distribution.
pnpm build
ESLint
warnings / errors check.
pnpm lint
Jest
Run all the defined test suites by running the following:
# Run tests and watch file changes.
pnpm test:watch
# Run tests in a CI environment.
pnpm test:ci
- See
package.json
file scripts for more info.
Run tests with coverage.
An HTTP server is then started to serve coverage files from ./coverage
folder.
â ď¸ You may see a blank page the first time you run this command. Simply refresh the browser to see the updates.
test:coverage:serve
Contributing
Contributions are truly welcome!
Please refer to the Contributing Doc for more information on how to start contributing to this project.
Help keep this project up to date with GitHub Sponsor.
Security
If you believe you have found a security vulnerability, we encourage you to responsibly disclose this and NOT open a public issue. We will investigate all legitimate reports. Email security@alessiofrittoli.it
to disclose any security vulnerabilities.