Authenticated Encryption with libsodium in C

The XOR exercise demonstrates reversible byte operations. Real secure communication needs more: ciphertext should reveal nothing useful about the message, and any alteration must be detected before decrypted data is used. Authenticated encryption provides both properties.

What AEAD Adds

Authenticated encryption with associated data—AEAD—combines:

  • Confidentiality: someone without the key cannot recover the plaintext.
  • Integrity and authenticity: modified ciphertext fails verification.
  • Associated data: unencrypted protocol headers can still be authenticated.

XChaCha20-Poly1305 uses a 256-bit secret key and a large 192-bit nonce. The nonce is public and travels with the ciphertext, but it must be unique for every message encrypted with the same key.

Encrypt and Decrypt One Message

#include <sodium.h>
#include <stdio.h>
#include <string.h>

int main(void)
{
    if (sodium_init() < 0) {
        fputs("libsodium initialization failed\n", stderr);
        return 1;
    }

    const unsigned char message[] = "Hello from authenticated encryption";
    const unsigned long long message_length = sizeof(message) - 1;

    unsigned char key[crypto_aead_xchacha20poly1305_ietf_KEYBYTES];
    unsigned char nonce[crypto_aead_xchacha20poly1305_ietf_NPUBBYTES];
    unsigned char ciphertext[
        sizeof(message) - 1 + crypto_aead_xchacha20poly1305_ietf_ABYTES
    ];
    unsigned long long ciphertext_length;

    crypto_aead_xchacha20poly1305_ietf_keygen(key);
    randombytes_buf(nonce, sizeof nonce);

    if (crypto_aead_xchacha20poly1305_ietf_encrypt(
            ciphertext, &ciphertext_length,
            message, message_length,
            NULL, 0,
            NULL, nonce, key) != 0) {
        fputs("encryption failed\n", stderr);
        sodium_memzero(key, sizeof key);
        return 1;
    }

    unsigned char decrypted[sizeof(message)];
    unsigned long long decrypted_length;

    if (crypto_aead_xchacha20poly1305_ietf_decrypt(
            decrypted, &decrypted_length,
            NULL,
            ciphertext, ciphertext_length,
            NULL, 0,
            nonce, key) != 0) {
        fputs("ciphertext was forged or corrupted\n", stderr);
        sodium_memzero(key, sizeof key);
        return 1;
    }

    decrypted[decrypted_length] = '\0';
    printf("Decrypted: %s\n", decrypted);

    sodium_memzero(key, sizeof key);
    return 0;
}

Compile the Example

Install the libsodium development package using your operating system’s package manager, then compile with:

cc -std=c11 -Wall -Wextra -pedantic \
  -o aead_example aead_example.c -lsodium

./aead_example

What Travels Over the Network?

A simple record can contain a version, the nonce, a ciphertext length, and the ciphertext with its authentication tag. The nonce does not need to be secret. The key must never be transmitted beside the message.

+---------+----------------+-------------------+----------------------+
| version | 24-byte nonce | ciphertext length | ciphertext and tag   |
+---------+----------------+-------------------+----------------------+

TCP still requires framing and complete read/write loops. Encryption does not restore message boundaries, so combine this format with the techniques in TCP Is a Byte Stream: Message Framing in C.

Key and Nonce Rules

  • Generate keys with crypto_aead_xchacha20poly1305_ietf_keygen() or another documented libsodium key-derivation process.
  • Never hard-code production keys into source code.
  • Use a fresh nonce for each message under a key. Random nonces are appropriate with XChaCha20’s large nonce space.
  • Transmit the nonce with the ciphertext and store it when ciphertext is stored.
  • Treat a decryption failure as a rejected message. Do not use unauthenticated output.
  • Erase temporary secret material with sodium_memzero() when it is no longer needed.

What This Example Does Not Solve

AEAD protects messages once both parties possess the right secret key. It does not decide who the peer is or distribute that key safely. Internet-facing applications usually need a complete protocol such as TLS rather than a custom encrypted socket format.

References