ChaCha20 Poly1305 Authenticated Cipher

2.1.0 · active · verified Wed Apr 22

The `chacha` package provides an implementation of the ChaCha20 Poly1305 authenticated encryption algorithm, designed to be compatible with Node.js's `crypto.createCipheriv()` and `createDecipheriv()` API for AES-GCM mode. It uses the more recent IETF draft for ChaCha20-Poly1305 AEAD, which features a 96-bit nonce, distinct from earlier drafts implemented in systems like BoringSSL (though it offers a 'Legacy Aead' for compatibility). The library supports both a pure JavaScript implementation and optional native bindings for performance in Node.js environments, automatically falling back to pure JS where native bindings are unavailable or explicitly opted out. It exposes APIs for the full AEAD, the ChaCha20 stream cipher, and the Poly1305 message authentication code independently. The current stable version is 2.1.0, and while a specific release cadence isn't published, the active GitHub repository and continuous integration suggest ongoing maintenance and stability.

Common errors

Warnings

Install

Imports

Quickstart

This quickstart demonstrates the full ChaCha20 Poly1305 Authenticated Encryption and Decryption (AEAD) flow, including setting the key, nonce, Additional Authenticated Data (AAD), and handling the authentication tag. It highlights the correct order of operations for `setAAD`, `update`, `final`, `getAuthTag`, and `setAuthTag`.

const chacha = require('chacha');
const { Buffer } = require('buffer');

// --- Configuration ---
// Keys and nonces must be Buffers of specific lengths
const key = Buffer.from('0123456789abcdef0123456789abcdef01234567', 'hex'); // 256-bit (32 bytes)
const nonce = Buffer.from('fedcba9876543210', 'hex'); // 96-bit (12 bytes)
const associatedData = Buffer.from('Example AAD for authentication', 'utf8');
const plaintext = Buffer.from('This is a highly confidential message that needs secure encryption!', 'utf8');

console.log('Original plaintext:', plaintext.toString('utf8'));

// --- Encryption ---
const cipher = chacha.createCipher(key, nonce);

// Set Additional Authenticated Data (AAD) BEFORE processing any data
cipher.setAAD(associatedData);

// Encrypt the plaintext in parts or all at once
const ciphertextChunks = [];
ciphertextChunks.push(cipher.update(plaintext.subarray(0, plaintext.length / 2)));
ciphertextChunks.push(cipher.update(plaintext.subarray(plaintext.length / 2)));
ciphertextChunks.push(cipher.final());
const ciphertext = Buffer.concat(ciphertextChunks);

// Get the authentication tag AFTER all data has been processed (after final())
const tag = cipher.getAuthTag();

console.log('\nEncrypted Ciphertext (hex):', ciphertext.toString('hex'));
console.log('Authentication Tag (hex):', tag.toString('hex'));

// --- Decryption ---
const decipher = chacha.createDecipher(key, nonce);

// Set AAD BEFORE processing any data (must match encryption AAD)
decipher.setAAD(associatedData);

// Set the authentication tag BEFORE processing any data (must match encryption tag)
decipher.setAuthTag(tag);

// Decrypt the ciphertext
let decryptedPlaintext;
try {
  const decryptedChunks = [];
  decryptedChunks.push(decipher.update(ciphertext.subarray(0, ciphertext.length / 2)));
  decryptedChunks.push(decipher.update(ciphertext.subarray(ciphertext.length / 2)));
  decryptedChunks.push(decipher.final());
  decryptedPlaintext = Buffer.concat(decryptedChunks);
  console.log('\nDecrypted Plaintext:', decryptedPlaintext.toString('utf8'));
  console.log('Decryption successful:', decryptedPlaintext.equals(plaintext));
} catch (e) {
  console.error('\nDecryption failed: The authentication tag or AAD did not match!', e.message);
}

view raw JSON →