diff --git a/include/ocpp/v16/charge_point.hpp b/include/ocpp/v16/charge_point.hpp index 86f956c02..04445b3c9 100644 --- a/include/ocpp/v16/charge_point.hpp +++ b/include/ocpp/v16/charge_point.hpp @@ -270,6 +270,12 @@ class ChargePoint { /// be called when an EV is plugged in but the authorization is present within the specified ConnectionTimeout void on_plugin_timeout(int32_t connector); + // \brief Notifies chargepoint that a SecurityEvent occurs. This will send a SecurityEventNotification.req to the + /// CSMS + /// \param type type of the security event + /// \param tech_info additional info of the security event + void on_security_event(const std::string& type, const std::string& tech_info); + /// registers a \p callback function that can be used to receive a arbitrary data transfer for the given \p /// vendorId and \p messageId /// \param vendorId @@ -420,6 +426,11 @@ class ChargePoint { void register_configuration_key_changed_callback(const CiString<50>& key, const std::function& callback); + /// \brief registers a \p callback function that can be used to react to a security event callback. This callback is + /// called only if the SecurityEvent occured internally within libocpp + void register_security_event_callback( + const std::function& callback); + /// \brief Gets the configured configuration key requested in the given \p request /// \param request specifies the keys that should be returned. If empty or not set, all keys will be reported /// \return a response containing the requested key(s) including the values and unkown keys if present diff --git a/include/ocpp/v16/charge_point_impl.hpp b/include/ocpp/v16/charge_point_impl.hpp index 67f652fb1..15b2c37ab 100644 --- a/include/ocpp/v16/charge_point_impl.hpp +++ b/include/ocpp/v16/charge_point_impl.hpp @@ -156,6 +156,7 @@ class ChargePointImpl : ocpp::ChargingStationBase { std::function update_firmware_callback; std::function signed_update_firmware_callback; + std::function security_event_callback; std::function idTag, std::optional> parent_id)> @@ -289,7 +290,8 @@ class ChargePointImpl : ocpp::ChargingStationBase { void handleInstallCertificateRequest(Call call); void handleGetLogRequest(Call call); void handleSignedUpdateFirmware(Call call); - void securityEventNotification(const SecurityEvent& type, const std::string& tech_info); + void securityEventNotification(const std::string& type, const std::string& tech_info, + const bool triggered_internally); void switchSecurityProfile(int32_t new_security_profile, int32_t max_connection_attempts); // Local Authorization List profile void handleSendLocalListRequest(Call call); @@ -524,6 +526,12 @@ class ChargePointImpl : ocpp::ChargingStationBase { /// be called when an EV is plugged in but the authorization is present within the specified ConnectionTimeout void on_plugin_timeout(int32_t connector); + /// \brief Notifies chargepoint that a SecurityEvent occurs. This will send a SecurityEventNotification.req to the + /// CSMS + /// \param type type of the security event + /// \param tech_info additional info of the security event + void on_security_event(const std::string& type, const std::string& tech_info); + /// registers a \p callback function that can be used to receive a arbitrary data transfer for the given \p /// vendorId and \p messageId /// \param vendorId @@ -674,6 +682,11 @@ class ChargePointImpl : ocpp::ChargingStationBase { void register_configuration_key_changed_callback(const CiString<50>& key, const std::function& callback); + /// \brief registers a \p callback function that can be used to react to a security event callback. This callback is + /// called only if the SecurityEvent occured internally within libocpp + void register_security_event_callback( + const std::function& callback); + /// \brief Gets the configured configuration key requested in the given \p request /// \param request specifies the keys that should be returned. If empty or not set, all keys will be reported /// \return a response containing the requested key(s) including the values and unkown keys if present diff --git a/include/ocpp/v16/enums.hpp b/include/ocpp/v16/enums.hpp index 5ce878f6c..3fe551e46 100644 --- a/include/ocpp/v16/enums.hpp +++ b/include/ocpp/v16/enums.hpp @@ -845,42 +845,6 @@ ResetStatus string_to_reset_status(const std::string& s); /// \returns an output stream with the ResetStatus written to std::ostream& operator<<(std::ostream& os, const ResetStatus& reset_status); -// from: SecurityEventNotificationRequest -enum class SecurityEvent { - FirmwareUpdated, - FailedToAuthenticateAtCentralSystem, - CentralSystemFailedToAuthenticate, - SettingSystemTime, - StartupOfTheDevice, - ResetOrReboot, - SecurityLogWasCleared, - ReconfigurationOfSecurityParameters, - MemoryExhaustion, - InvalidMessages, - AttemptedReplayAttacks, - TamperDetectionActivated, - InvalidFirmwareSignature, - InvalidFirmwareSigningCertificate, - InvalidCentralSystemCertificate, - InvalidChargePointCertificate, - InvalidTLSVersion, - InvalidTLSCipherSuite, -}; - -namespace conversions { -/// \brief Converts the given SecurityEvent \p e to human readable string -/// \returns a string representation of the SecurityEvent -std::string security_event_to_string(SecurityEvent e); - -/// \brief Converts the given std::string \p s to SecurityEvent -/// \returns a SecurityEvent from a string representation -SecurityEvent string_to_security_event(const std::string& s); -} // namespace conversions - -/// \brief Writes the string representation of the given SecurityEvent \p security_event to the given output stream \p -/// os \returns an output stream with the SecurityEvent written to -std::ostream& operator<<(std::ostream& os, const SecurityEvent& security_event); - // from: SendLocalListRequest enum class UpdateType { Differential, diff --git a/include/ocpp/v16/messages/SecurityEventNotification.hpp b/include/ocpp/v16/messages/SecurityEventNotification.hpp index 21bfd2543..e674560e3 100644 --- a/include/ocpp/v16/messages/SecurityEventNotification.hpp +++ b/include/ocpp/v16/messages/SecurityEventNotification.hpp @@ -7,7 +7,6 @@ #include #include -#include #include namespace ocpp { @@ -15,7 +14,7 @@ namespace v16 { /// \brief Contains a OCPP SecurityEventNotification message struct SecurityEventNotificationRequest : public ocpp::Message { - SecurityEvent type; + CiString<50> type; ocpp::DateTime timestamp; std::optional> techInfo; diff --git a/lib/ocpp/common/message_queue.cpp b/lib/ocpp/common/message_queue.cpp index 65e717a3a..1a76b916d 100644 --- a/lib/ocpp/common/message_queue.cpp +++ b/lib/ocpp/common/message_queue.cpp @@ -27,7 +27,8 @@ bool MessageQueue::isTransactionMessage( } if (message->messageType == v16::MessageType::StartTransaction || message->messageType == v16::MessageType::StopTransaction || - message->messageType == v16::MessageType::MeterValues) { + message->messageType == v16::MessageType::MeterValues || + message->messageType == v16::MessageType::SecurityEventNotification) { return true; } return false; diff --git a/lib/ocpp/v16/charge_point.cpp b/lib/ocpp/v16/charge_point.cpp index 43a30f9d0..9cece2a4f 100644 --- a/lib/ocpp/v16/charge_point.cpp +++ b/lib/ocpp/v16/charge_point.cpp @@ -157,6 +157,10 @@ void ChargePoint::on_plugin_timeout(int32_t connector) { this->charge_point->on_plugin_timeout(connector); } +void ChargePoint::on_security_event(const std::string& type, const std::string& tech_info) { + this->charge_point->on_security_event(type, tech_info); +} + void ChargePoint::register_data_transfer_callback( const CiString<255>& vendorId, const CiString<50>& messageId, const std::function& msg)>& callback) { @@ -266,6 +270,11 @@ void ChargePoint::register_configuration_key_changed_callback( this->charge_point->register_configuration_key_changed_callback(key, callback); } +void ChargePoint::register_security_event_callback( + const std::function& callback) { + this->charge_point->register_security_event_callback(callback); +} + GetConfigurationResponse ChargePoint::get_configuration_key(const GetConfigurationRequest& request) { return this->charge_point->get_configuration_key(request); } diff --git a/lib/ocpp/v16/charge_point_impl.cpp b/lib/ocpp/v16/charge_point_impl.cpp index 2fbb53aa0..4054922e6 100644 --- a/lib/ocpp/v16/charge_point_impl.cpp +++ b/lib/ocpp/v16/charge_point_impl.cpp @@ -1110,7 +1110,6 @@ void ChargePointImpl::handleBootNotificationResponse(ocpp::CallResultset_system_time_callback != nullptr) { this->set_system_time_callback(call_result.msg.currentTime.to_rfc3339()); } - // we are allowed to send messages to the central system // activate heartbeat this->update_heartbeat_interval(); @@ -1124,6 +1123,7 @@ void ChargePointImpl::handleBootNotificationResponse(ocpp::CallResultstop_pending_transactions(); + this->message_queue->get_transaction_messages_from_db(); if (this->is_pnc_enabled()) { this->ocsp_request_timer->timeout(INITIAL_CERTIFICATE_REQUESTS_DELAY); @@ -2096,8 +2096,8 @@ void ChargePointImpl::handleCertificateSignedRequest(ocpp::Callsend(call_result); if (response.status == CertificateSignedStatusEnumType::Rejected) { - this->securityEventNotification(SecurityEvent::InvalidChargePointCertificate, - ocpp::conversions::install_certificate_result_to_string(result)); + this->securityEventNotification("InvalidChargePointCertificate", + ocpp::conversions::install_certificate_result_to_string(result), true); } // reconnect with new certificate if valid and security profile is 3 @@ -2179,8 +2179,8 @@ void ChargePointImpl::handleInstallCertificateRequest(ocpp::Callsend(call_result); if (response.status == InstallCertificateStatusEnumType::Rejected) { - this->securityEventNotification(SecurityEvent::InvalidCentralSystemCertificate, - ocpp::conversions::install_certificate_result_to_string(result)); + this->securityEventNotification("InvalidCentralSystemCertificate", + ocpp::conversions::install_certificate_result_to_string(result), true); } } @@ -2215,11 +2215,12 @@ void ChargePointImpl::handleSignedUpdateFirmware(ocpp::CallsecurityEventNotification(SecurityEvent::InvalidFirmwareSigningCertificate, "Certificate is invalid."); + this->securityEventNotification("InvalidFirmwareSigningCertificate", "Certificate is invalid.", true); } } -void ChargePointImpl::securityEventNotification(const SecurityEvent& type, const std::string& tech_info) { +void ChargePointImpl::securityEventNotification(const std::string& type, const std::string& tech_info, + const bool triggered_internally) { SecurityEventNotificationRequest req; req.type = type; @@ -2228,6 +2229,10 @@ void ChargePointImpl::securityEventNotification(const SecurityEvent& type, const ocpp::Call call(req, this->message_queue->createMessageId()); this->send(call); + + if (triggered_internally and this->security_event_callback != nullptr) { + this->security_event_callback(type, tech_info); + } } void ChargePointImpl::log_status_notification(UploadLogStatusEnumType status, int requestId) { @@ -2258,7 +2263,7 @@ void ChargePointImpl::signed_firmware_update_status_notification(FirmwareStatusE this->send(call); if (status == FirmwareStatusEnumType::InvalidSignature) { - this->securityEventNotification(SecurityEvent::InvalidFirmwareSignature, "techinfo"); + this->securityEventNotification("InvalidFirmwareSignature", "", true); } } @@ -2785,7 +2790,7 @@ void ChargePointImpl::handle_data_transfer_pnc_certificate_signed(Callsend(call_result); if (certificate_response.status == CertificateSignedStatusEnumType::Rejected) { - this->securityEventNotification(SecurityEvent::InvalidChargePointCertificate, tech_info); + this->securityEventNotification("InvalidChargePointCertificate", tech_info, true); } } catch (const json::exception& e) { EVLOG_warning << "Could not parse data of DataTransfer message CertificateSigned.req: " << e.what(); @@ -3362,6 +3367,11 @@ void ChargePointImpl::register_configuration_key_changed_callback( this->configuration_key_changed_callbacks[key] = callback; } +void ChargePointImpl::register_security_event_callback( + const std::function& callback) { + this->security_event_callback = callback; +} + void ChargePointImpl::on_reservation_start(int32_t connector) { this->status->submit_event(connector, FSMEvent::ReserveConnector); } @@ -3382,6 +3392,10 @@ void ChargePointImpl::on_plugin_timeout(int32_t connector) { this->status->submit_event(connector, FSMEvent::TransactionStoppedAndUserActionRequired); } +void ChargePointImpl::on_security_event(const std::string& type, const std::string& tech_info) { + this->securityEventNotification(type, tech_info, false); +} + GetConfigurationResponse ChargePointImpl::get_configuration_key(const GetConfigurationRequest& request) { GetConfigurationResponse response; std::vector configurationKey; diff --git a/lib/ocpp/v16/enums.cpp b/lib/ocpp/v16/enums.cpp index eda2b3ac0..1fe0af433 100644 --- a/lib/ocpp/v16/enums.cpp +++ b/lib/ocpp/v16/enums.cpp @@ -1573,116 +1573,6 @@ std::ostream& operator<<(std::ostream& os, const ResetStatus& reset_status) { return os; } -// from: SecurityEventNotificationRequest -namespace conversions { -std::string security_event_to_string(SecurityEvent e) { - switch (e) { - case SecurityEvent::FirmwareUpdated: - return "FirmwareUpdated"; - case SecurityEvent::FailedToAuthenticateAtCentralSystem: - return "FailedToAuthenticateAtCentralSystem"; - case SecurityEvent::CentralSystemFailedToAuthenticate: - return "CentralSystemFailedToAuthenticate"; - case SecurityEvent::SettingSystemTime: - return "SettingSystemTime"; - case SecurityEvent::StartupOfTheDevice: - return "StartupOfTheDevice"; - case SecurityEvent::ResetOrReboot: - return "ResetOrReboot"; - case SecurityEvent::SecurityLogWasCleared: - return "SecurityLogWasCleared"; - case SecurityEvent::ReconfigurationOfSecurityParameters: - return "ReconfigurationOfSecurityParameters"; - case SecurityEvent::MemoryExhaustion: - return "MemoryExhaustion"; - case SecurityEvent::InvalidMessages: - return "InvalidMessages"; - case SecurityEvent::AttemptedReplayAttacks: - return "AttemptedReplayAttacks"; - case SecurityEvent::TamperDetectionActivated: - return "TamperDetectionActivated"; - case SecurityEvent::InvalidFirmwareSignature: - return "InvalidFirmwareSignature"; - case SecurityEvent::InvalidFirmwareSigningCertificate: - return "InvalidFirmwareSigningCertificate"; - case SecurityEvent::InvalidCentralSystemCertificate: - return "InvalidCentralSystemCertificate"; - case SecurityEvent::InvalidChargePointCertificate: - return "InvalidChargePointCertificate"; - case SecurityEvent::InvalidTLSVersion: - return "InvalidTLSVersion"; - case SecurityEvent::InvalidTLSCipherSuite: - return "InvalidTLSCipherSuite"; - } - - throw std::out_of_range("No known string conversion for provided enum of type SecurityEvent"); -} - -SecurityEvent string_to_security_event(const std::string& s) { - if (s == "FirmwareUpdated") { - return SecurityEvent::FirmwareUpdated; - } - if (s == "FailedToAuthenticateAtCentralSystem") { - return SecurityEvent::FailedToAuthenticateAtCentralSystem; - } - if (s == "CentralSystemFailedToAuthenticate") { - return SecurityEvent::CentralSystemFailedToAuthenticate; - } - if (s == "SettingSystemTime") { - return SecurityEvent::SettingSystemTime; - } - if (s == "StartupOfTheDevice") { - return SecurityEvent::StartupOfTheDevice; - } - if (s == "ResetOrReboot") { - return SecurityEvent::ResetOrReboot; - } - if (s == "SecurityLogWasCleared") { - return SecurityEvent::SecurityLogWasCleared; - } - if (s == "ReconfigurationOfSecurityParameters") { - return SecurityEvent::ReconfigurationOfSecurityParameters; - } - if (s == "MemoryExhaustion") { - return SecurityEvent::MemoryExhaustion; - } - if (s == "InvalidMessages") { - return SecurityEvent::InvalidMessages; - } - if (s == "AttemptedReplayAttacks") { - return SecurityEvent::AttemptedReplayAttacks; - } - if (s == "TamperDetectionActivated") { - return SecurityEvent::TamperDetectionActivated; - } - if (s == "InvalidFirmwareSignature") { - return SecurityEvent::InvalidFirmwareSignature; - } - if (s == "InvalidFirmwareSigningCertificate") { - return SecurityEvent::InvalidFirmwareSigningCertificate; - } - if (s == "InvalidCentralSystemCertificate") { - return SecurityEvent::InvalidCentralSystemCertificate; - } - if (s == "InvalidChargePointCertificate") { - return SecurityEvent::InvalidChargePointCertificate; - } - if (s == "InvalidTLSVersion") { - return SecurityEvent::InvalidTLSVersion; - } - if (s == "InvalidTLSCipherSuite") { - return SecurityEvent::InvalidTLSCipherSuite; - } - - throw std::out_of_range("Provided string " + s + " could not be converted to enum of type SecurityEvent"); -} -} // namespace conversions - -std::ostream& operator<<(std::ostream& os, const SecurityEvent& security_event) { - os << conversions::security_event_to_string(security_event); - return os; -} - // from: SendLocalListRequest namespace conversions { std::string update_type_to_string(UpdateType e) { diff --git a/lib/ocpp/v16/messages/SecurityEventNotification.cpp b/lib/ocpp/v16/messages/SecurityEventNotification.cpp index e80cad241..2438dd88b 100644 --- a/lib/ocpp/v16/messages/SecurityEventNotification.cpp +++ b/lib/ocpp/v16/messages/SecurityEventNotification.cpp @@ -20,7 +20,7 @@ std::string SecurityEventNotificationRequest::get_type() const { void to_json(json& j, const SecurityEventNotificationRequest& k) { // the required parts of the message j = json{ - {"type", conversions::security_event_to_string(k.type)}, + {"type", k.type}, {"timestamp", k.timestamp.to_rfc3339()}, }; // the optional parts of the message @@ -31,7 +31,7 @@ void to_json(json& j, const SecurityEventNotificationRequest& k) { void from_json(const json& j, SecurityEventNotificationRequest& k) { // the required parts of the message - k.type = conversions::string_to_security_event(j.at("type")); + k.type = j.at("type"); k.timestamp = ocpp::DateTime(std::string(j.at("timestamp"))); // the optional parts of the message