From 920c32e76aeb07b33f2a3d0e0334d4ef0b40c2c5 Mon Sep 17 00:00:00 2001 From: Shubham Mittal Date: Mon, 21 Jul 2025 14:06:06 -0700 Subject: [PATCH 01/11] pkeyutl implementation --- tool-openssl/CMakeLists.txt | 1 + tool-openssl/internal.h | 1 + tool-openssl/pkeyutl.cc | 454 ++++++++++++++++++++++++++++++++++++ tool-openssl/tool.cc | 3 +- 4 files changed, 458 insertions(+), 1 deletion(-) create mode 100644 tool-openssl/pkeyutl.cc diff --git a/tool-openssl/CMakeLists.txt b/tool-openssl/CMakeLists.txt index 0ba2865ccf..0ff24d04b2 100644 --- a/tool-openssl/CMakeLists.txt +++ b/tool-openssl/CMakeLists.txt @@ -11,6 +11,7 @@ add_executable( dgst.cc pkcs8.cc pkey.cc + pkeyutl.cc rehash.cc req.cc ordered_args.cc diff --git a/tool-openssl/internal.h b/tool-openssl/internal.h index 26af3230b6..e2ac567894 100644 --- a/tool-openssl/internal.h +++ b/tool-openssl/internal.h @@ -40,6 +40,7 @@ bool dgstTool(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); +bool pkeyutlTool(const args_list_t &args); bool RehashTool(const args_list_t &args); bool reqTool(const args_list_t &args); bool rsaTool(const args_list_t &args); diff --git a/tool-openssl/pkeyutl.cc b/tool-openssl/pkeyutl.cc new file mode 100644 index 0000000000..1c76c4b5fe --- /dev/null +++ b/tool-openssl/pkeyutl.cc @@ -0,0 +1,454 @@ +// Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. +// SPDX-License-Identifier: Apache-2.0 OR ISC + +#include +#include +#include +#include +#include +#include "internal.h" +#include + +#define KEY_NONE 0 +#define KEY_PRIVKEY 1 +#define KEY_PUBKEY 2 + +static const argument_t kArguments[] = { + { "-help", kBooleanArgument, "Display option summary" }, + { "-in", kOptionalArgument, "Input file - default stdin" }, + { "-out", kOptionalArgument, "Output file - default stdout" }, + { "-sign", kBooleanArgument, "Sign input data with private key" }, + { "-verify", kBooleanArgument, "Verify with public key" }, + { "-digest", kOptionalArgument, "Specify digest algorithm when signing raw input data" }, + { "-inkey", kOptionalArgument, "Input private key file" }, + { "-rawin", kBooleanArgument, "Indicate input data is in raw form" }, + { "-pubin", kBooleanArgument, "Input is a public key" }, + { "-passin", kOptionalArgument, "Input file pass phrase source" }, + { "-hexdump", kBooleanArgument, "Hex dump output" }, + { "", kOptionalArgument, "" } +}; + +static bool LoadPrivateKey(const std::string &keyfile, const std::string &passin_arg, + bssl::UniquePtr &pkey) { + ScopedFILE key_file; + if (keyfile.empty()) { + fprintf(stderr, "Error: no private key given (-inkey parameter)\n"); + return false; + } + + key_file.reset(fopen(keyfile.c_str(), "rb")); + if (!key_file) { + fprintf(stderr, "Error: unable to load private key from '%s'\n", keyfile.c_str()); + return false; + } + + // For simplicity, we'll handle basic password-protected keys + // In a full implementation, we'd parse passin_arg for different password sources + const char *password = nullptr; + if (!passin_arg.empty()) { + // Simple case: assume it's a direct password (not a file or other source) + password = passin_arg.c_str(); + } + + pkey.reset(PEM_read_PrivateKey(key_file.get(), nullptr, nullptr, + const_cast(password))); + if (!pkey) { + fprintf(stderr, "Error: error reading private key from '%s'\n", keyfile.c_str()); + ERR_print_errors_fp(stderr); + return false; + } + + return true; +} + +static bool LoadPublicKey(const std::string &keyfile, bssl::UniquePtr &pkey) { + ScopedFILE key_file; + if (keyfile.empty()) { + fprintf(stderr, "Error: no public key given (-inkey parameter)\n"); + return false; + } + + key_file.reset(fopen(keyfile.c_str(), "rb")); + if (!key_file) { + fprintf(stderr, "Error: unable to load public key from '%s'\n", keyfile.c_str()); + return false; + } + + pkey.reset(PEM_read_PUBKEY(key_file.get(), nullptr, nullptr, nullptr)); + if (!pkey) { + fprintf(stderr, "Error: error reading public key from '%s'\n", keyfile.c_str()); + ERR_print_errors_fp(stderr); + return false; + } + + return true; +} + +static bool ReadInputData(const std::string &in_path, std::vector &data) { + 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 open input file '%s'\n", in_path.c_str()); + return false; + } + } + + if (!ReadAll(&data, in_file.get())) { + fprintf(stderr, "Error: error reading input data\n"); + return false; + } + + return true; +} + +static bool DoSign(EVP_PKEY *pkey, const std::vector &input_data, + std::vector &signature) { + bssl::UniquePtr ctx(EVP_PKEY_CTX_new(pkey, nullptr)); + if (!ctx) { + fprintf(stderr, "Error: failed to create signing context\n"); + return false; + } + + if (EVP_PKEY_sign_init(ctx.get()) <= 0) { + fprintf(stderr, "Error: failed to initialize signing context\n"); + ERR_print_errors_fp(stderr); + return false; + } + + size_t sig_len = 0; + if (EVP_PKEY_sign(ctx.get(), nullptr, &sig_len, input_data.data(), input_data.size()) <= 0) { + fprintf(stderr, "Error: failed to determine signature length\n"); + ERR_print_errors_fp(stderr); + return false; + } + + signature.resize(sig_len); + if (EVP_PKEY_sign(ctx.get(), signature.data(), &sig_len, + input_data.data(), input_data.size()) <= 0) { + fprintf(stderr, "Error: failed to sign data\n"); + ERR_print_errors_fp(stderr); + return false; + } + + signature.resize(sig_len); + return true; +} + +static bool DoVerify(EVP_PKEY *pkey, const std::vector &input_data, + const std::vector &signature) { + bssl::UniquePtr ctx(EVP_PKEY_CTX_new(pkey, nullptr)); + if (!ctx) { + fprintf(stderr, "Error: failed to create verification context\n"); + return false; + } + + if (EVP_PKEY_verify_init(ctx.get()) <= 0) { + fprintf(stderr, "Error: failed to initialize verification context\n"); + ERR_print_errors_fp(stderr); + return false; + } + + int result = EVP_PKEY_verify(ctx.get(), signature.data(), signature.size(), + input_data.data(), input_data.size()); + if (result == 1) { + return true; + } else if (result == 0) { + return false; // Verification failed + } else { + fprintf(stderr, "Error: verification operation failed\n"); + ERR_print_errors_fp(stderr); + return false; + } +} + +static bool DoRawSign(EVP_PKEY *pkey, const std::string &digest_name, + const std::string &in_path, std::vector &signature) { + bssl::UniquePtr mctx(EVP_MD_CTX_new()); + if (!mctx) { + fprintf(stderr, "Error: failed to create digest context\n"); + return false; + } + + const EVP_MD *md = nullptr; + if (!digest_name.empty()) { + md = EVP_get_digestbyname(digest_name.c_str()); + if (!md) { + fprintf(stderr, "Error: unknown digest algorithm '%s'\n", digest_name.c_str()); + return false; + } + } else { + md = EVP_sha256(); // Default digest + } + + if (EVP_DigestSignInit(mctx.get(), nullptr, md, nullptr, pkey) <= 0) { + fprintf(stderr, "Error: failed to initialize digest signing\n"); + ERR_print_errors_fp(stderr); + return false; + } + + 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 open input file '%s'\n", in_path.c_str()); + return false; + } + } + + // Read and update digest in chunks + uint8_t buffer[4096]; + size_t bytes_read; + while ((bytes_read = fread(buffer, 1, sizeof(buffer), in_file.get())) > 0) { + if (EVP_DigestSignUpdate(mctx.get(), buffer, bytes_read) <= 0) { + fprintf(stderr, "Error: failed to update digest\n"); + ERR_print_errors_fp(stderr); + return false; + } + } + + size_t sig_len = 0; + if (EVP_DigestSignFinal(mctx.get(), nullptr, &sig_len) <= 0) { + fprintf(stderr, "Error: failed to determine signature length\n"); + ERR_print_errors_fp(stderr); + return false; + } + + signature.resize(sig_len); + if (EVP_DigestSignFinal(mctx.get(), signature.data(), &sig_len) <= 0) { + fprintf(stderr, "Error: failed to finalize signature\n"); + ERR_print_errors_fp(stderr); + return false; + } + + signature.resize(sig_len); + return true; +} + +static bool DoRawVerify(EVP_PKEY *pkey, const std::string &digest_name, + const std::string &in_path, const std::vector &signature) { + bssl::UniquePtr mctx(EVP_MD_CTX_new()); + if (!mctx) { + fprintf(stderr, "Error: failed to create digest context\n"); + return false; + } + + const EVP_MD *md = nullptr; + if (!digest_name.empty()) { + md = EVP_get_digestbyname(digest_name.c_str()); + if (!md) { + fprintf(stderr, "Error: unknown digest algorithm '%s'\n", digest_name.c_str()); + return false; + } + } else { + md = EVP_sha256(); // Default digest + } + + if (EVP_DigestVerifyInit(mctx.get(), nullptr, md, nullptr, pkey) <= 0) { + fprintf(stderr, "Error: failed to initialize digest verification\n"); + ERR_print_errors_fp(stderr); + return false; + } + + 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 open input file '%s'\n", in_path.c_str()); + return false; + } + } + + // Read and update digest in chunks + uint8_t buffer[4096]; + size_t bytes_read; + while ((bytes_read = fread(buffer, 1, sizeof(buffer), in_file.get())) > 0) { + if (EVP_DigestVerifyUpdate(mctx.get(), buffer, bytes_read) <= 0) { + fprintf(stderr, "Error: failed to update digest\n"); + ERR_print_errors_fp(stderr); + return false; + } + } + + int result = EVP_DigestVerifyFinal(mctx.get(), signature.data(), signature.size()); + if (result == 1) { + return true; + } else if (result == 0) { + return false; // Verification failed + } else { + fprintf(stderr, "Error: verification operation failed\n"); + ERR_print_errors_fp(stderr); + return false; + } +} + +static bool WriteOutput(const std::vector &data, const std::string &out_path, + bool hexdump) { + 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 (BIO_write_filename(output_bio.get(), out_path.c_str()) <= 0) { + fprintf(stderr, "Error: failed to open output file '%s'\n", out_path.c_str()); + return false; + } + } + + if (!output_bio) { + fprintf(stderr, "Error: unable to create output BIO\n"); + return false; + } + + if (hexdump) { + BIO_dump(output_bio.get(), reinterpret_cast(data.data()), data.size()); + } else { + BIO_write(output_bio.get(), data.data(), data.size()); + } + + return true; +} + +bool pkeyutlTool(const args_list_t &args) { + using namespace ordered_args; + ordered_args_map_t parsed_args; + args_list_t extra_args; + + if (!ParseOrderedKeyValueArguments(parsed_args, extra_args, args, kArguments) || + extra_args.size() > 0) { + PrintUsage(kArguments); + return false; + } + + std::string in_path, out_path, inkey_path, passin_arg, digest_name; + bool sign = false, verify = false, rawin = false, pubin = false, hexdump = false; + + GetString(&in_path, "-in", "", parsed_args); + GetString(&out_path, "-out", "", parsed_args); + GetString(&inkey_path, "-inkey", "", parsed_args); + GetString(&passin_arg, "-passin", "", parsed_args); + GetString(&digest_name, "-digest", "", parsed_args); + GetBoolArgument(&sign, "-sign", parsed_args); + GetBoolArgument(&verify, "-verify", parsed_args); + GetBoolArgument(&rawin, "-rawin", parsed_args); + GetBoolArgument(&pubin, "-pubin", parsed_args); + GetBoolArgument(&hexdump, "-hexdump", parsed_args); + + // Display help + if (HasArgument(parsed_args, "-help")) { + PrintUsage(kArguments); + return true; + } + + // Validate arguments + if (!sign && !verify) { + fprintf(stderr, "Error: must specify either -sign or -verify\n"); + return false; + } + + if (sign && verify) { + fprintf(stderr, "Error: cannot specify both -sign and -verify\n"); + return false; + } + + if (rawin && !sign && !verify) { + fprintf(stderr, "Error: -rawin can only be used with -sign or -verify\n"); + return false; + } + + if (!digest_name.empty() && !rawin) { + fprintf(stderr, "Error: -digest can only be used with -rawin\n"); + return false; + } + + if (inkey_path.empty()) { + fprintf(stderr, "Error: no key given (-inkey parameter)\n"); + return false; + } + + // Load the key + bssl::UniquePtr pkey; + if (pubin || verify) { + if (!LoadPublicKey(inkey_path, pkey)) { + return false; + } + } else { + if (!LoadPrivateKey(inkey_path, passin_arg, pkey)) { + return false; + } + } + + if (sign) { + std::vector signature; + bool success = false; + + if (rawin) { + success = DoRawSign(pkey.get(), digest_name, in_path, signature); + } else { + std::vector input_data; + if (!ReadInputData(in_path, input_data)) { + return false; + } + + // Sanity check for non-raw input + if (input_data.size() > EVP_MAX_MD_SIZE) { + fprintf(stderr, "Error: input data looks too long to be a hash\n"); + return false; + } + + success = DoSign(pkey.get(), input_data, signature); + } + + if (!success) { + return false; + } + + if (!WriteOutput(signature, out_path, hexdump)) { + return false; + } + } else if (verify) { + // For verification, we need a signature file or signature data + // For simplicity, we'll read signature from stdin if no specific sig file + std::vector signature; + if (!ReadInputData("", signature)) { // Read signature from stdin + return false; + } + + bool success = false; + if (rawin) { + success = DoRawVerify(pkey.get(), digest_name, in_path, signature); + } else { + std::vector input_data; + if (!ReadInputData(in_path, input_data)) { + return false; + } + + success = DoVerify(pkey.get(), input_data, signature); + } + + 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 (BIO_write_filename(output_bio.get(), out_path.c_str()) <= 0) { + fprintf(stderr, "Error: failed to open output file '%s'\n", out_path.c_str()); + return false; + } + } + + if (success) { + BIO_puts(output_bio.get(), "Signature Verified Successfully\n"); + } else { + BIO_puts(output_bio.get(), "Signature Verification Failure\n"); + } + } + + return true; +} diff --git a/tool-openssl/tool.cc b/tool-openssl/tool.cc index 4ef0fa14cd..c7ad54cd1c 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}, {"md5", md5Tool}, {"pkcs8", pkcs8Tool}, {"pkey", pkeyTool}, + {"pkeyutl", pkeyutlTool}, {"rehash", RehashTool}, {"req", reqTool}, {"rsa", rsaTool}, From 223c9843d824963eb63e2a0eb31482f0339ab2eb Mon Sep 17 00:00:00 2001 From: Shubham Mittal Date: Tue, 22 Jul 2025 13:25:42 -0700 Subject: [PATCH 02/11] round 1 testing --- tool-openssl/CMakeLists.txt | 2 + tool-openssl/pkeyutl_test.cc | 422 +++++++++++++++++++++++++++++++++++ 2 files changed, 424 insertions(+) create mode 100644 tool-openssl/pkeyutl_test.cc diff --git a/tool-openssl/CMakeLists.txt b/tool-openssl/CMakeLists.txt index 0ff24d04b2..76b4909a4f 100644 --- a/tool-openssl/CMakeLists.txt +++ b/tool-openssl/CMakeLists.txt @@ -93,6 +93,8 @@ if(BUILD_TESTING) pkcs8_test.cc pkey.cc pkey_test.cc + pkeyutl.cc + pkeyutl_test.cc rehash.cc rehash_test.cc req.cc diff --git a/tool-openssl/pkeyutl_test.cc b/tool-openssl/pkeyutl_test.cc new file mode 100644 index 0000000000..c13a286ef0 --- /dev/null +++ b/tool-openssl/pkeyutl_test.cc @@ -0,0 +1,422 @@ +// 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" +#include "../crypto/test/test_util.h" +#include + +// Helper function to create a test key +EVP_PKEY* CreateTestKey(int key_bits) { + bssl::UniquePtr pkey(EVP_PKEY_new()); + if (!pkey) { + return nullptr; + } + + bssl::UniquePtr rsa(RSA_new()); + bssl::UniquePtr bn(BN_new()); + if (!bn || !BN_set_word(bn.get(), RSA_F4) || + !RSA_generate_key_ex(rsa.get(), key_bits, bn.get(), nullptr) || + !EVP_PKEY_assign_RSA(pkey.get(), rsa.release())) { + return nullptr; + } + + return pkey.release(); +} + +class PKeyUtlTest : public ::testing::Test { +protected: + void SetUp() override { + ASSERT_GT(createTempFILEpath(in_path), 0u); + ASSERT_GT(createTempFILEpath(out_path), 0u); + ASSERT_GT(createTempFILEpath(sig_path), 0u); + ASSERT_GT(createTempFILEpath(key_path), 0u); + ASSERT_GT(createTempFILEpath(pubkey_path), 0u); + ASSERT_GT(createTempFILEpath(protected_key_path), 0u); + + // Create and save a private key in PEM format + bssl::UniquePtr pkey(CreateTestKey(2048)); + ASSERT_TRUE(pkey); + + ScopedFILE key_file(fopen(key_path, "wb")); + ASSERT_TRUE(key_file); + ASSERT_TRUE(PEM_write_PrivateKey(key_file.get(), pkey.get(), nullptr, nullptr, 0, nullptr, nullptr)); + + // Create a public key file + ScopedFILE pubkey_file(fopen(pubkey_path, "wb")); + ASSERT_TRUE(pubkey_file); + ASSERT_TRUE(PEM_write_PUBKEY(pubkey_file.get(), pkey.get())); + + // Create a password-protected private key + ScopedFILE protected_key_file(fopen(protected_key_path, "wb")); + ASSERT_TRUE(protected_key_file); + ASSERT_TRUE(PEM_write_PrivateKey(protected_key_file.get(), pkey.get(), + EVP_aes_256_cbc(), + (unsigned char*)"testpassword", 11, + nullptr, nullptr)); + + // Create a test input file with some data + ScopedFILE in_file(fopen(in_path, "wb")); + ASSERT_TRUE(in_file); + const char* test_data = "Test data for signing and verification"; + ASSERT_EQ(fwrite(test_data, 1, strlen(test_data), in_file.get()), strlen(test_data)); + } + + void TearDown() override { + RemoveFile(in_path); + RemoveFile(out_path); + RemoveFile(sig_path); + RemoveFile(key_path); + RemoveFile(pubkey_path); + RemoveFile(protected_key_path); + } + + char in_path[PATH_MAX]; + char out_path[PATH_MAX]; + char sig_path[PATH_MAX]; + char key_path[PATH_MAX]; + char pubkey_path[PATH_MAX]; + char protected_key_path[PATH_MAX]; +}; + +// ----------------------------- PKeyUtl Option Tests ----------------------------- + +// Test basic signing operation +TEST_F(PKeyUtlTest, SignTest) { + args_list_t args = {"-sign", "-inkey", key_path, "-in", in_path, "-out", out_path}; + bool result = pkeyutlTool(args); + ASSERT_TRUE(result); + + // Verify the signature file was created and has content + struct stat st; + ASSERT_EQ(stat(out_path, &st), 0); + ASSERT_GT(st.st_size, 0); +} + +// Test basic verification operation +TEST_F(PKeyUtlTest, VerifyTest) { + // First sign the data + { + args_list_t args = {"-sign", "-inkey", key_path, "-in", in_path, "-out", sig_path}; + bool result = pkeyutlTool(args); + ASSERT_TRUE(result); + } + + // Then verify the signature + { + // For verification, we need to provide the signature on stdin + // and the original data as the input file + std::string verify_cmd = "cat " + std::string(sig_path) + " | " + + "AWSLC_TOOL_PATH=./tool-openssl/tool-openssl " + + "./tool-openssl/tool-openssl pkeyutl -verify -pubin -inkey " + + pubkey_path + " -in " + in_path + " > " + out_path; + + int cmd_result = system(verify_cmd.c_str()); + ASSERT_EQ(cmd_result, 0); + + // Check that the output contains "Signature Verified Successfully" + std::string output = ReadFileToString(out_path); + ASSERT_NE(output.find("Signature Verified Successfully"), std::string::npos); + } +} + +// Test raw input signing and verification +TEST_F(PKeyUtlTest, RawInTest) { + // Sign with raw input + { + args_list_t args = {"-sign", "-rawin", "-inkey", key_path, "-in", in_path, "-out", sig_path}; + bool result = pkeyutlTool(args); + ASSERT_TRUE(result); + } + + // Verify with raw input + { + // For verification, we need to provide the signature on stdin + // and the original data as the input file + std::string verify_cmd = "cat " + std::string(sig_path) + " | " + + "AWSLC_TOOL_PATH=./tool-openssl/tool-openssl " + + "./tool-openssl/tool-openssl pkeyutl -verify -rawin -pubin -inkey " + + pubkey_path + " -in " + in_path + " > " + out_path; + + int cmd_result = system(verify_cmd.c_str()); + ASSERT_EQ(cmd_result, 0); + + // Check that the output contains "Signature Verified Successfully" + std::string output = ReadFileToString(out_path); + ASSERT_NE(output.find("Signature Verified Successfully"), std::string::npos); + } +} + +// Test with specific digest algorithm +TEST_F(PKeyUtlTest, DigestTest) { + // Sign with specific digest + { + args_list_t args = {"-sign", "-rawin", "-digest", "sha256", "-inkey", key_path, "-in", in_path, "-out", sig_path}; + bool result = pkeyutlTool(args); + ASSERT_TRUE(result); + } + + // Verify with specific digest + { + // For verification, we need to provide the signature on stdin + // and the original data as the input file + std::string verify_cmd = "cat " + std::string(sig_path) + " | " + + "AWSLC_TOOL_PATH=./tool-openssl/tool-openssl " + + "./tool-openssl/tool-openssl pkeyutl -verify -rawin -digest sha256 -pubin -inkey " + + pubkey_path + " -in " + in_path + " > " + out_path; + + int cmd_result = system(verify_cmd.c_str()); + ASSERT_EQ(cmd_result, 0); + + // Check that the output contains "Signature Verified Successfully" + std::string output = ReadFileToString(out_path); + ASSERT_NE(output.find("Signature Verified Successfully"), std::string::npos); + } +} + +// Test with hexdump output +TEST_F(PKeyUtlTest, HexdumpTest) { + args_list_t args = {"-sign", "-inkey", key_path, "-in", in_path, "-out", out_path, "-hexdump"}; + bool result = pkeyutlTool(args); + ASSERT_TRUE(result); + + // Check that the output is in hexdump format (contains hex digits and addresses) + std::string output = ReadFileToString(out_path); + ASSERT_FALSE(output.empty()); + + // Hexdump typically contains colons and spaces + ASSERT_NE(output.find(":"), std::string::npos); + + // Hexdump should contain only printable characters + for (char c : output) { + ASSERT_TRUE(std::isprint(c) || std::isspace(c)); + } +} + +// Test with password-protected key +TEST_F(PKeyUtlTest, PassinTest) { + args_list_t args = {"-sign", "-inkey", protected_key_path, "-passin", "pass:testpassword", "-in", in_path, "-out", out_path}; + bool result = pkeyutlTool(args); + ASSERT_TRUE(result); + + // Verify the signature file was created and has content + struct stat st; + ASSERT_EQ(stat(out_path, &st), 0); + ASSERT_GT(st.st_size, 0); +} + +// -------------------- PKeyUtl Option Usage Error Tests -------------------------- + +class PKeyUtlOptionUsageErrorsTest : public PKeyUtlTest { +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 = pkeyutlTool(c_args); + ASSERT_FALSE(result); + } +}; + +// Test invalid option combinations +TEST_F(PKeyUtlOptionUsageErrorsTest, InvalidOptionCombinationsTest) { + std::vector> testparams = { + // Both sign and verify specified + {"-sign", "-verify", "-inkey", key_path, "-in", in_path}, + // Missing inkey + {"-sign", "-in", in_path}, + // digest without rawin + {"-sign", "-digest", "sha256", "-inkey", key_path, "-in", in_path}, + // rawin without sign or verify + {"-rawin", "-inkey", key_path, "-in", in_path}, + }; + + for (const auto& args : testparams) { + TestOptionUsageErrors(args); + } +} + +// -------------------- PKeyUtl OpenSSL Comparison Tests -------------------------- + +// Comparison tests cannot run without set up of environment variables: +// AWSLC_TOOL_PATH and OPENSSL_TOOL_PATH. + +class PKeyUtlComparisonTest : 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); + ASSERT_GT(createTempFILEpath(sig_path_tool), 0u); + ASSERT_GT(createTempFILEpath(sig_path_openssl), 0u); + ASSERT_GT(createTempFILEpath(key_path), 0u); + ASSERT_GT(createTempFILEpath(pubkey_path), 0u); + + // Create and save a private key + pkey.reset(CreateTestKey(2048)); + ASSERT_TRUE(pkey); + + ScopedFILE key_file(fopen(key_path, "wb")); + ASSERT_TRUE(key_file); + ASSERT_TRUE(PEM_write_PrivateKey(key_file.get(), pkey.get(), nullptr, nullptr, 0, nullptr, nullptr)); + + // Create a public key file + ScopedFILE pubkey_file(fopen(pubkey_path, "wb")); + ASSERT_TRUE(pubkey_file); + ASSERT_TRUE(PEM_write_PUBKEY(pubkey_file.get(), pkey.get())); + + // Create a test input file with some data + ScopedFILE in_file(fopen(in_path, "wb")); + ASSERT_TRUE(in_file); + const char* test_data = "Test data for signing and verification"; + ASSERT_EQ(fwrite(test_data, 1, strlen(test_data), in_file.get()), strlen(test_data)); + } + + void TearDown() override { + if (tool_executable_path != nullptr && openssl_executable_path != nullptr) { + RemoveFile(in_path); + RemoveFile(out_path_tool); + RemoveFile(out_path_openssl); + RemoveFile(sig_path_tool); + RemoveFile(sig_path_openssl); + RemoveFile(key_path); + RemoveFile(pubkey_path); + } + } + + char in_path[PATH_MAX]; + char out_path_tool[PATH_MAX]; + char out_path_openssl[PATH_MAX]; + char sig_path_tool[PATH_MAX]; + char sig_path_openssl[PATH_MAX]; + char key_path[PATH_MAX]; + char pubkey_path[PATH_MAX]; + bssl::UniquePtr pkey; + const char* tool_executable_path; + const char* openssl_executable_path; + std::string tool_output_str; + std::string openssl_output_str; +}; + +// Test signing operation against OpenSSL +TEST_F(PKeyUtlComparisonTest, SignCompareOpenSSL) { + std::string tool_command = std::string(tool_executable_path) + " pkeyutl -sign -inkey " + + key_path + " -in " + in_path + " -out " + sig_path_tool; + std::string openssl_command = std::string(openssl_executable_path) + " pkeyutl -sign -inkey " + + key_path + " -in " + in_path + " -out " + sig_path_openssl; + + 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; + + // Verify both signatures with the public key + std::string tool_verify_cmd = std::string(tool_executable_path) + " pkeyutl -verify -pubin -inkey " + + pubkey_path + " -in " + in_path + " -sigfile " + sig_path_tool + + " > " + out_path_tool; + std::string openssl_verify_cmd = std::string(openssl_executable_path) + " pkeyutl -verify -pubin -inkey " + + pubkey_path + " -in " + in_path + " -sigfile " + sig_path_openssl + + " > " + out_path_openssl; + + ASSERT_EQ(system(tool_verify_cmd.c_str()), 0); + ASSERT_EQ(system(openssl_verify_cmd.c_str()), 0); + + // Read verification results + 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()); + + // Both should verify successfully + ASSERT_NE(tool_output_str.find("Signature Verified Successfully"), std::string::npos); + ASSERT_NE(openssl_output_str.find("Signature Verified Successfully"), std::string::npos); +} + +// Test raw input signing against OpenSSL +TEST_F(PKeyUtlComparisonTest, RawSignCompareOpenSSL) { + std::string tool_command = std::string(tool_executable_path) + " pkeyutl -sign -rawin -digest sha256 -inkey " + + key_path + " -in " + in_path + " -out " + sig_path_tool; + std::string openssl_command = std::string(openssl_executable_path) + " pkeyutl -sign -rawin -digest sha256 -inkey " + + key_path + " -in " + in_path + " -out " + sig_path_openssl; + + 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; + + // Verify both signatures with the public key + std::string tool_verify_cmd = std::string(tool_executable_path) + " pkeyutl -verify -rawin -digest sha256 -pubin -inkey " + + pubkey_path + " -in " + in_path + " -sigfile " + sig_path_tool + + " > " + out_path_tool; + std::string openssl_verify_cmd = std::string(openssl_executable_path) + " pkeyutl -verify -rawin -digest sha256 -pubin -inkey " + + pubkey_path + " -in " + in_path + " -sigfile " + sig_path_openssl + + " > " + out_path_openssl; + + ASSERT_EQ(system(tool_verify_cmd.c_str()), 0); + ASSERT_EQ(system(openssl_verify_cmd.c_str()), 0); + + // Read verification results + 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()); + + // Both should verify successfully + ASSERT_NE(tool_output_str.find("Signature Verified Successfully"), std::string::npos); + ASSERT_NE(openssl_output_str.find("Signature Verified Successfully"), std::string::npos); +} + +// Test hexdump output against OpenSSL +TEST_F(PKeyUtlComparisonTest, HexdumpCompareOpenSSL) { + std::string tool_command = std::string(tool_executable_path) + " pkeyutl -sign -inkey " + + key_path + " -in " + in_path + " -hexdump -out " + out_path_tool; + std::string openssl_command = std::string(openssl_executable_path) + " pkeyutl -sign -inkey " + + key_path + " -in " + in_path + " -hexdump -out " + out_path_openssl; + + RunCommandsAndCompareOutput(tool_command, openssl_command, out_path_tool, out_path_openssl, tool_output_str, openssl_output_str); + + // Both outputs should be in hexdump format + ASSERT_NE(tool_output_str.find(":"), std::string::npos); + ASSERT_NE(openssl_output_str.find(":"), std::string::npos); + + // The hexdump format might differ slightly between implementations, + // but both should contain only printable characters + for (char c : tool_output_str) { + ASSERT_TRUE(std::isprint(c) || std::isspace(c)); + } + + for (char c : openssl_output_str) { + ASSERT_TRUE(std::isprint(c) || std::isspace(c)); + } +} + +// Test help output against OpenSSL +TEST_F(PKeyUtlComparisonTest, HelpCompareOpenSSL) { + std::string tool_command = std::string(tool_executable_path) + " pkeyutl -help > " + out_path_tool; + std::string openssl_command = std::string(openssl_executable_path) + " pkeyutl -help > " + out_path_openssl; + + RunCommandsAndCompareOutput(tool_command, openssl_command, out_path_tool, out_path_openssl, tool_output_str, openssl_output_str); + + // Both should contain help information for the supported options + ASSERT_NE(tool_output_str.find("-sign"), std::string::npos); + ASSERT_NE(tool_output_str.find("-verify"), std::string::npos); + ASSERT_NE(tool_output_str.find("-inkey"), std::string::npos); + ASSERT_NE(tool_output_str.find("-pubin"), std::string::npos); + ASSERT_NE(tool_output_str.find("-rawin"), std::string::npos); + ASSERT_NE(tool_output_str.find("-digest"), std::string::npos); + ASSERT_NE(tool_output_str.find("-hexdump"), std::string::npos); +} From 37e286235349126ac6d32dcba744b1186c4ea447 Mon Sep 17 00:00:00 2001 From: Shubham Mittal Date: Mon, 28 Jul 2025 15:29:54 -0700 Subject: [PATCH 03/11] add sigfile, remove erroneous options --- tool-openssl/pkeyutl.cc | 196 ++++++----------------------------- tool-openssl/pkeyutl_test.cc | 107 ++----------------- 2 files changed, 39 insertions(+), 264 deletions(-) diff --git a/tool-openssl/pkeyutl.cc b/tool-openssl/pkeyutl.cc index 1c76c4b5fe..64f878e267 100644 --- a/tool-openssl/pkeyutl.cc +++ b/tool-openssl/pkeyutl.cc @@ -19,9 +19,8 @@ static const argument_t kArguments[] = { { "-out", kOptionalArgument, "Output file - default stdout" }, { "-sign", kBooleanArgument, "Sign input data with private key" }, { "-verify", kBooleanArgument, "Verify with public key" }, - { "-digest", kOptionalArgument, "Specify digest algorithm when signing raw input data" }, + { "-sigfile", kOptionalArgument, "Signature file, required for verify operations only" }, { "-inkey", kOptionalArgument, "Input private key file" }, - { "-rawin", kBooleanArgument, "Indicate input data is in raw form" }, { "-pubin", kBooleanArgument, "Input is a public key" }, { "-passin", kOptionalArgument, "Input file pass phrase source" }, { "-hexdump", kBooleanArgument, "Hex dump output" }, @@ -164,130 +163,6 @@ static bool DoVerify(EVP_PKEY *pkey, const std::vector &input_data, } } -static bool DoRawSign(EVP_PKEY *pkey, const std::string &digest_name, - const std::string &in_path, std::vector &signature) { - bssl::UniquePtr mctx(EVP_MD_CTX_new()); - if (!mctx) { - fprintf(stderr, "Error: failed to create digest context\n"); - return false; - } - - const EVP_MD *md = nullptr; - if (!digest_name.empty()) { - md = EVP_get_digestbyname(digest_name.c_str()); - if (!md) { - fprintf(stderr, "Error: unknown digest algorithm '%s'\n", digest_name.c_str()); - return false; - } - } else { - md = EVP_sha256(); // Default digest - } - - if (EVP_DigestSignInit(mctx.get(), nullptr, md, nullptr, pkey) <= 0) { - fprintf(stderr, "Error: failed to initialize digest signing\n"); - ERR_print_errors_fp(stderr); - return false; - } - - 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 open input file '%s'\n", in_path.c_str()); - return false; - } - } - - // Read and update digest in chunks - uint8_t buffer[4096]; - size_t bytes_read; - while ((bytes_read = fread(buffer, 1, sizeof(buffer), in_file.get())) > 0) { - if (EVP_DigestSignUpdate(mctx.get(), buffer, bytes_read) <= 0) { - fprintf(stderr, "Error: failed to update digest\n"); - ERR_print_errors_fp(stderr); - return false; - } - } - - size_t sig_len = 0; - if (EVP_DigestSignFinal(mctx.get(), nullptr, &sig_len) <= 0) { - fprintf(stderr, "Error: failed to determine signature length\n"); - ERR_print_errors_fp(stderr); - return false; - } - - signature.resize(sig_len); - if (EVP_DigestSignFinal(mctx.get(), signature.data(), &sig_len) <= 0) { - fprintf(stderr, "Error: failed to finalize signature\n"); - ERR_print_errors_fp(stderr); - return false; - } - - signature.resize(sig_len); - return true; -} - -static bool DoRawVerify(EVP_PKEY *pkey, const std::string &digest_name, - const std::string &in_path, const std::vector &signature) { - bssl::UniquePtr mctx(EVP_MD_CTX_new()); - if (!mctx) { - fprintf(stderr, "Error: failed to create digest context\n"); - return false; - } - - const EVP_MD *md = nullptr; - if (!digest_name.empty()) { - md = EVP_get_digestbyname(digest_name.c_str()); - if (!md) { - fprintf(stderr, "Error: unknown digest algorithm '%s'\n", digest_name.c_str()); - return false; - } - } else { - md = EVP_sha256(); // Default digest - } - - if (EVP_DigestVerifyInit(mctx.get(), nullptr, md, nullptr, pkey) <= 0) { - fprintf(stderr, "Error: failed to initialize digest verification\n"); - ERR_print_errors_fp(stderr); - return false; - } - - 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 open input file '%s'\n", in_path.c_str()); - return false; - } - } - - // Read and update digest in chunks - uint8_t buffer[4096]; - size_t bytes_read; - while ((bytes_read = fread(buffer, 1, sizeof(buffer), in_file.get())) > 0) { - if (EVP_DigestVerifyUpdate(mctx.get(), buffer, bytes_read) <= 0) { - fprintf(stderr, "Error: failed to update digest\n"); - ERR_print_errors_fp(stderr); - return false; - } - } - - int result = EVP_DigestVerifyFinal(mctx.get(), signature.data(), signature.size()); - if (result == 1) { - return true; - } else if (result == 0) { - return false; // Verification failed - } else { - fprintf(stderr, "Error: verification operation failed\n"); - ERR_print_errors_fp(stderr); - return false; - } -} - static bool WriteOutput(const std::vector &data, const std::string &out_path, bool hexdump) { bssl::UniquePtr output_bio; @@ -326,17 +201,16 @@ bool pkeyutlTool(const args_list_t &args) { return false; } - std::string in_path, out_path, inkey_path, passin_arg, digest_name; - bool sign = false, verify = false, rawin = false, pubin = false, hexdump = false; + std::string in_path, out_path, inkey_path, passin_arg, sigfile_path; + bool sign = false, verify = false, pubin = false, hexdump = false; GetString(&in_path, "-in", "", parsed_args); GetString(&out_path, "-out", "", parsed_args); GetString(&inkey_path, "-inkey", "", parsed_args); GetString(&passin_arg, "-passin", "", parsed_args); - GetString(&digest_name, "-digest", "", parsed_args); + GetString(&sigfile_path, "-sigfile", "", parsed_args); GetBoolArgument(&sign, "-sign", parsed_args); GetBoolArgument(&verify, "-verify", parsed_args); - GetBoolArgument(&rawin, "-rawin", parsed_args); GetBoolArgument(&pubin, "-pubin", parsed_args); GetBoolArgument(&hexdump, "-hexdump", parsed_args); @@ -357,13 +231,13 @@ bool pkeyutlTool(const args_list_t &args) { return false; } - if (rawin && !sign && !verify) { - fprintf(stderr, "Error: -rawin can only be used with -sign or -verify\n"); + if (verify && sigfile_path.empty()) { + fprintf(stderr, "Error: No signature file specified for verify (-sigfile parameter)\n"); return false; } - if (!digest_name.empty() && !rawin) { - fprintf(stderr, "Error: -digest can only be used with -rawin\n"); + if (!verify && !sigfile_path.empty()) { + fprintf(stderr, "Error: Signature file specified for non-verify operation\n"); return false; } @@ -386,26 +260,18 @@ bool pkeyutlTool(const args_list_t &args) { if (sign) { std::vector signature; - bool success = false; - - if (rawin) { - success = DoRawSign(pkey.get(), digest_name, in_path, signature); - } else { - std::vector input_data; - if (!ReadInputData(in_path, input_data)) { - return false; - } - - // Sanity check for non-raw input - if (input_data.size() > EVP_MAX_MD_SIZE) { - fprintf(stderr, "Error: input data looks too long to be a hash\n"); - return false; - } + std::vector input_data; + if (!ReadInputData(in_path, input_data)) { + return false; + } - success = DoSign(pkey.get(), input_data, signature); + // Sanity check for non-raw input + if (input_data.size() > EVP_MAX_MD_SIZE) { + fprintf(stderr, "Error: input data looks too long to be a hash\n"); + return false; } - if (!success) { + if (!DoSign(pkey.get(), input_data, signature)) { return false; } @@ -413,25 +279,27 @@ bool pkeyutlTool(const args_list_t &args) { return false; } } else if (verify) { - // For verification, we need a signature file or signature data - // For simplicity, we'll read signature from stdin if no specific sig file + // Read signature from sigfile std::vector signature; - if (!ReadInputData("", signature)) { // Read signature from stdin + ScopedFILE sig_file; + sig_file.reset(fopen(sigfile_path.c_str(), "rb")); + if (!sig_file) { + fprintf(stderr, "Error: unable to open signature file '%s'\n", sigfile_path.c_str()); + return false; + } + + if (!ReadAll(&signature, sig_file.get())) { + fprintf(stderr, "Error: error reading signature data\n"); return false; } - bool success = false; - if (rawin) { - success = DoRawVerify(pkey.get(), digest_name, in_path, signature); - } else { - std::vector input_data; - if (!ReadInputData(in_path, input_data)) { - return false; - } - - success = DoVerify(pkey.get(), input_data, signature); + std::vector input_data; + if (!ReadInputData(in_path, input_data)) { + return false; } + bool success = DoVerify(pkey.get(), input_data, signature); + bssl::UniquePtr output_bio; if (out_path.empty()) { output_bio.reset(BIO_new_fp(stdout, BIO_NOCLOSE)); diff --git a/tool-openssl/pkeyutl_test.cc b/tool-openssl/pkeyutl_test.cc index c13a286ef0..3a10b5e749 100644 --- a/tool-openssl/pkeyutl_test.cc +++ b/tool-openssl/pkeyutl_test.cc @@ -108,42 +108,10 @@ TEST_F(PKeyUtlTest, VerifyTest) { // Then verify the signature { - // For verification, we need to provide the signature on stdin - // and the original data as the input file - std::string verify_cmd = "cat " + std::string(sig_path) + " | " + - "AWSLC_TOOL_PATH=./tool-openssl/tool-openssl " + - "./tool-openssl/tool-openssl pkeyutl -verify -pubin -inkey " + - pubkey_path + " -in " + in_path + " > " + out_path; - - int cmd_result = system(verify_cmd.c_str()); - ASSERT_EQ(cmd_result, 0); - - // Check that the output contains "Signature Verified Successfully" - std::string output = ReadFileToString(out_path); - ASSERT_NE(output.find("Signature Verified Successfully"), std::string::npos); - } -} - -// Test raw input signing and verification -TEST_F(PKeyUtlTest, RawInTest) { - // Sign with raw input - { - args_list_t args = {"-sign", "-rawin", "-inkey", key_path, "-in", in_path, "-out", sig_path}; + args_list_t args = {"-verify", "-pubin", "-inkey", pubkey_path, "-in", in_path, + "-sigfile", sig_path, "-out", out_path}; bool result = pkeyutlTool(args); ASSERT_TRUE(result); - } - - // Verify with raw input - { - // For verification, we need to provide the signature on stdin - // and the original data as the input file - std::string verify_cmd = "cat " + std::string(sig_path) + " | " + - "AWSLC_TOOL_PATH=./tool-openssl/tool-openssl " + - "./tool-openssl/tool-openssl pkeyutl -verify -rawin -pubin -inkey " + - pubkey_path + " -in " + in_path + " > " + out_path; - - int cmd_result = system(verify_cmd.c_str()); - ASSERT_EQ(cmd_result, 0); // Check that the output contains "Signature Verified Successfully" std::string output = ReadFileToString(out_path); @@ -151,32 +119,6 @@ TEST_F(PKeyUtlTest, RawInTest) { } } -// Test with specific digest algorithm -TEST_F(PKeyUtlTest, DigestTest) { - // Sign with specific digest - { - args_list_t args = {"-sign", "-rawin", "-digest", "sha256", "-inkey", key_path, "-in", in_path, "-out", sig_path}; - bool result = pkeyutlTool(args); - ASSERT_TRUE(result); - } - - // Verify with specific digest - { - // For verification, we need to provide the signature on stdin - // and the original data as the input file - std::string verify_cmd = "cat " + std::string(sig_path) + " | " + - "AWSLC_TOOL_PATH=./tool-openssl/tool-openssl " + - "./tool-openssl/tool-openssl pkeyutl -verify -rawin -digest sha256 -pubin -inkey " + - pubkey_path + " -in " + in_path + " > " + out_path; - - int cmd_result = system(verify_cmd.c_str()); - ASSERT_EQ(cmd_result, 0); - - // Check that the output contains "Signature Verified Successfully" - std::string output = ReadFileToString(out_path); - ASSERT_NE(output.find("Signature Verified Successfully"), std::string::npos); - } -} // Test with hexdump output TEST_F(PKeyUtlTest, HexdumpTest) { @@ -230,10 +172,10 @@ TEST_F(PKeyUtlOptionUsageErrorsTest, InvalidOptionCombinationsTest) { {"-sign", "-verify", "-inkey", key_path, "-in", in_path}, // Missing inkey {"-sign", "-in", in_path}, - // digest without rawin - {"-sign", "-digest", "sha256", "-inkey", key_path, "-in", in_path}, - // rawin without sign or verify - {"-rawin", "-inkey", key_path, "-in", in_path}, + // Verify without sigfile + {"-verify", "-inkey", key_path, "-in", in_path}, + // Sigfile with sign operation + {"-sign", "-inkey", key_path, "-in", in_path, "-sigfile", sig_path}, }; for (const auto& args : testparams) { @@ -345,40 +287,6 @@ TEST_F(PKeyUtlComparisonTest, SignCompareOpenSSL) { ASSERT_NE(openssl_output_str.find("Signature Verified Successfully"), std::string::npos); } -// Test raw input signing against OpenSSL -TEST_F(PKeyUtlComparisonTest, RawSignCompareOpenSSL) { - std::string tool_command = std::string(tool_executable_path) + " pkeyutl -sign -rawin -digest sha256 -inkey " + - key_path + " -in " + in_path + " -out " + sig_path_tool; - std::string openssl_command = std::string(openssl_executable_path) + " pkeyutl -sign -rawin -digest sha256 -inkey " + - key_path + " -in " + in_path + " -out " + sig_path_openssl; - - 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; - - // Verify both signatures with the public key - std::string tool_verify_cmd = std::string(tool_executable_path) + " pkeyutl -verify -rawin -digest sha256 -pubin -inkey " + - pubkey_path + " -in " + in_path + " -sigfile " + sig_path_tool + - " > " + out_path_tool; - std::string openssl_verify_cmd = std::string(openssl_executable_path) + " pkeyutl -verify -rawin -digest sha256 -pubin -inkey " + - pubkey_path + " -in " + in_path + " -sigfile " + sig_path_openssl + - " > " + out_path_openssl; - - ASSERT_EQ(system(tool_verify_cmd.c_str()), 0); - ASSERT_EQ(system(openssl_verify_cmd.c_str()), 0); - - // Read verification results - 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()); - - // Both should verify successfully - ASSERT_NE(tool_output_str.find("Signature Verified Successfully"), std::string::npos); - ASSERT_NE(openssl_output_str.find("Signature Verified Successfully"), std::string::npos); -} // Test hexdump output against OpenSSL TEST_F(PKeyUtlComparisonTest, HexdumpCompareOpenSSL) { @@ -416,7 +324,6 @@ TEST_F(PKeyUtlComparisonTest, HelpCompareOpenSSL) { ASSERT_NE(tool_output_str.find("-verify"), std::string::npos); ASSERT_NE(tool_output_str.find("-inkey"), std::string::npos); ASSERT_NE(tool_output_str.find("-pubin"), std::string::npos); - ASSERT_NE(tool_output_str.find("-rawin"), std::string::npos); - ASSERT_NE(tool_output_str.find("-digest"), std::string::npos); + ASSERT_NE(tool_output_str.find("-sigfile"), std::string::npos); ASSERT_NE(tool_output_str.find("-hexdump"), std::string::npos); } From b95263d76eb08faa90dfb852a0e29f46fb54f04c Mon Sep 17 00:00:00 2001 From: Shubham Mittal Date: Mon, 28 Jul 2025 15:31:57 -0700 Subject: [PATCH 04/11] remove duplicate CreateTestKey --- tool-openssl/pkeyutl_test.cc | 18 ------------------ 1 file changed, 18 deletions(-) diff --git a/tool-openssl/pkeyutl_test.cc b/tool-openssl/pkeyutl_test.cc index 3a10b5e749..98a25d1521 100644 --- a/tool-openssl/pkeyutl_test.cc +++ b/tool-openssl/pkeyutl_test.cc @@ -10,24 +10,6 @@ #include "../crypto/test/test_util.h" #include -// Helper function to create a test key -EVP_PKEY* CreateTestKey(int key_bits) { - bssl::UniquePtr pkey(EVP_PKEY_new()); - if (!pkey) { - return nullptr; - } - - bssl::UniquePtr rsa(RSA_new()); - bssl::UniquePtr bn(BN_new()); - if (!bn || !BN_set_word(bn.get(), RSA_F4) || - !RSA_generate_key_ex(rsa.get(), key_bits, bn.get(), nullptr) || - !EVP_PKEY_assign_RSA(pkey.get(), rsa.release())) { - return nullptr; - } - - return pkey.release(); -} - class PKeyUtlTest : public ::testing::Test { protected: void SetUp() override { From 92fca7c03e86566dbf431683aefca4ac63d29cdc Mon Sep 17 00:00:00 2001 From: Shubham Mittal Date: Tue, 29 Jul 2025 17:22:42 -0700 Subject: [PATCH 05/11] remove hexdump option --- tool-openssl/pkeyutl.cc | 16 +++--------- tool-openssl/pkeyutl_test.cc | 48 ++++-------------------------------- 2 files changed, 9 insertions(+), 55 deletions(-) diff --git a/tool-openssl/pkeyutl.cc b/tool-openssl/pkeyutl.cc index 64f878e267..fc9eaa608b 100644 --- a/tool-openssl/pkeyutl.cc +++ b/tool-openssl/pkeyutl.cc @@ -23,7 +23,6 @@ static const argument_t kArguments[] = { { "-inkey", kOptionalArgument, "Input private key file" }, { "-pubin", kBooleanArgument, "Input is a public key" }, { "-passin", kOptionalArgument, "Input file pass phrase source" }, - { "-hexdump", kBooleanArgument, "Hex dump output" }, { "", kOptionalArgument, "" } }; @@ -163,8 +162,7 @@ static bool DoVerify(EVP_PKEY *pkey, const std::vector &input_data, } } -static bool WriteOutput(const std::vector &data, const std::string &out_path, - bool hexdump) { +static bool WriteOutput(const std::vector &data, const std::string &out_path) { bssl::UniquePtr output_bio; if (out_path.empty()) { output_bio.reset(BIO_new_fp(stdout, BIO_NOCLOSE)); @@ -181,12 +179,7 @@ static bool WriteOutput(const std::vector &data, const std::string &out return false; } - if (hexdump) { - BIO_dump(output_bio.get(), reinterpret_cast(data.data()), data.size()); - } else { - BIO_write(output_bio.get(), data.data(), data.size()); - } - + BIO_write(output_bio.get(), data.data(), data.size()); return true; } @@ -202,7 +195,7 @@ bool pkeyutlTool(const args_list_t &args) { } std::string in_path, out_path, inkey_path, passin_arg, sigfile_path; - bool sign = false, verify = false, pubin = false, hexdump = false; + bool sign = false, verify = false, pubin = false; GetString(&in_path, "-in", "", parsed_args); GetString(&out_path, "-out", "", parsed_args); @@ -212,7 +205,6 @@ bool pkeyutlTool(const args_list_t &args) { GetBoolArgument(&sign, "-sign", parsed_args); GetBoolArgument(&verify, "-verify", parsed_args); GetBoolArgument(&pubin, "-pubin", parsed_args); - GetBoolArgument(&hexdump, "-hexdump", parsed_args); // Display help if (HasArgument(parsed_args, "-help")) { @@ -275,7 +267,7 @@ bool pkeyutlTool(const args_list_t &args) { return false; } - if (!WriteOutput(signature, out_path, hexdump)) { + if (!WriteOutput(signature, out_path)) { return false; } } else if (verify) { diff --git a/tool-openssl/pkeyutl_test.cc b/tool-openssl/pkeyutl_test.cc index 98a25d1521..06795284b2 100644 --- a/tool-openssl/pkeyutl_test.cc +++ b/tool-openssl/pkeyutl_test.cc @@ -9,6 +9,10 @@ #include "test_util.h" #include "../crypto/test/test_util.h" #include +#include +#include +#include + class PKeyUtlTest : public ::testing::Test { protected: @@ -102,28 +106,10 @@ TEST_F(PKeyUtlTest, VerifyTest) { } -// Test with hexdump output -TEST_F(PKeyUtlTest, HexdumpTest) { - args_list_t args = {"-sign", "-inkey", key_path, "-in", in_path, "-out", out_path, "-hexdump"}; - bool result = pkeyutlTool(args); - ASSERT_TRUE(result); - - // Check that the output is in hexdump format (contains hex digits and addresses) - std::string output = ReadFileToString(out_path); - ASSERT_FALSE(output.empty()); - - // Hexdump typically contains colons and spaces - ASSERT_NE(output.find(":"), std::string::npos); - - // Hexdump should contain only printable characters - for (char c : output) { - ASSERT_TRUE(std::isprint(c) || std::isspace(c)); - } -} // Test with password-protected key TEST_F(PKeyUtlTest, PassinTest) { - args_list_t args = {"-sign", "-inkey", protected_key_path, "-passin", "pass:testpassword", "-in", in_path, "-out", out_path}; + args_list_t args = {"-sign", "-inkey", protected_key_path, "-passin", "testpassword", "-in", in_path, "-out", out_path}; bool result = pkeyutlTool(args); ASSERT_TRUE(result); @@ -270,29 +256,6 @@ TEST_F(PKeyUtlComparisonTest, SignCompareOpenSSL) { } -// Test hexdump output against OpenSSL -TEST_F(PKeyUtlComparisonTest, HexdumpCompareOpenSSL) { - std::string tool_command = std::string(tool_executable_path) + " pkeyutl -sign -inkey " + - key_path + " -in " + in_path + " -hexdump -out " + out_path_tool; - std::string openssl_command = std::string(openssl_executable_path) + " pkeyutl -sign -inkey " + - key_path + " -in " + in_path + " -hexdump -out " + out_path_openssl; - - RunCommandsAndCompareOutput(tool_command, openssl_command, out_path_tool, out_path_openssl, tool_output_str, openssl_output_str); - - // Both outputs should be in hexdump format - ASSERT_NE(tool_output_str.find(":"), std::string::npos); - ASSERT_NE(openssl_output_str.find(":"), std::string::npos); - - // The hexdump format might differ slightly between implementations, - // but both should contain only printable characters - for (char c : tool_output_str) { - ASSERT_TRUE(std::isprint(c) || std::isspace(c)); - } - - for (char c : openssl_output_str) { - ASSERT_TRUE(std::isprint(c) || std::isspace(c)); - } -} // Test help output against OpenSSL TEST_F(PKeyUtlComparisonTest, HelpCompareOpenSSL) { @@ -307,5 +270,4 @@ TEST_F(PKeyUtlComparisonTest, HelpCompareOpenSSL) { ASSERT_NE(tool_output_str.find("-inkey"), std::string::npos); ASSERT_NE(tool_output_str.find("-pubin"), std::string::npos); ASSERT_NE(tool_output_str.find("-sigfile"), std::string::npos); - ASSERT_NE(tool_output_str.find("-hexdump"), std::string::npos); } From 18f8de6fcfd768716c3b9a91cc9d745b40afac90 Mon Sep 17 00:00:00 2001 From: Shubham Mittal Date: Tue, 29 Jul 2025 22:38:27 -0700 Subject: [PATCH 06/11] fix pass len --- tool-openssl/pkeyutl_test.cc | 6 +----- 1 file changed, 1 insertion(+), 5 deletions(-) diff --git a/tool-openssl/pkeyutl_test.cc b/tool-openssl/pkeyutl_test.cc index 06795284b2..f70e9f031c 100644 --- a/tool-openssl/pkeyutl_test.cc +++ b/tool-openssl/pkeyutl_test.cc @@ -9,10 +9,6 @@ #include "test_util.h" #include "../crypto/test/test_util.h" #include -#include -#include -#include - class PKeyUtlTest : public ::testing::Test { protected: @@ -42,7 +38,7 @@ class PKeyUtlTest : public ::testing::Test { ASSERT_TRUE(protected_key_file); ASSERT_TRUE(PEM_write_PrivateKey(protected_key_file.get(), pkey.get(), EVP_aes_256_cbc(), - (unsigned char*)"testpassword", 11, + (unsigned char*)"testpassword", 12, nullptr, nullptr)); // Create a test input file with some data From a18fc215b4933d20de8b18cdef2c254ed76aa42c Mon Sep 17 00:00:00 2001 From: Shubham Mittal Date: Wed, 30 Jul 2025 16:11:19 -0700 Subject: [PATCH 07/11] fixes for CI --- tool-openssl/pkeyutl.cc | 4 ++-- tool-openssl/pkeyutl_test.cc | 17 ----------------- 2 files changed, 2 insertions(+), 19 deletions(-) diff --git a/tool-openssl/pkeyutl.cc b/tool-openssl/pkeyutl.cc index fc9eaa608b..6584d29e4f 100644 --- a/tool-openssl/pkeyutl.cc +++ b/tool-openssl/pkeyutl.cc @@ -165,7 +165,7 @@ static bool DoVerify(EVP_PKEY *pkey, const std::vector &input_data, static bool WriteOutput(const std::vector &data, const std::string &out_path) { bssl::UniquePtr output_bio; if (out_path.empty()) { - output_bio.reset(BIO_new_fp(stdout, BIO_NOCLOSE)); + output_bio.reset(BIO_new_fp(stdout, BIO_CLOSE)); } else { output_bio.reset(BIO_new(BIO_s_file())); if (BIO_write_filename(output_bio.get(), out_path.c_str()) <= 0) { @@ -294,7 +294,7 @@ bool pkeyutlTool(const args_list_t &args) { bssl::UniquePtr output_bio; if (out_path.empty()) { - output_bio.reset(BIO_new_fp(stdout, BIO_NOCLOSE)); + output_bio.reset(BIO_new_fp(stdout, BIO_CLOSE)); } else { output_bio.reset(BIO_new(BIO_s_file())); if (BIO_write_filename(output_bio.get(), out_path.c_str()) <= 0) { diff --git a/tool-openssl/pkeyutl_test.cc b/tool-openssl/pkeyutl_test.cc index f70e9f031c..4542fbeeca 100644 --- a/tool-openssl/pkeyutl_test.cc +++ b/tool-openssl/pkeyutl_test.cc @@ -250,20 +250,3 @@ TEST_F(PKeyUtlComparisonTest, SignCompareOpenSSL) { ASSERT_NE(tool_output_str.find("Signature Verified Successfully"), std::string::npos); ASSERT_NE(openssl_output_str.find("Signature Verified Successfully"), std::string::npos); } - - - -// Test help output against OpenSSL -TEST_F(PKeyUtlComparisonTest, HelpCompareOpenSSL) { - std::string tool_command = std::string(tool_executable_path) + " pkeyutl -help > " + out_path_tool; - std::string openssl_command = std::string(openssl_executable_path) + " pkeyutl -help > " + out_path_openssl; - - RunCommandsAndCompareOutput(tool_command, openssl_command, out_path_tool, out_path_openssl, tool_output_str, openssl_output_str); - - // Both should contain help information for the supported options - ASSERT_NE(tool_output_str.find("-sign"), std::string::npos); - ASSERT_NE(tool_output_str.find("-verify"), std::string::npos); - ASSERT_NE(tool_output_str.find("-inkey"), std::string::npos); - ASSERT_NE(tool_output_str.find("-pubin"), std::string::npos); - ASSERT_NE(tool_output_str.find("-sigfile"), std::string::npos); -} From 4ea98cb986f816abdaf2011d6992c7236a1470ad Mon Sep 17 00:00:00 2001 From: Shubham Mittal Date: Wed, 30 Jul 2025 16:47:14 -0700 Subject: [PATCH 08/11] more print statements --- tool-openssl/test_util.h | 44 +++++++++++++++++++++++++++++++++++++--- 1 file changed, 41 insertions(+), 3 deletions(-) diff --git a/tool-openssl/test_util.h b/tool-openssl/test_util.h index 26f47bc069..b8c6885282 100644 --- a/tool-openssl/test_util.h +++ b/tool-openssl/test_util.h @@ -29,25 +29,63 @@ static inline std::string &trim(std::string &s) { // Helper function to read file content into a string inline std::string ReadFileToString(const std::string& file_path) { + fprintf(stderr, "DEBUG: ReadFileToString - Starting for path: %s\n", file_path.c_str()); + if (file_path.empty()) { + fprintf(stderr, "DEBUG: ReadFileToString - Empty file path provided\n"); return ""; } // Check if file exists first + fprintf(stderr, "DEBUG: ReadFileToString - Checking if file exists with stat()\n"); struct stat stat_buffer; - if (stat(file_path.c_str(), &stat_buffer) != 0) { + int stat_result = stat(file_path.c_str(), &stat_buffer); + if (stat_result != 0) { + fprintf(stderr, "DEBUG: ReadFileToString - stat() failed with error code: %d, errno: %d\n", + stat_result, errno); return ""; } + fprintf(stderr, "DEBUG: ReadFileToString - File exists, size: %lld bytes\n", + (long long)stat_buffer.st_size); + fprintf(stderr, "DEBUG: ReadFileToString - Opening file stream\n"); std::ifstream file_stream(file_path, std::ios::binary); if (!file_stream.is_open()) { + fprintf(stderr, "DEBUG: ReadFileToString - Failed to open file stream, errno: %d\n", errno); return ""; } + fprintf(stderr, "DEBUG: ReadFileToString - File stream opened successfully\n"); + fprintf(stderr, "DEBUG: ReadFileToString - Creating output buffer and reading file content\n"); std::ostringstream output_buffer; - output_buffer << file_stream.rdbuf(); - return output_buffer.str(); + // Check stream state before reading + if (file_stream.good()) { + output_buffer << file_stream.rdbuf(); + fprintf(stderr, "DEBUG: ReadFileToString - File content read into buffer\n"); + } else { + fprintf(stderr, "DEBUG: ReadFileToString - File stream not in good state before reading\n"); + return ""; + } + + // Check stream state after reading + if (!file_stream.good() && !file_stream.eof()) { + fprintf(stderr, "DEBUG: ReadFileToString - File stream in bad state after reading, error flags: %d\n", + (int)file_stream.rdstate()); + return ""; + } + + std::string result = output_buffer.str(); + fprintf(stderr, "DEBUG: ReadFileToString - Created result string, length: %zu bytes\n", result.length()); + + fprintf(stderr, "DEBUG: ReadFileToString - Closing file stream\n"); + file_stream.close(); + if (file_stream.fail()) { + fprintf(stderr, "DEBUG: ReadFileToString - Warning: Error closing file stream\n"); + } + + fprintf(stderr, "DEBUG: ReadFileToString - Returning result\n"); + return result; } inline void RunCommandsAndCompareOutput(const std::string &tool_command, const std::string &openssl_command, From 6deb5e1a812c00c93db7a2c10648107b12bb8086 Mon Sep 17 00:00:00 2001 From: Shubham Mittal Date: Fri, 1 Aug 2025 14:46:59 -0700 Subject: [PATCH 09/11] used mixed path for ucrt64 with ReadFileToString --- .github/workflows/windows-alt.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/windows-alt.yml b/.github/workflows/windows-alt.yml index fa7b43f548..c27ed13c93 100644 --- a/.github/workflows/windows-alt.yml +++ b/.github/workflows/windows-alt.yml @@ -226,7 +226,7 @@ jobs: echo "GOPATH=${SYSROOT}" >> $GITHUB_ENV echo "GOROOT=${SYSROOT}/lib/go" >> $GITHUB_ENV echo "CMAKE_GENERATOR=${{ matrix.generator }}" >> $GITHUB_ENV - cygpath -w ${SYSROOT}/bin >> $GITHUB_PATH + cygpath -m ${SYSROOT}/bin >> $GITHUB_PATH - name: Checkout uses: actions/checkout@v4 - name: Setup CMake From 67826522ec25f8349b46d6ad566e97b40eff10ff Mon Sep 17 00:00:00 2001 From: Justin Smith Date: Mon, 4 Aug 2025 10:38:16 -0400 Subject: [PATCH 10/11] debug ucrt64 failures --- .github/workflows/windows-alt.yml | 5 ----- 1 file changed, 5 deletions(-) diff --git a/.github/workflows/windows-alt.yml b/.github/workflows/windows-alt.yml index c27ed13c93..7b8446d4ac 100644 --- a/.github/workflows/windows-alt.yml +++ b/.github/workflows/windows-alt.yml @@ -242,10 +242,5 @@ jobs: CMAKE_FIND_ROOT_PATH_MODE_INCLUDE=ONLY \ CMAKE_BUILD_TYPE=Release \ - name: Run tests - if: matrix.generator != 'MSYS Makefiles' - shell: 'bash' - run: cmake --build ./build --target run_tests - - name: Run tests - if: matrix.generator == 'MSYS Makefiles' shell: 'msys2 {0}' run: cmake --build ./build --target run_tests From 53e5039f63424dee04741fd7ba4f9efa51bffcd7 Mon Sep 17 00:00:00 2001 From: Justin Smith Date: Mon, 4 Aug 2025 11:51:18 -0400 Subject: [PATCH 11/11] Remove test debug statements --- tool-openssl/test_util.h | 44 +++------------------------------------- 1 file changed, 3 insertions(+), 41 deletions(-) diff --git a/tool-openssl/test_util.h b/tool-openssl/test_util.h index b8c6885282..26f47bc069 100644 --- a/tool-openssl/test_util.h +++ b/tool-openssl/test_util.h @@ -29,63 +29,25 @@ static inline std::string &trim(std::string &s) { // Helper function to read file content into a string inline std::string ReadFileToString(const std::string& file_path) { - fprintf(stderr, "DEBUG: ReadFileToString - Starting for path: %s\n", file_path.c_str()); - if (file_path.empty()) { - fprintf(stderr, "DEBUG: ReadFileToString - Empty file path provided\n"); return ""; } // Check if file exists first - fprintf(stderr, "DEBUG: ReadFileToString - Checking if file exists with stat()\n"); struct stat stat_buffer; - int stat_result = stat(file_path.c_str(), &stat_buffer); - if (stat_result != 0) { - fprintf(stderr, "DEBUG: ReadFileToString - stat() failed with error code: %d, errno: %d\n", - stat_result, errno); + if (stat(file_path.c_str(), &stat_buffer) != 0) { return ""; } - fprintf(stderr, "DEBUG: ReadFileToString - File exists, size: %lld bytes\n", - (long long)stat_buffer.st_size); - fprintf(stderr, "DEBUG: ReadFileToString - Opening file stream\n"); std::ifstream file_stream(file_path, std::ios::binary); if (!file_stream.is_open()) { - fprintf(stderr, "DEBUG: ReadFileToString - Failed to open file stream, errno: %d\n", errno); return ""; } - fprintf(stderr, "DEBUG: ReadFileToString - File stream opened successfully\n"); - fprintf(stderr, "DEBUG: ReadFileToString - Creating output buffer and reading file content\n"); std::ostringstream output_buffer; + output_buffer << file_stream.rdbuf(); - // Check stream state before reading - if (file_stream.good()) { - output_buffer << file_stream.rdbuf(); - fprintf(stderr, "DEBUG: ReadFileToString - File content read into buffer\n"); - } else { - fprintf(stderr, "DEBUG: ReadFileToString - File stream not in good state before reading\n"); - return ""; - } - - // Check stream state after reading - if (!file_stream.good() && !file_stream.eof()) { - fprintf(stderr, "DEBUG: ReadFileToString - File stream in bad state after reading, error flags: %d\n", - (int)file_stream.rdstate()); - return ""; - } - - std::string result = output_buffer.str(); - fprintf(stderr, "DEBUG: ReadFileToString - Created result string, length: %zu bytes\n", result.length()); - - fprintf(stderr, "DEBUG: ReadFileToString - Closing file stream\n"); - file_stream.close(); - if (file_stream.fail()) { - fprintf(stderr, "DEBUG: ReadFileToString - Warning: Error closing file stream\n"); - } - - fprintf(stderr, "DEBUG: ReadFileToString - Returning result\n"); - return result; + return output_buffer.str(); } inline void RunCommandsAndCompareOutput(const std::string &tool_command, const std::string &openssl_command,