Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add initial x509 tool #1666

Merged
merged 16 commits into from
Jul 2, 2024
1 change: 1 addition & 0 deletions CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -949,6 +949,7 @@ if(BUILD_LIBSSL)
add_subdirectory(ssl)
if(BUILD_TOOL)
add_subdirectory(tool)
add_subdirectory(tool-openssl)
justsmth marked this conversation as resolved.
Show resolved Hide resolved
endif()
endif()
add_subdirectory(util/fipstools)
Expand Down
54 changes: 54 additions & 0 deletions tool-openssl/CMakeLists.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,54 @@
add_executable(
openssl
justsmth marked this conversation as resolved.
Show resolved Hide resolved

../tool/args.cc
../tool/file.cc
tool.cc
x509.cc
)

target_include_directories(openssl PUBLIC ${PROJECT_SOURCE_DIR}/include)
target_compile_options(openssl PUBLIC -DINTERNAL_TOOL)

if(WIN32)
target_link_libraries(openssl ws2_32)
endif()

if(APPLE OR WIN32 OR ANDROID)
target_link_libraries(openssl ssl crypto)
set(LIBRT_FLAG "")
else()
find_library(FOUND_LIBRT rt)
if(FOUND_LIBRT)
target_link_libraries(openssl ssl crypto -lrt)
set(LIBRT_FLAG "-lrt")
else()
target_link_libraries(openssl ssl crypto)
set(LIBRT_FLAG "")
endif()
endif()

target_include_directories(openssl BEFORE PRIVATE ${PROJECT_BINARY_DIR}/symbol_prefix_include)

install(TARGETS openssl
RUNTIME DESTINATION ${CMAKE_INSTALL_BINDIR}
BUNDLE DESTINATION ${CMAKE_INSTALL_BINDIR}
)

if(MSVC AND CMAKE_BUILD_TYPE_LOWER MATCHES "relwithdebinfo" AND FIPS)
install (FILES $<TARGET_FILE_DIR:openssl>/openssl.pdb DESTINATION ${CMAKE_INSTALL_LIBDIR})
endif()

if(BUILD_TESTING)
add_executable(
tool_openssl_test

x509_test.cc
../tool/args.cc
../tool/file.cc
x509.cc
)

target_link_libraries(tool_openssl_test boringssl_gtest_main ssl crypto)
add_dependencies(all_tests tool_openssl_test)
endif()
8 changes: 8 additions & 0 deletions tool-openssl/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
# OpenSSL Tools for AWS-LC
*Files expected to change*

Current status:
* Developed structure for new OpenSSL tools
* Contains initial implementation for OpenSSL x509 tool, options -in and -out (x509.cc), and unit test (x509_test.cc)
* x509_test.cc contains test portions ultimately to be used for future options but unnecessary for -in/-out unit test

21 changes: 21 additions & 0 deletions tool-openssl/internal.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
// Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
// SPDX-License-Identifier: Apache-2.0 OR ISC

#ifndef INTERNAL_H
#define INTERNAL_H

#include "../tool/internal.h"
#include <string>
#include <vector>

typedef bool (*tool_func_t)(const std::vector<std::string> &args);

X509* CreateAndSignX509Certificate();
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);

#endif //INTERNAL_H


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

#include <array>
#include <iostream>
#include <openssl/ssl.h>

#if defined(OPENSSL_WINDOWS)
#include <fcntl.h>
#include <io.h>
#else
#include <libgen.h>
#include <signal.h>
#endif

#include "./internal.h"

typedef bool (*tool_func_t)(const std::vector<std::string> &args);
ecdeye marked this conversation as resolved.
Show resolved Hide resolved

struct Tool {
const char *name;
tool_func_t func;
};

static const std::array<Tool, 1> kTools = {{
{ "x509", X509Tool },
}};

static void usage(const std::string &name) {
std::cout << "Usage: " << name << " COMMAND\n\n";
std::cout << "Available commands:\n";

for (const auto& tool : kTools) {
if (tool.func == nullptr) {
break;
}
std::cout << " " << tool.name << "\n";
}
ecdeye marked this conversation as resolved.
Show resolved Hide resolved
}

static void initialize() {
#if defined(OPENSSL_WINDOWS)
ecdeye marked this conversation as resolved.
Show resolved Hide resolved
// Read and write in binary mode. This makes bssl on Windows consistent with
// bssl on other platforms, and also makes it consistent with MSYS's commands
// like diff(1) and md5sum(1). This is especially important for the digest
// commands.
if (_setmode(_fileno(stdin), _O_BINARY) == -1) {
perror("_setmode(_fileno(stdin), O_BINARY)");
exit(1);
}
if (_setmode(_fileno(stdout), _O_BINARY) == -1) {
perror("_setmode(_fileno(stdout), O_BINARY)");
exit(1);
}
if (_setmode(_fileno(stderr), _O_BINARY) == -1) {
perror("_setmode(_fileno(stderr), O_BINARY)");
exit(1);
}
#else
// Ignore SIGPIPE to prevent the process from terminating if it tries to
// write to a pipe that has been closed by the reading end. SIGPIPE can be
// received when writing to sockets or pipes that are no longer connected.
signal(SIGPIPE, SIG_IGN);
ecdeye marked this conversation as resolved.
Show resolved Hide resolved
#endif
}

tool_func_t FindTool(const std::string &name) {
for (const auto& tool : kTools) {
if (tool.name == name) {
return tool.func;
}
}
return nullptr;
}

tool_func_t FindTool(int argc, char **argv, int &starting_arg) {
#if !defined(OPENSSL_WINDOWS)
tool_func_t tool = FindTool(basename(argv[0]));
if (tool != nullptr) {
return tool;
}
#endif
starting_arg++;
if (argc > 1) {
return FindTool(argv[1]);
}
return nullptr;
}

int main(int argc, char **argv) {
initialize();
CRYPTO_library_init();

int starting_arg = 1;
tool_func_t tool = FindTool(argc, argv, starting_arg);

if (tool == nullptr) {
usage(argv[0]);
return 1;
}

args_list_t args;
for (int i = starting_arg; i < argc; i++) {
args.emplace_back(argv[i]);
}

if (!tool(args)) {
ERR_print_errors_fp(stderr);
return 1;
}

return 0;
ecdeye marked this conversation as resolved.
Show resolved Hide resolved
}
74 changes: 74 additions & 0 deletions tool-openssl/x509.cc
Original file line number Diff line number Diff line change
@@ -0,0 +1,74 @@
// Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
// SPDX-License-Identifier: Apache-2.0 OR ISC

#include <openssl/x509.h>
#include <openssl/pem.h>
#include "internal.h"

static const argument_t kArguments[] = {
{ "-in", kRequiredArgument, "Input file" },
{ "-out", kRequiredArgument, "Output file" },
{ "", kOptionalArgument, "" }
};

// Map arguments using tool/args.cc
bool X509Tool(const args_list_t &args) {
justsmth marked this conversation as resolved.
Show resolved Hide resolved
args_map_t parsed_args;
if (!ParseKeyValueArguments(&parsed_args, args, kArguments)) {
PrintUsage(kArguments);
return false;
}

// Check for required arguments
std::string in_path, out_path;
if (!GetString(&in_path, "-in", "", parsed_args)) {
fprintf(stderr, "Missing required argument: -in\n");
PrintUsage(kArguments);
return false;
}
if (!GetString(&out_path, "-out", "", parsed_args)) {
fprintf(stderr, "Missing required argument: -out\n");
PrintUsage(kArguments);
return false;
}

// Read input file using ReadAll function from tool/file.cc
std::vector<uint8_t> input_data;
ScopedFILE in_file(fopen(in_path.c_str(), "rb"));
if (!in_file) {
fprintf(stderr, "Failed to open input file '%s'.\n", in_path.c_str());
return false;
}
if (!ReadAll(&input_data, in_file.get())) {
fprintf(stderr, "Failed to read input file '%s'.\n", in_path.c_str());
return false;
}

// Parse x509 certificate from input file
const uint8_t *p = input_data.data();
bssl::UniquePtr<X509> x509(d2i_X509(nullptr, &p, input_data.size()));
if (!x509) {
fprintf(stderr, "Failed to parse X509 certificate from '%s'.\n", in_path.c_str());
ERR_print_errors_fp(stderr);
return false;
}

// Serialize certificate to DER format
uint8_t *out_data = nullptr;
int len = i2d_X509(x509.get(), &out_data);
if (len < 0) {
fprintf(stderr, "Failed to serialize X509 certificate.\n");
ERR_print_errors_fp(stderr);
return false;
}

// Write output file using WriteToFile function from tool/file.cc
if (!WriteToFile(out_path, out_data, len)) {
fprintf(stderr, "Failed to write X509 certificate to '%s'.\n", out_path.c_str());
OPENSSL_free(out_data);
return false;
}

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

#include "openssl/x509.h"
#include <openssl/err.h>
#include <gtest/gtest.h>
#include "../tool/internal.h"
#include "internal.h"

size_t createTempFILEpath(char buffer[PATH_MAX]);

X509* CreateAndSignX509Certificate() {
bssl::UniquePtr<X509> x509(X509_new());
if (!x509) return nullptr;
ecdeye marked this conversation as resolved.
Show resolved Hide resolved

// Set validity period for 1 year
if (!X509_gmtime_adj(X509_getm_notBefore(x509.get()), 0) ||
!X509_gmtime_adj(X509_getm_notAfter(x509.get()), 31536000L)) {
return nullptr;
}

// Generate and set the public key
bssl::UniquePtr<EVP_PKEY> pkey(EVP_PKEY_new());
if (!pkey) {
return nullptr;
}
bssl::UniquePtr<RSA> rsa(RSA_new());
bssl::UniquePtr<BIGNUM> bn(BN_new());
if (!bn || !BN_set_word(bn.get(), RSA_F4) ||
!RSA_generate_key_ex(rsa.get(), 2048, bn.get(), nullptr) ||
!EVP_PKEY_assign_RSA(pkey.get(), rsa.release())) {
return nullptr;
}
if (!X509_set_pubkey(x509.get(), pkey.get())) {
return nullptr;
}

// Sign certificate
if (X509_sign(x509.get(), pkey.get(), EVP_sha256()) <= 0) {
return nullptr;
}

return x509.release();
}

// Test x509 -in and -out
TEST(X509Test, X509ToolTest) {
char in_path[PATH_MAX];
char out_path[PATH_MAX];

ASSERT_GT(createTempFILEpath(in_path), 0u);
ASSERT_GT(createTempFILEpath(out_path), 0u);

bssl::UniquePtr<X509> x509(CreateAndSignX509Certificate());
ASSERT_TRUE(x509);

// Serialize certificate to DER format
uint8_t *der_data = nullptr;
int len = i2d_X509(x509.get(), &der_data);
ASSERT_GT(static_cast<size_t>(len), 0u);

ScopedFILE in_file(fopen(in_path, "wb"));
ASSERT_TRUE(in_file);
fwrite(der_data, 1, len, in_file.get());
OPENSSL_free(der_data);

in_file.reset();

// Set up x509 tool arguments
args_list_t args = {"-in", in_path, "-out", out_path};

// Call x509 tool function
bool result = X509Tool(args);
ASSERT_TRUE(result);

// Read and verify output file
ScopedFILE out_file(fopen(out_path, "rb"));
ASSERT_TRUE(out_file);

std::vector<uint8_t> output_data;
ASSERT_TRUE(ReadAll(&output_data, out_file.get()));

// Ensure output data not empty
ASSERT_FALSE(output_data.empty());

// Parse x509 cert from output file
const uint8_t *p = output_data.data();
bssl::UniquePtr<X509> parsed_x509(d2i_X509(nullptr, &p, output_data.size()));
ASSERT_TRUE(parsed_x509);

remove(in_path);
remove(out_path);
}
5 changes: 5 additions & 0 deletions util/all_tests.json
ecdeye marked this conversation as resolved.
Show resolved Hide resolved
Original file line number Diff line number Diff line change
Expand Up @@ -125,5 +125,10 @@
"cmd": [
"crypto/rwlock_static_init"
]
},
{
"cmd": [
"tool-openssl/tool_openssl_test"
]
}
]
Loading