Add EVP_CIPHER_CTX_max_next_update and EVP_CIPHER_CTX_max_final This is intended to replace Conscrypt's current reaching into EVP_CIPHER_CTX internals to do the calculation itself. I've bumped BORINGSSL_API_VERSION to help coordinate, but I suspect this one will be easiest handled by waiting for all the copies of BoringSSL to update, since it changes the C++ <-> JNI boundary. Change-Id: Ic28c9463bc66ddd0e25945a3293803191c9e628a Reviewed-on: https://boringssl-review.googlesource.com/c/boringssl/+/96327 Auto-Submit: David Benjamin <davidben@google.com> Commit-Queue: David Benjamin <davidben@google.com> Reviewed-by: Rudolf Polzer <rpolzer@google.com>
diff --git a/crypto/cipher/cipher_test.cc b/crypto/cipher/cipher_test.cc index 665f344..57aee67 100644 --- a/crypto/cipher/cipher_test.cc +++ b/crypto/cipher/cipher_test.cc
@@ -262,6 +262,7 @@ size_t todo = chunk_size == 0 ? in.size() : std::min(in.size(), chunk_size); EXPECT_LE(todo, static_cast<size_t>(INT_MAX)); ASSERT_TRUE(MaybeCopyCipherContext(copy, &ctx)); + size_t max = EVP_CIPHER_CTX_max_next_update(ctx.get(), todo); switch (api) { case API::kCipher: // `EVP_Cipher` sometimes returns the number of bytes written, or -1 on @@ -288,6 +289,9 @@ } } ASSERT_GE(len, 0); + // `EVP_CIPHER_CTX_max_next_update` is, for all currently-implemented + // ciphers, exact. + EXPECT_EQ(static_cast<size_t>(len), max); total += static_cast<size_t>(len); in = in.subspan(todo); } @@ -314,6 +318,8 @@ } } } else { + ASSERT_TRUE(MaybeCopyCipherContext(copy, &ctx)); + size_t max = EVP_CIPHER_CTX_max_final(ctx.get()); switch (api) { case API::kCipher: if (is_custom_cipher) { @@ -336,6 +342,13 @@ } } ASSERT_GE(len, 0); + // `EVP_CIPHER_CTX_max_final` is, for currently-implemented ciphers, exact + // for all but padded decrypt. + if (op == Operation::kDecrypt && padding) { + EXPECT_LE(static_cast<size_t>(len), max); + } else { + EXPECT_EQ(static_cast<size_t>(len), max); + } total += static_cast<size_t>(len); result.resize(total); EXPECT_EQ(Bytes(expected), Bytes(result));
diff --git a/crypto/fipsmodule/cipher/cipher.cc.inc b/crypto/fipsmodule/cipher/cipher.cc.inc index d293f60..49fb979 100644 --- a/crypto/fipsmodule/cipher/cipher.cc.inc +++ b/crypto/fipsmodule/cipher/cipher.cc.inc
@@ -680,6 +680,49 @@ } } +size_t EVP_CIPHER_CTX_max_next_update(const EVP_CIPHER_CTX *ctx, + size_t in_len) { + if (in_len == 0) { + return 0; + } + + size_t block_size = ctx->cipher->block_size; + // |block_size| must be a power of 2. + assert(block_size != 0 && (block_size & (block_size - 1)) == 0); + size_t buf_len = ctx->buf_len; + + // Any buffered input is combined with |in_len|, then we round down to a + // multiple of the block size. + size_t ret = (in_len + buf_len) & ~(block_size - 1); + if (!ctx->encrypt && block_size > 1 && !(ctx->flags & EVP_CIPH_NO_PADDING)) { + if (ctx->final_used) { + // There was a buffered decrypted block. Now that it is known to not have + // padding, DecryptUpdate will output it. + ret += block_size; + } + if (block_remainder(ctx, in_len + buf_len) == 0) { + // This call ends on a block boundary. The last block will be buffered in + // |ctx->final| until it is known to have padding. + assert(ret >= block_size); + ret -= block_size; + } + } + return ret; +} + +size_t EVP_CIPHER_CTX_max_final(const EVP_CIPHER_CTX *ctx) { + size_t block_size = ctx->cipher->block_size; + if (block_size == 1 || (ctx->flags & EVP_CIPH_NO_PADDING)) { + return 0; + } + if (ctx->encrypt) { + return block_size; + } else { + // At least one byte will be removed when processing padding. + return block_size - 1; + } +} + const EVP_CIPHER *EVP_CIPHER_CTX_cipher(const EVP_CIPHER_CTX *ctx) { return ctx->cipher; }
diff --git a/include/openssl/base.h b/include/openssl/base.h index 8ded377..82d5a84 100644 --- a/include/openssl/base.h +++ b/include/openssl/base.h
@@ -73,7 +73,7 @@ // A consumer may use this symbol in the preprocessor to temporarily build // against multiple revisions of BoringSSL at the same time. It is not // recommended to do so for longer than is necessary. -#define BORINGSSL_API_VERSION 41 +#define BORINGSSL_API_VERSION 42 #if defined(BORINGSSL_SHARED_LIBRARY)
diff --git a/include/openssl/cipher.h b/include/openssl/cipher.h index 285e2e5..0b27aa8 100644 --- a/include/openssl/cipher.h +++ b/include/openssl/cipher.h
@@ -170,12 +170,13 @@ // Cipher operations. // EVP_EncryptUpdate_ex encrypts `in_len` bytes from `in` and writes up to -// `max_out` bytes of ciphertext to `out`. On success, it sets `*out_len` to +// `max_out_len` bytes of ciphertext to `out`. On success, it sets `*out_len` to // the number of output bytes and returns one. Otherwise, it returns zero. // -// If `max_out` is not large enough for the output, the function will return +// If `max_out_len` is not large enough for the output, the function will return // zero. The size of output buffer needed depends on the cipher and the number -// of bytes encrypted by `ctx` thus far. +// of bytes encrypted by `ctx` thus far. `EVP_CIPHER_CTX_max_next_update` will +// return the maximum output for this call. // // In ciphers whose block size is not 1, such as CBC, individual calls to // `EVP_EncryptUpdate_ex` may output more or less than `in_len` bytes: a single @@ -188,12 +189,13 @@ const uint8_t *in, size_t in_len); // EVP_EncryptFinal_ex2 finishes an encryption operation and writes up to -// `max_out` bytes of output to out. On success, it sets `*out_len` to the +// `max_out_len` bytes of output to out. On success, it sets `*out_len` to the // number of bytes written and returns one. Otherwise, it returns zero. // -// If `max_out` is not large enough for the output, the function will return +// If `max_out_len` is not large enough for the output, the function will return // zero. The size of output buffer needed depends on the cipher and the number -// of bytes encrypted. +// of bytes encrypted. `EVP_CIPHER_CTX_max_final` will return the maximum output +// for this call. // // If the block size is 1, there will be no final output at all; otherwise, at // most one block of ciphertext will be written to the output. @@ -207,12 +209,13 @@ size_t *out_len, size_t max_out_len); // EVP_DecryptUpdate_ex decrypts `in_len` bytes from `in` and writes up to -// `max_out` bytes of plaintext to `out`. On success, it sets `*out_len` to +// `max_out_len` bytes of plaintext to `out`. On success, it sets `*out_len` to // the number of output bytes and returns one. Otherwise, it returns zero. // -// If `max_out` is not large enough for the output, the function will return +// If `max_out_len` is not large enough for the output, the function will return // zero. The size of output buffer needed depends on the cipher and the number -// of bytes decrypted by `ctx` thus far. +// of bytes decrypted by `ctx` thus far. `EVP_CIPHER_CTX_max_next_update` will +// return the maximum output for this call. // // In ciphers whose block size is not 1, such as CBC, individual calls to // `EVP_DecryptUpdate_ex` may output more or less than `in_len` bytes: a single @@ -228,12 +231,13 @@ const uint8_t *in, size_t in_len); // EVP_DecryptFinal_ex2 finishes a decryption operation and writes up to -// `max_out` bytes of output to out. On success, it sets `*out_len` to the +// `max_out_len` bytes of output to out. On success, it sets `*out_len` to the // number of bytes written and returns one. Otherwise, it returns zero. // -// If `max_out` is not large enough for the output, the function will return +// If `max_out_len` is not large enough for the output, the function will return // zero. The size of output buffer needed depends on the cipher and the number -// of bytes decrypted. +// of bytes decrypted. `EVP_CIPHER_CTX_max_final` will return the maximum output +// for this call. // // If the block size is 1, there will be no final output at all; otherwise, at // most one block of ciphertext will be written to the output. @@ -265,6 +269,19 @@ OPENSSL_EXPORT int EVP_CipherFinal_ex2(EVP_CIPHER_CTX *ctx, uint8_t *out, size_t *out_len, size_t max_out_len); +// EVP_CIPHER_CTX_max_next_update returns the maximum number of bytes that may +// be output from `ctx` by a call to `EVP_CipherUpdate_ex` with `in_len` bytes +// of input. This takes the current state of `ctx` into account, so the output +// will change as bytes are passed in and out. +OPENSSL_EXPORT size_t EVP_CIPHER_CTX_max_next_update(const EVP_CIPHER_CTX *ctx, + size_t in_len); + +// EVP_CIPHER_CTX_max_final returns the maximum number of bytes that may +// be output from `ctx` by a call to `EVP_CipherFinal_ex2`. This takes the +// current state of `ctx` into account, so the output will change as bytes are +// passed in and out. +OPENSSL_EXPORT size_t EVP_CIPHER_CTX_max_final(const EVP_CIPHER_CTX *ctx); + // Cipher context accessors.
diff --git a/include/openssl/prefix_symbols.h b/include/openssl/prefix_symbols.h index 5adae37..5dd00f9 100644 --- a/include/openssl/prefix_symbols.h +++ b/include/openssl/prefix_symbols.h
@@ -965,6 +965,8 @@ #pragma redefine_extname EVP_CIPHER_CTX_init BORINGSSL_ADD_USER_LABEL_AND_PREFIX(EVP_CIPHER_CTX_init) #pragma redefine_extname EVP_CIPHER_CTX_iv_length BORINGSSL_ADD_USER_LABEL_AND_PREFIX(EVP_CIPHER_CTX_iv_length) #pragma redefine_extname EVP_CIPHER_CTX_key_length BORINGSSL_ADD_USER_LABEL_AND_PREFIX(EVP_CIPHER_CTX_key_length) +#pragma redefine_extname EVP_CIPHER_CTX_max_final BORINGSSL_ADD_USER_LABEL_AND_PREFIX(EVP_CIPHER_CTX_max_final) +#pragma redefine_extname EVP_CIPHER_CTX_max_next_update BORINGSSL_ADD_USER_LABEL_AND_PREFIX(EVP_CIPHER_CTX_max_next_update) #pragma redefine_extname EVP_CIPHER_CTX_mode BORINGSSL_ADD_USER_LABEL_AND_PREFIX(EVP_CIPHER_CTX_mode) #pragma redefine_extname EVP_CIPHER_CTX_new BORINGSSL_ADD_USER_LABEL_AND_PREFIX(EVP_CIPHER_CTX_new) #pragma redefine_extname EVP_CIPHER_CTX_nid BORINGSSL_ADD_USER_LABEL_AND_PREFIX(EVP_CIPHER_CTX_nid) @@ -4081,6 +4083,8 @@ #define EVP_CIPHER_CTX_init BORINGSSL_ADD_PREFIX(EVP_CIPHER_CTX_init) #define EVP_CIPHER_CTX_iv_length BORINGSSL_ADD_PREFIX(EVP_CIPHER_CTX_iv_length) #define EVP_CIPHER_CTX_key_length BORINGSSL_ADD_PREFIX(EVP_CIPHER_CTX_key_length) +#define EVP_CIPHER_CTX_max_final BORINGSSL_ADD_PREFIX(EVP_CIPHER_CTX_max_final) +#define EVP_CIPHER_CTX_max_next_update BORINGSSL_ADD_PREFIX(EVP_CIPHER_CTX_max_next_update) #define EVP_CIPHER_CTX_mode BORINGSSL_ADD_PREFIX(EVP_CIPHER_CTX_mode) #define EVP_CIPHER_CTX_new BORINGSSL_ADD_PREFIX(EVP_CIPHER_CTX_new) #define EVP_CIPHER_CTX_nid BORINGSSL_ADD_PREFIX(EVP_CIPHER_CTX_nid)