From 5d45e956fd748cde2a41b28f5c90e9445c426b52 Mon Sep 17 00:00:00 2001 From: Shubham Mittal Date: Thu, 7 Nov 2024 22:17:19 -0800 Subject: [PATCH 1/3] added CRL tool to CLI --- tool-openssl/CMakeLists.txt | 3 + tool-openssl/crl.cc | 87 +++++++++++++++++ tool-openssl/crl_test.cc | 185 ++++++++++++++++++++++++++++++++++++ tool-openssl/internal.h | 1 + tool-openssl/tool.cc | 3 +- 5 files changed, 278 insertions(+), 1 deletion(-) create mode 100644 tool-openssl/crl.cc create mode 100644 tool-openssl/crl_test.cc diff --git a/tool-openssl/CMakeLists.txt b/tool-openssl/CMakeLists.txt index 815489213d..d9a7c6a72c 100644 --- a/tool-openssl/CMakeLists.txt +++ b/tool-openssl/CMakeLists.txt @@ -9,6 +9,7 @@ add_executable( rsa.cc tool.cc x509.cc + crl.cc version.cc ) @@ -59,6 +60,8 @@ if(BUILD_TESTING) rsa_test.cc x509.cc x509_test.cc + crl.cc + crl_test.cc ) target_link_libraries(tool_openssl_test boringssl_gtest_main ssl crypto) diff --git a/tool-openssl/crl.cc b/tool-openssl/crl.cc new file mode 100644 index 0000000000..c0a92f8555 --- /dev/null +++ b/tool-openssl/crl.cc @@ -0,0 +1,87 @@ +// Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. +// SPDX-License-Identifier: Apache-2.0 OR ISC + +#include "internal.h" +#include +#include +#include + +static const argument_t kArguments[] = { + { "-help", kBooleanArgument, "Display option summary" }, + { "-in", kOptionalArgument, "Input file, default stdin" }, + { "-hash", kBooleanArgument, "Print hash value" }, + { "-fingerprint", kBooleanArgument, "Print CRL fingerprint" }, + { "-noout", kBooleanArgument, "No CRL output" }, + { "", kOptionalArgument, "" } +}; + +bool CRLTool(const args_list_t &args) { + args_map_t parsed_args; + if (!ParseKeyValueArguments(&parsed_args, args, kArguments)) { + PrintUsage(kArguments); + return false; + } + + std::string in; + bool help = false, hash = false, fingerprint = false, noout = false; + + GetBoolArgument(&help, "-help", parsed_args); + GetBoolArgument(&hash, "-hash", parsed_args); + GetBoolArgument(&fingerprint, "-fingerprint", parsed_args); + GetBoolArgument(&noout, "-noout", parsed_args); + GetString(&in, "-in", "", parsed_args); + + // Display crl tool option summary + if (help) { + PrintUsage(kArguments); + return false; + } + + // Read from stdin if no -in path provided + ScopedFILE in_file; + if (in.empty()) { + in_file.reset(stdin); + } else { + in_file.reset(fopen(in.c_str(), "rb")); + if (!in_file) { + fprintf(stderr, "unable to load CRL\n"); + return false; + } + } + + bssl::UniquePtr x(PEM_read_X509_CRL(in_file.get(), NULL, NULL, NULL)); + + if (x == NULL) { + fprintf(stderr, "unable to load CRL\n"); + return false; + } + + if (hash) { + fprintf(stdout, "%08x\n", X509_NAME_hash(X509_CRL_get_issuer(x.get()))); + } + + if (fingerprint) { + int j; + unsigned int n; + unsigned char md[EVP_MAX_MD_SIZE]; + + if (!X509_CRL_digest(x.get(), EVP_sha1(), md, &n)) { + fprintf(stderr, "out of memory\n"); + return false; + } + fprintf(stdout, "%s Fingerprint=", OBJ_nid2sn(EVP_MD_type(EVP_sha1()))); + + for (j = 0; j < (int)n; j++) { + fprintf(stdout, "%02X%c", md[j], (j + 1 == (int)n) ? '\n' : ':'); + } + } + + if (!noout) { + if(!PEM_write_X509_CRL(stdout, x.get())) { + fprintf(stdout, "unable to write CRL\n"); + return false; + } + } + + return true; +} diff --git a/tool-openssl/crl_test.cc b/tool-openssl/crl_test.cc new file mode 100644 index 0000000000..744ba09022 --- /dev/null +++ b/tool-openssl/crl_test.cc @@ -0,0 +1,185 @@ +// Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. +// SPDX-License-Identifier: Apache-2.0 OR ISC + +#include "openssl/x509.h" +#include +#include +#include "internal.h" +#include "test_util.h" +#include "../crypto/test/test_util.h" +#include + +static X509_CRL* createTestCRL() { + X509_CRL *crl = X509_CRL_new(); + if (!crl) { + ERR_print_errors_fp(stderr); + return nullptr; + } + + // Set issuer name + X509_NAME *issuer = X509_NAME_new(); + X509_NAME_add_entry_by_txt(issuer, "CN", MBSTRING_ASC, (unsigned char *)"Test CA", -1, -1, 0); + X509_CRL_set_issuer_name(crl, issuer); + + // Set times + ASN1_TIME *lastUpdate = ASN1_TIME_new(); + ASN1_TIME *nextUpdate = ASN1_TIME_new(); + X509_gmtime_adj(lastUpdate, 0); + X509_gmtime_adj(nextUpdate, 86400L); // 24 hours from now + X509_CRL_set1_lastUpdate(crl, lastUpdate); + X509_CRL_set1_nextUpdate(crl, nextUpdate); + + // Add a revoked certificate + X509_REVOKED *revoked = X509_REVOKED_new(); + ASN1_INTEGER *serialNumber = ASN1_INTEGER_new(); + ASN1_INTEGER_set(serialNumber, 1); // Serial number of revoked cert + X509_REVOKED_set_serialNumber(revoked, serialNumber); + X509_REVOKED_set_revocationDate(revoked, lastUpdate); + X509_CRL_add0_revoked(crl, revoked); + + // Generate a key pair for signing + EVP_PKEY *pkey = EVP_PKEY_new(); + RSA *rsa = RSA_generate_key(2048, RSA_F4, NULL, NULL); + EVP_PKEY_assign_RSA(pkey, rsa); + + // Sign the CRL + X509_CRL_sign(crl, pkey, EVP_sha256()); + + // Free resources + X509_NAME_free(issuer); + ASN1_TIME_free(lastUpdate); + ASN1_TIME_free(nextUpdate); + ASN1_INTEGER_free(serialNumber); + EVP_PKEY_free(pkey); + + return crl; +} + +class CRLTest : public ::testing::Test { +protected: + void SetUp() override { + ASSERT_GT(createTempFILEpath(in_path), 0u); + + // Create a test CRL + crl.reset(createTestCRL()); + ASSERT_TRUE(crl); + + + ScopedFILE in_file(fopen(in_path, "wb")); + ASSERT_TRUE(in_file); + PEM_write_X509_CRL(in_file.get(), crl.get()); + } + + void TearDown() override { + RemoveFile(in_path); + } + + char in_path[PATH_MAX]; + bssl::UniquePtr crl; +}; + + +// ----------------------------- CRL Option Tests ----------------------------- + +// Test -in +TEST_F(CRLTest, CRLTestIn) { + args_list_t args = {"-in", in_path}; + bool result = CRLTool(args); + ASSERT_TRUE(result); +} + +// Test -hash +TEST_F(CRLTest, CRLTestHash) { + args_list_t args = {"-in", in_path, "-hash"}; + bool result = CRLTool(args); + ASSERT_TRUE(result); +} + +// Test -fingerprint +TEST_F(CRLTest, CRLTestFingerprint) { + args_list_t args = {"-in", in_path, "-fingerprint"}; + bool result = CRLTool(args); + ASSERT_TRUE(result); +} + +// Test -noout +TEST_F(CRLTest, CRLTestNoout) { + args_list_t args = {"-in", in_path, "-noout"}; + bool result = CRLTool(args); + ASSERT_TRUE(result); +} + +// -------------------- CRL OpenSSL Comparison Tests -------------------------- + +// Comparison tests cannot run without set up of environment variables: +// AWSLC_TOOL_PATH and OPENSSL_TOOL_PATH. + +class CRLComparisonTest : 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 a test CRL + crl.reset(createTestCRL()); + ASSERT_TRUE(crl); + + ScopedFILE in_file(fopen(in_path, "wb")); + ASSERT_TRUE(in_file); + PEM_write_X509_CRL(in_file.get(), crl.get()); + } + + 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]; + const char* tool_executable_path; + const char* openssl_executable_path; + std::string tool_output_str; + std::string openssl_output_str; + bssl::UniquePtr crl; +}; + +// Test against OpenSSL output "openssl crl -in file" +TEST_F(CRLComparisonTest, CRLToolCompareOpenSSL) { + std::string tool_command = std::string(tool_executable_path) + " crl -in " + in_path + " > " + out_path_tool; + std::string openssl_command = std::string(openssl_executable_path) + " crl -in " + in_path + " > " + out_path_openssl; + + RunCommandsAndCompareOutput(tool_command, openssl_command, out_path_tool, out_path_openssl, tool_output_str, openssl_output_str); +} + +// Test against OpenSSL output "openssl crl -in file -hash -fingerprint" +TEST_F(CRLComparisonTest, CRLToolCompareHashFingerprintOpenSSL) { + std::string tool_command = std::string(tool_executable_path) + " crl -in " + in_path + " -hash -fingerprint > " + out_path_tool; + std::string openssl_command = std::string(openssl_executable_path) + " crl -in " + in_path + " -hash -fingerprint > " + out_path_openssl; + + RunCommandsAndCompareOutput(tool_command, openssl_command, out_path_tool, out_path_openssl, tool_output_str, openssl_output_str); +} + +// Test against OpenSSL output "openssl crl -in file -hash -fingerprint -noout" +TEST_F(CRLComparisonTest, CRLToolCompareHashFingerprintNoOutOpenSSL) { + std::string tool_command = std::string(tool_executable_path) + " crl -in " + in_path + " -hash -fingerprint -noout > " + out_path_tool; + std::string openssl_command = std::string(openssl_executable_path) + " crl -in " + in_path + " -hash -fingerprint -noout > " + out_path_openssl; + + RunCommandsAndCompareOutput(tool_command, openssl_command, out_path_tool, out_path_openssl, tool_output_str, openssl_output_str); +} + + + + diff --git a/tool-openssl/internal.h b/tool-openssl/internal.h index 5453a5fdae..3364f4639f 100644 --- a/tool-openssl/internal.h +++ b/tool-openssl/internal.h @@ -32,6 +32,7 @@ bool dgstTool(const args_list_t &args); bool md5Tool(const args_list_t &args); bool rsaTool(const args_list_t &args); bool X509Tool(const args_list_t &args); +bool CRLTool(const args_list_t &args); bool VersionTool(const args_list_t &args); #endif //INTERNAL_H diff --git a/tool-openssl/tool.cc b/tool-openssl/tool.cc index c4d2f24412..ae42679eca 100644 --- a/tool-openssl/tool.cc +++ b/tool-openssl/tool.cc @@ -15,11 +15,12 @@ #include "./internal.h" -static const std::array kTools = {{ +static const std::array kTools = {{ {"dgst", dgstTool}, {"md5", md5Tool}, {"rsa", rsaTool}, {"x509", X509Tool}, + {"crl", CRLTool}, {"version", VersionTool} }}; From 87a56801897af6ecca014c050f38fddaeb116450 Mon Sep 17 00:00:00 2001 From: Shubham Mittal Date: Thu, 7 Nov 2024 22:20:15 -0800 Subject: [PATCH 2/3] removed empty spaces --- tool-openssl/crl_test.cc | 4 ---- 1 file changed, 4 deletions(-) diff --git a/tool-openssl/crl_test.cc b/tool-openssl/crl_test.cc index 744ba09022..c16ce89292 100644 --- a/tool-openssl/crl_test.cc +++ b/tool-openssl/crl_test.cc @@ -179,7 +179,3 @@ TEST_F(CRLComparisonTest, CRLToolCompareHashFingerprintNoOutOpenSSL) { RunCommandsAndCompareOutput(tool_command, openssl_command, out_path_tool, out_path_openssl, tool_output_str, openssl_output_str); } - - - - From b9753d4e91c62b4e8762c45feda909c73ed7a656 Mon Sep 17 00:00:00 2001 From: Shubham Mittal Date: Thu, 14 Nov 2024 21:23:23 -0800 Subject: [PATCH 3/3] addressed PR comments --- tool-openssl/crl.cc | 16 +++++------ tool-openssl/crl_test.cc | 57 +++++++++++++++++++++------------------- 2 files changed, 38 insertions(+), 35 deletions(-) diff --git a/tool-openssl/crl.cc b/tool-openssl/crl.cc index c0a92f8555..5449c4b2b7 100644 --- a/tool-openssl/crl.cc +++ b/tool-openssl/crl.cc @@ -26,10 +26,10 @@ bool CRLTool(const args_list_t &args) { bool help = false, hash = false, fingerprint = false, noout = false; GetBoolArgument(&help, "-help", parsed_args); + GetString(&in, "-in", "", parsed_args); GetBoolArgument(&hash, "-hash", parsed_args); GetBoolArgument(&fingerprint, "-fingerprint", parsed_args); GetBoolArgument(&noout, "-noout", parsed_args); - GetString(&in, "-in", "", parsed_args); // Display crl tool option summary if (help) { @@ -49,15 +49,15 @@ bool CRLTool(const args_list_t &args) { } } - bssl::UniquePtr x(PEM_read_X509_CRL(in_file.get(), NULL, NULL, NULL)); + bssl::UniquePtr crl(PEM_read_X509_CRL(in_file.get(), NULL, NULL, NULL)); - if (x == NULL) { + if (crl == NULL) { fprintf(stderr, "unable to load CRL\n"); return false; } if (hash) { - fprintf(stdout, "%08x\n", X509_NAME_hash(X509_CRL_get_issuer(x.get()))); + fprintf(stdout, "%08x\n", X509_NAME_hash(X509_CRL_get_issuer(crl.get()))); } if (fingerprint) { @@ -65,8 +65,8 @@ bool CRLTool(const args_list_t &args) { unsigned int n; unsigned char md[EVP_MAX_MD_SIZE]; - if (!X509_CRL_digest(x.get(), EVP_sha1(), md, &n)) { - fprintf(stderr, "out of memory\n"); + if (!X509_CRL_digest(crl.get(), EVP_sha1(), md, &n)) { + fprintf(stderr, "unable to get encoding of CRL\n"); return false; } fprintf(stdout, "%s Fingerprint=", OBJ_nid2sn(EVP_MD_type(EVP_sha1()))); @@ -77,8 +77,8 @@ bool CRLTool(const args_list_t &args) { } if (!noout) { - if(!PEM_write_X509_CRL(stdout, x.get())) { - fprintf(stdout, "unable to write CRL\n"); + if(!PEM_write_X509_CRL(stdout, crl.get())) { + fprintf(stderr, "unable to write CRL\n"); return false; } } diff --git a/tool-openssl/crl_test.cc b/tool-openssl/crl_test.cc index c16ce89292..0eba58c807 100644 --- a/tool-openssl/crl_test.cc +++ b/tool-openssl/crl_test.cc @@ -10,49 +10,52 @@ #include static X509_CRL* createTestCRL() { - X509_CRL *crl = X509_CRL_new(); + bssl::UniquePtr crl(X509_CRL_new()); if (!crl) { ERR_print_errors_fp(stderr); return nullptr; } // Set issuer name - X509_NAME *issuer = X509_NAME_new(); - X509_NAME_add_entry_by_txt(issuer, "CN", MBSTRING_ASC, (unsigned char *)"Test CA", -1, -1, 0); - X509_CRL_set_issuer_name(crl, issuer); + bssl::UniquePtr issuer(X509_NAME_new()); + if (!issuer || + !X509_NAME_add_entry_by_txt(issuer.get(), "CN", MBSTRING_ASC, (unsigned char *)"Test CA", -1, -1, 0) || + !X509_CRL_set_issuer_name(crl.get(), issuer.get())) { + return nullptr; + } // Set times - ASN1_TIME *lastUpdate = ASN1_TIME_new(); - ASN1_TIME *nextUpdate = ASN1_TIME_new(); - X509_gmtime_adj(lastUpdate, 0); - X509_gmtime_adj(nextUpdate, 86400L); // 24 hours from now - X509_CRL_set1_lastUpdate(crl, lastUpdate); - X509_CRL_set1_nextUpdate(crl, nextUpdate); + bssl::UniquePtr lastUpdate(ASN1_TIME_new()); + bssl::UniquePtr nextUpdate(ASN1_TIME_new()); + if (!lastUpdate || !nextUpdate || !X509_gmtime_adj(lastUpdate.get(), 0) || + !X509_gmtime_adj(nextUpdate.get(), 86400L) || // 24 hours from now + !X509_CRL_set1_lastUpdate(crl.get(), lastUpdate.get()) || + !X509_CRL_set1_nextUpdate(crl.get(), nextUpdate.get())) { + return nullptr; + } // Add a revoked certificate X509_REVOKED *revoked = X509_REVOKED_new(); - ASN1_INTEGER *serialNumber = ASN1_INTEGER_new(); - ASN1_INTEGER_set(serialNumber, 1); // Serial number of revoked cert - X509_REVOKED_set_serialNumber(revoked, serialNumber); - X509_REVOKED_set_revocationDate(revoked, lastUpdate); - X509_CRL_add0_revoked(crl, revoked); + bssl::UniquePtr serialNumber(ASN1_INTEGER_new()); + if (!revoked || !serialNumber || !ASN1_INTEGER_set(serialNumber.get(), 1) || // Serial number of revoked cert + !X509_REVOKED_set_serialNumber(revoked, serialNumber.get()) || + !X509_REVOKED_set_revocationDate(revoked, lastUpdate.get()) || + !X509_CRL_add0_revoked(crl.get(), revoked)) { + return nullptr; + } // Generate a key pair for signing - EVP_PKEY *pkey = EVP_PKEY_new(); + bssl::UniquePtr pkey(EVP_PKEY_new()); RSA *rsa = RSA_generate_key(2048, RSA_F4, NULL, NULL); - EVP_PKEY_assign_RSA(pkey, rsa); + if (!pkey || !rsa || !EVP_PKEY_assign_RSA(pkey.get(), rsa)) { + return nullptr; + } // Sign the CRL - X509_CRL_sign(crl, pkey, EVP_sha256()); - - // Free resources - X509_NAME_free(issuer); - ASN1_TIME_free(lastUpdate); - ASN1_TIME_free(nextUpdate); - ASN1_INTEGER_free(serialNumber); - EVP_PKEY_free(pkey); - - return crl; + if (!X509_CRL_sign(crl.get(), pkey.get(), EVP_sha256())) { + return nullptr; + } + return crl.release(); } class CRLTest : public ::testing::Test {