Quickstart and FAQ

Boilerplate

A project using libsodium should include the sodium.h header. Including individual headers is neither required nor recommended.

The sodium_init() function must be called before any other function. It is safe to call sodium_init() multiple times or from different threads; it will immediately return 1 without doing anything if the library has already been initialized.

#include <sodium.h>

int main(void)
{
    if (sodium_init() < 0) {
        /* panic! the library couldn't be initialized; it is not safe to use */
    }
    return 0;
}

Is libsodium cross-platform?

Yes, a message can be encrypted in Python on a MIPS CPU, decrypted in JavaScript using Chrome on Windows, and its signature can then be verified by an iPhone app written in Swift.

The available algorithms are the same on all supported platforms, input and output formats are also identical on all platforms, and bindings for various programming languages tend to leverage the original algorithms and formats.

How do I generate random numbers that are safe to use for cryptography?

Use the randombytes API.

How do I compute a hash?

Use the crypto_generichash API.

What is the difference between a secret key and a password?

A password has a couple of requirements to fulfill:

  • It has to be reasonably short.

  • It must be easy to type on any keyboard.

  • Users may have to remember it.

As a result, the number of possible passwords of a given size is fairly small. For the same size, the number of distinct passwords actually used is much smaller, and their distribution is skewed. Even if mitigations exist, guessing (brute-forcing) a password is often practical.

A secret key, on the other hand, is a sequence of bits that can be of any size, doesn’t have any restrictions, and is generated by software or specialized hardware. A 32-byte key represents 256 bits, none of them being predicable by observing other bits from that key or previous keys.

Secret keys are not designed to be typed on a keyboard. They are binary data stored as files, kept in secure memory, or sent over the network. They can be unique and offer far more security than passwords.

Libsodium almost exclusively uses secret keys.

If an application must use a password as a secret key, it should call the crypto_pwhash() function first. This computes a secret key from a password using an intentionally CPU-intensive and memory-hard function to slow down brute-force attacks.

Native secret keys using the *_keygen() function should always be preferred.

How do I initialize the library from another library?

If the other library doesn’t have any initialization function, it can use a DllMain() function (Windows) or __attribute__((constructor)) (GCC, Clang, icc on macOS and ELF-based systems) to call sodium_init() on load.

However, explicitly calling initialization functions is recommended as automatic initialization makes it difficult to safely recover from errors.

How do I check if a function call succeeded?

Functions returning an int return 0 on success and -1 to indicate an error.

Functions returning a pointer return a valid address on success and NULL on error.

Some operations include variants with slightly different algorithms. For example, authenticated encryption can be done with XChaCha20-Poly1305 or AES-GCM. For consistency and to make high-level wrappers using dynamic dispatch easier to implement, all the variants of a function share the same prototype.

In the above example, AES-GCM can fail due to message size limits, but XChaCha20 cannot; its limits are only theoretical, so checking the return value for that variant would not be strictly necessary. However, it is best to check unconditionally in applications because it is cheap, good hygiene, and functions can easily be replaced later if necessary.

Some returned values must be checked. For example, the outcome of the verification of an authentication token. These functions are tagged with the warn_unused_result attribute and will cause a compiler warning if ignored. Such warnings must not be ignored.

How do I encrypt data?

One-shot encryption where everything fits in memory

  1. Create a secret key using crypto_secretbox_keygen().

  2. Create a nonce using randombytes_buf(nonce, sizeof nonce).

  3. Use crypto_secretbox_easy() to encrypt the message and send/store the resulting ciphertext along with the nonce. Unlike the key, the nonce doesn’t have to be secret.

  4. Use crypto_secretbox_open_easy() to decrypt the ciphertext using the same key and nonce.

If everything doesn’t fit in memory or is not available as a single chunk

How do I safely store and later verify a password?

How do I encrypt a file using a password?

  1. Derive an encryption key from the password using crypto_pwhash().

  2. Use that key with the crypto_secretstream API.

Secret file metadata should be part of the encrypted data, and non-secret metadata can be included as additional data.

How can A and B securely communicate without a pre-shared secret key?

Use the key exchange API:

  1. A and B both call crypto_kx_keypair() to create their own key pair. Secret keys must remain secret, but A can send their public key to B or make it available to everyone. The same applies to B.

  2. A uses crypto_kx_client_session_keys() along with B’s public key and their own key pair to create a set of shared keys to communicate with B.

  3. B uses crypto_kx_server_session_keys() along with A’s public key and their own key pair to create a set of shared keys to communicate with A.

The shared keys computed by A and B will be identical. There are two of them, so one can be used to encrypt and decrypt messages in one direction (from A to B), and the other can be used to encrypt and decrypt messages in the other direction (from B to A).

To encrypt and decrypt data using one of these shared secret keys, use one of crypto_secretbox_*(), crypto_secretstream_*(), or crypto_aead_*().

How can I derive multiple keys from a single master key like what HKDF does?

Use the key derivation API.

Do I need to add a signature to encrypted messages to detect if they have been tampered with?

No, signatures are designed to allow non-secret data to be verified by many parties using a public key. For example, a signature can be used to verify the authenticity of a firmware update.

When A encrypts a message for B using a shared secret key using crypto_box(), crypto_secretbox(), crypto_seal(), crypto_secretstream(), or crypto_aead(), an authentication tag is also computed and should be sent to B along with the encrypted payload.

During the decryption process, the secret key is used to check that the authentication tag is valid for the given encrypted message. If that message has been modified, the tag will not be valid and decryption functions will return an error code. Knowing the secret key is required to create a valid tag, and therefore only A and B can create such a tag.

Also, if shared keys are computed using the key exchange API (crypto_kx), a valid tag for a message can only be created by the sender.

How can I sign and encrypt using the same key pair?

First, read the previous section. In most cases, signing a message in addition to encrypting it is not required.

If you need to sign and encrypt a message, possibly for signatures to be publicly verified, consider using signcryption.

Alternatively, signing key pairs can be converted to X25519 key exchange key pairs. This can be used to encrypt and sign independently.

However, this is not recommended and is usually not necessary. Prefer using distinct key pairs instead as this will always be safer.

Remember that public keys for both operations are very small (only 32 bytes). Concatenating both produces an aggregate public key that is only 64 bytes long. For most applications, the overhead is negligible, and conversions are no longer required.

On the sender side, crypto_sign_seed_keypair() and crypto_kx_seed_keypair() can derive specialized key pairs from the same 32-byte seed.

If you really need to use the same key pair for both operations, Diffie-Hellman key exchange can be made over Edwards25519, the same group as the one used for signatures. Libsodium provides the crypto_scalarmult_ed25519() and crypto_scalarmult_ed25519_base() functions for scalar multiplication over edwards25519.

The following code illustrates how to do it:

unsigned char ed25519_pk[crypto_sign_PUBLICKEYBYTES];
unsigned char ed25519_sk[crypto_sign_SECRETKEYBYTES];
unsigned char ed25519_seed[crypto_sign_SEEDBYTES];
unsigned char edwards25519_pk[crypto_scalarmult_ed25519_BYTES];
unsigned char edwards25519_sk[crypto_scalarmult_ed25519_SCALARBYTES];
unsigned char h[crypto_hash_sha512_BYTES];

// create an Ed25519 keypair
crypto_sign_keypair(ed25519_pk, ed25519_sk);

// Extract the seed from the Ed25519 secret key
crypto_sign_ed25519_sk_to_seed(ed25519_seed, ed25519_sk);

// Ed25519 internally derives two values from the seed; this is how it does it.
crypto_hash_sha512(h, ed25519_seed, sizeof ed25519_seed);

// The first half of the output corresponds to the scalar.
memcpy(edwards25519_sk, h, sizeof edwards25519_sk);

// The Ed25519 public key is the Edwards25519 base point multiplied
// by the scalar. We can copy ed25519_pk, or recompute is as follows:
crypto_scalarmult_ed25519_base(edwards25519_pk, edwards25519_sk);

// (edwards25519_pk, edwards25519_sk) can be used for Diffie-Hellman
// using crypto_scalarmult_ed25519() if you like to. Do not forget to
// hash the result in order to get a key suitable for encryption.

Finally, if, for some reason, you want to implement your own signcryption scheme:

  • If public verifiability is not required, sign (encryption_key || message) first then encrypt (recipient_id || signature || message).

  • If public verifiability is required, encrypt (sender_id || message) then sign the ciphertext.

  • If public verifiability is required, and the same message can be decrypted by multiple recipients, encrypt (H(sender_id || message) || message) (if using AES-GCM or Salsa20/ChaCha20-Poly1305) then sign the ciphertext.

sender_id and recipient_id are public data that uniquely identifies a party.

Verify the metadata when opening a signed plus encrypted message.

If this section looks complicated, ignore it, and use distinct keys for encryption and signing. This is easier and more secure. Furthermore, signing and encryption keys don't necessarily have the same life time. It is common to frequently rotate encryption keys, while signing keys are long-term.

Also keep in mind that with post-quantum schemes, key pairs for signature and encryption systems are completely different and incompatible. So, if only for future-proofing your applications and protocols, do not assume that a single key pair can be used for both operations.

How do I hide the length of a message?

Use padding.

Should I call crypto_generichash_blake2b or just crypto_generichash?

Always use the high-level API if one is available. The low-level API it is based on is guaranteed not to change before a major revision of the library. If a high-level API needs to use a different construction, it will expose a different set of functions.

This is true for all APIs provided by this library.

Why is crypto_stream() barely documented and not even present in some bindings?

The crypto_stream() API generates a deterministic sequence of bytes from a seed and optionally applies the XOR operation between that sequence and some input sequence.

Performing the XOR operation once produces content that resembles random data, and performing the same operation twice restores the initial input sequence.

This can be seen as a form of encryption. However,

  • If an adversary replaces the ciphertext with nul bytes, the original, complete sequence derived from the seed will be decrypted. This can have catastrophic implications with some (badly designed) protocols.

  • More generally, anyone can modify the ciphertext without this being detected. Since a stream cipher is XOR’d with a message, targeted bits can be flipped. Knowing the secret key is not required.

If a deterministic sequence must be derived from a seed (e.g. for unit testing), libsodium provides the randombytes_buf_deterministic() function.

For actual encryption, other options such as crypto_secretbox, crypto_aead, and crypto_secretstream should be used over crypto_stream as they will add and verify an authentication tag to detect data that has been corrupted or tampered with.

crypto_stream() is only useful as a building block to design custom constructions. As-is, it is completely insecure.

Is encryption without nonces possible?

The encryption schemes implemented in libsodium are fast but require a unique (key, nonce) tuple for every message.

Nonces don’t have to be secret, but they must not be reused. Reusing a nonce would destroy the confidentiality of messages sharing the same nonce and allow an attacker to craft additional valid ciphertexts.

On a platform where counters cannot be maintained and no trusted source of randomness exists, deterministic encryption can be used as a last resort.

XChaCha20-SIV is such a construction, which can also be used for key wrapping.

On such systems, libhydrogen may also be a better option.

I want to write bindings for my favorite language, where should I start?

Start with the crypto_generichash and crypto_secretstream APIs. These are the trickiest to implement bindings for and will provide good insights about how to design your bindings.

Last updated