This is an automated email from the ASF dual-hosted git repository.

wzhou pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/impala.git

commit a61b90f86091ed9f190fd3a23d64ae99f887e411
Author: pranavyl <[email protected]>
AuthorDate: Fri Jun 23 23:05:21 2023 -0700

    IMPALA-13039: AES Encryption/ Decryption Support in Impala
    
    AES (Advanced Encryption Standard) crypto functions are
    widely recognized and respected encryption algorithm used to protect
    sensitive data which operate by transforming plaintext data into
    ciphertext using a symmetric key, ensuring confidentiality and
    integrity. This standard specifies the Rijndael algorithm, a symmetric
    block cipher that can process data blocks of 128 bits, using cipher
    keys with lengths of 128 and 256 bits. The patch makes use of the
    EVP_*() algorithms from the OpenSSL library.
    
    The patch includes:
    1. AES-GCM, AES-CTR, and AES-CFB encryption functionalities and
    AES-GCM, AES-ECB, AES-CTR, and AES-CFB decryption functionalities.
    2. Support for both 128-bit and 256-bit key sizes for GCM and ECB modes.
    3. Enhancements to EncryptionKey class to accommodate various AES modes.
    
    The aes_encrypt() and aes_decrypt() functions serve as entry
    points for encryption and decryption operations, handling
    encryption and decryption based on user-provided keys, AES modes,
    and initialization vectors (IVs). The implementation includes key
    length validation and IV vector size checks to ensure data
    integrity and confidentiality.
    
    Multiple AES modes: GCM, CFB, CTR for encryption, and GCM, CFB, CTR
    and ECB for decryption are supported to provide flexibility and
    compatibility with various use cases and OpenSSL features. AES-GCM
    is set as the default mode due to its strong security properties.
    AES-CTR and AES-CFB are provided as fallbacks for environments where
    AES-GCM may not be supported. Note that AES-GCM is not available in
    OpenSSL versions prior to 1.0.1, so having multiple methods ensures
    broader compatibility.
    
    Testing: The patch is thouroughly tested and the tests are included in
    exprs.test.
    
    Change-Id: I3902f2b1d95da4d06995cbd687e79c48e16190c9
    Reviewed-on: http://gerrit.cloudera.org:8080/20447
    Reviewed-by: Daniel Becker <[email protected]>
    Tested-by: Impala Public Jenkins <[email protected]>
---
 be/src/exprs/string-functions-ir.cc                |  92 +++--
 be/src/exprs/string-functions.cc                   | 183 +++++++++
 be/src/exprs/string-functions.h                    |  16 +-
 be/src/runtime/tmp-file-mgr.cc                     |   2 +-
 be/src/udf/udf-ir.cc                               |   2 +-
 be/src/util/openssl-util-test.cc                   |  57 ++-
 be/src/util/openssl-util.cc                        | 240 ++++++++---
 be/src/util/openssl-util.h                         | 124 ++++--
 common/function-registry/impala_functions.py       |   6 +
 .../queries/QueryTest/encryption_exprs.test        | 456 +++++++++++++++++++++
 tests/query_test/test_exprs.py                     |   6 +
 11 files changed, 1048 insertions(+), 136 deletions(-)

diff --git a/be/src/exprs/string-functions-ir.cc 
b/be/src/exprs/string-functions-ir.cc
index 0a2319483..e9803f809 100644
--- a/be/src/exprs/string-functions-ir.cc
+++ b/be/src/exprs/string-functions-ir.cc
@@ -155,8 +155,8 @@ StringVal StringFunctions::Space(FunctionContext* context, 
const BigIntVal& len)
   if (len.val <= 0) return StringVal();
   if (len.val > StringVal::MAX_LENGTH) {
     context->SetError(Substitute(ERROR_CHARACTER_LIMIT_EXCEEDED,
-         "space() result",
-         PrettyPrinter::Print(StringVal::MAX_LENGTH, TUnit::BYTES)).c_str());
+        "space() result",
+        PrettyPrinter::Print(StringVal::MAX_LENGTH, TUnit::BYTES)).c_str());
     return StringVal::null();
   }
   StringVal result(context, len.val);
@@ -259,7 +259,7 @@ StringVal StringFunctions::Rpad(FunctionContext* context, 
const StringVal& str,
 
 IntVal StringFunctions::Length(FunctionContext* context, const StringVal& str) 
{
   if (str.is_null) return IntVal::null();
-  if (context->impl()->GetConstFnAttr(FunctionContextImpl::UTF8_MODE)) {
+  if (context->impl()->GetConstFnAttr(FunctionContextImpl::UTF8_MODE, 0)) {
     return Utf8Length(context, str);
   }
   return IntVal(str.len);
@@ -1255,8 +1255,8 @@ StringVal StringFunctions::Concat(
 
   if (total_size > StringVal::MAX_LENGTH) {
     context->SetError(Substitute(ERROR_CHARACTER_LIMIT_EXCEEDED,
-         "Concatenated string length",
-         PrettyPrinter::Print(StringVal::MAX_LENGTH, TUnit::BYTES)).c_str());
+        "Concatenated string length",
+        PrettyPrinter::Print(StringVal::MAX_LENGTH, TUnit::BYTES)).c_str());
     return StringVal::null();
   }
 
@@ -1303,8 +1303,8 @@ StringVal StringFunctions::ConcatWs(FunctionContext* 
context, const StringVal& s
 
   if (total_size > StringVal::MAX_LENGTH) {
     context->SetError(Substitute(ERROR_CHARACTER_LIMIT_EXCEEDED,
-         "Concatenated string length",
-         PrettyPrinter::Print(StringVal::MAX_LENGTH, TUnit::BYTES)).c_str());
+        "Concatenated string length",
+        PrettyPrinter::Print(StringVal::MAX_LENGTH, TUnit::BYTES)).c_str());
     return StringVal::null();
   }
 
@@ -1435,7 +1435,7 @@ StringVal StringFunctions::ParseUrlKey(FunctionContext* 
ctx, const StringVal& ur
 
   StringValue result;
   if (!UrlParser::ParseUrlKey(StringValue::FromStringVal(url), url_part,
-                              StringValue::FromStringVal(key), &result)) {
+      StringValue::FromStringVal(key), &result)) {
     // url is malformed, or url_part is invalid.
     if (url_part == UrlParser::INVALID) {
       stringstream ss;
@@ -1715,8 +1715,8 @@ DoubleVal StringFunctions::JaroSimilarity(
   }
   double m = static_cast<double>(matching_characters);
   double jaro_similarity = 1.0 / 3.0  * ( m / static_cast<double>(s1len)
-                                        + m / static_cast<double>(s2len)
-                                        + (m - transpositions) / m );
+      + m / static_cast<double>(s2len)
+      + (m - transpositions) / m );
 
   ctx->Free(reinterpret_cast<uint8_t*>(s1_matching));
   ctx->Free(reinterpret_cast<uint8_t*>(s2_matching));
@@ -1734,27 +1734,27 @@ DoubleVal StringFunctions::JaroDistance(
 }
 
 DoubleVal StringFunctions::JaroWinklerDistance(FunctionContext* ctx,
-      const StringVal& s1, const StringVal& s2) {
+    const StringVal& s1, const StringVal& s2) {
   return StringFunctions::JaroWinklerDistance(ctx, s1, s2,
-    DoubleVal(0.1), DoubleVal(0.7));
+      DoubleVal(0.1), DoubleVal(0.7));
 }
 
 DoubleVal StringFunctions::JaroWinklerDistance(FunctionContext* ctx,
-      const StringVal& s1, const StringVal& s2,
-      const DoubleVal& scaling_factor) {
+    const StringVal& s1, const StringVal& s2,
+    const DoubleVal& scaling_factor) {
   return StringFunctions::JaroWinklerDistance(ctx, s1, s2,
-    scaling_factor, DoubleVal(0.7));
+      scaling_factor, DoubleVal(0.7));
 }
 
 // Based on https://en.wikipedia.org/wiki/Jaro%E2%80%93Winkler_distance
 // Implements Jaro-Winkler distance
 // Extended with boost_theshold: Winkler's modification only applies if Jaro 
exceeds it
 DoubleVal StringFunctions::JaroWinklerDistance(FunctionContext* ctx,
-      const StringVal& s1, const StringVal& s2,
-      const DoubleVal& scaling_factor, const DoubleVal& boost_threshold) {
+    const StringVal& s1, const StringVal& s2,
+    const DoubleVal& scaling_factor, const DoubleVal& boost_threshold) {
 
   DoubleVal jaro_winkler_similarity = StringFunctions::JaroWinklerSimilarity(
-    ctx, s1, s2, scaling_factor, boost_threshold);
+      ctx, s1, s2, scaling_factor, boost_threshold);
 
   if (jaro_winkler_similarity.is_null) return DoubleVal::null();
   if (jaro_winkler_similarity.val == -1.0) return DoubleVal(-1.0);
@@ -1762,24 +1762,24 @@ DoubleVal 
StringFunctions::JaroWinklerDistance(FunctionContext* ctx,
 }
 
 DoubleVal StringFunctions::JaroWinklerSimilarity(FunctionContext* ctx,
-      const StringVal& s1, const StringVal& s2) {
+    const StringVal& s1, const StringVal& s2) {
   return StringFunctions::JaroWinklerSimilarity(ctx, s1, s2,
-    DoubleVal(0.1), DoubleVal(0.7));
+      DoubleVal(0.1), DoubleVal(0.7));
 }
 
 DoubleVal StringFunctions::JaroWinklerSimilarity(FunctionContext* ctx,
-      const StringVal& s1, const StringVal& s2,
-      const DoubleVal& scaling_factor) {
+    const StringVal& s1, const StringVal& s2,
+    const DoubleVal& scaling_factor) {
   return StringFunctions::JaroWinklerSimilarity(ctx, s1, s2,
-    scaling_factor, DoubleVal(0.7));
+      scaling_factor, DoubleVal(0.7));
 }
 
 // Based on https://en.wikipedia.org/wiki/Jaro%E2%80%93Winkler_distance
 // Implements Jaro-Winkler similarity
 // Extended with boost_theshold: Winkler's modification only applies if Jaro 
exceeds it
 DoubleVal StringFunctions::JaroWinklerSimilarity(FunctionContext* ctx,
-      const StringVal& s1, const StringVal& s2,
-      const DoubleVal& scaling_factor, const DoubleVal& boost_threshold) {
+    const StringVal& s1, const StringVal& s2,
+    const DoubleVal& scaling_factor, const DoubleVal& boost_threshold) {
 
   constexpr int MAX_PREFIX_LENGTH = 4;
   int s1len = s1.len;
@@ -1813,12 +1813,12 @@ DoubleVal 
StringFunctions::JaroWinklerSimilarity(FunctionContext* ctx,
     int common_length = std::min(MAX_PREFIX_LENGTH, std::min(s1len, s2len));
     int common_prefix = 0;
     while (common_prefix < common_length &&
-           s1.ptr[common_prefix] == s2.ptr[common_prefix]) {
+        s1.ptr[common_prefix] == s2.ptr[common_prefix]) {
       common_prefix++;
     }
 
     jaro_winkler_similarity += common_prefix * scaling_factor.val *
-      (1.0 - jaro_similarity.val);
+        (1.0 - jaro_similarity.val);
   }
   return DoubleVal(jaro_winkler_similarity);
 }
@@ -1881,8 +1881,8 @@ IntVal StringFunctions::DamerauLevenshtein(
         l_cost = 1;
       }
       d[i][j] = std::min(d[i - 1][j - 1] + l_cost, // substitution
-                         std::min(d[i][j - 1] + 1, // insertion
-                                  d[i - 1][j] + 1) // deletion
+          std::min(d[i][j - 1] + 1, // insertion
+              d[i - 1][j] + 1) // deletion
       );
       if (i > 1 && j > 1 && s1.ptr[i - 1] == s2.ptr[j - 2]
           && s1.ptr[i - 2] == s2.ptr[j - 1]) {
@@ -1934,4 +1934,38 @@ StringVal 
StringFunctions::PrettyPrintMemory(FunctionContext* context,
   return prettyPrint(context, bytes, TUnit::BYTES);
 }
 
+// The state is a bool used to reduce excessive logs.
+void StringFunctions::AesPrepare(FunctionContext* context,
+    FunctionContext::FunctionStateScope scope) {
+  if (scope != FunctionContext::THREAD_LOCAL) return;
+  bool* state = reinterpret_cast<bool*>(context->Allocate(sizeof(bool)));
+  if (state == nullptr) {
+    context->AddWarning("Failed to allocate memory for function state.");
+    return;
+  }
+  *state = false;
+  context->SetFunctionState(scope, state);
+}
+
+void StringFunctions::AesClose(FunctionContext* context,
+    FunctionContext::FunctionStateScope scope) {
+  if (scope != FunctionContext::THREAD_LOCAL) return;
+  bool* state = reinterpret_cast<bool*>(
+      context->GetFunctionState(FunctionContext::THREAD_LOCAL));
+  if (state != nullptr) {
+    context->Free(reinterpret_cast<uint8_t*>(state));
+    context->SetFunctionState(scope, nullptr);
+  }
+}
+
+// Implementation details and comments are provided in string-functions.cc 
file.
+StringVal StringFunctions::AesDecrypt(FunctionContext* ctx, const StringVal& 
expr,
+    const StringVal& key, const StringVal& mode, const StringVal& iv) {
+  return AesDecryptImpl(ctx, expr, key, mode, iv);
+}
+
+StringVal StringFunctions::AesEncrypt(FunctionContext* ctx, const StringVal& 
expr,
+    const StringVal& key, const StringVal& mode, const StringVal& iv) {
+  return AesEncryptImpl(ctx, expr, key, mode, iv);
+}
 }
diff --git a/be/src/exprs/string-functions.cc b/be/src/exprs/string-functions.cc
index d1bc8341a..60835d7ea 100644
--- a/be/src/exprs/string-functions.cc
+++ b/be/src/exprs/string-functions.cc
@@ -20,13 +20,19 @@
 
 #include "exprs/string-functions.h"
 
+#include <openssl/bio.h>
+#include <openssl/evp.h>
+
+#include "gutil/strings/stringpiece.h"
 #include <gutil/strings/util.h>
 #include <rapidjson/document.h>
 #include <rapidjson/stringbuffer.h>
 #include <rapidjson/error/en.h>
 #include <rapidjson/writer.h>
+#include <string>
 
 #include "exprs/anyval-util.h"
+#include "util/openssl-util.h"
 #include "util/string-util.h"
 #include "util/string-parser.h"
 
@@ -401,4 +407,181 @@ StringVal 
StringFunctions::GetJsonObjectImpl(FunctionContext* ctx,
 
   return ToStringVal(ctx, queue, &allocator);
 }
+
+// Initializes the EncryptionKey for AES Encryption/ Decryption by validating 
arguments.
+Status InitializeEncryptionKey(FunctionContext* ctx, const StringVal& expr,
+    const StringVal& key, const StringVal& mode, const StringVal& iv, bool 
is_encrypt,
+    EncryptionKey* encryption_key) {
+  if (key.is_null) {
+    return Status(Substitute("Key cannot be NULL."));
+  }
+  if (key.len != 16 && key.len != 32) {
+    return Status(Substitute("AES only supports 128 and 256 bit key 
lengths."));
+  }
+  AES_CIPHER_MODE cipher_mode;
+
+  // If user passed a "NULL" field in mode, default AES encryption mode is 
chosen.
+  if (mode.is_null) {
+    cipher_mode = EncryptionKey::GetSupportedDefaultMode();
+    bool* state = reinterpret_cast<bool*>(
+        ctx->GetFunctionState(FunctionContext::THREAD_LOCAL));
+    if (state == nullptr || *state == false) {
+      VLOG_QUERY <<  "No AES mode was specified by user. Using " <<
+          EncryptionKey::ModeToString(cipher_mode) << " mode as default.";
+      if (state != nullptr) {
+        *state = true;
+      }
+    }
+  } else {
+       cipher_mode = EncryptionKey::StringToMode
+           (std::string_view(reinterpret_cast<const char*>(mode.ptr), 
mode.len));
+  }
+
+  if (cipher_mode == AES_CIPHER_MODE::INVALID) {
+    return Status(Substitute("Invalid AES 'mode': $0", StringPiece
+        (reinterpret_cast<const char*>(mode.ptr), mode.len)).c_str());
+  }
+
+  bool is_ecb = (cipher_mode == AES_CIPHER_MODE::AES_256_ECB
+      || cipher_mode == AES_CIPHER_MODE::AES_128_ECB);
+
+  // Check if iv is null in case of non ECB modes.
+  if (!is_ecb && iv.is_null) {
+    return Status(Substitute("IV vector required for $0 mode",
+        EncryptionKey::ModeToString(cipher_mode)).c_str());
+  }
+
+  // Check if IV vector size is valid (<= AES_BLOCK_SIZE) in case of non ECB 
modes.
+  if (!is_ecb && iv.len > AES_BLOCK_SIZE) {
+    return Status(Substitute("IV vector size is greater than 16 bytes."));
+  }
+
+  // ECB mode is not supported for Encryption.
+  if (is_encrypt && is_ecb) {
+    return Status(Substitute("ECB mode is not supported for encryption."));
+  }
+
+  // Initialize key and IV.
+  Status status = encryption_key->InitializeFields(key.ptr, key.len,
+      iv.ptr, iv.len, cipher_mode);
+
+  return status;
+}
+
+// An entrypoint to perform AES decryption on a given expression string.
+// GCM mode expects expression, key, AES mode and iv vector.
+// CTR and CFB modes expect expression, key, AES mode and iv vector.
+// ECB mode expects expression, key, AES mode.
+// If the mode passed by the user is supported by Impala (a valid member of
+// AES_CIPHER_MODE except for INVALID) but not supported by the OpenSSL
+// library used internally, then the default mode of the library is chosen.
+//
+// This is different from the case where the user enters a mode that Impala 
does not
+// support (e.g., a nonexistent or invalid mode). In such cases, the mode is
+// considered invalid, and an error is returned.
+//
+// Description of the modes supported:
+// AES-GCM (Advanced Encryption Standard Galois/Counter Mode) is a mode of 
operation
+// for symmetric key cryptographic block ciphers. It combines the AES block 
cipher
+// with the Galois/Counter Mode (GCM) operation for authenticated encryption.
+// AES-GCM provides both confidentiality and integrity, making it suitable for 
secure
+// communication and storage. Due to its security features and efficiency,
+// AES-GCM is chosen as the default choice for the current implementation.
+//
+// AES-ECB (Electronic Codebook) mode is a basic mode of operation for the AES
+// block cipher. In ECB mode, each block of plaintext is encrypted 
independently with
+// the same key, resulting in identical plaintext blocks producing identical 
ciphertext
+// blocks. It is included for bringing compatibility with legacy systems.
+// NOTE: This mode is only supported for decryption.
+//
+// CTR Mode:
+// AES-CTR (Counter) mode is a mode of operation for block ciphers that turns 
a block
+// cipher into a stream cipher. It works by encrypting a counter value to 
produce a
+// stream of key material, which is then XORed with the plaintext to produce 
the
+// ciphertext. CTR mode offers parallel encryption and decryption.
+//
+// CFB Mode:
+// AES-CFB (Cipher Feedback) mode is another mode of operation for block 
ciphers, where
+// ciphertext feedback is used to create a stream of key material. It operates 
on a
+// block-by-block basis, where the previous ciphertext block is encrypted and 
then XORed
+// with the plaintext to produce the next ciphertext block.
+StringVal StringFunctions::AesDecryptImpl(FunctionContext* ctx, const 
StringVal& expr,
+    const StringVal& key, const StringVal& mode, const StringVal& iv) {
+  if (expr.is_null) {
+    return StringVal::null();
+  }
+  EncryptionKey encryption_key;
+  Status status = InitializeEncryptionKey(ctx, expr, key, mode, iv, false,
+      &encryption_key);
+  if (!status.ok()) {
+    ctx->SetError(status.msg().msg().data());
+    return StringVal::null();
+  }
+  int64_t len = expr.len;
+
+  // Remove spaces and set value of gcm_tag in case of GCM mode
+  if (encryption_key.IsGcmMode()) {
+    len -= AES_BLOCK_SIZE;
+    encryption_key.SetGcmTag(expr.ptr + len);
+  }
+
+  StringVal result = StringVal(ctx, len);
+
+  // Decrypt the input
+  int64_t out_len = 0;
+  status = encryption_key.Decrypt(expr.ptr, len, result.ptr, &out_len);
+  if (!status.ok()) {
+    ctx->SetError("AES decryption failed");
+    return StringVal::null();
+  }
+  const int64_t len_diff = result.len - out_len;
+  DCHECK(len_diff == 0 || (encryption_key.IsEcbMode() && 0 < len_diff &&
+      len_diff <= AES_BLOCK_SIZE));
+  result.len = out_len;
+  return result;
+}
+
+// An entrypoint to perform AES encryption on a given expression string. In 
contrast
+// to AesDecryptImpl(), it does not support ECB modes. For other details, see 
the
+// comment at AesDecryptImpl().
+StringVal StringFunctions::AesEncryptImpl(FunctionContext* ctx, const 
StringVal& expr,
+    const StringVal& key, const StringVal& mode, const StringVal& iv) {
+  if (expr.is_null) {
+    return StringVal::null();
+  }
+  EncryptionKey encryption_key;
+  Status status = InitializeEncryptionKey(ctx, expr, key, mode, iv, true,
+      &encryption_key);
+  if (!status.ok()) {
+    ctx->SetError(status.msg().msg().data());
+    return StringVal::null();
+  }
+
+  // Calculate expected output length
+  int expected_out_len = expr.len;
+
+  // Append space for gcm_tag in case of GCM mode
+  if (encryption_key.IsGcmMode()) {
+    expected_out_len += AES_BLOCK_SIZE;
+  }
+
+  // Allocate buffer for output
+  StringVal result = StringVal(ctx, expected_out_len);
+  // Encrypt the input
+  int64_t out_len = 0;
+  status = encryption_key.Encrypt(expr.ptr, expr.len, result.ptr,
+      &out_len);
+  if (!status.ok()) {
+    ctx->SetError("AES encryption failed.");
+    return StringVal::null();
+  }
+  // Append gcm_tag to encrypted buffer
+  if (encryption_key.IsGcmMode()) {
+    encryption_key.GetGcmTag(result.ptr + out_len);
+    out_len += AES_BLOCK_SIZE;
+  }
+  // Ensure expected output length matches actual output length
+  DCHECK_EQ(expected_out_len, out_len);
+  return result;
+}
 }
diff --git a/be/src/exprs/string-functions.h b/be/src/exprs/string-functions.h
index 2121caf28..8636d54f6 100644
--- a/be/src/exprs/string-functions.h
+++ b/be/src/exprs/string-functions.h
@@ -135,6 +135,20 @@ class StringFunctions {
   /// Cleans up the work done by TrimPrepare above.
   static void TrimClose(FunctionContext*, FunctionContext::FunctionStateScope);
 
+  /// AES encryption functions in Impala, using openSSL libraries.
+  static void AesPrepare(FunctionContext* context,
+      FunctionContext::FunctionStateScope scope);
+  static StringVal AesDecrypt(FunctionContext* ctx, const StringVal& expr,
+      const StringVal& key, const StringVal& mode, const StringVal& iv);
+  static StringVal AesEncrypt(FunctionContext* ctx, const StringVal& expr,
+      const StringVal& key, const StringVal& mode, const StringVal& iv);
+  static StringVal AesDecryptImpl(FunctionContext* ctx, const StringVal& expr,
+      const StringVal& key, const StringVal& mode, const StringVal& iv);
+  static StringVal AesEncryptImpl(FunctionContext* ctx, const StringVal& expr,
+      const StringVal& key, const StringVal& mode, const StringVal& iv);
+  static void AesClose(FunctionContext* context,
+      FunctionContext::FunctionStateScope scope);
+
   /// Trims occurrences of the characters in 'chars_to_trim' string from
   /// the beginning of string 'str'.
   static StringVal LTrimString(FunctionContext* ctx, const StringVal& str,
@@ -265,4 +279,4 @@ class StringFunctions {
   static StringVal DoUtf8TrimString(const StringVal& str, const TrimContext& 
trim_ctx);
 };
 }
-#endif
+#endif
\ No newline at end of file
diff --git a/be/src/runtime/tmp-file-mgr.cc b/be/src/runtime/tmp-file-mgr.cc
index dfcba68c6..e25bc2ca0 100644
--- a/be/src/runtime/tmp-file-mgr.cc
+++ b/be/src/runtime/tmp-file-mgr.cc
@@ -2083,7 +2083,7 @@ Status TmpWriteHandle::EncryptAndHash(
       counters == nullptr ? nullptr : counters->encryption_time);
   // Since we're using GCM/CTR/CFB mode, we must take care not to reuse a
   // key/IV pair. Regenerate a new key and IV for every data buffer we write.
-  key_.InitializeRandom();
+  RETURN_IF_ERROR(key_.InitializeRandom(AES_BLOCK_SIZE, 
key_.GetSupportedDefaultMode()));
   RETURN_IF_ERROR(key_.Encrypt(buffer.data(), buffer.len(), buffer.data()));
 
   if (!key_.IsGcmMode()) {
diff --git a/be/src/udf/udf-ir.cc b/be/src/udf/udf-ir.cc
index 58f8257b9..91201dc58 100644
--- a/be/src/udf/udf-ir.cc
+++ b/be/src/udf/udf-ir.cc
@@ -59,4 +59,4 @@ uint8_t* FnCtxAllocateForResults(FunctionContext* ctx, 
int64_t byte_size) {
   assert(ctx != nullptr);
   uint8_t* ptr = ctx->impl()->AllocateForResults(byte_size);
   return ptr;
-}
+}
\ No newline at end of file
diff --git a/be/src/util/openssl-util-test.cc b/be/src/util/openssl-util-test.cc
index 9700cfe27..bfc302c02 100644
--- a/be/src/util/openssl-util-test.cc
+++ b/be/src/util/openssl-util-test.cc
@@ -57,7 +57,8 @@ class OpenSSLUtilTest : public ::testing::Test {
 
   void TestEncryptionDecryption(const int64_t buffer_size) {
     vector<uint8_t> original(buffer_size);
-    vector<uint8_t> scratch(buffer_size); // Scratch buffer for in-place 
encryption.
+    // Scratch buffer for in-place encryption.
+    vector<uint8_t> scratch(buffer_size + AES_BLOCK_SIZE);
     if (buffer_size % 8 == 0) {
       GenerateRandomData(original.data(), buffer_size);
     } else {
@@ -65,18 +66,32 @@ class OpenSSLUtilTest : public ::testing::Test {
     }
 
     // Check all the modes
-    AES_CIPHER_MODE modes[] = {AES_256_GCM, AES_256_CTR, AES_256_CFB};
+    AES_CIPHER_MODE modes[] = {
+        AES_CIPHER_MODE::AES_256_GCM,
+        AES_CIPHER_MODE::AES_256_CTR,
+        AES_CIPHER_MODE::AES_256_CFB,
+        AES_CIPHER_MODE::AES_256_ECB,
+        AES_CIPHER_MODE::AES_128_GCM,
+        AES_CIPHER_MODE::AES_128_ECB,
+    };
     for (auto m : modes) {
       memcpy(scratch.data(), original.data(), buffer_size);
 
       EncryptionKey key;
-      key.InitializeRandom();
-      key.SetCipherMode(m);
+      ASSERT_OK(key.InitializeRandom(AES_BLOCK_SIZE, m));
+
+      int64_t encrypted_length;
+      if (key.IsEcbMode() && buffer_size > numeric_limits<int>::max() - 
AES_BLOCK_SIZE) {
+          ASSERT_ERROR_MSG(key.Encrypt(scratch.data(),buffer_size, 
scratch.data()),
+              "Input buffer length exceeds the supported length for ECB 
mode.");
+          continue;
+      }
+      ASSERT_OK(key.Encrypt(scratch.data(), buffer_size, scratch.data(),
+          &encrypted_length));
 
-      ASSERT_OK(key.Encrypt(scratch.data(), buffer_size, scratch.data()));
       // Check that encryption did something
       ASSERT_NE(0, memcmp(original.data(), scratch.data(), buffer_size));
-      ASSERT_OK(key.Decrypt(scratch.data(), buffer_size, scratch.data()));
+      ASSERT_OK(key.Decrypt(scratch.data(), encrypted_length, scratch.data()));
       // Check that we get the original data back.
       ASSERT_EQ(0, memcmp(original.data(), scratch.data(), buffer_size));
     }
@@ -94,14 +109,19 @@ TEST_F(OpenSSLUtilTest, Encryption) {
   vector<uint8_t> decrypted(buffer_size);
   GenerateRandomData(original.data(), buffer_size);
 
-  // Check both CTR & CFB
-  AES_CIPHER_MODE modes[] = {AES_256_GCM, AES_256_CTR, AES_256_CFB};
+  // Check GCM, CTR and CFB
+  AES_CIPHER_MODE modes[] = {
+      AES_CIPHER_MODE::AES_256_GCM,
+      AES_CIPHER_MODE::AES_256_CTR,
+      AES_CIPHER_MODE::AES_256_CFB,
+      AES_CIPHER_MODE::AES_128_GCM
+  };
   for (auto m : modes) {
     // Iterate multiple times to ensure that key regeneration works correctly.
     EncryptionKey key;
     for (int i = 0; i < 2; ++i) {
-      key.InitializeRandom(); // Generate a new key for each iteration.
-      key.SetCipherMode(m);
+      // Generate a new key for each iteration.
+      ASSERT_OK(key.InitializeRandom(AES_BLOCK_SIZE, m));
 
       // Check that OpenSSL is happy with the amount of entropy we're feeding 
it.
       DCHECK_EQ(1, RAND_status());
@@ -120,6 +140,18 @@ TEST_F(OpenSSLUtilTest, Encryption) {
   }
 }
 
+/// Test to check whether key and mode are validated in InitializeFields().
+TEST_F(OpenSSLUtilTest, ValidateInitialize) {
+  EncryptionKey key;
+  uint8_t IV[AES_BLOCK_SIZE] = {};
+  uint8_t key16bits[16] = {};
+  Status status_initialize_fields = key.InitializeFields
+      (key16bits,16, IV, AES_BLOCK_SIZE, AES_CIPHER_MODE::AES_256_GCM);
+  ASSERT_FALSE(status_initialize_fields.ok());
+  ASSERT_OK(key.InitializeFields(key16bits,
+      16, IV, AES_BLOCK_SIZE, AES_CIPHER_MODE::AES_128_GCM));
+}
+
 /// Test that encryption and decryption work in-place.
 TEST_F(OpenSSLUtilTest, EncryptInPlace) {
   const int buffer_size = 1024 * 1024;
@@ -145,8 +177,7 @@ TEST_F(OpenSSLUtilTest, GcmIntegrity) {
   vector<uint8_t> buffer(buffer_size);
 
   EncryptionKey key;
-  key.InitializeRandom();
-  key.SetCipherMode(AES_256_GCM);
+  ASSERT_OK(key.InitializeRandom(AES_BLOCK_SIZE, 
AES_CIPHER_MODE::AES_256_GCM));
 
   // Even it has been set as GCM mode, it may fall back to other modes.
   // Check if GCM mode is supported at runtime.
@@ -205,7 +236,7 @@ TEST_F(OpenSSLUtilTest, RandSeeding) {
     DCHECK_EQ(1, RAND_status());
 
     EncryptionKey key;
-    key.InitializeRandom();
+    ASSERT_OK(key.InitializeRandom(AES_BLOCK_SIZE, 
key.GetSupportedDefaultMode()));
   }
 }
 }
diff --git a/be/src/util/openssl-util.cc b/be/src/util/openssl-util.cc
index 8d9a647c5..7de123da1 100644
--- a/be/src/util/openssl-util.cc
+++ b/be/src/util/openssl-util.cc
@@ -17,8 +17,12 @@
 
 #include "util/openssl-util.h"
 
+#include <boost/algorithm/string.hpp>
+#include <string_view>
+
 #include <limits.h>
 #include <sstream>
+#include <iostream>
 
 #include <glog/logging.h>
 #include <openssl/err.h>
@@ -56,6 +60,9 @@ const EVP_CIPHER* EVP_aes_256_ctr();
 
 ATTRIBUTE_WEAK
 const EVP_CIPHER* EVP_aes_256_gcm();
+
+ATTRIBUTE_WEAK
+const EVP_CIPHER* EVP_aes_128_gcm();
 }
 
 namespace impala {
@@ -75,7 +82,7 @@ int MaxSupportedTlsVersion() {
   return SSLv23_method()->version;
 #else
   // OpenSSL 1.1+ doesn't let us detect the supported TLS version at runtime. 
Assume
-  // that the OpenSSL library we're linked against supports only up to TLS1.2
+  // that the OpenSSL library we're linked against supports only up to TLS1.2.
   return TLS1_2_VERSION;
 #endif
 }
@@ -187,7 +194,47 @@ bool AuthenticationHash::Verify(
   return memcmp(signature, out, HashLen()) == 0;
 }
 
-void EncryptionKey::InitializeRandom() {
+int GetKeyLenForMode(AES_CIPHER_MODE m) {
+  switch (m) {
+    case AES_CIPHER_MODE::AES_128_ECB:
+    case AES_CIPHER_MODE::AES_128_GCM:
+      return 16;
+    case AES_CIPHER_MODE::AES_256_ECB:
+    case AES_CIPHER_MODE::AES_256_GCM:
+    case AES_CIPHER_MODE::AES_256_CTR:
+    case AES_CIPHER_MODE::AES_256_CFB:
+      return 32;
+    default:
+      return -1;
+  }
+}
+
+Status ValidateModeAndKeyLength(AES_CIPHER_MODE m, int key_len) {
+  if (m == AES_CIPHER_MODE::INVALID) {
+    return Status("Invalid AES mode specified.");
+  }
+
+  int expected_key_len = GetKeyLenForMode(m);
+  DCHECK_GT(expected_key_len, 0);
+  if (key_len != expected_key_len) {
+    return Status("Mismatch between mode and key length.");
+  }
+
+  return Status::OK();
+}
+
+Status EncryptionKey::InitializeRandom(int iv_len, AES_CIPHER_MODE m) {
+  mode_ = m;
+  if (!IsModeSupported(m)) {
+    mode_ = GetSupportedDefaultMode();
+    LOG(WARNING) << Substitute("$0 is not supported, fall back to $1.",
+        ModeToString(m), ModeToString(mode_));
+  }
+
+  if (m == AES_CIPHER_MODE::INVALID) {
+    return Status("Invalid AES mode specified.");
+  }
+
   uint64_t next_key_num = keys_generated.Add(1);
   if (next_key_num % RNG_RESEED_INTERVAL == 0) {
     SeedOpenSSLRNG();
@@ -196,63 +243,92 @@ void EncryptionKey::InitializeRandom() {
   RAND_bytes(iv_, sizeof(iv_));
   memset(gcm_tag_, 0, sizeof(gcm_tag_));
   initialized_ = true;
+  key_length_ = GetKeyLenForMode(mode_);
+  iv_length_ = iv_len;
+  return Status::OK();
 }
 
-Status EncryptionKey::Encrypt(const uint8_t* data, int64_t len, uint8_t* out) {
-  return EncryptInternal(true, data, len, out);
+Status EncryptionKey::Encrypt(const uint8_t* data, int64_t len, uint8_t* out,
+    int64_t* out_len) {
+  return EncryptInternal(true, data, len, out, out_len);
 }
 
-Status EncryptionKey::Decrypt(const uint8_t* data, int64_t len, uint8_t* out) {
-  return EncryptInternal(false, data, len, out);
+Status EncryptionKey::Decrypt(const uint8_t* data, int64_t len, uint8_t* out,
+    int64_t* out_len) {
+  return EncryptInternal(false, data, len, out, out_len);
 }
 
 Status EncryptionKey::EncryptInternal(
-    bool encrypt, const uint8_t* data, int64_t len, uint8_t* out) {
+    bool encrypt, const uint8_t* data, int64_t len, uint8_t* out, int64_t* 
out_len) {
   DCHECK(initialized_);
   DCHECK_GE(len, 0);
+  if (IsEcbMode()) {
+    if ((encrypt && len > numeric_limits<int>::max() - AES_BLOCK_SIZE)
+        || (!encrypt && len > numeric_limits<int>::max())) {
+      return Status("Input buffer length exceeds the supported length for ECB 
mode.");
+    }
+  }
+
   const char* err_context = encrypt ? "encrypting" : "decrypting";
-  // Create and initialize the context for encryption
-  ScopedEVPCipherCtx ctx(0);
-
-  // Start encryption/decryption.  We use a 256-bit AES key, and the cipher 
block mode
-  // is either CTR or CFB(stream cipher), both of which support arbitrary 
length
-  // ciphertexts - it doesn't have to be a multiple of 16 bytes. Additionally, 
CTR
-  // mode is well-optimized(instruction level parallelism) with hardware 
acceleration
-  // on x86 and PowerPC
+  // Create and initialize the context for encryption.
+  // If it is ECB mode then padding will be enabled
+  // for this ctx, otherwise it will be disabled.
+  bool padding_enabled = IsEcbMode();
+  int padding_flag = padding_enabled ? 1 : 0;
+  ScopedEVPCipherCtx ctx(padding_flag);
+
+  // Start encryption/decryption. We use a 128/256-bit AES key and support 
GCM, CTR
+  // CFB and ECB for encryption and decryption. When the cipher block mode is 
either
+  // GCM, CTR or CFB(stream cipher), it supports arbitrary length ciphertexts 
- it
+  // doesn't have to be a multiple of 16 bytes. While for ECB decryption,
+  // the length has to be multiples of 16. Additionally, CTR mode is
+  // well-optimized (instruction level parallelism) with hardware acceleration
+  // on x86 and PowerPC.
+
+  // In the first initialization, only evpCipher is initialized, and in the 
second
+  // initialization, the key and IV vector are set. This approach is necessary 
because a
+  // variable-length IV vector is used. Therefore, in GCM mode, the IV length 
must be
+  // initialized before setting the IV vector.
   const EVP_CIPHER* evpCipher = GetCipher();
-  int success = encrypt ? EVP_EncryptInit_ex(ctx.ctx, evpCipher, NULL, NULL, 
NULL) :
-                          EVP_DecryptInit_ex(ctx.ctx, evpCipher, NULL, NULL, 
NULL);
+  DCHECK(evpCipher != nullptr);
+  int success = encrypt ?
+      EVP_EncryptInit_ex(ctx.ctx, evpCipher, nullptr, nullptr, nullptr):
+      EVP_DecryptInit_ex(ctx.ctx, evpCipher, nullptr, nullptr, nullptr);
   if (success != 1) {
     return OpenSSLErr(encrypt ? "EVP_EncryptInit_ex" : "EVP_DecryptInit_ex", 
err_context);
   }
+
   if (IsGcmMode()) {
-    if (EVP_CIPHER_CTX_ctrl(ctx.ctx, EVP_CTRL_GCM_SET_IVLEN, AES_BLOCK_SIZE, 
NULL)
-        != 1) {
+    // Set iv_vector for GCM mode.
+    if (EVP_CIPHER_CTX_ctrl(ctx.ctx, EVP_CTRL_GCM_SET_IVLEN, iv_length_, 
nullptr)!= 1) {
       return OpenSSLErr("EVP_CIPHER_CTX_ctrl", err_context);
     }
   }
   // setting iv after changing iv len, see 
https://github.com/openssl/openssl/pull/22590
-  success = encrypt ? EVP_EncryptInit_ex(ctx.ctx, NULL, NULL, key_, iv_) :
-                      EVP_DecryptInit_ex(ctx.ctx, NULL, NULL, key_, iv_);
+  success = encrypt ? EVP_EncryptInit_ex(ctx.ctx, nullptr, nullptr, key_, iv_):
+                      EVP_DecryptInit_ex(ctx.ctx, nullptr, nullptr, key_, iv_);
   if (success != 1) {
     return OpenSSLErr(encrypt ? "EVP_EncryptInit_ex" : "EVP_DecryptInit_ex", 
err_context);
   }
 
-  // The OpenSSL encryption APIs use ints for buffer lengths for some reason. 
To support
-  // larger buffers we need to chunk larger buffers into smaller parts.
-  int64_t offset = 0;
-  while (offset < len) {
-    int in_len = static_cast<int>(min<int64_t>(len - offset, 
numeric_limits<int>::max()));
-    int out_len;
+  // The OpenSSL encryption APIs use INT for buffer lengths. To support larger 
buffers,
+  // larger buffers need to be chunked into smaller parts.
+  int64_t output_offset = 0;
+  int64_t input_offset = 0;
+  while (input_offset < len) {
+    int in_len = static_cast<int>(min<int64_t>(len - input_offset,
+        numeric_limits<int>::max()));
+    int output_len;
+    uint8_t* out_addr = out + output_offset;
+    const uint8_t* in_addr = data + input_offset;
     success = encrypt ?
-        EVP_EncryptUpdate(ctx.ctx, out + offset, &out_len, data + offset, 
in_len) :
-        EVP_DecryptUpdate(ctx.ctx, out + offset, &out_len, data + offset, 
in_len);
+        EVP_EncryptUpdate(ctx.ctx, out_addr, &output_len, in_addr, in_len) :
+        EVP_DecryptUpdate(ctx.ctx, out_addr, &output_len, in_addr, in_len);
     if (success != 1) {
       return OpenSSLErr(encrypt ? "EVP_EncryptUpdate" : "EVP_DecryptUpdate", 
err_context);
     }
-    // This is safe because we're using CTR/CFB mode without padding.
-    DCHECK_EQ(in_len, out_len);
-    offset += in_len;
+    output_offset += output_len;
+    input_offset += in_len;
   }
 
   if (IsGcmMode() && !encrypt) {
@@ -265,45 +341,70 @@ Status EncryptionKey::EncryptInternal(
 
   // Finalize encryption or decryption.
   int final_out_len;
-  success = encrypt ? EVP_EncryptFinal_ex(ctx.ctx, out + offset, 
&final_out_len) :
-                      EVP_DecryptFinal_ex(ctx.ctx, out + offset, 
&final_out_len);
+  success = encrypt ? EVP_EncryptFinal_ex(ctx.ctx, out + output_offset, 
&final_out_len) :
+                      EVP_DecryptFinal_ex(ctx.ctx, out + output_offset, 
&final_out_len);
+
   if (success != 1) {
     return OpenSSLErr(encrypt ? "EVP_EncryptFinal" : "EVP_DecryptFinal", 
err_context);
   }
 
+  if (out_len != nullptr) *out_len = output_offset + final_out_len;
+
   if (IsGcmMode() && encrypt) {
     if (EVP_CIPHER_CTX_ctrl(ctx.ctx, EVP_CTRL_GCM_GET_TAG, AES_BLOCK_SIZE, 
gcm_tag_)
         != 1) {
       return OpenSSLErr("EVP_CIPHER_CTX_ctrl", err_context);
     }
   }
-  // Again safe due to GCM/CTR/CFB with no padding
-  DCHECK_EQ(final_out_len, 0);
+
   DCHECK_EQ(ERR_peek_error(), 0) << "Did not clear OpenSSL error queue";
   return Status::OK();
 }
 
 const EVP_CIPHER* EncryptionKey::GetCipher() const {
   // use weak symbol to avoid compiling error on OpenSSL 1.0.0 environment
-  if (mode_ == AES_256_CTR) return EVP_aes_256_ctr();
-  if (mode_ == AES_256_GCM) return EVP_aes_256_gcm();
-
-  return EVP_aes_256_cfb();
+  switch (mode_) {
+    case AES_CIPHER_MODE::AES_256_CTR: return EVP_aes_256_ctr();
+    case AES_CIPHER_MODE::AES_256_GCM: return EVP_aes_256_gcm();
+    case AES_CIPHER_MODE::AES_256_CFB: return EVP_aes_256_cfb();
+    case AES_CIPHER_MODE::AES_256_ECB: return EVP_aes_256_ecb();
+    case AES_CIPHER_MODE::AES_128_GCM: return EVP_aes_128_gcm();
+    case AES_CIPHER_MODE::AES_128_ECB: return EVP_aes_128_ecb();
+    default: return nullptr;
+  }
 }
 
-void EncryptionKey::SetCipherMode(AES_CIPHER_MODE m) {
+Status EncryptionKey::InitializeFields(const uint8_t* key, int key_len, const 
uint8_t* iv,
+    int iv_len, AES_CIPHER_MODE m) {
   mode_ = m;
-
   if (!IsModeSupported(m)) {
     mode_ = GetSupportedDefaultMode();
     LOG(WARNING) << Substitute("$0 is not supported, fall back to $1.",
         ModeToString(m), ModeToString(mode_));
   }
+  Status status = ValidateModeAndKeyLength(m, key_len);
+  RETURN_IF_ERROR(status);
+
+  key_length_ = key_len;
+  iv_length_ = iv_len;
+  memcpy(key_, key, key_length_);
+  if (iv_length_ != 0) {
+    memcpy(iv_, iv, iv_length_);
+  }
+  initialized_ = true;
+  return Status::OK();
+}
+
+void EncryptionKey::SetGcmTag(const uint8_t* tag)  {
+  memcpy(gcm_tag_, tag, AES_BLOCK_SIZE);
+}
+
+void EncryptionKey::GetGcmTag(uint8_t* out) const {
+  memcpy(out, gcm_tag_, AES_BLOCK_SIZE);
 }
 
 bool EncryptionKey::IsModeSupported(AES_CIPHER_MODE m) {
   switch (m) {
-    case AES_256_GCM:
       // It becomes a bit tricky for GCM mode, because GCM mode is enabled 
since
       // OpenSSL 1.0.1, but the tag validation only works since 1.0.1d. We have
       // to make sure that OpenSSL version >= 1.0.1d for GCM. So we need
@@ -313,34 +414,67 @@ bool EncryptionKey::IsModeSupported(AES_CIPHER_MODE m) {
       // will try to fall back to CTR mode, so it is not ideal but is OK to use
       // SSLeay() for GCM mode here since in the worst case, we will be using
       // AES_256_CTR in a system that supports AES_256_GCM.
+    case AES_CIPHER_MODE::AES_256_GCM:
       return (CpuInfo::IsSupported(CpuInfo::PCLMULQDQ)
           && SSLeay() >= OPENSSL_VERSION_1_0_1D && EVP_aes_256_gcm);
 
-    case AES_256_CTR:
+    case AES_CIPHER_MODE::AES_128_GCM:
+      return (CpuInfo::IsSupported(CpuInfo::PCLMULQDQ)
+          && SSLeay() >= OPENSSL_VERSION_1_0_1D && EVP_aes_128_gcm);
+
+    case AES_CIPHER_MODE::AES_256_CTR:
       // If TLS1.2 is supported, then we're on a verison of OpenSSL that
       // supports AES-256-CTR.
       return (MaxSupportedTlsVersion() >= TLS1_2_VERSION && EVP_aes_256_ctr);
 
-    case AES_256_CFB:
+    case AES_CIPHER_MODE::AES_256_CFB:
+    case AES_CIPHER_MODE::AES_256_ECB:
+    case AES_CIPHER_MODE::AES_128_ECB:
       return true;
-
+    case AES_CIPHER_MODE::INVALID:
+      return false;
     default:
       return false;
   }
 }
 
 AES_CIPHER_MODE EncryptionKey::GetSupportedDefaultMode() {
-  if (IsModeSupported(AES_256_GCM)) return AES_256_GCM;
-  if (IsModeSupported(AES_256_CTR)) return AES_256_CTR;
-  return AES_256_CFB;
+  if (IsModeSupported(AES_CIPHER_MODE::AES_256_GCM)) {
+    return AES_CIPHER_MODE::AES_256_GCM;
+  }
+  if (IsModeSupported(AES_CIPHER_MODE::AES_256_CTR)) {
+    return AES_CIPHER_MODE::AES_256_CTR;
+  }
+  return AES_CIPHER_MODE::AES_256_CFB;
 }
 
 const string EncryptionKey::ModeToString(AES_CIPHER_MODE m) {
   switch(m) {
-    case AES_256_GCM: return "AES-GCM";
-    case AES_256_CTR: return "AES-CTR";
-    case AES_256_CFB: return "AES-CFB";
+    case AES_CIPHER_MODE::AES_256_GCM: return std::string(AES_256_GCM_STR);
+    case AES_CIPHER_MODE::AES_256_CTR: return std::string(AES_256_CTR_STR);
+    case AES_CIPHER_MODE::AES_256_CFB: return std::string(AES_256_CFB_STR);
+    case AES_CIPHER_MODE::AES_256_ECB: return std::string(AES_256_ECB_STR);
+    case AES_CIPHER_MODE::AES_128_GCM: return std::string(AES_128_GCM_STR);
+    case AES_CIPHER_MODE::AES_128_ECB: return std::string(AES_128_ECB_STR);
+    case AES_CIPHER_MODE::INVALID: return "INVALID";
   }
-  return "Unknown mode";
+  return "INVALID";
 }
+
+AES_CIPHER_MODE EncryptionKey::StringToMode(std:: string_view str) {
+  if (boost::iequals(str, AES_256_GCM_STR)) {
+    return AES_CIPHER_MODE::AES_256_GCM;
+  } else if (boost::iequals(str, AES_256_CTR_STR)) {
+    return AES_CIPHER_MODE::AES_256_CTR;
+  } else if (boost::iequals(str, AES_256_CFB_STR)) {
+    return AES_CIPHER_MODE::AES_256_CFB;
+  } else if (boost::iequals(str, AES_256_ECB_STR)) {
+    return AES_CIPHER_MODE::AES_256_ECB;
+  } else if (boost::iequals(str, AES_128_GCM_STR)) {
+    return AES_CIPHER_MODE::AES_128_GCM;
+  } else if (boost::iequals(str, AES_128_ECB_STR)) {
+    return AES_CIPHER_MODE::AES_128_ECB;
+  }
+  return AES_CIPHER_MODE::INVALID;
 }
+}
\ No newline at end of file
diff --git a/be/src/util/openssl-util.h b/be/src/util/openssl-util.h
index 0864e5fb6..88f2c4d1d 100644
--- a/be/src/util/openssl-util.h
+++ b/be/src/util/openssl-util.h
@@ -17,12 +17,13 @@
 
 #pragma once
 
+#include <string_view>
+
 #include <openssl/aes.h>
 #include <openssl/crypto.h>
 #include <openssl/evp.h>
 #include <openssl/sha.h>
 #include <openssl/ssl.h>
-
 #include "common/status.h"
 
 namespace impala {
@@ -69,12 +70,27 @@ inline bool IsFIPSMode() {
 #endif
 };
 
-enum AES_CIPHER_MODE {
+// Enum of all the AES modes that are currently supported. INVALID is used 
whenever
+// user inputs a wrong AES Mode, for example, AES_128_CTF etc.
+enum class AES_CIPHER_MODE {
   AES_256_CFB,
   AES_256_CTR,
-  AES_256_GCM
+  AES_256_GCM,
+  AES_256_ECB,
+  AES_128_GCM,
+  AES_128_ECB,
+  INVALID
 };
 
+// AES cipher mode strings as constexpr static std::string_view used in 
StringToMode()
+// and ModeToString() functions for string comparisons.
+static constexpr std::string_view AES_256_GCM_STR = "AES_256_GCM";
+static constexpr std::string_view AES_256_CTR_STR = "AES_256_CTR";
+static constexpr std::string_view AES_256_CFB_STR = "AES_256_CFB";
+static constexpr std::string_view AES_256_ECB_STR = "AES_256_ECB";
+static constexpr std::string_view AES_128_GCM_STR = "AES_128_GCM";
+static constexpr std::string_view AES_128_ECB_STR = "AES_128_ECB";
+
 /// The hash of a data buffer used for checking integrity. A SHA256 hash is 
used
 /// internally.
 class IntegrityHash {
@@ -117,14 +133,14 @@ class AuthenticationHash {
 /// The key and initialization vector (IV) required to encrypt and decrypt a 
buffer of
 /// data. This should be regenerated for each buffer of data.
 ///
-/// We use AES with a 256-bit key and GCM/CTR/CFB cipher block mode, which 
gives us a
-/// stream cipher that can support arbitrary-length ciphertexts. The mode is 
chosen
-/// depends on the OpenSSL version & the hardware support at runtime. The IV 
is used as
-/// an input to the cipher as the "block to supply before the first block of 
plaintext".
-/// This is required because all ciphers (except the weak ECB) are built such 
that each
-/// block depends on the output from the previous block. Since the first block 
doesn't
-/// have a previous block, we supply this IV. Think of it  as starting off the 
chain of
-/// encryption.
+/// AES is employed with a 256-bit/128-bit key, and the cipher block mode is 
chosen
+/// from GCM, CTR, CFB, or ECB based on the OpenSSL version and hardware 
support
+/// at runtime. This configuration yields a cipher capable of handling
+/// arbitrary-length ciphertexts (except ECB). The IV serves as input to the 
cipher,
+/// acting as the "block to supply before the first block of plaintext." This 
is necessary
+/// because all ciphers (except ECB) are designed such that each block depends 
on
+/// the output from the preceding block. Since the first block lacks a previous
+/// block, the IV is supplied to initiate the encryption chain.
 ///
 /// Notes for GCM:
 /// (1) GCM mode was supported since OpenSSL 1.0.1, however the tag 
verification
@@ -137,54 +153,84 @@ class AuthenticationHash {
 
 class EncryptionKey {
  public:
-  EncryptionKey() : initialized_(false) { mode_ = GetSupportedDefaultMode(); }
+  EncryptionKey() : initialized_(false) { mode_ = AES_CIPHER_MODE::INVALID; }
 
   /// Initializes a key for temporary use with randomly generated data, and 
clears the
   /// tag for GCM mode. Reinitializes with new random values if the key was 
already
   /// initialized. We use AES-GCM/AES-CTR/AES-CFB mode so key/IV pairs should 
not be
   /// reused. This function automatically reseeds the RNG periodically, so 
callers do
   /// not need to do it.
-  void InitializeRandom();
+  Status InitializeRandom(int iv_len, AES_CIPHER_MODE mode);
 
   /// Encrypts a buffer of input data 'data' of length 'len' into an output 
buffer 'out'.
-  /// Exactly 'len' bytes will be written to 'out'. This key must be 
initialized before
-  /// calling. Operates in-place if 'in' == 'out', otherwise the buffers must 
not overlap.
-  /// For GCM mode, the hash tag will be kept inside(gcm_tag_ variable).
-  Status Encrypt(const uint8_t* data, int64_t len, uint8_t* out) 
WARN_UNUSED_RESULT;
-
-  /// Decrypts a buffer of input data 'data' of length 'len' that was 
encrypted with this
-  /// key into an output buffer 'out'. Exactly 'len' bytes will be written to 
'out'.
-  /// This key must be initialized before calling. Operates in-place if 'in' 
== 'out',
-  /// otherwise the buffers must not overlap. For GCM mode, the hash tag, 
which is
-  /// computed during encryption, will be used for intgerity verification.
-  Status Decrypt(const uint8_t* data, int64_t len, uint8_t* out) 
WARN_UNUSED_RESULT;
-
-  /// Specify a cipher mode. Currently used only for testing but maybe in 
future we
-  /// can provide a configuration option for the end user who can choose a 
preferred
-  /// mode(GCM, CTR, CFB...) based on their software/hardware environment.
-  /// If not supported, fall back to the supported mode at runtime.
-  void SetCipherMode(AES_CIPHER_MODE m);
-
-  /// If is GCM mode at runtime
-  bool IsGcmMode() const { return mode_ == AES_256_GCM; }
-
-  /// Returns the a default mode which is supported at runtime. If GCM mode
+  /// The 'out' buffer must contain sufficient length to hold the extra 
padding block in
+  /// case of ECB mode. In other modes, the 'out' buffer should at least be as 
big as the
+  /// length of the 'data' buffer. This key must be initialized before calling 
this
+  /// function. If 'in' == 'out', the operation is performed in-place; 
otherwise, the
+  /// buffers must not overlap. In GCM mode, the hash tag is kept internally 
(in the
+  /// 'gcm_tag_' variable). 'out_len' (if not NULL) will be set to the output 
length.
+  Status Encrypt(const uint8_t* data, int64_t len, uint8_t* out,
+      int64_t* out_len = nullptr) WARN_UNUSED_RESULT;
+
+  /// Decrypts a buffer of input data 'data' of length 'len', encrypted with 
this key,
+  /// into an output buffer 'out'.'out' buffer should be at least as big as the
+  /// length of the 'data' buffer.
+  /// Prior to calling this function, the key must be initialized. If 'in' == 
'out', the
+  /// operation is performed in-place; otherwise, the buffers must not 
overlap. In GCM
+  /// mode, the hash tag computed during encryption is used for integrity 
verification.
+  /// 'out_len' (if not NULL) will be set to the output length.
+  Status Decrypt(const uint8_t* data, int64_t len, uint8_t* out,
+      int64_t* out_len = nullptr) WARN_UNUSED_RESULT;
+
+  /// If it is GCM mode at runtime
+  bool IsGcmMode() const {
+      return mode_ == AES_CIPHER_MODE::AES_256_GCM
+          || mode_ == AES_CIPHER_MODE::AES_128_GCM;
+  }
+
+  /// If it is ECB mode at runtime.
+  bool IsEcbMode() const {
+      return mode_ == AES_CIPHER_MODE::AES_256_ECB
+          || mode_ == AES_CIPHER_MODE::AES_128_ECB;
+  }
+
+  /// It initializes 'key_' and 'iv_'.
+  /// Specifies a cipher mode. It gives an option to configure this mode for 
end users,
+  /// allowing them to choose a preferred mode (e.g., GCM, CTR, CFB) based on 
their
+  /// software/hardware environment. If the specified mode is not supported, 
the
+  /// implementation falls back to a supported mode at runtime.
+  Status InitializeFields(const uint8_t* key, int key_len, const uint8_t* iv, 
int iv_len,
+      AES_CIPHER_MODE mode);
+
+  /// Getter function for 'gcm_tag_' which is required for encryption using 
GCM mode.
+  void GetGcmTag(uint8_t* out) const;
+  /// Setter function for setting 'gcm_tag_' which is required for decryption 
using GCM
+  /// mode.
+  void SetGcmTag(const uint8_t* tag);
+
+  /// Returns the default mode which is supported at runtime. If GCM mode
   /// is supported, return AES_256_GCM as the default. If GCM is not supported,
   /// but CTR is still supported, return AES_256_CTR. When both GCM and
   /// CTR modes are not supported, return AES_256_CFB.
+  /// ECB mode is not used unless requested by the user specifically.
   static AES_CIPHER_MODE GetSupportedDefaultMode();
 
   /// Converts mode type to string.
   static const std::string ModeToString(AES_CIPHER_MODE m);
+  static AES_CIPHER_MODE StringToMode(std::string_view str);
 
  private:
   /// Helper method that encrypts/decrypts if 'encrypt' is true/false 
respectively.
   /// A buffer of input data 'data' of length 'len' is encrypted/decrypted 
with this
-  /// key into an output buffer 'out'. Exactly 'len' bytes will be written to 
'out'.
+  /// key into an output buffer 'out'.
+  /// The 'out' buffer must contain sufficient length to hold the extra 
padding block in
+  /// case of ECB mode. In other modes, 'out' buffer should be at least as big 
as the
+  /// length of the 'data' buffer.
   /// This key must be initialized before calling. Operates in-place if 'in' 
== 'out',
   /// otherwise the buffers must not overlap.
+  /// 'out_len' (if not NULL) will be set to the output length.
   Status EncryptInternal(bool encrypt, const uint8_t* data, int64_t len,
-      uint8_t* out) WARN_UNUSED_RESULT;
+      uint8_t* out, int64_t* out_len) WARN_UNUSED_RESULT;
 
   /// Check if mode m is supported at runtime
   static bool IsModeSupported(AES_CIPHER_MODE m);
@@ -198,9 +244,11 @@ class EncryptionKey {
 
   /// An AES 256-bit key.
   uint8_t key_[32];
+  int key_length_;
 
   /// An initialization vector to feed as the first block to AES.
   uint8_t iv_[AES_BLOCK_SIZE];
+  int iv_length_;
 
   /// Tag for GCM mode
   uint8_t gcm_tag_[AES_BLOCK_SIZE];
@@ -209,4 +257,4 @@ class EncryptionKey {
   AES_CIPHER_MODE mode_;
 };
 
-}
+}
\ No newline at end of file
diff --git a/common/function-registry/impala_functions.py 
b/common/function-registry/impala_functions.py
index f2ed85f97..eb7a53197 100644
--- a/common/function-registry/impala_functions.py
+++ b/common/function-registry/impala_functions.py
@@ -522,6 +522,12 @@ visible_functions = [
   [['ai_generate_text_default'], 'STRING', ['STRING'],
    'impala::AiFunctions::AiGenerateTextDefault'],
   [['bytes'], 'INT', ['STRING'], 'impala::StringFunctions::Bytes'],
+  [['aes_encrypt'], 'STRING', ['STRING', 'STRING', 'STRING', 'STRING'], 
'impala::StringFunctions::AesEncrypt',
+   
'_ZN6impala15StringFunctions10AesPrepareEPN10impala_udf15FunctionContextENS2_18FunctionStateScopeE',
+   
'_ZN6impala15StringFunctions8AesCloseEPN10impala_udf15FunctionContextENS2_18FunctionStateScopeE'],
+  [['aes_decrypt'], 'STRING', ['STRING', 'STRING', 'STRING', 'STRING'], 
'impala::StringFunctions::AesDecrypt',
+   
'_ZN6impala15StringFunctions10AesPrepareEPN10impala_udf15FunctionContextENS2_18FunctionStateScopeE',
+   
'_ZN6impala15StringFunctions8AesCloseEPN10impala_udf15FunctionContextENS2_18FunctionStateScopeE'],
   [['length'], 'INT', ['STRING'], 'impala::StringFunctions::Length'],
   [['length'], 'INT', ['BINARY'], 'impala::StringFunctions::Bytes'],
   [['length'], 'INT', ['CHAR'], 'impala::StringFunctions::CharLength'],
diff --git 
a/testdata/workloads/functional-query/queries/QueryTest/encryption_exprs.test 
b/testdata/workloads/functional-query/queries/QueryTest/encryption_exprs.test
new file mode 100644
index 000000000..e3d004096
--- /dev/null
+++ 
b/testdata/workloads/functional-query/queries/QueryTest/encryption_exprs.test
@@ -0,0 +1,456 @@
+====
+---- QUERY
+# AES encryption/decryption examples:
+select 
aes_decrypt(base64decode('y6Ss+zCYObpCbgfWfyNWTw=='),'1234567890123456','AES_128_ECB','');
+---- RESULTS
+'ABC'
+---- TYPES
+STRING
+====
+---- QUERY
+select 
aes_decrypt(base64decode('BQGHoM3lqYcsurCRq3PlUw=='),'1234567890123456','AES_128_ECB','');
+---- RESULTS
+''
+---- TYPES
+STRING
+====
+---- QUERY
+select 
aes_decrypt(base64decode('E1zl+pDv/GY4JLk254KAIQ=='),'12345678901234567890123456789012','AES_256_ECB','');
+---- RESULTS
+'ABC'
+---- TYPES
+STRING
+====
+---- QUERY
+select base64encode(aes_encrypt('ABC', 
'1234567890123456','AES_128_GCM','1234567890123456'));
+---- RESULTS
+'x+am+BIqtrEK9FpC/zrvpOycjQ=='
+---- TYPES
+STRING
+====
+---- QUERY
+select 
aes_decrypt(base64decode('x+am+BIqtrEK9FpC/zrvpOycjQ=='),'1234567890123456','AES_128_GCM','1234567890123456');
+---- RESULTS
+'ABC'
+---- TYPES
+STRING
+====
+---- QUERY
+select base64encode(aes_encrypt('', 
'1234567890123456','AES_128_GCM','1234567890123456'));
+---- RESULTS
+'moMhTz224yot8uRtksO+pw=='
+---- TYPES
+STRING
+====
+---- QUERY
+select 
aes_decrypt(base64decode('moMhTz224yot8uRtksO+pw=='),'1234567890123456','AES_128_GCM','1234567890123456');
+---- RESULTS
+''
+---- TYPES
+STRING
+====
+---- QUERY
+select base64encode(aes_encrypt('ABC', 
'12345678901234567890123456789012','AES_256_GCM','1234567890123456'));
+---- RESULTS
+'F/DLkSwEikFOlqzXVCysy1JX7Q=='
+---- TYPES
+STRING
+====
+---- QUERY
+select 
aes_decrypt(base64decode('F/DLkSwEikFOlqzXVCysy1JX7Q=='),'12345678901234567890123456789012','AES_256_GCM','1234567890123456');
+---- RESULTS
+'ABC'
+---- TYPES
+STRING
+====
+---- QUERY
+select aes_decrypt(aes_encrypt('ABC', 
'12345678901234567890123456789012','AES_256_GCM','1234567890123456'),
+'12345678901234567890123456789012','AES_256_GCM','1234567890123456');
+---- RESULTS
+'ABC'
+---- TYPES
+STRING
+====
+---- QUERY
+select 
base64encode(aes_encrypt(aes_decrypt(base64decode('F/DLkSwEikFOlqzXVCysy1JX7Q=='),
+'12345678901234567890123456789012','AES_256_GCM','1234567890123456'),'12345678901234567890123456789012','AES_256_GCM',
+'1234567890123456'));
+---- RESULTS
+'F/DLkSwEikFOlqzXVCysy1JX7Q=='
+---- TYPES
+STRING
+====
+---- QUERY
+select 
aes_decrypt(base64decode('ofMshL41XkG6NQ+v6bawAQ=='),'1234567890123456','AES_128_ECB','');
+---- RESULTS
+'Hello World!'
+---- TYPES
+STRING
+====
+---- QUERY
+select base64encode(aes_encrypt('The quick brown fox jumps over the lazy dog', 
'1234567890123456','AES_128_GCM',
+'1234567890123456'));
+---- RESULTS
+'0syAnPYY0qT/RNtD7s0UkV/4P7HSl8xI1ZCz9AINV3r5W0xLkUs/gAlXWD+i6TIx+WiWiuipZJMXLmM='
+---- TYPES
+STRING
+====
+---- QUERY
+select 
aes_decrypt(base64decode('0syAnPYY0qT/RNtD7s0UkV/4P7HSl8xI1ZCz9AINV3r5W0xLkUs/gAlXWD+i6TIx+WiWiuipZJMXLmM='),
+'1234567890123456','AES_128_GCM','1234567890123456');
+---- RESULTS
+'The quick brown fox jumps over the lazy dog'
+---- TYPES
+STRING
+====
+---- QUERY
+select base64encode(aes_encrypt('Impala', 
'1234567890123456','AES_128_GCM','12345678901'));
+---- RESULTS
+'sBDdvFMLqNtJvuHUP8s7oWaH0NWXNQ=='
+---- TYPES
+STRING
+====
+---- QUERY
+select 
aes_decrypt(base64decode('sBDdvFMLqNtJvuHUP8s7oWaH0NWXNQ=='),'1234567890123456','AES_128_GCM','12345678901');
+---- RESULTS
+'Impala'
+---- TYPES
+STRING
+====
+---- QUERY
+select aes_decrypt(aes_encrypt('Impala', 
'1234567890123456','AES_128_GCM','12345678901'),
+'1234567890123456','AES_128_GCM','12345678901');
+---- RESULTS
+'Impala'
+---- TYPES
+STRING
+====
+---- QUERY
+select base64encode(aes_encrypt('impalaaaaaaaaaaaaaaaaaaaa', 
'12345678901234567890123456789012','AES_256_CFB',
+'1234567890123456'));
+---- RESULTS
+'pkw4Y8WLkJnACfAL4R4BNUBzdnUeUo7vTw=='
+---- TYPES
+STRING
+====
+---- QUERY
+select 
aes_decrypt(base64decode('pkw4Y8WLkJnACfAL4R4BNUBzdnUeUo7vTw=='),'12345678901234567890123456789012',
+'AES_256_CFB','1234567890123456');
+---- RESULTS
+'impalaaaaaaaaaaaaaaaaaaaa'
+---- TYPES
+STRING
+====
+---- QUERY
+select base64encode(aes_encrypt('', 
'12345678901234567890123456789012','AES_256_CFB','1234567890123456'));
+---- RESULTS
+''
+---- TYPES
+STRING
+====
+---- QUERY
+select 
aes_decrypt(base64decode(''),'12345678901234567890123456789012','AES_256_CFB','1234567890123456');
+---- RESULTS
+''
+---- TYPES
+STRING
+====
+---- QUERY
+select base64encode(aes_encrypt('impalaaaaaaaaaaaaaaaaaaaa', 
'12345678901234567890123456789012','AES_256_CTR',
+'1234567890123456'));
+---- RESULTS
+'pkw4Y8WLkJnACfAL4R4BNb0HYMEjpcA63A=='
+---- TYPES
+STRING
+====
+---- QUERY
+select 
aes_decrypt(base64decode('pkw4Y8WLkJnACfAL4R4BNb0HYMEjpcA63A=='),'12345678901234567890123456789012',
+'AES_256_CTR','1234567890123456');
+---- RESULTS
+'impalaaaaaaaaaaaaaaaaaaaa'
+---- TYPES
+STRING
+====
+---- QUERY
+select base64encode(aes_encrypt('', 
'12345678901234567890123456789012','AES_256_CTR','1234567890123456'));
+---- RESULTS
+''
+---- TYPES
+STRING
+====
+---- QUERY
+select 
aes_decrypt(base64decode(''),'12345678901234567890123456789012','AES_256_CTR','1234567890123456');
+---- RESULTS
+''
+---- TYPES
+STRING
+====
+---- QUERY
+select aes_decrypt((aes_encrypt('impalaaaaaaaaaaaaaaaaaaaa', 
'12345678901234567890123456789012','AES_256_CFB',
+'1234567890123456')),'12345678901234567890123456789012','AES_256_CFB','1234567890123456');
+---- RESULTS
+'impalaaaaaaaaaaaaaaaaaaaa'
+---- TYPES
+STRING
+====
+---- QUERY
+select aes_decrypt((aes_encrypt('impalaaaaaaaaaaaaaaaaaaaa', 
'12345678901234567890123456789012','AES_256_CTR',
+'1234567890123456')),'12345678901234567890123456789012','AES_256_CTR','1234567890123456');
+---- RESULTS
+'impalaaaaaaaaaaaaaaaaaaaa'
+---- TYPES
+STRING
+====
+---- QUERY
+# Encryption/ decryption when mode is NULL, defaulting to GCM mode.
+select base64encode(aes_encrypt('ABC', 
'12345678901234567890123456789012',NULL,'1234567890123456'));
+---- RESULTS
+'F/DLkSwEikFOlqzXVCysy1JX7Q=='
+---- TYPES
+STRING
+====
+---- QUERY
+select aes_decrypt(aes_encrypt('ABC', 
'12345678901234567890123456789012','AES_256_GCM','1234567890123456'),
+'12345678901234567890123456789012',NULL,'1234567890123456');
+---- RESULTS
+'ABC'
+---- TYPES
+STRING
+====
+---- QUERY
+select 
aes_decrypt(base64decode('F/DLkSwEikFOlqzXVCysy1JX7Q=='),'12345678901234567890123456789012',NULL,'1234567890123456');
+---- RESULTS
+'ABC'
+---- TYPES
+STRING
+====
+---- QUERY
+# Encryption/ decryption with expr as NULL.
+select base64encode(aes_encrypt(NULL, 
'12345678901234567890123456789012','AES_256_GCM','1234567890123456'));
+---- RESULTS
+'NULL'
+---- TYPES
+STRING
+====
+---- QUERY
+select 
aes_decrypt(NULL,'12345678901234567890123456789012','AES_256_GCM','1234567890123456');
+---- RESULTS
+'NULL'
+---- TYPES
+STRING
+====
+---- QUERY
+select base64encode(aes_encrypt(NULL, 
'12345678901234567890123456789012','AES_256_CTR','1234567890123456'));
+---- RESULTS
+'NULL'
+---- TYPES
+STRING
+====
+---- QUERY
+select 
aes_decrypt(NULL,'12345678901234567890123456789012','AES_256_CTR','1234567890123456');
+---- RESULTS
+'NULL'
+---- TYPES
+STRING
+====
+---- QUERY
+select base64encode(aes_encrypt(NULL, 
'12345678901234567890123456789012','AES_256_CFB','1234567890123456'));
+---- RESULTS
+'NULL'
+---- TYPES
+STRING
+====
+---- QUERY
+select 
aes_decrypt(NULL,'12345678901234567890123456789012','AES_256_CFB','1234567890123456');
+---- RESULTS
+'NULL'
+---- TYPES
+STRING
+====
+---- QUERY
+# Key cannot be NULL.
+select base64encode(aes_encrypt('ABC',NULL,'AES_256_CFB','1234567890123456'));
+---- RESULTS
+---- CATCH
+Key cannot be NULL.
+====
+---- QUERY
+select 
aes_decrypt(base64decode('F/DLkSwEikFOlqzXVCysy1JX7Q=='),NULL,'AES_256_CFB','1234567890123456');
+---- RESULTS
+---- CATCH
+UDF ERROR: Key cannot be NULL.
+====
+---- QUERY
+# Key cannot be NULL, with the default mode GCM.
+select base64encode(aes_encrypt('ABC',NULL,'AES_256_GCM','1234567890123456'));
+---- RESULTS
+---- CATCH
+Key cannot be NULL.
+====
+---- QUERY
+select 
aes_decrypt(base64decode('F/DLkSwEikFOlqzXVCysy1JX7Q=='),NULL,'AES_256_GCM','1234567890123456');
+---- RESULTS
+---- CATCH
+UDF ERROR: Key cannot be NULL.
+====
+---- QUERY
+# ECB not supported for AES encryption.
+select base64encode(aes_encrypt('ABC', 
'12345678901234567890123456789012','AES_256_ECB','1234567890123456'));
+---- RESULTS
+---- CATCH
+ECB mode is not supported for encryption.
+====
+---- QUERY
+# iv cannot be NULL for GCM mode.
+select base64encode(aes_encrypt('ABC', 
'12345678901234567890123456789012','AES_256_GCM',NULL));
+---- RESULTS
+---- CATCH
+IV vector required for AES_256_GCM mode
+====
+---- QUERY
+select 
aes_decrypt(base64decode('F/DLkSwEikFOlqzXVCysy1JX7Q=='),'12345678901234567890123456789012','AES_256_GCM',NULL);
+---- RESULTS
+---- CATCH
+UDF ERROR: IV vector required for AES_256_GCM mode
+====
+---- QUERY
+# iv cannot be NULL for CFB mode.
+select base64encode(aes_encrypt('ABC', 
'12345678901234567890123456789012','AES_256_CFB',NULL));
+---- RESULTS
+---- CATCH
+IV vector required for AES_256_CFB mode
+====
+---- QUERY
+select 
aes_decrypt(base64decode('F/DLkSwEikFOlqzXVCysy1JX7Q=='),'12345678901234567890123456789012','AES_256_CFB',NULL);
+---- RESULTS
+---- CATCH
+UDF ERROR: IV vector required for AES_256_CFB mode
+====
+---- QUERY
+# iv cannot be NULL for CTR mode.
+select base64encode(aes_encrypt('ABC', 
'12345678901234567890123456789012','AES_256_CTR',NULL));
+---- RESULTS
+---- CATCH
+IV vector required for AES_256_CTR mode
+====
+---- QUERY
+select 
aes_decrypt(base64decode('F/DLkSwEikFOlqzXVCysy1JX7Q=='),'12345678901234567890123456789012','AES_256_CTR',NULL);
+---- RESULTS
+---- CATCH
+UDF ERROR: IV vector required for AES_256_CTR mode
+====
+---- QUERY
+# Error resulting due to user entered incorrect mode.
+select base64encode(aes_encrypt('ABC', 
'12345678901234567890123456789012','AES_256_CTB','1234567890123456'));
+---- RESULTS
+---- CATCH
+Invalid AES 'mode': AES_256_CTB
+====
+---- QUERY
+select 
aes_decrypt(base64decode('F/DLkSwEikFOlqzXVCysy1JX7Q=='),'12345678901234567890123456789012','AES_256_CTB',
+'1234567890123456');
+---- RESULTS
+---- CATCH
+UDF ERROR: Invalid AES 'mode': AES_256_CTB
+====
+---- QUERY
+# Error with incorrect key length.
+select base64encode(aes_encrypt('ABC', 
'123456789012345678901234567890121','AES_256_GCM','1234567890123456'));
+---- RESULTS
+---- CATCH
+AES only supports 128 and 256 bit key lengths
+====
+---- QUERY
+select 
aes_decrypt(base64decode('F/DLkSwEikFOlqzXVCysy1JX7Q=='),'123456789012345678901234567890121','AES_256_GCM',
+'1234567890123456');
+---- RESULTS
+---- CATCH
+UDF ERROR: AES only supports 128 and 256 bit key lengths
+====
+---- QUERY
+# Error with incorrect iv length.
+select base64encode(aes_encrypt('ABC', 
'12345678901234567890123456789012','AES_256_GCM','12345678901234567'));
+---- RESULTS
+---- CATCH
+IV vector size is greater than 16 bytes
+====
+---- QUERY
+select 
aes_decrypt(base64decode('F/DLkSwEikFOlqzXVCysy1JX7Q=='),'12345678901234567890123456789012','AES_256_GCM',
+'12345678901234567');
+---- RESULTS
+---- CATCH
+UDF ERROR: IV vector size is greater than 16 bytes
+====
+---- QUERY
+# Prefixes of supported modes are not accepted
+select base64encode(aes_encrypt('ABC', 
'12345678901234567890123456789012','','1234567890123456'));
+---- RESULTS
+---- CATCH
+Invalid AES 'mode':
+====
+---- QUERY
+select 
aes_decrypt(base64decode('F/DLkSwEikFOlqzXVCysy1JX7Q=='),'12345678901234567890123456789012','',
+'1234567890123456');
+---- RESULTS
+---- CATCH
+UDF ERROR: Invalid AES 'mode':
+====
+---- QUERY
+select base64encode(aes_encrypt('ABC', 
'12345678901234567890123456789012','AES_256','1234567890123456'));
+---- RESULTS
+---- CATCH
+Invalid AES 'mode': AES_256
+====
+---- QUERY
+select 
aes_decrypt(base64decode('F/DLkSwEikFOlqzXVCysy1JX7Q=='),'12345678901234567890123456789012','AES_256',
+'1234567890123456');
+---- RESULTS
+---- CATCH
+UDF ERROR: Invalid AES 'mode': AES_256
+====
+---- QUERY
+select base64encode(aes_encrypt('ABC', 
'12345678901234567890123456789012','AES_256_GC','1234567890123456'));
+---- RESULTS
+---- CATCH
+Invalid AES 'mode': AES_256_GC
+====
+---- QUERY
+select 
aes_decrypt(base64decode('F/DLkSwEikFOlqzXVCysy1JX7Q=='),'12345678901234567890123456789012','AES_256_GC',
+'1234567890123456');
+---- RESULTS
+---- CATCH
+UDF ERROR: Invalid AES 'mode': AES_256_GC
+====
+---- QUERY
+# Modes are case-insensitive.
+select base64encode(aes_encrypt('ABC', 
'12345678901234567890123456789012','aes_256_gcm','1234567890123456'));
+---- RESULTS
+'F/DLkSwEikFOlqzXVCysy1JX7Q=='
+---- TYPES
+STRING
+====
+---- QUERY
+select 
aes_decrypt(base64decode('F/DLkSwEikFOlqzXVCysy1JX7Q=='),'12345678901234567890123456789012','aes_256_gcm',
+'1234567890123456');
+---- RESULTS
+'ABC'
+---- TYPES
+STRING
+====
+---- QUERY
+select count(*) from functional.alltypes where string_col = 
aes_decrypt(aes_encrypt(string_col, '1234567890123456',
+'AES_128_GCM', '1234567890123456'), '1234567890123456', 'AES_128_GCM', 
'1234567890123456');
+---- RESULTS
+7300
+---- TYPES
+BIGINT
+====
+---- QUERY
+select count(*) from functional_parquet.alltypes where CAST(timestamp_col AS 
STRING) =
+aes_decrypt(aes_encrypt(CAST(timestamp_col AS STRING), '1234567890123456', 
'AES_128_GCM', '1234567890123456'),
+'1234567890123456', 'AES_128_GCM', '1234567890123456');
+---- RESULTS
+7300
+---- TYPES
+BIGINT
+====
\ No newline at end of file
diff --git a/tests/query_test/test_exprs.py b/tests/query_test/test_exprs.py
index 572277fed..bdfa188bb 100644
--- a/tests/query_test/test_exprs.py
+++ b/tests/query_test/test_exprs.py
@@ -72,6 +72,12 @@ class TestExprs(ImpalaTestSuite):
         vector.get_value('enable_expr_rewrites')
     self.run_test_case('QueryTest/special-strings', vector)
 
+  def test_encryption_exprs(self, vector):
+    """Test handling encryption/ decryption functionality"""
+    vector.get_value('exec_option')['enable_expr_rewrites'] = \
+      vector.get_value('enable_expr_rewrites')
+    self.run_test_case('QueryTest/encryption_exprs', vector)
+
 # Tests very deep expression trees and expressions with many children. Impala 
defines
 # a 'safe' upper bound on the expr depth and the number of expr children in the
 # FE Expr.java and any changes to those limits should be reflected in this 
test.

Reply via email to