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.