diff --git a/include/zephyr/net/ocpp.h b/include/zephyr/net/ocpp.h index b34ef5fb6eeea..00788a0306265 100644 --- a/include/zephyr/net/ocpp.h +++ b/include/zephyr/net/ocpp.h @@ -24,6 +24,10 @@ #include #include +#if defined(CONFIG_NET_SOCKETS_SOCKOPT_TLS) +#include +#endif + #ifdef __cplusplus extern "C" { #endif @@ -92,7 +96,6 @@ enum ocpp_meter_measurand { * is valid */ union ocpp_io_value { - struct { /** Input to user, requested connector_id or 0 - main meter */ int id_con; @@ -139,12 +142,25 @@ struct ocpp_cp_info { char *meter_type; /**< Main power meter type */ }; +#if defined(CONFIG_NET_SOCKETS_SOCKOPT_TLS) +/** @brief TLS credentials information of the central system (CS) */ +struct ocpp_tls_credentials { + const sec_tag_t *sec_tag_list; /**< TLS Sec tag list */ + char *tls_hostname; /**< TLS Hostname */ + size_t sec_tag_list_size; /**< Size of TLS Sec tag list */ + size_t tls_hostname_size; /**< Size of TLS Hostname */ +}; +#endif + /** @brief Parameters for ocpp_init information about central system (CS) */ struct ocpp_cs_info { char *cs_ip; /**< Central system IP address */ char *ws_url; /**< Websocket url exclude ipaddr & port */ int port; /**< Central system port number */ sa_family_t sa_family; /** IP protocol family type 4/6 */ +#if defined(CONFIG_NET_SOCKETS_SOCKOPT_TLS) + struct ocpp_tls_credentials creds; /**< Central system TLS Credentials */ +#endif }; /** @brief Parameters opaque session handle for ocpp_* API */ diff --git a/samples/net/ocpp/CMakeLists.txt b/samples/net/ocpp/CMakeLists.txt index ecb7d24bb8ff7..af671e752b99c 100644 --- a/samples/net/ocpp/CMakeLists.txt +++ b/samples/net/ocpp/CMakeLists.txt @@ -3,6 +3,20 @@ cmake_minimum_required(VERSION 3.20.0) find_package(Zephyr REQUIRED HINTS $ENV{ZEPHYR_BASE}) -project(hello_world) +project(ocpp) target_sources(app PRIVATE src/main.c) +set(gen_dir ${ZEPHYR_BINARY_DIR}/include/generated/) + +function(pem_to_mbedtls target input_file_fullpath) + get_filename_component(input_file ${input_file_fullpath} NAME) + file(READ ${input_file_fullpath} input_file_content) + string(REGEX REPLACE "\n" "\\\\n" input_file_content "${input_file_content}") + set(GENERATED_FILE ${gen_dir}/${input_file}.inc) + file(WRITE ${GENERATED_FILE} "\"${input_file_content}\"\n") + generate_unique_target_name_from_filename(${input_file} generated_target_name) + add_custom_target(${generated_target_name} DEPENDS ${GENERATED_FILE}) + add_dependencies(${target} ${generated_target_name}) +endfunction() + +pem_to_mbedtls(app ${ZEPHYR_BASE}/samples/net/ocpp/src/credentials/ec.crt) diff --git a/samples/net/ocpp/overlay-ec.conf b/samples/net/ocpp/overlay-ec.conf new file mode 100644 index 0000000000000..c109147eb8e50 --- /dev/null +++ b/samples/net/ocpp/overlay-ec.conf @@ -0,0 +1,7 @@ +CONFIG_PSA_WANT_ALG_ECDH=y +CONFIG_PSA_WANT_ALG_ECDSA=y +CONFIG_PSA_WANT_KEY_TYPE_ECC_KEY_PAIR_GENERATE=y +CONFIG_PSA_WANT_KEY_TYPE_ECC_KEY_PAIR_IMPORT=y +CONFIG_PSA_WANT_KEY_TYPE_ECC_KEY_PAIR_EXPORT=y +CONFIG_PSA_WANT_KEY_TYPE_ECC_KEY_PAIR_DERIVE=y +CONFIG_PSA_WANT_ECC_SECP_R1_256=y diff --git a/samples/net/ocpp/overlay-tls13.conf b/samples/net/ocpp/overlay-tls13.conf new file mode 100644 index 0000000000000..8e881223daee9 --- /dev/null +++ b/samples/net/ocpp/overlay-tls13.conf @@ -0,0 +1,38 @@ +# Enable Secure Sockets +CONFIG_NET_SOCKETS_SOCKOPT_TLS=y + +# Mbed TLS configuration +CONFIG_MBEDTLS=y +CONFIG_MBEDTLS_BUILTIN=y +CONFIG_MBEDTLS_ENABLE_HEAP=y +CONFIG_MBEDTLS_HEAP_SIZE=20000 +CONFIG_MBEDTLS_SSL_MAX_CONTENT_LEN=2048 +CONFIG_MBEDTLS_PEM_CERTIFICATE_FORMAT=y +CONFIG_MBEDTLS_SERVER_NAME_INDICATION=y +# Build the PSA Crypto core so that the TLS stack uses the PSA crypto API. +CONFIG_MBEDTLS_PSA_CRYPTO_C=y +CONFIG_ENTROPY_GENERATOR=y + +# Disable some Kconfigs that are implied by CONFIG_NET_SOCKETS_SOCKOPT_TLS. +CONFIG_MBEDTLS_TLS_VERSION_1_2=n +CONFIG_MBEDTLS_KEY_EXCHANGE_RSA_ENABLED=n +CONFIG_MBEDTLS_MD=n +CONFIG_MBEDTLS_RSA_C=n +CONFIG_MBEDTLS_CIPHER_AES_ENABLED=n +CONFIG_PSA_WANT_KEY_TYPE_AES=n +CONFIG_PSA_WANT_ALG_CBC_NO_PADDING=n + +# Debug log options (optional) +# CONFIG_MBEDTLS_LOG_LEVEL_DBG=y +# CONFIG_MBEDTLS_DEBUG=y + +# TLS1.3 Configuration +CONFIG_MBEDTLS_TLS_VERSION_1_3=y +CONFIG_PSA_WANT_ALG_HKDF_EXTRACT=y +CONFIG_PSA_WANT_ALG_HKDF_EXPAND=y +CONFIG_PSA_WANT_ALG_GCM=y +CONFIG_PSA_WANT_KEY_TYPE_AES=y +CONFIG_PSA_WANT_ALG_CBC_NO_PADDING=y +CONFIG_PSA_WANT_ALG_SHA_256=y +CONFIG_PSA_WANT_ALG_SHA_384=y +CONFIG_MBEDTLS_SSL_TLS1_3_KEY_EXCHANGE_MODE_EPHEMERAL_ENABLED=y diff --git a/samples/net/ocpp/prj.conf b/samples/net/ocpp/prj.conf index 502096c889889..758621ae82f69 100644 --- a/samples/net/ocpp/prj.conf +++ b/samples/net/ocpp/prj.conf @@ -1,4 +1,3 @@ -# HTTP & Websocket CONFIG_HTTP_CLIENT=y CONFIG_WEBSOCKET_CLIENT=y @@ -12,7 +11,12 @@ CONFIG_NET_SOCKETS_POLL_MAX=4 CONFIG_NET_L2_ETHERNET=y CONFIG_ETH_DRIVER=y CONFIG_LOG=y -CONFIG_NET_LOG=y + +CONFIG_LOG_MODE_IMMEDIATE=y + +# Networking debug (Optional) +CONFIG_NET_LOG=n +CONFIG_NET_SOCKETS_LOG_LEVEL_DBG=n # OCPP CONFIG_OCPP=y @@ -20,12 +24,12 @@ CONFIG_OCPP=y # Please set the server addresses when compiling the sample CONFIG_NET_SAMPLE_SNTP_SERVER="" CONFIG_NET_SAMPLE_OCPP_SERVER="" -CONFIG_NET_SAMPLE_OCPP_PORT=8180 +CONFIG_NET_SAMPLE_OCPP_PORT=8443 CONFIG_MAIN_STACK_SIZE=4096 CONFIG_HEAP_MEM_POOL_SIZE=15000 -CONFIG_JSONC=y +CONFIG_JSON_LIBRARY=y CONFIG_PICOLIBC=y CONFIG_NET_ARP=y CONFIG_NET_UDP=y @@ -44,5 +48,10 @@ CONFIG_SNTP=y CONFIG_ZBUS=y CONFIG_ZBUS_RUNTIME_OBSERVERS=y -CONFIG_POSIX_CLOCK=y + CONFIG_POSIX_API=y +CONFIG_POSIX_CLOCK_SELECTION=y + +# Logging +CONFIG_LOG=y +CONFIG_PRINTK=y diff --git a/samples/net/ocpp/src/credentials/certificate.h b/samples/net/ocpp/src/credentials/certificate.h new file mode 100644 index 0000000000000..c0c03ad711325 --- /dev/null +++ b/samples/net/ocpp/src/credentials/certificate.h @@ -0,0 +1,11 @@ +#ifndef __CERTIFICATE_H__ +#define __CERTIFICATE_H__ + +#define CA_CERTIFICATE_TAG 1 +#define TLS_PEER_HOSTNAME "localhost" + +static const unsigned char ca_certificate[] = { +#include "ec.crt.inc" +}; + +#endif diff --git a/samples/net/ocpp/src/credentials/ec-priv.key b/samples/net/ocpp/src/credentials/ec-priv.key new file mode 100644 index 0000000000000..782c19d3fa0f8 --- /dev/null +++ b/samples/net/ocpp/src/credentials/ec-priv.key @@ -0,0 +1,5 @@ +-----BEGIN PRIVATE KEY----- +MIGHAgEAMBMGByqGSM49AgEGCCqGSM49AwEHBG0wawIBAQQgwecn6Plv8ONZs2cF +IIlr4B4xaoPYEMHm9mSG4esgQ2uhRANCAARVR4/COFJhHmmGdERod/1DhM5hBcq/ +xQHuUtxC1a977tMFCzINWTy155+/E8uj35FUhsLeFMoyGtgvKHKIBpgt +-----END PRIVATE KEY----- diff --git a/samples/net/ocpp/src/credentials/ec.crt b/samples/net/ocpp/src/credentials/ec.crt new file mode 100644 index 0000000000000..8ee9a3598fe06 --- /dev/null +++ b/samples/net/ocpp/src/credentials/ec.crt @@ -0,0 +1,10 @@ +-----BEGIN CERTIFICATE----- +MIIBfDCCASOgAwIBAgIUW0crZnSm9CwlYmnYdDSohFSG5UwwCgYIKoZIzj0EAwIw +FDESMBAGA1UEAwwJbG9jYWxob3N0MB4XDTI0MDgyNzA5NDcxN1oXDTM0MDgyNTA5 +NDcxN1owFDESMBAGA1UEAwwJbG9jYWxob3N0MFkwEwYHKoZIzj0CAQYIKoZIzj0D +AQcDQgAEVUePwjhSYR5phnREaHf9Q4TOYQXKv8UB7lLcQtWve+7TBQsyDVk8teef +vxPLo9+RVIbC3hTKMhrYLyhyiAaYLaNTMFEwHQYDVR0OBBYEFDi6b5XH5Z5d4cSe +S5OVBHaWjB8SMB8GA1UdIwQYMBaAFDi6b5XH5Z5d4cSeS5OVBHaWjB8SMA8GA1Ud +EwEB/wQFMAMBAf8wCgYIKoZIzj0EAwIDRwAwRAIgMEnVFWqIHRphQtWn5CbXomkH +H/mDhf4ux5k55dmRRH8CIFwL0gYBrp26n0AsRSpVN1RroAt7M1MpCgEycVr3QNMQ +-----END CERTIFICATE----- diff --git a/samples/net/ocpp/src/main.c b/samples/net/ocpp/src/main.c index 87ad2a7405d72..e3e2a67d5dd8f 100644 --- a/samples/net/ocpp/src/main.c +++ b/samples/net/ocpp/src/main.c @@ -16,6 +16,10 @@ #include #include +#if defined(CONFIG_NET_SOCKETS_SOCKOPT_TLS) +#include "credentials/certificate.h" +#endif + LOG_MODULE_REGISTER(main, LOG_LEVEL_INF); #define NO_OF_CONN 2 @@ -207,6 +211,7 @@ static void ocpp_cp_entry(void *p1, void *p2, void *p3) /* Avoid quick retry since authorization request is possible only * after Bootnotification process (handled in lib) completed. */ + k_sleep(K_SECONDS(5)); ret = ocpp_authorize(sh, idtag, @@ -220,6 +225,7 @@ static void ocpp_cp_entry(void *p1, void *p2, void *p3) ret, idcon, status); break; } + } if (status != OCPP_AUTH_ACCEPTED) { @@ -331,9 +337,9 @@ int main(void) struct ocpp_cp_info cpi = { "basic", "zephyr", .num_of_con = NO_OF_CONN }; struct ocpp_cs_info csi = { NULL, - "/steve/websocket/CentralSystemService/zephyr", - CONFIG_NET_SAMPLE_OCPP_PORT, - AF_INET }; + "/steve/websocket/CentralSystemService/basic", + CONFIG_NET_SAMPLE_OCPP_PORT, + AF_INET}; printk("OCPP sample %s\n", CONFIG_BOARD); @@ -353,6 +359,24 @@ int main(void) ocpp_get_time_from_sntp(); +#if defined(CONFIG_NET_SOCKETS_SOCKOPT_TLS) + sec_tag_t tags[] = {CA_CERTIFICATE_TAG}; + + ret = tls_credential_add(CA_CERTIFICATE_TAG, + TLS_CREDENTIAL_CA_CERTIFICATE, + ca_certificate, + sizeof(ca_certificate)); + if (ret < 0) { + LOG_ERR("Failed to register CA certificate: %d", ret); + return ret; + } + + csi.creds.sec_tag_list = tags; + csi.creds.tls_hostname = TLS_PEER_HOSTNAME; + csi.creds.sec_tag_list_size = sizeof(tags); + csi.creds.tls_hostname_size = strlen(TLS_PEER_HOSTNAME); +#endif + ret = ocpp_init(&cpi, &csi, user_notify_cb, diff --git a/subsys/net/lib/ocpp/ocpp.c b/subsys/net/lib/ocpp/ocpp.c index 7c668374e990d..5e9d0ca238d9d 100644 --- a/subsys/net/lib/ocpp/ocpp.c +++ b/subsys/net/lib/ocpp/ocpp.c @@ -75,12 +75,12 @@ void ocpp_get_utc_now(char utc[CISTR25]) gmtime_r(&tv.tv_sec, &htime); snprintk(utc, CISTR25, "%04d-%02d-%02dT%02d:%02d:%02dZ", - htime.tm_year + 1900, - htime.tm_mon + 1, - htime.tm_mday, - htime.tm_hour, - htime.tm_min, - htime.tm_sec); + htime.tm_year + 1900, + htime.tm_mon + 1, + htime.tm_mday, + htime.tm_hour, + htime.tm_min, + htime.tm_sec); } bool ocpp_session_is_valid(struct ocpp_session *sh) @@ -173,7 +173,6 @@ static int ocpp_connect_to_cs(struct ocpp_info *ctx) } ui->wssock = ret; } - LOG_DBG("WS connect success %d", ui->wssock); return 0; } @@ -309,7 +308,7 @@ static int ocpp_process_server_msg(struct ocpp_info *ctx) if (is_rsp) { buf = strtok_r(uid, "-", &tmp); - sh = (struct ocpp_session *) atoi(buf); + sh = (struct ocpp_session *)atoi(buf); buf = strtok_r(NULL, "-", &tmp); pdu = atoi(buf); @@ -460,14 +459,13 @@ static void ocpp_wsreader(void *p1, void *p2, void *p3) ctx->is_cs_offline = true; tcpfd.fd = ui->tcpsock; + tcpfd.events = ZSOCK_POLLIN | ZSOCK_POLLERR | ZSOCK_POLLHUP | ZSOCK_POLLNVAL; while (1) { - if (ctx->is_cs_offline && !(retry_cnt++ % TCP_CONNECT_AFTER)) { - k_mutex_lock(&ctx->ilock, K_FOREVER); ocpp_connect_to_cs(ctx); k_mutex_unlock(&ctx->ilock); @@ -507,19 +505,47 @@ static void ocpp_wsreader(void *p1, void *p2, void *p3) int ocpp_upstream_init(struct ocpp_info *ctx, struct ocpp_cs_info *csi) { + int ret; struct ocpp_upstream_info *ui = &ctx->ui; - LOG_INF("upstream init"); - ui->csi.ws_url = strdup(csi->ws_url); ui->csi.cs_ip = strdup(csi->cs_ip); ui->csi.port = csi->port; ui->csi.sa_family = csi->sa_family; - ui->tcpsock = zsock_socket(csi->sa_family, SOCK_STREAM, - IPPROTO_TCP); + + LOG_INF("upstream init"); + +#if defined(CONFIG_NET_SOCKETS_SOCKOPT_TLS) + ui->csi.creds.sec_tag_list = csi->creds.sec_tag_list; + ui->csi.creds.sec_tag_list_size = csi->creds.sec_tag_list_size; + ui->csi.creds.tls_hostname = csi->creds.tls_hostname; + + ui->tcpsock = zsock_socket(csi->sa_family, SOCK_STREAM, IPPROTO_TLS_1_3); + if (ui->tcpsock < 0) { + LOG_ERR("Failed to create TLS socket: %d", errno); + return -errno; + } + + ret = zsock_setsockopt(ui->tcpsock, SOL_TLS, TLS_SEC_TAG_LIST, csi->creds.sec_tag_list, + csi->creds.sec_tag_list_size); + if (ret < 0) { + LOG_ERR("Failed to set TLS_SEC_TAG_LIST: %d", -errno); + goto fail; + } + + ret = zsock_setsockopt(ui->tcpsock, SOL_TLS, TLS_HOSTNAME, csi->creds.tls_hostname, + csi->creds.tls_hostname_size); + if (ret < 0) { + LOG_ERR("Failed to set TLS_HOSTNAME: %d", -errno); + goto fail; + } +#else + ui->tcpsock = zsock_socket(csi->sa_family, SOCK_STREAM, IPPROTO_TCP); if (ui->tcpsock < 0) { + LOG_ERR("Failed to create TCP socket: %d", -errno); return -errno; } +#endif k_mutex_init(&ui->ws_sndlock); k_poll_signal_init(&ui->ws_rspsig); @@ -530,9 +556,16 @@ int ocpp_upstream_init(struct ocpp_info *ctx, struct ocpp_cs_info *csi) k_thread_create(&ui->tinfo, ocpp_wsreader_stack, CONFIG_OCPP_WSREADER_THREAD_STACKSIZE, ocpp_wsreader, ctx, NULL, NULL, - OCPP_UPSTREAM_PRIORITY, 0, K_MSEC(100)); + OCPP_UPSTREAM_PRIORITY, 0, K_MSEC(100)); return 0; + +fail: + if (ui->tcpsock >= 0) { + zsock_close(ui->tcpsock); + ui->tcpsock = -1; + } + return -errno; } static void timer_heartbeat_cb(struct k_timer *t)