CtrlK
BlogDocsLog inGet started
Tessl Logo

tessl/npm-tweetnacl

Port of TweetNaCl cryptographic library to JavaScript providing comprehensive cryptographic primitives

Pending
Overview
Eval results
Files

box.mddocs/

Public-Key Authenticated Encryption

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.

Capabilities

Basic Encryption and Decryption

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 | null

Usage 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!"
}

Optimized Operations with Precomputed Keys

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 | null

Usage 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);

Key Generation

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.publicKey

Constants

nacl.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 messages

Error Handling

Common 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;
  }
}

Security Considerations

  • Nonce Uniqueness: Each message encrypted with the same key pair must use a unique nonce. Reusing nonces breaks security.
  • Key Management: Keep secret keys secure and never transmit them. Only share public keys.
  • Message Integrity: If nacl.box.open returns null, the message was tampered with or authentication failed.
  • Overhead: Encrypted messages are 16 bytes longer than the original due to authentication data.

Install with Tessl CLI

npx tessl i tessl/npm-tweetnacl

docs

box.md

index.md

lowlevel.md

scalarmult.md

secretbox.md

sign.md

utilities.md

tile.json