Port of TweetNaCl cryptographic library to JavaScript providing comprehensive cryptographic primitives
—
Public-key authenticated encryption using x25519-xsalsa20-poly1305. This system provides confidentiality and authenticity between two parties using their respective public and secret key pairs, without requiring a shared secret.
Encrypt and decrypt messages using public-key cryptography.
/**
* Encrypts and authenticates a message using peer's public key and our secret key
* @param {Uint8Array} message - The plaintext message to encrypt
* @param {Uint8Array} nonce - Unique nonce for this encryption (24 bytes)
* @param {Uint8Array} theirPublicKey - Recipient's public key (32 bytes)
* @param {Uint8Array} mySecretKey - Sender's secret key (32 bytes)
* @returns {Uint8Array} Encrypted and authenticated message (16 bytes longer than original)
*/
nacl.box(message, nonce, theirPublicKey, mySecretKey): Uint8Array
/**
* Authenticates and decrypts a box using peer's public key and our secret key
* @param {Uint8Array} box - The encrypted message to decrypt
* @param {Uint8Array} nonce - The nonce used for encryption (24 bytes)
* @param {Uint8Array} theirPublicKey - Sender's public key (32 bytes)
* @param {Uint8Array} mySecretKey - Recipient's secret key (32 bytes)
* @returns {Uint8Array | null} Decrypted message or null if authentication fails
*/
nacl.box.open(box, nonce, theirPublicKey, mySecretKey): Uint8Array | nullUsage Example:
const nacl = require('tweetnacl');
// Generate key pairs for Alice and Bob
const alice = nacl.box.keyPair();
const bob = nacl.box.keyPair();
// Alice encrypts a message for Bob
const message = new TextEncoder().encode("Hello Bob!");
const nonce = nacl.randomBytes(nacl.box.nonceLength);
const encrypted = nacl.box(message, nonce, bob.publicKey, alice.secretKey);
// Bob decrypts Alice's message
const decrypted = nacl.box.open(encrypted, nonce, alice.publicKey, bob.secretKey);
if (decrypted) {
console.log(new TextDecoder().decode(decrypted)); // "Hello Bob!"
}For multiple operations between the same key pairs, precompute shared keys for better performance.
/**
* Precomputes a shared key for multiple box operations between the same key pairs
* @param {Uint8Array} theirPublicKey - Peer's public key (32 bytes)
* @param {Uint8Array} mySecretKey - Our secret key (32 bytes)
* @returns {Uint8Array} Precomputed shared key (32 bytes)
*/
nacl.box.before(theirPublicKey, mySecretKey): Uint8Array
/**
* Encrypts using a precomputed shared key
* @param {Uint8Array} message - The plaintext message to encrypt
* @param {Uint8Array} nonce - Unique nonce for this encryption (24 bytes)
* @param {Uint8Array} sharedKey - Precomputed shared key from nacl.box.before
* @returns {Uint8Array} Encrypted and authenticated message
*/
nacl.box.after(message, nonce, sharedKey): Uint8Array
/**
* Decrypts using a precomputed shared key
* @param {Uint8Array} box - The encrypted message to decrypt
* @param {Uint8Array} nonce - The nonce used for encryption (24 bytes)
* @param {Uint8Array} sharedKey - Precomputed shared key from nacl.box.before
* @returns {Uint8Array | null} Decrypted message or null if authentication fails
*/
nacl.box.open.after(box, nonce, sharedKey): Uint8Array | nullUsage Example:
const nacl = require('tweetnacl');
const alice = nacl.box.keyPair();
const bob = nacl.box.keyPair();
// Precompute shared keys (do this once)
const aliceSharedKey = nacl.box.before(bob.publicKey, alice.secretKey);
const bobSharedKey = nacl.box.before(alice.publicKey, bob.secretKey);
// Now encrypt/decrypt multiple messages efficiently
const message1 = new TextEncoder().encode("First message");
const message2 = new TextEncoder().encode("Second message");
const nonce1 = nacl.randomBytes(nacl.box.nonceLength);
const nonce2 = nacl.randomBytes(nacl.box.nonceLength);
const encrypted1 = nacl.box.after(message1, nonce1, aliceSharedKey);
const encrypted2 = nacl.box.after(message2, nonce2, aliceSharedKey);
const decrypted1 = nacl.box.open.after(encrypted1, nonce1, bobSharedKey);
const decrypted2 = nacl.box.open.after(encrypted2, nonce2, bobSharedKey);Generate key pairs for box operations.
/**
* Generates a new random key pair for box operations
* @returns {{publicKey: Uint8Array, secretKey: Uint8Array}} Key pair object
*/
nacl.box.keyPair(): {publicKey: Uint8Array, secretKey: Uint8Array}
/**
* Derives a key pair from an existing secret key
* @param {Uint8Array} secretKey - The secret key (32 bytes)
* @returns {{publicKey: Uint8Array, secretKey: Uint8Array}} Key pair with corresponding public key
*/
nacl.box.keyPair.fromSecretKey(secretKey): {publicKey: Uint8Array, secretKey: Uint8Array}Usage Example:
const nacl = require('tweetnacl');
// Generate a new random key pair
const keyPair = nacl.box.keyPair();
console.log(keyPair.publicKey.length); // 32
console.log(keyPair.secretKey.length); // 32
// Derive key pair from existing secret key
const newKeyPair = nacl.box.keyPair.fromSecretKey(keyPair.secretKey);
// newKeyPair.publicKey will be identical to keyPair.publicKeynacl.box.publicKeyLength: number // 32 - Length of public key in bytes
nacl.box.secretKeyLength: number // 32 - Length of secret key in bytes
nacl.box.sharedKeyLength: number // 32 - Length of precomputed shared key in bytes
nacl.box.nonceLength: number // 24 - Length of nonce in bytes
nacl.box.overheadLength: number // 16 - Length of overhead added to encrypted messagesCommon error patterns and proper handling:
const nacl = require('tweetnacl');
function safeEncrypt(message, theirPublicKey, mySecretKey) {
try {
// Validate inputs
if (!message || !theirPublicKey || !mySecretKey) {
throw new Error('Missing required parameters');
}
if (theirPublicKey.length !== nacl.box.publicKeyLength) {
throw new Error(`Invalid public key length: expected ${nacl.box.publicKeyLength}, got ${theirPublicKey.length}`);
}
if (mySecretKey.length !== nacl.box.secretKeyLength) {
throw new Error(`Invalid secret key length: expected ${nacl.box.secretKeyLength}, got ${mySecretKey.length}`);
}
const nonce = nacl.randomBytes(nacl.box.nonceLength);
const encrypted = nacl.box(message, nonce, theirPublicKey, mySecretKey);
return { encrypted, nonce };
} catch (error) {
console.error('Encryption failed:', error.message);
return null;
}
}
function safeDecrypt(encrypted, nonce, theirPublicKey, mySecretKey) {
try {
const decrypted = nacl.box.open(encrypted, nonce, theirPublicKey, mySecretKey);
if (decrypted === null) {
throw new Error('Decryption failed: message was tampered with or keys are incorrect');
}
return decrypted;
} catch (error) {
console.error('Decryption failed:', error.message);
return null;
}
}nacl.box.open returns null, the message was tampered with or authentication failed.Install with Tessl CLI
npx tessl i tessl/npm-tweetnacl