From df1c36a1da3460b954cbbca5fa50b23e09dc68ea Mon Sep 17 00:00:00 2001 From: pennam Date: Mon, 5 Feb 2024 21:50:40 +0100 Subject: [PATCH 1/5] Add WiFi command to configure ECC slot - This is used to configure client certificate and client key in the ssl client The certificate is sent as buffer via AT command and the key is read from secure element nvs namespace --- UNOR4USBBridge/at_handler.cpp | 2 +- UNOR4USBBridge/at_handler.h | 2 + UNOR4USBBridge/cmds_wifi_SSL.h | 101 +++++++++++++++++++++++++++++++++ UNOR4USBBridge/commands.h | 1 + 4 files changed, 105 insertions(+), 1 deletion(-) diff --git a/UNOR4USBBridge/at_handler.cpp b/UNOR4USBBridge/at_handler.cpp index 433902c..3a9a3dc 100644 --- a/UNOR4USBBridge/at_handler.cpp +++ b/UNOR4USBBridge/at_handler.cpp @@ -8,11 +8,11 @@ #include "cmds_wifi_station.h" #include "cmds_wifi_softAP.h" #include "cmds_wifi_netif.h" +#include "cmds_preferences.h" #include "cmds_wifi_SSL.h" #include "cmds_wifi_udp.h" #include "cmds_ble_bridge.h" #include "cmds_ota.h" -#include "cmds_preferences.h" #include "cmds_se.h" using namespace SudoMaker; diff --git a/UNOR4USBBridge/at_handler.h b/UNOR4USBBridge/at_handler.h index 765a682..d7811b7 100644 --- a/UNOR4USBBridge/at_handler.h +++ b/UNOR4USBBridge/at_handler.h @@ -87,6 +87,8 @@ class CAtHandler { public: std::vector cert_buf; std::vector se_buf; + std::vector client_cert_pem; + std::vector client_key_pem; CAtHandler(HardwareSerial *s); CAtHandler() = delete ; static void onWiFiEvent(WiFiEvent_t event); diff --git a/UNOR4USBBridge/cmds_wifi_SSL.h b/UNOR4USBBridge/cmds_wifi_SSL.h index eff4912..b05bd75 100644 --- a/UNOR4USBBridge/cmds_wifi_SSL.h +++ b/UNOR4USBBridge/cmds_wifi_SSL.h @@ -11,6 +11,8 @@ INCBIN(x509_crt_bundle, PATH_CERT_BUNDLE); #endif #include "at_handler.h" +#include "mbedtls/pem.h" +#include "SSE.h" #ifndef WIFI_CLIENT_DEF_CONN_TIMEOUT_MS #define WIFI_CLIENT_DEF_CONN_TIMEOUT_MS (3000) @@ -110,6 +112,105 @@ void CAtHandler::add_cmds_wifi_SSL() { return chAT::CommandStatus::ERROR; } }; + + /* ....................................................................... */ + command_table[_SETECCSLOT] = [this](auto & srv, auto & parser) { + /* ....................................................................... */ + switch (parser.cmd_mode) { + case chAT::CommandMode::Write: { + if (parser.args.size() != 3) { + return chAT::CommandStatus::ERROR; + } + + auto &sock_num = parser.args[0]; + auto &slot_num = parser.args[1]; + auto &cert_len = parser.args[2]; + if (sock_num.empty() || slot_num.empty() || cert_len.empty()) { + return chAT::CommandStatus::ERROR; + } + + int sock = atoi(sock_num.c_str()); + int size = atoi(cert_len.c_str()); + + CClientWrapper the_client = getClient(sock); + if (the_client.sslclient == nullptr) { + return chAT::CommandStatus::ERROR; + } + + std::vector client_cert_der; + client_cert_der = srv.inhibit_read(size); + size_t offset = client_cert_der.size(); + + if(offset < size) { + client_cert_der.resize(size); + do { + offset += serial->read(client_cert_der.data() + offset, size - offset); + } while (offset < size); + } + srv.continue_read(); + +#if ECC_DEBUG_ENABLED + log_v("_SETECCSLOT: input cert"); + log_buf_v((const uint8_t *)client_cert_der.data(), size); +#endif + + /* Convert client certificate DER buffer into PEM */ + client_cert_pem.resize(1024); + size_t olen; + mbedtls_pem_write_buffer("-----BEGIN CERTIFICATE-----\n", + "-----END CERTIFICATE-----\n", + client_cert_der.data(), size, + client_cert_pem.data(), 1024, + &olen); + client_cert_pem.resize(olen); + +#if ECC_DEBUG_ENABLED + log_v("_SETECCSLOT: output cert"); + log_v("\n%s", client_cert_pem.data()); +#endif + + /* Set client certificate */ + the_client.sslclient->setCertificate((const char *)client_cert_pem.data()); + + /* Read private key from non volatile storage in DER format */ + std::vector client_key_der; + int len = sse.getBytesLength(slot_num.c_str()); + int ret = -1; + client_key_der.resize(len); + if ((ret = sse.getBytes(slot_num.c_str(), client_key_der.data(), len)) < len) { + log_e(" failed\n ! sse.getBytes returned -0x%04x", (unsigned int) -ret); + return chAT::CommandStatus::ERROR; + } + +#if ECC_DEBUG_ENABLED + log_v("_SETECCSLOT: input key"); + log_buf_v((const uint8_t *)client_key_der.data(), ret); +#endif + + /* Convert private key in PEM format */ + client_key_pem.resize(1024); + mbedtls_pem_write_buffer("-----BEGIN EC PRIVATE KEY-----\n", + "-----END EC PRIVATE KEY-----\n", + client_key_der.data(), len, + client_key_pem.data(), 1024, + &olen); + client_key_pem.resize(olen); + +#if ECC_DEBUG_ENABLED + log_v("_SETECCSLOT: output key"); + log_v("\n%s", client_key_pem.data()); +#endif + + /* Set client key */ + the_client.sslclient->setPrivateKey((const char *)client_key_pem.data()); + + return chAT::CommandStatus::OK; + } + default: + return chAT::CommandStatus::ERROR; + } + }; + /* ....................................................................... */ command_table[_SSLCLIENTSTATE] = [this](auto & srv, auto & parser) { /* ....................................................................... */ diff --git a/UNOR4USBBridge/commands.h b/UNOR4USBBridge/commands.h index b1b7ba3..ffa9555 100644 --- a/UNOR4USBBridge/commands.h +++ b/UNOR4USBBridge/commands.h @@ -58,6 +58,7 @@ enum file_op { #define _CLIENTCONNECTED "+CLIENTCONNECTED" #define _SSLBEGINCLIENT "+SSLBEGINCLIENT" #define _SETCAROOT "+SETCAROOT" +#define _SETECCSLOT "+SETECCSLOT" #define _SSLCLIENTSTATE "+SSLCLIENTSTATE" #define _SSLCLIENTCONNECTNAME "+SSLCLIENTCONNECTNAME" #define _SSLCLIENTCONNECT "+SSLCLIENTCONNECT" From 9824da7f7a16044019e7eb5a24925a1631f0453d Mon Sep 17 00:00:00 2001 From: pennam Date: Thu, 8 Feb 2024 14:32:13 +0100 Subject: [PATCH 2/5] at_handler use dedicated buffer for preferences - avoid cert_buf overwriting using preferences and custom ca --- UNOR4USBBridge/at_handler.h | 4 ++++ UNOR4USBBridge/cmds_preferences.h | 10 +++++----- 2 files changed, 9 insertions(+), 5 deletions(-) diff --git a/UNOR4USBBridge/at_handler.h b/UNOR4USBBridge/at_handler.h index d7811b7..aff2b93 100644 --- a/UNOR4USBBridge/at_handler.h +++ b/UNOR4USBBridge/at_handler.h @@ -89,6 +89,10 @@ class CAtHandler { std::vector se_buf; std::vector client_cert_pem; std::vector client_key_pem; + + /* Used by cmds_preferences */ + std::vector pref_buf; + CAtHandler(HardwareSerial *s); CAtHandler() = delete ; static void onWiFiEvent(WiFiEvent_t event); diff --git a/UNOR4USBBridge/cmds_preferences.h b/UNOR4USBBridge/cmds_preferences.h index 0c6e4c0..c8c1e38 100644 --- a/UNOR4USBBridge/cmds_preferences.h +++ b/UNOR4USBBridge/cmds_preferences.h @@ -171,16 +171,16 @@ void CAtHandler::add_cmds_preferences() { break; case PreferenceType::PT_BLOB: { int value = atoi(parser.args[2].c_str()); - cert_buf = srv.inhibit_read(value); - size_t offset = cert_buf.size(); + pref_buf = srv.inhibit_read(value); + size_t offset = pref_buf.size(); if(offset < value) { - cert_buf.resize(value); + pref_buf.resize(value); do { - offset += serial->read(cert_buf.data() + offset, value - offset); + offset += serial->read(pref_buf.data() + offset, value - offset); } while (offset < value); } srv.continue_read(); - error = String(pref.putBytes(key.c_str(), cert_buf.data(), value)) + "\r\n"; + error = String(pref.putBytes(key.c_str(), pref_buf.data(), value)) + "\r\n"; } break; default: From 706a8f6f8b81e04437b3b94f25cb6fa601b55a1c Mon Sep 17 00:00:00 2001 From: pennam Date: Thu, 8 Feb 2024 14:34:42 +0100 Subject: [PATCH 3/5] at_handler use dedicated buffer for ota - avoid cert_buf overwriting using ota and custom ca --- UNOR4USBBridge/at_handler.h | 3 +++ UNOR4USBBridge/cmds_ota.h | 10 +++++----- 2 files changed, 8 insertions(+), 5 deletions(-) diff --git a/UNOR4USBBridge/at_handler.h b/UNOR4USBBridge/at_handler.h index aff2b93..43f3432 100644 --- a/UNOR4USBBridge/at_handler.h +++ b/UNOR4USBBridge/at_handler.h @@ -90,6 +90,9 @@ class CAtHandler { std::vector client_cert_pem; std::vector client_key_pem; + /* Used by cmds_ota */ + std::vector ota_cert_buf; + /* Used by cmds_preferences */ std::vector pref_buf; diff --git a/UNOR4USBBridge/cmds_ota.h b/UNOR4USBBridge/cmds_ota.h index 8b2ce11..de1f7f7 100644 --- a/UNOR4USBBridge/cmds_ota.h +++ b/UNOR4USBBridge/cmds_ota.h @@ -27,15 +27,15 @@ void CAtHandler::add_cmds_ota() { return chAT::CommandStatus::ERROR; } - cert_buf = srv.inhibit_read(ca_root_size); - size_t offset = cert_buf.size(); + ota_cert_buf = srv.inhibit_read(ca_root_size); + size_t offset = ota_cert_buf.size(); if(offset < ca_root_size) { - cert_buf.resize(ca_root_size); + ota_cert_buf.resize(ca_root_size); do { - offset += serial->read(cert_buf.data() + offset, ca_root_size - offset); + offset += serial->read(ota_cert_buf.data() + offset, ca_root_size - offset); } while (offset < ca_root_size); } - OTA.setCACert((const char *)cert_buf.data()); + OTA.setCACert((const char *)ota_cert_buf.data()); srv.continue_read(); return chAT::CommandStatus::OK; } From a6aadef49970118fe452be8dd5ee5f9e48e202ec Mon Sep 17 00:00:00 2001 From: pennam Date: Thu, 8 Feb 2024 14:39:30 +0100 Subject: [PATCH 4/5] wifi ssl: properly handle multiple client certificates --- UNOR4USBBridge/at_handler.cpp | 3 + UNOR4USBBridge/at_handler.h | 7 ++- UNOR4USBBridge/cmds_wifi_SSL.h | 105 +++++++++++++++++++++++++++------ 3 files changed, 95 insertions(+), 20 deletions(-) diff --git a/UNOR4USBBridge/at_handler.cpp b/UNOR4USBBridge/at_handler.cpp index 3a9a3dc..fc08c75 100644 --- a/UNOR4USBBridge/at_handler.cpp +++ b/UNOR4USBBridge/at_handler.cpp @@ -92,6 +92,9 @@ CAtHandler::CAtHandler(HardwareSerial *s) : last_server_client_sock(0) { for(int i = 0; i < MAX_CLIENT_AVAILABLE; i++) { sslclients[i] = nullptr; + clients_ca[i].clear(); + clients_cert_pem[i].clear(); + clients_key_pem[i].clear(); } /* set up serial */ diff --git a/UNOR4USBBridge/at_handler.h b/UNOR4USBBridge/at_handler.h index 43f3432..a47588b 100644 --- a/UNOR4USBBridge/at_handler.h +++ b/UNOR4USBBridge/at_handler.h @@ -63,6 +63,9 @@ class CAtHandler { WiFiClient * clients[MAX_CLIENT_AVAILABLE]; CServerClient serverClients[MAX_CLIENT_AVAILABLE]; WiFiClientSecure * sslclients[MAX_CLIENT_AVAILABLE]; + std::vector clients_ca[MAX_CLIENT_AVAILABLE]; + std::vector clients_cert_pem[MAX_CLIENT_AVAILABLE]; + std::vector clients_key_pem[MAX_CLIENT_AVAILABLE]; int udps_num = 0; int servers_num = 0; int clientsToServer_num = 0; @@ -85,10 +88,8 @@ class CAtHandler { void add_cmds_preferences(); void add_cmds_se(); public: - std::vector cert_buf; + /* Used by cmds_se */ std::vector se_buf; - std::vector client_cert_pem; - std::vector client_key_pem; /* Used by cmds_ota */ std::vector ota_cert_buf; diff --git a/UNOR4USBBridge/cmds_wifi_SSL.h b/UNOR4USBBridge/cmds_wifi_SSL.h index b05bd75..e3f6f72 100644 --- a/UNOR4USBBridge/cmds_wifi_SSL.h +++ b/UNOR4USBBridge/cmds_wifi_SSL.h @@ -68,6 +68,11 @@ void CAtHandler::add_cmds_wifi_SSL() { return chAT::CommandStatus::ERROR; } + const int internal_sock = the_client.can_delete; + if (internal_sock == -1) { + return chAT::CommandStatus::ERROR; + } + bool ca_root_custom = false; int ca_root_size = 0; if (parser.args.size() >= 2){ @@ -80,19 +85,17 @@ void CAtHandler::add_cmds_wifi_SSL() { } if(ca_root_custom) { - - - cert_buf = srv.inhibit_read(ca_root_size); - size_t offset = cert_buf.size(); + clients_ca[internal_sock] = srv.inhibit_read(ca_root_size); + size_t offset = clients_ca[internal_sock].size(); if(offset < ca_root_size) { - cert_buf.resize(ca_root_size); + clients_ca[internal_sock].resize(ca_root_size); do { - offset += serial->read(cert_buf.data() + offset, ca_root_size - offset); + offset += serial->read(clients_ca[internal_sock].data() + offset, ca_root_size - offset); } while (offset < ca_root_size); } - the_client.sslclient->setCACert((const char *)cert_buf.data()); + the_client.sslclient->setCACert((const char *)clients_ca[internal_sock].data()); srv.continue_read(); } else { #ifdef BUNDLED_CA_ROOT_CRT @@ -137,6 +140,11 @@ void CAtHandler::add_cmds_wifi_SSL() { return chAT::CommandStatus::ERROR; } + const int internal_sock = the_client.can_delete; + if (internal_sock == -1) { + return chAT::CommandStatus::ERROR; + } + std::vector client_cert_der; client_cert_der = srv.inhibit_read(size); size_t offset = client_cert_der.size(); @@ -155,22 +163,22 @@ void CAtHandler::add_cmds_wifi_SSL() { #endif /* Convert client certificate DER buffer into PEM */ - client_cert_pem.resize(1024); + clients_cert_pem[internal_sock].resize(1024); size_t olen; mbedtls_pem_write_buffer("-----BEGIN CERTIFICATE-----\n", "-----END CERTIFICATE-----\n", client_cert_der.data(), size, - client_cert_pem.data(), 1024, + clients_cert_pem[internal_sock].data(), 1024, &olen); - client_cert_pem.resize(olen); + clients_cert_pem[internal_sock].resize(olen); #if ECC_DEBUG_ENABLED log_v("_SETECCSLOT: output cert"); - log_v("\n%s", client_cert_pem.data()); + log_v("\n%s", clients_cert_pem[internal_sock].data()); #endif /* Set client certificate */ - the_client.sslclient->setCertificate((const char *)client_cert_pem.data()); + the_client.sslclient->setCertificate((const char *)clients_cert_pem[internal_sock].data()); /* Read private key from non volatile storage in DER format */ std::vector client_key_der; @@ -188,21 +196,21 @@ void CAtHandler::add_cmds_wifi_SSL() { #endif /* Convert private key in PEM format */ - client_key_pem.resize(1024); + clients_key_pem[internal_sock].resize(1024); mbedtls_pem_write_buffer("-----BEGIN EC PRIVATE KEY-----\n", "-----END EC PRIVATE KEY-----\n", client_key_der.data(), len, - client_key_pem.data(), 1024, + clients_key_pem[internal_sock].data(), 1024, &olen); - client_key_pem.resize(olen); + clients_key_pem[internal_sock].resize(olen); #if ECC_DEBUG_ENABLED log_v("_SETECCSLOT: output key"); - log_v("\n%s", client_key_pem.data()); + log_v("\n%s", clients_key_pem[internal_sock].data()); #endif /* Set client key */ - the_client.sslclient->setPrivateKey((const char *)client_key_pem.data()); + the_client.sslclient->setPrivateKey((const char *)clients_key_pem[internal_sock].data()); return chAT::CommandStatus::OK; } @@ -267,6 +275,11 @@ void CAtHandler::add_cmds_wifi_SSL() { return chAT::CommandStatus::ERROR; } + const int internal_sock = the_client.can_delete; + if (internal_sock == -1) { + return chAT::CommandStatus::ERROR; + } + auto &host = parser.args[1]; if (host.empty()) { return chAT::CommandStatus::ERROR; @@ -277,6 +290,21 @@ void CAtHandler::add_cmds_wifi_SSL() { return chAT::CommandStatus::ERROR; } + /* Set custom root ca */ + if (clients_ca[internal_sock].size()) { + the_client.sslclient->setCACert((const char *)clients_ca[internal_sock].data()); + } + /* Default ca bundle is configured automatically on connect by the WiFiSSLClient */ + + if (clients_cert_pem[internal_sock].size()) { + /* Set client certificate */ + the_client.sslclient->setCertificate((const char *)clients_cert_pem[internal_sock].data()); + } + if (clients_key_pem[internal_sock].size()) { + /* Set client key */ + the_client.sslclient->setPrivateKey((const char *)clients_key_pem[internal_sock].data()); + } + if (!the_client.sslclient->connect(host.c_str(), atoi(port.c_str()))) { return chAT::CommandStatus::ERROR; } @@ -311,6 +339,11 @@ void CAtHandler::add_cmds_wifi_SSL() { return chAT::CommandStatus::ERROR; } + const int internal_sock = the_client.can_delete; + if (internal_sock == -1) { + return chAT::CommandStatus::ERROR; + } + auto &hostip = parser.args[1]; if (hostip.empty()) { return chAT::CommandStatus::ERROR; @@ -326,6 +359,21 @@ void CAtHandler::add_cmds_wifi_SSL() { return chAT::CommandStatus::ERROR; } + /* Set custom root ca */ + if (clients_ca[internal_sock].size()) { + the_client.sslclient->setCACert((const char *)clients_ca[internal_sock].data()); + } + /* Default ca bundle is configured automatically on connect by the WiFiSSLClient */ + + if (clients_cert_pem[internal_sock].size()) { + /* Set client certificate */ + the_client.sslclient->setCertificate((const char *)clients_cert_pem[internal_sock].data()); + } + if (clients_key_pem[internal_sock].size()) { + /* Set client key */ + the_client.sslclient->setPrivateKey((const char *)clients_key_pem[internal_sock].data()); + } + if (!the_client.sslclient->connect(address, atoi(hostport.c_str()))) { return chAT::CommandStatus::ERROR; } @@ -359,6 +407,11 @@ void CAtHandler::add_cmds_wifi_SSL() { return chAT::CommandStatus::ERROR; } + const int internal_sock = the_client.can_delete; + if (internal_sock == -1) { + return chAT::CommandStatus::ERROR; + } + auto &host = parser.args[1]; if (host.empty()) { return chAT::CommandStatus::ERROR; @@ -381,6 +434,21 @@ void CAtHandler::add_cmds_wifi_SSL() { } } + /* Set custom root ca */ + if (clients_ca[internal_sock].size()) { + the_client.sslclient->setCACert((const char *)clients_ca[internal_sock].data()); + } + /* Default ca bundle is configured automatically on connect by the WiFiSSLClient */ + + if (clients_cert_pem[internal_sock].size()) { + /* Set client certificate */ + the_client.sslclient->setCertificate((const char *)clients_cert_pem[internal_sock].data()); + } + if (clients_key_pem[internal_sock].size()) { + /* Set client key */ + the_client.sslclient->setPrivateKey((const char *)clients_key_pem[internal_sock].data()); + } + if (!the_client.sslclient->connect(host.c_str(), atoi(port.c_str()), timeout)) { return chAT::CommandStatus::ERROR; } @@ -498,6 +566,9 @@ void CAtHandler::add_cmds_wifi_SSL() { if(the_client.can_delete >= 0) { delete sslclients[the_client.can_delete]; sslclients[the_client.can_delete] = nullptr; + clients_ca[the_client.can_delete].clear(); + clients_cert_pem[the_client.can_delete].clear(); + clients_key_pem[the_client.can_delete].clear(); sslclients_num--; } } From 7495f3fca9ede059d54da9f93a30469293092a8a Mon Sep 17 00:00:00 2001 From: pennam Date: Tue, 13 Feb 2024 09:22:12 +0100 Subject: [PATCH 5/5] wifi ssl: handle mbedtls_pem_write_buffer return value --- UNOR4USBBridge/cmds_wifi_SSL.h | 32 +++++++++++++++++++++----------- 1 file changed, 21 insertions(+), 11 deletions(-) diff --git a/UNOR4USBBridge/cmds_wifi_SSL.h b/UNOR4USBBridge/cmds_wifi_SSL.h index e3f6f72..79535c0 100644 --- a/UNOR4USBBridge/cmds_wifi_SSL.h +++ b/UNOR4USBBridge/cmds_wifi_SSL.h @@ -165,11 +165,17 @@ void CAtHandler::add_cmds_wifi_SSL() { /* Convert client certificate DER buffer into PEM */ clients_cert_pem[internal_sock].resize(1024); size_t olen; - mbedtls_pem_write_buffer("-----BEGIN CERTIFICATE-----\n", - "-----END CERTIFICATE-----\n", - client_cert_der.data(), size, - clients_cert_pem[internal_sock].data(), 1024, - &olen); + int ret = -1; + if ((ret = mbedtls_pem_write_buffer("-----BEGIN CERTIFICATE-----\n", + "-----END CERTIFICATE-----\n", + client_cert_der.data(), size, + clients_cert_pem[internal_sock].data(), 1024, + &olen)) != 0) + { + log_e(" failed\n ! mbedtls_pem_write_buffer returned -0x%04x", (unsigned int) -ret); + clients_cert_pem[internal_sock].clear(); + return chAT::CommandStatus::ERROR; + } clients_cert_pem[internal_sock].resize(olen); #if ECC_DEBUG_ENABLED @@ -183,7 +189,6 @@ void CAtHandler::add_cmds_wifi_SSL() { /* Read private key from non volatile storage in DER format */ std::vector client_key_der; int len = sse.getBytesLength(slot_num.c_str()); - int ret = -1; client_key_der.resize(len); if ((ret = sse.getBytes(slot_num.c_str(), client_key_der.data(), len)) < len) { log_e(" failed\n ! sse.getBytes returned -0x%04x", (unsigned int) -ret); @@ -197,11 +202,16 @@ void CAtHandler::add_cmds_wifi_SSL() { /* Convert private key in PEM format */ clients_key_pem[internal_sock].resize(1024); - mbedtls_pem_write_buffer("-----BEGIN EC PRIVATE KEY-----\n", - "-----END EC PRIVATE KEY-----\n", - client_key_der.data(), len, - clients_key_pem[internal_sock].data(), 1024, - &olen); + if ((ret = mbedtls_pem_write_buffer("-----BEGIN EC PRIVATE KEY-----\n", + "-----END EC PRIVATE KEY-----\n", + client_key_der.data(), len, + clients_key_pem[internal_sock].data(), 1024, + &olen)) != 0) + { + log_e(" failed\n ! mbedtls_pem_write_buffer returned -0x%04x", (unsigned int) -ret); + clients_cert_pem[internal_sock].clear(); + return chAT::CommandStatus::ERROR; + } clients_key_pem[internal_sock].resize(olen); #if ECC_DEBUG_ENABLED