Using Libsodium in PHP Projects

A guide to using the libsodium PHP extension for modern, secure, and fast cryptography. Open Source.

Basic Secret-key Cryptography

Secret-key cryptography is used when only the intended participants of a communication are in possession of the same secret key. This can be the result of a shared password (see Chapter 8) or Diffie Hellman key agreement (see Chapter 9).

Contrast with Public-key cryptography.

Secret-key Authenticated Encryption

Libsodium makes secret-key encryption a breeze. Instead of having to understand the fine details of encryption versus authentication, you only need to know two functions.

Encrypt a message

string \Sodium\crypto_secretbox(string $plaintext, string $nonce, string $key)

This operation:

  • Encrypts a message with a key and a nonce to keep it confidential
  • Computes an authentication tag. This tag is used to make sure that the message hasn't been tampered with before decrypting it.

A single key is used both to encrypt/sign and verify/decrypt messages. For this reason, it is critical to keep the key confidential.

The same message encrypted with the same key, but with two different nonces, will produce two totally different ciphertexts.

The nonce doesn't have to be confidential, but it should never ever be reused with the same key. The easiest way to generate a nonce is to use randombytes_buf().

// Generating your encryption key
$key = \Sodium\randombytes_buf(\Sodium\CRYPTO_SECRETBOX_KEYBYTES);

// Using your key to encrypt information
$nonce = \Sodium\randombytes_buf(\Sodium\CRYPTO_SECRETBOX_NONCEBYTES);
$ciphertext = \Sodium\crypto_secretbox('test', $nonce, $key);

Decrypt a message

string|bool \Sodium\crypto_secretbox_open(string $ciphertext, string $nonce, string $key)

Decrypting a message requires the same nonce and key that was used to encrypt it.

$plaintext = \Sodium\crypto_secretbox_open($ciphertext, $nonce, $key);
if ($plaintext === false) {
    throw new Exception("Bad ciphertext");
}

Secret-key Authentication

Sometimes you don't need to hide the contents of a message with encryption, but you still want to ensure that nobody on the network can tamper with it. For example, if you want to eschew server-side session storage and instead use HTTP cookies as your storage mechanism.

First you need an encryption key that is \Sodium\CRYPTO_AUTH_KEYBYTES long.

$key = \Sodium\randombytes_buf(\Sodium\CRYPTO_AUTH_KEYBYTES);

Authenticating a Message

string \Sodium\crypto_auth(string $message, string $key);

This calculates a Message Authentication Code (MAC) of a given $message with a given secret $key. Typically you want to prepend or append the MAC to the message before sending it.

$message = json_encode($some_array);
$mac = \Sodium\crypto_auth($message, $key);
$outbound = $mac . $message;

Verifying the Authenticity of a Message

bool \Sodium\crypto_auth_verify(string $mac, string $message, string $key)

This function returns TRUE if the given $mac is valid for a particular $message and $key. Otherwise it returns FALSE. This operation is constant-time and side-channel resistant.

if (\Sodium\crypto_auth_verify($mac, $message, $key)) {
    $data = json_decode($message, true);
} else {
    \Sodium\memzero($key);
    throw new Exception("Malformed message or invalid MAC");
}

Extra Information