Skip to content

Commit

Permalink
Bugfix/66 gracefull crash handling (EVerest#604)
Browse files Browse the repository at this point in the history
* Updated interface usage
* Updated dependencies for new interface
* Fixed mocks
* Updated deps
* Updated native interface usage
* Updated deps
* Updated for comments
* Implemented code comments
* Updated dependencies
* Updated some comments
* Update legacy websocket++
* Updated deps
---------

Signed-off-by: AssemblyJohn <[email protected]>
Signed-off-by: Daniel Moore <[email protected]>
  • Loading branch information
AssemblyJohn authored and drmrd committed Jun 4, 2024
1 parent 5ef8400 commit 72107f1
Show file tree
Hide file tree
Showing 11 changed files with 275 additions and 82 deletions.
2 changes: 1 addition & 1 deletion dependencies.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -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: 6e702ef5df568c2f9929a3c3b97a09c0cb4c5b21
libwebsockets:
git: https://github.com/warmcat/libwebsockets.git
git_tag: v4.3.3
Expand Down
30 changes: 16 additions & 14 deletions include/ocpp/common/evse_security.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -80,25 +80,27 @@ 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
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;

/// \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 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 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 encoding specifies PEM or DER format
/// \return key pair of certificate and key if present, else std::nullopt
virtual std::optional<KeyPair> get_key_pair(const CertificateSigningUseEnum& certificate_type) = 0;
/// \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;

/// \brief Updates the certificate and key links for the given \p certificate_type
virtual bool update_certificate_links(const CertificateSigningUseEnum& certificate_type) = 0;
Expand Down
18 changes: 12 additions & 6 deletions include/ocpp/common/evse_security_impl.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -48,29 +48,34 @@ 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<KeyPair> get_key_pair(const CertificateSigningUseEnum& certificate_type) 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;
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;
};

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);
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);

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);
Expand All @@ -83,7 +88,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

Expand Down
48 changes: 43 additions & 5 deletions include/ocpp/common/types.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -531,11 +531,43 @@ 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<std::string> password; // optional password for the private key
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
};

enum class GetCertificateInfoStatus {
Accepted,
Rejected,
NotFound,
NotFoundValid,
PrivateKeyNotFound,
};

struct GetCertificateSignRequestResult {
GetCertificateSignRequestStatus status;
std::optional<std::string> csr;
};

struct CertificateOCSP {
CertificateHashDataType hash;
std::optional<fs::path> ocsp_path;
};

struct CertificateInfo {
std::optional<fs::path> certificate_path; // path to the full certificate chain
std::optional<fs::path> 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<std::string> password; // optional password for the private key
std::vector<CertificateOCSP> ocsp; // OCSP data if requested
};

struct GetCertificateInfoResult {
GetCertificateInfoStatus status;
std::optional<CertificateInfo> info;
};

enum class LeafCertificateType {
Expand Down Expand Up @@ -580,6 +612,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
Expand Down Expand Up @@ -613,6 +650,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
Expand Down
115 changes: 95 additions & 20 deletions lib/ocpp/common/evse_security_impl.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -96,23 +96,34 @@ 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<KeyPair> 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);

if (key_pair.status == evse_security::GetKeyPairStatus::Accepted && key_pair.pair.has_value()) {
return conversions::to_ocpp(key_pair.pair.value());
} else {
return std::nullopt;
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);

GetCertificateSignRequestResult result;

result.status = conversions::to_ocpp(csr_response.status);
result.csr = csr_response.csr;

return result;
}

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);

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) {
Expand All @@ -129,6 +140,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:
Expand Down Expand Up @@ -187,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:
Expand Down Expand Up @@ -284,12 +329,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;
}

Expand Down Expand Up @@ -440,12 +500,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;
}

Expand Down
17 changes: 17 additions & 0 deletions lib/ocpp/common/types.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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) {
Expand Down
25 changes: 16 additions & 9 deletions lib/ocpp/common/websocket/websocket_libwebsockets.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -448,21 +448,28 @@ void WebsocketTlsTPM::client_loop() {
std::optional<std::string> password;

if (this->connection_options.security_profile == 3) {
const auto certificate_response =
this->evse_security->get_leaf_certificate_info(CertificateSigningUseEnum::ChargingStationCertificate);

const auto certificate_key_pair =
this->evse_security->get_key_pair(CertificateSigningUseEnum::ChargingStationCertificate);

if (!certificate_key_pair.has_value()) {
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"));
}

path_chain = certificate_key_pair.value().certificate_path;
if (path_chain.empty()) {
path_chain = certificate_key_pair.value().certificate_single_path;
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 {
EVLOG_AND_THROW(std::runtime_error(
"Connecting with security profile 3 but no client side certificate is present or valid"));
}
path_key = certificate_key_pair.value().key_path;
password = certificate_key_pair.value().password;

path_key = certificate_info.key_path;
password = certificate_info.password;
}

SSL_CTX* ssl_ctx = nullptr;
Expand Down
Loading

0 comments on commit 72107f1

Please sign in to comment.