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 even chacha20-poly1305.
- In-memory buffer encryption and decrpytion.
- Robust support for encrypting and decrypting streams (in-memory and file based).
- Hybrid encryption methods for combining symmetric and asymmetric cryptography.
Security Considerations
- Random
salt and IV generation.
- AEAD - Authenticated encryption modes with proper
authTag and Additional Authenticated Data handling.
Readable and Modular
- Separation of concerns with clear method responsibilities.
- Comprehensive JSDoc comments enhance maintainability and readability.
What's Changed
š Core updates in the latest release:
Cipher methods have been refactored to provide a solid and easy usage
Cipher.HybridEncrypt() and Cipher.HybridDecrypt() have been added. These methods will allow you to encrypt/decrypt in-memory buffer data using hybrid encryption algorithms.
Cipher.stream subgroup has been added to keep method names consistent across the library.
- hybrid encryption doesn't require a password anymore which was redundant. an RSA key pair is all what you need.
- providing RSA key length during hybrid decryption is no longer needed.
Migration guide
Migrating from v2.x.x to v3.0.0
Cipher.encrypt()
Is now renamed to Cipher.Encrypt(). It's API implementation remain the same.
Cipher.decrypt()
Is now renamed to Cipher.Decrypt(). It's API implementation remain the same.
Cipher.streamEncrypt()
Before
await Cipher.streamEncrypt(password, { input, output }).encrypt();
Now
await Cipher.stream.Encrypt(password, { input, output });
Cipher.streamDecrypt()
Before
const { decrypt } = await Cipher.streamDecrypt(password, { input, output });
await decrypt();
Now
await Cipher.stream.Decrypt(password, { input, output });
Cipher.hybridEncrypt()
Before
const { encrypt } = Cipher.hybridEncrypt(
password,
{
key: keyPair.publicKey,
padding: crypto.constants.RSA_PKCS1_OAEP_PADDING,
oaepHash: "SHA-256",
},
{ input, output }
);
await encrypt();
Now
await Cipher.stream.HybridEncrypt(keyPair.publicKey, { input, output });
Cipher.hybridDecrypt()
Before
const { decrypt } = await Cipher.hybridDecrypt(
{
key: keyPair.privateKey,
passphrase: passphrase,
padding: crypto.constants.RSA_PKCS1_OAEP_PADDING,
oaepHash: "SHA-256",
},
{ input, output, rsaKeyLength }
);
await decrypt();
Now
await Cipher.stream.HybridDecrypt(
{ key: keyPair.privateKey, passphrase },
{ input, output }
);
Or even
await Cipher.stream.HybridDecrypt(keyPair.privateKey, { input, output });
API Reference
Constants
Cipher.SALT_LENGTH
Defines the minimum, maximum, and default lengths for salt.
Properties
Cipher.IV_LENGTH
Defines the minimum, maximum, and default lengths for initialization vectors (IV).
Properties
Cipher.AUTH_TAG_LENGTH
Defines the minimum, maximum, and default lengths for authentication tags.
Properties
Cipher.AAD_LENGTH
Defines the minimum, maximum, and default lengths for additional authenticated data (AAD).
Properties
Cipher.DEFAULT_ALGORITHM
Specifies default AES algorithms for buffer and stream operations.
Properties
buffer | aes-256-gcm | Default algorithm used for buffer data encryption/decryption |
stream | aes-256-cbc | Default algorithm used for stream encryption/decryption (Cipher Block Chaining mode). |
Cipher.ALGORITHM
An object defining algorithm names.
Properties
AES_128_CBC | 'aes-128-cbc' |
AES_192_CBC | 'aes-192-cbc' |
AES_256_CBC | 'aes-256-cbc' |
AES_128_CCM | 'aes-128-ccm' |
AES_192_CCM | 'aes-192-ccm' |
AES_256_CCM | 'aes-256-ccm' |
AES_128_GCM | 'aes-128-gcm' |
AES_192_GCM | 'aes-192-gcm' |
AES_256_GCM | 'aes-256-gcm' |
AES_128_OCB | 'aes-128-ocb' |
AES_192_OCB | 'aes-192-ocb' |
AES_256_OCB | 'aes-256-ocb' |
CHACHA_20_POLY | 'chacha20-poly1305' |
Cipher.ALGORITHMS
An array of supported AES algorithms. This array includes all values in Cipher.ALGORITHM constant.
Methods
Cipher.Encrypt()
Encrypts an in-memory data buffer.
[!WARNING]
This is not suitable for large data encryption. Use Cipher.stream.Encrypt() or Cipher.stream.HybridEncrypt() methods for large data encryption.
Parameters
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.
Cipher.Decrypt()
Decrypts an in-memory data buffer.
[!WARNING]
This is not suitable for large data decryption. Use Cipher.stream.Decrypt() or Cipher.stream.HybridDecrypt() methods for large data decryption.
Parameters
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.
Cipher.HybridEncrypt()
Encrypts in-memory data using hybrid encryption.
[!WARNING]
This is not suitable for large data encryption. Use Cipher.stream.HybridEncrypt() method for large data encryption.
[!WARNING]
Please, note that when using hybrid encryption/decryption algorithms:
- an RSA keypair is required.
- if a passphrase is set for the Private Key, please make sure to use one of the Cipher Block Chaining algorithm or
chacha20-poly1305 algorithm:
- aes-128-cbc (
type can be pkcs1 or pkcs8)
- aes-192-cbc (
type can be pkcs1 or pkcs8)
- aes-256-cbc (
type can be pkcs1 or pkcs8)
- chacha20-poly1305 (
type can only be pkcs1)
Parameters
data | CoerceToUint8ArrayInput | - | The data to encrypt. |
key | crypto.KeyLike | - | The RSA Public Key. |
options | Cph.Options | - | (Optional) Additional options. |
Returns
Type: Buffer
The encrypted result buffer.
Cipher.HybridDecrypt()
Decrypts in-memory data using hybrid encryption.
[!WARNING]
This is not suitable for large data decryption. Use Cipher.stream.HybridDecrypt() method for large data decryption.
[!WARNING]
Please, note that when using hybrid encryption/decryption algorithms:
- an RSA keypair is required.
- if a passphrase is set for the Private Key, please make sure to use one of the Cipher Block Chaining algorithm or
chacha20-poly1305 algorithm:
- aes-128-cbc (
type can be pkcs1 or pkcs8)
- aes-192-cbc (
type can be pkcs1 or pkcs8)
- aes-256-cbc (
type can be pkcs1 or pkcs8)
- chacha20-poly1305 (
type can only be pkcs1)
Parameters
data | CoerceToUint8ArrayInput | - | The data to encrypt. |
key | Cph.PrivateKey | - | The RSA Private Key. |
options | Cph.Options | - | (Optional) Additional options. |
Returns
Type: Buffer.
The encrypted result Buffer.
Cipher.stream.Encrypt()
Encrypt stream data.
Parameters
secret | CoerceToUint8ArrayInput | - | The secret key used to encrypt the data. |
options | Cph.Stream.EncryptOptions | - | An object defining required options. |
options.input | Readable | - | The Readable Stream where raw data to encrypt is read. |
options.output | Writable | - | The Writable Stream where encrypted data is written. |
options.algorithm | Cph.CBCTypes | aes-256-cbc | One of the Cipher Block Chaining algorithm. |
Returns
Type: Promise<void>
A new Promise that resolves void once stream encryption is completed.
Cipher.stream.Decrypt()
Decrypt stream data.
Parameters
secret | CoerceToUint8ArrayInput | - | The secret key used to decrypt the data. |
options | Cph.Stream.DecryptOptions | - | An object defining required options. |
options.input | Readable | - | The Readable Stream where encrypted data is read. |
options.output | Writable | - | The Writable Stream where decrypted data is written. |
options.algorithm | Cph.CBCTypes | aes-256-cbc | One of the Cipher Block Chaining algorithm. |
Returns
Type: Promise<void>
A new Promise that resolves void once stream decryption is completed.
Cipher.stream.HybridEncrypt()
Encrypt stream data using hybrid encryption.
[!WARNING]
Please, note that when using hybrid encryption/decryption algorithms:
- an RSA keypair is required.
- if a passphrase is set for the Private Key, please make sure to use one of the Cipher Block Chaining algorithm or
chacha20-poly1305 algorithm:
- aes-128-cbc (
type can be pkcs1 or pkcs8)
- aes-192-cbc (
type can be pkcs1 or pkcs8)
- aes-256-cbc (
type can be pkcs1 or pkcs8)
- chacha20-poly1305 (
type can only be pkcs1)
Parameters
key | crypto.KeyLike | - | The RSA Public Key. |
options | Cph.Stream.EncryptOptions | - | An object defining required options. |
options.input | Readable | - | The Readable Stream where raw data to encrypt is read. |
options.output | Writable | - | The Writable Stream where encrypted data is written. |
options.algorithm | Cph.CBCTypes | aes-256-cbc | One of the Cipher Block Chaining algorithm. |
Returns
Type: Promise<void>
A new Promise that resolves void once stream encryption is completed.
Cipher.stream.HybridDecrypt()
Decrypt stream data using hybrid decryption.
[!WARNING]
Please, note that when using hybrid encryption/decryption algorithms:
- an RSA keypair is required.
- if a passphrase is set for the Private Key, please make sure to use one of the Cipher Block Chaining algorithm or
chacha20-poly1305 algorithm:
- aes-128-cbc (
type can be pkcs1 or pkcs8)
- aes-192-cbc (
type can be pkcs1 or pkcs8)
- aes-256-cbc (
type can be pkcs1 or pkcs8)
- chacha20-poly1305 (
type can only be pkcs1)
Parameters
key | Cph.PrivateKey | - | The RSA Private Key. |
options | Cph.Stream.DecryptOptions | - | An object defining required options. |
options.input | Readable | - | The Readable Stream where encrypted data is read. |
options.output | Writable | - | The Writable Stream where decrypted data is written. |
options.algorithm | Cph.CBCTypes | aes-256-cbc | One of the Cipher Block Chaining algorithm. |
Returns
Type: Promise<void>
A new Promise that resolves void once stream encryption is completed.
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.
Cph.CBCTypes
AES Cipher Block Chaining algorithms.
Cph.AesAlgorithm
All supported AES algorithms.
Cph.Options<T>
Common options in encryption/decryption processes.
Type parameters
T | Cph.AesAlgorithm | Accepted algorithm in Cph.Options. This is usefull to constraint specifc algorithms. |
Properties
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.EncryptOptions
Stream symmetric encryption options.
Properties
input | Readable | The Readable Stream from where raw data to encrypt is read. |
output | Writable | The Writable Stream where encrypted data is written. |
algorithm | Cph.CBCTypes | One of the Cipher Block Chaining algorithm. |
Cph.Stream.DecryptOptions
Stream symmetric decryption options.
Properties
input | Readable | The Readable Stream from where encrypted data is read. |
output | Writable | The Writable Stream where decrypted data is written. |
algorithm | Cph.CBCTypes | One of the Cipher Block Chaining algorithm. |
Cph.PrivateKey
The RSA Private Key.
It could be:
- a
crypto.KeyLike
- an object defining
key and passphrase where key is a crypto.KeyLike
Examples
In-memory data buffer encryption/decryption
The simpliest way to encrypt/decrypt in-memory data buffers.
const data = "my top-secret data";
const password = "my-very-strong-password";
const encrypted = Cipher.Encrypt(data, password);
const decrypted = Cipher.Decrypt(encrypted, password);
console.log(decrypted);
In-memory data buffer hybrid encryption/decryption
Hybrid encryption offers an higher level of security since only the RSA Private Key owner will be able to decrypt the data.
[!WARNING]
Please, note that when using hybrid encryption/decryption algorithms:
- an RSA keypair is required.
- if a passphrase is set for the Private Key, please make sure to use one of the Cipher Block Chaining algorithm or
chacha20-poly1305 algorithm:
- aes-128-cbc (
type can be pkcs1 or pkcs8)
- aes-192-cbc (
type can be pkcs1 or pkcs8)
- aes-256-cbc (
type can be pkcs1 or pkcs8)
- chacha20-poly1305 (
type can only be pkcs1)
Keypair
import { Cipher } from "@alessiofrittoli/crypto-cipher";
const keyPair = crypto.generateKeyPairSync("rsa", {
modulusLength: 512 * 8,
publicKeyEncoding: { type: "spki", format: "pem" },
privateKeyEncoding: { type: "pkcs1", format: "pem" },
});
const passphrase = "custompassphrase";
const keyPair = crypto.generateKeyPairSync("rsa", {
modulusLength: 512 * 8,
publicKeyEncoding: { type: "spki", format: "pem" },
privateKeyEncoding: {
type: "pkcs1",
format: "pem",
passphrase,
cipher: Cipher.ALGORITHM.CHACHA_20_POLY,
},
});
import { Cipher } from "@alessiofrittoli/crypto-cipher";
const data = "my top-secret data";
const encrypted = Cipher.HybridEncrypt(data, keypair.publicKey);
const decrypted = Cipher.HybridDecrypt(encrypted, {
key: keypair.privateKey,
passphrase,
});
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
import { Readable, Writable } from "stream";
import { Stream } from "@alessiofrittoli/stream-writer";
const routeHandler = () => {
const password = "my-very-strong-password";
const stream = new Stream();
const headers = new Headers(stream.headers);
const input = Readable.from([
Buffer.from("Chunk n.1"),
Buffer.from("Chunk n.2"),
Buffer.from("Chunk n.3"),
Buffer.from("Chunk n.4"),
]);
const output = new Writable({
async write(chunk, encoding, callback) {
await new Promise((resolve) => setTimeout(resolve, 2000));
await stream.write(chunk);
callback();
},
async final(callback) {
await stream.close();
callback();
},
});
Cipher.stream.Encrypt(password, { input, output }).catch(async () => {
await stream.close();
});
return (
new Response(stream.readable, { headers })
);
};
Decrypting received stream
import { Transform, Writable } from "stream";
import { Cipher } from "@alessiofrittoli/crypto-cipher";
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 Stream<Buffer, string>({
transform(chunk, controller) {
controller.enqueue(chunk.toString());
},
});
const headers = new Headers(stream.headers);
headers.set("Content-Type", "text/html");
const reader = new StreamReader<Uint8Array, Buffer, false>(response.body, {
inMemory: false,
transform: Buffer.from,
});
const input = new Transform();
reader.on("data", (chunk) => input.push(chunk));
reader.on("close", () => input.end());
reader.read();
const output = new Writable({
async write(chunk: Buffer, encoding, callback) {
await stream.write(chunk);
callback();
},
final(callback) {
stream.close();
callback();
},
});
Cipher.stream.Decrypt(password, { input, output }).catch(async (error) => {
console.error(error);
await stream.close();
});
return new Response(stream.readable, { headers });
});
In-memory data stream with hybrid encryption/decryption
Hybrid encryption offers an higher level of security since only the RSA Private Key owner will be able to decrypt the data.
[!WARNING]
Please, note that when using hybrid encryption/decryption algorithms:
- an RSA keypair is required.
- if a passphrase is set for the Private Key, please make sure to use one of the Cipher Block Chaining algorithm or
chacha20-poly1305 algorithm:
- aes-128-cbc (
type can be pkcs1 or pkcs8)
- aes-192-cbc (
type can be pkcs1 or pkcs8)
- aes-256-cbc (
type can be pkcs1 or pkcs8)
- chacha20-poly1305 (
type can only be pkcs1)
Keypair
import { Cipher } from "@alessiofrittoli/crypto-cipher";
const keyPair = crypto.generateKeyPairSync("rsa", {
modulusLength: 512 * 8,
publicKeyEncoding: { type: "spki", format: "pem" },
privateKeyEncoding: { type: "pkcs1", format: "pem" },
});
const passphrase = "custompassphrase";
const keyPair = crypto.generateKeyPairSync("rsa", {
modulusLength: 512 * 8,
publicKeyEncoding: { type: "spki", format: "pem" },
privateKeyEncoding: {
type: "pkcs1",
format: "pem",
passphrase,
cipher: Cipher.ALGORITHM.CHACHA_20_POLY,
},
});
Encrypt
const data = "my top-secret data";
const encryptedChunks: Buffer[] = [];
const input = new Readable({
read() {
this.push(data);
this.push(null);
},
});
const output = new Writable({
write(chunk, encoding, callback) {
encryptedChunks.push(chunk);
callback();
},
});
await Cipher.stream.HybridEncrypt(keyPair.publicKey, { input, output });
Decrypt
const chunks: Buffer[] = [];
const input = Readable.from(encryptedChunks);
const output = new Writable({
write(chunk, encoding, callback) {
chunks.push(chunk);
callback();
},
});
await Cipher.stream.HybridDecrypt(
{
key: keyPair.privateKey,
passphrase,
},
{ input, output }
);
console.log(Buffer.concat(chunks).toString());
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";
const input = fs.createReadStream("my-very-large-top-secret-file.pdf");
const output = fs.createWriteStream("my-very-large-top-secret-file.encrypted");
await Cipher.stream.Encrypt(password, { input, output });
Decrypt a file
import fs from "fs";
const password = "my-very-strong-password";
const input = fs.createReadStream("my-very-large-top-secret-file.encrypted");
const output = fs.createWriteStream(
"my-very-large-top-secret-file-decrypted.pdf"
);
await Cipher.stream.Decrypt(password, { input, output });
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.
[!WARNING]
Please, note that when using hybrid encryption/decryption algorithms:
- an RSA keypair is required.
- if a passphrase is set for the Private Key, please make sure to use one of the Cipher Block Chaining algorithm or
chacha20-poly1305 algorithm:
- aes-128-cbc (
type can be pkcs1 or pkcs8)
- aes-192-cbc (
type can be pkcs1 or pkcs8)
- aes-256-cbc (
type can be pkcs1 or pkcs8)
- chacha20-poly1305 (
type can only be pkcs1)
Keypair
import { Cipher } from "@alessiofrittoli/crypto-cipher";
const keyPair = crypto.generateKeyPairSync("rsa", {
modulusLength: 512 * 8,
publicKeyEncoding: { type: "spki", format: "pem" },
privateKeyEncoding: { type: "pkcs1", format: "pem" },
});
const passphrase = "custompassphrase";
const keyPair = crypto.generateKeyPairSync("rsa", {
modulusLength: 512 * 8,
publicKeyEncoding: { type: "spki", format: "pem" },
privateKeyEncoding: {
type: "pkcs1",
format: "pem",
passphrase,
cipher: Cipher.ALGORITHM.CHACHA_20_POLY,
},
});
Encrypt a file
import fs from "fs";
const input = fs.createReadStream("my-very-large-top-secret-file.pdf");
const output = fs.createWriteStream("my-very-large-top-secret-file.encrypted");
await Cipher.stream.HybridEncrypt(keyPair.publicKey, { input, output });
Decrypt a file
import fs from "fs";
const input = fs.createReadStream("my-very-large-top-secret-file.encrypted");
const output = fs.createWriteStream(
"my-very-large-top-secret-file-decrypted.pdf"
);
const { decrypt } = await Cipher.stream.HybridDecrypt(
{
key: keyPair.privateKey,
passphrase,
},
{ input, output }
);
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
warnings / errors check.
pnpm lint
Run all the defined test suites by running the following:
pnpm test:watch
pnpm test:ci
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.
Made with ā