Quickstart / Libsodium FAQ

Boilerplate

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

The sodium_init() function should then 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 had 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 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 even way 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 bytes key represents 256 bits, none of them being predicable by observing other bits from that key or from previous keys.

Secret keys are not designed to be typed on a keyboard. This is binary data. They are 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 really needs to 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).

But native secret keys using the *_keygen() function should always be prefered.

How do I encrypt data?

One-shot encryption, where everything fits in memory

  • Create a secret key using crypto_secretbox_keygen()
  • Create a nonce using randombytes_buf(nonce, sizeof nonce)
  • 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.
  • 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?

  • Use the crypto_pwhash_str() and crypto_pwhash_str_verify() functions, described in the password hashing guide.

How do I encrypt a file using a password?

  • Derive an encryption key from the password using crypto_pwhash().
  • Use that key with the crypto_secretstream API.
  • File metadata should probably be part of the encrypted data, or, if it is not secret, included as additional data.

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

Use the key exchange API:

  • A and B both call crypto_kx_keypair() to create their own key pair. Secret keys have to remain secret, but A can send its public key to B or even make it available to everyone. The same applies to B's public key.
  • A uses crypto_kx_client_session_keys() along with B's public key and its key pair to create a set of shared keys to communicate with B.
  • B uses crypto_kx_server_session_keys() along with A's public key and its 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. One can be used to encrypt and decrypt message in one direction (from A to B) and the other one to encrypt and decrypt messages in the other direction (from B to A).

To actually 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 examples, 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 in order to create a valid tag, therefore only A and B can create such a tag.

How do I hide the length of a message?

Use padding.

Shall 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. And 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 the 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, what will be decrypted is the original, complete sequence derived from the seed. 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 has to be derived from a seed, for example for unit testing, libsodium provides the randombytes_buf_deterministic() function.

For actual encryption, other options such as crypto_secretbox, crypto_aead and crypto_secretstream must generally 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.

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

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

results matching ""

    No results matching ""