Skip to content

Commit

Permalink
Split OCSP data requests into two separate functions (#61)
Browse files Browse the repository at this point in the history
* now two functions to get OCSP request data exist: get_v2g_ocsp_request_data and get_mo_ocsp_request_data
* implemented both functions according to requirements of Plug&Charge
* added LeafCertificate::MO to conversions
* bump version to 0.5
---------

Signed-off-by: pietfried <[email protected]>
Signed-off-by: AssemblyJohn <[email protected]>
Signed-off-by: John <[email protected]>
Co-authored-by: AssemblyJohn <[email protected]>
Co-authored-by: John <[email protected]>
  • Loading branch information
3 people authored Mar 20, 2024
1 parent 5cd5f82 commit bce1ba4
Show file tree
Hide file tree
Showing 4 changed files with 73 additions and 51 deletions.
2 changes: 1 addition & 1 deletion CMakeLists.txt
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
cmake_minimum_required(VERSION 3.14)

project(everest-evse_security VERSION 0.4.3
project(everest-evse_security VERSION 0.5
DESCRIPTION "Implementation of EVSE related security operations"
LANGUAGES CXX C
)
Expand Down
9 changes: 4 additions & 5 deletions include/evse_security/evse_security.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -114,16 +114,15 @@ class EvseSecurity {
/// @brief Retrieves the certificate count applying the \p certificate_types filter.
int get_count_of_installed_certificates(const std::vector<CertificateType>& certificate_types);

/// @brief Retrieves the OCSP request data of the V2G certificates
/// @brief Command to retrieve the OCSP request data of the V2G certificates (SubCAs and possibly V2G leaf)
/// @return contains OCSP request data
OCSPRequestDataList get_ocsp_request_data();
OCSPRequestDataList get_v2g_ocsp_request_data();

/// @brief Retrieves the OCSP request data of the given \p certificate_chain
/// @brief Retrieves the OCSP request data of the given contract \p certificate_chain
/// @param certificate_chain PEM formatted certificate or certificate chain
/// @param certificate_type type of the leaf certificate
/// @return contains OCSP request data
OCSPRequestDataList get_ocsp_request_data(const std::string& certificate_chain,
const CaCertificateType certificate_type);
OCSPRequestDataList get_mo_ocsp_request_data(const std::string& certificate_chain);

/// @brief Updates the OCSP cache for the given \p certificate_hash_data with the given \p ocsp_response
/// @param certificate_hash_data identifies the certificate for which the \p ocsp_response is specified
Expand Down
109 changes: 64 additions & 45 deletions lib/evse_security/evse_security.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -160,9 +160,8 @@ static std::set<fs::path> get_certificate_path_of_key(const fs::path& key, const
throw NoCertificateValidException(error);
}

X509CertificateBundle get_leaf_certificates(const fs::path& cert_dir) {
return X509CertificateBundle(cert_dir, EncodingFormat::PEM);
}
// Declared here to avoid requirement of X509Wrapper include in header
static OCSPRequestDataList get_ocsp_request_data_internal(fs::path& root_path, std::vector<X509Wrapper>& leaf_chain);

std::mutex EvseSecurity::security_mutex;

Expand Down Expand Up @@ -621,69 +620,89 @@ int EvseSecurity::get_count_of_installed_certificates(const std::vector<Certific
return count;
}

OCSPRequestDataList EvseSecurity::get_ocsp_request_data() {
OCSPRequestDataList EvseSecurity::get_v2g_ocsp_request_data() {
std::lock_guard<std::mutex> guard(EvseSecurity::security_mutex);

OCSPRequestDataList response;
std::vector<OCSPRequestData> ocsp_request_data_list;

try {
X509CertificateBundle ca_bundle(this->ca_bundle_path_map.at(CaCertificateType::V2G), EncodingFormat::PEM);

// Build hierarchy for the bundle
auto& hierarchy = ca_bundle.get_certficate_hierarchy();

// Iterate cache, get hashes
hierarchy.for_each([&](const X509Node& node) {
std::string responder_url = node.certificate.get_responder_url();
if (!responder_url.empty()) {
auto certificate_hash_data = node.hash;
OCSPRequestData ocsp_request_data = {certificate_hash_data, responder_url};
ocsp_request_data_list.push_back(ocsp_request_data);
}
const auto secc_key_pair = this->get_key_pair_internal(LeafCertificateType::V2G, EncodingFormat::PEM);

return true;
});
if (secc_key_pair.status != GetKeyPairStatus::Accepted or !secc_key_pair.pair.has_value()) {
EVLOG_error << "Could not get key pair, for v2g ocsp request!";
return OCSPRequestDataList();
}

response.ocsp_request_data_list = ocsp_request_data_list;
std::vector<X509Wrapper> chain =
std::move(X509CertificateBundle(secc_key_pair.pair.value().certificate, EncodingFormat::PEM).split());
return get_ocsp_request_data_internal(this->ca_bundle_path_map.at(CaCertificateType::V2G), chain);
} catch (const CertificateLoadException& e) {
EVLOG_error << "Could not get ocsp cache, certificate load failure: " << e.what();
EVLOG_error << "Could not get v2g ocsp cache, certificate load failure: " << e.what();
}

return response;
return OCSPRequestDataList();
}

OCSPRequestDataList EvseSecurity::get_ocsp_request_data(const std::string& certificate_chain,
const CaCertificateType certificate_type) {
OCSPRequestDataList EvseSecurity::get_mo_ocsp_request_data(const std::string& certificate_chain) {
std::lock_guard<std::mutex> guard(EvseSecurity::security_mutex);

try {
std::vector<X509Wrapper> chain =
std::move(X509CertificateBundle(certificate_chain, EncodingFormat::PEM).split());

// Find the MO root
return get_ocsp_request_data_internal(this->ca_bundle_path_map.at(CaCertificateType::MO), chain);
} catch (const CertificateLoadException& e) {
EVLOG_error << "Could not get mo ocsp cache, certificate load failure: " << e.what();
}

return OCSPRequestDataList();
}

OCSPRequestDataList get_ocsp_request_data_internal(fs::path& root_path, std::vector<X509Wrapper>& leaf_chain) {
OCSPRequestDataList response;
std::vector<OCSPRequestData> ocsp_request_data_list;

try {
X509CertificateBundle leaf_bundle(certificate_chain, EncodingFormat::PEM);
X509CertificateBundle root_bundle(this->ca_bundle_path_map.at(certificate_type), EncodingFormat::PEM);
std::vector<X509Wrapper> full_hierarchy = X509CertificateBundle(root_path, EncodingFormat::PEM).split();
std::move(std::begin(leaf_chain), std::end(leaf_chain), std::back_inserter(full_hierarchy));

auto full_list = root_bundle.split();
const auto leaf_certificates = leaf_bundle.split();
for (const auto& certif : leaf_certificates) {
full_list.push_back(std::move(certif));
}
X509CertificateHierarchy full_hierarchy = X509CertificateHierarchy::build_hierarchy(full_list);

for (const auto& certificate : leaf_certificates) {
std::string responder_url = certificate.get_responder_url();
if (!responder_url.empty()) {
auto certificate_hash_data = full_hierarchy.get_certificate_hash(certificate);
OCSPRequestData ocsp_request_data = {certificate_hash_data, responder_url};
ocsp_request_data_list.push_back(ocsp_request_data);
// Build the full hierarchy
auto hierarchy = X509CertificateHierarchy::build_hierarchy(full_hierarchy);

// Search for the first valid root, and collect all the chain
for (auto& root : hierarchy.get_hierarchy()) {
if (root.certificate.is_selfsigned() && root.certificate.is_valid()) {
// Collect the chain
std::vector<X509Wrapper> descendants = hierarchy.collect_descendants(root.certificate);
bool has_proper_descendants = (descendants.size() > 0);

for (auto& certificate : descendants) {
std::string responder_url = certificate.get_responder_url();

if (!responder_url.empty()) {
try {
auto certificate_hash_data = hierarchy.get_certificate_hash(certificate);
OCSPRequestData ocsp_request_data = {certificate_hash_data, responder_url};
ocsp_request_data_list.push_back(ocsp_request_data);
} catch (const NoCertificateFound& e) {
EVLOG_error << "Could not find hash for certificate: " << certificate.get_common_name()
<< " with error: " << e.what();
}
}
}

// If we have collected the descendants we can break
// else we can continue iterating for a proper root
if (has_proper_descendants) {
break;
}
}
}

response.ocsp_request_data_list = ocsp_request_data_list;
} catch (const CertificateLoadException& e) {
EVLOG_error << "Could not get ocsp cache, certificate load failure: " << e.what()
<< " for chain type: " << conversions::ca_certificate_type_to_string(certificate_type);
EVLOG_error << "Could not get ocsp cache, certificate load failure: " << e.what();
} catch (const NoCertificateFound& e) {
EVLOG_error << "Could not find proper root: " << e.what();
}

return response;
Expand Down Expand Up @@ -846,7 +865,7 @@ GetKeyPairResult EvseSecurity::get_key_pair_internal(LeafCertificateType certifi

// choose appropriate cert (valid_from / valid_to)
try {
auto leaf_certificates = std::move(get_leaf_certificates(cert_dir));
auto leaf_certificates = X509CertificateBundle(cert_dir, EncodingFormat::PEM);

if (leaf_certificates.empty()) {
EVLOG_warning << "Could not find any key pair";
Expand Down
4 changes: 4 additions & 0 deletions lib/evse_security/evse_types.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,8 @@ std::string leaf_certificate_type_to_string(LeafCertificateType e) {
return "V2G";
case LeafCertificateType::MF:
return "MF";
case LeafCertificateType::MO:
return "MO";
default:
throw std::out_of_range("Could not convert LeafCertificateType to string");
}
Expand All @@ -54,6 +56,8 @@ std::string leaf_certificate_type_to_filename(LeafCertificateType e) {
return "SECC_LEAF_";
case LeafCertificateType::MF:
return "MF_LEAF_";
case LeafCertificateType::MO:
return "MO_LEAF_";
default:
throw std::out_of_range("Could not convert LeafCertificateType to string");
}
Expand Down

0 comments on commit bce1ba4

Please sign in to comment.