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

Build time configuration for CSR #50

Merged
merged 2 commits into from
Feb 21, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
11 changes: 11 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,15 @@ make -j$(nproc) install
make test
```

## Certificate Signing Request

There are two configuration options that will add a DNS name and IP address to the
subject alternative name in the certificate signing request.
By default they are not added.

- `cmake -DCSR_DNS_NAME=charger.pionix.de ...` to include a DNS name
- `cmake -DCSR_IP_ADDRESS=192.168.2.1 ...` to include an IPv4 address

## TPM
There is a configuration option to configure OpenSSL for use with a TPM.<br>
`cmake` ... `-DUSING_TPM2=ON`<br>
Expand All @@ -45,6 +54,7 @@ The library will use the `UseTPM` flag and the PEM private key file to
configure whether to use the `default` provider or the `tpm2` provider.

Configuration is managed via propquery strings (see CMakeLists.txt)

- `PROPQUERY_DEFAULT` is the string to use when selecting the default provider
- `PROPQUERY_TPM2` is the string to use when selecting the tpm2 provider

Expand All @@ -56,6 +66,7 @@ propquery|action
"?provider=tpm2,tpm2.digest!=yes"|prefer the tpm2 provider but not for message digests

For more information see:

- [Provider for integration of TPM 2.0 to OpenSSL 3.x](https://github.com/tpm2-software/tpm2-openssl)
- [OpenSSL property](https://www.openssl.org/docs/man3.0/man7/property.html)
- [OpenSSL provider](https://www.openssl.org/docs/man3.0/man7/provider.html)
Expand Down
7 changes: 7 additions & 0 deletions include/evse_security/crypto/interface/crypto_types.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,8 @@
#include <chrono>
#include <memory>
#include <optional>
#include <stdexcept>
#include <string>

namespace evse_security {

Expand Down Expand Up @@ -49,6 +51,11 @@ struct CertificateSigningRequestInfo {
std::string organization;
std::string commonName;

/// @brief incude a subjectAlternativeName DNSName
std::optional<std::string> dns_name;
/// @brief incude a subjectAlternativeName IPAddress
std::optional<std::string> ip_address;

KeyGenerationInfo key_info;
};
class CertificateLoadException : public std::runtime_error {
Expand Down
12 changes: 12 additions & 0 deletions lib/evse_security/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -80,4 +80,16 @@ if(USING_TPM2)
)
endif()

if(CSR_DNS_NAME)
target_compile_definitions(evse_security PRIVATE
CSR_DNS_NAME="${CSR_DNS_NAME}"
)
endif()

if(CSR_IP_ADDRESS)
target_compile_definitions(evse_security PRIVATE
CSR_IP_ADDRESS="${CSR_IP_ADDRESS}"
)
endif()

target_compile_features(evse_security PUBLIC cxx_std_17)
21 changes: 21 additions & 0 deletions lib/evse_security/crypto/openssl/openssl_supplier.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -7,8 +7,12 @@

#include <everest/logging.hpp>

#include <algorithm>
#include <chrono>
#include <cstring>
#include <numeric>
#include <string>
#include <vector>

#include <openssl/bio.h>
#include <openssl/err.h>
Expand Down Expand Up @@ -724,9 +728,26 @@ bool OpenSSLSupplier::x509_generate_csr(const CertificateSigningRequestInfo& csr
sk_X509_EXTENSION_push(extensions, ext_key_usage);
sk_X509_EXTENSION_push(extensions, ext_basic_constraints);

std::vector<std::string> names;
if (csr_info.dns_name.has_value()) {
names.push_back({std::string("DNS:") + csr_info.dns_name.value()});
}
if (csr_info.ip_address.has_value()) {
names.push_back({std::string("IP:") + csr_info.ip_address.value()});
}

X509_EXTENSION* ext_san = nullptr;
if (!names.empty()) {
auto comma_fold = [](std::string a, const std::string& b) { return std::move(a) + ',' + b; };
std::string value = std::accumulate(std::next(names.begin()), names.end(), std::string(names[0]), comma_fold);
ext_san = X509V3_EXT_conf_nid(NULL, NULL, NID_subject_alt_name, value.c_str());
sk_X509_EXTENSION_push(extensions, ext_san);
}

const bool result = X509_REQ_add_extensions(x509_req_ptr.get(), extensions);
X509_EXTENSION_free(ext_key_usage);
X509_EXTENSION_free(ext_basic_constraints);
X509_EXTENSION_free(ext_san);
sk_X509_EXTENSION_free(extensions);
if (!result) {
EVLOG_error << "Failed to add csr extensions!";
Expand Down
11 changes: 11 additions & 0 deletions lib/evse_security/evse_security.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@
#include <algorithm>
#include <fstream>
#include <iostream>
#include <optional>
#include <set>
#include <stdio.h>

Expand Down Expand Up @@ -724,6 +725,16 @@ std::string EvseSecurity::generate_certificate_signing_request(LeafCertificateTy
info.commonName = common;
info.country = country;
info.organization = organization;
#ifdef CSR_DNS_NAME
info.dns_name = CSR_DNS_NAME;
#else
info.dns_name = std::nullopt;
#endif
#ifdef CSR_IP_ADDRESS
info.ip_address = CSR_IP_ADDRESS;
#else
info.ip_address = std::nullopt;
#endif

info.key_info.key_type = CryptoKeyType::EC_prime256v1;
info.key_info.generate_on_tpm = use_tpm;
Expand Down
77 changes: 75 additions & 2 deletions tests/openssl_supplier_test.cpp
Original file line number Diff line number Diff line change
@@ -1,10 +1,11 @@
#include <cstdlib>
#include <filesystem>
#include <fstream>
#include <gtest/gtest.h>
#include <iostream>

#include <evse_security/crypto/openssl/openssl_supplier.hpp>
#include <optional>

// #define OUTPUT_CSR

using namespace evse_security;

Expand Down Expand Up @@ -107,10 +108,82 @@ TEST_F(OpenSSLSupplierTest, x509_generate_csr) {
"UK",
"Pionix",
"0123456789",
.dns_name = std::nullopt,
.ip_address = std::nullopt,
{CryptoKeyType::EC_prime256v1, false, std::nullopt, "pki/csr_key.pem", std::nullopt}};
auto res = OpenSSLSupplier::x509_generate_csr(csr_info, csr);
ASSERT_TRUE(res);

std::ofstream out("csr.pem");
out << csr;
out.close();

ASSERT_GT(csr.size(), 0);
}

TEST_F(OpenSSLSupplierTest, x509_generate_csr_dns) {
std::string csr;
CertificateSigningRequestInfo csr_info = {
0,
"UK",
"Pionix",
"0123456789",
.dns_name = "cs.pionix.de",
.ip_address = std::nullopt,
{CryptoKeyType::EC_prime256v1, false, std::nullopt, "pki/csr_key.pem", std::nullopt}};
auto res = OpenSSLSupplier::x509_generate_csr(csr_info, csr);
ASSERT_TRUE(res);

#ifdef OUTPUT_CSR
std::ofstream out("csr_dns.pem");
out << csr;
out.close();
#endif

ASSERT_GT(csr.size(), 0);
}

TEST_F(OpenSSLSupplierTest, x509_generate_csr_ip) {
std::string csr;
CertificateSigningRequestInfo csr_info = {
0,
"UK",
"Pionix",
"0123456789",
.dns_name = std::nullopt,
.ip_address = "127.0.0.1",
{CryptoKeyType::EC_prime256v1, false, std::nullopt, "pki/csr_key.pem", std::nullopt}};
auto res = OpenSSLSupplier::x509_generate_csr(csr_info, csr);
ASSERT_TRUE(res);

#ifdef OUTPUT_CSR
std::ofstream out("csr_ip.pem");
out << csr;
out.close();
#endif

ASSERT_GT(csr.size(), 0);
}

TEST_F(OpenSSLSupplierTest, x509_generate_csr_dns_ip) {
std::string csr;
CertificateSigningRequestInfo csr_info = {
0,
"UK",
"Pionix",
"0123456789",
.dns_name = "cs.pionix.de",
.ip_address = "127.0.0.1",
{CryptoKeyType::EC_prime256v1, false, std::nullopt, "pki/csr_key.pem", std::nullopt}};
auto res = OpenSSLSupplier::x509_generate_csr(csr_info, csr);
ASSERT_TRUE(res);

#ifdef OUTPUT_CSR
std::ofstream out("csr_dns_ip.pem");
out << csr;
out.close();
#endif

ASSERT_GT(csr.size(), 0);
}

Expand Down
2 changes: 2 additions & 0 deletions tests/openssl_supplier_test_tpm.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -109,6 +109,8 @@ TEST_F(OpenSSLSupplierTpmTest, x509_generate_csr) {
"UK",
"Pionix",
"0123456789",
.dns_name = std::nullopt,
.ip_address = std::nullopt,
{CryptoKeyType::EC_prime256v1, true, std::nullopt, "tpm_pki/csr_key.pem", std::nullopt}};

// std::cout << "tpm2 pre: " << OSSL_PROVIDER_available(nullptr, "tpm2") << std::endl;
Expand Down
12 changes: 8 additions & 4 deletions tests/tests.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@

#include <fstream>
#include <gtest/gtest.h>
#include <openssl/crypto.h>
#include <regex>
#include <sstream>
#include <string>
Expand Down Expand Up @@ -70,19 +71,22 @@ bool equal_certificate_strings(const std::string& cert1, const std::string& cert
#if USING_OPENSSL_3
bool supports_tpm_usage() {
bool supports_tpm = false;
auto libctx = OSSL_LIB_CTX_new();

OSSL_PROVIDER* tpm2_provider = OSSL_PROVIDER_load(nullptr, evse_security::PROVIDER_TPM);
OSSL_PROVIDER* tpm2_provider = OSSL_PROVIDER_load(libctx, evse_security::PROVIDER_TPM);

if (tpm2_provider != nullptr) {
supports_tpm =
OSSL_PROVIDER_available(nullptr, evse_security::PROVIDER_TPM) && OSSL_PROVIDER_self_test(tpm2_provider);
OSSL_PROVIDER_available(libctx, evse_security::PROVIDER_TPM) && OSSL_PROVIDER_self_test(tpm2_provider);
OSSL_PROVIDER_unload(tpm2_provider);
} else {
supports_tpm = false;
}

// Load default again
OSSL_PROVIDER_load(nullptr, evse_security::PROVIDER_DEFAULT);
// Load default again (removed - not needed and causes a memory leak)
// OSSL_PROVIDER_load(nullptr, evse_security::PROVIDER_DEFAULT);

OSSL_LIB_CTX_free(libctx);

std::cout << "Supports TPM usage: " << supports_tpm << std::endl;
return supports_tpm;
Expand Down
Loading