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.