Skip to content

Commit

Permalink
Added support for libwebsockets for tpm/secprofile 2/3 (#320)
Browse files Browse the repository at this point in the history
* Added support for:
- libwebsockets for tpm/secprofile 2/3
- TPM with hardware generated key
- config for TPM usage
- cmake for compile-time libwebsockets usage

* Make libwebsockets dependency conditional if using edm >= 0.5.6
* Use identical settings for libwebsockets as used in everest-framework
* Add missing newlines
* Add explanation about LIBOCPP_ENABLE_LIBWEBSOCKETS cmake option to README
* Update lib/ocpp/common/websocket/websocket_tls_tpm.cpp
* Apply suggestions from code review
* Codacity warnings

---------

Signed-off-by: AssemblyJohn <[email protected]>
Signed-off-by: Kai-Uwe Hermann <[email protected]>
Co-authored-by: Kai-Uwe Hermann <[email protected]>
Co-authored-by: barsnick <[email protected]>
  • Loading branch information
3 people authored Dec 22, 2023
1 parent a6cd837 commit cf644b8
Show file tree
Hide file tree
Showing 13 changed files with 1,102 additions and 2 deletions.
9 changes: 9 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,7 @@ Libocpp provides a complete implementation of OCPP 1.6. The implementation of OC
- [Building the doxygen documentation](#building-the-doxygen-documentation)
- [Unit testing](#unit-testing)
- [Building with FetchContent instead of EDM](#building-with-fetchcontent-instead-of-edm)
- [Support for security profile 2 and 3 with TPM in OCPP 1.6 using libwebsockets](#support-for-security-profile-2-and-3-with-tpm-in-ocpp-16-using-libwebsockets)

## Feature Support

Expand Down Expand Up @@ -514,3 +515,11 @@ Run the unit tests

## Building with FetchContent instead of EDM
In [doc/build-with-fetchcontent](doc/build-with-fetchcontent) you can find an example how to build libocpp with FetchContent instead of EDM.

## Support for security profile 2 and 3 with TPM in OCPP 1.6 using libwebsockets

If you want to try the new websocket implementation based on libwebsockets (supporting security profile 2 and 3 with TPM) you can set the following cmake option.

```bash
cmake .. -DLIBOCPP_ENABLE_LIBWEBSOCKETS=ON
```
3 changes: 2 additions & 1 deletion config/v16/config-docker.json
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,8 @@
"ChargePointModel": "Yeti",
"ChargePointVendor": "Pionix",
"FirmwareVersion": "0.1",
"AllowChargingProfileWithoutStartSchedule": true
"AllowChargingProfileWithoutStartSchedule": true,
"UseTPM" : false
},
"Core": {
"AuthorizeRemoteTxRequests": false,
Expand Down
5 changes: 5 additions & 0 deletions config/v16/profile_schemas/Internal.json
Original file line number Diff line number Diff line change
Expand Up @@ -104,6 +104,11 @@
"TLS_AES_128_GCM_SHA256"
]
},
"UseTPM": {
"type": "boolean",
"readOnly": true,
"default": false
},
"RetryBackoffRandomRange": {
"$comment": "maximum value for the random part of the websocket reconnect back-off time",
"type": "integer",
Expand Down
24 changes: 24 additions & 0 deletions dependencies.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -31,3 +31,27 @@ websocketpp:
libevse-security:
git: https://github.com/EVerest/libevse-security.git
git_tag: v0.3.0
libwebsockets:
git: https://github.com/warmcat/libwebsockets.git
git_tag: v4.3.3
cmake_condition: "LIBOCPP_ENABLE_LIBWEBSOCKETS"
options:
- CMAKE_POLICY_DEFAULT_CMP0077 NEW
- LWS_ROLE_RAW_FILE OFF
- LWS_UNIX_SOCK OFF
- LWS_WITH_SYS_STATE OFF
- LWS_WITH_SYS_SMD OFF
- LWS_WITH_UPNG OFF
- LWS_WITH_JPEG OFF
- LWS_WITH_DLO OFF
- LWS_WITH_SECURE_STREAMS OFF
- LWS_WITH_STATIC OFF
- LWS_WITH_LHP OFF
- LWS_WITH_LEJP_CONF OFF
- LWS_WITH_MINIMAL_EXAMPLES OFF
- LWS_WITH_CACHE_NSCOOKIEJAR OFF
- LWS_WITHOUT_TESTAPPS ON
- LWS_WITHOUT_TEST_SERVER ON
- LWS_WITHOUT_TEST_SERVER_EXTPOLL ON
- LWS_WITHOUT_TEST_PING ON
- LWS_WITHOUT_TEST_CLIENT ON
1 change: 1 addition & 0 deletions include/ocpp/common/websocket/websocket_base.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,7 @@ struct WebsocketConnectionOptions {
std::optional<bool> additional_root_certificate_check;
std::optional<std::string> hostName;
bool verify_csms_common_name;
bool use_tpm_tls;
};

///
Expand Down
92 changes: 92 additions & 0 deletions include/ocpp/common/websocket/websocket_tls_tpm.hpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,92 @@
// SPDX-License-Identifier: Apache-2.0
// Copyright 2020 - 2023 Pionix GmbH and Contributors to EVerest
#ifndef OCPP_WEBSOCKET_TLS_TPM_HPP
#define OCPP_WEBSOCKET_TLS_TPM_HPP

#include <ocpp/common/evse_security.hpp>
#include <ocpp/common/websocket/websocket_base.hpp>

#include <queue>
namespace ocpp {

struct ConnectionData;
struct WebsocketMessage;

/// \brief Experimental libwebsockets TLS connection
class WebsocketTlsTPM final : public WebsocketBase {
public:
/// \brief Creates a new Websocket object with the providede \p connection_options
explicit WebsocketTlsTPM(const WebsocketConnectionOptions& connection_options,
std::shared_ptr<EvseSecurity> evse_security);

~WebsocketTlsTPM();

void set_connection_options(const WebsocketConnectionOptions& connection_options) override;

/// \brief connect to a TLS websocket
/// \returns true if the websocket is initialized and a connection attempt is made
bool connect() override;

/// \brief Reconnects the websocket using the delay, a reason for this reconnect can be provided with the
/// \param reason parameter
/// \param delay delay of the reconnect attempt
void reconnect(std::error_code reason, long delay) override;

/// \brief closes the websocket
void close(websocketpp::close::status::value code, const std::string& reason) override;

/// \brief send a \p message over the websocket
/// \returns true if the message was sent successfully
bool send(const std::string& message) override;

/// \brief send a websocket ping
void ping() override;

public:
int process_callback(void* wsi_ptr, int callback_reason, void* user, void* in, size_t len);

private:
void tls_init();
void client_loop();
void recv_loop();

/// \brief Called when a TLS websocket connection is established, calls the connected callback
void on_conn_connected();

/// \brief Called when a TLS websocket connection is closed
void on_conn_close();

/// \brief Called when a TLS websocket connection fails to be established
void on_conn_fail();

/// \brief When the connection can send data
void on_writable();

/// \brief Called when a message is received over the TLS websocket, calls the message callback
void on_message(void* msg, size_t len);

void request_write();

void poll_message(const std::shared_ptr<WebsocketMessage>& msg, bool wait_sendaf);

private:
std::shared_ptr<EvseSecurity> evse_security;

// Connection related data
std::unique_ptr<Everest::SteadyTimer> reconnect_timer_tpm;
std::unique_ptr<std::thread> websocket_thread;
std::shared_ptr<ConnectionData> conn_data;
std::condition_variable conn_cv;

std::mutex queue_mutex;
std::queue<std::shared_ptr<WebsocketMessage>> message_queue;
std::condition_variable msg_send_cv;

std::unique_ptr<std::thread> recv_message_thread;
std::mutex recv_mutex;
std::queue<std::string> recv_message_queue;
std::condition_variable recv_message_cv;
};

} // namespace ocpp
#endif // OCPP_WEBSOCKET_HPP
8 changes: 8 additions & 0 deletions include/ocpp/common/websocket/websocket_uri.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,14 @@ class Uri {
return this->chargepoint_id;
}

std::string get_path() {
return this->path_without_chargepoint_id;
}

uint16_t get_port() {
return this->port;
}

std::string string() {
auto uri = get_websocketpp_uri();
return uri.str();
Expand Down
1 change: 1 addition & 0 deletions include/ocpp/v16/charge_point_configuration.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -81,6 +81,7 @@ class ChargePointConfiguration {
KeyValue getUseSslDefaultVerifyPathsKeyValue();
bool getVerifyCsmsCommonName();
KeyValue getVerifyCsmsCommonNameKeyValue();
bool getUseTPM();

int32_t getRetryBackoffRandomRange();
void setRetryBackoffRandomRange(int32_t retry_backoff_random_range);
Expand Down
15 changes: 14 additions & 1 deletion lib/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -52,6 +52,7 @@ add_subdirectory(ocpp/v16/messages)
add_subdirectory(ocpp/v201/messages)

option(LIBOCPP_USE_BOOST_FILESYSTEM "Usage of boost/filesystem.hpp instead of std::filesystem" OFF)
option(LIBOCPP_ENABLE_LIBWEBSOCKETS "Usage of libwebsockets instead of websockets++" OFF)

target_include_directories(ocpp
PUBLIC
Expand Down Expand Up @@ -103,13 +104,25 @@ target_link_libraries(ocpp
date::date-tz
)

if(LIBOCPP_ENABLE_LIBWEBSOCKETS)
find_package(libwebsockets REQUIRED)
target_link_libraries(ocpp
PUBLIC
websockets_shared
)
target_compile_definitions(ocpp
PRIVATE
LIBOCPP_ENABLE_LIBWEBSOCKETS
)
endif()

if(LIBOCPP_USE_BOOST_FILESYSTEM)
find_package(Boost REQUIRED COMPONENTS filesystem)
target_link_libraries(ocpp
PRIVATE
Boost::filesystem
)
target_compile_definitions(log
target_compile_definitions(ocpp
PRIVATE
LIBOCPP_USE_BOOST_FILESYSTEM
)
Expand Down
7 changes: 7 additions & 0 deletions lib/ocpp/common/websocket/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -7,3 +7,10 @@ target_sources(ocpp
websocket_tls.cpp
websocket.cpp
)

if(LIBOCPP_ENABLE_LIBWEBSOCKETS)
target_sources(ocpp
PRIVATE
websocket_tls_tpm.cpp
)
endif()
9 changes: 9 additions & 0 deletions lib/ocpp/common/websocket/websocket.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,10 @@
#include <ocpp/common/websocket/websocket.hpp>
#include <ocpp/v16/types.hpp>

#ifdef LIBOCPP_ENABLE_LIBWEBSOCKETS
#include <ocpp/common/websocket/websocket_tls_tpm.hpp>
#endif

#include <boost/algorithm/string.hpp>

using json = nlohmann::json;
Expand All @@ -14,11 +18,16 @@ namespace ocpp {
Websocket::Websocket(const WebsocketConnectionOptions& connection_options, std::shared_ptr<EvseSecurity> evse_security,
std::shared_ptr<MessageLogging> logging) :
logging(logging) {

#ifdef LIBOCPP_ENABLE_LIBWEBSOCKETS
this->websocket = std::make_unique<WebsocketTlsTPM>(connection_options, evse_security);
#else
if (connection_options.security_profile <= 1) {
this->websocket = std::make_unique<WebsocketPlain>(connection_options);
} else if (connection_options.security_profile >= 2) {
this->websocket = std::make_unique<WebsocketTLS>(connection_options, evse_security);
}
#endif
}

Websocket::~Websocket() {
Expand Down
Loading

0 comments on commit cf644b8

Please sign in to comment.