If you're not familiar with cryptography terminology, [read this page first](https://paragonie.com/blog/2015/08/you-wouldnt-base64-a-password-cryptography-decoded). It covers everything from terms (e.g. *nonce*) to concepts (e.g. *public-key encryption*).
-----
Last Friday at [Day Camp 4 Developers](https://daycamp4developers.com), I presented a talk titled *Cooking with Sodium in PHP 7.2*, which was largely live-demoing the various cryptography features provided by libsodium. One of the questions I was asked by attendees was about knowing which feature to use to solve specific problems. This is the sort of problem that I suspect many people run into, so here's a quick reference table followed by a detailed explanation.
In the table below, all encryption modes utilize [authenticated encryption](https://paragonie.com/blog/2015/05/using-encryption-and-authentication-correctly).
Libsodium Function | Type | Notes / Use Case |
Libsodium Function | Type | Notes / Use Case |
crypto_pwhash |
Password hashing |
Secure password storage, key derivation from user input |
crypto_generichash |
Cryptographic hash function |
Collision- and preimage-resistant; replace MD5/SHA1/etc. |
crypto_shorthash |
Short-input hash function |
Hash tables, bloom filters, cache lookup keys |
crypto_auth |
Symmetric authentication |
Both parties can verify and/or forge messages |
crypto_sign |
Asymmetric authentication |
Digital signature (anyone can verify, only sender can sign) |
crypto_aead_* |
Symmetric encryption |
Shared-secret encryption with additional data |
crypto_secretbox |
Symmetric encryption |
Compatibility with NaCl, TweetNaCl, etc. |
crypto_box |
Asymmetric encryption |
Both sender and receiver can decrypt |
crypto_box_seal |
Asymmetric encryption |
Only receiver can decrypt; sender is anonymous |
# Solving Common Tasks with Libsodium
**I need to store an encrypted or hashed password ([actually never encrypted, only hashed](https://paragonie.com/blog/2015/08/you-wouldnt-base64-a-password-cryptography-decoded#passwords))**
Use [`crypto_pwhash_str()` and `crypto_pwhash_str_verify()`](https://paragonie.com/blog/2017/06/libsodium-quick-reference-quick-comparison-similar-functions-and-which-one-use#crypto-pwhash).
-----
**I need to encrypt a string and then decrypt it later (on the *same* machine)**
For simplicity: use [`crypto_secretbox()` and `crypto_secretbox_open()`](https://paragonie.com/blog/2017/06/libsodium-quick-reference-quick-comparison-similar-functions-and-which-one-use#crypto-secretbox).
It's actually better to use [`crypto_aead_*`](https://paragonie.com/blog/2017/06/libsodium-quick-reference-quick-comparison-similar-functions-and-which-one-use#crypto-aead) (especially if you use the [sample code](https://paragonie.com/blog/2017/06/libsodium-quick-reference-quick-comparison-similar-functions-and-which-one-use#crypto-aead-sample-php), but the `secretbox` API is simple and secure.
-----
**I need to encrypt a string and then decrypt it later (on a *different* machine)**
*It depends!*
Do you have a two-way data flow (`A <=> B`) or a one-way data flow (`A -> B`)?
If you're only ever encrypting on Node A, and only ever decrypting on Node B, you'll want [`crypto_box_seal()` and `crypto_box_seal_open()`](https://paragonie.com/blog/2017/06/libsodium-quick-reference-quick-comparison-similar-functions-and-which-one-use#crypto-box-seal). This prevents the sender (Node A) from decrypting messages after they've been sent to recipient (Node B).
Otherwise, you'll simply want to use [`crypto_box()` and `crypto_box_open()`](https://paragonie.com/blog/2017/06/libsodium-quick-reference-quick-comparison-similar-functions-and-which-one-use#crypto-box).
-----
**I previously used mcrypt_encrypt() or openssl_encrypt() and need to migrate my data**
First, decrypt your data as usual, then re-encrypt using [`crypto_secretbox()` and `crypto_secretbox_open()`](https://paragonie.com/blog/2017/06/libsodium-quick-reference-quick-comparison-similar-functions-and-which-one-use#crypto-secretbox).
-----
**I previously used openssl_public_encrypt() / openssl_private_decrypt() and need to migrate my data**
First, decrypt your data as usual, then re-encrypt using [`crypto_box`](https://paragonie.com/blog/2017/06/libsodium-quick-reference-quick-comparison-similar-functions-and-which-one-use#crypto-box) or [`crypto_box_seal`](https://paragonie.com/blog/2017/06/libsodium-quick-reference-quick-comparison-similar-functions-and-which-one-use#crypto-box-seal) (see previous answer about encryption between different machines for guidance on which one you want).
# Libsodium Function Analysis
**PHP Developers:** In PHP before 7.2 with libsodium from PECL, the functions below were defined in the `Sodium` name space. In PHP 7.2, the namespaces were dropped in favor of a `sodium_` prefix (to conform to the PHP internal development standards).
For example, in PHP 7.1 and below with ext/sodium from PECL, an example snippet for `crypto_secretbox()` might look like this:
<?php
// PHP < 7.2 with `pecl install libsodium`
$key = str_repeat("\x80", 32);
$nonce = random_bytes(24);
$data = $nonce . \Sodium\crypto_secretbox("Test", $nonce, $key);
When PHP 7.2 is released, the interface will look like this instead:
<?php
// PHP >= 7.2
$key = str_repeat("\x80", 32);
$nonce = random_bytes(24);
$data = $nonce . sodium_crypto_secretbox("Test", $nonce, $key);
For [sodium_compat](https://github.com/paragonie/sodium_compat) users, your code will look like this:
<?php
// PHP >= 7.2
$key = str_repeat("\x80", 32);
$nonce = random_bytes(24);
$data = $nonce . ParagonIE_Sodium_Compat::crypto_secretbox("Test", $nonce, $key);
In the examples below, when we define a libsodium function, the actual function name will be either namespaced, prefixed, or a static method on `ParagonIE_Sodium_Compat`. For simplicity, we're using the bare libsodium name since that part remains constant.
Hash Functions
crypto_pwhash()
Choose your own adventure:
* Key derivation: `crypto_pwhash()`
* Password hashing/verification: `crypto_pwhash_str()` and `crypto_pwhash_str_verify()`
Libsodium uses Argon2, the [Password Hashing Competition](https://password-hashing.net) winner. More specifically, it uses Argon2i (the side-channel resistant variant) in all current versions, but a future version may switch the default to Argon2id, which is more resistant to GPU attacks.
You want to use one of the `crypto_pwhash` functions whenever you are taking a **user-provided input** (i.e a password) and your goal is to either:
* turn their password into a cryptography key, or
* store it for secure verification at a later date
Make sure you check [the official libsodium documentation covering `crypto_pwhash`](https://download.libsodium.org/doc/password_hashing) for details on how to use it.
crypto_pwhash() Sample Code (PHP 7.2+)
<?php
/* This uses Argon2i with two numeric constants that correspond to
* the number of passes (OPSLIMIT) and the amount of memory to use
* (MEMLIMIT). A higher OPSLIMIT helps against CPU attacks, while a
* higher MEMLIMIT helps against GPU attacks.
*/
$storeInDatabase = sodium_crypto_pwhash_str(
$password,
SODIUM_CRYPTO_PWHASH_OPSLIMIT_INTERACTIVE,
SODIUM_CRYPTO_PWHASH_MEMLIMIT_INTERACTIVE
);
/* Once that's stored, you can just test against the hash like so: */
if (sodium_crypto_pwhash_str_verify($storeInDatabase, $password)) {
/* Logged in! */
} else {
/* Incorrect password. */
}
crypto_generichash()
For PHP developers: This is sort of like `hash()`, except it can also be `hash_hmac()` if you pass a key, and it only allows BLAKE2b (so you don't have to select the algorithm).
You want to use `crypto_generichash()` anywhere you'd normally use `hash()`, `md5()`, or `sha1()` (unless "normally" implies something that would cause a cryptographer would look at you funny). `crypto_generichash()` is attractive because it is all of the following:
* Collision-resistant
* Preimage-resistant
* Immune to length-extension attacks
* More secure than SHA256
* Faster than MD5
See also: [the libsodium documentation on `crypto_generichash`](https://download.libsodium.org/doc/hashing/generic_hashing.html).
crypto_generichash() Sample Code (PHP 7.2+)
<?php
$someData = 'This is a test message';
$someSecretKey = random_bytes(32);
$hash = sodium_crypto_generichash($someData);
var_dump(mb_strlen($hash, '8bit')); /* int(32) */
$blake2mac = sodium_crypto_generichash($someData, $someSecretKey);
var_dump(mb_strlen($blake2mac, '8bit')); /* int(32) */
$truncated = sodium_crypto_generichash($someData, '', 16);
var_dump(mb_strlen($truncated, '8bit')); /* int(16) */
crypto_shorthash()
SipHash, the algorithm powering `crypto_shorthash()`, is a secure keyed pseudo-random function. Or, in other words, it's a hash function like BLAKE2b, except it's only meant to be used with a key and its output size of 64 bits is too small to be collision-resistant or immune to brute-force searches.
Don't use `crypto_shorthash()` in places where `crypto_generichash()` would be appropriate. SipHash is better suited for building hash tables with a per-request key to resist hash-collision denial-of-service attacks. You can also use it for Bloom filters and cache lookups where micro-benchmarks matter. But generally, `crypto_generichash()` and `crypto_pwhash()` are probably better tools for the job.
See also: [the libsodium documentation on `crypto_shorthash`](https://download.libsodium.org/doc/hashing/short-input_hashing.html).
crypto_shorthash() Sample Code (PHP 7.2+)
<?php
$key = random_bytes(16);
$input = ['apple', 'boy', 'cat', 'dog', 'echo'];
$mapped = [];
foreach ($input as $item) {
$hash = sodium_crypto_shorthash($item, $key);
$mapped[bin2hex($hash)] = $item;
}
var_dump($mapped);
Authentication
crypto_auth()
`crypto_auth()` is from NaCl, and serves to provide symmetric-key authentication. If you know about HMAC, you know what this does. PHP developers will only really only need `crypto_auth()` and its counterpart `crypto_auth_verify()` if you're aiming to interoperate with other NaCl/libsodium projects. Otherwise, `hash_hmac()` and `hash_equals()` accomplish the same thing.
`crypto_auth()` is, in fact, HMAC-SHA512 truncated to 32 bytes.
It is important to note that, with HMAC, any party capable of verifying messages is also capable of signing them. That's what symmetric-key authentication means. If you need one entity to be able to sign, but everyone else in the universe to be able to only verify, then you want `crypto_sign()` instead.
See also: [the libsodium documentation on `crypto_auth`](https://download.libsodium.org/doc/secret-key_cryptography/secret-key_authentication.html).
crypto_auth() Sample Code (PHP 7.2+)
<?php
$key = random_bytes(32);
$message = 'authenticate me';
/* Get the message authentication code (MAC): */
$mac = sodium_crypto_auth($message, $key);
/* Verify a message: */
if (sodium_crypto_auth_verify($mac, $message, $key)) {
/* Verified */
} else {
/* Message has been tampered with; discard */
}
crypto_sign()
The `crypto_sign` functions come in two flavors:
* `crypto_sign()` and `crypto_sign_open()`, which are useful for sending signed messages in one go.
* `crypto_sign_detached()` and `crypto_sign_verify_detached()`, which are more generally useful.
Generally, you'll find yourself reaching for the latter two more in PHP land, but the original two are useful too.
Unlike `crypto_auth`, `crypto_sign` utilizes *asymmetric* (a.k.a. *public-key*) cryptography. You sign a message with your secret key, and anyone with your public key can verify the message.
Once again, the libsodium [documentation for `crypto_sign`](https://download.libsodium.org/doc/public-key_cryptography/public-key_signatures.html) is worth a read.
crypto_sign() Sample Code (PHP 7.2+)
<?php
$mySigningKeypair = sodium_crypto_sign_keypair();
$secretKey = sodium_crypto_sign_secretkey($mySigningKeypair);
$publicKey = sodium_crypto_sign_publickey($mySigningKeypair);
$message = 'authenticate me';
/* Sign the message, using your secret key (which is NOT given out): */
$signature = sodium_crypto_sign_detached($message, $secretKey);
/* Now validate the signature with your public key (which IS given out): */
if (sodium_crypto_sign_verify_detached($signature, $message, $publicKey)) {
/* Message was signed by me. */
} else {
throw new Exception('Invalid signature. Do not trust the message.');
}
Encryption
crypto_aead()
AEAD is a cryptographer acronym that stands for **A**uthenticated **E**ncryption with **A**ssociated **D**ata. The general idea is that you have:
* Your plaintext, which gets encrypted into the ciphertext.
* Some other data, which doesn't touch the ciphertext.
* An authentication tag, which covers both the ciphertext and the other data.
You want to use `crypto_aead` when you have either a pre-shared key or a negotiated one (e.g. via Elliptic Curve Diffie-Hellman).
AEAD modes are the preferred way to encrypt in 2017. Libsodium offers several options for its AEAD interface, all of which implement symmetric encryption. You will probably want to use, in order of preference:
* (One day): `crypto_aead_encrypt()` and `crypto_aead_decrypt()`
* Not available yet.
* Will be available after the selection of [CAESAR](https://competitions.cr.yp.to/caesar.html) finalists.
* `crypto_aead_xchacha20poly1305_ietf_*()`
* Allows you to use very large nonces, which can safely be generated randomly with no practical risk of accidental nonce reuse.
* `crypto_aead_chacha20poly1305_ietf_*()`
* The standardized ChaCha20-Poly1305 variant.
* `crypto_aead_aes256gcm_*()`
* Required specialized hardware support. Only use this if every device is guaranteed to have modern processors.
* `crypto_aead_chacha20poly1305_*()`
* Implemented before the IETF standardized their nonce/counter sizes for use in TLS. Less widely supported.
The [AEAD section of the libsodium documentation](https://download.libsodium.org/doc/secret-key_cryptography/aead.html) has more details about key/nonce sizes, etc.
crypto_aead_*() Sample Code (PHP with sodium_compat)
<?php
/**
* Wrap crypto_aead_*_encrypt() in a drop-dead-simple encryption interface
*
* @link https://paragonie.com/b/kIqqEWlp3VUOpRD7
* @param string $message
* @param string $key
* @return string
*/
function simpleEncrypt($message, $key)
{
$nonce = random_bytes(24); // NONCE = Number to be used ONCE, for each message
$encrypted = ParagonIE_Sodium_Compat::crypto_aead_xchacha20poly1305_ietf_encrypt(
$message,
$nonce,
$nonce,
$key
);
return $nonce . $encrypted;
}
/**
* Wrap crypto_aead_*_decrypt() in a drop-dead-simple decryption interface
*
* @link https://paragonie.com/b/kIqqEWlp3VUOpRD7
* @param string $message - Encrypted message
* @param string $key - Encryption key
* @return string
* @throws Exception
*/
function simpleDecrypt($message, $key)
{
$nonce = mb_substr($message, 0, 24, '8bit');
$ciphertext = mb_substr($message, 24, null, '8bit');
$plaintext = ParagonIE_Sodium_Compat::crypto_aead_xchacha20poly1305_ietf_decrypt(
$ciphertext,
$nonce,
$nonce,
$key
);
if (!is_string($plaintext)) {
throw new Exception('Invalid message');
}
return $plaintext;
}
$secretKey = random_bytes(32);
$message = 'Test message';
/* Encrypt the message: */
$ciphertext = simpleEncrypt($message, $secretKey);
/* Decrypt the message: */
try {
$decrypted = simpleDecrypt($ciphertext, $secretKey);
var_dump(hash_equals($decrypted, $message));
/* bool(true) */
} catch (Exception $ex) {
/* Someone is up to no good */
exit(255);
}
crypto_secretbox()
`crypto_secretbox()` is from NaCl and implements symmetric (shared-key) authenticated encryption. `secretbox` accepts a large nonce, which is safe to generate randomly (`random_bytes()` in PHP, `randombytes_buf()` in libsodium) and virtually never worry about reusing a nonce.
You want to use `crypto_secretbox()` when you have either a pre-shared key or a negotiated one (e.g. via Elliptic Curve Diffie-Hellman).
If you have to choose between `secretbox` and `aead_xchacha20poly1305`, go for the AEAD mode. Otherwise, use `secretbox`.
The relevant section of the libsodium documentation for `crypto_secretbox` can be found [here](https://download.libsodium.org/doc/secret-key_cryptography/authenticated_encryption.html).
crypto_secretbox_*() Sample Code (PHP 7.2+)
<?php
$key = random_bytes(32);
$message = 'Yellow submarine';
/* For each encryption: */
$nonce = random_bytes(24); /* Never repeat this! */
$ciphertext = sodium_crypto_secretbox($message, $nonce, $key);
$plaintext = sodium_crypto_secretbox_open($ciphertext, $nonce, $key);
crypto_box()
`crypto_box` is from NaCl and implements authenticated asymmetric (a.k.a. public-key) encryption. With `crypto_box()`, both the sender and recipient can read messages and verify the other party sent them. However, it allows each party to negotiate their own private keys and share distinct secrets with each other, so talking with one person doesn't allow you to eavesdrop on another's conversations with them.
You want to use `crypto_box()` where you'd normally implement RSA encryption. It's more commonly found in interactive protocols (e.g. messaging applications).
To learn more, see the [libsodium documentation for `crypto_box`](https://download.libsodium.org/doc/public-key_cryptography/authenticated_encryption.html).
crypto_box() Sample Code (PHP 7.2+)
<?php
# Setup:
/* On Node A */
$aliceKeypair = sodium_crypto_box_keypair();
$aliceSecretKey = sodium_crypto_box_secretkey($aliceKeypair);
$alicePublicKey = sodium_crypto_box_publickey($aliceKeypair);
// Then share $alicePublicKey with Node B
/* On Node B: */
$bobKeypair = sodium_crypto_box_keypair();
$bobSecretKey = sodium_crypto_box_secretkey($bobKeypair);
$bobPublicKey = sodium_crypto_box_publickey($bobKeypair);
// Then share $bobPublicKey with Node A
# Transmission:
/* Sending from Node A to Node B */
$message 'Hi there! :)';
$aliceToBob = $aliceSecretKey . $bobPublicKey;
$nonce = random_bytes(24); /* Never repeat this! */
$ciphertext = $nonce . sodium_crypto_box($message, $nonce, $aliceToBob);
/* On Node B, receiving an encrypted message from Node A */
$bobToAlice = $bobSecretKey . $alicePublicKey;
$nonce = mb_substr($ciphertext, 0, 24, '8bit');
$encrypted = mb_substr($ciphertext, 24, null, '8bit');
$decrypted = sodium_crypto_box_open($encrypted, $nonce, $bobToAlice);
# Alternatively:
/* Sending from Node B to Node A */
$message 'Hello yourself.';
$bobToAlice = $bobSecretKey . $alicePublicKey;
$nonce = random_bytes(24); /* Never repeat this! */
$ciphertext = $nonce . sodium_crypto_box($message, $nonce, $bobToAlice);
/* On Node A, receiving an encrypted message from Node B */
$aliceToBob = $aliceSecretKey . $bobPublicKey;
$nonce = mb_substr($ciphertext, 0, 24, '8bit');
$encrypted = mb_substr($ciphertext, 24, null, '8bit');
$decrypted = sodium_crypto_box_open($encrypted, $nonce, $aliceToBob);
crypto_box_seal()
`crypto_box_seal()` is a libsodium exclusive, which allows the sender to encrypt a message that only the recipient can decrypt. Sealing APIs are sometimes referred to as **anonymous public-key encryption**, because although it provides ciphertext integrity, the sender is not authenticated like with `crypto_box`.
You want to use `crypto_box_seal` whenever you're encrypting information that you don't want the sender to be able to decrypt. For example, storing encrypted data in an online database that can only be decrypted with key that is kept offline in an air-gapped machine.
If you're trying to decide between `crypto_box()` versus `crypto_box_seal()`, ask yourself if the system that performed the encryption should be capable of decrypting the messages they sent. If the answer is no, you want `crypto_box_seal()`. If you still need the sender to be authenticated, use `crypto_sign()` before `crypto_box_seal()` on the sending side and `crypto_sign_open()` after `crypto_box_seal_open()` on the receiving side.
As always, [the documentation for `crypto_box_seal`](https://download.libsodium.org/doc/public-key_cryptography/sealed_boxes.html) explains its usage in-depth.
crypto_box_seal() Sample Code (PHP 7.2+)
<?php
# Setup:
/* On Node B: */
$bobKeypair = sodium_crypto_box_keypair();
$bobPublicKey = sodium_crypto_box_publickey($bobKeypair);
// Then share $bobPublicKey with Node A
# Transmission:
/* Sending from Node A to Node B */
$message 'Hi there! :)';
$ciphertext = sodium_crypto_box_seal($message, $bobPublicKey);
/* On Node B, receiving an encrypted message from Node A */
$decrypted = sodium_crypto_box_seal_open($ciphertext, $bobKeypair);
# Other Functions
If you're confused about any of the libsodium functions not listed here, refer to the [official libsodium documentation](https://download.libsodium.org/doc/).