Add API functions for OpenSSL ciphers with sized output. These take an argument |out_size| for the actually allocated size of the output buffer, and will fail if the size is insufficient to store the encrypted/decrypted data. Bug: 42290361 Change-Id: Id662e4274f01e0ea95ce5a944943bd4258fe8f06 Reviewed-on: https://boringssl-review.googlesource.com/c/boringssl/+/83007 Reviewed-by: David Benjamin <davidben@google.com> Auto-Submit: Rudolf Polzer <rpolzer@google.com> Commit-Queue: David Benjamin <davidben@google.com>
diff --git a/crypto/cipher/cipher_test.cc b/crypto/cipher/cipher_test.cc index e21a52d..a2c3dbd 100644 --- a/crypto/cipher/cipher_test.cc +++ b/crypto/cipher/cipher_test.cc
@@ -13,6 +13,7 @@ // limitations under the License. #include <limits.h> +#include <stdint.h> #include <stdlib.h> #include <string.h> @@ -116,6 +117,27 @@ abort(); } +enum class API { + // kCipher tests the EVP_Cipher() API. Arcane! + kCipher, + // kUnsized tests the EVP_CipherUpdate_ex() API. No size checking! + kUnsized, + // kSized tests the EVP_CipherUpdate_Ex2() API. With size checking! + kSized, +}; + +static const char *APIToString(API api) { + switch (api) { + case API::kCipher: + return "Cipher"; + case API::kUnsized: + return "Unsized"; + case API::kSized: + return "Sized"; + } + abort(); +} + // MaybeCopyCipherContext, if |copy| is true, replaces |*ctx| with a, hopefully // equivalent, copy of it. static bool MaybeCopyCipherContext(bool copy, @@ -132,13 +154,15 @@ } static void TestCipherAPI(const EVP_CIPHER *cipher, Operation op, bool padding, - bool copy, bool in_place, bool use_evp_cipher, - size_t chunk_size, bssl::Span<const uint8_t> key, + bool copy, bool in_place, API api, size_t chunk_size, + bssl::Span<const uint8_t> key, bssl::Span<const uint8_t> iv, bssl::Span<const uint8_t> plaintext, bssl::Span<const uint8_t> ciphertext, bssl::Span<const uint8_t> aad, bssl::Span<const uint8_t> tag) { + SCOPED_TRACE(APIToString(api)); + bool encrypt = op == Operation::kEncrypt; bool is_custom_cipher = EVP_CIPHER_flags(cipher) & EVP_CIPH_FLAG_CUSTOM_CIPHER; @@ -193,18 +217,27 @@ while (!aad.empty()) { size_t todo = chunk_size == 0 ? aad.size() : std::min(aad.size(), chunk_size); - if (use_evp_cipher) { - // AEADs always use the "custom cipher" return value convention. Passing a - // null output pointer triggers the AAD logic. - ASSERT_TRUE(is_custom_cipher); - ASSERT_EQ(static_cast<int>(todo), - EVP_Cipher(ctx.get(), nullptr, aad.data(), todo)); - } else { - int len; - ASSERT_TRUE(EVP_CipherUpdate(ctx.get(), nullptr, &len, aad.data(), todo)); - // Although it doesn't output anything, |EVP_CipherUpdate| should claim to - // output the input length. - EXPECT_EQ(len, static_cast<int>(todo)); + switch (api) { + case API::kCipher: + // AEADs always use the "custom cipher" return value convention. Passing + // a null output pointer triggers the AAD logic. + ASSERT_TRUE(is_custom_cipher); + ASSERT_EQ(static_cast<int>(todo), + EVP_Cipher(ctx.get(), nullptr, aad.data(), todo)); + break; + case API::kUnsized: { + int len; + ASSERT_TRUE( + EVP_CipherUpdate(ctx.get(), nullptr, &len, aad.data(), todo)); + // Although it doesn't output anything, |EVP_CipherUpdate| should claim + // to output the input length. + EXPECT_EQ(len, static_cast<int>(todo)); + break; + } + case API::kSized: { + ASSERT_TRUE(EVP_CipherUpdateAAD(ctx.get(), aad.data(), todo)); + break; + } } aad = aad.subspan(todo); } @@ -229,46 +262,78 @@ 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)); - if (use_evp_cipher) { - // |EVP_Cipher| sometimes returns the number of bytes written, or -1 on - // error, and sometimes 1 or 0, implicitly writing |in_len| bytes. - if (is_custom_cipher) { - len = EVP_Cipher(ctx.get(), result.data() + total, in.data(), todo); - } else { - ASSERT_EQ( - 1, EVP_Cipher(ctx.get(), result.data() + total, in.data(), todo)); - len = static_cast<int>(todo); + switch (api) { + case API::kCipher: + // |EVP_Cipher| sometimes returns the number of bytes written, or -1 on + // error, and sometimes 1 or 0, implicitly writing |in_len| bytes. + if (is_custom_cipher) { + len = EVP_Cipher(ctx.get(), result.data() + total, in.data(), todo); + } else { + ASSERT_EQ( + 1, EVP_Cipher(ctx.get(), result.data() + total, in.data(), todo)); + len = static_cast<int>(todo); + } + break; + case API::kUnsized: + ASSERT_TRUE(EVP_CipherUpdate(ctx.get(), result.data() + total, &len, + in.data(), static_cast<int>(todo))); + break; + case API::kSized: { + size_t len_sz; + ASSERT_TRUE(EVP_CipherUpdate_ex(ctx.get(), result.data() + total, + &len_sz, result.size() - total, + in.data(), static_cast<int>(todo))); + len = static_cast<int>(len_sz); + break; } - } else { - ASSERT_TRUE(EVP_CipherUpdate(ctx.get(), result.data() + total, &len, - in.data(), static_cast<int>(todo))); } ASSERT_GE(len, 0); total += static_cast<size_t>(len); in = in.subspan(todo); } if (op == Operation::kInvalidDecrypt) { - if (use_evp_cipher) { - // Only the "custom cipher" return value convention can report failures. - // Passing all nulls should act like |EVP_CipherFinal_ex|. - ASSERT_TRUE(is_custom_cipher); - EXPECT_EQ(-1, EVP_Cipher(ctx.get(), nullptr, nullptr, 0)); - } else { - // Invalid padding and invalid tags all appear as a failed - // |EVP_CipherFinal_ex|. - EXPECT_FALSE(EVP_CipherFinal_ex(ctx.get(), result.data() + total, &len)); + switch (api) { + case API::kCipher: + // Only the "custom cipher" return value convention can report failures. + // Passing all nulls should act like |EVP_CipherFinal_ex|. + ASSERT_TRUE(is_custom_cipher); + EXPECT_EQ(-1, EVP_Cipher(ctx.get(), nullptr, nullptr, 0)); + break; + case API::kSized: + // Invalid padding and invalid tags all appear as a failed + // |EVP_CipherFinal_ex|. + EXPECT_FALSE( + EVP_CipherFinal_ex(ctx.get(), result.data() + total, &len)); + break; + case API::kUnsized: { + size_t len_sz; + EXPECT_FALSE(EVP_CipherFinal_ex2(ctx.get(), result.data() + total, + &len_sz, result.size() - total)); + len = static_cast<int>(len_sz); + break; + } } } else { - if (use_evp_cipher) { - if (is_custom_cipher) { - // Only the "custom cipher" convention has an |EVP_CipherFinal_ex| - // equivalent. - len = EVP_Cipher(ctx.get(), nullptr, nullptr, 0); - } else { - len = 0; + switch (api) { + case API::kCipher: + if (is_custom_cipher) { + // Only the "custom cipher" convention has an |EVP_CipherFinal_ex| + // equivalent. + len = EVP_Cipher(ctx.get(), nullptr, nullptr, 0); + } else { + len = 0; + } + break; + case API::kUnsized: + ASSERT_TRUE(EVP_CipherFinal_ex(ctx.get(), result.data() + total, &len)); + break; + case API::kSized: { + size_t len_sz; + ASSERT_TRUE(EVP_CipherFinal_ex2(ctx.get(), result.data() + total, + &len_sz, result.size() - total)); + len = static_cast<int>(len_sz); + break; } - } else { - ASSERT_TRUE(EVP_CipherFinal_ex(ctx.get(), result.data() + total, &len)); } ASSERT_GE(len, 0); total += static_cast<size_t>(len); @@ -374,6 +439,164 @@ } } +enum class SizedAPIFailCase { kNever, kUpdate1, kUpdate2, kFinal }; + +static const char *SizedAPIFailCaseToString(SizedAPIFailCase fail_case) { + switch (fail_case) { + case SizedAPIFailCase::kNever: + return "Never"; + case SizedAPIFailCase::kUpdate1: + return "Update1"; + case SizedAPIFailCase::kUpdate2: + return "Update2"; + case SizedAPIFailCase::kFinal: + return "Final"; + } + abort(); +} + +static size_t RoundDown(size_t n, size_t block_size) { + return n / block_size * block_size; +} + +static void TestSizedAPIRangeChecks(const EVP_CIPHER *cipher, Operation op, + bool padding, bssl::Span<const uint8_t> key, + bssl::Span<const uint8_t> iv, + bssl::Span<const uint8_t> plaintext, + bssl::Span<const uint8_t> ciphertext, + bssl::Span<const uint8_t> aad, + bssl::Span<const uint8_t> tag) { + bool encrypt = op == Operation::kEncrypt; + bssl::Span<const uint8_t> in = encrypt ? plaintext : ciphertext; + bssl::Span<const uint8_t> expected = encrypt ? ciphertext : plaintext; + bool is_aead = EVP_CIPHER_mode(cipher) == EVP_CIPH_GCM_MODE; + + // Some |EVP_CIPHER|s take a variable-length key, and need to first be + // configured with the key length, which requires configuring the cipher. + bssl::UniquePtr<EVP_CIPHER_CTX> ctx(EVP_CIPHER_CTX_new()); + ASSERT_TRUE(ctx); + ASSERT_TRUE(EVP_CipherInit_ex(ctx.get(), cipher, /*engine=*/nullptr, + /*key=*/nullptr, /*iv=*/nullptr, + encrypt ? 1 : 0)); + ASSERT_TRUE(EVP_CIPHER_CTX_set_key_length(ctx.get(), key.size())); + if (!padding) { + ASSERT_TRUE(EVP_CIPHER_CTX_set_padding(ctx.get(), 0)); + } + + // Configure the key. + ASSERT_TRUE(EVP_CipherInit_ex(ctx.get(), /*cipher=*/nullptr, + /*engine=*/nullptr, key.data(), + /*iv=*/nullptr, + /*enc=*/-1)); + + // Configure the IV to run the actual operation. Callers that wish to use + // a key for multiple, potentially concurrent, operations will likely copy + // at this point. The |EVP_CIPHER_CTX| API uses the same type to represent + // a pre-computed key schedule and a streaming operation. + if (is_aead) { + ASSERT_LE(iv.size(), size_t{INT_MAX}); + ASSERT_TRUE(EVP_CIPHER_CTX_ctrl(ctx.get(), EVP_CTRL_AEAD_SET_IVLEN, + static_cast<int>(iv.size()), nullptr)); + ASSERT_EQ(EVP_CIPHER_CTX_iv_length(ctx.get()), iv.size()); + } else { + ASSERT_EQ(iv.size(), EVP_CIPHER_CTX_iv_length(ctx.get())); + } + ASSERT_TRUE(EVP_CipherInit_ex(ctx.get(), /*cipher=*/nullptr, + /*engine=*/nullptr, + /*key=*/nullptr, iv.data(), /*enc=*/-1)); + + if (is_aead) { + if (!encrypt) { + ASSERT_TRUE(EVP_CIPHER_CTX_ctrl(ctx.get(), EVP_CTRL_AEAD_SET_TAG, + tag.size(), + const_cast<uint8_t *>(tag.data()))); + } + ASSERT_TRUE(EVP_CipherUpdateAAD(ctx.get(), aad.data(), aad.size())); + } + + size_t block_size = EVP_CIPHER_block_size(cipher); + for (size_t len_to_split = 0; len_to_split <= in.size(); ++len_to_split) { + SCOPED_TRACE(len_to_split); + for (SizedAPIFailCase fail_case : + {SizedAPIFailCase::kNever, SizedAPIFailCase::kUpdate1, + SizedAPIFailCase::kUpdate2, SizedAPIFailCase::kFinal}) { + SCOPED_TRACE(SizedAPIFailCaseToString(fail_case)); + + bssl::UniquePtr<EVP_CIPHER_CTX> thisctx(EVP_CIPHER_CTX_new()); + ASSERT_TRUE(thisctx); + ASSERT_TRUE(EVP_CIPHER_CTX_copy(thisctx.get(), ctx.get())); + + size_t update_size = RoundDown( + (padding && !encrypt) + // When decrypting, the final block will not be output right away. + ? std::max(in.size(), size_t{1}) - 1 + : in.size(), + block_size); + bssl::Span<const uint8_t> in1 = in.first(len_to_split); + size_t update1_size = RoundDown( + (padding && !encrypt) + // When decrypting, the final block will not be output right away. + ? std::max(in1.size(), size_t{1}) - 1 + : in1.size(), + block_size); + size_t update2_size = update_size - update1_size; + bssl::Span<const uint8_t> in2 = in.subspan(len_to_split); + size_t final_size = expected.size() - update1_size - update2_size; + + // Now, finally, the cipher is ready to be abused. + size_t len; + if (fail_case == SizedAPIFailCase::kUpdate1 && update1_size > 0) { + std::vector<uint8_t> buf(update1_size - 1); + EXPECT_FALSE(EVP_CipherUpdate_ex(thisctx.get(), buf.data(), &len, + buf.size(), in1.data(), in1.size())); + EXPECT_EQ(len, size_t{0}); + continue; + } else { + std::vector<uint8_t> buf(update1_size); + EXPECT_TRUE(EVP_CipherUpdate_ex(thisctx.get(), buf.data(), &len, + buf.size(), in1.data(), in1.size())); + EXPECT_EQ(len, update1_size); + EXPECT_EQ(Bytes(expected.first(update1_size)), Bytes(buf)); + } + if (fail_case == SizedAPIFailCase::kUpdate2 && update2_size > 0) { + std::vector<uint8_t> buf(update2_size - 1); + EXPECT_FALSE(EVP_CipherUpdate_ex(thisctx.get(), buf.data(), &len, + buf.size(), in2.data(), in2.size())); + EXPECT_EQ(len, size_t{0}); + continue; + } else { + std::vector<uint8_t> buf(update2_size); + EXPECT_TRUE(EVP_CipherUpdate_ex(thisctx.get(), buf.data(), &len, + buf.size(), in2.data(), in2.size())); + EXPECT_EQ(len, update2_size); + EXPECT_EQ(Bytes(expected.subspan(update1_size, update2_size)), + Bytes(buf)); + } + if (fail_case == SizedAPIFailCase::kFinal && final_size > 0) { + std::vector<uint8_t> buf(final_size - 1); + EXPECT_FALSE( + EVP_CipherFinal_ex2(thisctx.get(), buf.data(), &len, buf.size())); + EXPECT_EQ(len, size_t{0}); + continue; + } else { + std::vector<uint8_t> buf(final_size); + EXPECT_TRUE( + EVP_CipherFinal_ex2(thisctx.get(), buf.data(), &len, buf.size())); + EXPECT_EQ(len, final_size); + EXPECT_EQ(Bytes(expected.subspan(update1_size + update2_size)), + Bytes(buf)); + } + if (encrypt && is_aead) { + uint8_t rtag[16]; + ASSERT_LE(tag.size(), sizeof(rtag)); + ASSERT_TRUE(EVP_CIPHER_CTX_ctrl(thisctx.get(), EVP_CTRL_AEAD_GET_TAG, + tag.size(), rtag)); + EXPECT_EQ(Bytes(tag), Bytes(rtag, tag.size())); + } + } + } +} + static void TestCipher(const EVP_CIPHER *cipher, Operation input_op, bool padding, bssl::Span<const uint8_t> key, bssl::Span<const uint8_t> iv, @@ -403,13 +626,13 @@ SCOPED_TRACE(in_place); for (bool copy : {false, true}) { SCOPED_TRACE(copy); - TestCipherAPI(cipher, op, padding, copy, in_place, - /*use_evp_cipher=*/false, chunk_size, key, iv, - plaintext, ciphertext, aad, tag); + TestCipherAPI(cipher, op, padding, copy, in_place, API::kSized, + chunk_size, key, iv, plaintext, ciphertext, aad, tag); + TestCipherAPI(cipher, op, padding, copy, in_place, API::kUnsized, + chunk_size, key, iv, plaintext, ciphertext, aad, tag); if (!padding && chunk_size % block_size == 0) { - TestCipherAPI(cipher, op, padding, copy, in_place, - /*use_evp_cipher=*/true, chunk_size, key, iv, - plaintext, ciphertext, aad, tag); + TestCipherAPI(cipher, op, padding, copy, in_place, API::kCipher, + chunk_size, key, iv, plaintext, ciphertext, aad, tag); } } if (!padding) { @@ -418,12 +641,17 @@ } } } + if (op != Operation::kInvalidDecrypt) { + TestSizedAPIRangeChecks(cipher, op, padding, key, iv, plaintext, + ciphertext, aad, tag); + } } } static void CipherFileTest(FileTest *t) { std::string cipher_str; ASSERT_TRUE(t->GetAttribute(&cipher_str, "Cipher")); + SCOPED_TRACE(cipher_str); const EVP_CIPHER *cipher = GetCipher(cipher_str); ASSERT_TRUE(cipher); @@ -501,6 +729,8 @@ std::string key_size; ASSERT_TRUE(t->GetInstruction(&key_size, "keySize")); + SCOPED_TRACE(std::string{"aes_"} + key_size + + std::string{"_cbc"}); const EVP_CIPHER *cipher; switch (atoi(key_size.c_str())) { case 128:
diff --git a/crypto/cipher/e_des.cc b/crypto/cipher/e_des.cc index cf85371..cf3180c 100644 --- a/crypto/cipher/e_des.cc +++ b/crypto/cipher/e_des.cc
@@ -12,6 +12,8 @@ // See the License for the specific language governing permissions and // limitations under the License. +#include <stddef.h> + #include <openssl/cipher.h> #include <openssl/des.h> #include <openssl/nid.h> @@ -35,10 +37,10 @@ return 1; } -static int des_cbc_cipher(EVP_CIPHER_CTX *ctx, uint8_t *out, const uint8_t *in, - size_t in_len) { +static int des_cbc_cipher_update(EVP_CIPHER_CTX *ctx, uint8_t *out, + const uint8_t *in, size_t len) { EVP_DES_KEY *dat = (EVP_DES_KEY *)ctx->cipher_data; - DES_ncbc_encrypt_ex(in, out, in_len, &dat->ks.ks, ctx->iv, ctx->encrypt); + DES_ncbc_encrypt_ex(in, out, len, &dat->ks.ks, ctx->iv, ctx->encrypt); return 1; } @@ -50,22 +52,24 @@ /*ctx_size=*/sizeof(EVP_DES_KEY), /*flags=*/EVP_CIPH_CBC_MODE, /*init=*/des_init_key, - /*cipher=*/des_cbc_cipher, + /*cipher_update=*/des_cbc_cipher_update, + /*cipher_final=*/nullptr, + /*update_aad=*/nullptr, /*cleanup=*/nullptr, /*ctrl=*/nullptr, }; const EVP_CIPHER *EVP_des_cbc(void) { return &evp_des_cbc; } -static int des_ecb_cipher(EVP_CIPHER_CTX *ctx, uint8_t *out, const uint8_t *in, - size_t in_len) { - if (in_len < ctx->cipher->block_size) { +static int des_ecb_cipher_update(EVP_CIPHER_CTX *ctx, uint8_t *out, + const uint8_t *in, size_t len) { + if (len < ctx->cipher->block_size) { return 1; } - in_len -= ctx->cipher->block_size; + len -= ctx->cipher->block_size; EVP_DES_KEY *dat = (EVP_DES_KEY *)ctx->cipher_data; - for (size_t i = 0; i <= in_len; i += ctx->cipher->block_size) { + for (size_t i = 0; i <= len; i += ctx->cipher->block_size) { DES_ecb_encrypt_ex(in + i, out + i, &dat->ks.ks, ctx->encrypt); } return 1; @@ -79,7 +83,9 @@ /*ctx_size=*/sizeof(EVP_DES_KEY), /*flags=*/EVP_CIPH_ECB_MODE, /*init=*/des_init_key, - /*cipher=*/des_ecb_cipher, + /*cipher_update=*/des_ecb_cipher_update, + /*cipher_final=*/nullptr, + /*update_aad=*/nullptr, /*cleanup=*/nullptr, /*ctrl=*/nullptr, }; @@ -102,10 +108,10 @@ return 1; } -static int des_ede3_cbc_cipher(EVP_CIPHER_CTX *ctx, uint8_t *out, - const uint8_t *in, size_t in_len) { +static int des_ede3_cbc_cipher_update(EVP_CIPHER_CTX *ctx, uint8_t *out, + const uint8_t *in, size_t len) { DES_EDE_KEY *dat = (DES_EDE_KEY *)ctx->cipher_data; - DES_ede3_cbc_encrypt_ex(in, out, in_len, &dat->ks.ks[0], &dat->ks.ks[1], + DES_ede3_cbc_encrypt_ex(in, out, len, &dat->ks.ks[0], &dat->ks.ks[1], &dat->ks.ks[2], ctx->iv, ctx->encrypt); return 1; } @@ -118,7 +124,9 @@ /*ctx_size=*/sizeof(DES_EDE_KEY), /*flags=*/EVP_CIPH_CBC_MODE, /*init=*/des_ede3_init_key, - /*cipher=*/des_ede3_cbc_cipher, + /*cipher_update=*/des_ede3_cbc_cipher_update, + /*cipher_final=*/nullptr, + /*update_aad=*/nullptr, /*cleanup=*/nullptr, /*ctrl=*/nullptr, }; @@ -143,22 +151,24 @@ /*ctx_size=*/sizeof(DES_EDE_KEY), /*flags=*/EVP_CIPH_CBC_MODE, /*init=*/des_ede_init_key, - /*cipher=*/des_ede3_cbc_cipher, + /*cipher_update=*/des_ede3_cbc_cipher_update, + /*cipher_final=*/nullptr, + /*update_aad=*/nullptr, /*cleanup=*/nullptr, /*ctrl=*/nullptr, }; const EVP_CIPHER *EVP_des_ede_cbc(void) { return &evp_des_ede_cbc; } -static int des_ede_ecb_cipher(EVP_CIPHER_CTX *ctx, uint8_t *out, - const uint8_t *in, size_t in_len) { - if (in_len < ctx->cipher->block_size) { +static int des_ede_ecb_cipher_update(EVP_CIPHER_CTX *ctx, uint8_t *out, + const uint8_t *in, size_t len) { + if (len < ctx->cipher->block_size) { return 1; } - in_len -= ctx->cipher->block_size; + len -= ctx->cipher->block_size; DES_EDE_KEY *dat = (DES_EDE_KEY *)ctx->cipher_data; - for (size_t i = 0; i <= in_len; i += ctx->cipher->block_size) { + for (size_t i = 0; i <= len; i += ctx->cipher->block_size) { DES_ecb3_encrypt_ex(in + i, out + i, &dat->ks.ks[0], &dat->ks.ks[1], &dat->ks.ks[2], ctx->encrypt); } @@ -173,7 +183,9 @@ /*ctx_size=*/sizeof(DES_EDE_KEY), /*flags=*/EVP_CIPH_ECB_MODE, /*init=*/des_ede_init_key, - /*cipher=*/des_ede_ecb_cipher, + /*cipher_update=*/des_ede_ecb_cipher_update, + /*cipher_final=*/nullptr, + /*update_aad=*/nullptr, /*cleanup=*/nullptr, /*ctrl=*/nullptr, }; @@ -188,7 +200,9 @@ /*ctx_size=*/sizeof(DES_EDE_KEY), /*flags=*/EVP_CIPH_ECB_MODE, /*init=*/des_ede3_init_key, - /*cipher=*/des_ede_ecb_cipher, + /*cipher_update=*/des_ede_ecb_cipher_update, + /*cipher_final=*/nullptr, + /*update_aad=*/nullptr, /*cleanup=*/nullptr, /*ctrl=*/nullptr, };
diff --git a/crypto/cipher/e_null.cc b/crypto/cipher/e_null.cc index 4e59448..4dfe6e4 100644 --- a/crypto/cipher/e_null.cc +++ b/crypto/cipher/e_null.cc
@@ -27,10 +27,10 @@ return 1; } -static int null_cipher(EVP_CIPHER_CTX *ctx, uint8_t *out, const uint8_t *in, - size_t in_len) { +static int null_cipher_update(EVP_CIPHER_CTX *ctx, uint8_t *out, + const uint8_t *in, size_t len) { if (in != out) { - OPENSSL_memcpy(out, in, in_len); + OPENSSL_memcpy(out, in, len); } return 1; } @@ -43,7 +43,9 @@ /*ctx_size=*/0, /*flags=*/0, /*init=*/null_init_key, - /*cipher=*/null_cipher, + /*cipher_update=*/null_cipher_update, + /*cipher_final=*/nullptr, + /*update_aad=*/nullptr, /*cleanup=*/nullptr, /*ctrl=*/nullptr, };
diff --git a/crypto/cipher/e_rc2.cc b/crypto/cipher/e_rc2.cc index 250351a..f52faa5 100644 --- a/crypto/cipher/e_rc2.cc +++ b/crypto/cipher/e_rc2.cc
@@ -351,19 +351,19 @@ return 1; } -static int rc2_cbc_cipher(EVP_CIPHER_CTX *ctx, uint8_t *out, const uint8_t *in, - size_t inl) { +static int rc2_cbc_cipher_update(EVP_CIPHER_CTX *ctx, uint8_t *out, + const uint8_t *in, size_t len) { EVP_RC2_KEY *key = (EVP_RC2_KEY *)ctx->cipher_data; static const size_t kChunkSize = 0x10000; - while (inl >= kChunkSize) { + while (len >= kChunkSize) { RC2_cbc_encrypt(in, out, kChunkSize, &key->ks, ctx->iv, ctx->encrypt); - inl -= kChunkSize; + len -= kChunkSize; in += kChunkSize; out += kChunkSize; } - if (inl) { - RC2_cbc_encrypt(in, out, inl, &key->ks, ctx->iv, ctx->encrypt); + if (len) { + RC2_cbc_encrypt(in, out, len, &key->ks, ctx->iv, ctx->encrypt); } return 1; } @@ -394,7 +394,9 @@ /*ctx_size=*/sizeof(EVP_RC2_KEY), /*flags=*/EVP_CIPH_CBC_MODE | EVP_CIPH_VARIABLE_LENGTH | EVP_CIPH_CTRL_INIT, /*init=*/rc2_init_key, - /*cipher=*/rc2_cbc_cipher, + /*cipher_update=*/rc2_cbc_cipher_update, + /*cipher_final=*/nullptr, + /*update_aad=*/nullptr, /*cleanup=*/nullptr, /*ctrl=*/rc2_ctrl, }; @@ -409,7 +411,9 @@ /*ctx_size=*/sizeof(EVP_RC2_KEY), /*flags=*/EVP_CIPH_CBC_MODE | EVP_CIPH_VARIABLE_LENGTH | EVP_CIPH_CTRL_INIT, /*init=*/rc2_init_key, - /*cipher=*/rc2_cbc_cipher, + /*cipher_update=*/rc2_cbc_cipher_update, + /*cipher_final=*/nullptr, + /*update_aad=*/nullptr, /*cleanup=*/nullptr, /*ctrl=*/rc2_ctrl, };
diff --git a/crypto/cipher/e_rc4.cc b/crypto/cipher/e_rc4.cc index 2450a73..c2e6d44 100644 --- a/crypto/cipher/e_rc4.cc +++ b/crypto/cipher/e_rc4.cc
@@ -30,11 +30,11 @@ return 1; } -static int rc4_cipher(EVP_CIPHER_CTX *ctx, uint8_t *out, const uint8_t *in, - size_t in_len) { +static int rc4_cipher_update(EVP_CIPHER_CTX *ctx, uint8_t *out, + const uint8_t *in, size_t len) { RC4_KEY *rc4key = (RC4_KEY *)ctx->cipher_data; - RC4(rc4key, in_len, in, out); + RC4(rc4key, len, in, out); return 1; } @@ -46,7 +46,9 @@ /*ctx_size=*/sizeof(RC4_KEY), /*flags=*/EVP_CIPH_VARIABLE_LENGTH, /*init=*/rc4_init_key, - /*cipher=*/rc4_cipher, + /*cipher_update=*/rc4_cipher_update, + /*cipher_final=*/nullptr, + /*update_aad=*/nullptr, /*cleanup=*/nullptr, /*ctrl=*/nullptr, };
diff --git a/crypto/compiler_test.cc b/crypto/compiler_test.cc index ef29e78..042d514 100644 --- a/crypto/compiler_test.cc +++ b/crypto/compiler_test.cc
@@ -107,6 +107,9 @@ // size_t does not exceed uint64_t. static_assert(sizeof(size_t) <= 8u, "size_t must not exceed uint64_t"); + // The positive maximum of |int| must fit into |size_t|. + static_assert(sizeof(int) <= sizeof(size_t), "int must not exceed size_t"); + // Require that |int| be exactly 32 bits. OpenSSL historically mixed up // |unsigned| and |uint32_t|, so we require it be at least 32 bits. Requiring // at most 32-bits is a bit more subtle. C promotes arithmetic operands to
diff --git a/crypto/fipsmodule/cipher/cipher.cc.inc b/crypto/fipsmodule/cipher/cipher.cc.inc index bd07890..c2bbb9f 100644 --- a/crypto/fipsmodule/cipher/cipher.cc.inc +++ b/crypto/fipsmodule/cipher/cipher.cc.inc
@@ -205,7 +205,7 @@ // block_remainder returns the number of bytes to remove from |len| to get a // multiple of |ctx|'s block size. -static int block_remainder(const EVP_CIPHER_CTX *ctx, int len) { +static size_t block_remainder(const EVP_CIPHER_CTX *ctx, size_t len) { // |block_size| must be a power of two. assert(ctx->cipher->block_size != 0); assert((ctx->cipher->block_size & (ctx->cipher->block_size - 1)) == 0); @@ -214,6 +214,34 @@ int EVP_EncryptUpdate(EVP_CIPHER_CTX *ctx, uint8_t *out, int *out_len, const uint8_t *in, int in_len) { + *out_len = 0; + if (in_len < 0) { + OPENSSL_PUT_ERROR(CIPHER, ERR_R_OVERFLOW); + return 0; + } + size_t in_len_sz = static_cast<size_t>(in_len); + size_t out_len_sz; + if ((ctx->cipher->flags & EVP_CIPH_FLAG_CUSTOM_CIPHER) && out == nullptr) { + if (!EVP_CipherUpdateAAD(ctx, in, in_len_sz)) { + return 0; + } + out_len_sz = in_len_sz; // Even though no output was written! + } else { + // in_len_sz is < INT_MAX which is no more than half of SIZE_MAX. + size_t max_out_len = + std::min(in_len_sz + ctx->cipher->block_size - 1, size_t{INT_MAX}); + if (!EVP_EncryptUpdate_ex(ctx, out, &out_len_sz, max_out_len, in, + in_len_sz)) { + return 0; + } + } + *out_len = static_cast<int>(out_len_sz); + return 1; +} + +int EVP_EncryptUpdate_ex(EVP_CIPHER_CTX *ctx, uint8_t *out, size_t *out_len, + size_t max_out_len, const uint8_t *in, size_t in_len) { + *out_len = 0; if (ctx->poisoned) { OPENSSL_PUT_ERROR(CIPHER, ERR_R_SHOULD_NOT_HAVE_BEEN_CALLED); return 0; @@ -223,205 +251,76 @@ // callers do not continue to use the object in that case. ctx->poisoned = 1; - // Ciphers that use blocks may write up to |bl| extra bytes. Ensure the output - // does not overflow |*out_len|. - int bl = ctx->cipher->block_size; - if (bl > 1 && in_len > INT_MAX - bl) { - OPENSSL_PUT_ERROR(CIPHER, ERR_R_OVERFLOW); - return 0; - } + // Ciphers that use blocks may write up to |block_size| extra bytes. Ensure + // the output does not overflow |*out_len|. + size_t block_size = ctx->cipher->block_size; - if (ctx->cipher->flags & EVP_CIPH_FLAG_CUSTOM_CIPHER) { - int ret = ctx->cipher->cipher(ctx, out, in, in_len); - if (ret < 0) { - return 0; - } else { - *out_len = ret; - } + if (in_len == 0) { ctx->poisoned = 0; return 1; } - if (in_len <= 0) { - *out_len = 0; - if (in_len == 0) { - ctx->poisoned = 0; - return 1; - } - return 0; - } - - if (ctx->buf_len == 0 && block_remainder(ctx, in_len) == 0) { - if (ctx->cipher->cipher(ctx, out, in, in_len)) { - *out_len = in_len; - ctx->poisoned = 0; - return 1; - } else { - *out_len = 0; - return 0; - } - } - - int i = ctx->buf_len; - assert(bl <= (int)sizeof(ctx->buf)); - if (i != 0) { - if (bl - i > in_len) { - OPENSSL_memcpy(&ctx->buf[i], in, in_len); + size_t buf_len = ctx->buf_len; + assert(block_size <= sizeof(ctx->buf)); + if (buf_len != 0) { + if (block_size - buf_len > in_len) { + OPENSSL_memcpy(&ctx->buf[buf_len], in, in_len); ctx->buf_len += in_len; - *out_len = 0; ctx->poisoned = 0; return 1; } else { - int j = bl - i; - OPENSSL_memcpy(&ctx->buf[i], in, j); - if (!ctx->cipher->cipher(ctx, out, ctx->buf, bl)) { + size_t j = block_size - buf_len; + OPENSSL_memcpy(&ctx->buf[buf_len], in, j); + if (max_out_len < block_size) { + OPENSSL_PUT_ERROR(CIPHER, CIPHER_R_BUFFER_TOO_SMALL); + return 0; + } + if (!ctx->cipher->cipher_update(ctx, out, ctx->buf, block_size)) { return 0; } in_len -= j; in += j; - out += bl; - *out_len = bl; + out += block_size; + *out_len = block_size; + max_out_len -= block_size; } - } else { - *out_len = 0; } - i = block_remainder(ctx, in_len); - in_len -= i; + size_t remainder = block_remainder(ctx, in_len); + in_len -= remainder; if (in_len > 0) { - if (!ctx->cipher->cipher(ctx, out, in, in_len)) { + if (max_out_len < in_len) { + OPENSSL_PUT_ERROR(CIPHER, CIPHER_R_BUFFER_TOO_SMALL); + *out_len = 0; + return 0; + } + if (!ctx->cipher->cipher_update(ctx, out, in, in_len)) { + *out_len = 0; return 0; } *out_len += in_len; + max_out_len -= in_len; } - if (i != 0) { - OPENSSL_memcpy(ctx->buf, &in[in_len], i); + if (remainder != 0) { + OPENSSL_memcpy(ctx->buf, &in[in_len], remainder); } - ctx->buf_len = i; + ctx->buf_len = remainder; ctx->poisoned = 0; return 1; } int EVP_EncryptFinal_ex(EVP_CIPHER_CTX *ctx, uint8_t *out, int *out_len) { - int n; - unsigned int i, b, bl; - - if (ctx->poisoned) { - OPENSSL_PUT_ERROR(CIPHER, ERR_R_SHOULD_NOT_HAVE_BEEN_CALLED); - return 0; - } - - if (ctx->cipher->flags & EVP_CIPH_FLAG_CUSTOM_CIPHER) { - // When EVP_CIPH_FLAG_CUSTOM_CIPHER is set, the return value of |cipher| is - // the number of bytes written, or -1 on error. Otherwise the return value - // is one on success and zero on error. - const int num_bytes = ctx->cipher->cipher(ctx, out, nullptr, 0); - if (num_bytes < 0) { - return 0; - } - *out_len = num_bytes; - goto out; - } - - b = ctx->cipher->block_size; - assert(b <= sizeof(ctx->buf)); - if (b == 1) { - *out_len = 0; - goto out; - } - - bl = ctx->buf_len; - if (ctx->flags & EVP_CIPH_NO_PADDING) { - if (bl) { - OPENSSL_PUT_ERROR(CIPHER, CIPHER_R_DATA_NOT_MULTIPLE_OF_BLOCK_LENGTH); - return 0; - } - *out_len = 0; - goto out; - } - - n = b - bl; - for (i = bl; i < b; i++) { - ctx->buf[i] = n; - } - if (!ctx->cipher->cipher(ctx, out, ctx->buf, b)) { - return 0; - } - *out_len = b; - -out: - EVP_Cipher_verify_service_indicator(ctx); - return 1; + size_t out_len_sz; + int ret = + EVP_EncryptFinal_ex2(ctx, out, &out_len_sz, ctx->cipher->block_size); + static_assert(EVP_MAX_BLOCK_LENGTH <= INT_MAX); + *out_len = static_cast<int>(out_len_sz); + return ret; } -int EVP_DecryptUpdate(EVP_CIPHER_CTX *ctx, uint8_t *out, int *out_len, - const uint8_t *in, int in_len) { - if (ctx->poisoned) { - OPENSSL_PUT_ERROR(CIPHER, ERR_R_SHOULD_NOT_HAVE_BEEN_CALLED); - return 0; - } - - // Ciphers that use blocks may write up to |bl| extra bytes. Ensure the output - // does not overflow |*out_len|. - unsigned int b = ctx->cipher->block_size; - if (b > 1 && in_len > INT_MAX - (int)b) { - OPENSSL_PUT_ERROR(CIPHER, ERR_R_OVERFLOW); - return 0; - } - - if (ctx->cipher->flags & EVP_CIPH_FLAG_CUSTOM_CIPHER) { - int r = ctx->cipher->cipher(ctx, out, in, in_len); - if (r < 0) { - *out_len = 0; - return 0; - } else { - *out_len = r; - } - return 1; - } - - if (in_len <= 0) { - *out_len = 0; - return in_len == 0; - } - - if (ctx->flags & EVP_CIPH_NO_PADDING) { - return EVP_EncryptUpdate(ctx, out, out_len, in, in_len); - } - - assert(b <= sizeof(ctx->final)); - int fix_len = 0; - if (ctx->final_used) { - OPENSSL_memcpy(out, ctx->final, b); - out += b; - fix_len = 1; - } - - if (!EVP_EncryptUpdate(ctx, out, out_len, in, in_len)) { - return 0; - } - - // if we have 'decrypted' a multiple of block size, make sure - // we have a copy of this last block - if (b > 1 && !ctx->buf_len) { - *out_len -= b; - ctx->final_used = 1; - OPENSSL_memcpy(ctx->final, &out[*out_len], b); - } else { - ctx->final_used = 0; - } - - if (fix_len) { - *out_len += b; - } - - return 1; -} - -int EVP_DecryptFinal_ex(EVP_CIPHER_CTX *ctx, unsigned char *out, int *out_len) { - int i, n; - unsigned int b; +int EVP_EncryptFinal_ex2(EVP_CIPHER_CTX *ctx, uint8_t *out, size_t *out_len, + size_t max_out_len) { *out_len = 0; if (ctx->poisoned) { @@ -429,65 +328,250 @@ return 0; } - if (ctx->cipher->flags & EVP_CIPH_FLAG_CUSTOM_CIPHER) { - i = ctx->cipher->cipher(ctx, out, nullptr, 0); - if (i < 0) { - return 0; - } else { - *out_len = i; - } - goto out; - } - - b = ctx->cipher->block_size; - if (ctx->flags & EVP_CIPH_NO_PADDING) { - if (ctx->buf_len) { - OPENSSL_PUT_ERROR(CIPHER, CIPHER_R_DATA_NOT_MULTIPLE_OF_BLOCK_LENGTH); - return 0; - } - *out_len = 0; - goto out; - } - - if (b > 1) { - if (ctx->buf_len || !ctx->final_used) { - OPENSSL_PUT_ERROR(CIPHER, CIPHER_R_WRONG_FINAL_BLOCK_LENGTH); - return 0; - } - assert(b <= sizeof(ctx->final)); - - // The following assumes that the ciphertext has been authenticated. - // Otherwise it provides a padding oracle. - n = ctx->final[b - 1]; - if (n == 0 || n > (int)b) { - OPENSSL_PUT_ERROR(CIPHER, CIPHER_R_BAD_DECRYPT); - return 0; - } - - for (i = 0; i < n; i++) { - if (ctx->final[--b] != n) { - OPENSSL_PUT_ERROR(CIPHER, CIPHER_R_BAD_DECRYPT); + size_t block_size = ctx->cipher->block_size; + assert(block_size <= sizeof(ctx->buf)); + if (block_size == 1) { + if (ctx->cipher->flags & EVP_CIPH_FLAG_CUSTOM_CIPHER) { + if (!ctx->cipher->cipher_final(ctx)) { return 0; } } - - n = ctx->cipher->block_size - n; - for (i = 0; i < n; i++) { - out[i] = ctx->final[i]; - } - *out_len = n; - } else { - *out_len = 0; + EVP_Cipher_verify_service_indicator(ctx); + return 1; } -out: + size_t buf_len = ctx->buf_len; + if (ctx->flags & EVP_CIPH_NO_PADDING) { + if (buf_len) { + OPENSSL_PUT_ERROR(CIPHER, CIPHER_R_DATA_NOT_MULTIPLE_OF_BLOCK_LENGTH); + return 0; + } + EVP_Cipher_verify_service_indicator(ctx); + return 1; + } + + size_t padding = block_size - buf_len; + for (size_t i = buf_len; i < block_size; i++) { + ctx->buf[i] = padding; + } + if (max_out_len < block_size) { + OPENSSL_PUT_ERROR(CIPHER, CIPHER_R_BUFFER_TOO_SMALL); + return 0; + } + if (!ctx->cipher->cipher_update(ctx, out, ctx->buf, block_size)) { + return 0; + } + *out_len = block_size; + EVP_Cipher_verify_service_indicator(ctx); + return 1; +} + +int EVP_DecryptUpdate(EVP_CIPHER_CTX *ctx, uint8_t *out, int *out_len, + const uint8_t *in, int in_len) { + *out_len = 0; + if (in_len < 0) { + OPENSSL_PUT_ERROR(CIPHER, ERR_R_OVERFLOW); + return 0; + } + size_t in_len_sz = static_cast<size_t>(in_len); + size_t out_len_sz; + if ((ctx->cipher->flags & EVP_CIPH_FLAG_CUSTOM_CIPHER) && out == nullptr) { + if (!EVP_CipherUpdateAAD(ctx, in, in_len_sz)) { + return 0; + } + out_len_sz = in_len_sz; + } else { + // in_len_sz is < INT_MAX which is no more than half of SIZE_MAX. + size_t max_out_len = std::min( + in_len_sz + (ctx->cipher->block_size > 1 ? ctx->cipher->block_size : 0), + size_t{INT_MAX}); + if (!EVP_DecryptUpdate_ex(ctx, out, &out_len_sz, max_out_len, in, + in_len_sz)) { + return 0; + } + } + *out_len = static_cast<int>(out_len_sz); + return 1; +} + +int EVP_DecryptUpdate_ex(EVP_CIPHER_CTX *ctx, uint8_t *out, size_t *out_len, + size_t max_out_len, const uint8_t *in, size_t in_len) { + *out_len = 0; + if (ctx->poisoned) { + OPENSSL_PUT_ERROR(CIPHER, ERR_R_SHOULD_NOT_HAVE_BEEN_CALLED); + return 0; + } + + // Ciphers that use blocks may write up to |block_size| extra bytes. Ensure + // the output does not overflow |*out_len|. + size_t block_size = ctx->cipher->block_size; + + if (in_len == 0) { + return 1; + } + + if (ctx->flags & EVP_CIPH_NO_PADDING) { + // Use the shared block handling logic from encryption. + return EVP_EncryptUpdate_ex(ctx, out, out_len, max_out_len, in, in_len); + } + + assert(block_size <= sizeof(ctx->final)); + bool fix_len = false; + if (ctx->final_used) { + if (max_out_len < block_size) { + OPENSSL_PUT_ERROR(CIPHER, CIPHER_R_BUFFER_TOO_SMALL); + return 0; + } + OPENSSL_memcpy(out, ctx->final, block_size); + ctx->final_used = 0; + out += block_size; + max_out_len -= block_size; + fix_len = true; + } + + // Use the shared block handling logic from encryption. + if (block_size > 1 && block_remainder(ctx, ctx->buf_len + in_len) == 0) { + // Decryption would end on a block boundary. In this case, although we + // can decrypt up to the block boundary, we cannot output the final + // plaintext block yet. It may be the final block, with padding to + // remove. + // + // Instead, output all but the final block's decryption, then decrypt the + // final block into ctx->final, to be processed later. + + // NOTE: Not _really_ necessary, but let's try aligning the second + // EVP_EncryptUpdate_ex call to a block boundary to mess with the buffer + // less. + size_t head = in_len > block_size ? in_len - block_size : 0; + if (!EVP_EncryptUpdate_ex(ctx, out, out_len, max_out_len, in, head)) { + return 0; + } + size_t final_size; + if (!EVP_EncryptUpdate_ex(ctx, ctx->final, &final_size, sizeof(ctx->final), + in + head, in_len - head)) { + return 0; + } + ctx->final_used = 1; + assert(final_size == block_size); + assert(ctx->buf_len == 0); + } else { + // Buffer will be non-empty. + if (!EVP_EncryptUpdate_ex(ctx, out, out_len, max_out_len, in, in_len)) { + return 0; + } + assert(block_size == 1 || ctx->buf_len != 0); + } + + if (fix_len) { + *out_len += block_size; + } + + return 1; +} + +int EVP_DecryptFinal_ex(EVP_CIPHER_CTX *ctx, uint8_t *out, int *out_len) { + size_t out_len_sz; + int ret = + EVP_DecryptFinal_ex2(ctx, out, &out_len_sz, ctx->cipher->block_size); + static_assert(EVP_MAX_BLOCK_LENGTH <= INT_MAX); + *out_len = static_cast<int>(out_len_sz); + return ret; +} + +int EVP_DecryptFinal_ex2(EVP_CIPHER_CTX *ctx, unsigned char *out, + size_t *out_len, size_t max_out_len) { + *out_len = 0; + + if (ctx->poisoned) { + OPENSSL_PUT_ERROR(CIPHER, ERR_R_SHOULD_NOT_HAVE_BEEN_CALLED); + return 0; + } + + size_t block_size = ctx->cipher->block_size; + assert(block_size <= sizeof(ctx->buf)); + if (block_size == 1) { + if (ctx->cipher->flags & EVP_CIPH_FLAG_CUSTOM_CIPHER) { + if (!ctx->cipher->cipher_final(ctx)) { + return 0; + } + } + EVP_Cipher_verify_service_indicator(ctx); + return 1; + } + + size_t buf_len = ctx->buf_len; + if (ctx->flags & EVP_CIPH_NO_PADDING) { + if (buf_len) { + OPENSSL_PUT_ERROR(CIPHER, CIPHER_R_DATA_NOT_MULTIPLE_OF_BLOCK_LENGTH); + return 0; + } + EVP_Cipher_verify_service_indicator(ctx); + return 1; + } + + if (buf_len || !ctx->final_used) { + OPENSSL_PUT_ERROR(CIPHER, CIPHER_R_WRONG_FINAL_BLOCK_LENGTH); + return 0; + } + assert(block_size <= sizeof(ctx->final)); + + // The following assumes that the ciphertext has been authenticated. + // Otherwise it provides a padding oracle. + size_t padding = ctx->final[block_size - 1]; + if (padding == 0 || padding > block_size) { + OPENSSL_PUT_ERROR(CIPHER, CIPHER_R_BAD_DECRYPT); + return 0; + } + + for (size_t i = block_size - padding; i < block_size; i++) { + if (ctx->final[i] != padding) { + OPENSSL_PUT_ERROR(CIPHER, CIPHER_R_BAD_DECRYPT); + return 0; + } + } + + size_t payload = ctx->cipher->block_size - padding; + if (max_out_len < payload) { + OPENSSL_PUT_ERROR(CIPHER, CIPHER_R_BUFFER_TOO_SMALL); + return 0; + } + OPENSSL_memcpy(out, ctx->final, payload); + *out_len = payload; + EVP_Cipher_verify_service_indicator(ctx); return 1; } int EVP_Cipher(EVP_CIPHER_CTX *ctx, uint8_t *out, const uint8_t *in, size_t in_len) { - const int ret = ctx->cipher->cipher(ctx, out, in, in_len); + const int kError = + (ctx->cipher->flags & EVP_CIPH_FLAG_CUSTOM_CIPHER) ? -1 : 0; + + if ((ctx->cipher->flags & EVP_CIPH_FLAG_CUSTOM_CIPHER) && + in_len > size_t{INT_MAX}) { + // Can't represent the return value? That'd be bad. + OPENSSL_PUT_ERROR(CIPHER, ERR_R_OVERFLOW); + return kError; + } + + size_t out_len; + if ((ctx->cipher->flags & EVP_CIPH_FLAG_CUSTOM_CIPHER) && in == nullptr) { + if (!ctx->cipher->cipher_final(ctx)) { + return kError; + } + out_len = 0; + } else if ((ctx->cipher->flags & EVP_CIPH_FLAG_CUSTOM_CIPHER) && + out == nullptr) { + if (!ctx->cipher->update_aad(ctx, in, in_len)) { + return kError; + } + out_len = in_len; // Yes, even though no output was written! + } else { + if (!ctx->cipher->cipher_update(ctx, out, in, in_len)) { + return kError; + } + out_len = in_len; + } // |EVP_CIPH_FLAG_CUSTOM_CIPHER| never sets the FIPS indicator via // |EVP_Cipher| because it's complicated whether the operation has completed @@ -499,11 +583,15 @@ // because whether |ret| indicates success or not depends on whether // |EVP_CIPH_FLAG_CUSTOM_CIPHER| is set. (This unreasonable, but matches // OpenSSL.) - if (!(ctx->cipher->flags & EVP_CIPH_FLAG_CUSTOM_CIPHER) && ret) { + if (!(ctx->cipher->flags & EVP_CIPH_FLAG_CUSTOM_CIPHER)) { EVP_Cipher_verify_service_indicator(ctx); } - return ret; + // Custom ciphers return byte count; regular ciphers return boolean. + if (ctx->cipher->flags & EVP_CIPH_FLAG_CUSTOM_CIPHER) { + return static_cast<int>(out_len); + } + return 1; } int EVP_CipherUpdate(EVP_CIPHER_CTX *ctx, uint8_t *out, int *out_len, @@ -515,6 +603,32 @@ } } +int EVP_CipherUpdate_ex(EVP_CIPHER_CTX *ctx, uint8_t *out, size_t *out_len, + size_t max_out_len, const uint8_t *in, size_t in_len) { + if (ctx->encrypt) { + return EVP_EncryptUpdate_ex(ctx, out, out_len, max_out_len, in, in_len); + } else { + return EVP_DecryptUpdate_ex(ctx, out, out_len, max_out_len, in, in_len); + } +} + +int EVP_CipherUpdateAAD(EVP_CIPHER_CTX *ctx, const uint8_t *in, size_t in_len) { + // This code is identical for encryption and decryption, so the implementation + // can be here. + if (ctx->poisoned || !(ctx->cipher->flags & EVP_CIPH_FLAG_CUSTOM_CIPHER)) { + OPENSSL_PUT_ERROR(CIPHER, ERR_R_SHOULD_NOT_HAVE_BEEN_CALLED); + return 0; + } + ctx->poisoned = 1; + + if (!ctx->cipher->update_aad(ctx, in, in_len)) { + return 0; + } + + ctx->poisoned = 0; + return 1; +} + int EVP_CipherFinal_ex(EVP_CIPHER_CTX *ctx, uint8_t *out, int *out_len) { if (ctx->encrypt) { return EVP_EncryptFinal_ex(ctx, out, out_len); @@ -523,6 +637,15 @@ } } +int EVP_CipherFinal_ex2(EVP_CIPHER_CTX *ctx, uint8_t *out, size_t *out_len, + size_t max_out_len) { + if (ctx->encrypt) { + return EVP_EncryptFinal_ex2(ctx, out, out_len, max_out_len); + } else { + return EVP_DecryptFinal_ex2(ctx, out, out_len, max_out_len); + } +} + const EVP_CIPHER *EVP_CIPHER_CTX_cipher(const EVP_CIPHER_CTX *ctx) { return ctx->cipher; }
diff --git a/crypto/fipsmodule/cipher/e_aes.cc.inc b/crypto/fipsmodule/cipher/e_aes.cc.inc index 685fa24..52ffca5 100644 --- a/crypto/fipsmodule/cipher/e_aes.cc.inc +++ b/crypto/fipsmodule/cipher/e_aes.cc.inc
@@ -154,8 +154,8 @@ return 1; } -static int aes_cbc_cipher(EVP_CIPHER_CTX *ctx, uint8_t *out, const uint8_t *in, - size_t len) { +static int aes_cbc_cipher_update(EVP_CIPHER_CTX *ctx, uint8_t *out, + const uint8_t *in, size_t len) { EVP_AES_KEY *dat = (EVP_AES_KEY *)ctx->cipher_data; if (dat->stream.cbc) { @@ -169,8 +169,8 @@ return 1; } -static int aes_ecb_cipher(EVP_CIPHER_CTX *ctx, uint8_t *out, const uint8_t *in, - size_t len) { +static int aes_ecb_cipher_update(EVP_CIPHER_CTX *ctx, uint8_t *out, + const uint8_t *in, size_t len) { size_t bl = ctx->cipher->block_size; EVP_AES_KEY *dat = (EVP_AES_KEY *)ctx->cipher_data; @@ -186,16 +186,16 @@ return 1; } -static int aes_ctr_cipher(EVP_CIPHER_CTX *ctx, uint8_t *out, const uint8_t *in, - size_t len) { +static int aes_ctr_cipher_update(EVP_CIPHER_CTX *ctx, uint8_t *out, + const uint8_t *in, size_t len) { EVP_AES_KEY *dat = (EVP_AES_KEY *)ctx->cipher_data; CRYPTO_ctr128_encrypt_ctr32(in, out, len, &dat->ks.ks, ctx->iv, ctx->buf, &ctx->num, dat->stream.ctr); return 1; } -static int aes_ofb_cipher(EVP_CIPHER_CTX *ctx, uint8_t *out, const uint8_t *in, - size_t len) { +static int aes_ofb_cipher_update(EVP_CIPHER_CTX *ctx, uint8_t *out, + const uint8_t *in, size_t len) { EVP_AES_KEY *dat = (EVP_AES_KEY *)ctx->cipher_data; CRYPTO_ofb128_encrypt(in, out, len, &dat->ks.ks, ctx->iv, &ctx->num, @@ -366,56 +366,55 @@ } } -static int aes_gcm_cipher(EVP_CIPHER_CTX *ctx, uint8_t *out, const uint8_t *in, - size_t len) { +static int aes_gcm_cipher_update(EVP_CIPHER_CTX *ctx, uint8_t *out, + const uint8_t *in, size_t len) { EVP_AES_GCM_CTX *gctx = reinterpret_cast<EVP_AES_GCM_CTX *>(ctx->cipher_data); // If not set up, return error - if (!gctx->key_set) { - return -1; - } - if (!gctx->iv_set) { - return -1; - } - - if (len > INT_MAX) { - // This function signature can only express up to |INT_MAX| bytes encrypted. - // - // TODO(https://crbug.com/boringssl/494): Make the internal |EVP_CIPHER| - // calling convention |size_t|-clean. - return -1; - } - - if (in) { - if (out == nullptr) { - if (!CRYPTO_gcm128_aad(&gctx->key, &gctx->gcm, in, len)) { - return -1; - } - } else if (ctx->encrypt) { - if (!CRYPTO_gcm128_encrypt(&gctx->key, &gctx->gcm, in, out, len)) { - return -1; - } - } else { - if (!CRYPTO_gcm128_decrypt(&gctx->key, &gctx->gcm, in, out, len)) { - return -1; - } - } - return (int)len; - } else { - if (!ctx->encrypt) { - if (gctx->taglen < 0 || !CRYPTO_gcm128_finish(&gctx->key, &gctx->gcm, - ctx->buf, gctx->taglen)) { - return -1; - } - gctx->iv_set = 0; - return 0; - } - CRYPTO_gcm128_tag(&gctx->key, &gctx->gcm, ctx->buf, 16); - gctx->taglen = 16; - // Don't reuse the IV - gctx->iv_set = 0; + if (!gctx->key_set || !gctx->iv_set) { return 0; } + + if (ctx->encrypt) { + return CRYPTO_gcm128_encrypt(&gctx->key, &gctx->gcm, in, out, len); + } else { + return CRYPTO_gcm128_decrypt(&gctx->key, &gctx->gcm, in, out, len); + } +} + +static int aes_gcm_cipher_final(EVP_CIPHER_CTX *ctx) { + EVP_AES_GCM_CTX *gctx = reinterpret_cast<EVP_AES_GCM_CTX *>(ctx->cipher_data); + + // If not set up, return error + if (!gctx->key_set || !gctx->iv_set) { + return 0; + } + + if (!ctx->encrypt) { + if (gctx->taglen < 0 || + !CRYPTO_gcm128_finish(&gctx->key, &gctx->gcm, ctx->buf, gctx->taglen)) { + return 0; + } + gctx->iv_set = 0; + return 1; + } + CRYPTO_gcm128_tag(&gctx->key, &gctx->gcm, ctx->buf, 16); + gctx->taglen = 16; + // Don't reuse the IV + gctx->iv_set = 0; + return 1; +} + +static int aes_gcm_update_aad(EVP_CIPHER_CTX *ctx, const uint8_t *in, + size_t len) { + EVP_AES_GCM_CTX *gctx = reinterpret_cast<EVP_AES_GCM_CTX *>(ctx->cipher_data); + + // If not set up, return error + if (!gctx->key_set || !gctx->iv_set) { + return 0; + } + + return CRYPTO_gcm128_aad(&gctx->key, &gctx->gcm, in, len); } DEFINE_METHOD_FUNCTION(EVP_CIPHER, EVP_aes_128_cbc) { @@ -428,7 +427,7 @@ out->ctx_size = sizeof(EVP_AES_KEY); out->flags = EVP_CIPH_CBC_MODE; out->init = aes_init_key; - out->cipher = aes_cbc_cipher; + out->cipher_update = aes_cbc_cipher_update; } DEFINE_METHOD_FUNCTION(EVP_CIPHER, EVP_aes_128_ctr) { @@ -441,7 +440,7 @@ out->ctx_size = sizeof(EVP_AES_KEY); out->flags = EVP_CIPH_CTR_MODE; out->init = aes_init_key; - out->cipher = aes_ctr_cipher; + out->cipher_update = aes_ctr_cipher_update; } DEFINE_LOCAL_DATA(EVP_CIPHER, aes_128_ecb_generic) { @@ -453,7 +452,7 @@ out->ctx_size = sizeof(EVP_AES_KEY); out->flags = EVP_CIPH_ECB_MODE; out->init = aes_init_key; - out->cipher = aes_ecb_cipher; + out->cipher_update = aes_ecb_cipher_update; } DEFINE_METHOD_FUNCTION(EVP_CIPHER, EVP_aes_128_ofb) { @@ -466,7 +465,7 @@ out->ctx_size = sizeof(EVP_AES_KEY); out->flags = EVP_CIPH_OFB_MODE; out->init = aes_init_key; - out->cipher = aes_ofb_cipher; + out->cipher_update = aes_ofb_cipher_update; } DEFINE_METHOD_FUNCTION(EVP_CIPHER, EVP_aes_128_gcm) { @@ -481,7 +480,9 @@ EVP_CIPH_FLAG_CUSTOM_CIPHER | EVP_CIPH_ALWAYS_CALL_INIT | EVP_CIPH_CTRL_INIT | EVP_CIPH_FLAG_AEAD_CIPHER; out->init = aes_gcm_init_key; - out->cipher = aes_gcm_cipher; + out->cipher_update = aes_gcm_cipher_update; + out->cipher_final = aes_gcm_cipher_final; + out->update_aad = aes_gcm_update_aad; out->cleanup = aes_gcm_cleanup; out->ctrl = aes_gcm_ctrl; } @@ -496,7 +497,7 @@ out->ctx_size = sizeof(EVP_AES_KEY); out->flags = EVP_CIPH_CBC_MODE; out->init = aes_init_key; - out->cipher = aes_cbc_cipher; + out->cipher_update = aes_cbc_cipher_update; } DEFINE_METHOD_FUNCTION(EVP_CIPHER, EVP_aes_192_ctr) { @@ -509,7 +510,7 @@ out->ctx_size = sizeof(EVP_AES_KEY); out->flags = EVP_CIPH_CTR_MODE; out->init = aes_init_key; - out->cipher = aes_ctr_cipher; + out->cipher_update = aes_ctr_cipher_update; } DEFINE_LOCAL_DATA(EVP_CIPHER, aes_192_ecb_generic) { @@ -521,7 +522,7 @@ out->ctx_size = sizeof(EVP_AES_KEY); out->flags = EVP_CIPH_ECB_MODE; out->init = aes_init_key; - out->cipher = aes_ecb_cipher; + out->cipher_update = aes_ecb_cipher_update; } DEFINE_METHOD_FUNCTION(EVP_CIPHER, EVP_aes_192_ofb) { @@ -534,7 +535,7 @@ out->ctx_size = sizeof(EVP_AES_KEY); out->flags = EVP_CIPH_OFB_MODE; out->init = aes_init_key; - out->cipher = aes_ofb_cipher; + out->cipher_update = aes_ofb_cipher_update; } DEFINE_METHOD_FUNCTION(EVP_CIPHER, EVP_aes_192_gcm) { @@ -549,7 +550,9 @@ EVP_CIPH_FLAG_CUSTOM_CIPHER | EVP_CIPH_ALWAYS_CALL_INIT | EVP_CIPH_CTRL_INIT | EVP_CIPH_FLAG_AEAD_CIPHER; out->init = aes_gcm_init_key; - out->cipher = aes_gcm_cipher; + out->cipher_update = aes_gcm_cipher_update; + out->cipher_final = aes_gcm_cipher_final; + out->update_aad = aes_gcm_update_aad; out->cleanup = aes_gcm_cleanup; out->ctrl = aes_gcm_ctrl; } @@ -564,7 +567,7 @@ out->ctx_size = sizeof(EVP_AES_KEY); out->flags = EVP_CIPH_CBC_MODE; out->init = aes_init_key; - out->cipher = aes_cbc_cipher; + out->cipher_update = aes_cbc_cipher_update; } DEFINE_METHOD_FUNCTION(EVP_CIPHER, EVP_aes_256_ctr) { @@ -577,7 +580,7 @@ out->ctx_size = sizeof(EVP_AES_KEY); out->flags = EVP_CIPH_CTR_MODE; out->init = aes_init_key; - out->cipher = aes_ctr_cipher; + out->cipher_update = aes_ctr_cipher_update; } DEFINE_LOCAL_DATA(EVP_CIPHER, aes_256_ecb_generic) { @@ -589,7 +592,7 @@ out->ctx_size = sizeof(EVP_AES_KEY); out->flags = EVP_CIPH_ECB_MODE; out->init = aes_init_key; - out->cipher = aes_ecb_cipher; + out->cipher_update = aes_ecb_cipher_update; } DEFINE_METHOD_FUNCTION(EVP_CIPHER, EVP_aes_256_ofb) { @@ -602,7 +605,7 @@ out->ctx_size = sizeof(EVP_AES_KEY); out->flags = EVP_CIPH_OFB_MODE; out->init = aes_init_key; - out->cipher = aes_ofb_cipher; + out->cipher_update = aes_ofb_cipher_update; } DEFINE_METHOD_FUNCTION(EVP_CIPHER, EVP_aes_256_gcm) { @@ -617,15 +620,17 @@ EVP_CIPH_FLAG_CUSTOM_CIPHER | EVP_CIPH_ALWAYS_CALL_INIT | EVP_CIPH_CTRL_INIT | EVP_CIPH_FLAG_AEAD_CIPHER; out->init = aes_gcm_init_key; - out->cipher = aes_gcm_cipher; + out->cipher_update = aes_gcm_cipher_update; + out->cipher_final = aes_gcm_cipher_final; + out->update_aad = aes_gcm_update_aad; out->cleanup = aes_gcm_cleanup; out->ctrl = aes_gcm_ctrl; } #if defined(HWAES_ECB) -static int aes_hw_ecb_cipher(EVP_CIPHER_CTX *ctx, uint8_t *out, - const uint8_t *in, size_t len) { +static int aes_hw_ecb_cipher_update(EVP_CIPHER_CTX *ctx, uint8_t *out, + const uint8_t *in, size_t len) { size_t bl = ctx->cipher->block_size; if (len < bl) { @@ -648,7 +653,7 @@ out->ctx_size = sizeof(EVP_AES_KEY); out->flags = EVP_CIPH_ECB_MODE; out->init = aes_init_key; - out->cipher = aes_hw_ecb_cipher; + out->cipher_update = aes_hw_ecb_cipher_update; } DEFINE_LOCAL_DATA(EVP_CIPHER, aes_hw_192_ecb) { @@ -660,7 +665,7 @@ out->ctx_size = sizeof(EVP_AES_KEY); out->flags = EVP_CIPH_ECB_MODE; out->init = aes_init_key; - out->cipher = aes_hw_ecb_cipher; + out->cipher_update = aes_hw_ecb_cipher_update; } DEFINE_LOCAL_DATA(EVP_CIPHER, aes_hw_256_ecb) { @@ -672,7 +677,7 @@ out->ctx_size = sizeof(EVP_AES_KEY); out->flags = EVP_CIPH_ECB_MODE; out->init = aes_init_key; - out->cipher = aes_hw_ecb_cipher; + out->cipher_update = aes_hw_ecb_cipher_update; } #define EVP_ECB_CIPHER_FUNCTION(keybits) \
diff --git a/crypto/fipsmodule/cipher/internal.h b/crypto/fipsmodule/cipher/internal.h index c5373bb..794db01 100644 --- a/crypto/fipsmodule/cipher/internal.h +++ b/crypto/fipsmodule/cipher/internal.h
@@ -95,8 +95,32 @@ int (*init)(EVP_CIPHER_CTX *ctx, const uint8_t *key, const uint8_t *iv, int enc); - int (*cipher)(EVP_CIPHER_CTX *ctx, uint8_t *out, const uint8_t *in, - size_t inl); + // cipher encrypts/decrypts |in|, write output to |out|. Writes exactly |len| + // bytes, which must be a multiple of the |block_size|. + // + // For ciphers where encryption and decryption operations differ, |init| + // shall set an internal state for this. + // + // Returns 1 on success, or 0 on error. + int (*cipher_update)(EVP_CIPHER_CTX *ctx, uint8_t *out, const uint8_t *in, + size_t len); + + // cipher_final finalizes the cipher, performing possible final + // authentication checks. + // + // Only used for |EVP_CIPH_FLAG_CUSTOM_CIPHER| ciphers. + // + // Returns 1 on success, or 0 on error. When decrypting, if an error is + // returned, the decrypted data must not be used. + int (*cipher_final)(EVP_CIPHER_CTX *ctx); + + // update_aad adds |in| (of length |inl|) to the authenticated data for the + // encryption operation. + // + // Only used for |EVP_CIPH_FLAG_CUSTOM_CIPHER| ciphers. + // + // Returns 1 on success, or 0 on error. + int (*update_aad)(EVP_CIPHER_CTX *ctx, const uint8_t *in, size_t inl); // cleanup, if non-NULL, releases memory associated with the context. It is // called if |EVP_CTRL_INIT| succeeds. Note that |init| may not have been
diff --git a/decrepit/blowfish/blowfish.cc b/decrepit/blowfish/blowfish.cc index 1c8aa55..8cd5112 100644 --- a/decrepit/blowfish/blowfish.cc +++ b/decrepit/blowfish/blowfish.cc
@@ -521,8 +521,8 @@ return 1; } -static int bf_ecb_cipher(EVP_CIPHER_CTX *ctx, uint8_t *out, const uint8_t *in, - size_t len) { +static int bf_ecb_cipher_update(EVP_CIPHER_CTX *ctx, uint8_t *out, + const uint8_t *in, size_t len) { BF_KEY *bf_key = reinterpret_cast<BF_KEY *>(ctx->cipher_data); while (len >= BF_BLOCK) { @@ -536,15 +536,15 @@ return 1; } -static int bf_cbc_cipher(EVP_CIPHER_CTX *ctx, uint8_t *out, const uint8_t *in, - size_t len) { +static int bf_cbc_cipher_update(EVP_CIPHER_CTX *ctx, uint8_t *out, + const uint8_t *in, size_t len) { BF_KEY *bf_key = reinterpret_cast<BF_KEY *>(ctx->cipher_data); BF_cbc_encrypt(in, out, len, bf_key, ctx->iv, ctx->encrypt); return 1; } -static int bf_cfb_cipher(EVP_CIPHER_CTX *ctx, uint8_t *out, const uint8_t *in, - size_t len) { +static int bf_cfb_cipher_update(EVP_CIPHER_CTX *ctx, uint8_t *out, + const uint8_t *in, size_t len) { BF_KEY *bf_key = reinterpret_cast<BF_KEY *>(ctx->cipher_data); int num = ctx->num; BF_cfb64_encrypt(in, out, len, bf_key, ctx->iv, &num, ctx->encrypt); @@ -560,7 +560,9 @@ /* ctx_size= */ sizeof(BF_KEY), /* flags= */ EVP_CIPH_ECB_MODE | EVP_CIPH_VARIABLE_LENGTH, /* init= */ bf_init_key, - /* cipher= */ bf_ecb_cipher, + /* cipher_update= */ bf_ecb_cipher_update, + /* cipher_final= */ nullptr, + /* update_aad= */ nullptr, /* cleanup= */ nullptr, /* ctrl= */ nullptr, }; @@ -573,7 +575,9 @@ /* ctx_size= */ sizeof(BF_KEY), /* flags= */ EVP_CIPH_CBC_MODE | EVP_CIPH_VARIABLE_LENGTH, /* init= */ bf_init_key, - /* cipher= */ bf_cbc_cipher, + /* cipher_update= */ bf_cbc_cipher_update, + /* cipher_final= */ nullptr, + /* update_aad= */ nullptr, /* cleanup= */ nullptr, /* ctrl= */ nullptr, }; @@ -586,7 +590,9 @@ /* ctx_size= */ sizeof(BF_KEY), /* flags= */ EVP_CIPH_CFB_MODE | EVP_CIPH_VARIABLE_LENGTH, /* init= */ bf_init_key, - /* cipher= */ bf_cfb_cipher, + /* cipher_update= */ bf_cfb_cipher_update, + /* cipher_final= */ nullptr, + /* update_aad= */ nullptr, /* cleanup= */ nullptr, /* ctrl= */ nullptr, };
diff --git a/decrepit/cast/cast.cc b/decrepit/cast/cast.cc index 37e4684..1fc8057 100644 --- a/decrepit/cast/cast.cc +++ b/decrepit/cast/cast.cc
@@ -371,8 +371,8 @@ return 1; } -static int cast_ecb_cipher(EVP_CIPHER_CTX *ctx, uint8_t *out, const uint8_t *in, - size_t len) { +static int cast_ecb_cipher_update(EVP_CIPHER_CTX *ctx, uint8_t *out, + const uint8_t *in, size_t len) { CAST_KEY *cast_key = reinterpret_cast<CAST_KEY *>(ctx->cipher_data); while (len >= CAST_BLOCK) { @@ -386,8 +386,8 @@ return 1; } -static int cast_cbc_cipher(EVP_CIPHER_CTX *ctx, uint8_t *out, const uint8_t *in, - size_t len) { +static int cast_cbc_cipher_update(EVP_CIPHER_CTX *ctx, uint8_t *out, + const uint8_t *in, size_t len) { CAST_KEY *cast_key = reinterpret_cast<CAST_KEY *>(ctx->cipher_data); CAST_cbc_encrypt(in, out, len, cast_key, ctx->iv, ctx->encrypt); return 1; @@ -401,7 +401,9 @@ /* ctx_size= */ sizeof(CAST_KEY), /* flags= */ EVP_CIPH_ECB_MODE | EVP_CIPH_VARIABLE_LENGTH, /* init= */ cast_init_key, - /* cipher= */ cast_ecb_cipher, + /* cipher_update= */ cast_ecb_cipher_update, + /* cipher_final= */ nullptr, + /* update_aad= */ nullptr, /* cleanup= */ nullptr, /* ctrl= */ nullptr, }; @@ -414,7 +416,9 @@ /* ctx_size= */ sizeof(CAST_KEY), /* flags= */ EVP_CIPH_CBC_MODE | EVP_CIPH_VARIABLE_LENGTH, /* init= */ cast_init_key, - /* cipher= */ cast_cbc_cipher, + /* cipher_update= */ cast_cbc_cipher_update, + /* cipher_final= */ nullptr, + /* update_aad= */ nullptr, /* cleanup= */ nullptr, /* ctrl= */ nullptr, };
diff --git a/decrepit/cfb/cfb.cc b/decrepit/cfb/cfb.cc index b7fb30a..0123a5f 100644 --- a/decrepit/cfb/cfb.cc +++ b/decrepit/cfb/cfb.cc
@@ -36,8 +36,8 @@ return 1; } -static int aes_cfb128_cipher(EVP_CIPHER_CTX *ctx, uint8_t *out, - const uint8_t *in, size_t len) { +static int aes_cfb128_cipher_update(EVP_CIPHER_CTX *ctx, uint8_t *out, + const uint8_t *in, size_t len) { if (!out || !in) { return 0; } @@ -59,7 +59,9 @@ /* ctx_size= */ sizeof(EVP_CFB_CTX), /* flags= */ EVP_CIPH_CFB_MODE, /* init= */ aes_cfb_init_key, - /* cipher= */ aes_cfb128_cipher, + /* cipher_update= */ aes_cfb128_cipher_update, + /* cipher_final= */ nullptr, + /* update_aad= */ nullptr, /* cleanup= */ nullptr, /* ctrl= */ nullptr, }; @@ -72,7 +74,9 @@ /* ctx_size= */ sizeof(EVP_CFB_CTX), /* flags= */ EVP_CIPH_CFB_MODE, /* init= */ aes_cfb_init_key, - /* cipher= */ aes_cfb128_cipher, + /* cipher_update= */ aes_cfb128_cipher_update, + /* cipher_final= */ nullptr, + /* update_aad= */ nullptr, /* cleanup= */ nullptr, /* ctrl= */ nullptr, }; @@ -85,7 +89,9 @@ /* ctx_size= */ sizeof(EVP_CFB_CTX), /* flags= */ EVP_CIPH_CFB_MODE, /* init= */ aes_cfb_init_key, - /* cipher= */ aes_cfb128_cipher, + /* cipher= */ aes_cfb128_cipher_update, + /* cipher_final= */ nullptr, + /* update_aad= */ nullptr, /* cleanup= */ nullptr, /* ctrl= */ nullptr, };
diff --git a/decrepit/xts/xts.cc b/decrepit/xts/xts.cc index 3b7e96c..adccf0e 100644 --- a/decrepit/xts/xts.cc +++ b/decrepit/xts/xts.cc
@@ -159,8 +159,8 @@ return 1; } -static int aes_xts_cipher(EVP_CIPHER_CTX *ctx, uint8_t *out, const uint8_t *in, - size_t len) { +static int aes_xts_cipher_update(EVP_CIPHER_CTX *ctx, uint8_t *out, + const uint8_t *in, size_t len) { EVP_AES_XTS_CTX *xctx = reinterpret_cast<EVP_AES_XTS_CTX *>(ctx->cipher_data); if (!xctx->xts.key1 || !xctx->xts.key2 || !out || !in || len < AES_BLOCK_SIZE || @@ -208,7 +208,9 @@ EVP_CIPH_ALWAYS_CALL_INIT | EVP_CIPH_CTRL_INIT | EVP_CIPH_CUSTOM_COPY, /* init= */ aes_xts_init_key, - /* cipher= */ aes_xts_cipher, + /* cipher_update= */ aes_xts_cipher_update, + /* cipher_final= */ nullptr, + /* update_aad= */ nullptr, /* cleanup= */ nullptr, /* ctrl= */ aes_xts_ctrl, };
diff --git a/include/openssl/cipher.h b/include/openssl/cipher.h index a5533ca..8b47369 100644 --- a/include/openssl/cipher.h +++ b/include/openssl/cipher.h
@@ -128,73 +128,101 @@ // Cipher operations. -// EVP_EncryptUpdate encrypts |in_len| bytes from |in| to |out|. The number -// of output bytes may be up to |in_len| plus the block length minus one and -// |out| must have sufficient space. The number of bytes actually output is -// written to |*out_len|. It returns one on success and zero otherwise. +// 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 +// the number of output bytes and returns one. Otherwise, it returns zero. // -// Note that the total output length across |EVP_EncryptUpdate| and -// |EVP_EncryptFinal_ex| will never exceed the sum of |in_len|. However, in -// ciphers whose the block size is not 1, such as CBC, individual calls to -// |EVP_EncryptUpdate| may output more or less than |in_len| bytes. In this -// case, the maximum output length is |in_len| plus the block size minus one. +// If |max_out| 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. // -// If |ctx| is an AEAD cipher, e.g. |EVP_aes_128_gcm|, and |out| is NULL, this -// function instead adds |in_len| bytes from |in| to the AAD and sets |*out_len| -// to |in_len|. The AAD must be fully specified in this way before this function -// is used to encrypt plaintext. -OPENSSL_EXPORT int EVP_EncryptUpdate(EVP_CIPHER_CTX *ctx, uint8_t *out, - int *out_len, const uint8_t *in, - int in_len); +// 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 +// call to |EVP_EncryptUpdate_ex| may output at most |in_len + block_size - 1| +// bytes. Additionally, the total output across all |EVP_EncryptUpdate_ex| and +// |EVP_EncryptFinal_ex2| calls will be at most the total input plus one byte, +// rounded up to a multiple of the block size. +OPENSSL_EXPORT int EVP_EncryptUpdate_ex(EVP_CIPHER_CTX *ctx, uint8_t *out, + size_t *out_len, size_t max_out_len, + const uint8_t *in, size_t in_len); -// EVP_EncryptFinal_ex writes at most a block of ciphertext (if block length is -// >1) to |out| and sets |*out_len| to the number of bytes written. If padding -// is enabled (the default) then standard padding is applied to create the -// final block. If padding is disabled (with |EVP_CIPHER_CTX_set_padding|) then -// any partial block remaining will cause an error. The function returns one on -// success and zero otherwise. -OPENSSL_EXPORT int EVP_EncryptFinal_ex(EVP_CIPHER_CTX *ctx, uint8_t *out, - int *out_len); - -// EVP_DecryptUpdate decrypts |in_len| bytes from |in| to |out|. The number of -// output bytes may be up to |in_len| plus the block length (if block length is -// >1) and |out| must have sufficient space. The number of bytes actually -// output is written to |*out_len|. It returns one on success and zero +// 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 +// 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 +// zero. The size of output buffer needed depends on the cipher and the number +// of bytes encrypted. +// +// 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. +// +// If padding is enabled (the default) and the block size is not 1, then +// standard padding is applied to create the final block. If padding is +// disabled (with |EVP_CIPHER_CTX_set_padding|) then any partial block +// remaining will cause an error. The function returns one on success and zero // otherwise. -// -// Note that the total output length across |EVP_DecryptUpdate| and -// |EVP_DecryptFinal_ex| will never exceed the sum of |in_len|. However, in -// ciphers whose the block size is not 1, such as CBC, individual calls to -// |EVP_DecryptUpdate| may output more or less than |in_len| bytes. In this -// case, the maximum output length is |in_len| plus the block size. -// -// If |ctx| is an AEAD cipher, e.g. |EVP_aes_128_gcm|, and |out| is NULL, this -// function instead adds |in_len| bytes from |in| to the AAD and sets |*out_len| -// to |in_len|. The AAD must be fully specified in this way before this function -// is used to decrypt ciphertext. -OPENSSL_EXPORT int EVP_DecryptUpdate(EVP_CIPHER_CTX *ctx, uint8_t *out, - int *out_len, const uint8_t *in, - int in_len); +OPENSSL_EXPORT int EVP_EncryptFinal_ex2(EVP_CIPHER_CTX *ctx, uint8_t *out, + size_t *out_len, size_t max_out_len); -// EVP_DecryptFinal_ex writes at most a block of ciphertext (if block length is -// >1) to |out| and sets |*out_len| to the number of bytes written. If padding -// is enabled (the default) then padding is removed from the final block. +// 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 +// the number of output bytes and returns one. Otherwise, it returns zero. // -// WARNING: it is unsafe to call this function with unauthenticated -// ciphertext if padding is enabled. -OPENSSL_EXPORT int EVP_DecryptFinal_ex(EVP_CIPHER_CTX *ctx, uint8_t *out, - int *out_len); +// If |max_out| 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. +// +// 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 +// call to |EVP_DecryptUpdate_ex| may output at most |in_len + block_size| +// bytes. Additionally, the total output across all |EVP_DecryptUpdate_ex| and +// |EVP_DecryptFinal_ex2| calls will be at most the total input. +// +// WARNING: if the cipher is an AEAD cipher, decrypted data should not be +// parsed or otherwise processed until success has been returned by +// |EVP_EncryptFinal_ex2|. +OPENSSL_EXPORT int EVP_DecryptUpdate_ex(EVP_CIPHER_CTX *ctx, uint8_t *out, + size_t *out_len, size_t max_out_len, + const uint8_t *in, size_t in_len); -// EVP_CipherUpdate calls either |EVP_EncryptUpdate| or |EVP_DecryptUpdate| -// depending on how |ctx| has been setup. -OPENSSL_EXPORT int EVP_CipherUpdate(EVP_CIPHER_CTX *ctx, uint8_t *out, - int *out_len, const uint8_t *in, - int 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 +// 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 +// zero. The size of output buffer needed depends on the cipher and the number +// of bytes decrypted. +// +// 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. +// +// If padding is enabled (the default) and the block size is not 1, then +// standard padding is removed from the final block. +// +// WARNING: it is unsafe to call this function after decrypting unauthenticated +// ciphertext if padding is enabled and the block size is not 1 ("padding +// oracle"). +OPENSSL_EXPORT int EVP_DecryptFinal_ex2(EVP_CIPHER_CTX *ctx, uint8_t *out, + size_t *out_len, size_t max_out_len); -// EVP_CipherFinal_ex calls either |EVP_EncryptFinal_ex| or -// |EVP_DecryptFinal_ex| depending on how |ctx| has been setup. -OPENSSL_EXPORT int EVP_CipherFinal_ex(EVP_CIPHER_CTX *ctx, uint8_t *out, - int *out_len); +// EVP_CipherUpdate_ex calls either |EVP_EncryptUpdate_ex| or +// |EVP_DecryptUpdate_ex| depending on how |ctx| has been setup. +OPENSSL_EXPORT int EVP_CipherUpdate_ex(EVP_CIPHER_CTX *ctx, uint8_t *out, + size_t *out_len, size_t max_out_len, + const uint8_t *in, size_t in_len); + +// EVP_CipherUpdateAAD adds |in_len| bytes from |in| to the AAD. The AAD must +// be fully specified in this way before any plaintext or ciphertext is +// supplied to the other functions. Please consider moving to the |EVP_AEAD| +// APIs instead. +OPENSSL_EXPORT int EVP_CipherUpdateAAD(EVP_CIPHER_CTX *ctx, const uint8_t *in, + size_t in_len); + +// EVP_CipherFinal_ex2 calls either |EVP_EncryptFinal_ex2| or +// |EVP_DecryptFinal_ex2| depending on how |ctx| has been setup. +OPENSSL_EXPORT int EVP_CipherFinal_ex2(EVP_CIPHER_CTX *ctx, uint8_t *out, + size_t *out_len, size_t max_out_len); // Cipher context accessors. @@ -391,18 +419,81 @@ const EVP_CIPHER *cipher, const uint8_t *key, const uint8_t *iv); +// EVP_CipherUpdate does the same as |EVP_CipherUpdate_ex|, except that no +// output size is given and thus no bounds checking is performed. +// +// Additionally, if |ctx| is an AEAD cipher, e.g. |EVP_aes_128_gcm|, and |out| +// is NULL, this function instead behaves like |EVP_CipherUpdateAAD|. +// +// WARNING: This function does not check bounds on |out|, and correctly sizing +// the output buffer is difficult. Use |EVP_CipherUpdate_ex| or +// |EVP_CipherUpdateAAD| instead. +OPENSSL_EXPORT int EVP_CipherUpdate(EVP_CIPHER_CTX *ctx, uint8_t *out, + int *out_len, const uint8_t *in, + int in_len); + +// EVP_EncryptUpdate does the same as |EVP_EncryptUpdate_ex|, except that no +// output size is given and thus no bounds checking is performed. +// +// Additionally, if |ctx| is an AEAD cipher, e.g. |EVP_aes_128_gcm|, and |out| +// is NULL, this function instead behaves like |EVP_CipherUpdateAAD|. +// +// WARNING: This function does not check bounds on |out|, and correctly sizing +// the output buffer is difficult. Use |EVP_EncryptUpdate_ex| or +// |EVP_CipherUpdateAAD| instead. +OPENSSL_EXPORT int EVP_EncryptUpdate(EVP_CIPHER_CTX *ctx, uint8_t *out, + int *out_len, const uint8_t *in, + int in_len); + +// EVP_DecryptUpdate does the same as |EVP_DecryptUpdate_ex|, except that no +// output size is given and thus no bounds checking is performed. +// +// Additionally, if |ctx| is an AEAD cipher, e.g. |EVP_aes_128_gcm|, and |out| +// is NULL, this function instead behaves like |EVP_CipherUpdateAAD|. +// +// WARNING: This function does not check bounds on out, and correctly sizing +// the output buffer is difficult. Use |EVP_DecryptUpdate_ex| or +// |EVP_CipherUpdateAAD| instead. +OPENSSL_EXPORT int EVP_DecryptUpdate(EVP_CIPHER_CTX *ctx, uint8_t *out, + int *out_len, const uint8_t *in, + int in_len); + // EVP_CipherFinal calls |EVP_CipherFinal_ex|. OPENSSL_EXPORT int EVP_CipherFinal(EVP_CIPHER_CTX *ctx, uint8_t *out, int *out_len); +// EVP_CipherFinal_ex does the same as |EVP_CipherFinal_ex2|, except that no +// output size is given and thus no bounds checking is performed. +// +// WARNING: This function does not check bounds on out, and correctly sizing +// the output buffer is difficult. Use |EVP_CipherFinal_ex2| instead. +OPENSSL_EXPORT int EVP_CipherFinal_ex(EVP_CIPHER_CTX *ctx, uint8_t *out, + int *out_len); + // EVP_EncryptFinal calls |EVP_EncryptFinal_ex|. OPENSSL_EXPORT int EVP_EncryptFinal(EVP_CIPHER_CTX *ctx, uint8_t *out, int *out_len); +// EVP_EncryptFinal_ex does the same as |EVP_EncryptFinal_ex2|, except that no +// output size is given and thus no bounds checking is performed. +// +// WARNING: This function does not check bounds on out, and correctly sizing +// the output buffer is difficult. Use |EVP_EncryptFinal_ex2| instead. +OPENSSL_EXPORT int EVP_EncryptFinal_ex(EVP_CIPHER_CTX *ctx, uint8_t *out, + int *out_len); + // EVP_DecryptFinal calls |EVP_DecryptFinal_ex|. OPENSSL_EXPORT int EVP_DecryptFinal(EVP_CIPHER_CTX *ctx, uint8_t *out, int *out_len); +// EVP_DecryptFinal_ex does the same as |EVP_DecryptFinal_ex2|, except that no +// output size is given and thus no bounds checking is performed. +// +// WARNING: This function does not check bounds on out, and correctly sizing +// the output buffer is difficult. Use |EVP_DecryptFinal_ex2| instead. +OPENSSL_EXPORT int EVP_DecryptFinal_ex(EVP_CIPHER_CTX *ctx, uint8_t *out, + int *out_len); + // EVP_Cipher historically exposed an internal implementation detail of |ctx| // and should not be used. Use |EVP_CipherUpdate| and |EVP_CipherFinal_ex| // instead.