Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add critical and timestamp parameters to on_security_event functions #795

Merged
merged 2 commits into from
Sep 23, 2024
Merged
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
12 changes: 9 additions & 3 deletions include/ocpp/v16/charge_point.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -311,11 +311,17 @@ 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
/// \brief Notifies chargepoint that a SecurityEvent occurs. This will send a SecurityEventNotification.req to the
/// CSMS
/// \param type type of the security event
/// \param event_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);
/// \param critical if set this overwrites the default criticality recommended in the OCPP 1.6 security whitepaper.
/// A critical security event is transmitted as a message to the CSMS, a non-critical one is just written to the
/// security log
/// \param timestamp when this security event occured, if absent the current datetime is assumed
void on_security_event(const CiString<50>& event_type, const std::optional<CiString<255>>& tech_info,
const std::optional<bool>& critical = std::nullopt,
const std::optional<DateTime>& timestamp = std::nullopt);

/// \brief Handles an internal ChangeAvailabilityRequest (in the same way as if it was emitted by the CSMS).
/// \param request
Expand Down
15 changes: 11 additions & 4 deletions include/ocpp/v16/charge_point_impl.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -337,8 +337,9 @@ class ChargePointImpl : ocpp::ChargingStationBase {
void handleInstallCertificateRequest(Call<InstallCertificateRequest> call);
void handleGetLogRequest(Call<GetLogRequest> call);
void handleSignedUpdateFirmware(Call<SignedUpdateFirmwareRequest> call);
void securityEventNotification(const std::string& type, const std::string& tech_info,
const bool triggered_internally);
void securityEventNotification(const CiString<50>& event_type, const std::optional<CiString<255>>& tech_info,
const bool triggered_internally, const std::optional<bool>& critical = std::nullopt,
const std::optional<DateTime>& timestamp = std::nullopt);
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 @@ -647,9 +648,15 @@ class ChargePointImpl : ocpp::ChargingStationBase {

/// \brief Notifies chargepoint that a SecurityEvent occurs. This will send a SecurityEventNotification.req to the
/// CSMS
/// \param type type of the security event
/// \param event_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);
/// \param critical if set this overwrites the default criticality recommended in the OCPP 1.6 security whitepaper.
/// A critical security event is transmitted as a message to the CSMS, a non-critical one is just written to the
/// security log
/// \param timestamp when this security event occured, if absent the current datetime is assumed
void on_security_event(const CiString<50>& event_type, const std::optional<CiString<255>>& tech_info,
const std::optional<bool>& critical = std::nullopt,
const std::optional<DateTime>& timestamp = std::nullopt);

/// \brief Handles an internal ChangeAvailabilityRequest (in the same way as if it was emitted by the CSMS).
/// \param request
Expand Down
22 changes: 11 additions & 11 deletions include/ocpp/v16/utils.hpp
Original file line number Diff line number Diff line change
@@ -1,28 +1,28 @@
// SPDX-License-Identifier: Apache-2.0
// Copyright 2020 - 2024 Pionix GmbH and Contributors to EVerest
#ifndef V16_UTILS_HPP
#define V16_UTILS_HPP

#include <ocpp/common/call_types.hpp>
#include <ocpp/v16/messages/StopTransaction.hpp>
#include <ocpp/v16/ocpp_types.hpp>
#include <ocpp/v16/types.hpp>

namespace ocpp {
namespace v16 {
namespace utils {

size_t get_message_size(const ocpp::Call<StopTransactionRequest>& call) {
return json(call).at(CALL_PAYLOAD).dump().length();
}
size_t get_message_size(const ocpp::Call<StopTransactionRequest>& call);

/// \brief Drops every second entry from transactionData as long as the message size of the \p call is greater than the
/// \p max_message_size
void drop_transaction_data(size_t max_message_size, ocpp::Call<StopTransactionRequest>& call) {
auto& transaction_data = call.msg.transactionData.value();
while (get_message_size(call) > max_message_size && transaction_data.size() > 2) {
for (size_t i = 1; i < transaction_data.size() - 1; i = i + 2) {
transaction_data.erase(transaction_data.begin() + i);
}
}
}
void drop_transaction_data(size_t max_message_size, ocpp::Call<StopTransactionRequest>& call);

/// \brief Determines if a given \p security_event is critical as defined in the OCPP 1.6 security whitepaper
bool is_critical(const std::string& security_event);

} // namespace utils
} // namespace v16
} // namespace ocpp

#endif
10 changes: 7 additions & 3 deletions include/ocpp/v201/charge_point.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -352,8 +352,10 @@ class ChargePointInterface {
/// \param critical if set this overwrites the default criticality recommended in the OCPP 2.0.1 appendix. A
/// critical security event is transmitted as a message to the CSMS, a non-critical one is just written to the
/// security log
/// \param timestamp when this security event occured, if absent the current datetime is assumed
virtual void on_security_event(const CiString<50>& event_type, const std::optional<CiString<255>>& tech_info,
const std::optional<bool>& critical = std::nullopt) = 0;
const std::optional<bool>& critical = std::nullopt,
const std::optional<DateTime>& timestamp = std::nullopt) = 0;

/// \brief Event handler that will update the variable internally when it has been changed on the fly.
/// \param set_variable_data contains data of the variable to set
Expand Down Expand Up @@ -662,7 +664,8 @@ class ChargePoint : public ChargePointInterface, private ocpp::ChargingStationBa

// Functional Block A: Security
void security_event_notification_req(const CiString<50>& event_type, const std::optional<CiString<255>>& tech_info,
const bool triggered_internally, const bool critical);
const bool triggered_internally, const bool critical,
const std::optional<DateTime>& timestamp = std::nullopt);
void sign_certificate_req(const ocpp::CertificateSigningUseEnum& certificate_signing_use,
const bool initiated_by_trigger_message = false);

Expand Down Expand Up @@ -944,7 +947,8 @@ class ChargePoint : public ChargePointInterface, private ocpp::ChargingStationBa
void on_log_status_notification(UploadLogStatusEnum status, int32_t requestId) override;

void on_security_event(const CiString<50>& event_type, const std::optional<CiString<255>>& tech_info,
const std::optional<bool>& critical = std::nullopt) override;
const std::optional<bool>& critical = std::nullopt,
const std::optional<DateTime>& timestamp = std::nullopt) override;

void on_variable_changed(const SetVariableData& set_variable_data) override;

Expand Down
1 change: 1 addition & 0 deletions lib/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -46,6 +46,7 @@ if(LIBOCPP_ENABLE_V16)
ocpp/v16/ocpp_enums.cpp
ocpp/v16/ocpp_types.cpp
ocpp/v16/types.cpp
ocpp/v16/utils.cpp
)
add_subdirectory(ocpp/v16/messages)
endif()
Expand Down
5 changes: 3 additions & 2 deletions lib/ocpp/v16/charge_point.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -172,8 +172,9 @@ 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::on_security_event(const CiString<50>& event_type, const std::optional<CiString<255>>& tech_info,
const std::optional<bool>& critical, const std::optional<DateTime>& timestamp) {
this->charge_point->on_security_event(event_type, tech_info, critical, timestamp);
}

ChangeAvailabilityResponse ChargePoint::on_change_availability(const ChangeAvailabilityRequest& request) {
Expand Down
82 changes: 53 additions & 29 deletions lib/ocpp/v16/charge_point_impl.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1053,16 +1053,19 @@ bool ChargePointImpl::start(const std::map<int, ChargePointStatus>& connector_st

switch (bootreason) {
case BootReasonEnum::RemoteReset:
this->securityEventNotification(CiString<50>(ocpp::security_events::RESET_OR_REBOOT),
"Charging Station rebooted due to requested remote reset!", true);
this->securityEventNotification(
CiString<50>(ocpp::security_events::RESET_OR_REBOOT),
std::optional<CiString<255>>("Charging Station rebooted due to requested remote reset!"), true);
break;
case BootReasonEnum::ScheduledReset:
this->securityEventNotification(CiString<50>(ocpp::security_events::RESET_OR_REBOOT),
"Charging Station rebooted due to a scheduled reset!", true);
this->securityEventNotification(
CiString<50>(ocpp::security_events::RESET_OR_REBOOT),
std::optional<CiString<255>>("Charging Station rebooted due to a scheduled reset!"), true);
break;
case BootReasonEnum::FirmwareUpdate:
this->securityEventNotification(CiString<50>(ocpp::security_events::FIRMWARE_UPDATED),
"Charging Station rebooted due to firmware update!", true);
this->securityEventNotification(
CiString<50>(ocpp::security_events::FIRMWARE_UPDATED),
std::optional<CiString<255>>("Charging Station rebooted due to firmware update!"), true);
break;
case BootReasonEnum::ApplicationReset:
case BootReasonEnum::LocalReset:
Expand All @@ -1072,7 +1075,7 @@ bool ChargePointImpl::start(const std::map<int, ChargePointStatus>& connector_st
case BootReasonEnum::Watchdog:
default:
this->securityEventNotification(CiString<50>(ocpp::security_events::STARTUP_OF_THE_DEVICE),
"The Charge Point has booted", true);
std::optional<CiString<255>>("The Charge Point has booted"), true);
break;
}

Expand Down Expand Up @@ -1310,13 +1313,15 @@ void ChargePointImpl::message_callback(const std::string& message) {
if (json_message.is_array() && json_message.size() > MESSAGE_ID) {
auto call_error = CallError(enhanced_message.uniqueId, "FormationViolation", e.what(), json({}, true));
this->send(call_error);
this->securityEventNotification(ocpp::security_events::INVALIDMESSAGES, message, true);
this->securityEventNotification(ocpp::security_events::INVALIDMESSAGES,
std::optional<CiString<255>>(message), true);
}
} catch (const EnumConversionException& e) {
EVLOG_error << "EnumConversionException during handling of message: " << e.what();
auto call_error = CallError(enhanced_message.uniqueId, "FormationViolation", e.what(), json({}, true));
this->send(call_error);
this->securityEventNotification(ocpp::security_events::INVALIDMESSAGES, message, true);
this->securityEventNotification(ocpp::security_events::INVALIDMESSAGES, std::optional<CiString<255>>(message),
true);
}
}

Expand Down Expand Up @@ -2565,7 +2570,8 @@ void ChargePointImpl::sign_certificate(const ocpp::CertificateSigningUseEnum& ce
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);
this->securityEventNotification(ocpp::security_events::CSRGENERATIONFAILED,
std::optional<CiString<255>>(gen_error), true);

return;
}
Expand Down Expand Up @@ -2621,8 +2627,9 @@ void ChargePointImpl::handleCertificateSignedRequest(ocpp::Call<CertificateSigne
this->send<CertificateSignedResponse>(call_result);

if (response.status == CertificateSignedStatusEnumType::Rejected) {
this->securityEventNotification(ocpp::security_events::INVALIDCHARGEPOINTCERTIFICATE,
ocpp::conversions::install_certificate_result_to_string(result), true);
this->securityEventNotification(
ocpp::security_events::INVALIDCHARGEPOINTCERTIFICATE,
std::optional<CiString<255>>(ocpp::conversions::install_certificate_result_to_string(result)), true);
}

// reconnect with new certificate if valid and security profile is 3
Expand Down Expand Up @@ -2705,8 +2712,9 @@ void ChargePointImpl::handleInstallCertificateRequest(ocpp::Call<InstallCertific
this->send<InstallCertificateResponse>(call_result);

if (response.status == InstallCertificateStatusEnumType::Rejected) {
this->securityEventNotification(ocpp::security_events::INVALIDCENTRALSYSTEMCERTIFICATE,
ocpp::conversions::install_certificate_result_to_string(result), true);
this->securityEventNotification(
ocpp::security_events::INVALIDCENTRALSYSTEMCERTIFICATE,
std::optional<CiString<255>>(ocpp::conversions::install_certificate_result_to_string(result)), true);
}
}

Expand Down Expand Up @@ -2742,27 +2750,39 @@ void ChargePointImpl::handleSignedUpdateFirmware(ocpp::Call<SignedUpdateFirmware

if (response.status == UpdateFirmwareStatusEnumType::InvalidCertificate) {
this->securityEventNotification(ocpp::security_events::INVALIDFIRMWARESIGNINGCERTIFICATE,
"Certificate is invalid.", true);
std::optional<CiString<255>>("Certificate is invalid."), true);
}
}

void ChargePointImpl::securityEventNotification(const std::string& type, const std::string& tech_info,
const bool triggered_internally) {

void ChargePointImpl::securityEventNotification(const CiString<50>& event_type,
const std::optional<CiString<255>>& tech_info,
const bool triggered_internally, const std::optional<bool>& critical,
const std::optional<DateTime>& timestamp) {
SecurityEventNotificationRequest req;
req.type = type;
req.techInfo.emplace(tech_info);
req.timestamp = ocpp::DateTime();
req.type = event_type;
req.techInfo = tech_info;
if (timestamp.has_value()) {
req.timestamp = timestamp.value();
} else {
req.timestamp = ocpp::DateTime();
}

this->logging->security(json(req).dump());

if (!this->configuration->getDisableSecurityEventNotifications()) {
auto critical_security_event = true;
if (critical.has_value()) {
critical_security_event = critical.value();
} else {
critical_security_event = utils::is_critical(event_type);
}

if (critical_security_event and !this->configuration->getDisableSecurityEventNotifications()) {
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);
this->security_event_callback(event_type.get(), tech_info.value_or(CiString<255>("")).get());
}
}

Expand Down Expand Up @@ -2809,7 +2829,7 @@ void ChargePointImpl::signed_firmware_update_status_notification(FirmwareStatusE
this->send<SignedFirmwareStatusNotificationRequest>(call, initiated_by_trigger_message);

if (status == FirmwareStatusEnumType::InvalidSignature) {
this->securityEventNotification(ocpp::security_events::INVALIDFIRMWARESIGNATURE, "", true);
this->securityEventNotification(ocpp::security_events::INVALIDFIRMWARESIGNATURE, std::nullopt, true);
}

if (this->firmware_update_is_pending) {
Expand Down Expand Up @@ -3539,7 +3559,8 @@ void ChargePointImpl::data_transfer_pnc_sign_certificate() {

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);
this->securityEventNotification(ocpp::security_events::CSRGENERATIONFAILED,
std::optional<CiString<255>>(gen_error), true);

return;
}
Expand Down Expand Up @@ -3748,7 +3769,8 @@ void ChargePointImpl::handle_data_transfer_pnc_certificate_signed(Call<DataTrans
this->send<DataTransferResponse>(call_result);

if (certificate_response.status == CertificateSignedStatusEnumType::Rejected) {
this->securityEventNotification(ocpp::security_events::INVALIDCHARGEPOINTCERTIFICATE, tech_info, true);
this->securityEventNotification(ocpp::security_events::INVALIDCHARGEPOINTCERTIFICATE,
std::optional<CiString<255>>(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 @@ -4278,7 +4300,8 @@ void ChargePointImpl::on_firmware_update_status_notification(int32_t request_id,
}

if (firmware_update_status == FirmwareStatusNotification::Installed) {
this->securityEventNotification(ocpp::security_events::FIRMWARE_UPDATED, "Firmware update was installed", true);
this->securityEventNotification(ocpp::security_events::FIRMWARE_UPDATED,
std::optional<CiString<255>>("Firmware update was installed"), true);
}
}

Expand Down Expand Up @@ -4492,8 +4515,9 @@ void ChargePointImpl::on_plugin_timeout(int32_t connector) {
"ConnectionTimeout");
}

void ChargePointImpl::on_security_event(const std::string& type, const std::string& tech_info) {
this->securityEventNotification(type, tech_info, false);
void ChargePointImpl::on_security_event(const CiString<50>& event_type, const std::optional<CiString<255>>& tech_info,
const std::optional<bool>& critical, const std::optional<DateTime>& timestamp) {
this->securityEventNotification(event_type, tech_info, false, critical, timestamp);
}

ChangeAvailabilityResponse ChargePointImpl::on_change_availability(const ChangeAvailabilityRequest& request) {
Expand Down
Loading
Loading