Re: Clarification on doc for EVP_DecryptUpdate

Matt Caswell <[email protected]> Thu, 2 Oct 2025 17:09:12 +0100
Newsgroups gmane.comp.encryption.openssl.user
Message-ID <CAODx15ezFiGki-1Hd3w6U05Fv8ZfXZDMpevo7ZS07OLPCx0hvA@mail.gmail.com>
Resending because I forgot to include openssl-users in my response!!

Matt

On Thu, 2 Oct 2025 at 16:51, Matt Caswell <[email protected]> wrote:

>
>
> On Wed, 1 Oct 2025 at 22:59, 'Sands, Daniel N.' via openssl-users <
> [email protected]> wrote:
>
>> The docs state this:
>>
>> EVP_DecryptInit_ex2(), EVP_DecryptInit_ex(), EVP_DecryptUpdate() and
>> EVP_DecryptFinal_ex()
>>
>>
>>
>> These functions are the corresponding decryption operations.
>> EVP_DecryptFinal() will return an error code if padding is enabled and the
>> final block is not correctly formatted. The parameters and restrictions are
>> identical to the encryption operations except that if padding is enabled
>> the decrypted data buffer out passed to EVP_DecryptUpdate() should have
>> sufficient room for (inl + cipher_block_size) bytes unless the cipher block
>> size is 1 in which case inl bytes is sufficient.
>>
>> What version of the docs are you using? That isn't what the current
> version says:
>
> "These functions are the corresponding decryption operations.
> EVP_DecryptFinal() will return an error code if padding is enabled and the
> final block is not correctly formatted. The parameters and restrictions are
> identical to the encryption operations. ctx MUST NOT be NULL."
>
> Where the relevant encryption restriction is:
>
> "For most ciphers and modes, the amount of data written can be anything
> from zero bytes to (inl + cipher_block_size - 1) bytes"
>
>
>> Why does the decrypted buffer need to be “inl + cipher block size”?  The
>> ciphertext will be padded to a multiple of cipher block size, but the
>> amount of decrypted text will be less than the amount of ciphertext given.
>>
>
> You may call `EVP_DecryptUpdate` many times in order to stream data.
> However, when working with padded data we have no idea when the last block
> is coming. So if you pass some multiple of the block size bytes of
> ciphertext into `EVP_DecryptUpdate` then the last block will be buffered
> internally. If that turns out to be the final block of the whole stream of
> data then the padding will be stripped in the `EVP_DecryptFinal` call. But
> if you call `EVP_DecryptUpdate` again then the whole buffered block will be
> decrypted and passed back.
>
> So lets say the block size is 16, padding is in use and you call
> `EVP_DecryptUpdate` and pass 16 bytes of ciphertext. You will get no output
> at this point because we don't know if you're going to pass more, or
> whether that is the final block.
> Now lets say you pass an additional 1 byte of ciphertext. At this point we
> can decrypt the previously buffered block and pass all of that back. We
> can't yet decrypt the 1 byte you just passed because it's not a complete
> block.
> So in this case `inl` is 1, but the size of the output is the entire
> previously buffered block = 16. This is also equal to inl +
> cipher_block_size - 1 = 1 + 16 - 1 = 16
>
> Matt
>
>
>> In addition, there seems to be a restriction which is NOT documented here
>> (or is it a bug?)  I’ll put together a simple example:
>>
>> uint8_t *buf; <function arg, buffer the size of the expected plaintext>
>>
>> size_t     bufsize; <function arg, expected size>
>>
>>
>>
>> uint8_t pad[16];
>>
>>
>>
>> <Read the ciphertext into buf and the final block into pad.  Do the usual
>> setup for AES-128 CBC.>
>>
>>
>>
>> int len;
>>
>> EVP_DecryptUpdate(ctx, buf, &len, buf, bufsize & ~15);
>>
>>
>>
>> int padlen;
>>
>> EVP_DecryptUpdate(ctx, pad, &padlen, pad, 16);
>>
>>
>>
>> int finallen;
>>
>> EVP_DecryptFinal_ex(ctx, <any destination buffer>, &finallen) will fail.
>> This seems to be because pad was modified by in-place decryption.
>>
>>
>>
>>
>>
>> If I instead do the following (very slight tweak here):
>>
>>
>>
>> int len;
>>
>> EVP_DecryptUpdate(ctx, buf, &len, buf, bufsize & ~15);
>>
>>
>>
>> int padlen;
>>
>> EVP_DecryptUpdate(ctx, buf + len, &padlen, pad, 16);
>>
>>
>>
>> int finallen;
>>
>> EVP_DecryptFinal_ex(ctx, <any destination buffer including pad>,
>> &finallen) will succeed.  Oddly enough, it will succeed here even if pad
>> was erased just before this call.
>>
>>
>>
>> <Assert that len + padlen + finallen equals the buffer size>
>>
>>
>>
>> memcpy(buf + len + padlen, pad, finallen); // Assumes that pad was the
>> destination for EVP_DecryptFinal_ex
>>
>>
>>
>>
>> Final question:  Is the latter sequence considered couth for reading a
>> stream into a buffer that is only the expected decrypted size, or does this
>> depend on functionality that might change?
>>
>>
>>
>> --
>> You received this message because you are subscribed to the Google Groups
>> "openssl-users" group.
>> To unsubscribe from this group and stop receiving emails from it, send an
>> email to openssl-users+unsubscribe-MCmKBN63+Bmbup2nOX2J7Q@public.gmane.org
>> To view this discussion visit
>> https://groups.google.com/a/openssl.org/d/msgid/openssl-users/SA9PR09MB53093A0DDBB4F7A3688559F3DBE6A%40SA9PR09MB5309.namprd09.prod.outlook.com
>> <https://groups.google.com/a/openssl.org/d/msgid/openssl-users/SA9PR09MB53093A0DDBB4F7A3688559F3DBE6A%40SA9PR09MB5309.namprd09.prod.outlook.com?utm_medium=email&utm_source=footer>
>> .
>>
>

-- 
You received this message because you are subscribed to the Google Groups "openssl-users" group.
To unsubscribe from this group and stop receiving emails from it, send an email to openssl-users+unsubscribe-MCmKBN63+Bmbup2nOX2J7Q@public.gmane.org
To view this discussion visit https://groups.google.com/a/openssl.org/d/msgid/openssl-users/CAODx15ezFiGki-1Hd3w6U05Fv8ZfXZDMpevo7ZS07OLPCx0hvA%40mail.gmail.com.