Package Exports
Readme
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-cipheror using pnpm
pnpm i @alessiofrittoli/crypto-cipherKey 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
saltandIVgeneration. - Authenticated encryption modes with proper
authTagandAdditional Authenticated Datahandling.
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.
Properties
| Property | Value |
|---|---|
min |
16 |
max |
64 |
default |
32 |
Cipher.IV_LENGTH
Defines the minimum, maximum, and default lengths for initialization vectors (IV).
Properties
| Property | Value |
|---|---|
min |
8 |
max |
32 |
default |
16 |
Cipher.AUTH_TAG_LENGTH
Defines the minimum, maximum, and default lengths for authentication tags.
Properties
| Property | Value |
|---|---|
min |
4 |
max |
16 |
default |
16 |
Cipher.AAD_LENGTH
Defines the minimum, maximum, and default lengths for additional authenticated data (AAD).
Properties
| Property | Value |
|---|---|
min |
16 |
max |
4096 |
default |
32 |
Cipher.DEFAULT_ALGORITHM
Specifies default AES algorithms for buffer and stream operations.
Properties
| 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:
Properties
aes-128-gcmaes-192-gcmaes-256-gcmaes-128-ccmaes-192-ccmaes-256-ccmaes-128-ocbaes-192-ocbaes-256-ocbaes-128-cbcaes-192-cbcaes-256-cbcchacha20-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.
Parameters
| Name | Type | Description |
|---|---|---|
data |
CoerceToUint8ArrayInput |
Data to encrypt. |
secret |
CoerceToUint8ArrayInput |
Secret key for encryption. |
options |
Cph.Options |
(Optional) Additional encryption options. |
Returns
Type: Buffer
- The encrypted result buffer.
- See
CoerceToUint8ArrayInputfor more informations about supported input data types. - See
Cph.Optionsfor 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.
Parameters
| Name | Type | Description |
|---|---|---|
data |
CoerceToUint8ArrayInput |
Data to decrypt. |
secret |
CoerceToUint8ArrayInput |
Secret key for decryption. |
options |
Cph.Options |
(Optional) Decryption options (must match encryption). |
Returns
Type: Buffer
- The decrypted result buffer.
- See
CoerceToUint8ArrayInputfor more informations about supported input data types. - See
Cph.Optionsfor more informations about additional decryption options. - See In-memory data buffer encryption/decryption examples.
Cipher.streamEncrypt()
Encrypts a Readable stream to a Writable stream.
Parameters
| Name | Type | Description |
|---|---|---|
secret |
CoerceToUint8ArrayInput |
Secret key for encryption. |
options |
Cph.Stream.Symmetric.EncryptOptions |
Stream encryption options. |
Returns
Type: Cph.Stream.Symmetric.EncryptReturnType
- An object containing:
- a new instance of
crypto.Cipherallowing you to add listeners to thecipherencryption process. - the actual
encryptcallback that must be called and awaited in order to start the encryption process.
- a new instance of
- See
CoerceToUint8ArrayInputfor more informations about supported input data types. - See
Cph.Stream.Symmetric.EncryptOptionsfor 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.
Parameters
| Name | Type | Description |
|---|---|---|
secret |
CoerceToUint8ArrayInput |
Secret key for decryption. |
options |
Cph.Stream.Symmetric.DecryptOptions |
Stream decryption options. |
Returns
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.Decipherallowing you to add listeners to thedecipherdecryption process. - the actual
decryptcallback that must be called and awaited in order to start the decryption process.
- a new instance of
- See
CoerceToUint8ArrayInputfor more informations about supported input data types. - See
Cph.Stream.Symmetric.DecryptOptionsfor 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).
Parameters
| 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. |
Returns
Type: Cph.Stream.Hybrid.EncryptReturnType
- An object containing:
- a new instance of
cipherallowing you to add listeners to thecipherencryption process. - the actual
encryptcallback that must be called and awaited to start the encryption process.
- a new instance of
- See
CoerceToUint8ArrayInputfor more informations about supported input data types. - See
Cph.Stream.Hybrid.EncryptOptionsfor 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).
Parameters
| 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. |
Returns
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.Decipherallowing you to add listeners to thedecipherdecryption process. - the actual
decryptcallback that must be called and awaited in order to start the decryption process.
- a new instance of
- See
Cph.Stream.Hybrid.DecryptOptionsfor 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
coerceToUint8Arrayfor 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.
Type parameters
| Parameter | Default | Description |
|---|---|---|
T |
Cph.AesAlgorithm |
Accepted algorithm in Cph.Options. This is usefull to constraint specifc algorithms. |
Properties
| 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>.
Properties
| 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.
Properties
| 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.
Properties
| 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.
Properties
| 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.
Properties
| 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 dataIn-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 installor using pnpm
pnpm iBuild the source code
Run the following command to test and build code for distribution.
pnpm buildESLint
warnings / errors check.
pnpm lintJest
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:ciYou can eventually run specific suits like so:
pnpm test:buffer-in-memory
pnpm test:file-symmetric
pnpm test:file-hybrid
pnpm test:stream-symmetric
pnpm test:stream-hybrid
pnpm test:miscRun 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:serveContributing
Contributions are truly welcome!
Please refer to the Contributing Doc for more information on how to start contributing to this project.
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.
Made with ☕
|
|
|