diff --git a/tests/ci/run_openssl_comparison_tests.sh b/tests/ci/run_openssl_comparison_tests.sh index 8ad7886373..6d6d3b11e4 100644 --- a/tests/ci/run_openssl_comparison_tests.sh +++ b/tests/ci/run_openssl_comparison_tests.sh @@ -39,7 +39,10 @@ declare -A openssl_branches=( export AWSLC_TOOL_PATH="${BUILD_ROOT}/tool-openssl/openssl" for branch in "${!openssl_branches[@]}"; do export OPENSSL_TOOL_PATH="${install_dir}/openssl-${branch}/bin/openssl" - echo "Running X509ComparisonTests against OpenSSL ${branch}" - LD_LIBRARY_PATH="${install_dir}/openssl-${branch}/${openssl_branches[$branch]}" "${BUILD_ROOT}/tool-openssl/tool_openssl_test" --gtest_filter=X509ComparisonTest.* + LD_LIBRARY_PATH="${install_dir}/openssl-${branch}/${openssl_branches[$branch]}" + for test in X509ComparisonTest RSAComparisonTest MD5ComparisonTest; do + echo "Running ${test} against OpenSSL ${branch}" + LD_LIBRARY_PATH="${install_dir}/openssl-${branch}/${openssl_branches[$branch]}" "${BUILD_ROOT}/tool-openssl/tool_openssl_test" --gtest_filter=${test}.* + done done diff --git a/tool-openssl/CMakeLists.txt b/tool-openssl/CMakeLists.txt index 7f5b4231a0..ad48d7fd21 100644 --- a/tool-openssl/CMakeLists.txt +++ b/tool-openssl/CMakeLists.txt @@ -5,6 +5,8 @@ add_executable( ../tool/file.cc tool.cc x509.cc + rsa.cc + md5.cc ) target_include_directories(openssl PUBLIC ${PROJECT_SOURCE_DIR}/include) @@ -43,10 +45,14 @@ if(BUILD_TESTING) add_executable( tool_openssl_test - x509_test.cc ../tool/args.cc ../tool/file.cc + x509_test.cc + rsa_test.cc + md5_test.cc x509.cc + rsa.cc + md5.cc ) target_link_libraries(tool_openssl_test boringssl_gtest_main ssl crypto) diff --git a/tool-openssl/README.md b/tool-openssl/README.md index 5bbd4961b2..470608deff 100644 --- a/tool-openssl/README.md +++ b/tool-openssl/README.md @@ -2,6 +2,11 @@ *Files expected to change* Current status: -* Contains initial implementation for OpenSSL x509 tool, options -in -out, -req, -signkey, -modulus, -days, -dates, - -checkend, -noout (x509.cc), and unit test (x509_test.cc) -* OpenSSL rsa tool yet to be implemented +* Contains initial implementation for OpenSSL x509, rsa, and md5 tools + * x509 options: -in -out, -req, -signkey, -modulus, -days, -dates, + -checkend, -noout (x509.cc) + * rsa options: -in, -out, -noout, -modulus (rsa.cc) + * md5 options: N/A (md5.cc) +* Unit, integration, and OpenSSL comparison tests (x509_test.cc, rsa_test.cc, md5_test.cc) + * OpenSSL comparison tests require environment variables for both AWS-LC ("AWSLC_TOOL_PATH") and OpenSSL ("OPENSSL_TOOL_PATH") tools + diff --git a/tool-openssl/internal.h b/tool-openssl/internal.h index 1eba7fba2a..153c805f9d 100644 --- a/tool-openssl/internal.h +++ b/tool-openssl/internal.h @@ -8,6 +8,8 @@ #include #include + + typedef bool (*tool_func_t)(const std::vector &args); bool IsNumeric(const std::string& str); @@ -22,5 +24,7 @@ tool_func_t FindTool(const std::string &name); tool_func_t FindTool(int argc, char **argv, int &starting_arg); bool X509Tool(const args_list_t &args); +bool rsaTool(const args_list_t &args); +bool md5Tool(const args_list_t &args); #endif //INTERNAL_H diff --git a/tool-openssl/md5.cc b/tool-openssl/md5.cc new file mode 100644 index 0000000000..74333f7144 --- /dev/null +++ b/tool-openssl/md5.cc @@ -0,0 +1,50 @@ +// Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. +// SPDX-License-Identifier: Apache-2.0 OR ISC + +#include +#include +#include "internal.h" + +// MD5 command currently only supports stdin +static const argument_t kArguments[] = { + { "-help", kBooleanArgument, "Display option summary" }, + { "", kOptionalArgument, "" } +}; + +// Map arguments using tool/args.cc +bool md5Tool(const args_list_t &args) { + args_map_t parsed_args; + if (!ParseKeyValueArguments(&parsed_args, args, kArguments)) { + PrintUsage(kArguments); + return false; + } + + bool help = false; + GetBoolArgument(&help, "-help", parsed_args); + + if (help) { + PrintUsage(kArguments); + return false; + } + + // Read input from stdin + std::string input; + std::getline(std::cin, input); + + if (input.empty()) { + fprintf(stderr, "Error: no input provided\n"); + return false; + } + + unsigned char md5_digest[MD5_DIGEST_LENGTH]; + MD5(reinterpret_cast(input.c_str()), input.length(), md5_digest); + + printf("(stdin)= "); + for (int i = 0; i < MD5_DIGEST_LENGTH; ++i) { + printf("%02x", md5_digest[i]); + } + printf("\n"); + + return true; +} + diff --git a/tool-openssl/md5_test.cc b/tool-openssl/md5_test.cc new file mode 100644 index 0000000000..061fb3a8cc --- /dev/null +++ b/tool-openssl/md5_test.cc @@ -0,0 +1,93 @@ +// Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. +// SPDX-License-Identifier: Apache-2.0 OR ISC + +#include +#include +#include "internal.h" +#include "test_util.h" +#include + + +#ifdef _WIN32 +#include +#ifndef PATH_MAX +#define PATH_MAX MAX_PATH +#endif +#else +#include +#ifndef PATH_MAX +#define PATH_MAX 4096 +#endif +#endif + +size_t createTempFILEpath(char buffer[PATH_MAX]); + +std::string GetHash(const std::string& str); + + +// -------------------- MD5 OpenSSL Comparison Test --------------------------- + +// Comparison tests cannot run without set up of environment variables: +// AWSLC_TOOL_PATH and OPENSSL_TOOL_PATH. + +class MD5ComparisonTest : 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); + } + void TearDown() override { + if (tool_executable_path != nullptr && openssl_executable_path != nullptr) { + 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]; + bssl::UniquePtr rsa; + const char* tool_executable_path; + const char* openssl_executable_path; + std::string tool_output_str; + std::string openssl_output_str; +}; + +// OpenSSL versions 3.1.0 and later change from "(stdin)= " to "MD5(stdin) =" +std::string GetHash(const std::string& str) { + size_t pos = str.find('='); + if (pos == std::string::npos) { + return ""; + } + return str.substr(pos + 1); +} + +// Test against OpenSSL output +TEST_F(MD5ComparisonTest, MD5ToolCompareOpenSSL) { + std::string input_file = std::string(in_path); + std::ofstream ofs(input_file); + ofs << "AWS_LC_TEST_STRING_INPUT"; + ofs.close(); + + std::string tool_command = std::string(tool_executable_path) + " md5 < " + input_file + " > " + out_path_tool; + std::string openssl_command = std::string(openssl_executable_path) + " md5 < " + input_file + " > " + out_path_openssl; + + RunCommandsAndCompareOutput(tool_command, openssl_command, out_path_tool, out_path_openssl, tool_output_str, openssl_output_str); + + std::string tool_hash = GetHash(tool_output_str); + std::string openssl_hash = GetHash(openssl_output_str); + tool_hash = trim(tool_hash); + openssl_hash = trim(openssl_hash); + + ASSERT_EQ(tool_hash, openssl_hash); + + RemoveFile(input_file.c_str()); +} diff --git a/tool-openssl/rsa.cc b/tool-openssl/rsa.cc new file mode 100644 index 0000000000..9cc4da79a7 --- /dev/null +++ b/tool-openssl/rsa.cc @@ -0,0 +1,102 @@ +// Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. +// SPDX-License-Identifier: Apache-2.0 OR ISC + +#include +#include +#include "internal.h" + +static const argument_t kArguments[] = { + { "-help", kBooleanArgument, "Display option summary" }, + { "-in", kOptionalArgument, "RSA key input file" }, + { "-out", kOptionalArgument, "Output file to write to" }, + { "-noout", kBooleanArgument, "Prevents output of the encoded version of the RSA key" }, + { "-modulus", kBooleanArgument, "Prints out the value of the modulus of the RSA key" }, + { "", kOptionalArgument, "" } +}; + +// Map arguments using tool/args.cc +bool rsaTool(const args_list_t &args) { + args_map_t parsed_args; + if (!ParseKeyValueArguments(&parsed_args, args, kArguments)) { + PrintUsage(kArguments); + return false; + } + + std::string in_path, out_path; + bool noout = false, modulus = false; + bool help = false; + + GetBoolArgument(&help, "-help", parsed_args); + GetString(&in_path, "-in", "", parsed_args); + GetString(&out_path, "-out", "", parsed_args); + GetBoolArgument(&noout, "-noout", parsed_args); + GetBoolArgument(&modulus, "-modulus", parsed_args); + + // Display rsa tool option summary + if (help) { + PrintUsage(kArguments); + return false; + } + + // Check for required option -in + if (in_path.empty()) { + fprintf(stderr, "Error: missing required argument '-in'\n"); + return false; + } + + ScopedFILE in_file(fopen(in_path.c_str(), "rb")); + if (!in_file) { + fprintf(stderr, "Error: unable to load RSA key from '%s'\n", in_path.c_str()); + return false; + } + + bssl::UniquePtr rsa(PEM_read_RSAPrivateKey(in_file.get(), nullptr, nullptr, nullptr)); + if (!rsa) { + fprintf(stderr, "Error: unable to read RSA private key from '%s'\n", in_path.c_str()); + return false; + } + + ScopedFILE out_file; + if (!out_path.empty()) { + out_file.reset(fopen(out_path.c_str(), "wb")); + if (!out_file) { + fprintf(stderr, "Error: unable to open output file '%s'\n", out_path.c_str()); + return false; + } + } + + if (modulus) { + const BIGNUM *n = RSA_get0_n(rsa.get()); + if (!n) { + fprintf(stderr, "Error: unable to load modulus\n"); + return false; + } + char *hex_modulus = BN_bn2hex(n); + if (!hex_modulus) { + fprintf(stderr, "Error: unable to convert modulus to hex\n"); + return false; + } + for (char *p = hex_modulus; *p; ++p) { + *p = toupper(*p); + } + if (out_file) { + fprintf(out_file.get(), "Modulus=%s\n", hex_modulus); + } else { + printf("Modulus=%s\n", hex_modulus); + } + OPENSSL_free(hex_modulus); + } + + if (!noout) { + if (out_file) { + if (!PEM_write_RSAPrivateKey(out_file.get(), rsa.get(), nullptr, nullptr, 0, nullptr, nullptr)) { + fprintf(stderr, "Error: unable to write RSA private key to '%s'\n", out_path.c_str()); + return false; + } + } else { + PEM_write_RSAPrivateKey(stdout, rsa.get(), nullptr, nullptr, 0, nullptr, nullptr); + } + } + + return true; +} diff --git a/tool-openssl/rsa_test.cc b/tool-openssl/rsa_test.cc new file mode 100644 index 0000000000..10fba1e09f --- /dev/null +++ b/tool-openssl/rsa_test.cc @@ -0,0 +1,244 @@ +// Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. +// SPDX-License-Identifier: Apache-2.0 OR ISC + +#include "openssl/rsa.h" +#include +#include +#include "internal.h" +#include "test_util.h" +#include + + +#ifdef _WIN32 +#include +#ifndef PATH_MAX +#define PATH_MAX MAX_PATH +#endif +#else +#include +#ifndef PATH_MAX +#define PATH_MAX 4096 +#endif +#endif + +size_t createTempFILEpath(char buffer[PATH_MAX]); + +bool CheckBoundaries(const std::string &content, const std::string &begin1, const std::string &end1, const std::string &begin2, const std::string &end2); + +RSA* CreateRSAKey(); + +RSA* CreateRSAKey() { + bssl::UniquePtr bn(BN_new()); + if (!bn || !BN_set_word(bn.get(), RSA_F4)) { + return nullptr; + } + bssl::UniquePtr rsa(RSA_new()); + if (!rsa || !RSA_generate_key_ex(rsa.get(), 2048, bn.get(), nullptr)) { + return nullptr; + } + return rsa.release(); +} + +class RSATest : public ::testing::Test { +protected: + void SetUp() override { + ASSERT_GT(createTempFILEpath(in_path), 0u); + ASSERT_GT(createTempFILEpath(out_path), 0u); + + bssl::UniquePtr rsa(CreateRSAKey()); + ASSERT_TRUE(rsa); + + ScopedFILE in_file(fopen(in_path, "wb")); + ASSERT_TRUE(in_file); + ASSERT_TRUE(PEM_write_RSAPrivateKey(in_file.get(), rsa.get(), nullptr, nullptr, 0, nullptr, nullptr)); + } + void TearDown() override { + RemoveFile(in_path); + RemoveFile(out_path); + } + char in_path[PATH_MAX]; + char out_path[PATH_MAX]; +}; + +// ----------------------------- RSA Option Tests ----------------------------- + +// Test -in and -out +TEST_F(RSATest, RSAToolInOutTest) { + args_list_t args = {"-in", in_path, "-out", out_path}; + bool result = rsaTool(args); + ASSERT_TRUE(result); + { + ScopedFILE out_file(fopen(out_path, "rb")); + ASSERT_TRUE(out_file); + bssl::UniquePtr parsed_rsa(PEM_read_RSAPrivateKey(out_file.get(), nullptr, nullptr, nullptr)); + ASSERT_TRUE(parsed_rsa); + } +} + +// Test -modulus +TEST_F(RSATest, RSAToolModulusTest) { + args_list_t args = {"-in", in_path, "-modulus"}; + bool result = rsaTool(args); + ASSERT_TRUE(result); +} + +// Test -noout +TEST_F(RSATest, RSAToolNooutTest) { + args_list_t args = {"-in", in_path, "-noout"}; + bool result = rsaTool(args); + ASSERT_TRUE(result); +} + + +// -------------------- RSA Option Usage Error Tests -------------------------- + +class RSAOptionUsageErrorsTest : public RSATest { +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 = rsaTool(c_args); + ASSERT_FALSE(result); + } +}; + +// Test missing -in required option +TEST_F(RSAOptionUsageErrorsTest, RequiredOptionTests) { + std::vector> testparams = { + {"-out", "output.pem"}, + {"-modulus"}, + }; + for (const auto& args : testparams) { + TestOptionUsageErrors(args); + } +} + +// -------------------- RSA OpenSSL Comparison Tests -------------------------- + +// Comparison tests cannot run without set up of environment variables: +// AWSLC_TOOL_PATH and OPENSSL_TOOL_PATH. + +class RSAComparisonTest : 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); + + rsa.reset(CreateRSAKey()); + ASSERT_TRUE(rsa); + + ScopedFILE in_file(fopen(in_path, "wb")); + ASSERT_TRUE(in_file); + ASSERT_TRUE(PEM_write_RSAPrivateKey(in_file.get(), rsa.get(), nullptr, nullptr, 0, nullptr, nullptr)); + } + + void TearDown() override { + if (tool_executable_path != nullptr && openssl_executable_path != nullptr) { + 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]; + bssl::UniquePtr rsa; + const char* tool_executable_path; + const char* openssl_executable_path; + std::string tool_output_str; + std::string openssl_output_str; +}; + + +// RSA boundaries +const std::string RSA_BEGIN = "-----BEGIN RSA PRIVATE KEY-----"; +const std::string RSA_END = "-----END RSA PRIVATE KEY-----"; +const std::string BEGIN = "-----BEGIN PRIVATE KEY-----"; +const std::string END = "-----END PRIVATE KEY-----"; +const std::string MODULUS = "Modulus="; + +// OpenSSL versions 3.1.0 and later change PEM outputs from "BEGIN RSA PRIVATE KEY" to "BEGIN PRIVATE KEY" +bool CheckBoundaries(const std::string &content, const std::string &begin1, const std::string &end1, const std::string &begin2, const std::string &end2) { + return (content.compare(0, begin1.size(), begin1) == 0 && content.compare(content.size() - end1.size(), end1.size(), end1) == 0) || + (content.compare(0, begin2.size(), begin2) == 0 && content.compare(content.size() - end2.size(), end2.size(), end2) == 0); +} + +// Test against OpenSSL output "openssl rsa -in file -modulus" +// Rsa private key is printed to stdin +TEST_F(RSAComparisonTest, RSAToolCompareModulusOpenSSL) { + std::string tool_command = std::string(tool_executable_path) + " rsa -in " + in_path + " > " + out_path_tool; + std::string openssl_command = std::string(openssl_executable_path) + " rsa -in " + in_path + " > " + out_path_openssl; + + RunCommandsAndCompareOutput(tool_command, openssl_command, out_path_tool, out_path_openssl, tool_output_str, openssl_output_str); + + trim(tool_output_str); + ASSERT_TRUE(CheckBoundaries(tool_output_str, RSA_BEGIN, RSA_END, BEGIN, END)); + + trim(openssl_output_str); + ASSERT_TRUE(CheckBoundaries(openssl_output_str, RSA_BEGIN, RSA_END, BEGIN, END)); +} + +// Test against OpenSSL output "openssl rsa -in file -modulus -noout" +// Only modulus is printed to stdin +TEST_F(RSAComparisonTest, RSAToolCompareModulusNooutOpenSSL) { + std::string tool_command = std::string(tool_executable_path) + " rsa -in " + in_path + " -modulus -noout > " + out_path_tool; + std::string openssl_command = std::string(openssl_executable_path) + " rsa -in " + in_path + " -modulus -noout > " + out_path_openssl; + + RunCommandsAndCompareOutput(tool_command, openssl_command, out_path_tool, out_path_openssl, tool_output_str, openssl_output_str); + + ASSERT_EQ(tool_output_str, openssl_output_str); +} + +// Test against OpenSSL output "openssl rsa -in file -modulus -out out_file" +// Modulus and rsa private key are printed to output file +TEST_F(RSAComparisonTest, RSAToolCompareModulusOutOpenSSL) { + std::string tool_command = std::string(tool_executable_path) + " rsa -in " + in_path + " -modulus -out " + out_path_tool; + std::string openssl_command = std::string(openssl_executable_path) + " rsa -in " + in_path + " -modulus -out " + out_path_openssl; + + RunCommandsAndCompareOutput(tool_command, openssl_command, out_path_tool, out_path_openssl, tool_output_str, openssl_output_str); + + ScopedFILE tool_out_file(fopen(out_path_tool, "rb")); + ASSERT_TRUE(tool_out_file); + ScopedFILE openssl_out_file(fopen(out_path_openssl, "rb")); + ASSERT_TRUE(openssl_out_file); + + tool_output_str = ReadFileToString(out_path_tool); + openssl_output_str = ReadFileToString(out_path_openssl); + trim(tool_output_str); + ASSERT_TRUE(CheckBoundaries(tool_output_str, MODULUS, RSA_END, MODULUS, END)); + + trim(openssl_output_str); + ASSERT_TRUE(CheckBoundaries(openssl_output_str, MODULUS, RSA_END, MODULUS, END)); +} + +// Test against OpenSSL output "openssl rsa -in file -modulus -out out_file -noout" +// Only modulus is printed to output file +TEST_F(RSAComparisonTest, RSAToolCompareModulusOutNooutOpenSSL) { + std::string tool_command = std::string(tool_executable_path) + " rsa -in " + in_path + " -modulus -out " + out_path_tool + " -noout"; + std::string openssl_command = std::string(openssl_executable_path) + " rsa -in " + in_path + " -modulus -out " + out_path_openssl + " -noout"; + + RunCommandsAndCompareOutput(tool_command, openssl_command, out_path_tool, out_path_openssl, tool_output_str, openssl_output_str); + + ScopedFILE tool_out_file(fopen(out_path_tool, "rb")); + ASSERT_TRUE(tool_out_file); + ScopedFILE openssl_out_file(fopen(out_path_openssl, "rb")); + ASSERT_TRUE(openssl_out_file); + + tool_output_str = ReadFileToString(out_path_tool); + openssl_output_str = ReadFileToString(out_path_openssl); + trim(tool_output_str); + trim(openssl_output_str); + ASSERT_EQ(tool_output_str, openssl_output_str); +} diff --git a/tool-openssl/test_util.h b/tool-openssl/test_util.h new file mode 100644 index 0000000000..0792f4ca0c --- /dev/null +++ b/tool-openssl/test_util.h @@ -0,0 +1,67 @@ +// Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. +// SPDX-License-Identifier: Apache-2.0 OR ISC + +#ifndef TEST_UTIL_H +#define TEST_UTIL_H + +#include +#include +#include +#include +#include +#include +#include +#include + + +// Helper function to trim whitespace from both ends of a string to test comparison output +static inline std::string &trim(std::string &s) { + s.erase(s.begin(), std::find_if(s.begin(), s.end(), [](unsigned char ch) { + return !std::isspace(static_cast(ch)); + })); + s.erase(std::find_if(s.rbegin(), s.rend(), [](unsigned char ch) { + return !std::isspace(static_cast(ch)); + }).base(), s.end()); + return s; +} + + +// Helper function to read file content into a string +inline std::string ReadFileToString(const std::string& file_path) { + std::ifstream file_stream(file_path, std::ios::binary); + if (!file_stream) { + return ""; + } + std::ostringstream buffer; + buffer << file_stream.rdbuf(); + return buffer.str(); +} + +inline void RunCommandsAndCompareOutput(const std::string &tool_command, const std::string &openssl_command, + const std::string &out_path_tool, const std::string &out_path_openssl, + std::string &tool_output_str, std::string &openssl_output_str) { + int tool_result = system(tool_command.c_str()); + ASSERT_EQ(tool_result, 0) << "AWS-LC tool command failed: " << tool_command; + + int openssl_result = system(openssl_command.c_str()); + ASSERT_EQ(openssl_result, 0) << "OpenSSL command failed: " << openssl_command; + + std::ifstream tool_output(out_path_tool); + tool_output_str = std::string((std::istreambuf_iterator(tool_output)), std::istreambuf_iterator()); + std::ifstream openssl_output(out_path_openssl); + openssl_output_str = std::string((std::istreambuf_iterator(openssl_output)), std::istreambuf_iterator()); + + std::cout << "AWS-LC tool output:" << std::endl << tool_output_str << std::endl; + std::cout << "OpenSSL output:" << std::endl << openssl_output_str << std::endl; +} + +inline void RemoveFile(const char* path) { + struct stat buffer; + if (path != nullptr && stat(path, &buffer) == 0) { + if (remove(path) != 0) { + fprintf(stderr, "Error deleting %s: %s\n", path, strerror(errno)); + } + } +} + +#endif //TEST_UTIL_H diff --git a/tool-openssl/tool.cc b/tool-openssl/tool.cc index f363d885ce..983f43fe55 100644 --- a/tool-openssl/tool.cc +++ b/tool-openssl/tool.cc @@ -22,8 +22,10 @@ struct Tool { tool_func_t func; }; -static const std::array kTools = {{ - { "x509", X509Tool }, +static const std::array kTools = {{ +{ "x509", X509Tool }, +{"rsa", rsaTool}, +{"md5", md5Tool}, }}; static void usage(const std::string &name) { diff --git a/tool-openssl/x509_test.cc b/tool-openssl/x509_test.cc index 3594450e99..9c1ae0ce95 100644 --- a/tool-openssl/x509_test.cc +++ b/tool-openssl/x509_test.cc @@ -5,7 +5,7 @@ #include #include #include "internal.h" -#include +#include "test_util.h" #include @@ -23,8 +23,6 @@ size_t createTempFILEpath(char buffer[PATH_MAX]); -void RemoveFile(const char* path); - X509* CreateAndSignX509Certificate() { bssl::UniquePtr x509(X509_new()); if (!x509) return nullptr; @@ -57,15 +55,6 @@ X509* CreateAndSignX509Certificate() { return x509.release(); } -void RemoveFile(const char* path) { - struct stat buffer; - if (path != nullptr && stat(path, &buffer) == 0) { - if (remove(path) != 0) { - fprintf(stderr, "Error deleting %s: %s\n", path, strerror(errno)); - } - } -} - class X509Test : public ::testing::Test { protected: void SetUp() override { @@ -232,7 +221,6 @@ TEST_F(X509OptionUsageErrorsTest, DaysAndCheckendArgTests) { // Comparison tests cannot run without set up of environment variables: // AWSLC_TOOL_PATH and OPENSSL_TOOL_PATH. -// TODO: add instructions in readme class X509ComparisonTest : public ::testing::Test { protected: @@ -279,22 +267,6 @@ class X509ComparisonTest : public ::testing::Test { ASSERT_TRUE(PEM_write_X509_REQ(csr_file.get(), csr.get())); } - void RunCommandsAndCompareOutput(const std::string &tool_command, const std::string &openssl_command) { - int tool_result = system(tool_command.c_str()); - ASSERT_EQ(tool_result, 0) << "AWS-LC tool command failed: " << tool_command; - - int openssl_result = system(openssl_command.c_str()); - ASSERT_EQ(openssl_result, 0) << "OpenSSL command failed: " << openssl_command; - - std::ifstream tool_output(out_path_tool); - this->tool_output_str = std::string((std::istreambuf_iterator(tool_output)), std::istreambuf_iterator()); - std::ifstream openssl_output(out_path_openssl); - this->openssl_output_str = std::string((std::istreambuf_iterator(openssl_output)), std::istreambuf_iterator()); - - std::cout << "AWS-LC tool output:" << std::endl << this->tool_output_str << std::endl; - std::cout << "OpenSSL output:" << std::endl << this->openssl_output_str << std::endl; - } - void TearDown() override { if (tool_executable_path != nullptr && openssl_executable_path != nullptr) { RemoveFile(in_path); @@ -318,16 +290,6 @@ class X509ComparisonTest : public ::testing::Test { std::string openssl_output_str; }; -// Helper function to trim whitespace from both ends of a string to test certificate output -static inline std::string &trim(std::string &s) { - s.erase(s.begin(), std::find_if(s.begin(), s.end(), [](unsigned char ch) { - return !std::isspace(static_cast(ch)); - })); - s.erase(std::find_if(s.rbegin(), s.rend(), [](unsigned char ch) { - return !std::isspace(static_cast(ch)); - }).base(), s.end()); - return s; -} // Certificate boundaries const std::string CERT_BEGIN = "-----BEGIN CERTIFICATE-----"; const std::string CERT_END = "-----END CERTIFICATE-----"; @@ -337,7 +299,7 @@ TEST_F(X509ComparisonTest, X509ToolCompareModulusOpenSSL) { std::string tool_command = std::string(tool_executable_path) + " x509 -in " + in_path + " -modulus > " + out_path_tool; std::string openssl_command = std::string(openssl_executable_path) + " x509 -in " + in_path + " -modulus > " + out_path_openssl; - RunCommandsAndCompareOutput(tool_command, openssl_command); + RunCommandsAndCompareOutput(tool_command, openssl_command, out_path_tool, out_path_openssl, tool_output_str, openssl_output_str); ASSERT_EQ(tool_output_str, openssl_output_str); } @@ -347,7 +309,7 @@ TEST_F(X509ComparisonTest, X509ToolCompareCheckendOpenSSL) { std::string tool_command = std::string(tool_executable_path) + " x509 -in " + in_path + " -checkend 0 > " + out_path_tool; std::string openssl_command = std::string(openssl_executable_path) + " x509 -in " + in_path + " -checkend 0 > " + out_path_openssl; - RunCommandsAndCompareOutput(tool_command, openssl_command); + RunCommandsAndCompareOutput(tool_command, openssl_command, out_path_tool, out_path_openssl, tool_output_str, openssl_output_str); ASSERT_EQ(tool_output_str, openssl_output_str); } @@ -357,8 +319,7 @@ TEST_F(X509ComparisonTest, X509ToolCompareReqSignkeyDaysOpenSSL) { std::string tool_command = std::string(tool_executable_path) + " x509 -req -in " + csr_path + " -signkey " + signkey_path + " -days 80 -out " + out_path_tool; std::string openssl_command = std::string(openssl_executable_path) + " x509 -req -in " + csr_path + " -signkey " + signkey_path + " -days 80 -out " + out_path_openssl; - RunCommandsAndCompareOutput(tool_command, openssl_command); - + RunCommandsAndCompareOutput(tool_command, openssl_command, out_path_tool, out_path_openssl, tool_output_str, openssl_output_str); // Certificates will not be identical, therefore testing that cert header and footer are present trim(tool_output_str); ASSERT_EQ(tool_output_str.compare(0, CERT_BEGIN.size(), CERT_BEGIN), 0); @@ -374,7 +335,7 @@ TEST_F(X509ComparisonTest, X509ToolCompareDatesNooutOpenSSL) { std::string tool_command = std::string(tool_executable_path) + " x509 -in " + in_path + " -dates -noout > " + out_path_tool; std::string openssl_command = std::string(openssl_executable_path) + " x509 -in " + in_path + " -dates -noout > " + out_path_openssl; - RunCommandsAndCompareOutput(tool_command, openssl_command); + RunCommandsAndCompareOutput(tool_command, openssl_command, out_path_tool, out_path_openssl, tool_output_str, openssl_output_str); ASSERT_EQ(tool_output_str, openssl_output_str); }