aboutsummaryrefslogtreecommitdiff
path: root/cc/aead
diff options
context:
space:
mode:
authorambrosin <ambrosin@google.com>2023-05-03 07:35:42 -0700
committerCopybara-Service <copybara-worker@google.com>2023-05-03 07:37:08 -0700
commit723cb4ca67a8ee5cc57efd50ed0585771c7280cc (patch)
tree11d6f2ca51065801d049bf4afe74b5065828a942 /cc/aead
parent264e9ebfafce77f3e610a4b7914ec9d5314366b1 (diff)
downloadtink-723cb4ca67a8ee5cc57efd50ed0585771c7280cc.tar.gz
Avoid copying EVP_CIPHER_CTX from partial context in CordAesGcmBoringSsl if OpenSSL >= 3.0
Copying the context fails with OpenSSL 3.0, which doesn't provide a `dupctx` function for aead ciphers (see [1], [2]). * Rename `context_` into `partial_context_`. * Introduce helper functions that take care of trying to crated a context starting from `partial_context_` for BoringSSL and OpenSSL 1.1.1, and creating the context from scratch with OpenSSL 3.0+. * Pass `const util::SecretData&` to `CordAesGcmBoringSsl::New` [1] https://github.com/openssl/openssl/blob/eb52450f5151e8e78743ab05de21a344823316f5/crypto/evp/evp_enc.c#L1427 [2] https://github.com/openssl/openssl/blob/cac250755efd0c40cc6127a0e4baceb8d226c7e3/providers/implementations/include/prov/ciphercommon_aead.h#L30 PiperOrigin-RevId: 529085627
Diffstat (limited to 'cc/aead')
-rw-r--r--cc/aead/internal/BUILD.bazel2
-rw-r--r--cc/aead/internal/CMakeLists.txt2
-rw-r--r--cc/aead/internal/cord_aes_gcm_boringssl.cc147
-rw-r--r--cc/aead/internal/cord_aes_gcm_boringssl.h17
4 files changed, 121 insertions, 47 deletions
diff --git a/cc/aead/internal/BUILD.bazel b/cc/aead/internal/BUILD.bazel
index 7671b70c6..9cccade56 100644
--- a/cc/aead/internal/BUILD.bazel
+++ b/cc/aead/internal/BUILD.bazel
@@ -61,13 +61,11 @@ cc_library(
"//internal:ssl_unique_ptr",
"//subtle:random",
"//subtle:subtle_util",
- "//util:errors",
"//util:secret_data",
"//util:status",
"//util:statusor",
"@boringssl//:crypto",
"@com_google_absl//absl/status",
- "@com_google_absl//absl/strings",
"@com_google_absl//absl/strings:cord",
],
)
diff --git a/cc/aead/internal/CMakeLists.txt b/cc/aead/internal/CMakeLists.txt
index 67e2b1aec..4a6cc413d 100644
--- a/cc/aead/internal/CMakeLists.txt
+++ b/cc/aead/internal/CMakeLists.txt
@@ -53,14 +53,12 @@ tink_cc_library(
DEPS
tink::aead::internal::aead_util
absl::status
- absl::strings
absl::cord
crypto
tink::aead::cord_aead
tink::internal::ssl_unique_ptr
tink::subtle::random
tink::subtle::subtle_util
- tink::util::errors
tink::util::secret_data
tink::util::status
tink::util::statusor
diff --git a/cc/aead/internal/cord_aes_gcm_boringssl.cc b/cc/aead/internal/cord_aes_gcm_boringssl.cc
index 321e9bf4c..4356bee45 100644
--- a/cc/aead/internal/cord_aes_gcm_boringssl.cc
+++ b/cc/aead/internal/cord_aes_gcm_boringssl.cc
@@ -17,22 +17,18 @@
#include "tink/aead/internal/cord_aes_gcm_boringssl.h"
#include <cstdint>
-#include <iterator>
#include <memory>
#include <string>
#include <utility>
-#include <vector>
#include "absl/status/status.h"
#include "absl/strings/cord.h"
#include "openssl/evp.h"
-#include "openssl/err.h"
#include "tink/aead/cord_aead.h"
#include "tink/aead/internal/aead_util.h"
#include "tink/internal/ssl_unique_ptr.h"
#include "tink/subtle/random.h"
#include "tink/subtle/subtle_util.h"
-#include "tink/util/errors.h"
#include "tink/util/secret_data.h"
#include "tink/util/status.h"
#include "tink/util/statusor.h"
@@ -47,8 +43,8 @@ constexpr int kTagSizeInBytes = 16;
// Set the IV `iv` for the given `context`. if `encryption` is true, set the
// context for encryption, and for decryption otherwise.
-util::Status SetIv(EVP_CIPHER_CTX* context, absl::string_view iv,
- bool encryption) {
+util::Status SetIvAndDirection(EVP_CIPHER_CTX* context, absl::string_view iv,
+ bool encryption) {
const int encryption_flag = encryption ? 1 : 0;
// Set the IV size.
if (EVP_CIPHER_CTX_ctrl(context, EVP_CTRL_GCM_SET_IVLEN, iv.size(),
@@ -67,28 +63,100 @@ util::Status SetIv(EVP_CIPHER_CTX* context, absl::string_view iv,
return util::OkStatus();
}
+#if defined(OPENSSL_IS_BORINGSSL) || OPENSSL_VERSION_NUMBER < 0x30000000L
+// Returns a new EVP_CIPHER_CTX for encryption (`encryption` == true) or
+// decryption (`encryption` == false). It tries to skip part of the
+// initialization copying `partial_context`.
+util::StatusOr<internal::SslUniquePtr<EVP_CIPHER_CTX>> NewContextFromPartial(
+ EVP_CIPHER_CTX* partial_context, absl::string_view iv, bool encryption) {
+ internal::SslUniquePtr<EVP_CIPHER_CTX> context(EVP_CIPHER_CTX_new());
+ if (context == nullptr) {
+ return util::Status(absl::StatusCode::kInternal,
+ "EVP_CIPHER_CTX_new failed");
+ }
+ // Try making a copy of `partial_context` to skip some pre-computations.
+ //
+ // NOTE: With BoringSSL and OpenSSL 1.1.1 EVP_CIPHER_CTX_copy makes a copy
+ // of the `cipher_data` field of `context` as well, which contains the key
+ // material and IV (see [1] and [2]).
+ //
+ // [1]https://github.com/google/boringssl/blob/4c8bcf0da2951cacd8ed8eaa7fd2df4b22fca23b/crypto/fipsmodule/cipher/cipher.c#L116
+ // [2]https://github.com/openssl/openssl/blob/830bf8e1e4749ad65c51b6a1d0d769ae689404ba/crypto/evp/evp_enc.c#L703
+ if (EVP_CIPHER_CTX_copy(context.get(), partial_context) <= 0) {
+ return util::Status(absl::StatusCode::kInternal,
+ "EVP_CIPHER_CTX_copy failed");
+ }
+ util::Status res =
+ SetIvAndDirection(context.get(), iv, /*encryption=*/encryption);
+ if (!res.ok()) {
+ return res;
+ }
+ return std::move(context);
+}
+#else
+// Returns a new EVP_CIPHER_CTX for encryption (`encryption` == true) or
+// decryption (`encryption` == false) with given `key` and `iv`.
+//
+// NOTE: Copying the context fails with OpenSSL 3.0, which doesn't provide a
+// `dupctx` function for aead ciphers (see [1], [2]).
+//
+// [1]https://github.com/openssl/openssl/blob/eb52450f5151e8e78743ab05de21a344823316f5/crypto/evp/evp_enc.c#L1427
+// [2]https://github.com/openssl/openssl/blob/cac250755efd0c40cc6127a0e4baceb8d226c7e3/providers/implementations/include/prov/ciphercommon_aead.h#L30
+util::StatusOr<internal::SslUniquePtr<EVP_CIPHER_CTX>> NewContext(
+ const util::SecretData& key, absl::string_view iv, bool encryption) {
+ internal::SslUniquePtr<EVP_CIPHER_CTX> context(EVP_CIPHER_CTX_new());
+ if (context == nullptr) {
+ return util::Status(absl::StatusCode::kInternal,
+ "EVP_CIPHER_CTX_new failed");
+ }
+ util::StatusOr<const EVP_CIPHER*> cipher =
+ internal::GetAesGcmCipherForKeySize(key.size());
+ if (!cipher.ok()) {
+ return cipher.status();
+ }
+ if (EVP_CipherInit_ex(context.get(), *cipher, /*impl=*/nullptr,
+ reinterpret_cast<const uint8_t*>(key.data()),
+ /*iv=*/nullptr, /*enc=*/1) <= 0) {
+ return util::Status(absl::StatusCode::kInternal,
+ "Context initialization failed");
+ }
+ util::Status res =
+ SetIvAndDirection(context.get(), iv, /*encryption=*/encryption);
+ if (!res.ok()) {
+ return res;
+ }
+ return std::move(context);
+}
+#endif
+
} // namespace
-util::StatusOr<std::unique_ptr<CordAead> > CordAesGcmBoringSsl::New(
- util::SecretData key_value) {
+util::StatusOr<std::unique_ptr<CordAead>> CordAesGcmBoringSsl::New(
+ const util::SecretData& key_value) {
util::StatusOr<const EVP_CIPHER*> cipher =
internal::GetAesGcmCipherForKeySize(key_value.size());
if (!cipher.ok()) {
return cipher.status();
}
- internal::SslUniquePtr<EVP_CIPHER_CTX> context(EVP_CIPHER_CTX_new());
- // Initialize the cipher now to have some precomputations on the key. The
- // direction (enc/dec) is not important since it will be overwritten later.
- if (EVP_CipherInit_ex(context.get(), *cipher, /*engine=*/nullptr,
+ internal::SslUniquePtr<EVP_CIPHER_CTX> partial_context(EVP_CIPHER_CTX_new());
+ // Initialize a partial context for the cipher to allow OpenSSL/BoringSSL
+ // making some precomputations on the key. Encrypt and Decrypt will try making
+ // a copy of this context to avoid doing the same initializations again and to
+ // guarantee thread safety.
+ //
+ // NOTE: It doesn't matter at this point if we set the direction to encryption
+ // or decryption, it will be overwritten later any time we call
+ // EVP_CipherInit_ex.
+ if (EVP_CipherInit_ex(partial_context.get(), *cipher, /*engine=*/nullptr,
reinterpret_cast<const uint8_t*>(&key_value[0]),
/*iv=*/nullptr, /*enc=*/1) <= 0) {
return util::Status(absl::StatusCode::kInternal,
"Context initialization failed");
}
- std::unique_ptr<CordAead> aead =
- absl::WrapUnique(new CordAesGcmBoringSsl(std::move(context)));
+ std::unique_ptr<CordAead> aead = absl::WrapUnique(
+ new CordAesGcmBoringSsl(std::move(partial_context), key_value));
return std::move(aead);
}
@@ -96,18 +164,21 @@ util::StatusOr<absl::Cord> CordAesGcmBoringSsl::Encrypt(
absl::Cord plaintext, absl::Cord associated_data) const {
std::string iv = subtle::Random::GetRandomBytes(kIvSizeInBytes);
- internal::SslUniquePtr<EVP_CIPHER_CTX> context(EVP_CIPHER_CTX_new());
- EVP_CIPHER_CTX_copy(context.get(), context_.get());
-
- util::Status res = SetIv(context.get(), iv, /*encryption=*/true);
- if (!res.ok()) {
- return res;
+#if defined(OPENSSL_IS_BORINGSSL) || OPENSSL_VERSION_NUMBER < 0x30000000L
+ util::StatusOr<internal::SslUniquePtr<EVP_CIPHER_CTX>> context =
+ NewContextFromPartial(partial_context_.get(), iv, /*encryption=*/true);
+#else
+ util::StatusOr<internal::SslUniquePtr<EVP_CIPHER_CTX>> context =
+ NewContext(key_, iv, /*encryption=*/true);
+#endif
+ if (!context.ok()) {
+ return context.status();
}
int len = 0;
// Process AAD.
for (auto ad_chunk : associated_data.Chunks()) {
- if (!EVP_EncryptUpdate(context.get(), /*out=*/nullptr, &len,
+ if (!EVP_EncryptUpdate(context->get(), /*out=*/nullptr, &len,
reinterpret_cast<const uint8_t*>(ad_chunk.data()),
ad_chunk.size())) {
return util::Status(absl::StatusCode::kInternal, "Encryption failed");
@@ -124,7 +195,7 @@ util::StatusOr<absl::Cord> CordAesGcmBoringSsl::Encrypt(
for (auto plaintext_chunk : plaintext.Chunks()) {
if (!EVP_EncryptUpdate(
- context.get(),
+ context->get(),
reinterpret_cast<uint8_t*>(&(buffer[ciphertext_buffer_offset])),
&len, reinterpret_cast<const uint8_t*>(plaintext_chunk.data()),
plaintext_chunk.size())) {
@@ -132,13 +203,14 @@ util::StatusOr<absl::Cord> CordAesGcmBoringSsl::Encrypt(
}
ciphertext_buffer_offset += plaintext_chunk.size();
}
- if (!EVP_EncryptFinal_ex(context.get(), nullptr, &len)) {
+ if (!EVP_EncryptFinal_ex(context->get(), nullptr, &len)) {
return util::Status(absl::StatusCode::kInternal, "Encryption failed");
}
std::string tag;
subtle::ResizeStringUninitialized(&tag, kTagSizeInBytes);
- if (!EVP_CIPHER_CTX_ctrl(context.get(), EVP_CTRL_GCM_GET_TAG, kTagSizeInBytes,
+ if (!EVP_CIPHER_CTX_ctrl(context->get(), EVP_CTRL_GCM_GET_TAG,
+ kTagSizeInBytes,
reinterpret_cast<uint8_t*>(&tag[0]))) {
return util::Status(absl::StatusCode::kInternal, "Encryption failed");
}
@@ -157,23 +229,26 @@ util::StatusOr<absl::Cord> CordAesGcmBoringSsl::Decrypt(
return util::Status(absl::StatusCode::kInternal, "Ciphertext too short");
}
- // First bytes contain IV.
+ // First bytes contain the IV.
std::string iv = std::string(ciphertext.Subcord(0, kIvSizeInBytes));
absl::Cord raw_ciphertext = ciphertext.Subcord(
kIvSizeInBytes, ciphertext.size() - kIvSizeInBytes - kTagSizeInBytes);
- internal::SslUniquePtr<EVP_CIPHER_CTX> context(EVP_CIPHER_CTX_new());
- EVP_CIPHER_CTX_copy(context.get(), context_.get());
-
- util::Status res = SetIv(context.get(), iv, /*encryption=*/false);
- if (!res.ok()) {
- return res;
+#if defined(OPENSSL_IS_BORINGSSL) || OPENSSL_VERSION_NUMBER < 0x30000000L
+ util::StatusOr<internal::SslUniquePtr<EVP_CIPHER_CTX>> context =
+ NewContextFromPartial(partial_context_.get(), iv, /*encryption=*/false);
+#else
+ util::StatusOr<internal::SslUniquePtr<EVP_CIPHER_CTX>> context =
+ NewContext(key_, iv, /*encryption=*/false);
+#endif
+ if (!context.ok()) {
+ return context.status();
}
int len = 0;
// Process associated data.
for (auto ad_chunk : associated_data.Chunks()) {
- if (!EVP_DecryptUpdate(context.get(), nullptr, &len,
+ if (!EVP_DecryptUpdate(context->get(), /*out=*/nullptr, &len,
reinterpret_cast<const uint8_t*>(ad_chunk.data()),
ad_chunk.size())) {
return util::Status(absl::StatusCode::kInternal, "Decryption failed");
@@ -192,7 +267,7 @@ util::StatusOr<absl::Cord> CordAesGcmBoringSsl::Decrypt(
});
for (auto ct_chunk : raw_ciphertext.Chunks()) {
- if (!EVP_DecryptUpdate(context.get(),
+ if (!EVP_DecryptUpdate(context->get(),
reinterpret_cast<uint8_t*>(
&plaintext_buffer[plaintext_buffer_offset]),
&len,
@@ -207,13 +282,13 @@ util::StatusOr<absl::Cord> CordAesGcmBoringSsl::Decrypt(
std::string tag = std::string(
ciphertext.Subcord(ciphertext.size() - kTagSizeInBytes, kTagSizeInBytes));
- if (!EVP_CIPHER_CTX_ctrl(context.get(), EVP_CTRL_GCM_SET_TAG, kTagSizeInBytes,
- &tag[0])) {
+ if (!EVP_CIPHER_CTX_ctrl(context->get(), EVP_CTRL_GCM_SET_TAG,
+ kTagSizeInBytes, &tag[0])) {
return util::Status(absl::StatusCode::kInternal,
"Could not set authentication tag");
}
// Verify authentication tag.
- if (!EVP_DecryptFinal_ex(context.get(), nullptr, &len)) {
+ if (!EVP_DecryptFinal_ex(context->get(), nullptr, &len)) {
return util::Status(absl::StatusCode::kInternal, "Authentication failed");
}
return result;
diff --git a/cc/aead/internal/cord_aes_gcm_boringssl.h b/cc/aead/internal/cord_aes_gcm_boringssl.h
index 9dcd2881e..273b52329 100644
--- a/cc/aead/internal/cord_aes_gcm_boringssl.h
+++ b/cc/aead/internal/cord_aes_gcm_boringssl.h
@@ -20,12 +20,10 @@
#include <memory>
#include <utility>
-#include "absl/strings/string_view.h"
#include "openssl/evp.h"
#include "tink/aead/cord_aead.h"
#include "tink/internal/ssl_unique_ptr.h"
#include "tink/util/secret_data.h"
-#include "tink/util/status.h"
#include "tink/util/statusor.h"
namespace crypto {
@@ -35,7 +33,7 @@ namespace internal {
class CordAesGcmBoringSsl : public CordAead {
public:
static crypto::tink::util::StatusOr<std::unique_ptr<CordAead>> New(
- util::SecretData key_value);
+ const util::SecretData& key_value);
crypto::tink::util::StatusOr<absl::Cord> Encrypt(
absl::Cord plaintext, absl::Cord associated_data) const override;
@@ -44,10 +42,15 @@ class CordAesGcmBoringSsl : public CordAead {
absl::Cord ciphertext, absl::Cord associated_data) const override;
private:
- explicit CordAesGcmBoringSsl(internal::SslUniquePtr<EVP_CIPHER_CTX> context)
- : context_(std::move(context)) {}
-
- internal::SslUniquePtr<EVP_CIPHER_CTX> context_;
+ explicit CordAesGcmBoringSsl(
+ internal::SslUniquePtr<EVP_CIPHER_CTX> partial_context,
+ const util::SecretData& key)
+ : partial_context_(std::move(partial_context)), key_(key) {}
+
+ // Partially-initialized EVP_CIPHER_CTX context that is copied for every
+ // Encrypt/Decrypt operation.
+ internal::SslUniquePtr<EVP_CIPHER_CTX> partial_context_;
+ util::SecretData key_;
};
} // namespace internal