Skip to content

Commit

Permalink
Added CRL tool to CLI (#1976)
Browse files Browse the repository at this point in the history
### Issues:
`CryptoAlg-2679`

### Description of changes: 
Added the `crl` tool to the openssl cli with `-hash`, `-fingerprint`,
`-help`, `-in`, `-noout`

### Testing
Option and OpenSSL comparison tests

By submitting this pull request, I confirm that my contribution is made
under the terms of the Apache 2.0 license and the ISC license.
  • Loading branch information
smittals2 authored Nov 15, 2024
1 parent 745359e commit cc0ce55
Show file tree
Hide file tree
Showing 5 changed files with 277 additions and 1 deletion.
3 changes: 3 additions & 0 deletions tool-openssl/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ add_executable(
rsa.cc
tool.cc
x509.cc
crl.cc
version.cc
)

Expand Down Expand Up @@ -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)
Expand Down
87 changes: 87 additions & 0 deletions tool-openssl/crl.cc
Original file line number Diff line number Diff line change
@@ -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 <openssl/x509.h>
#include <openssl/pem.h>
#include <ctime>

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);
GetString(&in, "-in", "", parsed_args);
GetBoolArgument(&hash, "-hash", parsed_args);
GetBoolArgument(&fingerprint, "-fingerprint", parsed_args);
GetBoolArgument(&noout, "-noout", 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<X509_CRL> crl(PEM_read_X509_CRL(in_file.get(), NULL, NULL, 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(crl.get())));
}

if (fingerprint) {
int j;
unsigned int n;
unsigned char md[EVP_MAX_MD_SIZE];

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())));

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, crl.get())) {
fprintf(stderr, "unable to write CRL\n");
return false;
}
}

return true;
}
184 changes: 184 additions & 0 deletions tool-openssl/crl_test.cc
Original file line number Diff line number Diff line change
@@ -0,0 +1,184 @@
// Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
// SPDX-License-Identifier: Apache-2.0 OR ISC

#include "openssl/x509.h"
#include <gtest/gtest.h>
#include <openssl/pem.h>
#include "internal.h"
#include "test_util.h"
#include "../crypto/test/test_util.h"
#include <cctype>

static X509_CRL* createTestCRL() {
bssl::UniquePtr<X509_CRL> crl(X509_CRL_new());
if (!crl) {
ERR_print_errors_fp(stderr);
return nullptr;
}

// Set issuer name
bssl::UniquePtr<X509_NAME> 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
bssl::UniquePtr<ASN1_TIME> lastUpdate(ASN1_TIME_new());
bssl::UniquePtr<ASN1_TIME> 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();
bssl::UniquePtr<ASN1_INTEGER> 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
bssl::UniquePtr<EVP_PKEY> pkey(EVP_PKEY_new());
RSA *rsa = RSA_generate_key(2048, RSA_F4, NULL, NULL);
if (!pkey || !rsa || !EVP_PKEY_assign_RSA(pkey.get(), rsa)) {
return nullptr;
}

// Sign the CRL
if (!X509_CRL_sign(crl.get(), pkey.get(), EVP_sha256())) {
return nullptr;
}
return crl.release();
}

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<X509_CRL> 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<X509_CRL> 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);
}
1 change: 1 addition & 0 deletions tool-openssl/internal.h
Original file line number Diff line number Diff line change
Expand Up @@ -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
3 changes: 2 additions & 1 deletion tool-openssl/tool.cc
Original file line number Diff line number Diff line change
Expand Up @@ -15,11 +15,12 @@

#include "./internal.h"

static const std::array<Tool, 5> kTools = {{
static const std::array<Tool, 6> kTools = {{
{"dgst", dgstTool},
{"md5", md5Tool},
{"rsa", rsaTool},
{"x509", X509Tool},
{"crl", CRLTool},
{"version", VersionTool}
}};

Expand Down

0 comments on commit cc0ce55

Please sign in to comment.