Skip to content

Commit 61219a3

Browse files
authored
Merge pull request #87 from andresag01/iotssl-1247-tls-client-refactoring
Refactor tls-client example to improve readability
2 parents d977e0d + 367bb8b commit 61219a3

File tree

7 files changed

+688
-438
lines changed

7 files changed

+688
-438
lines changed

tests/tls-client.log

Lines changed: 13 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,13 @@
1-
Starting the TLS handshake...
2-
Server certificate:
3-
Certificate verification passed
4-
HTTPS: Received 'Hello world!' status ... [OK]
5-
HTTPS: Received message:
6-
Hello world!
1+
Starting mbed-os-example-tls/tls-client
2+
Using Mbed OS
3+
Successfully connected to os.mbed.com at port 443
4+
Starting the TLS handshake...
5+
Successfully completed the TLS handshake
6+
Server certificate:
7+
Certificate verification passed
8+
Established TLS connection to os.mbed.com
9+
HTTP: Received [0-9]+ chars from server
10+
HTTP: Received '200 OK' status ... OK
11+
HTTP: Received message:
12+
Hello world!
13+
DONE

tls-client/HelloHttpsClient.cpp

Lines changed: 374 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,374 @@
1+
/*
2+
* Hello world example of a TLS client: fetch an HTTPS page
3+
*
4+
* Copyright (C) 2006-2018, Arm Limited, All Rights Reserved
5+
* SPDX-License-Identifier: Apache-2.0
6+
*
7+
* Licensed under the Apache License, Version 2.0 (the "License"); you may
8+
* not use this file except in compliance with the License.
9+
* You may obtain a copy of the License at
10+
*
11+
* http://www.apache.org/licenses/LICENSE-2.0
12+
*
13+
* Unless required by applicable law or agreed to in writing, software
14+
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
15+
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
16+
* See the License for the specific language governing permissions and
17+
* limitations under the License.
18+
*
19+
* This file is part of Mbed TLS (https://tls.mbed.org)
20+
*/
21+
22+
#include "HelloHttpsClient.h"
23+
24+
#include "easy-connect.h"
25+
26+
#include "mbedtls/platform.h"
27+
#include "mbedtls/config.h"
28+
#include "mbedtls/ssl.h"
29+
#include "mbedtls/entropy.h"
30+
#include "mbedtls/ctr_drbg.h"
31+
#include "mbedtls/error.h"
32+
#include "mbedtls/debug.h"
33+
34+
#include <stdint.h>
35+
#include <string.h>
36+
37+
const char *HelloHttpsClient::DRBG_PERSONALIZED_STR =
38+
"Mbed TLS helloword client";
39+
40+
const size_t HelloHttpsClient::ERROR_LOG_BUFFER_LENGTH = 128;
41+
42+
const char *HelloHttpsClient::TLS_PEM_CA =
43+
"-----BEGIN CERTIFICATE-----\n"
44+
"MIIDdTCCAl2gAwIBAgILBAAAAAABFUtaw5QwDQYJKoZIhvcNAQEFBQAwVzELMAkG\n"
45+
"A1UEBhMCQkUxGTAXBgNVBAoTEEdsb2JhbFNpZ24gbnYtc2ExEDAOBgNVBAsTB1Jv\n"
46+
"b3QgQ0ExGzAZBgNVBAMTEkdsb2JhbFNpZ24gUm9vdCBDQTAeFw05ODA5MDExMjAw\n"
47+
"MDBaFw0yODAxMjgxMjAwMDBaMFcxCzAJBgNVBAYTAkJFMRkwFwYDVQQKExBHbG9i\n"
48+
"YWxTaWduIG52LXNhMRAwDgYDVQQLEwdSb290IENBMRswGQYDVQQDExJHbG9iYWxT\n"
49+
"aWduIFJvb3QgQ0EwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQDaDuaZ\n"
50+
"jc6j40+Kfvvxi4Mla+pIH/EqsLmVEQS98GPR4mdmzxzdzxtIK+6NiY6arymAZavp\n"
51+
"xy0Sy6scTHAHoT0KMM0VjU/43dSMUBUc71DuxC73/OlS8pF94G3VNTCOXkNz8kHp\n"
52+
"1Wrjsok6Vjk4bwY8iGlbKk3Fp1S4bInMm/k8yuX9ifUSPJJ4ltbcdG6TRGHRjcdG\n"
53+
"snUOhugZitVtbNV4FpWi6cgKOOvyJBNPc1STE4U6G7weNLWLBYy5d4ux2x8gkasJ\n"
54+
"U26Qzns3dLlwR5EiUWMWea6xrkEmCMgZK9FGqkjWZCrXgzT/LCrBbBlDSgeF59N8\n"
55+
"9iFo7+ryUp9/k5DPAgMBAAGjQjBAMA4GA1UdDwEB/wQEAwIBBjAPBgNVHRMBAf8E\n"
56+
"BTADAQH/MB0GA1UdDgQWBBRge2YaRQ2XyolQL30EzTSo//z9SzANBgkqhkiG9w0B\n"
57+
"AQUFAAOCAQEA1nPnfE920I2/7LqivjTFKDK1fPxsnCwrvQmeU79rXqoRSLblCKOz\n"
58+
"yj1hTdNGCbM+w6DjY1Ub8rrvrTnhQ7k4o+YviiY776BQVvnGCv04zcQLcFGUl5gE\n"
59+
"38NflNUVyRRBnMRddWQVDf9VMOyGj/8N7yy5Y0b2qvzfvGn9LhJIZJrglfCm7ymP\n"
60+
"AbEVtQwdpf5pLGkkeB6zpxxxYu7KyJesF12KwvhHhm4qxFYxldBniYUr+WymXUad\n"
61+
"DKqC5JlR3XC321Y9YeRq4VzW9v493kHMB65jUr9TU/Qr6cf9tveCX4XSQRjbgbME\n"
62+
"HMUfpIBvFSDJ3gyICh3WZlXi/EjJKSZp4A==\n"
63+
"-----END CERTIFICATE-----\n";
64+
65+
const char *HelloHttpsClient::HTTP_REQUEST_FILE_PATH =
66+
"/media/uploads/mbed_official/hello.txt";
67+
68+
const char *HelloHttpsClient::HTTP_HELLO_STR = "Hello world!";
69+
70+
const char *HelloHttpsClient::HTTP_OK_STR = "200 OK";
71+
72+
HelloHttpsClient::HelloHttpsClient(const char *in_server_name,
73+
const uint16_t in_server_port) :
74+
socket(),
75+
server_name(in_server_name),
76+
server_port(in_server_port)
77+
{
78+
mbedtls_entropy_init(&entropy);
79+
mbedtls_ctr_drbg_init(&ctr_drbg);
80+
mbedtls_x509_crt_init(&cacert);
81+
mbedtls_ssl_init(&ssl);
82+
mbedtls_ssl_config_init(&ssl_conf);
83+
}
84+
85+
HelloHttpsClient::~HelloHttpsClient()
86+
{
87+
mbedtls_entropy_free(&entropy);
88+
mbedtls_ctr_drbg_free(&ctr_drbg);
89+
mbedtls_x509_crt_free(&cacert);
90+
mbedtls_ssl_free(&ssl);
91+
mbedtls_ssl_config_free(&ssl_conf);
92+
93+
socket.close();
94+
}
95+
96+
int HelloHttpsClient::run()
97+
{
98+
int ret;
99+
size_t req_len, req_offset, resp_offset;
100+
uint32_t flags;
101+
bool resp_200, resp_hello;
102+
103+
/* Configure the TCPSocket */
104+
if ((ret = configureTCPSocket()) != 0)
105+
return ret;
106+
107+
/* Configure already initialized Mbed TLS structures */
108+
if ((ret = configureTlsContexts()) != 0)
109+
return ret;
110+
111+
/* Start a connection to the server */
112+
if ((ret = socket.connect(server_name, server_port)) != NSAPI_ERROR_OK) {
113+
mbedtls_printf("socket.connect() returned %d\n", ret);
114+
return ret;
115+
}
116+
mbedtls_printf("Successfully connected to %s at port %u\n",
117+
server_name, server_port);
118+
119+
/* Start the TLS handshake */
120+
mbedtls_printf("Starting the TLS handshake...\n");
121+
do {
122+
ret = mbedtls_ssl_handshake(&ssl);
123+
} while(ret != 0 &&
124+
(ret == MBEDTLS_ERR_SSL_WANT_READ ||
125+
ret == MBEDTLS_ERR_SSL_WANT_WRITE));
126+
if (ret < 0) {
127+
mbedtls_printf("mbedtls_ssl_handshake() returned -0x%04X\n", -ret);
128+
return ret;
129+
}
130+
mbedtls_printf("Successfully completed the TLS handshake\n");
131+
132+
/* Fill the request buffer */
133+
ret = snprintf(gp_buf, sizeof(gp_buf),
134+
"GET %s HTTP/1.1\nHost: %s\n\n", HTTP_REQUEST_FILE_PATH,
135+
server_name);
136+
req_len = static_cast<size_t>(ret);
137+
if (ret < 0 || req_len >= sizeof(gp_buf)) {
138+
mbedtls_printf("Failed to compose HTTP request using snprintf: %d\n",
139+
ret);
140+
return ret;
141+
}
142+
143+
/* Send the HTTP request to the server over TLS */
144+
req_offset = 0;
145+
do {
146+
ret = mbedtls_ssl_write(&ssl,
147+
reinterpret_cast<const unsigned char *>(gp_buf + req_offset),
148+
req_len - req_offset);
149+
if (ret > 0)
150+
req_offset += static_cast<size_t>(ret);
151+
}
152+
while(req_offset < req_len &&
153+
(ret > 0 ||
154+
ret == MBEDTLS_ERR_SSL_WANT_WRITE ||
155+
ret == MBEDTLS_ERR_SSL_WANT_READ));
156+
if (ret < 0) {
157+
mbedtls_printf("mbedtls_ssl_write() returned -0x%04X\n", -ret);
158+
return ret;
159+
}
160+
161+
/* Print information about the TLS connection */
162+
ret = mbedtls_x509_crt_info(gp_buf, sizeof(gp_buf),
163+
"\r ", mbedtls_ssl_get_peer_cert(&ssl));
164+
if (ret < 0) {
165+
mbedtls_printf("mbedtls_x509_crt_info() returned -0x%04X\n", -ret);
166+
return ret;
167+
}
168+
mbedtls_printf("Server certificate:\n%s\n", gp_buf);
169+
170+
/* Ensure certificate verification was successful */
171+
flags = mbedtls_ssl_get_verify_result(&ssl);
172+
if (flags != 0) {
173+
ret = mbedtls_x509_crt_verify_info(gp_buf, sizeof(gp_buf),
174+
"\r ! ", flags);
175+
if (ret < 0) {
176+
mbedtls_printf("mbedtls_x509_crt_verify_info() returned "
177+
"-0x%04X\n", -ret);
178+
return ret;
179+
} else {
180+
mbedtls_printf("Certificate verification failed (flags %lu):"
181+
"\n%s\n", flags, gp_buf);
182+
return -1;
183+
}
184+
} else {
185+
mbedtls_printf("Certificate verification passed\n");
186+
}
187+
188+
mbedtls_printf("Established TLS connection to %s\n", server_name);
189+
190+
/* Read response from the server */
191+
resp_offset = 0;
192+
resp_200 = false;
193+
resp_hello = false;
194+
do {
195+
ret = mbedtls_ssl_read(&ssl,
196+
reinterpret_cast<unsigned char *>(gp_buf + resp_offset),
197+
sizeof(gp_buf) - resp_offset - 1);
198+
if (ret > 0)
199+
resp_offset += static_cast<size_t>(ret);
200+
201+
/* Ensure that the response string is null-terminated */
202+
gp_buf[resp_offset] = '\0';
203+
204+
/* Check if we received expected string */
205+
resp_200 = resp_200 || strstr(gp_buf, HTTP_OK_STR) != NULL;
206+
resp_hello = resp_hello || strstr(gp_buf, HTTP_HELLO_STR) != NULL;
207+
} while((!resp_200 || !resp_hello) &&
208+
(ret > 0 ||
209+
ret == MBEDTLS_ERR_SSL_WANT_READ || MBEDTLS_ERR_SSL_WANT_WRITE));
210+
if (ret < 0) {
211+
mbedtls_printf("mbedtls_ssl_read() returned -0x%04X\n", -ret);
212+
return ret;
213+
}
214+
215+
/* Display response information */
216+
mbedtls_printf("HTTP: Received %u chars from server\n", resp_offset);
217+
mbedtls_printf("HTTP: Received '%s' status ... %s\n", HTTP_OK_STR,
218+
resp_200 ? "OK" : "FAIL");
219+
mbedtls_printf("HTTP: Received message:\n%s\n", gp_buf);
220+
221+
return 0;
222+
}
223+
224+
int HelloHttpsClient::configureTCPSocket()
225+
{
226+
int ret;
227+
228+
/*
229+
* Use easy-connect lib to support multiple network bearers. See
230+
* https://github.com/ARMmbed/easy-connect README.md for more information.
231+
*/
232+
#if HELLO_HTTPS_CLIENT_DEBUG_LEVEL > 0
233+
NetworkInterface *network = easy_connect(true);
234+
#else
235+
NetworkInterface *network = easy_connect(false);
236+
#endif /* HELLO_HTTPS_CLIENT_DEBUG_LEVEL > 0 */
237+
if(network == NULL) {
238+
mbedtls_printf("easy_connect() returned NULL\n"
239+
"Failed to connect to the network\n");
240+
return -1;
241+
}
242+
243+
if ((ret = socket.open(network)) != NSAPI_ERROR_OK) {
244+
mbedtls_printf("socket.open() returned %d\n", ret);
245+
return ret;
246+
}
247+
248+
socket.set_blocking(false);
249+
250+
return 0;
251+
}
252+
253+
int HelloHttpsClient::configureTlsContexts()
254+
{
255+
int ret;
256+
257+
ret = mbedtls_ctr_drbg_seed(&ctr_drbg, mbedtls_entropy_func, &entropy,
258+
reinterpret_cast<const unsigned char *>(DRBG_PERSONALIZED_STR),
259+
strlen(DRBG_PERSONALIZED_STR) + 1);
260+
if (ret != 0) {
261+
mbedtls_printf("mbedtls_ctr_drbg_seed() returned -0x%04X\n", -ret);
262+
return ret;
263+
}
264+
265+
ret = mbedtls_x509_crt_parse(&cacert,
266+
reinterpret_cast<const unsigned char *>(TLS_PEM_CA),
267+
strlen(TLS_PEM_CA) + 1);
268+
if (ret != 0) {
269+
mbedtls_printf("mbedtls_x509_crt_parse() returned -0x%04X\n", -ret);
270+
return ret;
271+
}
272+
273+
ret = mbedtls_ssl_config_defaults(&ssl_conf, MBEDTLS_SSL_IS_CLIENT,
274+
MBEDTLS_SSL_TRANSPORT_STREAM,
275+
MBEDTLS_SSL_PRESET_DEFAULT);
276+
if (ret != 0) {
277+
mbedtls_printf("mbedtls_ssl_config_defaults() returned -0x%04X\n",
278+
-ret);
279+
return ret;
280+
}
281+
282+
mbedtls_ssl_conf_ca_chain(&ssl_conf, &cacert, NULL);
283+
mbedtls_ssl_conf_rng(&ssl_conf, mbedtls_ctr_drbg_random, &ctr_drbg);
284+
285+
/*
286+
* It is possible to disable authentication by passing
287+
* MBEDTLS_SSL_VERIFY_NONE in the call to mbedtls_ssl_conf_authmode()
288+
*/
289+
mbedtls_ssl_conf_authmode(&ssl_conf, MBEDTLS_SSL_VERIFY_REQUIRED);
290+
291+
#if HELLO_HTTPS_CLIENT_DEBUG_LEVEL > 0
292+
mbedtls_ssl_conf_verify(&ssl_conf, sslVerify, this);
293+
mbedtls_ssl_conf_dbg(&ssl_conf, sslDebug, NULL);
294+
mbedtls_debug_set_threshold(HELLO_HTTPS_CLIENT_DEBUG_LEVEL);
295+
#endif /* HELLO_HTTPS_CLIENT_DEBUG_LEVEL > 0 */
296+
297+
if ((ret = mbedtls_ssl_setup( &ssl, &ssl_conf)) != 0) {
298+
mbedtls_printf("mbedtls_ssl_setup() returned -0x%04X\n", -ret);
299+
return ret;
300+
}
301+
302+
if ((ret = mbedtls_ssl_set_hostname( &ssl, server_name )) != 0) {
303+
mbedtls_printf("mbedtls_ssl_set_hostname() returned -0x%04X\n",
304+
-ret);
305+
return ret;
306+
}
307+
308+
mbedtls_ssl_set_bio(&ssl, static_cast<void *>(&socket), sslSend, sslRecv,
309+
NULL);
310+
311+
return 0;
312+
}
313+
314+
int HelloHttpsClient::sslRecv(void *ctx, unsigned char *buf, size_t len)
315+
{
316+
TCPSocket *socket = static_cast<TCPSocket *>(ctx);
317+
int ret = socket->recv(buf, len);
318+
319+
if (ret == NSAPI_ERROR_WOULD_BLOCK)
320+
ret = MBEDTLS_ERR_SSL_WANT_READ;
321+
else if (ret < 0)
322+
mbedtls_printf("socket.recv() returned %d\n", ret);
323+
324+
return ret;
325+
}
326+
327+
int HelloHttpsClient::sslSend(void *ctx, const unsigned char *buf, size_t len)
328+
{
329+
TCPSocket *socket = static_cast<TCPSocket *>(ctx);
330+
int ret = socket->send(buf, len);
331+
332+
if (ret == NSAPI_ERROR_WOULD_BLOCK)
333+
ret = MBEDTLS_ERR_SSL_WANT_WRITE;
334+
else if (ret < 0)
335+
mbedtls_printf("socket.send() returned %d\n", ret);
336+
337+
return ret;
338+
}
339+
340+
void HelloHttpsClient::sslDebug(void *ctx, int level, const char *file,
341+
int line, const char *str)
342+
{
343+
(void)ctx;
344+
345+
const char *p, *basename;
346+
347+
/* Extract basename from file */
348+
for (p = basename = file; *p != '\0'; p++) {
349+
if (*p == '/' || *p == '\\')
350+
basename = p + 1;
351+
}
352+
353+
mbedtls_printf("%s:%d: |%d| %s\r", basename, line, level, str);
354+
}
355+
356+
int HelloHttpsClient::sslVerify(void *ctx, mbedtls_x509_crt *crt, int depth,
357+
uint32_t *flags)
358+
{
359+
HelloHttpsClient *client = static_cast<HelloHttpsClient *>(ctx);
360+
361+
int ret = -1;
362+
363+
ret = mbedtls_x509_crt_info(client->gp_buf, sizeof(gp_buf), "\r ", crt);
364+
if (ret < 0) {
365+
mbedtls_printf("mbedtls_x509_crt_info() returned -0x%04X\n", -ret);
366+
} else {
367+
ret = 0;
368+
mbedtls_printf("Verifying certificate at depth %d:\n%s\n",
369+
depth, client->gp_buf);
370+
}
371+
372+
return ret;
373+
}
374+

0 commit comments

Comments
 (0)