feat(tls_cxx): Publish mbedtls component

Adds examples and tests.
Also supports DTLS now.
This commit is contained in:
David Cermak
2024-02-08 20:11:30 +01:00
parent c4d9cc6beb
commit 0140455ff7
46 changed files with 1181 additions and 23 deletions

View File

@ -99,5 +99,6 @@ jobs:
components/console_cmd_ifconfig;
components/console_cmd_wifi;
components/esp_wifi_remote;
components/mbedtls_cxx;
namespace: "espressif"
api_token: ${{ secrets.IDF_COMPONENT_API_TOKEN }}

30
.github/workflows/tls_cxx__build.yml vendored Normal file
View File

@ -0,0 +1,30 @@
name: "mbedtls-cxx: build-tests"
on:
push:
branches:
- master
pull_request:
types: [opened, synchronize, reopened, labeled]
jobs:
build_tls_cxx:
if: contains(github.event.pull_request.labels.*.name, 'tls_cxx') || github.event_name == 'push'
name: Build
strategy:
matrix:
idf_ver: ["latest", "release-v5.2", "release-v5.1"]
test: [ { app: client, path: "examples/tls_client" }, { app: udp, path: "examples/udp_mutual_auth" }, { app: test, path: "tests/uart_mutual_auth" } ]
runs-on: ubuntu-20.04
container: espressif/idf:${{ matrix.idf_ver }}
steps:
- name: Checkout esp-protocols
uses: actions/checkout@v3
with:
submodules: recursive
- name: Build ${{ matrix.test.app }} with IDF-${{ matrix.idf_ver }}
shell: bash
run: |
${IDF_PATH}/install.sh --enable-pytest
. ${IDF_PATH}/export.sh
python ./ci/build_apps.py ./components/mbedtls_cxx/${{ matrix.test.path }} -vv --preserve-all

View File

@ -61,8 +61,8 @@ repos:
- repo: local
hooks:
- id: commit message scopes
name: "commit message must be scoped with: mdns, modem, websocket, asio, mqtt_cxx, console, common, eppp, wifi_remote"
entry: '\A(?!(feat|fix|ci|bump|test|docs)\((mdns|modem|common|console|websocket|asio|mqtt_cxx|examples|eppp|wifi_remote)\)\:)'
name: "commit message must be scoped with: mdns, modem, websocket, asio, mqtt_cxx, console, common, eppp, wifi_remote, tls_cxx"
entry: '\A(?!(feat|fix|ci|bump|test|docs)\((mdns|modem|common|console|websocket|asio|mqtt_cxx|examples|eppp|wifi_remote|tls_cxx)\)\:)'
language: pygrep
args: [--multiline]
stages: [commit-msg]

View File

@ -57,3 +57,7 @@ Please refer to instructions in [ESP-IDF](https://github.com/espressif/esp-idf)
### esp_wifi_remote
* Brief introduction [README](components/esp_wifi_remote/README.md)
### mbedtls_cxx
* Brief introduction [README](components/mbedtls_cxx/README.md)

View File

@ -1,4 +1,4 @@
idf_component_register(SRCS mbedtls_wrap.cpp
tls_transport.cpp
idf_component_register(SRCS tls_transport.cpp
INCLUDE_DIRS include
REQUIRES tcp_transport)
REQUIRES tcp_transport
PRIV_REQUIRES mbedtls_cxx)

View File

@ -11,6 +11,8 @@
static const char *TAG = "tls_transport";
using namespace idf::mbedtls_cxx;
class TlsTransport: public Tls {
public:
explicit TlsTransport(esp_transport_handle_t parent) : Tls(), transport_(parent) {}

View File

@ -2,3 +2,6 @@ dependencies:
espressif/esp_modem:
version: "^1.0.1"
override_path: "../../../"
espressif/mbedtls_cxx:
version: "*"
override_path: "../../../../mbedtls_cxx"

View File

@ -2,7 +2,7 @@
# in this exact order for cmake to work correctly
cmake_minimum_required(VERSION 3.8)
set(EXTRA_COMPONENT_DIRS "../.." "../../examples/modem_tcp_client/components")
set(EXTRA_COMPONENT_DIRS "../.." "../../../mbedtls_cxx")
include($ENV{IDF_PATH}/tools/cmake/project.cmake)
project(ota_test)

View File

@ -1,3 +1,3 @@
idf_component_register(SRCS manual_ota.cpp transport_batch_tls.cpp
INCLUDE_DIRS "."
PRIV_REQUIRES extra_tcp_transports esp_http_client app_update)
PRIV_REQUIRES mbedtls_cxx esp_http_client app_update)

View File

@ -10,6 +10,8 @@
#define TAG "batch-tls"
using namespace idf::mbedtls_cxx;
class TlsTransport: public Tls {
public:
explicit TlsTransport(esp_transport_handle_t parent):

View File

@ -0,0 +1,8 @@
---
commitizen:
bump_message: 'bump(tls_cxx): $current_version -> $new_version'
pre_bump_hooks: python ../../ci/changelog.py mbedtls_cxx
tag_format: tls_cxx-v$version
version: 0.0.0
version_files:
- idf_component.yml

View File

@ -0,0 +1,3 @@
idf_component_register(SRCS mbedtls_wrap.cpp
INCLUDE_DIRS include
REQUIRES tcp_transport)

View File

@ -0,0 +1,3 @@
# mbedtls_cxx
This is a simplified C++ wrapper of mbedTLS for performing TLS and DTLS handshake a communication. This component allows for overriding low level IO functions (`send()` and `recv()`) and thus supporting TLS over various physical channels.

View File

@ -0,0 +1,8 @@
idf_component_register(
EMBED_TXTFILES srv.crt
EMBED_TXTFILES srv.key
EMBED_TXTFILES ca.crt
EMBED_TXTFILES client.crt
EMBED_TXTFILES client.key
INCLUDE_DIRS "."
REQUIRES mbedtls_cxx)

View File

@ -0,0 +1,4 @@
# Test Certificates and Keys
This is a utility component, that uses test certificates for server and client side in examples and tests.
These are self-signed certificates and generated only for test and demonstration purposes.

View File

@ -0,0 +1,19 @@
-----BEGIN CERTIFICATE-----
MIIDIzCCAgugAwIBAgIULOncUeRLKxgrihIh1kHGGlPV7ecwDQYJKoZIhvcNAQEL
BQAwITELMAkGA1UEBhMCQ1oxEjAQBgNVBAMMCUVzcHJlc3NpZjAeFw0yNDA0MDMw
OTE0MjNaFw0zNDA0MDEwOTE0MjNaMCExCzAJBgNVBAYTAkNaMRIwEAYDVQQDDAlF
c3ByZXNzaWYwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQCbayaZAuzQ
WrwRj3oiFP9AZK0ECaDvVlJec4M6yokded1pqNY+bNmA7VsHSQkf3d1rO1G5GwEX
oMPli15m7rJodq9iYp1J2LhLhpKDNapm19reyH9A4rAfjSyk/WyvT+3Y5sNHVFdE
2t1EetOyzy90CfOHT9JfWG9PiV6b1W65CqgjJVCHMWioppVAGQCoN+mDBf1VhD4a
m6onei+ijHdALJDfp74mSIOJGulm/IR7504s+yy7068PQ05V/wHkmd9O1Iww5fnJ
dRh2KvTFZVOB5u9y54MTJb0sGZj+JfxIbcFiIWAykLFVWBk5PO6yj8fNMmk/Ogb2
K4wo7AZnJ3qBAgMBAAGjUzBRMB0GA1UdDgQWBBT3j77hJHm/hI34fEn3tocHqB4I
NzAfBgNVHSMEGDAWgBT3j77hJHm/hI34fEn3tocHqB4INzAPBgNVHRMBAf8EBTAD
AQH/MA0GCSqGSIb3DQEBCwUAA4IBAQBstXfBIRvqZp4OBQ2kCJig/CErcfdB4qQO
S2LzQmpIOUQ4d/zvZOQD2WIw/x2Rd1/hto/+f57pOZNHsi8vfX2Z7kPOlD9ZG1wT
znl1v8wOMP01AFJuVtmJQV0C4lVupb2/Mmu42xqP9pr/uL5pJ2rFb8ujl2xakhSv
YVdMONtZL0mh9+hdnUb7Fj7KI3qWxzc7+uXGjCzh6LkOmcMBOB6+0V6xW2NVpUUP
tuXytK0t2oyWpDvwFIrl0J6qBNRlH1ON1iz33HOo73IjprMNx3hIo5y/N8+TTxY6
KEegbP67hSnJJhQ7tezoOu4OE0xmJp0XmGPMNewYARqL2UvHnZyf
-----END CERTIFICATE-----

View File

@ -0,0 +1,17 @@
-----BEGIN CERTIFICATE-----
MIICwjCCAaoCFAWE4aJdYWbMJAaBTMxVpoXMrhzvMA0GCSqGSIb3DQEBCwUAMCEx
CzAJBgNVBAYTAkNaMRIwEAYDVQQDDAlFc3ByZXNzaWYwHhcNMjQwNDAzMDkxNjE4
WhcNMzQwNDAxMDkxNjE4WjAaMRgwFgYDVQQDDA9lc3ByZXNzaWYubG9jYWwwggEi
MA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQDcjdBQ3sHbfuWRo2LlqZF37Dfu
ymz5/fq+szcOLUUWCccxku9qNNEdZAcZHMsINurezpHXa6ZNKGqmbmqZPVrEKzMU
IolpnQmcerRt/yKqxCZ/kgsJE3IZyqi1T+xDwaBEhgdB6+wxyrL0/uBlLCbEdZAA
7MPcauIKz8ykfIwo7Ht/vcHNxGaFFu+DcNoJI/Pw6hERlC9DHuUftK0/Lap1K2o+
6kFQKqhVrvNQmaiqnz3Dr9psPO90AvbRqeODmfpi7rtU4MKOprQhUrMS9s9d5yVd
JILp74pt6nzu3EnFiixRD5XD9PtK5NvP1sgDAgbWgTttwM9X7N6mzEe/gVUZAgMB
AAEwDQYJKoZIhvcNAQELBQADggEBAJVsbAamDRuZ1J2ogHLo/UmjmcmIATmqO7Eb
aoid7+FYme/2NFzofOFtJNaCumdhwxSyf7ER6m2DUO6bDseblqNCTyRDNNXzTHEF
QiYh2PThKSDdH0fbEf4IpcbOCnpSEpIg9C/ywEhq/wzYiOlxPhNWxBKHLhEkM7aW
BerAhInCRRXymfus2HUf6aTWZ0wigMoUVKwOu16Zh04D2d6qb314cEMgKvANPiTT
dgEae7Ot+rP1s2Zp75zUbWuz4uWd4wJDOHWR25mkD3ZELfbrpmEymbOTQ26zOpIU
iPNfYZ1t9KwEjkKN+jBTXKu7QhB/u+g5yBHjRL++LEli4YGGGiA=
-----END CERTIFICATE-----

View File

@ -0,0 +1,27 @@
-----BEGIN RSA PRIVATE KEY-----
MIIEpQIBAAKCAQEA3I3QUN7B237lkaNi5amRd+w37sps+f36vrM3Di1FFgnHMZLv
ajTRHWQHGRzLCDbq3s6R12umTShqpm5qmT1axCszFCKJaZ0JnHq0bf8iqsQmf5IL
CRNyGcqotU/sQ8GgRIYHQevsMcqy9P7gZSwmxHWQAOzD3GriCs/MpHyMKOx7f73B
zcRmhRbvg3DaCSPz8OoREZQvQx7lH7StPy2qdStqPupBUCqoVa7zUJmoqp89w6/a
bDzvdAL20anjg5n6Yu67VODCjqa0IVKzEvbPXeclXSSC6e+Kbep87txJxYosUQ+V
w/T7SuTbz9bIAwIG1oE7bcDPV+zepsxHv4FVGQIDAQABAoIBAQDPzzc224yg+iHo
ZaArcOhFrGbPMiAYNLxrroTzcKglqbTr+txmn7lhDfy6Jq0O4l/O66fy59Vb4fcL
NgJuvKanK2UHVbtPrc1+iQc0lS7e4866aKrJNG9P6emoXNPqy6fsqLRx4o88IxcX
TIe2DDHC7lpu5KdvKa4uLblOSqPtcZTHXPD9olVe8ZYF5CttMUTc4SkF4HSkY2jb
0j+6kASN4eQ2CqEt+IW4IxI5NiEzrlzZSOdyqIOeyGUZz+QcfILOProWZHYzH3jO
HQe4PJSXO57f2dojY1GqRcjnr3guQMpw1s7wmDYO1QekiBYwRERNzjEY7VhgDq8T
0rwJPHP5AoGBAPO13QpJVSj1NfQ/H0AgZlsJIIlIwIC2YwuwjA7b36A3JOFolkHj
tq4eNntThNRQbTL9OficbxJSHXQcOsQeo7dvwEhJjuVwEajL4/6cjc9b4oyGJxLl
TKTshUPmeKPfGWUjRZKGbVWbT3m4BXqGiv4laCZ0LDHiCt4DvEzQ1Bo3AoGBAOet
BClbcbUJhxkAme5HHI9A5VcqyOi2CrRh+HjKd/2IJlDA+Vmbl2iEB+9cMRGRubaz
brk1yAzgtW29GX8kngr8yxDtIM8M7lPR2NhXx7XbmCKwKosZ7l6hHNdnD12TFyLC
juuJlUA37sWXw8r33623mLFQlNVjnL0onUa1XSMvAoGACw47+cR73YDKMstOQp11
pzmRxUiMmworEhOvNtlYmq8FuEgDUPfgiKOMOyn9w5fmbEK6h4GpND6PYX4KWG0/
ZgnmwiC8H8Jmuq6NKDa35Ck57MAFM8E9Kdok7YCeBmkPgNwJwuzgNtr1zwK/FODX
m1HdGKl6e8TSU2H9/8oVZR8CgYEAoHSWI0awNCCLLufZtMwPna/mpz58s6ARPel0
u8QO4st/LgLZMBSxArQfAsqpOW/iXgVcNG5pRXIEdiK4G/TyeM2onup9BKoCDo+S
ThRNv0h9z9iPPpQRIf0YCp/YZojPR0XU0pERi86xUqzP8C1I//neiUA0NK6vCdut
QiGuhgUCgYEAp89EFcM1WvtPRJE+md8N8BUef5MJ+JJ0nb+BW1kkLY50Q1MVmsVX
dUowYupWLBgEfMn8fy8Q+xD9EeiISTF9MtT1X4iQSI/pzKW5LLd0OJYnqPMWzygg
ASzSNWdYBIGNkqsQGmGCtF9+i6V4acfTTbMD9LiB7u5/enQa8N0Qg+s=
-----END RSA PRIVATE KEY-----

View File

@ -0,0 +1,4 @@
dependencies:
espressif/mbedtls_cxx:
version: "*"
override_path: "../.."

View File

@ -0,0 +1,17 @@
-----BEGIN CERTIFICATE-----
MIICwjCCAaoCFAWE4aJdYWbMJAaBTMxVpoXMrhzuMA0GCSqGSIb3DQEBCwUAMCEx
CzAJBgNVBAYTAkNaMRIwEAYDVQQDDAlFc3ByZXNzaWYwHhcNMjQwNDAzMDkxNTI3
WhcNMzQwNDAxMDkxNTI3WjAaMRgwFgYDVQQDDA9lc3ByZXNzaWYubG9jYWwwggEi
MA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQDChMP2z+M0MSy4dNO64tuJoI37
lWBBFEmaRj7AkEQQ2opD36qJvgv17AU/S997vQvpmL3GLC0uIKNFvdUp0p7Zw4m0
UuPDU8NZTCUqjGJVV2advYzsCCRz3dlosX8z5JpN9GmxLsPjoy+jJskVU/ZCiUnv
Tipk86VZq9K2gHVGaghVk0qbo8QEorjPLk0w8KfYpSNjMV8XL3wMIWeUQ7ZzlRIg
fhFVv7QO7hCGq/h1lGeyMjszmajCmALp8DyzFggL0I6HKpqBGh7WD99Tndhl3qkL
3VFiNuNALKn+ZDW7xYseCLf2w3zxMYzNwsdgFCcOlYJ4cznx/OFFKPTybXc3AgMB
AAEwDQYJKoZIhvcNAQELBQADggEBAFD31VncaNRvs4T8fc9g8GtiHS4/f0fDzwQY
CFufm+I/J1V0NFQZtfhJEnjXu3c8CAlZx8texiJplZwMXhEsd2Qs2JkQ0i9/vlAs
ClhqR9+lPmCDISK2cQZL5gUwGE8JpGev3ty8LNxVEgTTxlSSM7IdVtVJVRpWSxCj
4zSIiozt0w4XwU240W+pDGEptnSh0CtXl8fPTQa3Zr3eepk4Io0z14+lDecYExhi
acgqAaEKLety0+EfApsR0IK/QWGnDECQMCZiduLOJhPhssE3Ybxr0bRcUOuS1ltY
63h/eVuaOxgcp+YMkIDaSuW/LBcl1p84ZQ3B+hrUTN5TztggCtU=
-----END CERTIFICATE-----

View File

@ -0,0 +1,27 @@
-----BEGIN RSA PRIVATE KEY-----
MIIEpQIBAAKCAQEAwoTD9s/jNDEsuHTTuuLbiaCN+5VgQRRJmkY+wJBEENqKQ9+q
ib4L9ewFP0vfe70L6Zi9xiwtLiCjRb3VKdKe2cOJtFLjw1PDWUwlKoxiVVdmnb2M
7Agkc93ZaLF/M+SaTfRpsS7D46MvoybJFVP2QolJ704qZPOlWavStoB1RmoIVZNK
m6PEBKK4zy5NMPCn2KUjYzFfFy98DCFnlEO2c5USIH4RVb+0Du4Qhqv4dZRnsjI7
M5mowpgC6fA8sxYIC9COhyqagRoe1g/fU53YZd6pC91RYjbjQCyp/mQ1u8WLHgi3
9sN88TGMzcLHYBQnDpWCeHM58fzhRSj08m13NwIDAQABAoIBAQCNJAqRDrzcRQYe
/V4YT71eOmprbzK9ZfwV/fxQex14YSpMH4G9mnFRSqVwDRymy0BWiibBIZLS0onF
8/008IekmNZVSoPLWf73z2F0YxKCu/1QXZZgOXRBwmqbJZeXzrsL71m1X6pxhRnu
txjW6epY0wcbpcrrH0MOMredBs6RfOBi55wpuXNPjEP1YchBNXem0OR/ai5I3hGG
5ifStVvMv/+2N1vwTTelz28YvXcKqQXpMFhbVxfkIPw5JVnNvcDAWuC5oOj+jWeX
p5lhBzFqtnRuNjiC81qSbv/R5zDLMtceewwRew9/yu4su0VUsn8frSqTqsUPtpqf
Wa7BQgjBAoGBAOgSLCF/BZmeiJwNlVw1Mk/uHrDgGqDzZzGbKUX1CTtIoWOqT/JA
yr4NFpMQq8imS/XAvZJ8HyvBlT+YScmews+4TTRr/4QoAo00q99c24g2k3y4gZ/r
ddD5LFwkyr/JUwGQbSyx4Vf9CnVhHbnibm8jnI033hq9f9oOBr5tSOlXAoGBANaT
WPQIzansIu7f9inVNGzVsZEzmSoKU9P/81HsVblfhySXA3elqIK4WKJzJlyyRWk/
bE5tSVdk04mMWpNBgWmz6rVvFRZMDRjlJVoumqJ6F5wKK/yGTshwWTixiR+gX3/q
PtJVWGcv8y8tlfZmuxjeJyQzz6GA/T/yU2717tUhAoGBAK5u2lTjEOaCztPdMeL4
6TRDGzZ/fAR2b55NrhwzLhktYoFzRlwkbz+ivtETuss6k+XIVbWBLjzJaNFAOlYd
yvgu43j/pDNFj4VrjbR6K1ibDQi19liptwi4AavQy8SUIpwOXsE1gteMDwxjTuCl
XbST49wRnhQjU7Im0NLZefBJAoGBALQcvOci2JeQegYvAi89IqNUgJdUS3Wpc5IZ
X0Hd8lsG36aMCEIPULJhUS0TIRlgHrlonl36iDyOiq4DnSfMLXfvqM0g7/9mWPwT
pFXdlrlZaKX3N9LhXhILlCjthR+B+4egtZjSQMFk02k/h4hr1RLbrtlJkDiN0IYz
v2r9jrthAoGANtSFvvDTpUi79RaXkUYID8OqXIdqujViaDeWGI+wVgE88b8JF8ac
XcMcHz1J6meebPsjmkcNLLrD3jWo7NyiKT1j2xeYg6c0JEEMGNG/pmmu7b0a/P+i
jcofvRHBh/yYZwodabhoSHHtWOI2d+w1L2ECx/iezQM0uMfaXCsZ6ak=
-----END RSA PRIVATE KEY-----

View File

@ -0,0 +1,51 @@
/*
* SPDX-FileCopyrightText: 2024 Espressif Systems (Shanghai) CO LTD
*
* SPDX-License-Identifier: Apache-2.0
*/
#pragma once
#include "mbedtls_wrap.hpp"
namespace test_certs {
using pem_format = const unsigned char;
extern pem_format cacert_start[] asm("_binary_ca_crt_start");
extern pem_format cacert_end[] asm("_binary_ca_crt_end");
extern pem_format clientcert_start[] asm("_binary_client_crt_start");
extern pem_format clientcert_end[] asm("_binary_client_crt_end");
extern pem_format clientkey_start[] asm("_binary_client_key_start");
extern pem_format clientkey_end[] asm("_binary_client_key_end");
extern pem_format servercert_start[] asm("_binary_srv_crt_start");
extern pem_format servercert_end[] asm("_binary_srv_crt_end");
extern pem_format serverkey_start[] asm("_binary_srv_key_start");
extern pem_format serverkey_end[] asm("_binary_srv_key_end");
enum class type {
cacert,
servercert,
serverkey,
clientcert,
clientkey
};
#define IF_BUF_TYPE(buf_type) \
if (t == type::buf_type) { \
return idf::mbedtls_cxx::const_buf{buf_type ## _start, buf_type ## _end - buf_type ## _start}; \
}
static inline idf::mbedtls_cxx::const_buf get_buf(type t)
{
IF_BUF_TYPE(cacert);
IF_BUF_TYPE(servercert);
IF_BUF_TYPE(serverkey);
IF_BUF_TYPE(clientcert);
IF_BUF_TYPE(clientkey);
return idf::mbedtls_cxx::const_buf{};
}
static inline const char *get_server_cn()
{
return "espressif.local";
}
}

View File

@ -0,0 +1,11 @@
# For more information about build system see
# https://docs.espressif.com/projects/esp-idf/en/latest/api-guides/build-system.html
# The following five lines of boilerplate have to be in your project's
# CMakeLists in this exact order for cmake to work correctly
cmake_minimum_required(VERSION 3.16)
include($ENV{IDF_PATH}/tools/cmake/project.cmake)
if("${IDF_TARGET}" STREQUAL "linux")
list(APPEND EXTRA_COMPONENT_DIRS "$ENV{IDF_PATH}/tools/mocks/freertos/")
endif()
project(tls_client)

View File

@ -0,0 +1,4 @@
# TCP client example
This is a simple example uses `mbedtls_cxx` to connect to a remote echo server.
The example needs a connection to internet (or a network where the TLS echo-server is available), it could be run on linux target as well as on ESP32.

View File

@ -0,0 +1,2 @@
idf_component_register(SRCS "tls_client.cpp"
INCLUDE_DIRS ".")

View File

@ -0,0 +1,7 @@
dependencies:
idf: ">=5.0"
espressif/mbedtls_cxx:
version: "*"
override_path: "../../.."
protocol_examples_common:
path: ${IDF_PATH}/examples/common_components/protocol_examples_common

View File

@ -0,0 +1,152 @@
/*
* SPDX-FileCopyrightText: 2024 Espressif Systems (Shanghai) CO LTD
*
* SPDX-License-Identifier: Unlicense OR CC0-1.0
*/
#include <sys/socket.h>
#include <netdb.h>
#include <unistd.h>
#include "esp_log.h"
#include "mbedtls_wrap.hpp"
using namespace idf::mbedtls_cxx;
namespace {
constexpr auto *TAG = "simple_tls_client";
}
class TlsSocketClient: public Tls {
public:
TlsSocketClient() = default;
~TlsSocketClient() override
{
if (sock >= 0) {
::close(sock);
}
}
int send(const unsigned char *buf, size_t len) override
{
return ::send(sock, buf, len, 0);
}
int recv(unsigned char *buf, size_t len) override
{
return ::recv(sock, buf, len, 0);
}
bool connect(const char *host, int port)
{
addr_info addr(host, AF_INET, SOCK_STREAM);
if (!addr) {
ESP_LOGE(TAG, "Failed to resolve host");
return false;
}
sock = addr.get_sock();
if (sock < 0) {
ESP_LOGE(TAG, "Failed to create socket");
return false;
}
if (::connect(sock, addr.get_addr(port), sizeof(struct sockaddr)) < 0) {
ESP_LOGE(TAG, "Failed to connect %d", errno);
return false;
}
if (!init(is_server{false}, do_verify{false})) {
return false;
}
return handshake() == 0;
}
private:
int sock{-1};
/**
* RAII wrapper of the address_info
*/
struct addr_info {
struct addrinfo *ai {
nullptr
};
~addr_info()
{
freeaddrinfo(ai);
}
explicit addr_info(const char *host, int family, int type)
{
struct addrinfo hints {};
hints.ai_family = family;
hints.ai_socktype = type;
if (getaddrinfo(host, nullptr, &hints, &ai) < 0) {
freeaddrinfo(ai);
ai = nullptr;
}
}
explicit operator bool() const
{
return ai != nullptr;
}
struct sockaddr *get_addr(uint16_t port) const {
auto *p = (struct sockaddr_in *)ai->ai_addr;
p->sin_port = htons(port);
return (struct sockaddr *)p;
}
int get_sock() const
{
return socket(ai->ai_family, ai->ai_socktype, ai->ai_protocol);
}
};
};
namespace {
void tls_client()
{
const unsigned char message[] = "Hello\n";
unsigned char reply[128];
TlsSocketClient client;
if (!client.connect("tcpbin.com", 4243)) {
ESP_LOGE(TAG, "Failed to connect! %d", errno);
return;
}
if (client.write(message, sizeof(message)) < 0) {
ESP_LOGE(TAG, "Failed to write!");
return;
}
int len = client.read(reply, sizeof(reply));
if (len < 0) {
ESP_LOGE(TAG, "Failed to read!");
return;
}
ESP_LOGI(TAG, "Successfully received: %.*s", len, reply);
}
} // namespace
#if CONFIG_IDF_TARGET_LINUX
/**
* Linux target: We're already connected, just run the client
*/
int main()
{
tls_client();
return 0;
}
#else
/**
* ESP32 chipsets: Need to initialize system components
* and connect to network
*/
#include "nvs_flash.h"
#include "esp_event.h"
#include "protocol_examples_common.h"
#include "esp_netif.h"
extern "C" void app_main()
{
ESP_ERROR_CHECK(nvs_flash_init());
ESP_ERROR_CHECK(esp_netif_init());
ESP_ERROR_CHECK(esp_event_loop_create_default());
ESP_ERROR_CHECK(example_connect());
tls_client();
}
#endif

View File

@ -0,0 +1 @@
CONFIG_ESP_MAIN_TASK_STACK_SIZE=8192

View File

@ -0,0 +1,11 @@
# For more information about build system see
# https://docs.espressif.com/projects/esp-idf/en/latest/api-guides/build-system.html
# The following five lines of boilerplate have to be in your project's
# CMakeLists in this exact order for cmake to work correctly
cmake_minimum_required(VERSION 3.16)
include($ENV{IDF_PATH}/tools/cmake/project.cmake)
if("${IDF_TARGET}" STREQUAL "linux")
list(APPEND EXTRA_COMPONENT_DIRS "$ENV{IDF_PATH}/tools/mocks/freertos/")
endif()
project(udp_mutual)

View File

@ -0,0 +1,4 @@
# UDP Mutual authentication example
This example uses `mbedtls_cxx` to perform a DTLS handshake and exchange a message between server and client.
The example uses UDP sockets on `'localhost'` interface, so no actual connection is needed, it could be run on linux target as well as on ESP32.

View File

@ -0,0 +1,2 @@
idf_component_register(SRCS "udp_mutual.cpp"
INCLUDE_DIRS ".")

View File

@ -0,0 +1,8 @@
dependencies:
idf: ">=5.0"
espressif/mbedtls_cxx:
version: "*"
override_path: "../../.."
test_certs:
version: "*"
path: "../../test_certs"

View File

@ -0,0 +1,229 @@
/*
* SPDX-FileCopyrightText: 2024 Espressif Systems (Shanghai) CO LTD
*
* SPDX-License-Identifier: Unlicense OR CC0-1.0
*/
#include <thread>
#include <sys/socket.h>
#include <netdb.h>
#include <unistd.h>
#include "esp_log.h"
#include "mbedtls_wrap.hpp"
#include "test_certs.hpp"
namespace {
constexpr auto *TAG = "udp_example";
}
using namespace idf::mbedtls_cxx;
using namespace test_certs;
class SecureLink: public Tls {
public:
explicit SecureLink() : Tls(), addr("localhost", 3333, AF_INET, SOCK_DGRAM) {}
~SecureLink() override
{
if (sock >= 0) {
::close(sock);
}
}
int send(const unsigned char *buf, size_t len) override
{
return sendto(sock, buf, len, 0, addr, ai_size);
}
int recv(unsigned char *buf, size_t len) override
{
socklen_t socklen = sizeof(sockaddr);
return recvfrom(sock, buf, len, 0, addr, &socklen);
}
int recv_timeout(unsigned char *buf, size_t len, int timeout) override
{
struct timeval tv {
timeout / 1000, (timeout % 1000 ) * 1000
};
fd_set read_fds;
FD_ZERO( &read_fds );
FD_SET( sock, &read_fds );
int ret = select(sock + 1, &read_fds, nullptr, nullptr, timeout == 0 ? nullptr : &tv);
if (ret == 0) {
return MBEDTLS_ERR_SSL_TIMEOUT;
}
if (ret < 0) {
if (errno == EINTR) {
return MBEDTLS_ERR_SSL_WANT_READ;
}
return ret;
}
return recv(buf, len);
}
bool open(bool server_not_client)
{
if (!addr) {
ESP_LOGE(TAG, "Failed to resolve endpoint");
return false;
}
sock = addr.get_sock();
if (sock < 0) {
ESP_LOGE(TAG, "Failed to create socket");
return false;
}
TlsConfig config{};
config.is_dtls = true;
config.timeout = 10000;
if (server_not_client) {
int err = bind(sock, addr, ai_size);
if (err < 0) {
ESP_LOGE(TAG, "Socket unable to bind: errno %d", errno);
return false;
}
const unsigned char client_id[] = "localhost";
config.client_id = std::make_pair(client_id, sizeof(client_id));
}
if (!init(is_server{server_not_client}, do_verify{true}, &config)) {
return false;
}
return handshake() == 0;
}
private:
int sock{-1};
/**
* RAII wrapper of the address_info
*/
struct addr_info {
struct addrinfo *ai = nullptr;
explicit addr_info(const char *host, int port, int family, int type)
{
struct addrinfo hints {};
hints.ai_family = family;
hints.ai_socktype = type;
if (getaddrinfo(host, nullptr, &hints, &ai) < 0) {
freeaddrinfo(ai);
ai = nullptr;
}
auto *p = (struct sockaddr_in *)ai->ai_addr;
p->sin_port = htons(port);
}
~addr_info()
{
freeaddrinfo(ai);
}
explicit operator bool() const
{
return ai != nullptr;
}
operator sockaddr *() const
{
auto *p = (struct sockaddr_in *)ai->ai_addr;
return (struct sockaddr *)p;
}
int get_sock() const
{
return socket(ai->ai_family, ai->ai_socktype, ai->ai_protocol);
}
} addr;
const int ai_size{sizeof(struct sockaddr_in)};
};
namespace {
void tls_client()
{
const unsigned char message[] = "Hello\n";
unsigned char reply[128];
SecureLink client;
client.set_hostname(get_server_cn());
if (!client.set_own_cert(get_buf(type::clientcert), get_buf(type::clientkey))) {
ESP_LOGE(TAG, "Failed to set own cert");
return;
}
if (!client.set_ca_cert(get_buf(type::cacert))) {
ESP_LOGE(TAG, "Failed to set peer's cert");
return;
}
if (!client.open(false)) {
ESP_LOGE(TAG, "Failed to CONNECT! %d", errno);
return;
}
ESP_LOGI(TAG, "client opened...");
if (client.write(message, sizeof(message)) < 0) {
ESP_LOGE(TAG, "Failed to write!");
return;
}
int len = client.read(reply, sizeof(reply));
if (len < 0) {
ESP_LOGE(TAG, "Failed to read!");
return;
}
ESP_LOGI(TAG, "Successfully received: %.*s", len, reply);
}
void tls_server()
{
unsigned char message[128];
SecureLink server;
if (!server.set_own_cert(get_buf(type::servercert), get_buf(type::serverkey))) {
ESP_LOGE(TAG, "Failed to set own cert");
return;
}
if (!server.set_ca_cert(get_buf(type::cacert))) {
ESP_LOGE(TAG, "Failed to set peer's cert");
return;
}
ESP_LOGI(TAG, "opening...");
if (!server.open(true)) {
ESP_LOGE(TAG, "Failed to OPEN! %d", errno);
return;
}
int len = server.read(message, sizeof(message));
if (len < 0) {
ESP_LOGE(TAG, "Failed to read!");
return;
}
ESP_LOGI(TAG, "Received from client: %.*s", len, message);
if (server.write(message, len) < 0) {
ESP_LOGE(TAG, "Failed to write!");
return;
}
ESP_LOGI(TAG, "Written back");
}
void udp_auth()
{
std::thread t2(tls_server);
std::thread t1(tls_client);
t1.join();
t2.join();
}
} // namespace
#if CONFIG_IDF_TARGET_LINUX
/**
* Linux target: We're already connected, just run the client
*/
int main()
{
udp_auth();
return 0;
}
#else
/**
* ESP32 chipsets: Need to initialize system components
* and connect to network
*/
#include "esp_event.h"
#include "esp_netif.h"
extern "C" void app_main()
{
ESP_ERROR_CHECK(esp_netif_init());
ESP_ERROR_CHECK(esp_event_loop_create_default());
udp_auth();
}
#endif

View File

@ -0,0 +1,2 @@
CONFIG_MBEDTLS_SSL_PROTO_DTLS=y
CONFIG_PTHREAD_TASK_STACK_SIZE_DEFAULT=8192

View File

@ -0,0 +1,7 @@
version: 0.0.0
url: https://github.com/espressif/esp-protocols/tree/master/components/mbedtls_cxx
description: C++ wrapper of mbedtls to perform (D)TLS connection
license: Apache-2.0
dependencies:
idf:
version: '>=5.0'

View File

@ -1,5 +1,5 @@
/*
* SPDX-FileCopyrightText: 2023 Espressif Systems (Shanghai) CO LTD
* SPDX-FileCopyrightText: 2023-2024 Espressif Systems (Shanghai) CO LTD
*
* SPDX-License-Identifier: Apache-2.0
*/
@ -7,34 +7,78 @@
#include <utility>
#include <memory>
#include <span>
#include <mbedtls/timing.h>
#include <mbedtls/ssl_cookie.h>
#include "mbedtls/ssl.h"
#include "mbedtls/entropy.h"
#include "mbedtls/ctr_drbg.h"
#include "mbedtls/error.h"
using const_buf = std::span<const unsigned char>;
namespace idf::mbedtls_cxx {
using const_buf = std::pair<const unsigned char *, std::size_t>;
using buf = std::pair<unsigned char *, std::size_t>;
struct TlsConfig {
bool is_dtls;
uint32_t timeout;
const_buf client_id;
};
/**
* @brief Application wrapper of (D)TLS for authentication and creating encrypted communication channels
*/
class Tls {
public:
enum class is_server : bool {};
enum class do_verify : bool {};
/**
* High level configs for this class are per below: (server/client, with/out verification, TLS/DTLS)
*/
enum class is_server : bool {
};
enum class do_verify : bool {
};
enum class is_dtls : bool {
};
Tls();
virtual ~Tls();
bool init(is_server server, do_verify verify);
bool init(is_server server, do_verify verify, TlsConfig *config = nullptr);
bool init_dtls_cookies();
bool set_client_id();
bool deinit();
int handshake();
int write(const unsigned char *buf, size_t len);
int read(unsigned char *buf, size_t len);
[[nodiscard]] bool set_own_cert(const_buf crt, const_buf key);
[[nodiscard]] bool set_ca_cert(const_buf crt);
bool set_hostname(const char *name);
virtual int send(const unsigned char *buf, size_t len) = 0;
virtual int recv(unsigned char *buf, size_t len) = 0;
virtual int recv_timeout(unsigned char *buf, size_t len, int timeout)
{
return recv(buf, len);
}
size_t get_available_bytes();
protected:
/**
* mbedTLS internal structures (available after inheritance)
*/
mbedtls_ssl_context ssl_{};
mbedtls_x509_crt public_cert_{};
mbedtls_pk_context pk_key_{};
@ -42,35 +86,56 @@ protected:
mbedtls_ssl_config conf_{};
mbedtls_ctr_drbg_context ctr_drbg_{};
mbedtls_entropy_context entropy_{};
mbedtls_timing_delay_context timer_{};
mbedtls_ssl_cookie_ctx cookie_{};
const_buf client_id_{};
virtual void delay() {}
bool is_server_{false};
bool is_dtls_{false};
bool set_session();
bool get_session();
void reset_session();
bool is_session_loaded();
private:
static void print_error(const char *function, int error_code);
static int bio_write(void *ctx, const unsigned char *buf, size_t len);
static int bio_read(void *ctx, unsigned char *buf, size_t len);
int mbedtls_pk_parse_key( mbedtls_pk_context *ctx,
const unsigned char *key, size_t keylen,
const unsigned char *pwd, size_t pwdlen);
static int bio_read_tout(void *ctx, unsigned char *buf, size_t len, uint32_t timeout);
int mbedtls_pk_parse_key(mbedtls_pk_context *ctx,
const unsigned char *key, size_t keylen,
const unsigned char *pwd, size_t pwdlen);
struct unique_session {
unique_session()
{
::mbedtls_ssl_session_init(&s);
}
~unique_session()
{
::mbedtls_ssl_session_free(&s);
}
mbedtls_ssl_session *ptr()
{
return &s;
}
mbedtls_ssl_session s;
};
std::unique_ptr<unique_session> session_;
};
}

View File

@ -1,23 +1,34 @@
/*
* SPDX-FileCopyrightText: 2023 Espressif Systems (Shanghai) CO LTD
* SPDX-FileCopyrightText: 2023-2024 Espressif Systems (Shanghai) CO LTD
*
* SPDX-License-Identifier: Apache-2.0
*/
#include <mbedtls/timing.h>
#include "mbedtls/ctr_drbg.h"
#include "mbedtls/ssl.h"
#include "mbedtls_wrap.hpp"
bool Tls::init(is_server server, do_verify verify)
using namespace idf::mbedtls_cxx;
bool Tls::init(is_server server, do_verify verify, TlsConfig *config)
{
const char pers[] = "mbedtls_wrapper";
is_server_ = server == is_server{true};
is_dtls_ = config ? config->is_dtls : false;
uint32_t timeout = config ? config->timeout : 0;
mbedtls_entropy_init(&entropy_);
mbedtls_ctr_drbg_seed(&ctr_drbg_, mbedtls_entropy_func, &entropy_, (const unsigned char *)pers, sizeof(pers));
int ret = mbedtls_ssl_config_defaults(&conf_, server == is_server{true} ? MBEDTLS_SSL_IS_SERVER : MBEDTLS_SSL_IS_CLIENT, MBEDTLS_SSL_TRANSPORT_STREAM, MBEDTLS_SSL_PRESET_DEFAULT);
int endpoint = server == is_server{true} ? MBEDTLS_SSL_IS_SERVER : MBEDTLS_SSL_IS_CLIENT;
int transport = is_dtls_ ? MBEDTLS_SSL_TRANSPORT_DATAGRAM : MBEDTLS_SSL_TRANSPORT_STREAM;
int ret = mbedtls_ssl_config_defaults(&conf_, endpoint, transport, MBEDTLS_SSL_PRESET_DEFAULT);
if (ret) {
print_error("mbedtls_ssl_config_defaults", ret);
return false;
}
mbedtls_ssl_conf_rng(&conf_, mbedtls_ctr_drbg_random, &ctr_drbg_);
if (timeout) {
mbedtls_ssl_conf_read_timeout(&conf_, timeout);
}
mbedtls_ssl_conf_authmode(&conf_, verify == do_verify{true} ? MBEDTLS_SSL_VERIFY_REQUIRED : MBEDTLS_SSL_VERIFY_NONE);
ret = mbedtls_ssl_conf_own_cert(&conf_, &public_cert_, &pk_key_);
if (ret) {
@ -27,11 +38,34 @@ bool Tls::init(is_server server, do_verify verify)
if (verify == do_verify{true}) {
mbedtls_ssl_conf_ca_chain(&conf_, &ca_cert_, nullptr);
}
#if CONFIG_MBEDTLS_SSL_PROTO_DTLS
if (is_server_ && is_dtls_) {
if (!init_dtls_cookies()) {
return false;
}
}
#endif // MBEDTLS_SSL_PROTO_DTLS
ret = mbedtls_ssl_setup(&ssl_, &conf_);
if (ret) {
print_error("mbedtls_ssl_setup", ret);
return false;
}
if (timeout) {
mbedtls_ssl_set_timer_cb(&ssl_, &timer_, mbedtls_timing_set_delay, mbedtls_timing_get_delay);
}
#if CONFIG_MBEDTLS_SSL_PROTO_DTLS
if (is_server_ && is_dtls_ && config && config->client_id != const_buf {}) {
client_id_ = config->client_id;
if (!set_client_id()) {
return false;
}
}
#endif // MBEDTLS_SSL_PROTO_DTLS
return true;
}
@ -57,10 +91,19 @@ void Tls::print_error(const char *function, int error_code)
int Tls::handshake()
{
int ret = 0;
mbedtls_ssl_set_bio(&ssl_, this, bio_write, bio_read, nullptr);
mbedtls_ssl_set_bio(&ssl_, this, bio_write, bio_read, is_dtls_ ? bio_read_tout : nullptr);
while ( ( ret = mbedtls_ssl_handshake( &ssl_ ) ) != 0 ) {
if ( ret != MBEDTLS_ERR_SSL_WANT_READ && ret != MBEDTLS_ERR_SSL_WANT_WRITE ) {
#if CONFIG_MBEDTLS_SSL_PROTO_DTLS
if (is_server_ && is_dtls_ && ret == MBEDTLS_ERR_SSL_HELLO_VERIFY_REQUIRED) {
// hello verification requested -> restart the session with this client_id
if (!set_client_id()) {
return -1;
}
continue;
}
#endif // MBEDTLS_SSL_PROTO_DTLS
print_error( "mbedtls_ssl_handshake returned", ret );
return -1;
}
@ -81,6 +124,12 @@ int Tls::bio_read(void *ctx, unsigned char *buf, size_t len)
return s->recv(buf, len);
}
int Tls::bio_read_tout(void *ctx, unsigned char *buf, size_t len, uint32_t timeout)
{
auto s = static_cast<Tls *>(ctx);
return s->recv_timeout(buf, len, timeout);
}
int Tls::write(const unsigned char *buf, size_t len)
{
return mbedtls_ssl_write( &ssl_, buf, len );
@ -93,12 +142,12 @@ int Tls::read(unsigned char *buf, size_t len)
bool Tls::set_own_cert(const_buf crt, const_buf key)
{
int ret = mbedtls_x509_crt_parse(&public_cert_, crt.data(), crt.size());
int ret = mbedtls_x509_crt_parse(&public_cert_, crt.first, crt.second);
if (ret < 0) {
print_error("mbedtls_x509_crt_parse", ret);
return false;
}
ret = mbedtls_pk_parse_key(&pk_key_, key.data(), key.size(), nullptr, 0);
ret = mbedtls_pk_parse_key(&pk_key_, key.first, key.second, nullptr, 0);
if (ret < 0) {
print_error("mbedtls_pk_parse_keyfile", ret);
return false;
@ -108,7 +157,7 @@ bool Tls::set_own_cert(const_buf crt, const_buf key)
bool Tls::set_ca_cert(const_buf crt)
{
int ret = mbedtls_x509_crt_parse(&ca_cert_, crt.data(), crt.size());
int ret = mbedtls_x509_crt_parse(&ca_cert_, crt.first, crt.second);
if (ret < 0) {
print_error("mbedtls_x509_crt_parse", ret);
return false;
@ -188,3 +237,32 @@ bool Tls::is_session_loaded()
{
return session_ != nullptr;
}
#if CONFIG_MBEDTLS_SSL_PROTO_DTLS
bool Tls::init_dtls_cookies()
{
int ret = mbedtls_ssl_cookie_setup(&cookie_, mbedtls_ctr_drbg_random, &ctr_drbg_);
if (ret != 0) {
print_error("mbedtls_ssl_cookie_setup() failed", ret);
return false;
}
mbedtls_ssl_conf_dtls_cookies(&conf_, mbedtls_ssl_cookie_write, mbedtls_ssl_cookie_check, &cookie_);
return true;
}
bool Tls::set_client_id()
{
int ret;
if (client_id_ == const_buf{}) {
printf("client_id is not set");
return false;
}
mbedtls_ssl_session_reset(&ssl_);
if ((ret = mbedtls_ssl_set_client_transport_id(&ssl_, client_id_.first, client_id_.second)) != 0) {
print_error("mbedtls_ssl_set_client_transport_id()", ret);
return false;
}
return true;
}
#endif

View File

@ -0,0 +1,8 @@
# For more information about build system see
# https://docs.espressif.com/projects/esp-idf/en/latest/api-guides/build-system.html
# The following five lines of boilerplate have to be in your project's
# CMakeLists in this exact order for cmake to work correctly
cmake_minimum_required(VERSION 3.16)
include($ENV{IDF_PATH}/tools/cmake/project.cmake)
project(uart_mutual)

View File

@ -0,0 +1,17 @@
# UART mutual authentication test
This test creates a (D)TLS server and a client on one device and checks if they can perform a TLS handshake and exchange a message.
The test uses UART as the physical layer of communication channel and, since it runs on a single ESP32, it expects two UART ports interconnected as per below:
```
+------------------------+
| ESP32 |
| |
| UART-1 UART-2 |
+---25---26------4---5---+
| | | |
| +-------+ |
+----------------+
```
The test runs in two configurations: TLS and DTLS.

View File

@ -0,0 +1,2 @@
idf_component_register(SRCS "uart_mutual.cpp"
INCLUDE_DIRS ".")

View File

@ -0,0 +1,19 @@
menu "Test Configuration"
choice TEST_CONNECTION_METHOD
prompt "Choose connection method"
default TEST_TLS
help
Select type of connection. Either TLS or DTLS
config TEST_TLS
bool "TLS"
help
Use TLS method.
config TEST_DTLS
bool "DTLS"
help
Use DTLS method.
endchoice # TEST_CONNECTION_METHOD
endmenu

View File

@ -0,0 +1,8 @@
dependencies:
idf: ">=5.0"
espressif/mbedtls_cxx:
version: "*"
override_path: "../../.."
test_certs:
version: "*"
path: "../../../examples/test_certs"

View File

@ -0,0 +1,282 @@
/*
* SPDX-FileCopyrightText: 2024 Espressif Systems (Shanghai) CO LTD
*
* SPDX-License-Identifier: Unlicense OR CC0-1.0
*/
#include <thread>
#include <sys/socket.h>
#include <unistd.h>
#include <esp_check.h>
#include "esp_log.h"
#include "mbedtls_wrap.hpp"
#include "driver/uart.h"
#include "esp_event.h"
#include "esp_netif.h"
#include "test_certs.hpp"
namespace {
constexpr auto *TAG = "uart_mutual_tls";
/**
* Using DTLS the below is set to true.
* In that case, we need to receive the entire datagram, not a fragment
* This defines a very simple datagram protocol over UART:
* | HEADER (2bytes) | PAYLOAD ... |
* | dgram_len | dgram_payload |
*
* If `use_dgrams` is set to false, we perform TLS on UART stream.
* The UART driver is already a stream-like API (using ringbufer), so we simple read and write to UART
*/
#if CONFIG_TEST_TLS
const bool use_dgrams = false;
#elif CONFIG_TEST_DTLS
const bool use_dgrams = true;
#endif
}
using namespace idf::mbedtls_cxx;
using namespace test_certs;
class SecureLink: public Tls {
public:
explicit SecureLink(uart_port_t port, int tx, int rx) : Tls(), uart(port, tx, rx) {}
~SecureLink() = default;
int send(const unsigned char *buf, size_t len) override
{
if (use_dgrams) {
// sends a separate dgram header
uint16_t header = len;
uart_write_bytes(uart.port_, &header, 2);
}
return uart_write_bytes(uart.port_, buf, len);
}
int recv(unsigned char *buf, size_t len) override
{
// stream read
return uart.recv(buf, len, 0);
}
int recv_timeout(unsigned char *buf, size_t len, int timeout) override
{
// dgram read
return uart.recv_dgram(buf, len, timeout);
}
bool listen() // open as server
{
return open(true);
}
bool connect() // open as client
{
return open(false);
}
private:
bool open(bool server_not_client)
{
if (uart.init() != ESP_OK) {
return false;
}
while (!uart.debounce(server_not_client)) {
printf("debouncing...\n");
usleep(10000);
}
TlsConfig config{};
config.is_dtls = use_dgrams;
config.timeout = 10000;
if (server_not_client) {
const unsigned char client_id[] = "Client1";
config.client_id = std::make_pair(client_id, sizeof(client_id));
}
if (!init(is_server{server_not_client}, do_verify{true}, &config)) {
return false;
}
return handshake() == 0;
}
/**
* RAII wrapper of UART
*/
struct uart_info {
uart_port_t port_;
QueueHandle_t queue_{};
int tx_, rx_;
// used for datagrams
bool header_{true};
int in_payload_{0};
int payload_len_{0};
uint8_t payload_[1600] {};
explicit uart_info(uart_port_t port, int tx, int rx): port_(port), tx_(tx), rx_(rx)
{
}
esp_err_t init()
{
uart_config_t uart_config = {};
uart_config.baud_rate = 115200;
uart_config.data_bits = UART_DATA_8_BITS;
uart_config.parity = UART_PARITY_DISABLE;
uart_config.stop_bits = UART_STOP_BITS_1;
uart_config.flow_ctrl = UART_HW_FLOWCTRL_DISABLE;
uart_config.source_clk = UART_SCLK_DEFAULT;
ESP_RETURN_ON_ERROR(uart_driver_install(port_, 1024, 0, 1, &queue_, 0), TAG, "Failed to install UART");
ESP_RETURN_ON_ERROR(uart_param_config(port_, &uart_config), TAG, "Failed to set params");
ESP_RETURN_ON_ERROR(uart_set_pin(port_, tx_, rx_, UART_PIN_NO_CHANGE, UART_PIN_NO_CHANGE), TAG, "Failed to set UART pins");
ESP_RETURN_ON_ERROR(uart_set_rx_timeout(port_, 10), TAG, "Failed to set UART Rx timeout");
return ESP_OK;
}
~uart_info()
{
uart_driver_delete(port_);
}
bool debounce(bool server)
{
uint8_t data = 0;
if (server) {
while (uart_read_bytes(port_, &data, 1, 0) != 0) {
if (data == 0x55) {
uart_write_bytes(port_, &data, 1);
return true;
}
}
return false;
}
data = 0x55;
uart_write_bytes(port_, &data, 1);
data = 0;
uart_read_bytes(port_, &data, 1, pdMS_TO_TICKS(1000));
if (data != 0x55) {
uart_flush_input(port_);
return false;
}
return true;
}
int recv(unsigned char *buf, size_t size, int timeout) // this is for stream transport
{
int len = uart_read_bytes(port_, buf, size, pdMS_TO_TICKS(timeout));
if (len == 0) {
return MBEDTLS_ERR_SSL_WANT_READ;
}
return len;
}
int recv_dgram(unsigned char *buf, size_t size, int timeout) // this is for datagrams
{
uart_event_t event = {};
size_t length;
uart_get_buffered_data_len(port_, &length);
if (length == 0) {
xQueueReceive(queue_, &event, pdMS_TO_TICKS(timeout));
}
uart_get_buffered_data_len(port_, &length);
if (length == 0) {
return MBEDTLS_ERR_SSL_WANT_READ;
}
if (header_) {
if (length >= 2) {
uart_read_bytes(port_, &payload_len_, 2, 0);
header_ = false;
length -= 2;
}
}
if (!header_ && length > 0) {
int to_read = payload_len_ - in_payload_;
int l = uart_read_bytes(port_, &payload_[in_payload_], to_read, 0);
in_payload_ += l;
if (payload_len_ == in_payload_) {
header_ = true;
memcpy(buf, payload_, payload_len_);
in_payload_ = 0;
return payload_len_;
}
}
return MBEDTLS_ERR_SSL_WANT_READ;
}
} uart;
};
namespace {
void tls_client()
{
const unsigned char message[] = "Hello\n";
unsigned char reply[128];
SecureLink client(UART_NUM_2, 4, 5);
client.set_hostname(get_server_cn());
if (!client.set_own_cert(get_buf(type::clientcert), get_buf(type::clientkey))) {
ESP_LOGE(TAG, "Failed to set own cert");
return;
}
if (!client.set_ca_cert(get_buf(type::cacert))) {
ESP_LOGE(TAG, "Failed to set peer's cert");
return;
}
if (!client.connect()) {
ESP_LOGE(TAG, "Failed to CONNECT! %d", errno);
return;
}
ESP_LOGI(TAG, "client opened...");
if (client.write(message, sizeof(message)) < 0) {
ESP_LOGE(TAG, "Failed to write!");
return;
}
int len;
while ((len = client.read(reply, sizeof(reply))) == MBEDTLS_ERR_SSL_WANT_READ) {
vTaskDelay(pdMS_TO_TICKS(500));
}
if (len < 0) {
ESP_LOGE(TAG, "Failed to read!");
return;
}
ESP_LOGI(TAG, "Successfully received: %.*s", len, reply);
}
void tls_server()
{
unsigned char message[128];
SecureLink server(UART_NUM_1, 25, 26);
if (!server.set_own_cert(get_buf(type::servercert), get_buf(type::serverkey))) {
ESP_LOGE(TAG, "Failed to set own cert");
return;
}
if (!server.set_ca_cert(get_buf(type::cacert))) {
ESP_LOGE(TAG, "Failed to set peer's cert");
return;
}
ESP_LOGI(TAG, "openning...");
if (!server.listen()) {
ESP_LOGE(TAG, "Failed to OPEN! %d", errno);
return;
}
int len;
while ((len = server.read(message, sizeof(message))) == MBEDTLS_ERR_SSL_WANT_READ) {
vTaskDelay(pdMS_TO_TICKS(500));
}
if (len < 0) {
ESP_LOGE(TAG, "Failed to read! %x", -len);
return;
}
ESP_LOGI(TAG, "Received from client: %.*s", len, message);
if (server.write(message, len) < 0) {
ESP_LOGE(TAG, "Failed to write!");
return;
}
ESP_LOGI(TAG, "Written back");
vTaskDelay(pdMS_TO_TICKS(500));
}
} // namespace
extern "C" void app_main()
{
ESP_ERROR_CHECK(esp_netif_init());
ESP_ERROR_CHECK(esp_event_loop_create_default());
std::thread t2(tls_server);
std::thread t1(tls_client);
t1.join();
t2.join();
}

View File

@ -0,0 +1,3 @@
CONFIG_TEST_DTLS=y
CONFIG_MBEDTLS_SSL_PROTO_DTLS=y
CONFIG_PTHREAD_TASK_STACK_SIZE_DEFAULT=8192

View File

@ -0,0 +1,3 @@
CONFIG_TEST_TLS=y
CONFIG_MBEDTLS_SSL_PROTO_DTLS=y
CONFIG_PTHREAD_TASK_STACK_SIZE_DEFAULT=8192

View File

@ -0,0 +1,3 @@
CONFIG_IDF_TARGET="esp32"
CONFIG_MBEDTLS_SSL_PROTO_DTLS=y
CONFIG_PTHREAD_TASK_STACK_SIZE_DEFAULT=8192