Skip to content

Commit fde2ece

Browse files
authored
Extended Use of SecurityEventNotification.req in OCPP1.6 (#425)
* added optional BootReason to charge_point start function * added optional BootReason to charge_point restart function * using string definitions for all SecurityEventNotifications in 1.6 * Sending SecurityEventNotification on startup if StartUpOfTheDevice or ResetOrReboot * added SecurityEventType InvalidChargePointCertificate for OCPP1.6 * added OCPP1.6 ConfigurationKey: DisableSecurityEventNotifications ; can be used to disable the sending of SecurityEventNotification.req * only send SecurityEventNotification.req if the DisableSecurityEventNotifications configuration key is false (which is the default) Signed-off-by: pietfried <pietgoempel@gmail.com>
1 parent d76c80a commit fde2ece

10 files changed

+113
-27
lines changed

config/v16/profile_schemas/Security.json

+6
Original file line numberDiff line numberDiff line change
@@ -41,6 +41,12 @@
4141
"maximum": 3,
4242
"description": "This configuration key is used to set the security profile used by the Charge Point",
4343
"readOnly": false
44+
},
45+
"DisableSecurityEventNotifications": {
46+
"type": "boolean",
47+
"description": "When set to true, no SecurityEventNotification.req messages will be sent by the Charge Point",
48+
"readOnly": false,
49+
"default": false
4450
}
4551
}
4652
}

include/ocpp/common/message_queue.hpp

+14-7
Original file line numberDiff line numberDiff line change
@@ -430,18 +430,25 @@ template <typename M> class MessageQueue {
430430
MessageQueue(send_callback, config, {}, databaseHandler) {
431431
}
432432

433-
void get_transaction_messages_from_db() {
433+
void get_transaction_messages_from_db(bool ignore_security_event_notifications = false) {
434434
std::vector<ocpp::common::DBTransactionMessage> transaction_messages =
435435
database_handler->get_transaction_messages();
436436

437437
if (!transaction_messages.empty()) {
438438
for (auto& transaction_message : transaction_messages) {
439-
std::shared_ptr<ControlMessage<M>> message =
440-
std::make_shared<ControlMessage<M>>(transaction_message.json_message);
441-
message->messageType = string_to_messagetype(transaction_message.message_type);
442-
message->timestamp = transaction_message.timestamp;
443-
message->message_attempts = transaction_message.message_attempts;
444-
transaction_message_queue.push_back(message);
439+
440+
if (ignore_security_event_notifications &&
441+
transaction_message.message_type == "SecurityEventNotification") {
442+
// remove from database in case SecurityEventNotification.req should not be sent
443+
this->database_handler->remove_transaction_message(transaction_message.unique_id);
444+
} else {
445+
std::shared_ptr<ControlMessage<M>> message =
446+
std::make_shared<ControlMessage<M>>(transaction_message.json_message);
447+
message->messageType = string_to_messagetype(transaction_message.message_type);
448+
message->timestamp = transaction_message.timestamp;
449+
message->message_attempts = transaction_message.message_attempts;
450+
transaction_message_queue.push_back(message);
451+
}
445452
}
446453

447454
this->new_message = true;

include/ocpp/common/types.hpp

+1
Original file line numberDiff line numberDiff line change
@@ -593,6 +593,7 @@ inline const std::string INVALIDFIRMWARESIGNATURE = "InvalidFirmwareSignature";
593593
inline const std::string INVALIDFIRMWARESIGNINGCERTIFICATE = "InvalidFirmwareSigningCertificate";
594594
inline const std::string INVALIDCSMSCERTIFICATE = "InvalidCsmsCertificate";
595595
inline const std::string INVALIDCHARGINGSTATIONCERTIFICATE = "InvalidChargingStationCertificate";
596+
inline const std::string INVALIDCHARGEPOINTCERTIFICATE = "InvalidChargePointCertificate"; // for OCPP1.6
596597
inline const std::string INVALIDTLSVERSION = "InvalidTLSVersion";
597598
inline const std::string INVALIDTLSCIPHERSUITE = "InvalidTLSCipherSuite";
598599
} // namespace security_events

include/ocpp/v16/charge_point.hpp

+6-2
Original file line numberDiff line numberDiff line change
@@ -73,15 +73,19 @@ class ChargePoint {
7373
/// \param connector_status_map initial state of connectors including connector 0 with reduced set of states
7474
/// (Available, Unavailable, Faulted). connector_status_map is empty, last availability states from the persistant
7575
/// storage will be used
76+
/// \param bootreason reason for calling the start function
7677
/// \return
77-
bool start(const std::map<int, ChargePointStatus>& connector_status_map = {});
78+
bool start(const std::map<int, ChargePointStatus>& connector_status_map = {},
79+
BootReasonEnum bootreason = BootReasonEnum::PowerUp);
7880

7981
/// \brief Restarts the ChargePoint if it has been stopped before. The ChargePoint is reinitialized, connects to the
8082
/// websocket and starts to communicate OCPP messages again
8183
/// \param connector_status_map initial state of connectors including connector 0 with reduced set of states
8284
/// (Available, Unavailable, Faulted). connector_status_map is empty, last availability states from the persistant
8385
/// storage will be used
84-
bool restart(const std::map<int, ChargePointStatus>& connector_status_map = {});
86+
/// \param bootreason reason for calling the start function
87+
bool restart(const std::map<int, ChargePointStatus>& connector_status_map = {},
88+
BootReasonEnum bootreason = BootReasonEnum::ApplicationReset);
8589

8690
// \brief Resets the internal state machine for the connectors using the given \p connector_status_map
8791
/// \param connector_status_map state of connectors including connector 0 with reduced set of states (Available,

include/ocpp/v16/charge_point_configuration.hpp

+5
Original file line numberDiff line numberDiff line change
@@ -330,6 +330,11 @@ class ChargePointConfiguration {
330330
void setSecurityProfile(int32_t security_profile);
331331
KeyValue getSecurityProfileKeyValue();
332332

333+
// // Security profile - optional with default
334+
bool getDisableSecurityEventNotifications();
335+
void setDisableSecurityEventNotifications(bool disable_security_event_notifications);
336+
KeyValue getDisableSecurityEventNotificationsKeyValue();
337+
333338
// Local Auth List Management Profile
334339
bool getLocalAuthListEnabled();
335340
void setLocalAuthListEnabled(bool local_auth_list_enabled);

include/ocpp/v16/charge_point_impl.hpp

+7-3
Original file line numberDiff line numberDiff line change
@@ -86,6 +86,7 @@ namespace v16 {
8686
class ChargePointImpl : ocpp::ChargingStationBase {
8787
private:
8888
bool initialized;
89+
BootReasonEnum bootreason;
8990
ChargePointConnectionState connection_state;
9091
bool boot_notification_callerror;
9192
RegistrationStatus registration_status;
@@ -373,15 +374,18 @@ class ChargePointImpl : ocpp::ChargingStationBase {
373374
/// \brief Starts the ChargePoint, initializes and connects to the Websocket endpoint and initializes a
374375
/// BootNotification.req
375376
/// \param connector_status_map initial state of connectors including connector 0 with reduced set of states
376-
/// (Available, Unavailable, Faulted) \return
377-
bool start(const std::map<int, ChargePointStatus>& connector_status_map);
377+
/// (Available, Unavailable, Faulted)
378+
/// \param bootreason reason for calling the start function
379+
/// \return
380+
bool start(const std::map<int, ChargePointStatus>& connector_status_map, BootReasonEnum bootreason);
378381

379382
/// \brief Restarts the ChargePoint if it has been stopped before. The ChargePoint is reinitialized, connects to the
380383
/// websocket and starts to communicate OCPP messages again
381384
/// \param connector_status_map initial state of connectors including connector 0 with reduced set of states
382385
/// (Available, Unavailable, Faulted). connector_status_map is empty, last availability states from the persistant
383386
/// storage will be used
384-
bool restart(const std::map<int, ChargePointStatus>& connector_status_map);
387+
/// \param bootreason reason for calling the restart function
388+
bool restart(const std::map<int, ChargePointStatus>& connector_status_map, BootReasonEnum bootreason);
385389

386390
/// \brief Resets the internal state machine for the connectors using the given \p connector_status_map
387391
/// \param connector_status_map state of connectors including connector 0 with reduced set of states (Available,

include/ocpp/v16/types.hpp

+13
Original file line numberDiff line numberDiff line change
@@ -186,6 +186,19 @@ struct AvailabilityChange {
186186
bool persist;
187187
};
188188

189+
/// \brief BootReasonEnum contains the different boot reasons of the charge point (copied from OCPP2.0.1 definition)
190+
enum BootReasonEnum {
191+
ApplicationReset,
192+
FirmwareUpdate,
193+
LocalReset,
194+
PowerUp,
195+
RemoteReset,
196+
ScheduledReset,
197+
Triggered,
198+
Unknown,
199+
Watchdog
200+
};
201+
189202
} // namespace v16
190203
} // namespace ocpp
191204

lib/ocpp/v16/charge_point.cpp

+4-4
Original file line numberDiff line numberDiff line change
@@ -21,12 +21,12 @@ ChargePoint::ChargePoint(const std::string& config, const fs::path& share_path,
2121

2222
ChargePoint::~ChargePoint() = default;
2323

24-
bool ChargePoint::start(const std::map<int, ChargePointStatus>& connector_status_map) {
25-
return this->charge_point->start(connector_status_map);
24+
bool ChargePoint::start(const std::map<int, ChargePointStatus>& connector_status_map, BootReasonEnum bootreason) {
25+
return this->charge_point->start(connector_status_map, bootreason);
2626
}
2727

28-
bool ChargePoint::restart(const std::map<int, ChargePointStatus>& connector_status_map) {
29-
return this->charge_point->restart(connector_status_map);
28+
bool ChargePoint::restart(const std::map<int, ChargePointStatus>& connector_status_map, BootReasonEnum bootreason) {
29+
return this->charge_point->restart(connector_status_map, bootreason);
3030
}
3131

3232
bool ChargePoint::stop() {

lib/ocpp/v16/charge_point_configuration.cpp

+23
Original file line numberDiff line numberDiff line change
@@ -1719,6 +1719,23 @@ KeyValue ChargePointConfiguration::getSecurityProfileKeyValue() {
17191719
return kv;
17201720
}
17211721

1722+
bool ChargePointConfiguration::getDisableSecurityEventNotifications() {
1723+
return this->config["Security"]["DisableSecurityEventNotifications"];
1724+
}
1725+
1726+
void ChargePointConfiguration::setDisableSecurityEventNotifications(bool disable_security_event_notifications) {
1727+
this->config["Security"]["DisableSecurityEventNotifications"] = disable_security_event_notifications;
1728+
this->setInUserConfig("Security", "DisableSecurityEventNotifications", disable_security_event_notifications);
1729+
}
1730+
1731+
KeyValue ChargePointConfiguration::getDisableSecurityEventNotificationsKeyValue() {
1732+
KeyValue kv;
1733+
kv.key = "DisableSecurityEventNotifications";
1734+
kv.readonly = false;
1735+
kv.value.emplace(ocpp::conversions::bool_to_string(this->getDisableSecurityEventNotifications()));
1736+
return kv;
1737+
}
1738+
17221739
// Local Auth List Management Profile
17231740
bool ChargePointConfiguration::getLocalAuthListEnabled() {
17241741
if (this->config.contains("LocalAuthListManagement")) {
@@ -2287,6 +2304,9 @@ std::optional<KeyValue> ChargePointConfiguration::get(CiString<50> key) {
22872304
if (key == "SecurityProfile") {
22882305
return this->getSecurityProfileKeyValue();
22892306
}
2307+
if (key == "DisableSecurityEventNotifications") {
2308+
return this->getDisableSecurityEventNotificationsKeyValue();
2309+
}
22902310
if (key == "StopTransactionOnEVSideDisconnect") {
22912311
return this->getStopTransactionOnEVSideDisconnectKeyValue();
22922312
}
@@ -2530,6 +2550,9 @@ ConfigurationStatus ChargePointConfiguration::set(CiString<50> key, CiString<500
25302550
if (key == "CpoName") {
25312551
this->setCpoName(value.get());
25322552
}
2553+
if (key == "DisableSecurityEventNotifications") {
2554+
this->setDisableSecurityEventNotifications(ocpp::conversions::string_to_bool(value.get()));
2555+
}
25332556
if (key == "HeartbeatInterval") {
25342557
try {
25352558
auto [valid, interval] = is_positive_integer(value.get());

lib/ocpp/v16/charge_point_impl.cpp

+34-11
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,7 @@ ChargePointImpl::ChargePointImpl(const std::string& config, const fs::path& shar
2828
ocpp::ChargingStationBase(evse_security, security_configuration),
2929
boot_notification_callerror(false),
3030
initialized(false),
31+
bootreason(BootReasonEnum::PowerUp),
3132
connection_state(ChargePointConnectionState::Disconnected),
3233
registration_status(RegistrationStatus::Pending),
3334
diagnostics_status(DiagnosticsStatus::Idle),
@@ -790,7 +791,8 @@ void ChargePointImpl::send_meter_value(int32_t connector, MeterValue meter_value
790791
this->send<MeterValuesRequest>(call, initiated_by_trigger_message);
791792
}
792793

793-
bool ChargePointImpl::start(const std::map<int, ChargePointStatus>& connector_status_map) {
794+
bool ChargePointImpl::start(const std::map<int, ChargePointStatus>& connector_status_map, BootReasonEnum bootreason) {
795+
this->bootreason = bootreason;
794796
this->init_state_machine(connector_status_map);
795797
this->init_websocket();
796798
this->websocket->connect();
@@ -801,15 +803,15 @@ bool ChargePointImpl::start(const std::map<int, ChargePointStatus>& connector_st
801803
return true;
802804
}
803805

804-
bool ChargePointImpl::restart(const std::map<int, ChargePointStatus>& connector_status_map) {
806+
bool ChargePointImpl::restart(const std::map<int, ChargePointStatus>& connector_status_map, BootReasonEnum bootreason) {
805807
if (this->stopped) {
806808
EVLOG_info << "Restarting OCPP Chargepoint";
807809
this->database_handler->open_db_connection(this->configuration->getNumberOfConnectors());
808810
// instantiating new message queue on restart
809811
this->message_queue = this->create_message_queue();
810812
this->initialized = true;
811813

812-
return this->start(connector_status_map);
814+
return this->start(connector_status_map, bootreason);
813815
} else {
814816
EVLOG_warning << "Attempting to restart Chargepoint while it has not been stopped before";
815817
return false;
@@ -1014,6 +1016,7 @@ void ChargePointImpl::message_callback(const std::string& message) {
10141016
auto call_error = CallError(MessageId(json_message.at(MESSAGE_ID).get<std::string>()), "FormationViolation",
10151017
e.what(), json({}, true));
10161018
this->send(call_error);
1019+
this->securityEventNotification(ocpp::security_events::INVALIDMESSAGES, message, true);
10171020
}
10181021
}
10191022
}
@@ -1183,7 +1186,9 @@ void ChargePointImpl::handleBootNotificationResponse(ocpp::CallResult<BootNotifi
11831186
ocpp::DateTime());
11841187
}
11851188

1186-
this->message_queue->get_transaction_messages_from_db();
1189+
// push transaction messages including SecurityEventNotification.req onto the message queue
1190+
this->message_queue->get_transaction_messages_from_db(
1191+
this->configuration->getDisableSecurityEventNotifications());
11871192

11881193
if (this->is_pnc_enabled()) {
11891194
this->ocsp_request_timer->timeout(INITIAL_CERTIFICATE_REQUESTS_DELAY);
@@ -1199,6 +1204,17 @@ void ChargePointImpl::handleBootNotificationResponse(ocpp::CallResult<BootNotifi
11991204
this->ocsp_request_timer->timeout(INITIAL_CERTIFICATE_REQUESTS_DELAY);
12001205
}
12011206

1207+
if (this->bootreason == BootReasonEnum::RemoteReset) {
1208+
this->securityEventNotification(CiString<50>(ocpp::security_events::RESET_OR_REBOOT),
1209+
"Charging Station rebooted due to requested remote reset!", true);
1210+
} else if (this->bootreason == BootReasonEnum::ScheduledReset) {
1211+
this->securityEventNotification(CiString<50>(ocpp::security_events::RESET_OR_REBOOT),
1212+
"Charging Station rebooted due to a scheduled reset!", true);
1213+
} else if (this->bootreason == BootReasonEnum::PowerUp) {
1214+
this->securityEventNotification(CiString<50>(ocpp::security_events::STARTUP_OF_THE_DEVICE),
1215+
"The Charge Point has booted", true);
1216+
}
1217+
12021218
this->stop_pending_transactions();
12031219

12041220
break;
@@ -2230,7 +2246,7 @@ void ChargePointImpl::handleCertificateSignedRequest(ocpp::Call<CertificateSigne
22302246
this->send<CertificateSignedResponse>(call_result);
22312247

22322248
if (response.status == CertificateSignedStatusEnumType::Rejected) {
2233-
this->securityEventNotification("InvalidChargePointCertificate",
2249+
this->securityEventNotification(ocpp::security_events::INVALIDCHARGEPOINTCERTIFICATE,
22342250
ocpp::conversions::install_certificate_result_to_string(result), true);
22352251
}
22362252

@@ -2313,7 +2329,7 @@ void ChargePointImpl::handleInstallCertificateRequest(ocpp::Call<InstallCertific
23132329
this->send<InstallCertificateResponse>(call_result);
23142330

23152331
if (response.status == InstallCertificateStatusEnumType::Rejected) {
2316-
this->securityEventNotification("InvalidCentralSystemCertificate",
2332+
this->securityEventNotification(ocpp::security_events::INVALIDCSMSCERTIFICATE,
23172333
ocpp::conversions::install_certificate_result_to_string(result), true);
23182334
}
23192335
}
@@ -2349,7 +2365,8 @@ void ChargePointImpl::handleSignedUpdateFirmware(ocpp::Call<SignedUpdateFirmware
23492365
}
23502366

23512367
if (response.status == UpdateFirmwareStatusEnumType::InvalidCertificate) {
2352-
this->securityEventNotification("InvalidFirmwareSigningCertificate", "Certificate is invalid.", true);
2368+
this->securityEventNotification(ocpp::security_events::INVALIDFIRMWARESIGNINGCERTIFICATE,
2369+
"Certificate is invalid.", true);
23532370
}
23542371
}
23552372

@@ -2363,8 +2380,10 @@ void ChargePointImpl::securityEventNotification(const std::string& type, const s
23632380

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

2366-
ocpp::Call<SecurityEventNotificationRequest> call(req, this->message_queue->createMessageId());
2367-
this->send<SecurityEventNotificationRequest>(call);
2383+
if (!this->configuration->getDisableSecurityEventNotifications()) {
2384+
ocpp::Call<SecurityEventNotificationRequest> call(req, this->message_queue->createMessageId());
2385+
this->send<SecurityEventNotificationRequest>(call);
2386+
}
23682387

23692388
if (triggered_internally and this->security_event_callback != nullptr) {
23702389
this->security_event_callback(type, tech_info);
@@ -2414,7 +2433,7 @@ void ChargePointImpl::signed_firmware_update_status_notification(FirmwareStatusE
24142433
this->send<SignedFirmwareStatusNotificationRequest>(call, initiated_by_trigger_message);
24152434

24162435
if (status == FirmwareStatusEnumType::InvalidSignature) {
2417-
this->securityEventNotification("InvalidFirmwareSignature", "", true);
2436+
this->securityEventNotification(ocpp::security_events::INVALIDFIRMWARESIGNATURE, "", true);
24182437
}
24192438

24202439
if (this->firmware_update_is_pending) {
@@ -2958,7 +2977,7 @@ void ChargePointImpl::handle_data_transfer_pnc_certificate_signed(Call<DataTrans
29582977
this->send<DataTransferResponse>(call_result);
29592978

29602979
if (certificate_response.status == CertificateSignedStatusEnumType::Rejected) {
2961-
this->securityEventNotification("InvalidChargePointCertificate", tech_info, true);
2980+
this->securityEventNotification(ocpp::security_events::INVALIDCHARGEPOINTCERTIFICATE, tech_info, true);
29622981
}
29632982
} catch (const json::exception& e) {
29642983
EVLOG_warning << "Could not parse data of DataTransfer message CertificateSigned.req: " << e.what();
@@ -3425,6 +3444,10 @@ void ChargePointImpl::on_firmware_update_status_notification(int32_t request_id,
34253444
} catch (const std::out_of_range& e) {
34263445
EVLOG_debug << "Could not convert incoming FirmwareStatusNotification to OCPP type";
34273446
}
3447+
3448+
if (firmware_update_status == FirmwareStatusNotification::Installed) {
3449+
this->securityEventNotification(ocpp::security_events::FIRMWARE_UPDATED, "Firmware update was installed", true);
3450+
}
34283451
}
34293452

34303453
void ChargePointImpl::diagnostic_status_notification(DiagnosticsStatus status) {

0 commit comments

Comments
 (0)