Claude
Skills
Sign in
Back

libsodium

Included with Lifetime
$97 forever

libsodium — modern, easy-to-use, audited crypto library. Provides authenticated encryption (XSalsa20-Poly1305, XChaCha20-Poly1305, AES-GCM), public-key cryptography (X25519, Ed25519), key derivation (Argon2id, HKDF, BLAKE2b), password hashing, and authenticated streams (secretstream). Wraps NaCl with better defaults. Bindings for Rust (sodiumoxide, libsodium-sys-stable, dryoc), Python (PyNaCl), JS (libsodium-wrappers), Java/Android (lazysodium-android), Swift (Sodium / Clibsodium). USE WHEN: user mentions "libsodium", "NaCl", "Sodium", "secretbox", "crypto_secretstream", "Argon2", "Argon2id", "X25519", "Ed25519", "PyNaCl", "lazysodium", "ChaCha20-Poly1305", "XSalsa20" DO NOT USE FOR: SQLite encryption - use `databases/sqlcipher` DO NOT USE FOR: File encryption format - use `security/age-encryption` DO NOT USE FOR: Bitcoin/secp256k1 crypto - use `bitcoin/cryptography/*` DO NOT USE FOR: TLS - use `security/rustls` or platform TLS stack

Backend & APIs

What this skill does

# libsodium

> **Deep Knowledge**: Use `mcp__documentation__fetch_docs` with technology: `libsodium`.

## Why libsodium

libsodium is the safest general-purpose crypto library for application-level use:
- **Modern primitives** with sensible defaults (XChaCha20-Poly1305, X25519, Ed25519, Argon2id)
- **Misuse-resistant API** — no nonce reuse traps, no IV management mistakes
- **Audited** by multiple third parties
- **Bindings everywhere** — C, Rust, Python, JS, Java/Kotlin, Swift, Go
- **Permissive license** (ISC)
- **Constant-time** implementations

For wallet apps, libsodium covers everything except secp256k1 (Bitcoin). Pair with `secp256k1` for full coverage.

## Primitives Cheat Sheet

| Need | Use | Function family |
|---|---|---|
| Symmetric authenticated encryption | XChaCha20-Poly1305 | `crypto_secretbox` |
| Streaming symmetric encryption | XChaCha20-Poly1305 + chunks | `crypto_secretstream` |
| Public-key encryption | X25519 + XSalsa20-Poly1305 | `crypto_box` |
| Hybrid (sealed) public-key encryption | X25519 anonymous | `crypto_box_seal` |
| Digital signatures | Ed25519 | `crypto_sign` |
| Key exchange | X25519 | `crypto_kx` |
| Password hashing | Argon2id | `crypto_pwhash` |
| Generic hashing | BLAKE2b | `crypto_generichash` |
| MAC | HMAC-SHA256/512 | `crypto_auth` |
| Key derivation from key | HKDF / BLAKE2b | `crypto_kdf` |
| Random bytes | ChaCha20 (CSPRNG) | `randombytes_buf` |
| Constant-time compare | — | `sodium_memcmp` |
| Memory wipe | — | `sodium_memzero` |

## Rust — `sodiumoxide` or `dryoc`

For new Rust code, prefer **`dryoc`** (pure Rust, no system deps):

```toml
[dependencies]
dryoc = "0.7"
```

```rust
use dryoc::dryocsecretbox::DryocSecretBox;
use dryoc::types::*;

fn encrypt_seed(seed: &[u8], key: &[u8; 32]) -> (Vec<u8>, Vec<u8>) {
    let nonce = dryoc::dryocsecretbox::Nonce::gen();
    let key_array: dryoc::dryocsecretbox::Key = dryoc::dryocsecretbox::Key::try_from(key).unwrap();
    let ciphertext = DryocSecretBox::encrypt_to_vecbox(seed, &nonce, &key_array);
    (nonce.to_vec(), ciphertext.to_vec())
}

fn decrypt_seed(ciphertext: &[u8], nonce: &[u8], key: &[u8; 32]) -> Result<Vec<u8>, dryoc::Error> {
    let key_array: dryoc::dryocsecretbox::Key = dryoc::dryocsecretbox::Key::try_from(key).unwrap();
    let nonce_array: dryoc::dryocsecretbox::Nonce = dryoc::dryocsecretbox::Nonce::try_from(nonce).unwrap();
    let secret_box = DryocSecretBox::from_bytes(ciphertext)?;
    secret_box.decrypt_to_vec(&nonce_array, &key_array)
}
```

Or use **`sodiumoxide`** (binds the C lib):

```toml
[dependencies]
sodiumoxide = "0.2"
```

```rust
use sodiumoxide::crypto::secretbox;

sodiumoxide::init().unwrap();

let key = secretbox::gen_key();
let nonce = secretbox::gen_nonce();
let ciphertext = secretbox::seal(b"plaintext", &nonce, &key);
let plaintext = secretbox::open(&ciphertext, &nonce, &key).unwrap();
```

## SecretBox (Symmetric Authenticated Encryption)

```rust
use dryoc::dryocsecretbox::DryocSecretBox;

let key = dryoc::dryocsecretbox::Key::gen();      // 32 bytes
let nonce = dryoc::dryocsecretbox::Nonce::gen();  // 24 bytes (XSalsa20)

let ciphertext = DryocSecretBox::encrypt_to_vecbox(b"plaintext", &nonce, &key);
let plaintext = ciphertext.decrypt_to_vec(&nonce, &key).unwrap();
```

**Critical**: never reuse the same `(key, nonce)` pair. With XSalsa20's 24-byte nonce, random nonces are safe (~2^96 messages).

## SecretStream (Authenticated Streaming)

For encrypting files in chunks (constant memory, can stream from network):

```rust
use dryoc::dryocstream::*;

// Encrypt
let key = Key::gen();
let mut push_stream = DryocStream::init_push(&key);
let header = push_stream.header().clone();

let mut output = Vec::new();
output.extend_from_slice(header.as_array());

for chunk in chunks(input, 64 * 1024) {
    let tag = if chunk.is_last { Tag::FINAL } else { Tag::MESSAGE };
    let encrypted = push_stream.push_to_vec(chunk.data, None, tag).unwrap();
    output.extend_from_slice(&encrypted);
}

// Decrypt
let header_bytes = &input[0..Header::LEN];
let mut pull_stream = DryocStream::init_pull(&key, header_bytes.try_into().unwrap()).unwrap();

let mut decrypted = Vec::new();
let mut offset = Header::LEN;
while offset < input.len() {
    let chunk_end = (offset + chunk_size).min(input.len());
    let (data, tag) = pull_stream.pull_to_vec(&input[offset..chunk_end], None).unwrap();
    decrypted.extend_from_slice(&data);
    offset = chunk_end;
    if tag == Tag::FINAL { break; }
}
```

**Use case**: encrypted backups, log files, large blobs — anything you don't want to load entirely into memory.

## Password Hashing — Argon2id

For deriving keys from user passwords (or wrapping wallet seed encryption keys with a password).

```rust
use dryoc::pwhash::*;

let password = b"correct horse battery staple";
let salt = Salt::gen();

// Hash for storage (stretched with Argon2id)
let hash = VecPwHash::hash_with_salt(
    password,
    salt.clone(),
    Config::sensitive(),                             // OPSLIMIT=4, MEMLIMIT=1GB — for wallet master keys
).unwrap();

// Verify
let valid = hash.verify(password).is_ok();
```

`Config` presets:
- `interactive()` — fast (login, ~1s on phone, ~64MB RAM)
- `moderate()` — slower (~3s, 256MB)
- `sensitive()` — max security (~5s+, 1GB) — use for master wallet keys

For deriving a 32-byte key:

```rust
use dryoc::pwhash::PwHash;

let derived: [u8; 32] = PwHash::derive_key(
    password,
    &salt,
    Config::sensitive(),
).unwrap();
```

## Public-Key Encryption (X25519 + XSalsa20-Poly1305)

```rust
use dryoc::dryocbox::DryocBox;
use dryoc::keypair::*;

let alice_keypair: KeyPair = KeyPair::gen();
let bob_keypair: KeyPair = KeyPair::gen();

let nonce = dryoc::dryocbox::Nonce::gen();

// Alice encrypts for Bob
let ciphertext = DryocBox::encrypt_to_vecbox(
    b"hello bob",
    &nonce,
    &bob_keypair.public_key,
    &alice_keypair.secret_key,
).unwrap();

// Bob decrypts
let plaintext = ciphertext.decrypt_to_vec(
    &nonce,
    &alice_keypair.public_key,
    &bob_keypair.secret_key,
).unwrap();
```

For anonymous sender (sealed box):

```rust
use dryoc::dryocbox::DryocBox;

let bob_keypair = KeyPair::gen();

// Anyone can encrypt with Bob's public key (no sender identity needed)
let sealed = DryocBox::seal_to_vecbox(
    b"anonymous tip",
    &bob_keypair.public_key,
).unwrap();

// Only Bob can decrypt
let plaintext = sealed.unseal_to_vec(
    &bob_keypair.public_key,
    &bob_keypair.secret_key,
).unwrap();
```

## Digital Signatures — Ed25519

```rust
use dryoc::sign::*;

let signing_keypair = SigningKeyPair::gen();

let message = b"sign me";
let signed = SignedMessage::sign_to_vec(message, &signing_keypair.secret_key).unwrap();

// Verify
let verified_message = signed.verify_to_vec(&signing_keypair.public_key).unwrap();
assert_eq!(&verified_message, message);

// Detached signature
let signature = SigningKeyPair::sign_detached(message, &signing_keypair.secret_key);
let valid = SigningKeyPair::verify_detached(message, &signature, &signing_keypair.public_key);
```

## Generic Hashing — BLAKE2b

```rust
use dryoc::generichash::GenericHash;

let hash = GenericHash::hash_with_defaults_to_vec::<_, &[u8]>(b"input data", None).unwrap();
// 32-byte output by default; customizable

// Keyed (MAC-like)
let key: [u8; 32] = [0x42; 32];
let keyed_hash = GenericHash::hash_with_defaults_to_vec(b"input", Some(&key)).unwrap();
```

For HMAC use `crypto_auth_hmacsha256`/`hmacsha512` family.

## Key Derivation — HKDF / `crypto_kdf`

`crypto_kdf` derives subkeys from a master key (BLAKE2b-based):

```rust
use dryoc::kdf::*;

let master = Key::gen();
let context = *b"BHODL_v1";                       // 8 bytes

let subkey: [u8; 32] = master.derive_subkey(1, &context).into();
let another: [u8; 32] = master.derive_subkey(2, &context).into();
```

For HKDF (RFC 5869) use `crypto_kdf_hkdf_sha256_*` family directly.

## Random Bytes

```rust
use dryoc::rng::*;

let mut buf = [0u8; 32];
randombytes_buf(&mut buf);             

Related in Backend & APIs