Skip to content

Commit

Permalink
* added support to report and consume security events in OCPP1.6
Browse files Browse the repository at this point in the history
* now treating  SecurityEventNotification like transaction-related message
* Revert SecurityEvent enum back to strings

Signed-off-by: Kai-Uwe Hermann <kai-uwe.hermann@pionix.de>
Signed-off-by: pietfried <pietgoempel@gmail.com>
  • Loading branch information
Pietfried committed Oct 26, 2023
1 parent a133941 commit e865d20
Show file tree
Hide file tree
Showing 9 changed files with 62 additions and 161 deletions.
11 changes: 11 additions & 0 deletions include/ocpp/v16/charge_point.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down Expand Up @@ -420,6 +426,11 @@ class ChargePoint {
void register_configuration_key_changed_callback(const CiString<50>& key,
const std::function<void(const KeyValue& key_value)>& 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<void(const std::string& type, const std::string& tech_info)>& 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
Expand Down
15 changes: 14 additions & 1 deletion include/ocpp/v16/charge_point_impl.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -156,6 +156,7 @@ class ChargePointImpl : ocpp::ChargingStationBase {
std::function<void(const UpdateFirmwareRequest msg)> update_firmware_callback;

std::function<UpdateFirmwareStatusEnumType(const SignedUpdateFirmwareRequest msg)> signed_update_firmware_callback;
std::function<void(const std::string& type, const std::string& tech_info)> security_event_callback;

std::function<ReservationStatus(int32_t reservation_id, int32_t connector, ocpp::DateTime expiryDate,
CiString<20> idTag, std::optional<CiString<20>> parent_id)>
Expand Down Expand Up @@ -289,7 +290,8 @@ class ChargePointImpl : ocpp::ChargingStationBase {
void handleInstallCertificateRequest(Call<InstallCertificateRequest> call);
void handleGetLogRequest(Call<GetLogRequest> call);
void handleSignedUpdateFirmware(Call<SignedUpdateFirmwareRequest> 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<SendLocalListRequest> call);
Expand Down Expand Up @@ -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
Expand Down Expand Up @@ -674,6 +682,11 @@ class ChargePointImpl : ocpp::ChargingStationBase {
void register_configuration_key_changed_callback(const CiString<50>& key,
const std::function<void(const KeyValue& key_value)>& 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<void(const std::string& type, const std::string& tech_info)>& 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
Expand Down
36 changes: 0 additions & 36 deletions include/ocpp/v16/enums.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -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,
Expand Down
3 changes: 1 addition & 2 deletions include/ocpp/v16/messages/SecurityEventNotification.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -7,15 +7,14 @@
#include <optional>

#include <ocpp/common/types.hpp>
#include <ocpp/v16/enums.hpp>
#include <ocpp/v16/ocpp_types.hpp>

namespace ocpp {
namespace v16 {

/// \brief Contains a OCPP SecurityEventNotification message
struct SecurityEventNotificationRequest : public ocpp::Message {
SecurityEvent type;
CiString<50> type;
ocpp::DateTime timestamp;
std::optional<CiString<255>> techInfo;

Expand Down
3 changes: 2 additions & 1 deletion lib/ocpp/common/message_queue.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,8 @@ bool MessageQueue<v16::MessageType>::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;
Expand Down
9 changes: 9 additions & 0 deletions lib/ocpp/v16/charge_point.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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<DataTransferResponse(const std::optional<std::string>& msg)>& callback) {
Expand Down Expand Up @@ -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<void(const std::string& type, const std::string& tech_info)>& 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);
}
Expand Down
32 changes: 23 additions & 9 deletions lib/ocpp/v16/charge_point_impl.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1110,7 +1110,6 @@ void ChargePointImpl::handleBootNotificationResponse(ocpp::CallResult<BootNotifi
if (this->set_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();
Expand All @@ -1124,6 +1123,7 @@ void ChargePointImpl::handleBootNotificationResponse(ocpp::CallResult<BootNotifi
}

this->stop_pending_transactions();
this->message_queue->get_transaction_messages_from_db();

if (this->is_pnc_enabled()) {
this->ocsp_request_timer->timeout(INITIAL_CERTIFICATE_REQUESTS_DELAY);
Expand Down Expand Up @@ -2096,8 +2096,8 @@ void ChargePointImpl::handleCertificateSignedRequest(ocpp::Call<CertificateSigne
this->send<CertificateSignedResponse>(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
Expand Down Expand Up @@ -2179,8 +2179,8 @@ void ChargePointImpl::handleInstallCertificateRequest(ocpp::Call<InstallCertific
this->send<InstallCertificateResponse>(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);
}
}

Expand Down Expand Up @@ -2215,11 +2215,12 @@ void ChargePointImpl::handleSignedUpdateFirmware(ocpp::Call<SignedUpdateFirmware
}

if (response.status == UpdateFirmwareStatusEnumType::InvalidCertificate) {
this->securityEventNotification(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;
Expand All @@ -2228,6 +2229,10 @@ void ChargePointImpl::securityEventNotification(const SecurityEvent& type, const

ocpp::Call<SecurityEventNotificationRequest> call(req, this->message_queue->createMessageId());
this->send<SecurityEventNotificationRequest>(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) {
Expand Down Expand Up @@ -2258,7 +2263,7 @@ void ChargePointImpl::signed_firmware_update_status_notification(FirmwareStatusE
this->send<SignedFirmwareStatusNotificationRequest>(call);

if (status == FirmwareStatusEnumType::InvalidSignature) {
this->securityEventNotification(SecurityEvent::InvalidFirmwareSignature, "techinfo");
this->securityEventNotification("InvalidFirmwareSignature", "", true);
}
}

Expand Down Expand Up @@ -2785,7 +2790,7 @@ void ChargePointImpl::handle_data_transfer_pnc_certificate_signed(Call<DataTrans
this->send<DataTransferResponse>(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();
Expand Down Expand Up @@ -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<void(const std::string& type, const std::string& tech_info)>& callback) {
this->security_event_callback = callback;
}

void ChargePointImpl::on_reservation_start(int32_t connector) {
this->status->submit_event(connector, FSMEvent::ReserveConnector);
}
Expand All @@ -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<KeyValue> configurationKey;
Expand Down
110 changes: 0 additions & 110 deletions lib/ocpp/v16/enums.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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) {
Expand Down
Loading

0 comments on commit e865d20

Please sign in to comment.