From d932e0c557aa790251aa549d5fed4548f4bfbe63 Mon Sep 17 00:00:00 2001 From: Hailey Ho Date: Wed, 3 Dec 2025 17:31:58 +0000 Subject: [PATCH 1/3] Implement enc cli --- tool-openssl/CMakeLists.txt | 3 + tool-openssl/enc.cc | 183 ++++++++++++++++++ tool-openssl/enc_test.cc | 357 ++++++++++++++++++++++++++++++++++++ tool-openssl/internal.h | 4 +- tool-openssl/tool.cc | 3 +- 5 files changed, 547 insertions(+), 3 deletions(-) create mode 100644 tool-openssl/enc.cc create mode 100644 tool-openssl/enc_test.cc diff --git a/tool-openssl/CMakeLists.txt b/tool-openssl/CMakeLists.txt index 9a830e5d03d..e1ef92366c4 100644 --- a/tool-openssl/CMakeLists.txt +++ b/tool-openssl/CMakeLists.txt @@ -12,6 +12,7 @@ add_executable( dhparam.cc ecparam.cc ec.cc + enc.cc genrsa.cc pass_util.cc pkcs8.cc @@ -99,6 +100,8 @@ if(BUILD_TESTING) ec_test.cc ecparam.cc ecparam_test.cc + enc.cc + enc_test.cc genrsa.cc genrsa_test.cc ordered_args.cc diff --git a/tool-openssl/enc.cc b/tool-openssl/enc.cc new file mode 100644 index 00000000000..9c1351606a7 --- /dev/null +++ b/tool-openssl/enc.cc @@ -0,0 +1,183 @@ +// Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. +// SPDX-License-Identifier: Apache-2.0 OR ISC + +#include +#include +#include +#include +#include +#include "../tool/internal.h" +#include "internal.h" + +static const argument_t kArguments[] = { + // General options + {"-help", kBooleanArgument, "Display option summary"}, + {"-in", kOptionalArgument, "Input file, default stdin"}, + {"-out", kOptionalArgument, "Output file, default stdout"}, + {"-e", kBooleanArgument, "Encrypt"}, + {"-d", kBooleanArgument, "Decrypt"}, + {"-K", kOptionalArgument, "Raw key to use, in hex form"}, + {"-iv", kOptionalArgument, "IV to use, in hex form"}, + {"-aes-128-cbc", kExclusiveBooleanArgument, "Supported cipher"}, + {"", kOptionalArgument, ""}}; + +static bool HexToBinary(bssl::UniquePtr &buffer, + const std::string &hex_string, int size) { + // First validate that the string contains only valid hex characters + for (char c : hex_string) { + if (!OPENSSL_isxdigit(c)) { + return false; + } + } + + BIGNUM *raw = NULL; + if (BN_hex2bn(&raw, hex_string.c_str()) == 0) { + return false; + } + + int ret = BN_bn2binpad(raw, buffer.get(), size); + BN_free(raw); + return ret != -1; +} + +bool encTool(const args_list_t &args) { + ordered_args::ordered_args_map_t parsed_args; + args_list_t extra_args; + if (!ordered_args::ParseOrderedKeyValueArguments(parsed_args, extra_args, + args, kArguments) || + extra_args.size() > 0) { + PrintUsage(kArguments); + return false; + } + + std::string in_path, out_path, hiv, cipher_name; + bssl::UniquePtr hkey(new std::string); + bool help = false, e = false, d = false; + + ordered_args::GetBoolArgument(&help, "-help", parsed_args); + ordered_args::GetString(&in_path, "-in", "", parsed_args); + ordered_args::GetString(&out_path, "-out", "", parsed_args); + ordered_args::GetBoolArgument(&e, "-e", parsed_args); + ordered_args::GetBoolArgument(&d, "-d", parsed_args); + ordered_args::GetString(hkey.get(), "-K", "", parsed_args); + ordered_args::GetString(&hiv, "-iv", "", parsed_args); + ordered_args::GetExclusiveBoolArgument(&cipher_name, kArguments, "", + parsed_args); + + // Display enc tool option summary + if (help) { + PrintUsage(kArguments); + return true; + } + + // Since we do not implement key generation, a raw key is required + // TODO: remove/modify if we ever implement -k, -kfile, or -S + if (hkey->empty()) { + fprintf(stderr, "Error: A raw key is required\n"); + return false; + } + + if (e && d) { + fprintf(stderr, "Error: -e and -d are mutually exclusive\n"); + return false; + } + + bool enc = true; + if (d) { + enc = false; + } + + // Read from stdin if no -in path provided + ScopedFILE in_file; + if (in_path.empty()) { + in_file.reset(stdin); + } else { + in_file.reset(fopen(in_path.c_str(), "rb")); + if (!in_file) { + fprintf(stderr, "Error: unable to load certificate from '%s'\n", + in_path.c_str()); + return false; + } + } + + if (cipher_name.empty()) { + cipher_name = "aes-128-cbc"; + } else { + cipher_name = cipher_name.substr(1); + } + const EVP_CIPHER *cipher = EVP_get_cipherbyname(cipher_name.c_str()); + + int iv_length = EVP_CIPHER_iv_length(cipher); + bssl::UniquePtr iv((uint8_t *)OPENSSL_zalloc(EVP_MAX_IV_LENGTH)); + + if (!hiv.empty()) { + if (iv_length == 0) { + fprintf(stderr, "Warning: IV is not used by cipher %s\n", + cipher_name.c_str()); + } else { + if (!HexToBinary(iv, hiv, iv_length)) { + fprintf(stderr, "Error: Invalid hex IV value\n"); + return false; + } + } + } else { + if (iv_length != 0) { + fprintf(stderr, "Error: IV is required for cipher %s\n", + cipher_name.c_str()); + return false; + } + } + + bssl::UniquePtr key((uint8_t *)OPENSSL_zalloc(EVP_MAX_KEY_LENGTH)); + + if (!hkey->empty()) { + if (!HexToBinary(key, *hkey, EVP_CIPHER_key_length(cipher))) { + fprintf(stderr, "Error: Invalid hex key value\n"); + // pass_util::SensitiveStringDeleter(&hkey); + return false; + } + // pass_util::SensitiveStringDeleter(&hkey); + } + + bssl::UniquePtr output_bio; + if (out_path.empty()) { + output_bio.reset(BIO_new_fp(stdout, BIO_NOCLOSE)); + } else { + output_bio.reset(BIO_new(BIO_s_file())); + if (1 != BIO_write_filename(output_bio.get(), out_path.c_str())) { + fprintf(stderr, "Error: unable to write to '%s'\n", out_path.c_str()); + return false; + } + } + + // Create and initialize cipher context + bssl::UniquePtr ctx(EVP_CIPHER_CTX_new()); + if (!EVP_CipherInit_ex(ctx.get(), cipher, nullptr, key.get(), iv.get(), + enc)) { + fprintf(stderr, "Error: Failed to initialize cipher\n"); + return false; + } + + // Process the input file + uint8_t inbuf[1024]; + bssl::UniquePtr outbuf( + (uint8_t *)OPENSSL_zalloc(1024 + EVP_CIPHER_block_size(cipher))); + int inlen, outlen; + + while ((inlen = fread(inbuf, 1, sizeof(inbuf), in_file.get())) > 0) { + if (!EVP_CipherUpdate(ctx.get(), outbuf.get(), &outlen, inbuf, inlen)) { + fprintf(stderr, "Error: Cipher update failed\n"); + return false; + } + BIO_write(output_bio.get(), outbuf.get(), outlen); + } + + // Finalize + if (!EVP_CipherFinal_ex(ctx.get(), outbuf.get(), &outlen)) { + fprintf(stderr, "Error: Cipher final failed\n"); + return false; + } + BIO_write(output_bio.get(), outbuf.get(), outlen); + + return true; +} diff --git a/tool-openssl/enc_test.cc b/tool-openssl/enc_test.cc new file mode 100644 index 00000000000..199ee3bd01e --- /dev/null +++ b/tool-openssl/enc_test.cc @@ -0,0 +1,357 @@ +// Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. +// SPDX-License-Identifier: Apache-2.0 OR ISC + +#include +#include +#include +#include +#include "internal.h" +#include "test_util.h" + +class EncTest : public ::testing::Test { + protected: + void SetUp() override { + ASSERT_GT(createTempFILEpath(in_path), 0u); + ASSERT_GT(createTempFILEpath(out_path), 0u); + + // Create test input file with sample data + ScopedFILE in_file(fopen(in_path, "wb")); + ASSERT_TRUE(in_file); + const char *test_data = "Hello, World! This is test data for encryption."; + fwrite(test_data, 1, strlen(test_data), in_file.get()); + } + + void TearDown() override { + RemoveFile(in_path); + RemoveFile(out_path); + } + + char in_path[PATH_MAX]; + char out_path[PATH_MAX]; +}; + +// -------------------- Enc Basic Functionality Tests ------------------------- + +// Test help option +TEST_F(EncTest, EncToolHelpTest) { + args_list_t args = {"-help"}; + bool result = encTool(args); + ASSERT_TRUE(result); +} + +// Test basic encryption with AES-128-CBC +TEST_F(EncTest, EncToolBasicEncryptionTest) { + args_list_t args = {"-e", "-aes-128-cbc", + "-K", "0123456789abcdef0123456789abcdef", + "-iv", "0123456789abcdef0123456789abcdef", + "-in", in_path, + "-out", out_path}; + bool result = encTool(args); + ASSERT_TRUE(result); + + // Verify output file exists and has content + struct stat st; + ASSERT_EQ(stat(out_path, &st), 0); + ASSERT_GT(st.st_size, 0); +} + +// Test basic decryption with AES-128-CBC +TEST_F(EncTest, EncToolBasicDecryptionTest) { + // First encrypt + args_list_t encrypt_args = {"-e", "-aes-128-cbc", + "-K", "0123456789abcdef0123456789abcdef", + "-iv", "0123456789abcdef0123456789abcdef", + "-in", in_path, + "-out", out_path}; + bool result = encTool(encrypt_args); + ASSERT_TRUE(result); + + // Create temp file for decrypted output + char decrypt_path[PATH_MAX]; + ASSERT_GT(createTempFILEpath(decrypt_path), 0u); + + // Then decrypt + args_list_t decrypt_args = {"-d", "-aes-128-cbc", + "-K", "0123456789abcdef0123456789abcdef", + "-iv", "0123456789abcdef0123456789abcdef", + "-in", out_path, + "-out", decrypt_path}; + result = encTool(decrypt_args); + ASSERT_TRUE(result); + + // Verify decrypted content matches original + std::string original = ReadFileToString(in_path); + std::string decrypted = ReadFileToString(decrypt_path); + ASSERT_EQ(original, decrypted); + + RemoveFile(decrypt_path); +} + +// Test decryption with explicit -d flag +TEST_F(EncTest, EncToolExplicitDecryptionTest) { + // First encrypt + args_list_t encrypt_args = {"-e", "-aes-128-cbc", + "-K", "0123456789abcdef0123456789abcdef", + "-iv", "0123456789abcdef0123456789abcdef", + "-in", in_path, + "-out", out_path}; + bool result = encTool(encrypt_args); + ASSERT_TRUE(result); + + // Create temp file for decrypted output + char decrypt_path[PATH_MAX]; + ASSERT_GT(createTempFILEpath(decrypt_path), 0u); + + // Test explicit -d flag + args_list_t decrypt_args = {"-d", "-aes-128-cbc", + "-K", "0123456789abcdef0123456789abcdef", + "-iv", "0123456789abcdef0123456789abcdef", + "-in", out_path, + "-out", decrypt_path}; + result = encTool(decrypt_args); + ASSERT_TRUE(result); + + RemoveFile(decrypt_path); +} + +// Test decryption with default cipher +TEST_F(EncTest, EncToolDecryptionDefaultCipherTest) { + // First encrypt with default cipher + args_list_t encrypt_args = {"-e", + "-K", + "0123456789abcdef0123456789abcdef", + "-iv", + "0123456789abcdef0123456789abcdef", + "-in", + in_path, + "-out", + out_path}; + bool result = encTool(encrypt_args); + ASSERT_TRUE(result); + + // Create temp file for decrypted output + char decrypt_path[PATH_MAX]; + ASSERT_GT(createTempFILEpath(decrypt_path), 0u); + + // Decrypt with default cipher + args_list_t decrypt_args = {"-d", + "-K", + "0123456789abcdef0123456789abcdef", + "-iv", + "0123456789abcdef0123456789abcdef", + "-in", + out_path, + "-out", + decrypt_path}; + result = encTool(decrypt_args); + ASSERT_TRUE(result); + + RemoveFile(decrypt_path); +} + +// Test default cipher (should be aes-128-cbc) +TEST_F(EncTest, EncToolDefaultCipherTest) { + args_list_t args = {"-e", + "-K", + "0123456789abcdef0123456789abcdef", + "-iv", + "0123456789abcdef0123456789abcdef", + "-in", + in_path, + "-out", + out_path}; + bool result = encTool(args); + ASSERT_TRUE(result); +} + +// Test encryption without -e flag (should default to encrypt) +TEST_F(EncTest, EncToolDefaultEncryptTest) { + args_list_t args = {"-aes-128-cbc", + "-K", + "0123456789abcdef0123456789abcdef", + "-iv", + "0123456789abcdef0123456789abcdef", + "-in", + in_path, + "-out", + out_path}; + bool result = encTool(args); + ASSERT_TRUE(result); +} + +// -------------------- Enc Option Usage Error Tests -------------------------- + +class EncOptionUsageErrorsTest : public EncTest { + protected: + void TestOptionUsageErrors(const std::vector &args) { + args_list_t c_args; + for (const auto &arg : args) { + c_args.push_back(arg.c_str()); + } + bool result = encTool(c_args); + ASSERT_FALSE(result); + } +}; + +// Test missing required key +TEST_F(EncOptionUsageErrorsTest, MissingKeyTest) { + std::vector> testparams = { + {"-e", "-aes-128-cbc", "-iv", "0123456789abcdef0123456789abcdef", "-in", + in_path}, + {"-d", "-aes-128-cbc", "-iv", "0123456789abcdef0123456789abcdef", "-in", + in_path}, + {"-aes-128-cbc", "-iv", "0123456789abcdef0123456789abcdef", "-in", + in_path}}; + for (const auto &args : testparams) { + TestOptionUsageErrors(args); + } +} + +// Test mutually exclusive -e and -d options +TEST_F(EncOptionUsageErrorsTest, MutuallyExclusiveOptionsTest) { + std::vector> testparams = { + {"-e", "-d", "-aes-128-cbc", "-K", "0123456789abcdef0123456789abcdef", + "-iv", "0123456789abcdef0123456789abcdef", "-in", in_path}}; + for (const auto &args : testparams) { + TestOptionUsageErrors(args); + } +} + +// Test invalid hex key +TEST_F(EncOptionUsageErrorsTest, InvalidHexKeyTest) { + std::vector> testparams = { + {"-e", "-aes-128-cbc", "-K", "invalidhexkey", "-iv", + "0123456789abcdef0123456789abcdef", "-in", in_path}, + {"-e", "-aes-128-cbc", "-K", "0123456789abcdefg123456789abcdef", "-iv", + "0123456789abcdef0123456789abcdef", "-in", in_path}}; + for (const auto &args : testparams) { + TestOptionUsageErrors(args); + } +} + +// Test invalid hex IV +TEST_F(EncOptionUsageErrorsTest, InvalidHexIVTest) { + std::vector> testparams = { + {"-e", "-aes-128-cbc", "-K", "0123456789abcdef0123456789abcdef", "-iv", + "invalidhexiv", "-in", in_path}, + {"-e", "-aes-128-cbc", "-K", "0123456789abcdef0123456789abcdef", "-iv", + "0123456789abcdefg123456789abcdef", "-in", in_path}}; + for (const auto &args : testparams) { + TestOptionUsageErrors(args); + } +} + +// Test missing IV for cipher that requires it +TEST_F(EncOptionUsageErrorsTest, MissingIVTest) { + std::vector> testparams = { + {"-e", "-aes-128-cbc", "-K", "0123456789abcdef0123456789abcdef", "-in", + in_path}}; + for (const auto &args : testparams) { + TestOptionUsageErrors(args); + } +} + +// Test invalid input file +TEST_F(EncOptionUsageErrorsTest, InvalidInputFileTest) { + std::vector> testparams = { + {"-e", "-aes-128-cbc", "-K", "0123456789abcdef0123456789abcdef", "-iv", + "0123456789abcdef0123456789abcdef", "-in", "/nonexistent/file.txt"}}; + for (const auto &args : testparams) { + TestOptionUsageErrors(args); + } +} + +// -------------------- Enc OpenSSL Comparison Tests -------------------------- + +// Comparison tests cannot run without set up of environment variables: +// AWSLC_TOOL_PATH and OPENSSL_TOOL_PATH. + +class EncComparisonTest : public ::testing::Test { + protected: + void SetUp() override { + // Skip gtests if env variables not set + tool_executable_path = getenv("AWSLC_TOOL_PATH"); + openssl_executable_path = getenv("OPENSSL_TOOL_PATH"); + if (tool_executable_path == nullptr || openssl_executable_path == nullptr) { + GTEST_SKIP() << "Skipping test: AWSLC_TOOL_PATH and/or OPENSSL_TOOL_PATH " + "environment variables are not set"; + } + + ASSERT_GT(createTempFILEpath(in_path), 0u); + ASSERT_GT(createTempFILEpath(out_path_tool), 0u); + ASSERT_GT(createTempFILEpath(out_path_openssl), 0u); + + // Create test input file + ScopedFILE in_file(fopen(in_path, "wb")); + ASSERT_TRUE(in_file); + const char *test_data = + "Hello, World! This is test data for encryption comparison."; + fwrite(test_data, 1, strlen(test_data), in_file.get()); + } + + void TearDown() override { + RemoveFile(in_path); + RemoveFile(out_path_tool); + RemoveFile(out_path_openssl); + } + + char in_path[PATH_MAX]; + char out_path_tool[PATH_MAX]; + char out_path_openssl[PATH_MAX]; + const char *tool_executable_path; + const char *openssl_executable_path; +}; + +// Test encryption comparison with OpenSSL +TEST_F(EncComparisonTest, EncryptionComparisonTest) { + std::string key = "0123456789abcdef0123456789abcdef"; + std::string iv = "0123456789abcdef0123456789abcdef"; + + std::string tool_command = std::string(tool_executable_path) + + " enc -e -aes-128-cbc -K " + key + " -iv " + iv + + " -in " + in_path + " -out " + out_path_tool; + std::string openssl_command = + std::string(openssl_executable_path) + " enc -e -aes-128-cbc -K " + key + + " -iv " + iv + " -in " + in_path + " -out " + out_path_openssl; + + std::string tool_output_str, openssl_output_str; + RunCommandsAndCompareOutput(tool_command, openssl_command, out_path_tool, + out_path_openssl, tool_output_str, + openssl_output_str); + + // Compare encrypted outputs + ASSERT_EQ(tool_output_str, openssl_output_str); +} + +// Test decryption comparison with OpenSSL +TEST_F(EncComparisonTest, DecryptionComparisonTest) { + std::string key = "0123456789abcdef0123456789abcdef"; + std::string iv = "0123456789abcdef0123456789abcdef"; + + // First encrypt with OpenSSL to create encrypted data + char encrypted_path[PATH_MAX]; + ASSERT_GT(createTempFILEpath(encrypted_path), 0u); + + std::string openssl_encrypt_cmd = + std::string(openssl_executable_path) + " enc -e -aes-128-cbc -K " + key + + " -iv " + iv + " -in " + in_path + " -out " + encrypted_path; + ASSERT_EQ(ExecuteCommand(openssl_encrypt_cmd), 0); + + // Now test decryption comparison + std::string tool_command = + std::string(tool_executable_path) + " enc -d -aes-128-cbc -K " + key + + " -iv " + iv + " -in " + encrypted_path + " -out " + out_path_tool; + std::string openssl_command = + std::string(openssl_executable_path) + " enc -d -aes-128-cbc -K " + key + + " -iv " + iv + " -in " + encrypted_path + " -out " + out_path_openssl; + + std::string tool_output_str, openssl_output_str; + RunCommandsAndCompareOutput(tool_command, openssl_command, out_path_tool, + out_path_openssl, tool_output_str, + openssl_output_str); + + // Compare decrypted outputs + ASSERT_EQ(tool_output_str, openssl_output_str); + + RemoveFile(encrypted_path); +} diff --git a/tool-openssl/internal.h b/tool-openssl/internal.h index 6b31e4489de..1449a5036b8 100644 --- a/tool-openssl/internal.h +++ b/tool-openssl/internal.h @@ -89,9 +89,10 @@ tool_func_t FindTool(int argc, char **argv, int &starting_arg); bool CRLTool(const args_list_t &args); bool dgstTool(const args_list_t &args); bool dhparamTool(const args_list_t &args); +bool ecTool(const args_list_t &args); bool ecparamTool(const args_list_t &args); +bool encTool(const args_list_t &args); bool genrsaTool(const args_list_t &args); -bool ecTool(const args_list_t &args); bool md5Tool(const args_list_t &args); bool pkcs8Tool(const args_list_t &args); bool pkeyTool(const args_list_t &args); @@ -104,7 +105,6 @@ bool VerifyTool(const args_list_t &args); bool VersionTool(const args_list_t &args); bool X509Tool(const args_list_t &args); - // Req Tool Utilities bssl::UniquePtr ParseSubjectName(std::string &subject_string); diff --git a/tool-openssl/tool.cc b/tool-openssl/tool.cc index 6f8dcc0a54c..6d7fcd5643e 100644 --- a/tool-openssl/tool.cc +++ b/tool-openssl/tool.cc @@ -15,12 +15,13 @@ #include "./internal.h" -static const std::array kTools = {{ +static const std::array kTools = {{ {"crl", CRLTool}, {"dgst", dgstTool}, {"dhparam", dhparamTool}, {"ec", ecTool}, {"ecparam", ecparamTool}, + {"enc", encTool}, {"genrsa", genrsaTool}, {"md5", md5Tool}, {"pkcs8", pkcs8Tool}, From 66b47f1e1f378273162fa302e21df919dc8e0787 Mon Sep 17 00:00:00 2001 From: Hailey Ho Date: Sat, 6 Dec 2025 00:25:56 +0000 Subject: [PATCH 2/3] Address comments --- tool-openssl/enc.cc | 47 +++++++++++++++++++++++++++++----------- tool-openssl/enc_test.cc | 20 +++++++++++++++++ 2 files changed, 54 insertions(+), 13 deletions(-) diff --git a/tool-openssl/enc.cc b/tool-openssl/enc.cc index 9c1351606a7..3cfe99d873f 100644 --- a/tool-openssl/enc.cc +++ b/tool-openssl/enc.cc @@ -9,6 +9,8 @@ #include "../tool/internal.h" #include "internal.h" +#define BUF_SIZE 1024 + static const argument_t kArguments[] = { // General options {"-help", kBooleanArgument, "Display option summary"}, @@ -21,8 +23,8 @@ static const argument_t kArguments[] = { {"-aes-128-cbc", kExclusiveBooleanArgument, "Supported cipher"}, {"", kOptionalArgument, ""}}; -static bool HexToBinary(bssl::UniquePtr &buffer, - const std::string &hex_string, int size) { +static bool HexToBinary(bssl::UniquePtr &buffer, + const std::string &hex_string, unsigned int size) { // First validate that the string contains only valid hex characters for (char c : hex_string) { if (!OPENSSL_isxdigit(c)) { @@ -30,6 +32,10 @@ static bool HexToBinary(bssl::UniquePtr &buffer, } } + if (hex_string.size() != size * 2) { + return false; + } + BIGNUM *raw = NULL; if (BN_hex2bn(&raw, hex_string.c_str()) == 0) { return false; @@ -94,7 +100,7 @@ bool encTool(const args_list_t &args) { } else { in_file.reset(fopen(in_path.c_str(), "rb")); if (!in_file) { - fprintf(stderr, "Error: unable to load certificate from '%s'\n", + fprintf(stderr, "Error: unable to load data from '%s'\n", in_path.c_str()); return false; } @@ -107,8 +113,13 @@ bool encTool(const args_list_t &args) { } const EVP_CIPHER *cipher = EVP_get_cipherbyname(cipher_name.c_str()); - int iv_length = EVP_CIPHER_iv_length(cipher); - bssl::UniquePtr iv((uint8_t *)OPENSSL_zalloc(EVP_MAX_IV_LENGTH)); + if (cipher == nullptr) { + fprintf(stderr, "Error: Unknown cipher %s\n", cipher_name.c_str()); + return false; + } + + unsigned int iv_length = EVP_CIPHER_iv_length(cipher); + bssl::UniquePtr iv((uint8_t *)OPENSSL_zalloc(EVP_MAX_IV_LENGTH)); if (!hiv.empty()) { if (iv_length == 0) { @@ -128,15 +139,13 @@ bool encTool(const args_list_t &args) { } } - bssl::UniquePtr key((uint8_t *)OPENSSL_zalloc(EVP_MAX_KEY_LENGTH)); + bssl::UniquePtr key((uint8_t *)OPENSSL_zalloc(EVP_MAX_KEY_LENGTH)); if (!hkey->empty()) { if (!HexToBinary(key, *hkey, EVP_CIPHER_key_length(cipher))) { fprintf(stderr, "Error: Invalid hex key value\n"); - // pass_util::SensitiveStringDeleter(&hkey); return false; } - // pass_util::SensitiveStringDeleter(&hkey); } bssl::UniquePtr output_bio; @@ -159,12 +168,23 @@ bool encTool(const args_list_t &args) { } // Process the input file - uint8_t inbuf[1024]; - bssl::UniquePtr outbuf( - (uint8_t *)OPENSSL_zalloc(1024 + EVP_CIPHER_block_size(cipher))); - int inlen, outlen; + uint8_t inbuf[BUF_SIZE]; + bssl::UniquePtr outbuf( + (uint8_t *)OPENSSL_zalloc(BUF_SIZE + EVP_CIPHER_block_size(cipher))); + int inlen = 0, outlen = 0; + + for (;;) { + if (feof(in_file.get())) { + break; + } + + inlen = fread(inbuf, 1, sizeof(inbuf), in_file.get()); + + if (ferror(in_file.get())) { + fprintf(stderr, "Error reading from '%s'.\n", in_path.c_str()); + return false; + } - while ((inlen = fread(inbuf, 1, sizeof(inbuf), in_file.get())) > 0) { if (!EVP_CipherUpdate(ctx.get(), outbuf.get(), &outlen, inbuf, inlen)) { fprintf(stderr, "Error: Cipher update failed\n"); return false; @@ -177,6 +197,7 @@ bool encTool(const args_list_t &args) { fprintf(stderr, "Error: Cipher final failed\n"); return false; } + BIO_write(output_bio.get(), outbuf.get(), outlen); return true; diff --git a/tool-openssl/enc_test.cc b/tool-openssl/enc_test.cc index 199ee3bd01e..60e0d9ea167 100644 --- a/tool-openssl/enc_test.cc +++ b/tool-openssl/enc_test.cc @@ -241,6 +241,26 @@ TEST_F(EncOptionUsageErrorsTest, InvalidHexIVTest) { } } +// Test hex string size mismatch for key and IV +TEST_F(EncOptionUsageErrorsTest, HexStringSizeMismatchTest) { + std::vector> testparams = { + // Key too short (AES-128 needs 32 hex chars, providing 30) + {"-e", "-aes-128-cbc", "-K", "0123456789abcdef0123456789abcd", "-iv", + "0123456789abcdef0123456789abcdef", "-in", in_path}, + // Key too long (AES-128 needs 32 hex chars, providing 34) + {"-e", "-aes-128-cbc", "-K", "0123456789abcdef0123456789abcdef01", "-iv", + "0123456789abcdef0123456789abcdef", "-in", in_path}, + // IV too short (AES-128-CBC needs 32 hex chars, providing 30) + {"-e", "-aes-128-cbc", "-K", "0123456789abcdef0123456789abcdef", "-iv", + "0123456789abcdef0123456789abcd", "-in", in_path}, + // IV too long (AES-128-CBC needs 32 hex chars, providing 34) + {"-e", "-aes-128-cbc", "-K", "0123456789abcdef0123456789abcdef", "-iv", + "0123456789abcdef0123456789abcdef01", "-in", in_path}}; + for (const auto &args : testparams) { + TestOptionUsageErrors(args); + } +} + // Test missing IV for cipher that requires it TEST_F(EncOptionUsageErrorsTest, MissingIVTest) { std::vector> testparams = { From 4b0d8e6b1d26c56466c37691ccc06ad29821ed3c Mon Sep 17 00:00:00 2001 From: Justin W Smith <103147162+justsmth@users.noreply.github.com> Date: Mon, 8 Dec 2025 09:23:50 -0500 Subject: [PATCH 3/3] 19 tools --- tool-openssl/tool.cc | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tool-openssl/tool.cc b/tool-openssl/tool.cc index 315aecb65dc..1c1fe14e305 100644 --- a/tool-openssl/tool.cc +++ b/tool-openssl/tool.cc @@ -15,7 +15,7 @@ #include "./internal.h" -static const std::array kTools = {{ +static const std::array kTools = {{ {"crl", CRLTool}, {"dgst", dgstTool}, {"dhparam", dhparamTool},