Difference between revisions of "EVP Authenticated Encryption and Decryption"

From OpenSSLWiki
Jump to: navigation, search
(Added link to manual)
m (Minor fix to link)
Line 9: Line 9:
 
The output from the encryption operation will be the ciphertext, and a tag. The tag is subsequently used during the decryption operation to ensure that the ciphertext and AAD have not been tampered with.
 
The output from the encryption operation will be the ciphertext, and a tag. The tag is subsequently used during the decryption operation to ensure that the ciphertext and AAD have not been tampered with.
  
The OpenSSL manual describes the usage of the GCM and CCM modes [http://www.openssl.org/docs/crypto/EVP_EncryptInit.html#GCM_Mode|here].
+
The OpenSSL manual describes the usage of the GCM and CCM modes [here|http://www.openssl.org/docs/crypto/EVP_EncryptInit.html#GCM_Mode].
  
 
==Authenticated Encryption using GCM mode==
 
==Authenticated Encryption using GCM mode==

Revision as of 13:04, 5 March 2013

The EVP interface supports the ability to perform authenticated encryption and decryption. As for standard symmetric encryption you will need to know the following:

  • Your algorithm (currently only AES is supported)
  • Your mode (currently only GCM and CCM are supported)
  • Your key
  • Your Initialisation Vector (IV)

In addition you can (optionally) provide some "Additional Authenticated Data" (AAD). The AAD data is not encrypted, and is typically passed to the recipient in plaintext along with the ciphertext.

The output from the encryption operation will be the ciphertext, and a tag. The tag is subsequently used during the decryption operation to ensure that the ciphertext and AAD have not been tampered with.

The OpenSSL manual describes the usage of the GCM and CCM modes [here|http://www.openssl.org/docs/crypto/EVP_EncryptInit.html#GCM_Mode].

Authenticated Encryption using GCM mode

Encryption is performed in much the same way as for symmetric encryption as described here. The main differences are:

  • You may optionally pass through an IV length using EVP_CIPHER_CTX_ctrl
  • AAD data is passed through in zero or more calls to EVP_EncryptUpdate, with the output buffer set to NULL
  • After the EVP_EncryptFinal_ex call a new call to EVP_CIPHER_CTX_ctrl retrieves the tag

See the code below for an example:

int encrypt(unsigned char *plaintext, int plaintext_len, unsigned char *aad,
	int aad_len, unsigned char *key, unsigned char *iv,
	unsigned char *ciphertext, unsigned char *tag)
{
	EVP_CIPHER_CTX *ctx;

	int len;

	int ciphertext_len;


	/* Create and initialise the context */
	if(!(ctx = EVP_CIPHER_CTX_new())) handleErrors();

	/* Initialise the encryption operation. */
	if(!EVP_EncryptInit_ex(ctx, EVP_aes_256_gcm(), NULL, NULL, NULL))
		handleErrors();

	/* Set IV length if default 12 bytes (96 bits) is not appropriate */
	if(!EVP_CIPHER_CTX_ctrl(ctx, EVP_CTRL_GCM_SET_IVLEN, 16, NULL))
		handleErrors();

	/* Initialise key and IV */
	if(!EVP_EncryptInit_ex(ctx, NULL, NULL, key, iv)) handleErrors();

	/* Provide any AAD data. This can be called zero or more times as
	 * required
	 */
	if(!EVP_EncryptUpdate(ctx, NULL, &len, aad, aad_len))
		handleErrors();

	/* Provide the message to be encrypted, and obtain the encrypted output.
	 * EVP_EncryptUpdate can be called multiple times if necessary
	 */
	if(!EVP_EncryptUpdate(ctx, ciphertext, &len, plaintext, plaintext_len))
		handleErrors();
	ciphertext_len = len;

	/* Finalise the encryption. Normally ciphertext bytes may be written at
	 * this stage, but this does not occur in GCM mode
	 */
	if(!EVP_EncryptFinal_ex(ctx, ciphertext + len, &len)) handleErrors();
	ciphertext_len += len;

	/* Get the tag */
	if(!EVP_CIPHER_CTX_ctrl(ctx, EVP_CTRL_GCM_GET_TAG, 16, tag))
		handleErrors();

	/* Clean up */
	EVP_CIPHER_CTX_free(ctx);

	return ciphertext_len;
}

Authenticated Decryption using GCM mode

Again, the decryption operation is much the same as for normal symmetric decryption as described here. The main differences are:

  • You may optionally pass through an IV length using EVP_CIPHER_CTX_ctrl
  • AAD data is passed through in zero or more calls to EVP_DecryptUpdate, with the output buffer set to NULL
  • Prior to the EVP_DecryptFinal_ex call a new call to EVP_CIPHER_CTX_ctrl provides the tag
  • A non positive return value from EVP_DecryptFinal_ex should be considered as a failure to authenticate ciphertext and/or AAD. It does not necessarily indicate a more serious error.

See the code example below:

int decrypt(unsigned char *ciphertext, int ciphertext_len, unsigned char *aad,
	int aad_len, unsigned char *tag, unsigned char *key, unsigned char *iv,
	unsigned char *plaintext)
{
	EVP_CIPHER_CTX *ctx;
	int len;
	int plaintext_len;
	int ret;

	/* Create and initialise the context */
	if(!(ctx = EVP_CIPHER_CTX_new())) handleErrors();

	/* Initialise the decryption operation. */
	if(!EVP_DecryptInit_ex(ctx, EVP_aes_256_gcm(), NULL, NULL, NULL))
		handleErrors();

	/* Set IV length. Not necessary if this is 12 bytes (96 bits) */
	if(!EVP_CIPHER_CTX_ctrl(ctx, EVP_CTRL_GCM_SET_IVLEN, 16, NULL))
		handleErrors();

	/* Initialise key and IV */
	if(!EVP_DecryptInit_ex(ctx, NULL, NULL, key, iv)) handleErrors();

	/* Provide any AAD data. This can be called zero or more times as
	 * required
	 */
	if(!EVP_DecryptUpdate(ctx, NULL, &len, aad, aad_len))
		handleErrors();

	/* Provide the message to be decrypted, and obtain the plaintext output.
	 * EVP_DecryptUpdate can be called multiple times if necessary
	 */
	if(!EVP_DecryptUpdate(ctx, plaintext, &len, ciphertext, ciphertext_len))
		handleErrors();
	plaintext_len = len;

	/* Set expected tag value. Works in OpenSSL 1.0.1d and later */
	if(!EVP_CIPHER_CTX_ctrl(ctx, EVP_CTRL_GCM_SET_TAG, 16, tag))
		handleErrors();

	/* Finalise the decryption. A positive return value indicates success,
	 * anything else is a failure - the plaintext is not trustworthy.
	 */
	ret = EVP_DecryptFinal_ex(ctx, plaintext + len, &len);

	/* Clean up */
	EVP_CIPHER_CTX_free(ctx);

	if(ret > 0)
	{
		/* Success */
		plaintext_len += len;
		return plaintext_len;
	}
	else
	{
		/* Verify failed */
		return -1;
	}
}