From bcc79968ecbc179c7230f7843564896fb0ffa2f8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tobias=20Nie=C3=9Fen?= Date: Sat, 17 May 2025 16:16:22 +0100 Subject: [PATCH] crypto: forward auth tag to OpenSSL immediately This change simplifies the AEAD implementation. Instead of storing the authentication tag when the user calls `setAuthTag()` and passing it to OpenSSL later in `MaybePassAuthTagToOpenSSL()`, the modified code forwards it to OpenSSL from within `setAuthTag()` already, removing the need to store it. For clarity, I have also renamed the possible `AuthTagState` values to better reflect the actual state of the authentication tag. I assume that we did not originally do this due to issues with some old versions of OpenSSL when reordering certain function calls, but even with the recent additions I made to the relevant test (namely, 1ef99237f566c89247660b935667fbf4efa606ce and 53944c44629b651425f8d18c4d33f9bc99657d37), it seems to pass in both OpenSSL 3 and OpenSSL 1.1.1 with this simplification. --- src/crypto/crypto_cipher.cc | 49 +++++++++---------------------------- src/crypto/crypto_cipher.h | 10 +++----- 2 files changed, 16 insertions(+), 43 deletions(-) diff --git a/src/crypto/crypto_cipher.cc b/src/crypto/crypto_cipher.cc index 40c091d4ce2d29..5ed2f8b35a1e2f 100644 --- a/src/crypto/crypto_cipher.cc +++ b/src/crypto/crypto_cipher.cc @@ -514,9 +514,9 @@ void CipherBase::GetAuthTag(const FunctionCallbackInfo& args) { ASSIGN_OR_RETURN_UNWRAP(&cipher, args.This()); // Only callable after Final and if encrypting. - if (cipher->ctx_ || - cipher->kind_ != kCipher || - cipher->auth_tag_len_ == kNoAuthTagLength) { + if (cipher->ctx_ || cipher->kind_ != kCipher || + cipher->auth_tag_len_ == kNoAuthTagLength || + cipher->auth_tag_state_ != kAuthTagComputed) { return; } @@ -577,29 +577,16 @@ void CipherBase::SetAuthTag(const FunctionCallbackInfo& args) { } cipher->auth_tag_len_ = tag_len; - cipher->auth_tag_state_ = kAuthTagKnown; - CHECK_LE(cipher->auth_tag_len_, sizeof(cipher->auth_tag_)); + CHECK_LE(cipher->auth_tag_len_, ncrypto::Cipher::MAX_AUTH_TAG_LENGTH); - memset(cipher->auth_tag_, 0, sizeof(cipher->auth_tag_)); - auth_tag.CopyTo(cipher->auth_tag_, cipher->auth_tag_len_); + if (!cipher->ctx_.setAeadTag({auth_tag.data(), cipher->auth_tag_len_})) { + return args.GetReturnValue().Set(false); + } + cipher->auth_tag_state_ = kAuthTagSetByUser; args.GetReturnValue().Set(true); } -bool CipherBase::MaybePassAuthTagToOpenSSL() { - if (auth_tag_state_ == kAuthTagKnown) { - ncrypto::Buffer buffer{ - .data = auth_tag_, - .len = auth_tag_len_, - }; - if (!ctx_.setAeadTag(buffer)) { - return false; - } - auth_tag_state_ = kAuthTagPassedToOpenSSL; - } - return true; -} - bool CipherBase::SetAAD( const ArrayBufferOrViewContents& data, int plaintext_len) { @@ -622,10 +609,6 @@ bool CipherBase::SetAAD( return false; } - if (kind_ == kDecipher && !MaybePassAuthTagToOpenSSL()) { - return false; - } - ncrypto::Buffer buffer{ .data = nullptr, .len = static_cast(plaintext_len), @@ -670,12 +653,6 @@ CipherBase::UpdateResult CipherBase::Update( return kErrorMessageSize; } - // Pass the authentication tag to OpenSSL if possible. This will only happen - // once, usually on the first update. - if (kind_ == kDecipher && IsAuthenticatedMode()) { - CHECK(MaybePassAuthTagToOpenSSL()); - } - const int block_size = ctx_.getBlockSize(); CHECK_GT(block_size, 0); if (len + block_size > INT_MAX) return kErrorState; @@ -777,16 +754,11 @@ bool CipherBase::Final(std::unique_ptr* out) { static_cast(ctx_.getBlockSize()), BackingStoreInitializationMode::kUninitialized); - if (kind_ == kDecipher && - Cipher::FromCtx(ctx_).isSupportedAuthenticatedMode()) { - MaybePassAuthTagToOpenSSL(); - } - #if (OPENSSL_VERSION_NUMBER < 0x30000000L) // OpenSSL v1.x doesn't verify the presence of the auth tag so do // it ourselves, see https://github.com/nodejs/node/issues/45874. if (kind_ == kDecipher && ctx_.isChaCha20Poly1305() && - auth_tag_state_ != kAuthTagPassedToOpenSSL) { + auth_tag_state_ != kAuthTagSetByUser) { return false; } #endif @@ -824,6 +796,9 @@ bool CipherBase::Final(std::unique_ptr* out) { } ok = ctx_.getAeadTag(auth_tag_len_, reinterpret_cast(auth_tag_)); + if (ok) { + auth_tag_state_ = kAuthTagComputed; + } } } diff --git a/src/crypto/crypto_cipher.h b/src/crypto/crypto_cipher.h index 168943a890bda7..006d18a7118761 100644 --- a/src/crypto/crypto_cipher.h +++ b/src/crypto/crypto_cipher.h @@ -38,8 +38,8 @@ class CipherBase : public BaseObject { }; enum AuthTagState { kAuthTagUnknown, - kAuthTagKnown, - kAuthTagPassedToOpenSSL + kAuthTagSetByUser, + kAuthTagComputed, }; static const unsigned kNoAuthTagLength = static_cast(-1); @@ -64,10 +64,8 @@ class CipherBase : public BaseObject { bool SetAutoPadding(bool auto_padding); bool IsAuthenticatedMode() const; - bool SetAAD( - const ArrayBufferOrViewContents& data, - int plaintext_len); - bool MaybePassAuthTagToOpenSSL(); + bool SetAAD(const ArrayBufferOrViewContents& data, + int plaintext_len); static void New(const v8::FunctionCallbackInfo& args); static void Update(const v8::FunctionCallbackInfo& args);