Skip to content

Commit

Permalink
Build time configuration for CSR (#50)
Browse files Browse the repository at this point in the history
* feat: optional additions to a CSR

subject alternative name can be added with DNS name and/or IPv4 address

Signed-off-by: James Chapman <[email protected]>

* fix: QA issue

Signed-off-by: James Chapman <[email protected]>

---------

Signed-off-by: James Chapman <[email protected]>
  • Loading branch information
james-ctc authored Feb 21, 2024
1 parent e4a347b commit 34a7683
Show file tree
Hide file tree
Showing 8 changed files with 147 additions and 6 deletions.
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

0 comments on commit 34a7683

Please sign in to comment.