[openssl-users] OpenSSL AES encryption using AES_* functions and EVP_* functions

Dave Thompson dthompson at prinpay.com
Wed Dec 31 19:45:08 UTC 2014


> From: openssl-users On Behalf Of Purushotham Nayak
> Sent: Wednesday, December 31, 2014 12:22

> I have some data that was encrypted using the openssl (`AES_*`) functions. 
> I want update this code to use the newer (EVP_*) functions which are 
> FIPS compliant. But I should be able to decrypt data that was encrypted 
> using the old code.

> I've pasted below both the old and the new code. The encrypted/decrypted 
> contents are different. i.e. I can't use them interchangeably. This means 
> I can't upgrade the code without having to decrypt using the old code 
> and then re-encrypt.

> Are there any values for the parameters to EVP_BytesToKey so that 
> aes_key derived is the same in both cases. Or is there any other way 
> to accomplish the same using the (EVP_*) functions? I've tried <snip>

What on earth makes you think EVP_BytesToKey is appropriate? 
EVP_BytesToKey is used for *password-based* encryption, where 
the key (and sometimes IV) is derived in a complicated way from 
a character string intended to be remembered by humans. 
(Even for that it is outdated and PKCS5_PBKDF2* is better.)

If you have an actual key, as you did in your old code, you simply pass 
that same actual key to EVP_EncryptInit as the actual key.

> What algorithm is being used in AES_set_encrypt/decrypt_key function?

The key scheduling defined in FIPS 197 (and before that Rijndael).
And so is EVP_EncryptInit* when used on an AES cipher.

> The code using the `AES_*` functions
<snip>
>    int main()
>    {
>        unsigned char p_text[]="plain text";
>        unsigned char c_text[16];
<snip set_encrypt_key>
>      AES_encrypt(p_text, c_text, &aes_key);
    
This old code allocates a buffer of 11 bytes to contain the plaintext and passes 
it to AES_encrypt which reads 16 bytes (always). The remaining 5 bytes are 
undefined by the C standard -- in fact the *behavior* is undefined by standard, 
although in practice nearly all implementations will read *some* values. 
*What* values they read may depend on your compiler, version, options, 
computer, operating system, and the phase of the moon, which means you 
probably can't duplicate it in the new code. You can still decrypt old values 
with the fixes described below, but you can't faithfully recreate them.

> The code using the `EVP_*` functions. (Encryption code is below and the decryption code is similar).
<snip>
>    int main()
>    {
>        EVP_CIPHER_CTX *ctx = (EVP_CIPHER_CTX*)malloc(sizeof(EVP_CIPHER_CTX));
>        EVP_CIPHER_CTX_init(ctx);

EVP_CIPHER_CTX_new() is simpler.
  
>        const EVP_CIPHER *cipher = EVP_aes_128_ecb(); // key size 128, mode ecb (not FIPS compliant?)

FIPS140 covers only the algorithm not the mode, so all modes are equally "compliant".
However ECB is frequently insecure and a Very Bad Idea if used for anything other 
than random data. See Wikipedia, or practically any good crypto reference.

<snip EVP_BytesToKey and related: just remove that>
    
>        EVP_EncryptInit(ctx, cipher, aes_key, aes_iv);

Pass the actual key, of the correct size. ECB does not use an IV, so you can pass NULL 
or any value of the correct size which will be ignored. Not using an IV is one of the 
(several) things that makes ECB frequently insecure, see above.
    
EVP will by default add and remove PKCS5 (or more exactly PKCS7) padding. 
To avoid that, and particularly to decrypt values from your old code which used 
some garbage data (see above) rather than valid padding, 
    EVP_CIPHER_CTX_set_padding (ctx, 0);

>        unsigned char p_text[]="plain text"; int p_len = sizeof(p_text);
>        unsigned char c_text[16]; int c_len = 16;
>        int t_len;
>        EVP_EncryptUpdate(ctx, c_text, &c_len, p_text, p_len);
>        EVP_EncryptFinal(ctx, (c_text + c_len), &t_len);
>        c_len += t_len;
 
Happy New Year.




More information about the openssl-users mailing list