Skip to content

Commit 4bdcb63

Browse files
committed
DTLS/mbedTLS/ESP32 support
1 parent 9724c1c commit 4bdcb63

File tree

4 files changed

+305
-0
lines changed

4 files changed

+305
-0
lines changed

DtlsUdp.cpp

Lines changed: 145 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,145 @@
1+
#if defined(ESP32)
2+
// NOTE: This class is only available for ESP32 because it depends on mbedtls, which is provided by the ESP32 Arduino core.
3+
// DtlsUdp.cpp
4+
// mbedTLS DTLS wrapper class skeleton implementation for Arduino
5+
#include "DtlsUdp.h"
6+
7+
DtlsUdp::DtlsUdp() : connected(false) {
8+
mbedtls_net_init(&net_ctx);
9+
mbedtls_ssl_init(&ssl);
10+
mbedtls_ssl_config_init(&conf);
11+
mbedtls_entropy_init(&entropy);
12+
mbedtls_ctr_drbg_init(&ctr_drbg);
13+
mbedtls_x509_crt_init(&ca_cert);
14+
mbedtls_x509_crt_init(&client_cert);
15+
mbedtls_pk_init(&client_key);
16+
}
17+
18+
DtlsUdp::~DtlsUdp() {
19+
end();
20+
mbedtls_ssl_free(&ssl);
21+
mbedtls_ssl_config_free(&conf);
22+
mbedtls_ctr_drbg_free(&ctr_drbg);
23+
mbedtls_entropy_free(&entropy);
24+
mbedtls_net_free(&net_ctx);
25+
mbedtls_x509_crt_free(&ca_cert);
26+
mbedtls_x509_crt_free(&client_cert);
27+
mbedtls_pk_free(&client_key);
28+
}
29+
30+
uint8_t DtlsUdp::begin(uint16_t port) {
31+
// For DTLS: No need to initialize UDP socket
32+
return 1;
33+
}
34+
35+
bool DtlsUdp::connect(IPAddress ip, int port) {
36+
char ipstr[16];
37+
sprintf(ipstr, "%d.%d.%d.%d", ip[0], ip[1], ip[2], ip[3]);
38+
char portstr[8];
39+
snprintf(portstr, sizeof(portstr), "%d", port);
40+
if (mbedtls_net_connect(&net_ctx, ipstr, portstr, MBEDTLS_NET_PROTO_UDP) != 0) return false;
41+
if (mbedtls_ssl_config_defaults(&conf, MBEDTLS_SSL_IS_CLIENT, MBEDTLS_SSL_TRANSPORT_DATAGRAM, MBEDTLS_SSL_PRESET_DEFAULT) != 0) return false;
42+
mbedtls_ssl_conf_authmode(&conf, MBEDTLS_SSL_VERIFY_NONE);
43+
mbedtls_ssl_conf_rng(&conf, mbedtls_ctr_drbg_random, &ctr_drbg);
44+
if (mbedtls_ssl_setup(&ssl, &conf) != 0) return false;
45+
mbedtls_ssl_set_bio(&ssl, &net_ctx, mbedtls_net_send, mbedtls_net_recv, NULL);
46+
// DTLS handshake
47+
int ret;
48+
do {
49+
ret = mbedtls_ssl_handshake(&ssl);
50+
} while (ret == MBEDTLS_ERR_SSL_WANT_READ || ret == MBEDTLS_ERR_SSL_WANT_WRITE);
51+
connected = (ret == 0);
52+
return connected;
53+
}
54+
55+
int DtlsUdp::beginPacket(IPAddress ip, uint16_t port) {
56+
_remoteIP = ip;
57+
_remotePort = port;
58+
return 1;
59+
}
60+
61+
int DtlsUdp::beginPacket(const char *host, uint16_t port) {
62+
// Hostname resolution not supported (implement if needed)
63+
return 0;
64+
}
65+
66+
int DtlsUdp::endPacket() {
67+
// For DTLS: send is done directly in write
68+
return 1;
69+
}
70+
71+
size_t DtlsUdp::write(const uint8_t *buf, size_t size) {
72+
if (!connected) return 0;
73+
return mbedtls_ssl_write(&ssl, buf, size);
74+
}
75+
76+
size_t DtlsUdp::write(uint8_t data) {
77+
return write(&data, 1);
78+
}
79+
80+
int DtlsUdp::parsePacket() {
81+
// DTLS is connection-oriented, always treat as one packet
82+
return 1;
83+
}
84+
85+
int DtlsUdp::available() {
86+
// Check if receive buffer has data (simple implementation)
87+
return 1;
88+
}
89+
90+
int DtlsUdp::read(unsigned char* buffer, size_t len) {
91+
if (!connected) return 0;
92+
return mbedtls_ssl_read(&ssl, buffer, len);
93+
}
94+
95+
int DtlsUdp::read(char* buffer, size_t len) {
96+
if (!connected) return 0;
97+
return mbedtls_ssl_read(&ssl, (unsigned char*)buffer, len);
98+
}
99+
100+
int DtlsUdp::read() {
101+
unsigned char b;
102+
return read(&b, 1) == 1 ? b : -1;
103+
}
104+
105+
int DtlsUdp::peek() { return -1; }
106+
107+
void DtlsUdp::flush() {}
108+
109+
IPAddress DtlsUdp::remoteIP() { return _remoteIP; }
110+
111+
uint16_t DtlsUdp::remotePort() { return _remotePort; }
112+
113+
void DtlsUdp::end() {
114+
if (connected) {
115+
mbedtls_ssl_close_notify(&ssl);
116+
connected = false;
117+
}
118+
}
119+
120+
void DtlsUdp::stop() {
121+
// For DTLS: nothing to do
122+
}
123+
124+
bool DtlsUdp::setRootCA(const char* ca_pem) {
125+
mbedtls_x509_crt_free(&ca_cert);
126+
mbedtls_x509_crt_init(&ca_cert);
127+
int ret = mbedtls_x509_crt_parse(&ca_cert, (const unsigned char*)ca_pem, strlen(ca_pem)+1);
128+
if (ret != 0) return false;
129+
mbedtls_ssl_conf_ca_chain(&conf, &ca_cert, NULL);
130+
mbedtls_ssl_conf_authmode(&conf, MBEDTLS_SSL_VERIFY_REQUIRED);
131+
return true;
132+
}
133+
134+
bool DtlsUdp::setClientCert(const char* cert_pem, const char* key_pem) {
135+
mbedtls_x509_crt_free(&client_cert);
136+
mbedtls_x509_crt_init(&client_cert);
137+
mbedtls_pk_free(&client_key);
138+
mbedtls_pk_init(&client_key);
139+
int ret1 = mbedtls_x509_crt_parse(&client_cert, (const unsigned char*)cert_pem, strlen(cert_pem)+1);
140+
int ret2 = mbedtls_pk_parse_key(&client_key, (const unsigned char*)key_pem, strlen(key_pem)+1, NULL, 0, mbedtls_ctr_drbg_random, &ctr_drbg);
141+
if (ret1 != 0 || ret2 != 0) return false;
142+
mbedtls_ssl_conf_own_cert(&conf, &client_cert, &client_key);
143+
return true;
144+
}
145+
#endif // ARDUINO_ARCH_ESP32

DtlsUdp.h

Lines changed: 63 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,63 @@
1+
#if defined(ESP32)
2+
// DtlsUdp.h
3+
// mbedTLS DTLS wrapper class skeleton for Arduino
4+
// Example implementation: UDP-compatible API, DTLS communication using mbedTLS
5+
6+
#ifndef __DTLS_UDP_H__
7+
#define __DTLS_UDP_H__
8+
9+
#include <Arduino.h>
10+
#include <IPAddress.h>
11+
#include <mbedtls/ssl.h>
12+
#include <mbedtls/net_sockets.h>
13+
#include <mbedtls/entropy.h>
14+
#include <mbedtls/ctr_drbg.h>
15+
#include <mbedtls/error.h>
16+
#include "Udp.h"
17+
18+
// NOTE: This class is only available for ESP32 because it depends on mbedtls, which is provided by the ESP32 Arduino core.
19+
20+
class DtlsUdp : public UDP {
21+
public:
22+
DtlsUdp();
23+
~DtlsUdp();
24+
// UDPインターフェースの実装
25+
uint8_t begin(uint16_t port) override;
26+
void stop() override;
27+
int beginPacket(IPAddress ip, uint16_t port) override;
28+
int beginPacket(const char *host, uint16_t port) override;
29+
int endPacket() override;
30+
size_t write(const uint8_t *buf, size_t size) override;
31+
size_t write(uint8_t data) override;
32+
int parsePacket() override;
33+
int available() override;
34+
int read(unsigned char* buffer, size_t len) override;
35+
int read(char* buffer, size_t len) override;
36+
int read() override;
37+
int peek() override;
38+
void flush() override;
39+
IPAddress remoteIP() override;
40+
uint16_t remotePort() override;
41+
// DTLS独自
42+
bool connect(IPAddress ip, int port);
43+
void end();
44+
// --- 証明書/鍵の設定用API ---
45+
bool setRootCA(const char* ca_pem);
46+
bool setClientCert(const char* cert_pem, const char* key_pem);
47+
private:
48+
mbedtls_ssl_context ssl;
49+
mbedtls_ssl_config conf;
50+
mbedtls_net_context net_ctx;
51+
mbedtls_entropy_context entropy;
52+
mbedtls_ctr_drbg_context ctr_drbg;
53+
mbedtls_x509_crt ca_cert;
54+
mbedtls_x509_crt client_cert;
55+
mbedtls_pk_context client_key;
56+
bool connected;
57+
IPAddress _remoteIP;
58+
uint16_t _remotePort;
59+
};
60+
61+
#endif // __DTLS_UDP_H__
62+
63+
#endif // ESP32

README.md

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -39,3 +39,11 @@ gcc -o coap-client ./examples/client.c ./examples/coap_list.c -I./include -I. -L
3939

4040
## Particle Photon, Core compatible
4141
Check <a href="https://github.com/hirotakaster/CoAP">this</a> version of the library for Particle Photon, Core compatibility.
42+
43+
## DTLS/ESP32 Support (2025 Update)
44+
45+
- Added DTLS (CoAP over DTLS) client support using mbedTLS for ESP32 only.
46+
- New class: `DtlsUdp` (in `DtlsUdp.h`/`DtlsUdp.cpp`) provides a UDP-like interface for DTLS, leveraging mbedTLS (available only on ESP32 Arduino core).
47+
- Example: `examples/dtls_test/dtls_test.ino` demonstrates a DTLS CoAP client communicating with a libcoap server using Let's Encrypt Root CA.
48+
- **Note:** DTLS/mbedTLS features are only available for ESP32. The code is conditionally compiled and will not be included for other platforms.
49+
- See code comments for details on platform and dependency restrictions.

examples/dtls_test/dtls_test.ino

Lines changed: 89 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,89 @@
1+
// NOTE: This sketch is only available for ESP32 because it depends on mbedtls, which is provided by the ESP32 Arduino core.
2+
// dtls_test.ino
3+
// DTLS CoAP client auto test sketch
4+
// Checks GET response from libcoap server
5+
#include <SPI.h>
6+
#include <Dhcp.h>
7+
#include <Dns.h>
8+
#include <Ethernet.h>
9+
#include <coap-simple.h>
10+
#include "DtlsUdp.h"
11+
12+
byte mac[] = { 0x00, 0xAA, 0xBB, 0xCC, 0xDE, 0x02 };
13+
IPAddress dev_ip(10,10,10,10); // Change as needed
14+
15+
const int LED_PIN = 13;
16+
DtlsUdp dtlsUdp;
17+
Coap coap(dtlsUdp);
18+
bool testPassed = false;
19+
20+
// Let's Encrypt ISRG Root X1
21+
const char lets_encrypt_root_pem[] =
22+
"-----BEGIN CERTIFICATE-----\n"
23+
"MIIFazCCA1OgAwIBAgISA7lE0QJzi10BGiGgF2pQb7MA0GCSqGSIb3DQEBCwUA\n"
24+
"MEoxCzAJBgNVBAYTAlVTMRMwEQYDVQQKEwpJbnRlcm5ldCBTZWN1cml0eTEhMB8G\n"
25+
"A1UEAxMYSVNSRyBSb290IFgxIE5ldHdvcmsgQ0EwHhcNMTQwNzE2MTIxMzQwWhcN\n"
26+
"MzQwNzE2MTIxMzQwWjBKMQswCQYDVQQGEwJVUzETMBEGA1UEChMKSW50ZXJuZXQg\n"
27+
"U2VjdXJpdHkxITAfBgNVBAMTGEhSRyBSb290IFgxIE5ldHdvcmsgQ0EwggIiMA0G\n"
28+
"CSqGSIb3DQEBAQUAA4ICDwAwggIKAoICAQC7Ahh1gC1XkQwZ0pniS3pSkeCZMt2g\n"
29+
"kFh2RFxYDs2fzGEGZm4G6dkprdFM5lTyBC0bYKT1eZq9VHtV6nRvWmvMRGsiE9z1\n"
30+
"k5lFfW4rWuIwoMvVJE9idh+NangNh4tW7x1YgnSUZXoqBYwygJyI072QtdgQXl3k\n"
31+
"n2Ue+a83H8XIv2qxGn8pY/+bexdFv+DE5jBqFaUG2yygxN6E466+vWXTjhBMWent\n"
32+
"lMPmMXxMvmXrKE+g6u40qCMgfHdCqkfNNpJBbGAbIYW/W2PASi6DPd7OJbRRqtD9\n"
33+
"pz50jdK5Zk90un0nLBKBPXn1HULICwhf66A1VpzwuNFuIBqmoeZaZX6mE6xPD58x\n"
34+
"H5TADaBrZEcD3xKhsR4HIX66vepQP9enZ5bY3bT5iAG2wE8xmPKzW0fvk/sdzEYw\n"
35+
"+nOiYzcuEGoVYLRNvKcJsteVEh9UpAJZciV06P88GJEqn3Ejj6inUeJ8V+RaHcRU\n"
36+
"W2KIiMzFxljI0X58F3RrgPf63HgFUsVTNff7kwh28ykVfoCkb0z7dxyzKDn5Xxhx\n"
37+
"nQIDAQABo0IwQDAOBgNVHQ8BAf8EBAMCAQYwDwYDVR0TAQH/BAUwAwEB/zAdBgNV\n"
38+
"HQ4EFgQUXxhxL7sRKqzZo4PMBVXgS5aXoaIwDQYJKoZIhvcNAQELBQADggEBAJk1\n"
39+
"gQXl3k2I5OGucs5cjox96D65gis6pZeRAEIJ5zsxFrqOeE0zY4xXHzkwmo7aX6ix\n"
40+
"bAGP82afgAvFp8ZUBKbjDg7sWXBJgp7wa9u0edPFsKnzGWx/ju0RduCMsZ/HXXIQ\n"
41+
"t1vZ1tcHTul3e8DqRLVQjaxAg/P6nxsVXni4eWh05rq6ArlTc95xJMu38xpv8uK9\n"
42+
"B6f+GO6zkRgZNpmjQe7YQDdyCjTiMQuuLHcEGoVYLRNvKcJsteVEh9UpAJZciV06\n"
43+
"P88GJEqn3Ejj6inUeJ8V+RaHcRUW2KIiMzFxljI0X58F3RrgPf63HgFUsVTNff7k\n"
44+
"wh28ykVfoCkb0z7dxyzKDn5XxhxnQ==\n"
45+
"-----END CERTIFICATE-----\n";
46+
47+
void callback_response(CoapPacket &packet, IPAddress ip, int port) {
48+
Serial.println("[Coap DTLS Response got]");
49+
char p[packet.payloadlen + 1];
50+
memcpy(p, packet.payload, packet.payloadlen);
51+
p[packet.payloadlen] = NULL;
52+
Serial.print("Response: ");
53+
Serial.println(p);
54+
if (strstr(p, "Hello") != NULL) {
55+
testPassed = true;
56+
digitalWrite(LED_PIN, HIGH);
57+
} else {
58+
testPassed = false;
59+
digitalWrite(LED_PIN, LOW);
60+
}
61+
}
62+
63+
void setup() {
64+
Serial.begin(9600);
65+
pinMode(LED_PIN, OUTPUT);
66+
digitalWrite(LED_PIN, LOW);
67+
Ethernet.begin(mac, dev_ip);
68+
Serial.print("My IP address: ");
69+
Serial.println(Ethernet.localIP());
70+
dtlsUdp.begin(0);
71+
dtlsUdp.setRootCA(lets_encrypt_root_pem); // Set Root CA
72+
dtlsUdp.connect(IPAddress(10,10,10,20), 5684);
73+
Serial.println("Setup Response Callback");
74+
coap.response(callback_response);
75+
coap.start();
76+
}
77+
78+
void loop() {
79+
Serial.println("Send DTLS CoAP Test Request");
80+
int msgid = coap.get(IPAddress(10,10,10,20), 5684, "test");
81+
delay(2000);
82+
coap.loop();
83+
if (testPassed) {
84+
Serial.println("[TEST PASS] DTLS CoAP communication successful");
85+
} else {
86+
Serial.println("[TEST FAIL] Invalid response or communication failed");
87+
}
88+
delay(3000);
89+
}

0 commit comments

Comments
 (0)