Skip to content

Commit 0da9d45

Browse files
Add an example for how to enable TLS verification. (#381)
* Add an example for how to enable TLS verification. TLS should really be used with verification enabled, as otherwise you can still suffer from a "man in the middle" attack. Add an example that demonstrates how to do this. Fixes #337
1 parent 5f28220 commit 0da9d45

File tree

6 files changed

+369
-208
lines changed

6 files changed

+369
-208
lines changed

README.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -126,6 +126,7 @@ App|Description
126126
[picow_tcp_client](pico_w/wifi/tcp_client) | A simple TCP client. You can run [python_test_tcp_server.py](pico_w/wifi/python_test_tcp/python_test_tcp_server.py) for it to connect to.
127127
[picow_tcp_server](pico_w/wifi/tcp_server) | A simple TCP server. You can use [python_test_tcp_client.py](pico_w//wifi/python_test_tcp/python_test_tcp_client.py) to connect to it.
128128
[picow_tls_client](pico_w/wifi/tls_client) | Demonstrates how to make a HTTPS request using TLS.
129+
[picow_tls_verify](pico_w/wifi/tls_client) | Demonstrates how to make a HTTPS request using TLS with certificate verification.
129130
[picow_wifi_scan](pico_w/wifi/wifi_scan) | Scans for WiFi networks and prints the results.
130131
[picow_udp_beacon](pico_w/wifi/udp_beacon) | A simple UDP transmitter.
131132

pico_w/wifi/tls_client/CMakeLists.txt

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
add_executable(picow_tls_client_background
22
picow_tls_client.c
3+
tls_common.c
34
)
45
target_compile_definitions(picow_tls_client_background PRIVATE
56
WIFI_SSID=\"${WIFI_SSID}\"
@@ -19,6 +20,7 @@ pico_add_extra_outputs(picow_tls_client_background)
1920

2021
add_executable(picow_tls_client_poll
2122
picow_tls_client.c
23+
tls_common.c
2224
)
2325
target_compile_definitions(picow_tls_client_poll PRIVATE
2426
WIFI_SSID=\"${WIFI_SSID}\"
@@ -36,6 +38,30 @@ target_link_libraries(picow_tls_client_poll
3638
)
3739
pico_add_extra_outputs(picow_tls_client_poll)
3840

41+
# This version verifies the tls connection with a certificate
42+
add_executable(picow_tls_verify_background
43+
tls_verify.c
44+
tls_common.c
45+
)
46+
target_compile_definitions(picow_tls_verify_background PRIVATE
47+
WIFI_SSID=\"${WIFI_SSID}\"
48+
WIFI_PASSWORD=\"${WIFI_PASSWORD}\"
49+
# By default verification is optional (MBEDTLS_SSL_VERIFY_OPTIONAL)
50+
# Make it required for this test
51+
ALTCP_MBEDTLS_AUTHMODE=MBEDTLS_SSL_VERIFY_REQUIRED
52+
)
53+
target_include_directories(picow_tls_verify_background PRIVATE
54+
${CMAKE_CURRENT_LIST_DIR}
55+
${CMAKE_CURRENT_LIST_DIR}/.. # for our common lwipopts
56+
)
57+
target_link_libraries(picow_tls_verify_background
58+
pico_cyw43_arch_lwip_threadsafe_background
59+
pico_lwip_mbedtls
60+
pico_mbedtls
61+
pico_stdlib
62+
)
63+
pico_add_extra_outputs(picow_tls_verify_background)
64+
3965
# Ignore warnings from lwip code
4066
set_source_files_properties(
4167
${PICO_LWIP_PATH}/src/apps/altcp_tls/altcp_tls_mbedtls.c

pico_w/wifi/tls_client/mbedtls_config.h

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -61,3 +61,6 @@
6161
#define MBEDTLS_ECDSA_C
6262
#define MBEDTLS_ASN1_WRITE_C
6363

64+
// The following is needed to parse a certificate
65+
#define MBEDTLS_PEM_PARSE_C
66+
#define MBEDTLS_BASE64_C

pico_w/wifi/tls_client/picow_tls_client.c

Lines changed: 9 additions & 208 deletions
Original file line numberDiff line numberDiff line change
@@ -4,15 +4,8 @@
44
* SPDX-License-Identifier: BSD-3-Clause
55
*/
66

7-
#include <string.h>
8-
#include <time.h>
9-
107
#include "pico/stdlib.h"
118
#include "pico/cyw43_arch.h"
12-
#include "lwip/pbuf.h"
13-
#include "lwip/altcp_tcp.h"
14-
#include "lwip/altcp_tls.h"
15-
#include "lwip/dns.h"
169

1710
#define TLS_CLIENT_SERVER "worldtimeapi.org"
1811
#define TLS_CLIENT_HTTP_REQUEST "GET /api/ip HTTP/1.1\r\n" \
@@ -21,204 +14,7 @@
2114
"\r\n"
2215
#define TLS_CLIENT_TIMEOUT_SECS 15
2316

24-
25-
typedef struct TLS_CLIENT_T_ {
26-
struct altcp_pcb *pcb;
27-
bool complete;
28-
} TLS_CLIENT_T;
29-
30-
static struct altcp_tls_config *tls_config = NULL;
31-
32-
static err_t tls_client_close(void *arg) {
33-
TLS_CLIENT_T *state = (TLS_CLIENT_T*)arg;
34-
err_t err = ERR_OK;
35-
36-
state->complete = true;
37-
if (state->pcb != NULL) {
38-
altcp_arg(state->pcb, NULL);
39-
altcp_poll(state->pcb, NULL, 0);
40-
altcp_recv(state->pcb, NULL);
41-
altcp_err(state->pcb, NULL);
42-
err = altcp_close(state->pcb);
43-
if (err != ERR_OK) {
44-
printf("close failed %d, calling abort\n", err);
45-
altcp_abort(state->pcb);
46-
err = ERR_ABRT;
47-
}
48-
state->pcb = NULL;
49-
}
50-
return err;
51-
}
52-
53-
static err_t tls_client_connected(void *arg, struct altcp_pcb *pcb, err_t err) {
54-
TLS_CLIENT_T *state = (TLS_CLIENT_T*)arg;
55-
if (err != ERR_OK) {
56-
printf("connect failed %d\n", err);
57-
return tls_client_close(state);
58-
}
59-
60-
printf("connected to server, sending request\n");
61-
err = altcp_write(state->pcb, TLS_CLIENT_HTTP_REQUEST, strlen(TLS_CLIENT_HTTP_REQUEST), TCP_WRITE_FLAG_COPY);
62-
if (err != ERR_OK) {
63-
printf("error writing data, err=%d", err);
64-
return tls_client_close(state);
65-
}
66-
67-
return ERR_OK;
68-
}
69-
70-
static err_t tls_client_poll(void *arg, struct altcp_pcb *pcb) {
71-
printf("timed out");
72-
return tls_client_close(arg);
73-
}
74-
75-
static void tls_client_err(void *arg, err_t err) {
76-
TLS_CLIENT_T *state = (TLS_CLIENT_T*)arg;
77-
printf("tls_client_err %d\n", err);
78-
state->pcb = NULL; /* pcb freed by lwip when _err function is called */
79-
}
80-
81-
static err_t tls_client_recv(void *arg, struct altcp_pcb *pcb, struct pbuf *p, err_t err) {
82-
TLS_CLIENT_T *state = (TLS_CLIENT_T*)arg;
83-
if (!p) {
84-
printf("connection closed\n");
85-
return tls_client_close(state);
86-
}
87-
88-
if (p->tot_len > 0) {
89-
/* For simplicity this examples creates a buffer on stack the size of the data pending here,
90-
and copies all the data to it in one go.
91-
Do be aware that the amount of data can potentially be a bit large (TLS record size can be 16 KB),
92-
so you may want to use a smaller fixed size buffer and copy the data to it using a loop, if memory is a concern */
93-
char buf[p->tot_len + 1];
94-
95-
pbuf_copy_partial(p, buf, p->tot_len, 0);
96-
buf[p->tot_len] = 0;
97-
98-
printf("***\nnew data received from server:\n***\n\n%s\n", buf);
99-
100-
altcp_recved(pcb, p->tot_len);
101-
}
102-
pbuf_free(p);
103-
104-
return ERR_OK;
105-
}
106-
107-
static void tls_client_connect_to_server_ip(const ip_addr_t *ipaddr, TLS_CLIENT_T *state)
108-
{
109-
err_t err;
110-
u16_t port = 443;
111-
112-
printf("connecting to server IP %s port %d\n", ipaddr_ntoa(ipaddr), port);
113-
err = altcp_connect(state->pcb, ipaddr, port, tls_client_connected);
114-
if (err != ERR_OK)
115-
{
116-
fprintf(stderr, "error initiating connect, err=%d\n", err);
117-
tls_client_close(state);
118-
}
119-
}
120-
121-
static void tls_client_dns_found(const char* hostname, const ip_addr_t *ipaddr, void *arg)
122-
{
123-
if (ipaddr)
124-
{
125-
printf("DNS resolving complete\n");
126-
tls_client_connect_to_server_ip(ipaddr, (TLS_CLIENT_T *) arg);
127-
}
128-
else
129-
{
130-
printf("error resolving hostname %s\n", hostname);
131-
tls_client_close(arg);
132-
}
133-
}
134-
135-
136-
static bool tls_client_open(const char *hostname, void *arg) {
137-
err_t err;
138-
ip_addr_t server_ip;
139-
TLS_CLIENT_T *state = (TLS_CLIENT_T*)arg;
140-
141-
state->pcb = altcp_tls_new(tls_config, IPADDR_TYPE_ANY);
142-
if (!state->pcb) {
143-
printf("failed to create pcb\n");
144-
return false;
145-
}
146-
147-
altcp_arg(state->pcb, state);
148-
altcp_poll(state->pcb, tls_client_poll, TLS_CLIENT_TIMEOUT_SECS * 2);
149-
altcp_recv(state->pcb, tls_client_recv);
150-
altcp_err(state->pcb, tls_client_err);
151-
152-
/* Set SNI */
153-
mbedtls_ssl_set_hostname(altcp_tls_context(state->pcb), hostname);
154-
155-
printf("resolving %s\n", hostname);
156-
157-
// cyw43_arch_lwip_begin/end should be used around calls into lwIP to ensure correct locking.
158-
// You can omit them if you are in a callback from lwIP. Note that when using pico_cyw_arch_poll
159-
// these calls are a no-op and can be omitted, but it is a good practice to use them in
160-
// case you switch the cyw43_arch type later.
161-
cyw43_arch_lwip_begin();
162-
163-
err = dns_gethostbyname(hostname, &server_ip, tls_client_dns_found, state);
164-
if (err == ERR_OK)
165-
{
166-
/* host is in DNS cache */
167-
tls_client_connect_to_server_ip(&server_ip, state);
168-
}
169-
else if (err != ERR_INPROGRESS)
170-
{
171-
printf("error initiating DNS resolving, err=%d\n", err);
172-
tls_client_close(state->pcb);
173-
}
174-
175-
cyw43_arch_lwip_end();
176-
177-
return err == ERR_OK || err == ERR_INPROGRESS;
178-
}
179-
180-
// Perform initialisation
181-
static TLS_CLIENT_T* tls_client_init(void) {
182-
TLS_CLIENT_T *state = calloc(1, sizeof(TLS_CLIENT_T));
183-
if (!state) {
184-
printf("failed to allocate state\n");
185-
return NULL;
186-
}
187-
188-
return state;
189-
}
190-
191-
void run_tls_client_test(void) {
192-
/* No CA certificate checking */
193-
tls_config = altcp_tls_create_config_client(NULL, 0);
194-
195-
TLS_CLIENT_T *state = tls_client_init();
196-
if (!state) {
197-
return;
198-
}
199-
if (!tls_client_open(TLS_CLIENT_SERVER, state)) {
200-
return;
201-
}
202-
while(!state->complete) {
203-
// the following #ifdef is only here so this same example can be used in multiple modes;
204-
// you do not need it in your code
205-
#if PICO_CYW43_ARCH_POLL
206-
// if you are using pico_cyw43_arch_poll, then you must poll periodically from your
207-
// main loop (not from a timer) to check for Wi-Fi driver or lwIP work that needs to be done.
208-
cyw43_arch_poll();
209-
// you can poll as often as you like, however if you have nothing else to do you can
210-
// choose to sleep until either a specified time, or cyw43_arch_poll() has work to do:
211-
cyw43_arch_wait_for_work_until(make_timeout_time_ms(1000));
212-
#else
213-
// if you are not using pico_cyw43_arch_poll, then WiFI driver and lwIP work
214-
// is done via interrupt in the background. This sleep is just an example of some (blocking)
215-
// work you might be doing.
216-
sleep_ms(1000);
217-
#endif
218-
}
219-
free(state);
220-
altcp_tls_free_config(tls_config);
221-
}
17+
extern bool run_tls_client_test(const uint8_t *cert, size_t cert_len, const char *server, const char *request, int timeout);
22218

22319
int main() {
22420
stdio_init_all();
@@ -233,12 +29,17 @@ int main() {
23329
printf("failed to connect\n");
23430
return 1;
23531
}
236-
run_tls_client_test();
237-
32+
bool pass = run_tls_client_test(NULL, 0, TLS_CLIENT_SERVER, TLS_CLIENT_HTTP_REQUEST, TLS_CLIENT_TIMEOUT_SECS);
33+
if (pass) {
34+
printf("Test passed\n");
35+
} else {
36+
printf("Test failed\n");
37+
}
23838
/* sleep a bit to let usb stdio write out any buffer to host */
23939
sleep_ms(100);
24040

24141
cyw43_arch_deinit();
242-
return 0;
42+
printf("All done\n");
43+
return pass ? 0 : 1;
24344
}
24445

0 commit comments

Comments
 (0)