From 56430c463ce7f7d4e2bf1ea2ab2a7e7b74f7e872 Mon Sep 17 00:00:00 2001 From: AssemblyJohn Date: Mon, 29 Apr 2024 16:03:43 +0300 Subject: [PATCH 01/12] Updated interface usage Signed-off-by: AssemblyJohn --- include/ocpp/common/evse_security.hpp | 6 ++- include/ocpp/common/evse_security_impl.hpp | 9 ++-- include/ocpp/common/types.hpp | 19 ++++--- lib/ocpp/common/evse_security_impl.cpp | 49 +++++++++++++++---- .../websocket/websocket_libwebsockets.cpp | 18 ++++--- lib/ocpp/common/websocket/websocket_plain.cpp | 2 +- lib/ocpp/common/websocket/websocket_tls.cpp | 25 +++++----- lib/ocpp/v16/charge_point_impl.cpp | 3 +- lib/ocpp/v201/charge_point.cpp | 3 +- lib/ocpp/v201/device_model.cpp | 2 +- 10 files changed, 93 insertions(+), 43 deletions(-) diff --git a/include/ocpp/common/evse_security.hpp b/include/ocpp/common/evse_security.hpp index f0e8d6af2..eca5278be 100644 --- a/include/ocpp/common/evse_security.hpp +++ b/include/ocpp/common/evse_security.hpp @@ -97,8 +97,10 @@ class EvseSecurity { /// certificate, this function returns std::nullopt /// \param certificate_type type of the leaf certificate /// \param encoding specifies PEM or DER format - /// \return key pair of certificate and key if present, else std::nullopt - virtual std::optional get_key_pair(const CertificateSigningUseEnum& certificate_type) = 0; + /// \param include_ocsp if we should include certificate ocsp data + /// \return info of certificate and key if present, else std::nullopt + virtual std::optional get_leaf_certificate_info(const CertificateSigningUseEnum& certificate_type, + bool include_ocsp = false) = 0; /// \brief Updates the certificate and key links for the given \p certificate_type virtual bool update_certificate_links(const CertificateSigningUseEnum& certificate_type) = 0; diff --git a/include/ocpp/common/evse_security_impl.hpp b/include/ocpp/common/evse_security_impl.hpp index c6d4ce279..efc90f6cf 100644 --- a/include/ocpp/common/evse_security_impl.hpp +++ b/include/ocpp/common/evse_security_impl.hpp @@ -51,7 +51,8 @@ class EvseSecurityImpl : public EvseSecurity { std::string generate_certificate_signing_request(const CertificateSigningUseEnum& certificate_type, const std::string& country, const std::string& organization, const std::string& common, bool use_tpm) override; - std::optional get_key_pair(const CertificateSigningUseEnum& certificate_type) override; + std::optional get_leaf_certificate_info(const CertificateSigningUseEnum& certificate_type, + bool include_ocsp = false) override; bool update_certificate_links(const CertificateSigningUseEnum& certificate_type) override; std::string get_verify_file(const CaCertificateType& certificate_type) override; int get_leaf_expiry_days_count(const CertificateSigningUseEnum& certificate_type) override; @@ -70,7 +71,8 @@ DeleteCertificateResult to_ocpp(evse_security::DeleteCertificateResult other); CertificateHashDataType to_ocpp(evse_security::CertificateHashData other); CertificateHashDataChain to_ocpp(evse_security::CertificateHashDataChain other); OCSPRequestData to_ocpp(evse_security::OCSPRequestData other); -KeyPair to_ocpp(evse_security::KeyPair other); +CertificateOCSP to_ocpp(evse_security::CertificateOCSP other); +CertificateInfo to_ocpp(evse_security::CertificateInfo other); evse_security::CaCertificateType from_ocpp(CaCertificateType other); evse_security::LeafCertificateType from_ocpp(LeafCertificateType other); @@ -83,7 +85,8 @@ evse_security::DeleteCertificateResult from_ocpp(DeleteCertificateResult other); evse_security::CertificateHashData from_ocpp(CertificateHashDataType other); evse_security::CertificateHashDataChain from_ocpp(CertificateHashDataChain other); evse_security::OCSPRequestData from_ocpp(OCSPRequestData other); -evse_security::KeyPair from_ocpp(KeyPair other); +evse_security::CertificateOCSP from_ocpp(CertificateOCSP other); +evse_security::CertificateInfo from_ocpp(CertificateInfo other); }; // namespace conversions diff --git a/include/ocpp/common/types.hpp b/include/ocpp/common/types.hpp index 87a447e58..60148b285 100644 --- a/include/ocpp/common/types.hpp +++ b/include/ocpp/common/types.hpp @@ -531,11 +531,18 @@ struct OCSPRequestData { std::string responderUrl; }; -struct KeyPair { - fs::path certificate_path; // path to the full certificate chain - fs::path certificate_single_path; // path to the single leaf certificate - fs::path key_path; // path to private key of the leaf certificate - std::optional password; // optional password for the private key +struct CertificateOCSP { + CertificateHashDataType hash; + std::optional ocsp_path; +}; + +struct CertificateInfo { + std::optional certificate_path; // path to the full certificate chain + std::optional certificate_single_path; // path to the single leaf certificate + int certificate_count; // count of certs in the chain + fs::path key_path; // path to private key of the leaf certificate + std::optional password; // optional password for the private key + std::vector ocsp; // OCSP data if requested }; enum class LeafCertificateType { @@ -618,7 +625,7 @@ inline const std::string RESET_OR_REBOOT = "ResetOrReboot"; // CRI inline const std::string STARTUP_OF_THE_DEVICE = "StartupOfTheDevice"; // CRITICAL inline const std::string SECURITYLOGWASCLEARED = "SecurityLogWasCleared"; // CRITICAL inline const std::string RECONFIGURATIONOFSECURITYPARAMETERS = "ReconfigurationOfSecurityParameters"; -inline const std::string MEMORYEXHAUSTION = "MemoryExhaustion"; // CRITICAL +inline const std::string MEMORYEXHAUSTION = "MemoryExhaustion"; // CRITICAL inline const std::string INVALIDMESSAGES = "InvalidMessages"; inline const std::string ATTEMPTEDREPLAYATTACKS = "AttemptedReplayAttacks"; inline const std::string TAMPERDETECTIONACTIVATED = "TamperDetectionActivated"; // CRITICAL diff --git a/lib/ocpp/common/evse_security_impl.cpp b/lib/ocpp/common/evse_security_impl.cpp index 7815c960d..a31c69850 100644 --- a/lib/ocpp/common/evse_security_impl.cpp +++ b/lib/ocpp/common/evse_security_impl.cpp @@ -104,12 +104,13 @@ std::string EvseSecurityImpl::generate_certificate_signing_request(const Certifi organization, common, use_tpm); } -std::optional EvseSecurityImpl::get_key_pair(const CertificateSigningUseEnum& certificate_type) { - const auto key_pair = - this->evse_security->get_key_pair(conversions::from_ocpp(certificate_type), evse_security::EncodingFormat::PEM); +std::optional +EvseSecurityImpl::get_leaf_certificate_info(const CertificateSigningUseEnum& certificate_type, bool include_ocsp) { + const auto info_response = this->evse_security->get_leaf_certificate_info( + conversions::from_ocpp(certificate_type), evse_security::EncodingFormat::PEM, include_ocsp); - if (key_pair.status == evse_security::GetKeyPairStatus::Accepted && key_pair.pair.has_value()) { - return conversions::to_ocpp(key_pair.pair.value()); + if (info_response.status == evse_security::GetCertificateInfoStatus::Accepted && info_response.info.has_value()) { + return conversions::to_ocpp(info_response.info.value()); } else { return std::nullopt; } @@ -284,12 +285,27 @@ OCSPRequestData to_ocpp(evse_security::OCSPRequestData other) { return lhs; } -KeyPair to_ocpp(evse_security::KeyPair other) { - KeyPair lhs; +CertificateOCSP to_ocpp(evse_security::CertificateOCSP other) { + CertificateOCSP lhs; + lhs.hash = to_ocpp(other.hash); + lhs.ocsp_path = other.ocsp_path; + return lhs; +} + +CertificateInfo to_ocpp(evse_security::CertificateInfo other) { + CertificateInfo lhs; lhs.certificate_path = other.certificate; lhs.certificate_single_path = other.certificate_single; + lhs.certificate_count = other.certificate_count; lhs.key_path = other.key; lhs.password = other.password; + + if (other.ocsp.empty() == false) { + for (auto& ocsp_data : other.ocsp) { + lhs.ocsp.push_back(to_ocpp(ocsp_data)); + } + } + return lhs; } @@ -440,12 +456,27 @@ evse_security::OCSPRequestData from_ocpp(OCSPRequestData other) { return lhs; } -evse_security::KeyPair from_ocpp(KeyPair other) { - evse_security::KeyPair lhs; +evse_security::CertificateOCSP from_ocpp(CertificateOCSP other) { + evse_security::CertificateOCSP lhs; + lhs.hash = from_ocpp(other.hash); + lhs.ocsp_path = other.ocsp_path; + return lhs; +} + +evse_security::CertificateInfo from_ocpp(CertificateInfo other) { + evse_security::CertificateInfo lhs; lhs.certificate = other.certificate_path; lhs.certificate_single = other.certificate_single_path; + lhs.certificate_count = other.certificate_count; lhs.key = other.key_path; lhs.password = other.password; + + if (other.ocsp.empty() == false) { + for (auto& ocsp_data : other.ocsp) { + lhs.ocsp.push_back(from_ocpp(ocsp_data)); + } + } + return lhs; } diff --git a/lib/ocpp/common/websocket/websocket_libwebsockets.cpp b/lib/ocpp/common/websocket/websocket_libwebsockets.cpp index 66cc89575..73e022d03 100644 --- a/lib/ocpp/common/websocket/websocket_libwebsockets.cpp +++ b/lib/ocpp/common/websocket/websocket_libwebsockets.cpp @@ -449,20 +449,22 @@ void WebsocketTlsTPM::client_loop() { if (this->connection_options.security_profile == 3) { - const auto certificate_key_pair = - this->evse_security->get_key_pair(CertificateSigningUseEnum::ChargingStationCertificate); + const auto certificate_info = + this->evse_security->get_leaf_certificate_info(CertificateSigningUseEnum::ChargingStationCertificate); - if (!certificate_key_pair.has_value()) { + if (!certificate_info.has_value()) { EVLOG_AND_THROW(std::runtime_error( "Connecting with security profile 3 but no client side certificate is present or valid")); } - path_chain = certificate_key_pair.value().certificate_path; - if (path_chain.empty()) { - path_chain = certificate_key_pair.value().certificate_single_path; + if (certificate_info.value().certificate_path.has_value()) { + path_chain = certificate_info.value().certificate_path.value(); + } else { + path_chain = certificate_info.value().certificate_single_path.value(); } - path_key = certificate_key_pair.value().key_path; - password = certificate_key_pair.value().password; + + path_key = certificate_info.value().key_path; + password = certificate_info.value().password; } SSL_CTX* ssl_ctx = nullptr; diff --git a/lib/ocpp/common/websocket/websocket_plain.cpp b/lib/ocpp/common/websocket/websocket_plain.cpp index 1709102f6..973e4015c 100644 --- a/lib/ocpp/common/websocket/websocket_plain.cpp +++ b/lib/ocpp/common/websocket/websocket_plain.cpp @@ -214,7 +214,7 @@ void WebsocketPlain::connect_plain() { void WebsocketPlain::on_open_plain(client* c, websocketpp::connection_hdl hdl) { std::lock_guard lk(this->connection_mutex); - (void)c; // client is not used in this function + (void)c; // client is not used in this function EVLOG_info << "OCPP client successfully connected to plain websocket server"; this->connection_attempts = 1; // reset connection attempts this->m_is_connected = true; diff --git a/lib/ocpp/common/websocket/websocket_tls.cpp b/lib/ocpp/common/websocket/websocket_tls.cpp index 4f95eed54..d3d25b036 100644 --- a/lib/ocpp/common/websocket/websocket_tls.cpp +++ b/lib/ocpp/common/websocket/websocket_tls.cpp @@ -171,33 +171,36 @@ tls_context WebsocketTLS::on_tls_init(std::string hostname, websocketpp::connect } if (security_profile == 3) { - const auto certificate_key_pair = - this->evse_security->get_key_pair(CertificateSigningUseEnum::ChargingStationCertificate); + const auto certificate_info = + this->evse_security->get_leaf_certificate_info(CertificateSigningUseEnum::ChargingStationCertificate); - if (certificate_key_pair.has_value() && certificate_key_pair.value().password.has_value()) { - std::string passwd = certificate_key_pair.value().password.value(); + if (certificate_info.has_value() && certificate_info.value().password.has_value()) { + std::string passwd = certificate_info.value().password.value(); context->set_password_callback( [passwd](auto max_len, auto purpose) { return passwd.substr(0, max_len); }); } - if (!certificate_key_pair.has_value()) { + if (!certificate_info.has_value()) { EVLOG_AND_THROW(std::runtime_error( "Connecting with security profile 3 but no client side certificate is present or valid")); } // certificate_path contains the chain if not empty. Use certificate chain if available, else use // certificate_single_path - auto certificate_path = certificate_key_pair.value().certificate_path; - if (certificate_path.empty()) { - certificate_path = certificate_key_pair.value().certificate_single_path; + fs::path certificate_path; + + if (certificate_info.value().certificate_path.has_value()) { + certificate_path = certificate_info.value().certificate_path.value(); + } else { + certificate_path = certificate_info.value().certificate_single_path.value(); } EVLOG_info << "Using certificate: " << certificate_path; if (SSL_CTX_use_certificate_chain_file(context->native_handle(), certificate_path.c_str()) != 1) { EVLOG_AND_THROW(std::runtime_error("Could not use client certificate file within SSL context")); } - EVLOG_info << "Using key file: " << certificate_key_pair.value().key_path; - if (SSL_CTX_use_PrivateKey_file(context->native_handle(), certificate_key_pair.value().key_path.c_str(), + EVLOG_info << "Using key file: " << certificate_info.value().key_path; + if (SSL_CTX_use_PrivateKey_file(context->native_handle(), certificate_info.value().key_path.c_str(), SSL_FILETYPE_PEM) != 1) { EVLOG_AND_THROW(std::runtime_error("Could not set private key file within SSL context")); } @@ -304,7 +307,7 @@ void WebsocketTLS::connect_tls() { this->wss_client.connect(con); } void WebsocketTLS::on_open_tls(tls_client* c, websocketpp::connection_hdl hdl) { - (void)c; // tls_client is not used in this function + (void)c; // tls_client is not used in this function EVLOG_info << "OCPP client successfully connected to TLS websocket server"; this->connection_attempts = 1; // reset connection attempts this->m_is_connected = true; diff --git a/lib/ocpp/v16/charge_point_impl.cpp b/lib/ocpp/v16/charge_point_impl.cpp index aae18d5d5..f0588893a 100644 --- a/lib/ocpp/v16/charge_point_impl.cpp +++ b/lib/ocpp/v16/charge_point_impl.cpp @@ -1427,7 +1427,8 @@ void ChargePointImpl::handleChangeConfigurationRequest(ocpp::Callevse_security - ->get_key_pair(ocpp::CertificateSigningUseEnum::ChargingStationCertificate) + ->get_leaf_certificate_info( + ocpp::CertificateSigningUseEnum::ChargingStationCertificate) .has_value()) { EVLOG_warning << "New security level set to 3 but no Client Certificate is installed"; response.status = ConfigurationStatus::Rejected; diff --git a/lib/ocpp/v201/charge_point.cpp b/lib/ocpp/v201/charge_point.cpp index 928ba27d0..0180e78b2 100644 --- a/lib/ocpp/v201/charge_point.cpp +++ b/lib/ocpp/v201/charge_point.cpp @@ -1623,7 +1623,8 @@ bool ChargePoint::validate_set_variable(const SetVariableData& set_variable_data } if (network_profile.securityProfile == 3 and - !this->evse_security->get_key_pair(ocpp::CertificateSigningUseEnum::ChargingStationCertificate) + !this->evse_security + ->get_leaf_certificate_info(ocpp::CertificateSigningUseEnum::ChargingStationCertificate) .has_value()) { EVLOG_warning << "SecurityProfile of configurationSlot: " << configuration_slot << " is 3 but no CSMS Leaf Certificate is installed"; diff --git a/lib/ocpp/v201/device_model.cpp b/lib/ocpp/v201/device_model.cpp index c05eded22..e5846131a 100644 --- a/lib/ocpp/v201/device_model.cpp +++ b/lib/ocpp/v201/device_model.cpp @@ -58,7 +58,7 @@ bool DeviceModel::component_variables_match(const std::vector component_variables.begin(), component_variables.end(), [component, variable](ComponentVariable v) { return (component == v.component and !v.variable.has_value()) or // if component has no variable (component == v.component and v.variable.has_value() and - variable == v.variable.value()) or // if component has variables + variable == v.variable.value()) or // if component has variables (component == v.component and v.variable.has_value() and !v.variable.value().instance.has_value() and variable.name == v.variable.value().name) or // if component has no variable instances From 12776c26164ddc127e79b7ef217fbea85244147e Mon Sep 17 00:00:00 2001 From: AssemblyJohn Date: Mon, 29 Apr 2024 16:58:22 +0300 Subject: [PATCH 02/12] Updated dependencies for new interface Signed-off-by: AssemblyJohn --- dependencies.yaml | 2 +- include/ocpp/common/types.hpp | 2 +- lib/ocpp/common/websocket/websocket_plain.cpp | 2 +- lib/ocpp/common/websocket/websocket_tls.cpp | 2 +- lib/ocpp/v201/device_model.cpp | 2 +- 5 files changed, 5 insertions(+), 5 deletions(-) diff --git a/dependencies.yaml b/dependencies.yaml index 79487392f..c08689352 100644 --- a/dependencies.yaml +++ b/dependencies.yaml @@ -31,7 +31,7 @@ websocketpp: cmake_condition: "LIBOCPP_ENABLE_DEPRECATED_WEBSOCKETPP" libevse-security: git: https://github.com/EVerest/libevse-security.git - git_tag: v0.6.0 + git_tag: 57ba46d libwebsockets: git: https://github.com/warmcat/libwebsockets.git git_tag: v4.3.3 diff --git a/include/ocpp/common/types.hpp b/include/ocpp/common/types.hpp index 60148b285..14aa41ac9 100644 --- a/include/ocpp/common/types.hpp +++ b/include/ocpp/common/types.hpp @@ -625,7 +625,7 @@ inline const std::string RESET_OR_REBOOT = "ResetOrReboot"; // CRI inline const std::string STARTUP_OF_THE_DEVICE = "StartupOfTheDevice"; // CRITICAL inline const std::string SECURITYLOGWASCLEARED = "SecurityLogWasCleared"; // CRITICAL inline const std::string RECONFIGURATIONOFSECURITYPARAMETERS = "ReconfigurationOfSecurityParameters"; -inline const std::string MEMORYEXHAUSTION = "MemoryExhaustion"; // CRITICAL +inline const std::string MEMORYEXHAUSTION = "MemoryExhaustion"; // CRITICAL inline const std::string INVALIDMESSAGES = "InvalidMessages"; inline const std::string ATTEMPTEDREPLAYATTACKS = "AttemptedReplayAttacks"; inline const std::string TAMPERDETECTIONACTIVATED = "TamperDetectionActivated"; // CRITICAL diff --git a/lib/ocpp/common/websocket/websocket_plain.cpp b/lib/ocpp/common/websocket/websocket_plain.cpp index 973e4015c..1709102f6 100644 --- a/lib/ocpp/common/websocket/websocket_plain.cpp +++ b/lib/ocpp/common/websocket/websocket_plain.cpp @@ -214,7 +214,7 @@ void WebsocketPlain::connect_plain() { void WebsocketPlain::on_open_plain(client* c, websocketpp::connection_hdl hdl) { std::lock_guard lk(this->connection_mutex); - (void)c; // client is not used in this function + (void)c; // client is not used in this function EVLOG_info << "OCPP client successfully connected to plain websocket server"; this->connection_attempts = 1; // reset connection attempts this->m_is_connected = true; diff --git a/lib/ocpp/common/websocket/websocket_tls.cpp b/lib/ocpp/common/websocket/websocket_tls.cpp index d3d25b036..cbe96c23e 100644 --- a/lib/ocpp/common/websocket/websocket_tls.cpp +++ b/lib/ocpp/common/websocket/websocket_tls.cpp @@ -307,7 +307,7 @@ void WebsocketTLS::connect_tls() { this->wss_client.connect(con); } void WebsocketTLS::on_open_tls(tls_client* c, websocketpp::connection_hdl hdl) { - (void)c; // tls_client is not used in this function + (void)c; // tls_client is not used in this function EVLOG_info << "OCPP client successfully connected to TLS websocket server"; this->connection_attempts = 1; // reset connection attempts this->m_is_connected = true; diff --git a/lib/ocpp/v201/device_model.cpp b/lib/ocpp/v201/device_model.cpp index e5846131a..c05eded22 100644 --- a/lib/ocpp/v201/device_model.cpp +++ b/lib/ocpp/v201/device_model.cpp @@ -58,7 +58,7 @@ bool DeviceModel::component_variables_match(const std::vector component_variables.begin(), component_variables.end(), [component, variable](ComponentVariable v) { return (component == v.component and !v.variable.has_value()) or // if component has no variable (component == v.component and v.variable.has_value() and - variable == v.variable.value()) or // if component has variables + variable == v.variable.value()) or // if component has variables (component == v.component and v.variable.has_value() and !v.variable.value().instance.has_value() and variable.name == v.variable.value().name) or // if component has no variable instances From 1cbbc4616fb4f05e6f4db2b882f2a549e63db2a9 Mon Sep 17 00:00:00 2001 From: AssemblyJohn Date: Tue, 30 Apr 2024 15:25:13 +0300 Subject: [PATCH 03/12] Fixed mocks Signed-off-by: AssemblyJohn --- tests/evse_security_mock.hpp | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/tests/evse_security_mock.hpp b/tests/evse_security_mock.hpp index 1fe2ec3e0..de396378d 100644 --- a/tests/evse_security_mock.hpp +++ b/tests/evse_security_mock.hpp @@ -29,7 +29,8 @@ class EvseSecurityMock : public EvseSecurity { MOCK_METHOD(std::string, generate_certificate_signing_request, (const CertificateSigningUseEnum&, const std::string&, const std::string&, const std::string&, bool), (override)); - MOCK_METHOD(std::optional, get_key_pair, (const CertificateSigningUseEnum&), (override)); + MOCK_METHOD(std::optional, get_leaf_certificate_info, (const CertificateSigningUseEnum&, bool), + (override)); MOCK_METHOD(bool, update_certificate_links, (const CertificateSigningUseEnum&), (override)); MOCK_METHOD(std::string, get_verify_file, (const CaCertificateType&), (override)); MOCK_METHOD(int, get_leaf_expiry_days_count, (const CertificateSigningUseEnum&), (override)); From 808685663af82fd14d2681883276e0bfd33f9cfa Mon Sep 17 00:00:00 2001 From: AssemblyJohn Date: Tue, 30 Apr 2024 15:27:59 +0300 Subject: [PATCH 04/12] Updated deps Signed-off-by: AssemblyJohn --- dependencies.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/dependencies.yaml b/dependencies.yaml index c08689352..fbb4cd427 100644 --- a/dependencies.yaml +++ b/dependencies.yaml @@ -31,7 +31,7 @@ websocketpp: cmake_condition: "LIBOCPP_ENABLE_DEPRECATED_WEBSOCKETPP" libevse-security: git: https://github.com/EVerest/libevse-security.git - git_tag: 57ba46d + git_tag: d6e71cf libwebsockets: git: https://github.com/warmcat/libwebsockets.git git_tag: v4.3.3 From 012868cab237eca4cd9bcf508c7104b6ea324811 Mon Sep 17 00:00:00 2001 From: AssemblyJohn Date: Fri, 3 May 2024 11:59:57 +0300 Subject: [PATCH 05/12] Updated native interface usage Signed-off-by: AssemblyJohn --- include/ocpp/common/evse_security.hpp | 7 +++---- include/ocpp/common/evse_security_impl.hpp | 7 ++++--- lib/ocpp/common/evse_security_impl.cpp | 19 +++++++++++++------ lib/ocpp/v16/charge_point_impl.cpp | 17 +++++++++++++++-- lib/ocpp/v201/charge_point.cpp | 9 ++++++++- 5 files changed, 43 insertions(+), 16 deletions(-) diff --git a/include/ocpp/common/evse_security.hpp b/include/ocpp/common/evse_security.hpp index eca5278be..576151578 100644 --- a/include/ocpp/common/evse_security.hpp +++ b/include/ocpp/common/evse_security.hpp @@ -87,10 +87,9 @@ class EvseSecurity { /// \param organization /// \param common /// \return the PEM formatted certificate signing request - virtual std::string generate_certificate_signing_request(const CertificateSigningUseEnum& certificate_type, - const std::string& country, - const std::string& organization, const std::string& common, - bool use_tpm) = 0; + virtual std::optional + generate_certificate_signing_request(const CertificateSigningUseEnum& certificate_type, const std::string& country, + const std::string& organization, const std::string& common, bool use_tpm) = 0; /// \brief Searches the leaf certificate for the given \p certificate_type and retrieves the most recent certificate /// that is already valid and the respective key . If no certificate is present or no key is matching the diff --git a/include/ocpp/common/evse_security_impl.hpp b/include/ocpp/common/evse_security_impl.hpp index efc90f6cf..9d61ba92a 100644 --- a/include/ocpp/common/evse_security_impl.hpp +++ b/include/ocpp/common/evse_security_impl.hpp @@ -48,9 +48,10 @@ class EvseSecurityImpl : public EvseSecurity { void update_ocsp_cache(const CertificateHashDataType& certificate_hash_data, const std::string& ocsp_response) override; bool is_ca_certificate_installed(const CaCertificateType& certificate_type) override; - std::string generate_certificate_signing_request(const CertificateSigningUseEnum& certificate_type, - const std::string& country, const std::string& organization, - const std::string& common, bool use_tpm) override; + std::optional generate_certificate_signing_request(const CertificateSigningUseEnum& certificate_type, + const std::string& country, + const std::string& organization, + const std::string& common, bool use_tpm) override; std::optional get_leaf_certificate_info(const CertificateSigningUseEnum& certificate_type, bool include_ocsp = false) override; bool update_certificate_links(const CertificateSigningUseEnum& certificate_type) override; diff --git a/lib/ocpp/common/evse_security_impl.cpp b/lib/ocpp/common/evse_security_impl.cpp index a31c69850..b01063924 100644 --- a/lib/ocpp/common/evse_security_impl.cpp +++ b/lib/ocpp/common/evse_security_impl.cpp @@ -96,12 +96,19 @@ bool EvseSecurityImpl::is_ca_certificate_installed(const CaCertificateType& cert return this->evse_security->is_ca_certificate_installed(conversions::from_ocpp(certificate_type)); } -std::string EvseSecurityImpl::generate_certificate_signing_request(const CertificateSigningUseEnum& certificate_type, - const std::string& country, - const std::string& organization, - const std::string& common, bool use_tpm) { - return this->evse_security->generate_certificate_signing_request(conversions::from_ocpp(certificate_type), country, - organization, common, use_tpm); +std::optional +EvseSecurityImpl::generate_certificate_signing_request(const CertificateSigningUseEnum& certificate_type, + const std::string& country, const std::string& organization, + const std::string& common, bool use_tpm) { + auto csr_response = this->evse_security->generate_certificate_signing_request( + conversions::from_ocpp(certificate_type), country, organization, common, use_tpm); + + if (csr_response.status == evse_security::GetCertificateSignRequestStatus::Accepted && + csr_response.csr.has_value()) { + return csr_response.csr; + } else { + return std::nullopt; + } } std::optional diff --git a/lib/ocpp/v16/charge_point_impl.cpp b/lib/ocpp/v16/charge_point_impl.cpp index f0588893a..74516123c 100644 --- a/lib/ocpp/v16/charge_point_impl.cpp +++ b/lib/ocpp/v16/charge_point_impl.cpp @@ -2245,7 +2245,15 @@ void ChargePointImpl::sign_certificate(const ocpp::CertificateSigningUseEnum& ce this->configuration->getCpoName().value(), this->configuration->getChargeBoxSerialNumber(), this->configuration->getUseTPM()); - req.csr = csr; + if (!csr.has_value()) { + EVLOG_error << "Create CSR (TPM=" << this->configuration->getUseTPM() << ")" + << " failed for:" + << ocpp::conversions::certificate_signing_use_enum_to_string(certificate_signing_use); + return; + } + + req.csr = csr.value(); + ocpp::Call call(req, this->message_queue->createMessageId()); this->send(call, initiated_by_trigger_message); } @@ -2933,7 +2941,12 @@ void ChargePointImpl::data_transfer_pnc_sign_certificate() { this->configuration->getSeccLeafSubjectCommonName().value_or(this->configuration->getChargeBoxSerialNumber()), this->configuration->getUseTPM()); - csr_req.csr = csr; + if (!csr.has_value()) { + EVLOG_error << "Could not request new V2GCertificate, because the CSR was not successful."; + return; + } + + csr_req.csr = csr.value(); csr_req.certificateType = ocpp::v201::CertificateSigningUseEnum::V2GCertificate; req.data.emplace(json(csr_req).dump()); diff --git a/lib/ocpp/v201/charge_point.cpp b/lib/ocpp/v201/charge_point.cpp index 0180e78b2..9f7a5d0b5 100644 --- a/lib/ocpp/v201/charge_point.cpp +++ b/lib/ocpp/v201/charge_point.cpp @@ -1802,7 +1802,14 @@ void ChargePoint::sign_certificate_req(const ocpp::CertificateSigningUseEnum& ce const auto csr = this->evse_security->generate_certificate_signing_request( certificate_signing_use, country.value(), organization.value(), common.value(), should_use_tpm); - req.csr = csr; + + if (!csr.has_value()) { + EVLOG_error << "CSR generation was unsuccessful for sign request: " + << ocpp::conversions::certificate_signing_use_enum_to_string(certificate_signing_use); + return; + } + + req.csr = csr.value(); this->awaited_certificate_signing_use_enum = certificate_signing_use; From 116a122aa26036972e510d6a169f78edbb42db2a Mon Sep 17 00:00:00 2001 From: AssemblyJohn Date: Fri, 3 May 2024 12:52:07 +0300 Subject: [PATCH 06/12] Updated deps Signed-off-by: AssemblyJohn --- dependencies.yaml | 2 +- tests/evse_security_mock.hpp | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/dependencies.yaml b/dependencies.yaml index fbb4cd427..ff640729e 100644 --- a/dependencies.yaml +++ b/dependencies.yaml @@ -31,7 +31,7 @@ websocketpp: cmake_condition: "LIBOCPP_ENABLE_DEPRECATED_WEBSOCKETPP" libevse-security: git: https://github.com/EVerest/libevse-security.git - git_tag: d6e71cf + git_tag: b56760f libwebsockets: git: https://github.com/warmcat/libwebsockets.git git_tag: v4.3.3 diff --git a/tests/evse_security_mock.hpp b/tests/evse_security_mock.hpp index de396378d..2f8fb4e58 100644 --- a/tests/evse_security_mock.hpp +++ b/tests/evse_security_mock.hpp @@ -26,7 +26,7 @@ class EvseSecurityMock : public EvseSecurity { MOCK_METHOD(std::vector, get_mo_ocsp_request_data, (const std::string&), (override)); MOCK_METHOD(void, update_ocsp_cache, (const CertificateHashDataType&, const std::string&), (override)); MOCK_METHOD(bool, is_ca_certificate_installed, (const CaCertificateType&), (override)); - MOCK_METHOD(std::string, generate_certificate_signing_request, + MOCK_METHOD(std::optional, generate_certificate_signing_request, (const CertificateSigningUseEnum&, const std::string&, const std::string&, const std::string&, bool), (override)); MOCK_METHOD(std::optional, get_leaf_certificate_info, (const CertificateSigningUseEnum&, bool), From 3055563beadf47611033c2c8fe100ec6ddac925a Mon Sep 17 00:00:00 2001 From: AssemblyJohn Date: Mon, 6 May 2024 15:44:33 +0300 Subject: [PATCH 07/12] Updated for comments Signed-off-by: AssemblyJohn --- include/ocpp/common/evse_security.hpp | 2 +- include/ocpp/common/evse_security_impl.hpp | 9 ++++--- include/ocpp/common/types.hpp | 18 +++++++++++++ lib/ocpp/common/evse_security_impl.cpp | 30 +++++++++++++++++----- lib/ocpp/common/types.cpp | 17 ++++++++++++ lib/ocpp/v16/charge_point_impl.cpp | 23 ++++++++++++----- lib/ocpp/v201/charge_point.cpp | 11 +++++--- tests/evse_security_mock.hpp | 2 +- 8 files changed, 91 insertions(+), 21 deletions(-) diff --git a/include/ocpp/common/evse_security.hpp b/include/ocpp/common/evse_security.hpp index 576151578..cd4f45ec0 100644 --- a/include/ocpp/common/evse_security.hpp +++ b/include/ocpp/common/evse_security.hpp @@ -87,7 +87,7 @@ class EvseSecurity { /// \param organization /// \param common /// \return the PEM formatted certificate signing request - virtual std::optional + virtual GetCertificateSignRequestResult generate_certificate_signing_request(const CertificateSigningUseEnum& certificate_type, const std::string& country, const std::string& organization, const std::string& common, bool use_tpm) = 0; diff --git a/include/ocpp/common/evse_security_impl.hpp b/include/ocpp/common/evse_security_impl.hpp index 9d61ba92a..f3b90c4c8 100644 --- a/include/ocpp/common/evse_security_impl.hpp +++ b/include/ocpp/common/evse_security_impl.hpp @@ -48,10 +48,10 @@ class EvseSecurityImpl : public EvseSecurity { void update_ocsp_cache(const CertificateHashDataType& certificate_hash_data, const std::string& ocsp_response) override; bool is_ca_certificate_installed(const CaCertificateType& certificate_type) override; - std::optional generate_certificate_signing_request(const CertificateSigningUseEnum& certificate_type, - const std::string& country, - const std::string& organization, - const std::string& common, bool use_tpm) override; + GetCertificateSignRequestResult + generate_certificate_signing_request(const CertificateSigningUseEnum& certificate_type, const std::string& country, + const std::string& organization, const std::string& common, + bool use_tpm) override; std::optional get_leaf_certificate_info(const CertificateSigningUseEnum& certificate_type, bool include_ocsp = false) override; bool update_certificate_links(const CertificateSigningUseEnum& certificate_type) override; @@ -61,6 +61,7 @@ class EvseSecurityImpl : public EvseSecurity { namespace conversions { +GetCertificateSignRequestStatus to_ocpp(evse_security::GetCertificateSignRequestStatus other); CaCertificateType to_ocpp(evse_security::CaCertificateType other); CertificateSigningUseEnum to_ocpp(evse_security::LeafCertificateType other); CertificateType to_ocpp(evse_security::CertificateType other); diff --git a/include/ocpp/common/types.hpp b/include/ocpp/common/types.hpp index 14aa41ac9..6dded170f 100644 --- a/include/ocpp/common/types.hpp +++ b/include/ocpp/common/types.hpp @@ -531,6 +531,18 @@ struct OCSPRequestData { std::string responderUrl; }; +enum class GetCertificateSignRequestStatus { + Accepted, + InvalidRequestedType, ///< Requested a CSR for non CSMS/V2G leafs + KeyGenError, ///< The key could not be generated with the requested/default parameters + GenerationError, ///< Any other error when creating the CSR +}; + +struct GetCertificateSignRequestResult { + GetCertificateSignRequestStatus status; + std::optional csr; +}; + struct CertificateOCSP { CertificateHashDataType hash; std::optional ocsp_path; @@ -587,6 +599,11 @@ enum class FirmwareStatusNotification { SignatureVerified }; +namespace conversions { +/// \brief Converts GetCertificateSignRequestStatus to string +std::string generate_certificate_signing_request_status_to_string(const GetCertificateSignRequestStatus status); +} // namespace conversions + namespace conversions { /// \brief Converts ocpp::FirmwareStatusNotification to v16::FirmwareStatus @@ -620,6 +637,7 @@ namespace security_events { inline const std::string FIRMWARE_UPDATED = "FirmwareUpdated"; // CRITICAL inline const std::string FAILEDTOAUTHENTICATEATCSMS = "FailedToAuthenticateAtCsms"; inline const std::string CSMSFAILEDTOAUTHENTICATE = "CsmsFailedToAuthenticate"; +inline const std::string CSRGENERATIONFAILED = "CSRGenerationFailed"; inline const std::string SETTINGSYSTEMTIME = "SettingSystemTime"; // CRITICAL inline const std::string RESET_OR_REBOOT = "ResetOrReboot"; // CRITICAL inline const std::string STARTUP_OF_THE_DEVICE = "StartupOfTheDevice"; // CRITICAL diff --git a/lib/ocpp/common/evse_security_impl.cpp b/lib/ocpp/common/evse_security_impl.cpp index b01063924..f0a989835 100644 --- a/lib/ocpp/common/evse_security_impl.cpp +++ b/lib/ocpp/common/evse_security_impl.cpp @@ -96,19 +96,21 @@ bool EvseSecurityImpl::is_ca_certificate_installed(const CaCertificateType& cert return this->evse_security->is_ca_certificate_installed(conversions::from_ocpp(certificate_type)); } -std::optional +GetCertificateSignRequestResult EvseSecurityImpl::generate_certificate_signing_request(const CertificateSigningUseEnum& certificate_type, const std::string& country, const std::string& organization, const std::string& common, bool use_tpm) { auto csr_response = this->evse_security->generate_certificate_signing_request( conversions::from_ocpp(certificate_type), country, organization, common, use_tpm); - if (csr_response.status == evse_security::GetCertificateSignRequestStatus::Accepted && - csr_response.csr.has_value()) { - return csr_response.csr; - } else { - return std::nullopt; + GetCertificateSignRequestResult result; + result.status = conversions::to_ocpp(csr_response.status); + + if (csr_response.csr.has_value()) { + result.csr = csr_response.csr; } + + return result; } std::optional @@ -137,6 +139,22 @@ int EvseSecurityImpl::get_leaf_expiry_days_count(const CertificateSigningUseEnum namespace conversions { +GetCertificateSignRequestStatus to_ocpp(evse_security::GetCertificateSignRequestStatus other) { + switch (other) { + case evse_security::GetCertificateSignRequestStatus::Accepted: + return GetCertificateSignRequestStatus::Accepted; + case evse_security::GetCertificateSignRequestStatus::InvalidRequestedType: + return GetCertificateSignRequestStatus::InvalidRequestedType; + case evse_security::GetCertificateSignRequestStatus::KeyGenError: + return GetCertificateSignRequestStatus::KeyGenError; + case evse_security::GetCertificateSignRequestStatus::GenerationError: + return GetCertificateSignRequestStatus::GenerationError; + default: + throw std::runtime_error( + "Could not convert evse_security::GetCertificateSignRequestStatus to GetCertificateSignRequestStatus"); + } +} + CaCertificateType to_ocpp(evse_security::CaCertificateType other) { switch (other) { case evse_security::CaCertificateType::V2G: diff --git a/lib/ocpp/common/types.cpp b/lib/ocpp/common/types.cpp index 976bb1092..8342b613c 100644 --- a/lib/ocpp/common/types.cpp +++ b/lib/ocpp/common/types.cpp @@ -996,6 +996,23 @@ std::string double_to_string(double d) { } // namespace conversions +namespace conversions { +std::string generate_certificate_signing_request_status_to_string(const GetCertificateSignRequestStatus status) { + switch (status) { + case GetCertificateSignRequestStatus::Accepted: + return "Accepted"; + case GetCertificateSignRequestStatus::InvalidRequestedType: + return "InvalidRequestedType"; + case GetCertificateSignRequestStatus::KeyGenError: + return "KeyGenError"; + case GetCertificateSignRequestStatus::GenerationError: + return "GenerationError"; + default: + throw std::out_of_range("Could not convert GetCertificateSignRequestStatus to string"); + } +} +} // namespace conversions + namespace conversions { v16::FirmwareStatus firmware_status_notification_to_firmware_status(const FirmwareStatusNotification status) { switch (status) { diff --git a/lib/ocpp/v16/charge_point_impl.cpp b/lib/ocpp/v16/charge_point_impl.cpp index 74516123c..f638bd6d9 100644 --- a/lib/ocpp/v16/charge_point_impl.cpp +++ b/lib/ocpp/v16/charge_point_impl.cpp @@ -2240,19 +2240,25 @@ void ChargePointImpl::sign_certificate(const ocpp::CertificateSigningUseEnum& ce EVLOG_info << "Create CSR (TPM=" << this->configuration->getUseTPM() << ")"; SignCertificateRequest req; - const auto csr = this->evse_security->generate_certificate_signing_request( + const auto response = this->evse_security->generate_certificate_signing_request( certificate_signing_use, this->configuration->getSeccLeafSubjectCountry().value_or("DE"), this->configuration->getCpoName().value(), this->configuration->getChargeBoxSerialNumber(), this->configuration->getUseTPM()); - if (!csr.has_value()) { + if (response.status != GetCertificateSignRequestStatus::Accepted || !response.csr.has_value()) { EVLOG_error << "Create CSR (TPM=" << this->configuration->getUseTPM() << ")" << " failed for:" << ocpp::conversions::certificate_signing_use_enum_to_string(certificate_signing_use); + + std::string gen_error = + "Sign certificate failed due to:" + + ocpp::conversions::generate_certificate_signing_request_status_to_string(response.status); + this->securityEventNotification(ocpp::security_events::CSRGENERATIONFAILED, gen_error, true); + return; } - req.csr = csr.value(); + req.csr = response.csr.value(); ocpp::Call call(req, this->message_queue->createMessageId()); this->send(call, initiated_by_trigger_message); @@ -2934,19 +2940,24 @@ void ChargePointImpl::data_transfer_pnc_sign_certificate() { ocpp::v201::SignCertificateRequest csr_req; - const auto csr = this->evse_security->generate_certificate_signing_request( + const auto result = this->evse_security->generate_certificate_signing_request( ocpp::CertificateSigningUseEnum::V2GCertificate, this->configuration->getSeccLeafSubjectCountry().value_or("DE"), this->configuration->getSeccLeafSubjectOrganization().value_or(this->configuration->getCpoName().value()), this->configuration->getSeccLeafSubjectCommonName().value_or(this->configuration->getChargeBoxSerialNumber()), this->configuration->getUseTPM()); - if (!csr.has_value()) { + if (result.status != GetCertificateSignRequestStatus::Accepted || !result.csr.has_value()) { EVLOG_error << "Could not request new V2GCertificate, because the CSR was not successful."; + + std::string gen_error = "Data transfer pnc csr failed due to:" + + ocpp::conversions::generate_certificate_signing_request_status_to_string(result.status); + this->securityEventNotification(ocpp::security_events::CSRGENERATIONFAILED, gen_error, true); + return; } - csr_req.csr = csr.value(); + csr_req.csr = result.csr.value(); csr_req.certificateType = ocpp::v201::CertificateSigningUseEnum::V2GCertificate; req.data.emplace(json(csr_req).dump()); diff --git a/lib/ocpp/v201/charge_point.cpp b/lib/ocpp/v201/charge_point.cpp index 9f7a5d0b5..01df10e74 100644 --- a/lib/ocpp/v201/charge_point.cpp +++ b/lib/ocpp/v201/charge_point.cpp @@ -1800,16 +1800,21 @@ void ChargePoint::sign_certificate_req(const ocpp::CertificateSigningUseEnum& ce bool should_use_tpm = this->device_model->get_optional_value(ControllerComponentVariables::UseTPM).value_or(false); - const auto csr = this->evse_security->generate_certificate_signing_request( + const auto result = this->evse_security->generate_certificate_signing_request( certificate_signing_use, country.value(), organization.value(), common.value(), should_use_tpm); - if (!csr.has_value()) { + if (result.status != GetCertificateSignRequestStatus::Accepted || !result.csr.has_value()) { EVLOG_error << "CSR generation was unsuccessful for sign request: " << ocpp::conversions::certificate_signing_use_enum_to_string(certificate_signing_use); + + std::string gen_error = "Sign certificate req failed due to:" + + ocpp::conversions::generate_certificate_signing_request_status_to_string(result.status); + this->security_event_notification_req(ocpp::security_events::CSRGENERATIONFAILED, + std::optional>(gen_error), true, true); return; } - req.csr = csr.value(); + req.csr = result.csr.value(); this->awaited_certificate_signing_use_enum = certificate_signing_use; diff --git a/tests/evse_security_mock.hpp b/tests/evse_security_mock.hpp index 2f8fb4e58..a0d6699a3 100644 --- a/tests/evse_security_mock.hpp +++ b/tests/evse_security_mock.hpp @@ -26,7 +26,7 @@ class EvseSecurityMock : public EvseSecurity { MOCK_METHOD(std::vector, get_mo_ocsp_request_data, (const std::string&), (override)); MOCK_METHOD(void, update_ocsp_cache, (const CertificateHashDataType&, const std::string&), (override)); MOCK_METHOD(bool, is_ca_certificate_installed, (const CaCertificateType&), (override)); - MOCK_METHOD(std::optional, generate_certificate_signing_request, + MOCK_METHOD(GetCertificateSignRequestResult, generate_certificate_signing_request, (const CertificateSigningUseEnum&, const std::string&, const std::string&, const std::string&, bool), (override)); MOCK_METHOD(std::optional, get_leaf_certificate_info, (const CertificateSigningUseEnum&, bool), From e68e61f530882a452ae1d88e8ea9462a5f18b4d1 Mon Sep 17 00:00:00 2001 From: AssemblyJohn Date: Tue, 7 May 2024 15:05:58 +0300 Subject: [PATCH 08/12] Implemented code comments Signed-off-by: AssemblyJohn --- include/ocpp/common/evse_security.hpp | 4 +- include/ocpp/common/evse_security_impl.hpp | 5 ++- include/ocpp/common/types.hpp | 13 +++++++ lib/ocpp/common/evse_security_impl.cpp | 39 ++++++++++++++----- .../websocket/websocket_libwebsockets.cpp | 20 ++++++---- lib/ocpp/v16/charge_point_impl.cpp | 8 ++-- lib/ocpp/v201/charge_point.cpp | 6 +-- tests/evse_security_mock.hpp | 2 +- 8 files changed, 68 insertions(+), 29 deletions(-) diff --git a/include/ocpp/common/evse_security.hpp b/include/ocpp/common/evse_security.hpp index cd4f45ec0..a7fe9d9e5 100644 --- a/include/ocpp/common/evse_security.hpp +++ b/include/ocpp/common/evse_security.hpp @@ -98,8 +98,8 @@ class EvseSecurity { /// \param encoding specifies PEM or DER format /// \param include_ocsp if we should include certificate ocsp data /// \return info of certificate and key if present, else std::nullopt - virtual std::optional get_leaf_certificate_info(const CertificateSigningUseEnum& certificate_type, - bool include_ocsp = false) = 0; + virtual GetCertificateInfoResult get_leaf_certificate_info(const CertificateSigningUseEnum& certificate_type, + bool include_ocsp = false) = 0; /// \brief Updates the certificate and key links for the given \p certificate_type virtual bool update_certificate_links(const CertificateSigningUseEnum& certificate_type) = 0; diff --git a/include/ocpp/common/evse_security_impl.hpp b/include/ocpp/common/evse_security_impl.hpp index f3b90c4c8..173b68a3d 100644 --- a/include/ocpp/common/evse_security_impl.hpp +++ b/include/ocpp/common/evse_security_impl.hpp @@ -52,8 +52,8 @@ class EvseSecurityImpl : public EvseSecurity { generate_certificate_signing_request(const CertificateSigningUseEnum& certificate_type, const std::string& country, const std::string& organization, const std::string& common, bool use_tpm) override; - std::optional get_leaf_certificate_info(const CertificateSigningUseEnum& certificate_type, - bool include_ocsp = false) override; + GetCertificateInfoResult get_leaf_certificate_info(const CertificateSigningUseEnum& certificate_type, + bool include_ocsp = false) override; bool update_certificate_links(const CertificateSigningUseEnum& certificate_type) override; std::string get_verify_file(const CaCertificateType& certificate_type) override; int get_leaf_expiry_days_count(const CertificateSigningUseEnum& certificate_type) override; @@ -66,6 +66,7 @@ CaCertificateType to_ocpp(evse_security::CaCertificateType other); CertificateSigningUseEnum to_ocpp(evse_security::LeafCertificateType other); CertificateType to_ocpp(evse_security::CertificateType other); HashAlgorithmEnumType to_ocpp(evse_security::HashAlgorithm other); +GetCertificateInfoStatus to_ocpp(evse_security::GetCertificateInfoStatus other); InstallCertificateResult to_ocpp(evse_security::InstallCertificateResult other); CertificateValidationResult to_ocpp(evse_security::CertificateValidationResult other); DeleteCertificateResult to_ocpp(evse_security::DeleteCertificateResult other); diff --git a/include/ocpp/common/types.hpp b/include/ocpp/common/types.hpp index 6dded170f..4a1f0655f 100644 --- a/include/ocpp/common/types.hpp +++ b/include/ocpp/common/types.hpp @@ -538,6 +538,14 @@ enum class GetCertificateSignRequestStatus { GenerationError, ///< Any other error when creating the CSR }; +enum class GetCertificateInfoStatus { + Accepted, + Rejected, + NotFound, + NotFoundValid, + PrivateKeyNotFound, +}; + struct GetCertificateSignRequestResult { GetCertificateSignRequestStatus status; std::optional csr; @@ -557,6 +565,11 @@ struct CertificateInfo { std::vector ocsp; // OCSP data if requested }; +struct GetCertificateInfoResult { + GetCertificateInfoStatus status; + std::optional info; +}; + enum class LeafCertificateType { CSMS, // Charging Station Management System V2G, // Vehicle to grid diff --git a/lib/ocpp/common/evse_security_impl.cpp b/lib/ocpp/common/evse_security_impl.cpp index f0a989835..ef3df8628 100644 --- a/lib/ocpp/common/evse_security_impl.cpp +++ b/lib/ocpp/common/evse_security_impl.cpp @@ -104,25 +104,26 @@ EvseSecurityImpl::generate_certificate_signing_request(const CertificateSigningU conversions::from_ocpp(certificate_type), country, organization, common, use_tpm); GetCertificateSignRequestResult result; - result.status = conversions::to_ocpp(csr_response.status); - if (csr_response.csr.has_value()) { - result.csr = csr_response.csr; - } + result.status = conversions::to_ocpp(csr_response.status); + result.csr = csr_response.csr; return result; } -std::optional -EvseSecurityImpl::get_leaf_certificate_info(const CertificateSigningUseEnum& certificate_type, bool include_ocsp) { +GetCertificateInfoResult EvseSecurityImpl::get_leaf_certificate_info(const CertificateSigningUseEnum& certificate_type, + bool include_ocsp) { const auto info_response = this->evse_security->get_leaf_certificate_info( conversions::from_ocpp(certificate_type), evse_security::EncodingFormat::PEM, include_ocsp); - if (info_response.status == evse_security::GetCertificateInfoStatus::Accepted && info_response.info.has_value()) { - return conversions::to_ocpp(info_response.info.value()); - } else { - return std::nullopt; + GetCertificateInfoResult result; + + result.status = conversions::to_ocpp(info_response.status); + if (info_response.info.has_value()) { + result.info = conversions::to_ocpp(info_response.info.value()); } + + return result; } bool EvseSecurityImpl::update_certificate_links(const CertificateSigningUseEnum& certificate_type) { @@ -213,6 +214,24 @@ HashAlgorithmEnumType to_ocpp(evse_security::HashAlgorithm other) { } } +GetCertificateInfoStatus to_ocpp(evse_security::GetCertificateInfoStatus other) { + switch (other) { + case evse_security::GetCertificateInfoStatus::Accepted: + return GetCertificateInfoStatus::Accepted; + case evse_security::GetCertificateInfoStatus::Rejected: + return GetCertificateInfoStatus::Rejected; + case evse_security::GetCertificateInfoStatus::NotFound: + return GetCertificateInfoStatus::NotFound; + case evse_security::GetCertificateInfoStatus::NotFoundValid: + return GetCertificateInfoStatus::NotFoundValid; + case evse_security::GetCertificateInfoStatus::PrivateKeyNotFound: + return GetCertificateInfoStatus::PrivateKeyNotFound; + default: + throw std::runtime_error( + "Could not convert evse_security::GetCertificateInfoStatus to GetCertificateInfoStatus"); + } +} + InstallCertificateResult to_ocpp(evse_security::InstallCertificateResult other) { switch (other) { case evse_security::InstallCertificateResult::InvalidSignature: diff --git a/lib/ocpp/common/websocket/websocket_libwebsockets.cpp b/lib/ocpp/common/websocket/websocket_libwebsockets.cpp index 73e022d03..5c92e9503 100644 --- a/lib/ocpp/common/websocket/websocket_libwebsockets.cpp +++ b/lib/ocpp/common/websocket/websocket_libwebsockets.cpp @@ -449,22 +449,28 @@ void WebsocketTlsTPM::client_loop() { if (this->connection_options.security_profile == 3) { - const auto certificate_info = + const auto certificate_response = this->evse_security->get_leaf_certificate_info(CertificateSigningUseEnum::ChargingStationCertificate); - if (!certificate_info.has_value()) { + if (certificate_response.status != ocpp::GetCertificateInfoStatus::Accepted && + !certificate_response.info.has_value()) { EVLOG_AND_THROW(std::runtime_error( "Connecting with security profile 3 but no client side certificate is present or valid")); } - if (certificate_info.value().certificate_path.has_value()) { - path_chain = certificate_info.value().certificate_path.value(); + const auto& certificate_info = certificate_response.info.value(); + + if (certificate_info.certificate_path.has_value()) { + path_chain = certificate_info.certificate_path.value(); + } else if (certificate_info.certificate_single_path.has_value()) { + path_chain = certificate_info.certificate_single_path.value(); } else { - path_chain = certificate_info.value().certificate_single_path.value(); + EVLOG_AND_THROW(std::runtime_error( + "Connecting with security profile 3 but no client side certificate is present or valid")); } - path_key = certificate_info.value().key_path; - password = certificate_info.value().password; + path_key = certificate_info.key_path; + password = certificate_info.password; } SSL_CTX* ssl_ctx = nullptr; diff --git a/lib/ocpp/v16/charge_point_impl.cpp b/lib/ocpp/v16/charge_point_impl.cpp index f638bd6d9..f3f93290d 100644 --- a/lib/ocpp/v16/charge_point_impl.cpp +++ b/lib/ocpp/v16/charge_point_impl.cpp @@ -1426,10 +1426,10 @@ void ChargePointImpl::handleChangeConfigurationRequest(ocpp::Callevse_security - ->get_leaf_certificate_info( - ocpp::CertificateSigningUseEnum::ChargingStationCertificate) - .has_value()) { + this->evse_security + ->get_leaf_certificate_info( + ocpp::CertificateSigningUseEnum::ChargingStationCertificate) + .status != ocpp::GetCertificateInfoStatus::Accepted) { EVLOG_warning << "New security level set to 3 but no Client Certificate is installed"; response.status = ConfigurationStatus::Rejected; } else if (security_profile > 3) { diff --git a/lib/ocpp/v201/charge_point.cpp b/lib/ocpp/v201/charge_point.cpp index 01df10e74..bc0299888 100644 --- a/lib/ocpp/v201/charge_point.cpp +++ b/lib/ocpp/v201/charge_point.cpp @@ -1623,9 +1623,9 @@ bool ChargePoint::validate_set_variable(const SetVariableData& set_variable_data } if (network_profile.securityProfile == 3 and - !this->evse_security - ->get_leaf_certificate_info(ocpp::CertificateSigningUseEnum::ChargingStationCertificate) - .has_value()) { + this->evse_security + ->get_leaf_certificate_info(ocpp::CertificateSigningUseEnum::ChargingStationCertificate) + .status != ocpp::GetCertificateInfoStatus::Accepted) { EVLOG_warning << "SecurityProfile of configurationSlot: " << configuration_slot << " is 3 but no CSMS Leaf Certificate is installed"; return false; diff --git a/tests/evse_security_mock.hpp b/tests/evse_security_mock.hpp index a0d6699a3..b9b206876 100644 --- a/tests/evse_security_mock.hpp +++ b/tests/evse_security_mock.hpp @@ -29,7 +29,7 @@ class EvseSecurityMock : public EvseSecurity { MOCK_METHOD(GetCertificateSignRequestResult, generate_certificate_signing_request, (const CertificateSigningUseEnum&, const std::string&, const std::string&, const std::string&, bool), (override)); - MOCK_METHOD(std::optional, get_leaf_certificate_info, (const CertificateSigningUseEnum&, bool), + MOCK_METHOD(GetCertificateInfoResult, get_leaf_certificate_info, (const CertificateSigningUseEnum&, bool), (override)); MOCK_METHOD(bool, update_certificate_links, (const CertificateSigningUseEnum&), (override)); MOCK_METHOD(std::string, get_verify_file, (const CaCertificateType&), (override)); From 37242de85aa536411c419da7c656123a228e4658 Mon Sep 17 00:00:00 2001 From: AssemblyJohn Date: Tue, 7 May 2024 15:38:05 +0300 Subject: [PATCH 09/12] Updated dependencies Signed-off-by: AssemblyJohn --- dependencies.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/dependencies.yaml b/dependencies.yaml index ff640729e..cdd20944b 100644 --- a/dependencies.yaml +++ b/dependencies.yaml @@ -31,7 +31,7 @@ websocketpp: cmake_condition: "LIBOCPP_ENABLE_DEPRECATED_WEBSOCKETPP" libevse-security: git: https://github.com/EVerest/libevse-security.git - git_tag: b56760f + git_tag: 72e0b45fe41292227b6438e19e710b99976205b2 libwebsockets: git: https://github.com/warmcat/libwebsockets.git git_tag: v4.3.3 From 72e242b740f9427bfd28e55d9299f337b15d0adc Mon Sep 17 00:00:00 2001 From: AssemblyJohn Date: Tue, 7 May 2024 17:10:17 +0300 Subject: [PATCH 10/12] Updated some comments Signed-off-by: AssemblyJohn --- include/ocpp/common/evse_security.hpp | 21 ++++++++++--------- .../websocket/websocket_libwebsockets.cpp | 3 +-- 2 files changed, 12 insertions(+), 12 deletions(-) diff --git a/include/ocpp/common/evse_security.hpp b/include/ocpp/common/evse_security.hpp index a7fe9d9e5..68438c542 100644 --- a/include/ocpp/common/evse_security.hpp +++ b/include/ocpp/common/evse_security.hpp @@ -80,24 +80,25 @@ class EvseSecurity { virtual bool is_ca_certificate_installed(const CaCertificateType& certificate_type) = 0; /// \brief Generates a certificate signing request for the given \p certificate_type , \p country , \p organization - /// and \p common . This function respects the requirements of OCPP specified for the CSMS initiated message - /// SignCertificate.req . + /// and \p common , uses the TPM if \p use_tpm is true /// \param certificate_type /// \param country /// \param organization /// \param common - /// \return the PEM formatted certificate signing request + /// \param use_tpm If the TPM should be used for the CSR request + /// \return the status and an optional PEM formatted certificate signing request string virtual GetCertificateSignRequestResult generate_certificate_signing_request(const CertificateSigningUseEnum& certificate_type, const std::string& country, const std::string& organization, const std::string& common, bool use_tpm) = 0; - /// \brief Searches the leaf certificate for the given \p certificate_type and retrieves the most recent certificate - /// that is already valid and the respective key . If no certificate is present or no key is matching the - /// certificate, this function returns std::nullopt - /// \param certificate_type type of the leaf certificate - /// \param encoding specifies PEM or DER format - /// \param include_ocsp if we should include certificate ocsp data - /// \return info of certificate and key if present, else std::nullopt + /// \brief Searches the filesystem on the specified directories for the given \p certificate_type and retrieves the + /// most recent certificate that is already valid and the respective key. If no certificate is present or no key is + /// matching the certificate, this function returns a GetKeyPairStatus other than "Accepted". The function \ref + /// update_leaf_certificate will install two files for each leaf, one containing the single leaf and one containing + /// the leaf including any possible SUBCAs + /// \param certificate_type type of the leaf certificate + /// \param include_ocsp if OCSP data should be included + /// \return contains response result, with info related to the certificate chain and response status virtual GetCertificateInfoResult get_leaf_certificate_info(const CertificateSigningUseEnum& certificate_type, bool include_ocsp = false) = 0; diff --git a/lib/ocpp/common/websocket/websocket_libwebsockets.cpp b/lib/ocpp/common/websocket/websocket_libwebsockets.cpp index 5c92e9503..53e752436 100644 --- a/lib/ocpp/common/websocket/websocket_libwebsockets.cpp +++ b/lib/ocpp/common/websocket/websocket_libwebsockets.cpp @@ -448,11 +448,10 @@ void WebsocketTlsTPM::client_loop() { std::optional password; if (this->connection_options.security_profile == 3) { - const auto certificate_response = this->evse_security->get_leaf_certificate_info(CertificateSigningUseEnum::ChargingStationCertificate); - if (certificate_response.status != ocpp::GetCertificateInfoStatus::Accepted && + if (certificate_response.status != ocpp::GetCertificateInfoStatus::Accepted or !certificate_response.info.has_value()) { EVLOG_AND_THROW(std::runtime_error( "Connecting with security profile 3 but no client side certificate is present or valid")); From 188b7f03441edd371ce90a36415211a48a07fc5b Mon Sep 17 00:00:00 2001 From: AssemblyJohn Date: Wed, 8 May 2024 09:47:11 +0300 Subject: [PATCH 11/12] Update legacy websocket++ Signed-off-by: AssemblyJohn --- include/ocpp/common/evse_security.hpp | 2 +- lib/ocpp/common/websocket/websocket_tls.cpp | 32 ++++++++++++--------- 2 files changed, 20 insertions(+), 14 deletions(-) diff --git a/include/ocpp/common/evse_security.hpp b/include/ocpp/common/evse_security.hpp index 68438c542..042ee738b 100644 --- a/include/ocpp/common/evse_security.hpp +++ b/include/ocpp/common/evse_security.hpp @@ -96,7 +96,7 @@ class EvseSecurity { /// matching the certificate, this function returns a GetKeyPairStatus other than "Accepted". The function \ref /// update_leaf_certificate will install two files for each leaf, one containing the single leaf and one containing /// the leaf including any possible SUBCAs - /// \param certificate_type type of the leaf certificate + /// \param certificate_type type of the leaf certificate /// \param include_ocsp if OCSP data should be included /// \return contains response result, with info related to the certificate chain and response status virtual GetCertificateInfoResult get_leaf_certificate_info(const CertificateSigningUseEnum& certificate_type, diff --git a/lib/ocpp/common/websocket/websocket_tls.cpp b/lib/ocpp/common/websocket/websocket_tls.cpp index cbe96c23e..cfd5023d4 100644 --- a/lib/ocpp/common/websocket/websocket_tls.cpp +++ b/lib/ocpp/common/websocket/websocket_tls.cpp @@ -171,36 +171,42 @@ tls_context WebsocketTLS::on_tls_init(std::string hostname, websocketpp::connect } if (security_profile == 3) { - const auto certificate_info = + const auto certificate_result = this->evse_security->get_leaf_certificate_info(CertificateSigningUseEnum::ChargingStationCertificate); - if (certificate_info.has_value() && certificate_info.value().password.has_value()) { - std::string passwd = certificate_info.value().password.value(); - context->set_password_callback( - [passwd](auto max_len, auto purpose) { return passwd.substr(0, max_len); }); - } - - if (!certificate_info.has_value()) { + if (certificate_result.status != GetCertificateInfoStatus::Accepted || + !certificate_result.info.has_value()) { EVLOG_AND_THROW(std::runtime_error( "Connecting with security profile 3 but no client side certificate is present or valid")); } + const auto& certificate_info = certificate_result.info.value(); + + if (certificate_info.password.has_value()) { + std::string passwd = certificate_info.password.value(); + context->set_password_callback( + [passwd](auto max_len, auto purpose) { return passwd.substr(0, max_len); }); + } + // certificate_path contains the chain if not empty. Use certificate chain if available, else use // certificate_single_path fs::path certificate_path; - if (certificate_info.value().certificate_path.has_value()) { - certificate_path = certificate_info.value().certificate_path.value(); + if (certificate_info.certificate_path.has_value()) { + certificate_path = certificate_info.certificate_path.value(); + } else if (certificate_info.certificate_single_path.has_value()) { + certificate_path = certificate_info.certificate_single_path.value(); } else { - certificate_path = certificate_info.value().certificate_single_path.value(); + EVLOG_AND_THROW(std::runtime_error( + "Connecting with security profile 3 but no client side certificate is present or valid")); } EVLOG_info << "Using certificate: " << certificate_path; if (SSL_CTX_use_certificate_chain_file(context->native_handle(), certificate_path.c_str()) != 1) { EVLOG_AND_THROW(std::runtime_error("Could not use client certificate file within SSL context")); } - EVLOG_info << "Using key file: " << certificate_info.value().key_path; - if (SSL_CTX_use_PrivateKey_file(context->native_handle(), certificate_info.value().key_path.c_str(), + EVLOG_info << "Using key file: " << certificate_info.key_path; + if (SSL_CTX_use_PrivateKey_file(context->native_handle(), certificate_info.key_path.c_str(), SSL_FILETYPE_PEM) != 1) { EVLOG_AND_THROW(std::runtime_error("Could not set private key file within SSL context")); } From 9deeb534c7f38981a422b53a6f0416bdb791b368 Mon Sep 17 00:00:00 2001 From: AssemblyJohn Date: Wed, 8 May 2024 12:32:00 +0300 Subject: [PATCH 12/12] Updated deps Signed-off-by: AssemblyJohn --- dependencies.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/dependencies.yaml b/dependencies.yaml index cdd20944b..090db0d71 100644 --- a/dependencies.yaml +++ b/dependencies.yaml @@ -31,7 +31,7 @@ websocketpp: cmake_condition: "LIBOCPP_ENABLE_DEPRECATED_WEBSOCKETPP" libevse-security: git: https://github.com/EVerest/libevse-security.git - git_tag: 72e0b45fe41292227b6438e19e710b99976205b2 + git_tag: 6e702ef5df568c2f9929a3c3b97a09c0cb4c5b21 libwebsockets: git: https://github.com/warmcat/libwebsockets.git git_tag: v4.3.3