Skip to content

Commit

Permalink
Merge branch 'EVerest:main' into feature/k01-handle-set-charging-prof…
Browse files Browse the repository at this point in the history
…ile-request
  • Loading branch information
couryrr-afs authored Jul 29, 2024
2 parents f95e552 + ebe4fd5 commit e14ff8d
Show file tree
Hide file tree
Showing 7 changed files with 122 additions and 35 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.7.0
git_tag: 7624055c9a23c75e310ea7b3ebb7010063cf4c6c
libwebsockets:
git: https://github.com/warmcat/libwebsockets.git
git_tag: v4.3.3
Expand Down
2 changes: 1 addition & 1 deletion include/ocpp/common/websocket/websocket_libwebsockets.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -54,7 +54,7 @@ class WebsocketTlsTPM final : public WebsocketBase {
int process_callback(void* wsi_ptr, int callback_reason, void* user, void* in, size_t len);

private:
void tls_init(struct ssl_ctx_st* ctx, const std::string& path_chain, const std::string& path_key, bool tpm_key,
bool tls_init(struct ssl_ctx_st* ctx, const std::string& path_chain, const std::string& path_key, bool custom_key,
std::optional<std::string>& password);
void client_loop();
void recv_loop();
Expand Down
9 changes: 7 additions & 2 deletions include/ocpp/v201/ctrlr_component_variables.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -16,11 +16,9 @@ extern const Component& AuthCacheCtrlr;
extern const Component& AuthCtrlr;
extern const Component& ChargingStation;
extern const Component& ClockCtrlr;
extern const Component& Connector;
extern const Component& CustomizationCtrlr;
extern const Component& DeviceDataCtrlr;
extern const Component& DisplayMessageCtrlr;
extern const Component& EVSE;
extern const Component& ISO15118Ctrlr;
extern const Component& LocalAuthListCtrlr;
extern const Component& MonitoringCtrlr;
Expand All @@ -33,6 +31,13 @@ extern const Component& TariffCostCtrlr;
extern const Component& TxCtrlr;
} // namespace ControllerComponents

namespace StandardizedVariables {
extern const Variable& Problem;
extern const Variable& Tripped;
extern const Variable& Overload;
extern const Variable& Fallback;
}; // namespace StandardizedVariables

// Provides access to standardized variables of OCPP2.0.1 spec
namespace ControllerComponentVariables {
extern const ComponentVariable& InternalCtrlrEnabled;
Expand Down
106 changes: 82 additions & 24 deletions lib/ocpp/common/websocket/websocket_libwebsockets.cpp
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
// SPDX-License-Identifier: Apache-2.0
// Copyright 2020 - 2023 Pionix GmbH and Contributors to EVerest
#include <evse_security/crypto/openssl/openssl_tpm.hpp>
#include <evse_security/crypto/openssl/openssl_provider.hpp>
#include <ocpp/common/websocket/websocket_libwebsockets.hpp>

#include <everest/logging.hpp>
Expand Down Expand Up @@ -40,7 +40,7 @@ template <> class std::default_delete<SSL_CTX> {

namespace ocpp {

using evse_security::is_tpm_key_file;
using evse_security::is_custom_private_key_file;
using evse_security::OpenSSLProvider;

enum class EConnectionState {
Expand Down Expand Up @@ -276,16 +276,30 @@ static int callback_minimal(struct lws* wsi, enum lws_callback_reasons reason, v
return 0;
}

static int private_key_callback(char* buf, int size, int rwflag, void* userdata) {
const auto* password = static_cast<const std::string*>(userdata);
const std::size_t max_pass_len = (size - 1); // we exclude the endline
const std::size_t max_copy_chars =
std::min(max_pass_len, password->length()); // truncate if pass is too large and buffer too small

std::memset(buf, 0, size);
std::memcpy(buf, password->c_str(), max_copy_chars);

return max_copy_chars;
}

constexpr auto local_protocol_name = "lws-everest-client";
static const struct lws_protocols protocols[] = {{local_protocol_name, callback_minimal, 0, 0, 0, NULL, 0},
LWS_PROTOCOL_LIST_TERM};

void WebsocketTlsTPM::tls_init(SSL_CTX* ctx, const std::string& path_chain, const std::string& path_key, bool tpm_key,
std::optional<std::string>& password) {
bool WebsocketTlsTPM::tls_init(SSL_CTX* ctx, const std::string& path_chain, const std::string& path_key,
bool custom_key, std::optional<std::string>& password) {
auto rc = SSL_CTX_set_cipher_list(ctx, this->connection_options.supported_ciphers_12.c_str());
if (rc != 1) {
EVLOG_debug << "SSL_CTX_set_cipher_list return value: " << rc;
EVLOG_AND_THROW(std::runtime_error("Could not set TLSv1.2 cipher list"));
EVLOG_error << "Could not set TLSv1.2 cipher list";

return false;
}

rc = SSL_CTX_set_ciphersuites(ctx, this->connection_options.supported_ciphers_13.c_str());
Expand All @@ -298,24 +312,35 @@ void WebsocketTlsTPM::tls_init(SSL_CTX* ctx, const std::string& path_chain, cons
if (this->connection_options.security_profile == 3) {
if ((path_chain.empty()) || (path_key.empty())) {
EVLOG_error << "Cert chain: [" << path_chain << "] key: " << path_key << "]";
EVLOG_AND_THROW(std::runtime_error("No certificate and key for SSL"));
EVLOG_error << "No certificate or key found for SSL";

return false;
}

if (1 != SSL_CTX_use_certificate_chain_file(ctx, path_chain.c_str())) {
ERR_print_errors_fp(stderr);
EVLOG_AND_THROW(std::runtime_error("Could not use client certificate file within SSL context"));
EVLOG_error << "Could not use client certificate file within SSL context";

return false;
}

SSL_CTX_set_default_passwd_cb_userdata(ctx, reinterpret_cast<void*>(password.value_or("").data()));
if (password.has_value()) {
SSL_CTX_set_default_passwd_cb_userdata(ctx, &password.value());
SSL_CTX_set_default_passwd_cb(ctx, private_key_callback);
}

if (1 != SSL_CTX_use_PrivateKey_file(ctx, path_key.c_str(), SSL_FILETYPE_PEM)) {
ERR_print_errors_fp(stderr);
EVLOG_AND_THROW(std::runtime_error("Could not set private key file within SSL context"));
EVLOG_error << "Could not set private key file within SSL context";

return false;
}

if (false == SSL_CTX_check_private_key(ctx)) {
ERR_print_errors_fp(stderr);
EVLOG_AND_THROW(std::runtime_error("Could not check private key within SSL context"));
EVLOG_error << "Could not check private key within SSL context";

return false;
}
}

Expand All @@ -328,15 +353,15 @@ void WebsocketTlsTPM::tls_init(SSL_CTX* ctx, const std::string& path_chain, cons

if (rc != 1) {
EVLOG_error << "Could not load CA verify locations, error: " << ERR_error_string(ERR_get_error(), NULL);
EVLOG_AND_THROW(std::runtime_error("Could not load CA verify locations"));
return false;
}
}

if (this->connection_options.use_ssl_default_verify_paths) {
rc = SSL_CTX_set_default_verify_paths(ctx);
if (rc != 1) {
EVLOG_error << "Could not load default CA verify path, error: " << ERR_error_string(ERR_get_error(), NULL);
EVLOG_AND_THROW(std::runtime_error("Could not load CA verify locations"));
return false;
}
}

Expand Down Expand Up @@ -374,6 +399,8 @@ void WebsocketTlsTPM::tls_init(SSL_CTX* ctx, const std::string& path_chain, cons

// Extra info
SSL_CTX_set_verify(ctx, SSL_VERIFY_PEER, NULL); // to verify server certificate

return true;
}

void WebsocketTlsTPM::recv_loop() {
Expand Down Expand Up @@ -450,20 +477,28 @@ void WebsocketTlsTPM::client_loop() {

info.fd_limit_per_thread = 1 + 1 + 1;

// Lifetime of this is important since we use the data from this in private_key_callback()
std::optional<std::string> private_key_password;

if (this->connection_options.security_profile == 2 || this->connection_options.security_profile == 3) {
// Setup context - need to know the key type first
std::string path_key;
std::string path_chain;
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);

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"));
EVLOG_error << "Connecting with security profile 3 but no client side certificate is present or valid";

local_data->update_state(EConnectionState::ERROR);
on_conn_fail();

// Notify conn waiter
conn_cv.notify_one();
return;
}

const auto& certificate_info = certificate_response.info.value();
Expand All @@ -473,25 +508,31 @@ void WebsocketTlsTPM::client_loop() {
} 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"));
EVLOG_error << "Connecting with security profile 3 but no client side certificate is present or valid";

local_data->update_state(EConnectionState::ERROR);
on_conn_fail();

// Notify conn waiter
conn_cv.notify_one();
return;
}

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

SSL_CTX* ssl_ctx = nullptr;
bool tpm_key = false;
bool custom_key = false;

if (!path_key.empty()) {
tpm_key = is_tpm_key_file(path_key);
custom_key = is_custom_private_key_file(path_key);
}

OpenSSLProvider provider;

if (tpm_key) {
provider.set_tls_mode(OpenSSLProvider::mode_t::tpm2_provider);
if (custom_key) {
provider.set_tls_mode(OpenSSLProvider::mode_t::custom_provider);
} else {
provider.set_tls_mode(OpenSSLProvider::mode_t::default_provider);
}
Expand All @@ -501,11 +542,27 @@ void WebsocketTlsTPM::client_loop() {

if (ssl_ctx == nullptr) {
ERR_print_errors_fp(stderr);
EVLOG_AND_THROW(std::runtime_error("Unable to create ssl ctx."));
EVLOG_error << "Unable to create ssl ctx";

local_data->update_state(EConnectionState::ERROR);
on_conn_fail();

// Notify conn waiter
conn_cv.notify_one();
return;
}

// Init TLS data
tls_init(ssl_ctx, path_chain, path_key, tpm_key, password);
if (tls_init(ssl_ctx, path_chain, path_key, custom_key, private_key_password) == false) {
EVLOG_error << "Unable to init tls";

local_data->update_state(EConnectionState::ERROR);
on_conn_fail();

// Notify conn waiter
conn_cv.notify_one();
return;
}

// Setup our context
info.provided_client_ssl_ctx = ssl_ctx;
Expand All @@ -518,6 +575,7 @@ void WebsocketTlsTPM::client_loop() {
if (nullptr == lws_ctx) {
EVLOG_error << "lws init failed!";
local_data->update_state(EConnectionState::FINALIZED);
return;
}

// Conn acquire the lws context
Expand Down
4 changes: 0 additions & 4 deletions lib/ocpp/v201/charge_point.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -2412,10 +2412,6 @@ void ChargePoint::handle_get_base_report_req(Call<GetBaseReportRequest> call) {
GetBaseReportResponse response;
response.status = GenericDeviceModelStatusEnum::Accepted;

if (msg.reportBase == ReportBaseEnum::SummaryInventory) {
response.status = GenericDeviceModelStatusEnum::NotSupported;
}

ocpp::CallResult<GetBaseReportResponse> call_result(response, call.uniqueId);
this->send<GetBaseReportResponse>(call_result);

Expand Down
7 changes: 7 additions & 0 deletions lib/ocpp/v201/ctrlr_component_variables.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,13 @@ const Component& TariffCostCtrlr = {"TariffCostCtrlr"};
const Component& TxCtrlr = {"TxCtrlr"};
} // namespace ControllerComponents

namespace StandardizedVariables {
const Variable& Problem = {"Problem"};
const Variable& Tripped = {"Tripped"};
const Variable& Overload = {"Overload"};
const Variable& Fallback = {"Fallback"};
}; // namespace StandardizedVariables

namespace ControllerComponentVariables {

const ComponentVariable& InternalCtrlrEnabled = {
Expand Down
27 changes: 24 additions & 3 deletions lib/ocpp/v201/device_model.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -198,6 +198,24 @@ bool validate_value(const VariableCharacteristics& characteristics, const std::s
}
}

bool include_in_summary_inventory(const ComponentVariable& cv, const VariableAttribute& attribute) {
if (cv == ControllerComponentVariables::ChargingStationAvailabilityState) {
return true;
}
if (cv.component.name == "EVSE" and cv.variable == EvseComponentVariables::AvailabilityState) {
return true;
}
if (cv.component.name == "Connector" and cv.variable == ConnectorComponentVariables::AvailabilityState) {
return true;
}
if ((cv.variable == StandardizedVariables::Fallback or cv.variable == StandardizedVariables::Overload or
cv.variable == StandardizedVariables::Problem or cv.variable == StandardizedVariables::Tripped) and
attribute.value.value_or("") == "true") {
return true;
}
return false;
}

GetVariableStatusEnum DeviceModel::request_value_internal(const Component& component_id, const Variable& variable_id,
const AttributeEnum& attribute_enum, std::string& value,
bool allow_write_only) {
Expand Down Expand Up @@ -327,14 +345,13 @@ std::vector<ReportData> DeviceModel::get_base_report_data(const ReportBaseEnum&
report_data.component = component;
report_data.variable = variable;

ComponentVariable cv = {component, std::nullopt, variable};

// request the variable attribute from the device model storage
const auto variable_attributes = this->storage->get_variable_attributes(component, variable);

// iterate over possibly (Actual, Target, MinSet, MaxSet)
for (const auto& variable_attribute : variable_attributes) {
// FIXME(piet): Right now this reports only FullInventory (ReadOnly,
// ReadWrite or WriteOnly) and ConfigurationInventory (ReadWrite or WriteOnly) correctly
// TODO(piet): SummaryInventory
if (report_base == ReportBaseEnum::FullInventory or
(report_base == ReportBaseEnum::ConfigurationInventory and
(variable_attribute.mutability == MutabilityEnum::ReadWrite or
Expand All @@ -345,6 +362,10 @@ std::vector<ReportData> DeviceModel::get_base_report_data(const ReportBaseEnum&
report_data.variableAttribute.back().value.reset();
}
report_data.variableCharacteristics = variable_map.at(variable).characteristics;
} else if (report_base == ReportBaseEnum::SummaryInventory) {
if (include_in_summary_inventory(cv, variable_attribute)) {
report_data.variableAttribute.push_back(variable_attribute);
}
}
}
if (!report_data.variableAttribute.empty()) {
Expand Down

0 comments on commit e14ff8d

Please sign in to comment.