forked from espressif/arduino-esp32
-
-
Notifications
You must be signed in to change notification settings - Fork 7
/
Copy pathssl_client.cpp
327 lines (250 loc) · 11 KB
/
ssl_client.cpp
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
/* Provide SSL/TLS functions to ESP32 with Arduino IDE
*
* Adapted from the ssl_client1 example in mbedtls.
*
* Original Copyright (C) 2006-2015, ARM Limited, All Rights Reserved, Apache 2.0 License.
* Additions Copyright (C) 2017 Evandro Luis Copercini, Apache 2.0 License.
*/
#include "Arduino.h"
#include <lwip/sockets.h>
#include <lwip/err.h>
#include <lwip/sockets.h>
#include <lwip/sys.h>
#include <lwip/netdb.h>
#include "ssl_client.h"
const char *pers = "esp32-tls";
#define DEBUG true //Set false to supress debug messages
#ifdef DEBUG
#define DEBUG_PRINT(...) printf( __VA_ARGS__ )
#else
#define DEBUG_PRINT(x)
#endif
#ifdef MBEDTLS_DEBUG_C
#define MBEDTLS_DEBUG_LEVEL 4
/* mbedtls debug function that translates mbedTLS debug output
to ESP_LOGx debug output.
MBEDTLS_DEBUG_LEVEL 4 means all mbedTLS debug output gets sent here,
and then filtered to the ESP logging mechanism.
*/
static void mbedtls_debug(void *ctx, int level,
const char *file, int line,
const char *str)
{
const char *MBTAG = "mbedtls";
char *file_sep;
/* Shorten 'file' from the whole file path to just the filename
This is a bit wasteful because the macros are compiled in with
the full _FILE_ path in each case.
*/
file_sep = rindex(file, '/');
if (file_sep) {
file = file_sep + 1;
}
switch (level) {
case 1:
printf( "%s:%d %s \n", file, line, str);
break;
case 2:
case 3:
printf( "%s:%d %s \n", file, line, str);
case 4:
printf( "%s:%d %s \n", file, line, str);
break;
default:
printf( "Unexpected log level %d: %s \n", level, str);
break;
}
}
#endif
void ssl_init(sslclient_context *ssl_client)
{
/*
* Initialize the RNG and the session data
*/
mbedtls_ssl_init(&ssl_client->ssl_ctx);
mbedtls_ssl_config_init(&ssl_client->ssl_conf);
mbedtls_ctr_drbg_init(&ssl_client->drbg_ctx);
}
int start_ssl_client(sslclient_context *ssl_client, uint32_t ipAddress, uint32_t port, unsigned char *rootCABuff, unsigned char *cli_cert, unsigned char *cli_key)
{
char buf[512];
int ret, flags, len, timeout;
int enable = 1;
DEBUG_PRINT("Free heap before TLS %u\n", xPortGetFreeHeapSize());
do {
ssl_client->socket = -1;
ssl_client->socket = lwip_socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
if (ssl_client->socket < 0) {
printf("\r\nERROR opening socket\r\n");
return ssl_client->socket;
}
struct sockaddr_in serv_addr;
memset(&serv_addr, 0, sizeof(serv_addr));
serv_addr.sin_family = AF_INET;
serv_addr.sin_addr.s_addr = ipAddress;
serv_addr.sin_port = htons(port);
if (lwip_connect(ssl_client->socket, (struct sockaddr *)&serv_addr, sizeof(serv_addr)) == 0) {
timeout = 30000;
lwip_setsockopt(ssl_client->socket, SOL_SOCKET, SO_RCVTIMEO, &timeout, sizeof(timeout));
lwip_setsockopt(ssl_client->socket, SOL_SOCKET, SO_SNDTIMEO, &timeout, sizeof(timeout));
lwip_setsockopt(ssl_client->socket, IPPROTO_TCP, TCP_NODELAY, &enable, sizeof(enable));
lwip_setsockopt(ssl_client->socket, SOL_SOCKET, SO_KEEPALIVE, &enable, sizeof(enable));
} else {
printf("\r\nConnect to Server failed!\r\n");
ret = -1;
break;
}
fcntl( ssl_client->socket, F_SETFL, fcntl( ssl_client->socket, F_GETFL, 0 ) | O_NONBLOCK );
DEBUG_PRINT( "Seeding the random number generator\n");
mbedtls_entropy_init(&ssl_client->entropy_ctx);
if ((ret = mbedtls_ctr_drbg_seed(&ssl_client->drbg_ctx, mbedtls_entropy_func,
&ssl_client->entropy_ctx, (const unsigned char *) pers, strlen(pers))) != 0) {
printf( "mbedtls_ctr_drbg_seed returned %d \n", ret);
break;
}
/* MBEDTLS_SSL_VERIFY_REQUIRED if a CA certificate is defined on Arduino IDE and
MBEDTLS_SSL_VERIFY_NONE if not.
*/
if (rootCABuff != NULL) {
DEBUG_PRINT( "Loading CA cert\n");
mbedtls_x509_crt_init(&ssl_client->ca_cert);
mbedtls_ssl_conf_authmode(&ssl_client->ssl_conf, MBEDTLS_SSL_VERIFY_REQUIRED);
ret = mbedtls_x509_crt_parse(&ssl_client->ca_cert, (const unsigned char *)rootCABuff, strlen((const char *)rootCABuff) + 1);
mbedtls_ssl_conf_ca_chain(&ssl_client->ssl_conf, &ssl_client->ca_cert, NULL);
//mbedtls_ssl_conf_verify(&ssl_client->ssl_ctx, my_verify, NULL );
if (ret < 0) {
printf( "CA cert: mbedtls_x509_crt_parse returned -0x%x\n\n", -ret);
break;
}
} else {
mbedtls_ssl_conf_authmode(&ssl_client->ssl_conf, MBEDTLS_SSL_VERIFY_NONE);
}
if (cli_cert != NULL && cli_key != NULL) {
mbedtls_x509_crt_init(&ssl_client->client_cert);
mbedtls_pk_init(&ssl_client->client_key);
DEBUG_PRINT( "Loading CRT cert\n");
ret = mbedtls_x509_crt_parse(&ssl_client->client_cert, (const unsigned char *)cli_cert, strlen((const char *)cli_cert) + 1);
if (ret < 0) {
printf( "CRT cert: mbedtls_x509_crt_parse returned -0x%x\n\n", -ret);
break;
}
DEBUG_PRINT( "Loading private key\n");
ret = mbedtls_pk_parse_key(&ssl_client->client_key, (const unsigned char *)cli_key, strlen((const char *)cli_key) + 1, NULL, 0);
if (ret < 0) {
printf( "PRIVATE KEY: mbedtls_x509_crt_parse returned -0x%x\n\n", -ret);
break;
}
mbedtls_ssl_conf_own_cert(&ssl_client->ssl_conf, &ssl_client->client_cert, &ssl_client->client_key);
}
/*
// TODO: implement match CN verification
DEBUG_PRINT( "Setting hostname for TLS session...\n");
// Hostname set here should match CN in server certificate
if((ret = mbedtls_ssl_set_hostname(&ssl_client->ssl_ctx, host)) != 0)
{
printf( "mbedtls_ssl_set_hostname returned -0x%x\n", -ret);
break;
}
*/
DEBUG_PRINT( "Setting up the SSL/TLS structure...\n");
if ((ret = mbedtls_ssl_config_defaults(&ssl_client->ssl_conf,
MBEDTLS_SSL_IS_CLIENT,
MBEDTLS_SSL_TRANSPORT_STREAM,
MBEDTLS_SSL_PRESET_DEFAULT)) != 0) {
printf( "mbedtls_ssl_config_defaults returned %d\n", ret);
break;
}
mbedtls_ssl_conf_rng(&ssl_client->ssl_conf, mbedtls_ctr_drbg_random, &ssl_client->drbg_ctx);
#ifdef MBEDTLS_DEBUG_C
mbedtls_debug_set_threshold(MBEDTLS_DEBUG_LEVEL);
mbedtls_ssl_conf_dbg(&ssl_client->ssl_conf, mbedtls_debug, NULL);
#endif
if ((ret = mbedtls_ssl_setup(&ssl_client->ssl_ctx, &ssl_client->ssl_conf)) != 0) {
printf( "mbedtls_ssl_setup returned -0x%x\n\n", -ret);
break;
}
mbedtls_ssl_set_bio(&ssl_client->ssl_ctx, &ssl_client->socket, mbedtls_net_send, mbedtls_net_recv, NULL );
DEBUG_PRINT( "Performing the SSL/TLS handshake...\n");
while ((ret = mbedtls_ssl_handshake(&ssl_client->ssl_ctx)) != 0) {
if (ret != MBEDTLS_ERR_SSL_WANT_READ && ret != MBEDTLS_ERR_SSL_WANT_WRITE && ret != -76) {
printf( "mbedtls_ssl_handshake returned -0x%x\n", -ret);
break;
}
delay(10);
vPortYield();
}
if (cli_cert != NULL && cli_key != NULL) {
DEBUG_PRINT("Protocol is %s \nCiphersuite is %s\n", mbedtls_ssl_get_version(&ssl_client->ssl_ctx), mbedtls_ssl_get_ciphersuite(&ssl_client->ssl_ctx));
if ((ret = mbedtls_ssl_get_record_expansion(&ssl_client->ssl_ctx)) >= 0) {
DEBUG_PRINT("Record expansion is %d\n", ret);
} else {
DEBUG_PRINT("Record expansion is unknown (compression)\n");
}
}
DEBUG_PRINT( "Verifying peer X.509 certificate...\n");
if ((flags = mbedtls_ssl_get_verify_result(&ssl_client->ssl_ctx)) != 0) {
printf( "Failed to verify peer certificate!\n");
bzero(buf, sizeof(buf));
mbedtls_x509_crt_verify_info(buf, sizeof(buf), " ! ", flags);
printf( "verification info: %s\n", buf);
stop_ssl_socket(ssl_client, rootCABuff, cli_cert, cli_key); //It's not safe continue.
} else {
DEBUG_PRINT( "Certificate verified.\n");
}
} while (0);
DEBUG_PRINT("Free heap after TLS %u\n", xPortGetFreeHeapSize());
return ssl_client->socket;
}
void stop_ssl_socket(sslclient_context *ssl_client, unsigned char *rootCABuff, unsigned char *cli_cert, unsigned char *cli_key)
{
DEBUG_PRINT( "\nCleaning SSL connection.\n");
close(ssl_client->socket);
ssl_client->socket = -1;
mbedtls_ssl_free(&ssl_client->ssl_ctx);
mbedtls_ssl_config_free(&ssl_client->ssl_conf);
mbedtls_ctr_drbg_free(&ssl_client->drbg_ctx);
mbedtls_entropy_free(&ssl_client->entropy_ctx);
if (rootCABuff != NULL) {
mbedtls_x509_crt_free(&ssl_client->ca_cert);
}
if (cli_cert != NULL) {
mbedtls_x509_crt_free(&ssl_client->client_cert);
}
if (cli_key != NULL) {
mbedtls_pk_free(&ssl_client->client_key);
}
}
int data_to_read(sslclient_context *ssl_client)
{
int ret, res;
ret = mbedtls_ssl_read(&ssl_client->ssl_ctx, NULL, 0);
//printf("RET: %i\n",ret); //for low level debug
res = mbedtls_ssl_get_bytes_avail(&ssl_client->ssl_ctx);
//printf("RES: %i\n",res);
if (ret != MBEDTLS_ERR_SSL_WANT_READ && ret != MBEDTLS_ERR_SSL_WANT_WRITE && ret < 0 && ret != -76) {
printf("MbedTLS error %i", ret);
}
return res;
}
int send_ssl_data(sslclient_context *ssl_client, const uint8_t *data, uint16_t len)
{
//DEBUG_PRINT( "Writing HTTP request...\n"); //for low level debug
int ret = -1;
while ((ret = mbedtls_ssl_write(&ssl_client->ssl_ctx, data, len)) <= 0) {
if (ret != MBEDTLS_ERR_SSL_WANT_READ && ret != MBEDTLS_ERR_SSL_WANT_WRITE && ret != -76) {
printf( "mbedtls_ssl_write returned -0x%x\n", -ret);
break;
}
}
len = ret;
//DEBUG_PRINT( "%d bytes written\n", len); //for low level debug
return ret;
}
int get_ssl_receive(sslclient_context *ssl_client, uint8_t *data, int length)
{
//DEBUG_PRINT( "Reading HTTP response...\n"); //for low level debug
int ret = -1;
ret = mbedtls_ssl_read(&ssl_client->ssl_ctx, data, length);
//DEBUG_PRINT( "%d bytes readed\n", ret); //for low level debug
return ret;
}