Difference between revisions of "EVP Authenticated Encryption and Decryption"
(Added CCM encryption) |
(Added CCM decryption) |
||
Line 218: | Line 218: | ||
return ciphertext_len; | return ciphertext_len; | ||
+ | } | ||
+ | </pre> | ||
+ | |||
+ | ==Authenticated Decryption using CCCM mode== | ||
+ | |||
+ | Encryption with CCM mode is much the same as for decryption with CCM but with some additional things to bear in mind. | ||
+ | * you can only call EVP_DecryptUpdate once for AAD and once for the plaintext. | ||
+ | * The total ciphertext length must be passed to EVP_DecryptUpdate (only needed if AAD is passed) | ||
+ | * Optionally the tag and IV length can also be passed. If they are not then the defaults are used (12 bytes for AES tags, and 7 bytes for AES IVs) | ||
+ | * The tag verify is performed when you call the final EVP_DecryptUpdate and is reflected by the return value: there is no call to EVP_DecryptFinal. | ||
+ | |||
+ | See the code below for an example: | ||
+ | <pre> | ||
+ | int decryptccm(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_ccm(), NULL, NULL, NULL)) | ||
+ | handleErrors(); | ||
+ | |||
+ | /* Setting IV len to 7. Not strictly necessary as this is the default | ||
+ | * but shown here for the purposes of this example */ | ||
+ | if(!EVP_CIPHER_CTX_ctrl(ctx, EVP_CTRL_CCM_SET_IVLEN, 7, NULL)) | ||
+ | handleErrors(); | ||
+ | |||
+ | /* Set expected tag value. */ | ||
+ | if(!EVP_CIPHER_CTX_ctrl(ctx, EVP_CTRL_CCM_SET_TAG, 14, tag)) | ||
+ | handleErrors(); | ||
+ | |||
+ | /* Initialise key and IV */ | ||
+ | if(!EVP_DecryptInit_ex(ctx, NULL, NULL, key, iv)) handleErrors(); | ||
+ | |||
+ | |||
+ | /* Provide the total ciphertext length | ||
+ | */ | ||
+ | if(!EVP_DecryptUpdate(ctx, NULL, &len, NULL, ciphertext_len)) | ||
+ | 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 | ||
+ | */ | ||
+ | ret = EVP_DecryptUpdate(ctx, plaintext, &len, ciphertext, ciphertext_len); | ||
+ | |||
+ | plaintext_len = len; | ||
+ | |||
+ | /* Clean up */ | ||
+ | EVP_CIPHER_CTX_free(ctx); | ||
+ | |||
+ | if(ret > 0) | ||
+ | { | ||
+ | /* Success */ | ||
+ | return plaintext_len; | ||
+ | } | ||
+ | else | ||
+ | { | ||
+ | /* Verify failed */ | ||
+ | return -1; | ||
+ | } | ||
} | } | ||
</pre> | </pre> |
Revision as of 20:35, 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; } }
Authenticated Encryption using CCM mode
Encryption with CCM mode is much the same as for encryption with GCM but with some additional things to bear in mind.
- you can only call EVP_EncryptUpdate once for AAD and once for the plaintext.
- The total plaintext length must be passed to EVP_EncryptUpdate (only needed if AAD is passed)
- Optionally the tag and IV length can also be passed. If they are not then the defaults are used (12 bytes for AES tags, and 7 bytes for AES IVs)
See the code below for an example:
int encryptccm(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_ccm(), NULL, NULL, NULL)) handleErrors(); /* Setting IV len to 7. Not strictly necessary as this is the default * but shown here for the purposes of this example */ if(!EVP_CIPHER_CTX_ctrl(ctx, EVP_CTRL_CCM_SET_IVLEN, 7, NULL)) handleErrors(); /* Set tag length */ EVP_CIPHER_CTX_ctrl(ctx, EVP_CTRL_CCM_SET_TAG, 14, NULL); /* Initialise key and IV */ if(!EVP_EncryptInit_ex(ctx, NULL, NULL, key, iv)) handleErrors(); /* Provide the total plaintext length */ if(!EVP_EncryptUpdate(ctx, NULL, &len, NULL, plaintext_len)) handleErrors(); /* Provide any AAD data. This can be called zero or one 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 only be called once for this */ 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 CCM mode */ if(!EVP_EncryptFinal_ex(ctx, ciphertext + len, &len)) handleErrors(); ciphertext_len += len; /* Get the tag */ if(!EVP_CIPHER_CTX_ctrl(ctx, EVP_CTRL_CCM_GET_TAG, 14, tag)) handleErrors(); /* Clean up */ EVP_CIPHER_CTX_free(ctx); return ciphertext_len; }
Authenticated Decryption using CCCM mode
Encryption with CCM mode is much the same as for decryption with CCM but with some additional things to bear in mind.
- you can only call EVP_DecryptUpdate once for AAD and once for the plaintext.
- The total ciphertext length must be passed to EVP_DecryptUpdate (only needed if AAD is passed)
- Optionally the tag and IV length can also be passed. If they are not then the defaults are used (12 bytes for AES tags, and 7 bytes for AES IVs)
- The tag verify is performed when you call the final EVP_DecryptUpdate and is reflected by the return value: there is no call to EVP_DecryptFinal.
See the code below for an example:
int decryptccm(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_ccm(), NULL, NULL, NULL)) handleErrors(); /* Setting IV len to 7. Not strictly necessary as this is the default * but shown here for the purposes of this example */ if(!EVP_CIPHER_CTX_ctrl(ctx, EVP_CTRL_CCM_SET_IVLEN, 7, NULL)) handleErrors(); /* Set expected tag value. */ if(!EVP_CIPHER_CTX_ctrl(ctx, EVP_CTRL_CCM_SET_TAG, 14, tag)) handleErrors(); /* Initialise key and IV */ if(!EVP_DecryptInit_ex(ctx, NULL, NULL, key, iv)) handleErrors(); /* Provide the total ciphertext length */ if(!EVP_DecryptUpdate(ctx, NULL, &len, NULL, ciphertext_len)) 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 */ ret = EVP_DecryptUpdate(ctx, plaintext, &len, ciphertext, ciphertext_len); plaintext_len = len; /* Clean up */ EVP_CIPHER_CTX_free(ctx); if(ret > 0) { /* Success */ return plaintext_len; } else { /* Verify failed */ return -1; } }