AES and EVP_CIPHER question

Philip Prindeville philipp_subx at redfish-solutions.com
Fri May 13 17:18:53 UTC 2022



> On May 13, 2022, at 10:55 AM, Philip Prindeville <philipp_subx at redfish-solutions.com> wrote:
> 
> 
> 
>> On May 13, 2022, at 10:34 AM, Matt Caswell <matt at openssl.org> wrote:
>> 
>> 
>> 
>> On 13/05/2022 16:49, Philip Prindeville wrote:
>>> Hi,
>>> I'm trying to rewrite some legacy AES_* code to use EVP_CIPHER_* so it's forward compatible into 3.x.
>>> My code, in a nutshell, looks like:
>>> static int evp_cipher_aes_decrypt(const unsigned char *in, unsigned char *out, unsigned inlen, const ast_aes_decrypt_key *key)
>>> {
>>>        EVP_CIPHER_CTX *ctx;
>>>        int res, outlen, finallen;
>>>        unsigned char final[AST_CRYPTO_AES_BLOCKSIZE / 8];
>>>        if ((ctx = EVP_CIPHER_CTX_new()) == NULL) {
>>>                return -1;
>>>        }
>>>        EVP_CIPHER_CTX_set_padding(ctx, 0);
>>>        do {
>>>                if ((res = EVP_CipherInit(ctx, EVP_aes_128_ecb(), key->raw, NULL, 0)) <= 0) {
>>>                        break;
>>>                }
>>>                if ((res = EVP_CipherUpdate(ctx, out, &outlen, in, inlen)) <= 0) {
>>>                        break;
>>>                }
>>>                /* for ECB, this is a no-op */
>>>                if ((res = EVP_CipherFinal(ctx, final, &finallen)) <= 0) {
>>>                        break;
>>>                }
>>>                res = outlen;
>>>        } while (0);
>>>        EVP_CIPHER_CTX_free(ctx);
>>>        return res;
>>> }
>>> It's ECB, so there's no IV.  Or padding.  The block size and key size are both 128 bits.
>>> One thing I noticed right away is that EVP_CipherUpdate() returns 1, and sees "outlen" to zero.
>> 
>> What value does inlen have? If you're not doing padding then it must be a multiple of the block size.
> 
> 
> The length is 16, which matches the key size (and the AES-128-ECB block size).


One other bit of debugging information: I thought, "Maybe the EVP_CipherFinal() isn't necessary since we're not using padding or Additional Data, etc." but I single-stepped through the code, and noticed this:

* after the call to EVP_CipherUpdate() the "out" buffer is still unchanged;
* after the call to EVP_CipherFinal() the "out" buffer gets the plaintext deposited into it, even though it returns zero;

So... that's curious.  Is it a bug?  The man page says "EVP_CipherFinal_ex() returns 0 for a decryption failure or 1 for success." (Doesn't describe return values for EVP_CipherFinal() but imagine it's analogous).

Clearly the decryption *is* succeeding, but the wrong return value is coming back.

My test vector is:

        const unsigned char key[16] = {
		0x01, 0x23, 0x45, 0x67, 0x89, 0x01, 0x23, 0x45,
		0x67, 0x89, 0x01, 0x23, 0x45, 0x67, 0x89, 0x01
	};
        const unsigned char plaintext[16] = "Mary had a littl";
        const unsigned char crypttext[16] = {
                0xad, 0xc2, 0xcd, 0x9e, 0x6e, 0x8a, 0xda, 0x0c,
                0xe7, 0x71, 0xc8, 0x75, 0x52, 0xf9, 0x7d, 0xd5
        };

If that helps.

-Philip


> 
> 
>> 
>> Matt
>> 
>> 
>>> And then EVP_CipherFinal() returns 0, and sets "finallen" to zero.
>>> What's wrong with this code?
>>> I'm trying to write "naive" code that counts on the primitives to indicate how much resultant output is generated for the input I've given (yes, I know that it's 1:1 in the case of ECB, but I shouldn't have to hard-code that in case I want to use the same code with multiple block modes).
>>> The function is supposed to return <= 0 on error, otherwise the number of bytes decrypted into "out" on success.
>>> Thanks,
>>> -Philip
> 



More information about the openssl-users mailing list