Files
skills/docs/security-stack/crypto/crypto-sodium/src/engines/sodium-auth-box-engine/sodium-auth-box-engine.md
2026-05-29 08:20:56 +01:00

3.9 KiB

engines/sodium-auth-box-engine

(no submodules)

Purpose

sodiumAuthBoxEngine implements the AuthBoxEngine interface from @suaveplan/crypto using libsodium's crypto_box_easy and crypto_box_open_easy. This construction provides authenticated key-exchange encryption: both the sender and recipient are authenticated because the shared secret is derived from a static X25519 DH between sender's secret and recipient's public key, then used to encrypt with XSalsa20-Poly1305.

Unlike sodiumSealedBoxEngine, the sender's public key is not included in the ciphertext — the recipient must already know who sent the message (i.e., possess the sender's public key independently).

Features

  • Mutually authenticated encryption using crypto_box_easy / crypto_box_open_easy.
  • XSalsa20-Poly1305 authenticated encryption via X25519 key agreement.
  • 24-byte explicit nonce required from caller.
  • 16-byte MAC overhead in the ciphertext.
  • Throws SodiumError on authentication failure during open.
  • Conforms to AuthBoxEngine from @suaveplan/crypto/engines.

Usage: Basic Example

import { sodiumReady } from "@suaveplan/crypto-sodium/init";
import { sodiumAgreeEngine } from "@suaveplan/crypto-sodium/engines/agree";
import { sodiumAuthBoxEngine } from "@suaveplan/crypto-sodium/engines/auth-box";

await sodiumReady();

const alice = sodiumAgreeEngine.generateKeypair();
const bob = sodiumAgreeEngine.generateKeypair();
const nonce = crypto.getRandomValues(new Uint8Array(24));
const msg = new TextEncoder().encode("authenticated message");

// Alice sends to Bob
const ct = sodiumAuthBoxEngine.seal(
  alice.secretKey,
  bob.publicKey,
  msg,
  nonce
);

// Bob receives from Alice
const pt = sodiumAuthBoxEngine.open(
  alice.publicKey,
  bob.secretKey,
  ct,
  nonce
);
console.log(new TextDecoder().decode(pt)); // "authenticated message"

Usage: Advanced Example

Bidirectional messaging channel with distinct nonce counters per direction:

import { sodiumReady } from "@suaveplan/crypto-sodium/init";
import { sodiumAgreeEngine } from "@suaveplan/crypto-sodium/engines/agree";
import { sodiumAuthBoxEngine } from "@suaveplan/crypto-sodium/engines/auth-box";

await sodiumReady();

const alice = sodiumAgreeEngine.generateKeypair();
const bob = sodiumAgreeEngine.generateKeypair();

function incrementNonce(nonce: Uint8Array): Uint8Array {
  const n = nonce.slice();
  for (let i = n.length - 1; i >= 0; i--) {
    if (++n[i]) break;
  }
  return n;
}

let aliceNonce = new Uint8Array(24);
const ct1 = sodiumAuthBoxEngine.seal(
  alice.secretKey, bob.publicKey,
  new TextEncoder().encode("msg 1"), aliceNonce
);
aliceNonce = incrementNonce(aliceNonce);

API Reference

sodiumAuthBoxEngine: AuthBoxEngine

Singleton engine.

seal(senderSecret, recipientPublic, plaintext, nonce): Uint8Array

Encrypts plaintext using crypto_box_easy. Returns ciphertext with 16-byte MAC prepended.

Parameters:

  • senderSecret — sender's 32-byte secret key.
  • recipientPublic — recipient's 32-byte public key.
  • plaintext — message bytes.
  • nonce — 24-byte nonce (must be unique per message under the same keypair).

open(senderPublic, recipientSecret, ciphertext, nonce): Uint8Array

Decrypts and authenticates. Both keys must correspond to the same exchange as seal.

Throws: SodiumError (SODIUM.INIT.FAILED) if MAC verification fails.

Implementation Notes

crypto_box_easy computes Curve25519(senderSecret, recipientPublic) as the shared secret and uses HSalsa20 as KDF before XSalsa20-Poly1305. Nonces must be 24 bytes. The nonce management strategy (random, counter, or derived) is left to the caller — each (keypair, nonce) pair must be used at most once for confidentiality.

The MAC overhead is 16 bytes (crypto_box_MACBYTES). The nonce must be transmitted alongside the ciphertext; a common convention is to prepend it to the ciphertext.