Library

Caution! Do Not Mistake Encryption for Authentication

Have you ever had to answer the question: “What is encryption?” and you’ve easily explained it with the word “authentication”? It would be a grave sin to do so! Encryption is not the same as authentication, and it would be a design error for developers to assume otherwise.

So, what is encryption? Encryption entails making a message unreadable except with the use of the right key; authentication is a means of making a message tamper-proof and ascertaining it is from the expected source. According to the CIA triad, encryption provides confidentiality; authentication provides integrity.

An encrypted message may still be tampered, resulting in garbage. So encryption does not guarantee integrity. In other words, encryption alone is not enough to ward off third parties from sending malicious encrypted messages. On the other hand, one could make plaintext messages tamper-proof; meaning that authentication does not imply confidentiality.

Encryption

An encrypted message can only be read with the right key. In symmetric cryptography, the same key used for encryption is also used for decryption. For asymmetric cryptography, one can encrypt the message using a user’s “public key”, but it would require their “private key” to read it. As mentioned earlier, an encrypted message can be tampered, thereby passing on garbage to the receiver. In some cases, this fact can be used to bypass security.

For example, if you pass a 32-byte string for& key, through a code which provides AES encryption in Cipher-Block-Chaining mode, you can trick others into believing you provide 256-bit AES encryption for your cookies and that it is secure.

Now see how an unauthenticated encryption is

Imagine receiving a session cookie like the one below:

kHv9PAlStPZaZJHIYXzyCnuAhWdRRK7H0cNVUCwzCZ4M8fxH79xIIIbznxmiOxGQ7td8LwTzHFgwBmbqWuB+sQ==.

We could change a byte in the first block and keep sending the new cookie until we observe a change. After about 2405 requests, our string now looks like this:

kHv9PAlStPZaZZHIYXzyCnuAhWdRRK7H0cNVUCwzCZ4M8fxH79xIIIbznxmiOxGQ7td8LwTzHFgwBmbqWuB+sQ==

You can notice that only one character makes the difference from the cookie encoded in base 64: (kHv9PAlStPZaZJ vs kHv9PAlStPZaZZ):

- kHv9PAlStPZaZJHIYXzyCnuAhWdRRK7H0cNVUCwzCZ4M8fxH79xIIIbznxmiOxGQ7td8LwTzHFgwBmbqWuB+sQ==
+ kHv9PAlStPZaZZHIYXzyCnuAhWdRRK7H0cNVUCwzCZ4M8fxH79xIIIbznxmiOxGQ7td8LwTzHFgwBmbqWuB+sQ==

The initial date stored in the cookie appeared in this form:

array(2) {
["admin"]=>
int(0)
["user"]=>
"aaaaaaaaaaaaa"
}

But a single alteration of a byte in the first block enables us to change our message to become

array(2) {
["admin"]=>
int(1)
["user"]=>
"aaaaaaaaaaaaa"
}

Now, you see that we may change one bit and become an administrator, even though the cookies are encrypted.

Authentication

We’ve answered the question, what is encryption. On the other hand, authentication helps us to give integrity to our message and ensure it comes from the expected source. We can accomplish this by calculating a keyed-Hash Message Authentication Code (HMAC) for our message and link it with the message:

function hmac_sign($message, $key)
{
return hash_hmac('sha256', $message, $key) . $message;
}
function hmac_verify($bundle, $key)
{
$msgMAC = mb_substr($bundle, 0, 64, '8bit');
$message = mb_substr($bundle,  64, null, '8bit');
return hash_equals(
hash_hmac('sha256', $message, $key),
$msgMAC
);
}

The use of HMAC or any other appropriate cryptographic tool is recommended. Do not go for a mere hash function.

function unsafe_hash_sign($message, $key)
{
return md5($key.$message) . $message;
}
function unsafe_hash_verify($bundle, $key)
{
$msgHash = mb_substr($bundle, 0, 64, '8bit');
$message = mb_substr($bundle, 64, null, '8bit');
return md5($key.$message) == $msgHash;
}

The prefix “unsafe” indicates that the function is vulnerable to timing attacks, chosen prefix attacks on MD5, and non-strict equality operator bugs (mostly to PHP).

If using a simple hash with a key to authenticate our message is bad, using a hash without a key is catastrophic.  An attacker can easily calculate a non-keyed hash or a checksum of a counterfeit message. Using a message authentication code (MAC) would demand the authentication key from the attacker. So with MAC, you are assured integrity for your message.

Providing Authenticated Encryption

The only way to be doubly sure you are safe from bit-writing attacks is to authenticate your message after you have provided encryption. You also have to verify before decryption. Now let’s go back to our cookie and provide it some security:

function setLessUnsafeCookie($name, $cookieData, $eKey, $aKey)
{
$iv = mcrypt_create_iv(16, MCRYPT_DEV_URANDOM);
$ciphertext = mcrypt_encrypt(
MCRYPT_RIJNDAEL_128,
$eKey,
json_encode($cookieData),
MCRYPT_MODE_CBC,
$iv
);
// Note: We cover the IV in our HMAC
$hmac = hash_hmac('sha256', $iv.$ciphertext, $aKey, true);
return setcookie(
$name,
base64_encode(
$hmac.$iv.$ciphertext
)
);
}
function getLessUnsafeCookie($name, $eKey, $aKey)
{
if (!isset($_COOKIE[$name])) {
return null;
}
$decoded = base64_decode($_COOKIE[$name]);
$hmac = mb_substr($decoded, 0, 32, '8bit');
$iv = mb_substr($decoded, 32, 16, '8bit');
$ciphertext = mb_substr($decoded, 48, null, '8bit');
$calculated = hash_hmac('sha256', $iv.$ciphertext, $aKey, true);
if (hash_equals($hmac, $calculated)) {
$decrypted = rtrim(
mcrypt_decrypt(
MCRYPT_RIJNDAEL_128,
$eKey,
$ciphertext,
MCRYPT_MODE_CBC,
$iv
),
"\0"
);
return json_decode($decrypted, true);
}
}

In this example, the key used for encryption is different from that used in authentication.

But to achieve a vigorous symmetric authenticated encryption, we strongly recommend the use of an existing library. It does not only provide encryption with ease, but is a much better alternative than writing your own encryption. If you are a PHP developer, defuse/php-encryption or libsodium are your options. Below, is an encrypted cookie with libsodium:

/*
// At some point, we run this command:
$key = Sodium::randombytes_buf(Sodium::CRYPTO_AEAD_CHACHA20POLY1305_KEYBYTES);
*/
/**
* Store ciphertext in a cookie
*
* @param string $name - cookie name
* @param mixed $cookieData - cookie data
* @param string $key - crypto key
*/
function setSafeCookie($name, $cookieData, $key)
{
$nonce = Sodium::randombytes_buf(Sodium::CRYPTO_SECRETBOX_NONCEBYTES);
return setcookie(
$name,
base64_encode(
$nonce.
Sodium::crypto_secretbox(
json_encode($cookieData),
$nonce,
$key
)
)
);
}
/**
* Decrypt a cookie, expand to array
*
* @param string $name - cookie name
* @param string $key - crypto key
*/
function getSafeCookie($name, $key)
{
$hexSize = 2 * Sodium::Sodium::CRYPTO_SECRETBOX_NONCEBYTES;
if (!isset($_COOKIE[$name])) {
return array();
}
$decoded = base64_decode($_COOKIE[$name]);
$nonce = mb_substr($decoded, 0, $hexSize, '8bit');
$ciphertext = mb_substr($decoded, $hexSize, null, '8bit');
$decrypted = Sodium::crypto_secretbox_open(
$ciphertext,
$nonce,
$key
);
if (empty($decrypted)) {
return array();
}
return json_decode($decrypted, true);
}

AEAD, the In Thing

We have so far treated encryption and authentication as separate operations, focusing on AES in Cipher Block-Chaining mode. More flexible modes have been developed to handle encryption and authentication in the same operation. We refer to these modes as AEAD (Authenticated Encryption with Associated Data). AES-GCM and ChaCha20-Poly1305 are two dependable modes of AEAD.

DON’T GET IT TWISTED

Use encryption properly. It is not the same as authentication. With encryption, you are assured confidentiality; with authentication, you are assured integrity. You need both encryption and authentication to be doubly sure of your message privacy.

Leave a Comment