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
This commit is contained in:
@@ -126,6 +126,7 @@ App|Description
|
|||||||
[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.
|
[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.
|
||||||
[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.
|
[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.
|
||||||
[picow_tls_client](pico_w/wifi/tls_client) | Demonstrates how to make a HTTPS request using TLS.
|
[picow_tls_client](pico_w/wifi/tls_client) | Demonstrates how to make a HTTPS request using TLS.
|
||||||
|
[picow_tls_verify](pico_w/wifi/tls_client) | Demonstrates how to make a HTTPS request using TLS with certificate verification.
|
||||||
[picow_wifi_scan](pico_w/wifi/wifi_scan) | Scans for WiFi networks and prints the results.
|
[picow_wifi_scan](pico_w/wifi/wifi_scan) | Scans for WiFi networks and prints the results.
|
||||||
[picow_udp_beacon](pico_w/wifi/udp_beacon) | A simple UDP transmitter.
|
[picow_udp_beacon](pico_w/wifi/udp_beacon) | A simple UDP transmitter.
|
||||||
|
|
||||||
|
|||||||
@@ -1,5 +1,6 @@
|
|||||||
add_executable(picow_tls_client_background
|
add_executable(picow_tls_client_background
|
||||||
picow_tls_client.c
|
picow_tls_client.c
|
||||||
|
tls_common.c
|
||||||
)
|
)
|
||||||
target_compile_definitions(picow_tls_client_background PRIVATE
|
target_compile_definitions(picow_tls_client_background PRIVATE
|
||||||
WIFI_SSID=\"${WIFI_SSID}\"
|
WIFI_SSID=\"${WIFI_SSID}\"
|
||||||
@@ -19,6 +20,7 @@ pico_add_extra_outputs(picow_tls_client_background)
|
|||||||
|
|
||||||
add_executable(picow_tls_client_poll
|
add_executable(picow_tls_client_poll
|
||||||
picow_tls_client.c
|
picow_tls_client.c
|
||||||
|
tls_common.c
|
||||||
)
|
)
|
||||||
target_compile_definitions(picow_tls_client_poll PRIVATE
|
target_compile_definitions(picow_tls_client_poll PRIVATE
|
||||||
WIFI_SSID=\"${WIFI_SSID}\"
|
WIFI_SSID=\"${WIFI_SSID}\"
|
||||||
@@ -36,6 +38,30 @@ target_link_libraries(picow_tls_client_poll
|
|||||||
)
|
)
|
||||||
pico_add_extra_outputs(picow_tls_client_poll)
|
pico_add_extra_outputs(picow_tls_client_poll)
|
||||||
|
|
||||||
|
# This version verifies the tls connection with a certificate
|
||||||
|
add_executable(picow_tls_verify_background
|
||||||
|
tls_verify.c
|
||||||
|
tls_common.c
|
||||||
|
)
|
||||||
|
target_compile_definitions(picow_tls_verify_background PRIVATE
|
||||||
|
WIFI_SSID=\"${WIFI_SSID}\"
|
||||||
|
WIFI_PASSWORD=\"${WIFI_PASSWORD}\"
|
||||||
|
# By default verification is optional (MBEDTLS_SSL_VERIFY_OPTIONAL)
|
||||||
|
# Make it required for this test
|
||||||
|
ALTCP_MBEDTLS_AUTHMODE=MBEDTLS_SSL_VERIFY_REQUIRED
|
||||||
|
)
|
||||||
|
target_include_directories(picow_tls_verify_background PRIVATE
|
||||||
|
${CMAKE_CURRENT_LIST_DIR}
|
||||||
|
${CMAKE_CURRENT_LIST_DIR}/.. # for our common lwipopts
|
||||||
|
)
|
||||||
|
target_link_libraries(picow_tls_verify_background
|
||||||
|
pico_cyw43_arch_lwip_threadsafe_background
|
||||||
|
pico_lwip_mbedtls
|
||||||
|
pico_mbedtls
|
||||||
|
pico_stdlib
|
||||||
|
)
|
||||||
|
pico_add_extra_outputs(picow_tls_verify_background)
|
||||||
|
|
||||||
# Ignore warnings from lwip code
|
# Ignore warnings from lwip code
|
||||||
set_source_files_properties(
|
set_source_files_properties(
|
||||||
${PICO_LWIP_PATH}/src/apps/altcp_tls/altcp_tls_mbedtls.c
|
${PICO_LWIP_PATH}/src/apps/altcp_tls/altcp_tls_mbedtls.c
|
||||||
|
|||||||
@@ -61,3 +61,6 @@
|
|||||||
#define MBEDTLS_ECDSA_C
|
#define MBEDTLS_ECDSA_C
|
||||||
#define MBEDTLS_ASN1_WRITE_C
|
#define MBEDTLS_ASN1_WRITE_C
|
||||||
|
|
||||||
|
// The following is needed to parse a certificate
|
||||||
|
#define MBEDTLS_PEM_PARSE_C
|
||||||
|
#define MBEDTLS_BASE64_C
|
||||||
|
|||||||
@@ -4,15 +4,8 @@
|
|||||||
* SPDX-License-Identifier: BSD-3-Clause
|
* SPDX-License-Identifier: BSD-3-Clause
|
||||||
*/
|
*/
|
||||||
|
|
||||||
#include <string.h>
|
|
||||||
#include <time.h>
|
|
||||||
|
|
||||||
#include "pico/stdlib.h"
|
#include "pico/stdlib.h"
|
||||||
#include "pico/cyw43_arch.h"
|
#include "pico/cyw43_arch.h"
|
||||||
#include "lwip/pbuf.h"
|
|
||||||
#include "lwip/altcp_tcp.h"
|
|
||||||
#include "lwip/altcp_tls.h"
|
|
||||||
#include "lwip/dns.h"
|
|
||||||
|
|
||||||
#define TLS_CLIENT_SERVER "worldtimeapi.org"
|
#define TLS_CLIENT_SERVER "worldtimeapi.org"
|
||||||
#define TLS_CLIENT_HTTP_REQUEST "GET /api/ip HTTP/1.1\r\n" \
|
#define TLS_CLIENT_HTTP_REQUEST "GET /api/ip HTTP/1.1\r\n" \
|
||||||
@@ -21,204 +14,7 @@
|
|||||||
"\r\n"
|
"\r\n"
|
||||||
#define TLS_CLIENT_TIMEOUT_SECS 15
|
#define TLS_CLIENT_TIMEOUT_SECS 15
|
||||||
|
|
||||||
|
extern bool run_tls_client_test(const uint8_t *cert, size_t cert_len, const char *server, const char *request, int timeout);
|
||||||
typedef struct TLS_CLIENT_T_ {
|
|
||||||
struct altcp_pcb *pcb;
|
|
||||||
bool complete;
|
|
||||||
} TLS_CLIENT_T;
|
|
||||||
|
|
||||||
static struct altcp_tls_config *tls_config = NULL;
|
|
||||||
|
|
||||||
static err_t tls_client_close(void *arg) {
|
|
||||||
TLS_CLIENT_T *state = (TLS_CLIENT_T*)arg;
|
|
||||||
err_t err = ERR_OK;
|
|
||||||
|
|
||||||
state->complete = true;
|
|
||||||
if (state->pcb != NULL) {
|
|
||||||
altcp_arg(state->pcb, NULL);
|
|
||||||
altcp_poll(state->pcb, NULL, 0);
|
|
||||||
altcp_recv(state->pcb, NULL);
|
|
||||||
altcp_err(state->pcb, NULL);
|
|
||||||
err = altcp_close(state->pcb);
|
|
||||||
if (err != ERR_OK) {
|
|
||||||
printf("close failed %d, calling abort\n", err);
|
|
||||||
altcp_abort(state->pcb);
|
|
||||||
err = ERR_ABRT;
|
|
||||||
}
|
|
||||||
state->pcb = NULL;
|
|
||||||
}
|
|
||||||
return err;
|
|
||||||
}
|
|
||||||
|
|
||||||
static err_t tls_client_connected(void *arg, struct altcp_pcb *pcb, err_t err) {
|
|
||||||
TLS_CLIENT_T *state = (TLS_CLIENT_T*)arg;
|
|
||||||
if (err != ERR_OK) {
|
|
||||||
printf("connect failed %d\n", err);
|
|
||||||
return tls_client_close(state);
|
|
||||||
}
|
|
||||||
|
|
||||||
printf("connected to server, sending request\n");
|
|
||||||
err = altcp_write(state->pcb, TLS_CLIENT_HTTP_REQUEST, strlen(TLS_CLIENT_HTTP_REQUEST), TCP_WRITE_FLAG_COPY);
|
|
||||||
if (err != ERR_OK) {
|
|
||||||
printf("error writing data, err=%d", err);
|
|
||||||
return tls_client_close(state);
|
|
||||||
}
|
|
||||||
|
|
||||||
return ERR_OK;
|
|
||||||
}
|
|
||||||
|
|
||||||
static err_t tls_client_poll(void *arg, struct altcp_pcb *pcb) {
|
|
||||||
printf("timed out");
|
|
||||||
return tls_client_close(arg);
|
|
||||||
}
|
|
||||||
|
|
||||||
static void tls_client_err(void *arg, err_t err) {
|
|
||||||
TLS_CLIENT_T *state = (TLS_CLIENT_T*)arg;
|
|
||||||
printf("tls_client_err %d\n", err);
|
|
||||||
state->pcb = NULL; /* pcb freed by lwip when _err function is called */
|
|
||||||
}
|
|
||||||
|
|
||||||
static err_t tls_client_recv(void *arg, struct altcp_pcb *pcb, struct pbuf *p, err_t err) {
|
|
||||||
TLS_CLIENT_T *state = (TLS_CLIENT_T*)arg;
|
|
||||||
if (!p) {
|
|
||||||
printf("connection closed\n");
|
|
||||||
return tls_client_close(state);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (p->tot_len > 0) {
|
|
||||||
/* For simplicity this examples creates a buffer on stack the size of the data pending here,
|
|
||||||
and copies all the data to it in one go.
|
|
||||||
Do be aware that the amount of data can potentially be a bit large (TLS record size can be 16 KB),
|
|
||||||
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 */
|
|
||||||
char buf[p->tot_len + 1];
|
|
||||||
|
|
||||||
pbuf_copy_partial(p, buf, p->tot_len, 0);
|
|
||||||
buf[p->tot_len] = 0;
|
|
||||||
|
|
||||||
printf("***\nnew data received from server:\n***\n\n%s\n", buf);
|
|
||||||
|
|
||||||
altcp_recved(pcb, p->tot_len);
|
|
||||||
}
|
|
||||||
pbuf_free(p);
|
|
||||||
|
|
||||||
return ERR_OK;
|
|
||||||
}
|
|
||||||
|
|
||||||
static void tls_client_connect_to_server_ip(const ip_addr_t *ipaddr, TLS_CLIENT_T *state)
|
|
||||||
{
|
|
||||||
err_t err;
|
|
||||||
u16_t port = 443;
|
|
||||||
|
|
||||||
printf("connecting to server IP %s port %d\n", ipaddr_ntoa(ipaddr), port);
|
|
||||||
err = altcp_connect(state->pcb, ipaddr, port, tls_client_connected);
|
|
||||||
if (err != ERR_OK)
|
|
||||||
{
|
|
||||||
fprintf(stderr, "error initiating connect, err=%d\n", err);
|
|
||||||
tls_client_close(state);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
static void tls_client_dns_found(const char* hostname, const ip_addr_t *ipaddr, void *arg)
|
|
||||||
{
|
|
||||||
if (ipaddr)
|
|
||||||
{
|
|
||||||
printf("DNS resolving complete\n");
|
|
||||||
tls_client_connect_to_server_ip(ipaddr, (TLS_CLIENT_T *) arg);
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
printf("error resolving hostname %s\n", hostname);
|
|
||||||
tls_client_close(arg);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
static bool tls_client_open(const char *hostname, void *arg) {
|
|
||||||
err_t err;
|
|
||||||
ip_addr_t server_ip;
|
|
||||||
TLS_CLIENT_T *state = (TLS_CLIENT_T*)arg;
|
|
||||||
|
|
||||||
state->pcb = altcp_tls_new(tls_config, IPADDR_TYPE_ANY);
|
|
||||||
if (!state->pcb) {
|
|
||||||
printf("failed to create pcb\n");
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
altcp_arg(state->pcb, state);
|
|
||||||
altcp_poll(state->pcb, tls_client_poll, TLS_CLIENT_TIMEOUT_SECS * 2);
|
|
||||||
altcp_recv(state->pcb, tls_client_recv);
|
|
||||||
altcp_err(state->pcb, tls_client_err);
|
|
||||||
|
|
||||||
/* Set SNI */
|
|
||||||
mbedtls_ssl_set_hostname(altcp_tls_context(state->pcb), hostname);
|
|
||||||
|
|
||||||
printf("resolving %s\n", hostname);
|
|
||||||
|
|
||||||
// cyw43_arch_lwip_begin/end should be used around calls into lwIP to ensure correct locking.
|
|
||||||
// You can omit them if you are in a callback from lwIP. Note that when using pico_cyw_arch_poll
|
|
||||||
// these calls are a no-op and can be omitted, but it is a good practice to use them in
|
|
||||||
// case you switch the cyw43_arch type later.
|
|
||||||
cyw43_arch_lwip_begin();
|
|
||||||
|
|
||||||
err = dns_gethostbyname(hostname, &server_ip, tls_client_dns_found, state);
|
|
||||||
if (err == ERR_OK)
|
|
||||||
{
|
|
||||||
/* host is in DNS cache */
|
|
||||||
tls_client_connect_to_server_ip(&server_ip, state);
|
|
||||||
}
|
|
||||||
else if (err != ERR_INPROGRESS)
|
|
||||||
{
|
|
||||||
printf("error initiating DNS resolving, err=%d\n", err);
|
|
||||||
tls_client_close(state->pcb);
|
|
||||||
}
|
|
||||||
|
|
||||||
cyw43_arch_lwip_end();
|
|
||||||
|
|
||||||
return err == ERR_OK || err == ERR_INPROGRESS;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Perform initialisation
|
|
||||||
static TLS_CLIENT_T* tls_client_init(void) {
|
|
||||||
TLS_CLIENT_T *state = calloc(1, sizeof(TLS_CLIENT_T));
|
|
||||||
if (!state) {
|
|
||||||
printf("failed to allocate state\n");
|
|
||||||
return NULL;
|
|
||||||
}
|
|
||||||
|
|
||||||
return state;
|
|
||||||
}
|
|
||||||
|
|
||||||
void run_tls_client_test(void) {
|
|
||||||
/* No CA certificate checking */
|
|
||||||
tls_config = altcp_tls_create_config_client(NULL, 0);
|
|
||||||
|
|
||||||
TLS_CLIENT_T *state = tls_client_init();
|
|
||||||
if (!state) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
if (!tls_client_open(TLS_CLIENT_SERVER, state)) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
while(!state->complete) {
|
|
||||||
// the following #ifdef is only here so this same example can be used in multiple modes;
|
|
||||||
// you do not need it in your code
|
|
||||||
#if PICO_CYW43_ARCH_POLL
|
|
||||||
// if you are using pico_cyw43_arch_poll, then you must poll periodically from your
|
|
||||||
// main loop (not from a timer) to check for Wi-Fi driver or lwIP work that needs to be done.
|
|
||||||
cyw43_arch_poll();
|
|
||||||
// you can poll as often as you like, however if you have nothing else to do you can
|
|
||||||
// choose to sleep until either a specified time, or cyw43_arch_poll() has work to do:
|
|
||||||
cyw43_arch_wait_for_work_until(make_timeout_time_ms(1000));
|
|
||||||
#else
|
|
||||||
// if you are not using pico_cyw43_arch_poll, then WiFI driver and lwIP work
|
|
||||||
// is done via interrupt in the background. This sleep is just an example of some (blocking)
|
|
||||||
// work you might be doing.
|
|
||||||
sleep_ms(1000);
|
|
||||||
#endif
|
|
||||||
}
|
|
||||||
free(state);
|
|
||||||
altcp_tls_free_config(tls_config);
|
|
||||||
}
|
|
||||||
|
|
||||||
int main() {
|
int main() {
|
||||||
stdio_init_all();
|
stdio_init_all();
|
||||||
@@ -233,12 +29,17 @@ int main() {
|
|||||||
printf("failed to connect\n");
|
printf("failed to connect\n");
|
||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
run_tls_client_test();
|
bool pass = run_tls_client_test(NULL, 0, TLS_CLIENT_SERVER, TLS_CLIENT_HTTP_REQUEST, TLS_CLIENT_TIMEOUT_SECS);
|
||||||
|
if (pass) {
|
||||||
|
printf("Test passed\n");
|
||||||
|
} else {
|
||||||
|
printf("Test failed\n");
|
||||||
|
}
|
||||||
/* sleep a bit to let usb stdio write out any buffer to host */
|
/* sleep a bit to let usb stdio write out any buffer to host */
|
||||||
sleep_ms(100);
|
sleep_ms(100);
|
||||||
|
|
||||||
cyw43_arch_deinit();
|
cyw43_arch_deinit();
|
||||||
return 0;
|
printf("All done\n");
|
||||||
|
return pass ? 0 : 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
227
pico_w/wifi/tls_client/tls_common.c
Normal file
227
pico_w/wifi/tls_client/tls_common.c
Normal file
@@ -0,0 +1,227 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (c) 2023 Raspberry Pi (Trading) Ltd.
|
||||||
|
*
|
||||||
|
* SPDX-License-Identifier: BSD-3-Clause
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include <string.h>
|
||||||
|
#include <time.h>
|
||||||
|
|
||||||
|
#include "pico/stdlib.h"
|
||||||
|
#include "pico/cyw43_arch.h"
|
||||||
|
#include "lwip/pbuf.h"
|
||||||
|
#include "lwip/altcp_tcp.h"
|
||||||
|
#include "lwip/altcp_tls.h"
|
||||||
|
#include "lwip/dns.h"
|
||||||
|
|
||||||
|
typedef struct TLS_CLIENT_T_ {
|
||||||
|
struct altcp_pcb *pcb;
|
||||||
|
bool complete;
|
||||||
|
int error;
|
||||||
|
const char *http_request;
|
||||||
|
int timeout;
|
||||||
|
} TLS_CLIENT_T;
|
||||||
|
|
||||||
|
static struct altcp_tls_config *tls_config = NULL;
|
||||||
|
|
||||||
|
static err_t tls_client_close(void *arg) {
|
||||||
|
TLS_CLIENT_T *state = (TLS_CLIENT_T*)arg;
|
||||||
|
err_t err = ERR_OK;
|
||||||
|
|
||||||
|
state->complete = true;
|
||||||
|
if (state->pcb != NULL) {
|
||||||
|
altcp_arg(state->pcb, NULL);
|
||||||
|
altcp_poll(state->pcb, NULL, 0);
|
||||||
|
altcp_recv(state->pcb, NULL);
|
||||||
|
altcp_err(state->pcb, NULL);
|
||||||
|
err = altcp_close(state->pcb);
|
||||||
|
if (err != ERR_OK) {
|
||||||
|
printf("close failed %d, calling abort\n", err);
|
||||||
|
altcp_abort(state->pcb);
|
||||||
|
err = ERR_ABRT;
|
||||||
|
}
|
||||||
|
state->pcb = NULL;
|
||||||
|
}
|
||||||
|
return err;
|
||||||
|
}
|
||||||
|
|
||||||
|
static err_t tls_client_connected(void *arg, struct altcp_pcb *pcb, err_t err) {
|
||||||
|
TLS_CLIENT_T *state = (TLS_CLIENT_T*)arg;
|
||||||
|
if (err != ERR_OK) {
|
||||||
|
printf("connect failed %d\n", err);
|
||||||
|
return tls_client_close(state);
|
||||||
|
}
|
||||||
|
|
||||||
|
printf("connected to server, sending request\n");
|
||||||
|
err = altcp_write(state->pcb, state->http_request, strlen(state->http_request), TCP_WRITE_FLAG_COPY);
|
||||||
|
if (err != ERR_OK) {
|
||||||
|
printf("error writing data, err=%d", err);
|
||||||
|
return tls_client_close(state);
|
||||||
|
}
|
||||||
|
|
||||||
|
return ERR_OK;
|
||||||
|
}
|
||||||
|
|
||||||
|
static err_t tls_client_poll(void *arg, struct altcp_pcb *pcb) {
|
||||||
|
TLS_CLIENT_T *state = (TLS_CLIENT_T*)arg;
|
||||||
|
printf("timed out\n");
|
||||||
|
state->error = PICO_ERROR_TIMEOUT;
|
||||||
|
return tls_client_close(arg);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void tls_client_err(void *arg, err_t err) {
|
||||||
|
TLS_CLIENT_T *state = (TLS_CLIENT_T*)arg;
|
||||||
|
printf("tls_client_err %d\n", err);
|
||||||
|
tls_client_close(state);
|
||||||
|
state->error = PICO_ERROR_GENERIC;
|
||||||
|
}
|
||||||
|
|
||||||
|
static err_t tls_client_recv(void *arg, struct altcp_pcb *pcb, struct pbuf *p, err_t err) {
|
||||||
|
TLS_CLIENT_T *state = (TLS_CLIENT_T*)arg;
|
||||||
|
if (!p) {
|
||||||
|
printf("connection closed\n");
|
||||||
|
return tls_client_close(state);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (p->tot_len > 0) {
|
||||||
|
/* For simplicity this examples creates a buffer on stack the size of the data pending here,
|
||||||
|
and copies all the data to it in one go.
|
||||||
|
Do be aware that the amount of data can potentially be a bit large (TLS record size can be 16 KB),
|
||||||
|
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 */
|
||||||
|
char buf[p->tot_len + 1];
|
||||||
|
|
||||||
|
pbuf_copy_partial(p, buf, p->tot_len, 0);
|
||||||
|
buf[p->tot_len] = 0;
|
||||||
|
|
||||||
|
printf("***\nnew data received from server:\n***\n\n%s\n", buf);
|
||||||
|
|
||||||
|
altcp_recved(pcb, p->tot_len);
|
||||||
|
}
|
||||||
|
pbuf_free(p);
|
||||||
|
|
||||||
|
return ERR_OK;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void tls_client_connect_to_server_ip(const ip_addr_t *ipaddr, TLS_CLIENT_T *state)
|
||||||
|
{
|
||||||
|
err_t err;
|
||||||
|
u16_t port = 443;
|
||||||
|
|
||||||
|
printf("connecting to server IP %s port %d\n", ipaddr_ntoa(ipaddr), port);
|
||||||
|
err = altcp_connect(state->pcb, ipaddr, port, tls_client_connected);
|
||||||
|
if (err != ERR_OK)
|
||||||
|
{
|
||||||
|
fprintf(stderr, "error initiating connect, err=%d\n", err);
|
||||||
|
tls_client_close(state);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static void tls_client_dns_found(const char* hostname, const ip_addr_t *ipaddr, void *arg)
|
||||||
|
{
|
||||||
|
if (ipaddr)
|
||||||
|
{
|
||||||
|
printf("DNS resolving complete\n");
|
||||||
|
tls_client_connect_to_server_ip(ipaddr, (TLS_CLIENT_T *) arg);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
printf("error resolving hostname %s\n", hostname);
|
||||||
|
tls_client_close(arg);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
static bool tls_client_open(const char *hostname, void *arg) {
|
||||||
|
err_t err;
|
||||||
|
ip_addr_t server_ip;
|
||||||
|
TLS_CLIENT_T *state = (TLS_CLIENT_T*)arg;
|
||||||
|
|
||||||
|
state->pcb = altcp_tls_new(tls_config, IPADDR_TYPE_ANY);
|
||||||
|
if (!state->pcb) {
|
||||||
|
printf("failed to create pcb\n");
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
altcp_arg(state->pcb, state);
|
||||||
|
altcp_poll(state->pcb, tls_client_poll, state->timeout * 2);
|
||||||
|
altcp_recv(state->pcb, tls_client_recv);
|
||||||
|
altcp_err(state->pcb, tls_client_err);
|
||||||
|
|
||||||
|
/* Set SNI */
|
||||||
|
mbedtls_ssl_set_hostname(altcp_tls_context(state->pcb), hostname);
|
||||||
|
|
||||||
|
printf("resolving %s\n", hostname);
|
||||||
|
|
||||||
|
// cyw43_arch_lwip_begin/end should be used around calls into lwIP to ensure correct locking.
|
||||||
|
// You can omit them if you are in a callback from lwIP. Note that when using pico_cyw_arch_poll
|
||||||
|
// these calls are a no-op and can be omitted, but it is a good practice to use them in
|
||||||
|
// case you switch the cyw43_arch type later.
|
||||||
|
cyw43_arch_lwip_begin();
|
||||||
|
|
||||||
|
err = dns_gethostbyname(hostname, &server_ip, tls_client_dns_found, state);
|
||||||
|
if (err == ERR_OK)
|
||||||
|
{
|
||||||
|
/* host is in DNS cache */
|
||||||
|
tls_client_connect_to_server_ip(&server_ip, state);
|
||||||
|
}
|
||||||
|
else if (err != ERR_INPROGRESS)
|
||||||
|
{
|
||||||
|
printf("error initiating DNS resolving, err=%d\n", err);
|
||||||
|
tls_client_close(state->pcb);
|
||||||
|
}
|
||||||
|
|
||||||
|
cyw43_arch_lwip_end();
|
||||||
|
|
||||||
|
return err == ERR_OK || err == ERR_INPROGRESS;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Perform initialisation
|
||||||
|
static TLS_CLIENT_T* tls_client_init(void) {
|
||||||
|
TLS_CLIENT_T *state = calloc(1, sizeof(TLS_CLIENT_T));
|
||||||
|
if (!state) {
|
||||||
|
printf("failed to allocate state\n");
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
return state;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool run_tls_client_test(const uint8_t *cert, size_t cert_len, const char *server, const char *request, int timeout) {
|
||||||
|
|
||||||
|
/* No CA certificate checking */
|
||||||
|
tls_config = altcp_tls_create_config_client(cert, cert_len);
|
||||||
|
assert(tls_config);
|
||||||
|
|
||||||
|
//mbedtls_ssl_conf_authmode(&tls_config->conf, MBEDTLS_SSL_VERIFY_OPTIONAL);
|
||||||
|
|
||||||
|
TLS_CLIENT_T *state = tls_client_init();
|
||||||
|
if (!state) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
state->http_request = request;
|
||||||
|
state->timeout = timeout;
|
||||||
|
if (!tls_client_open(server, state)) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
while(!state->complete) {
|
||||||
|
// the following #ifdef is only here so this same example can be used in multiple modes;
|
||||||
|
// you do not need it in your code
|
||||||
|
#if PICO_CYW43_ARCH_POLL
|
||||||
|
// if you are using pico_cyw43_arch_poll, then you must poll periodically from your
|
||||||
|
// main loop (not from a timer) to check for Wi-Fi driver or lwIP work that needs to be done.
|
||||||
|
cyw43_arch_poll();
|
||||||
|
// you can poll as often as you like, however if you have nothing else to do you can
|
||||||
|
// choose to sleep until either a specified time, or cyw43_arch_poll() has work to do:
|
||||||
|
cyw43_arch_wait_for_work_until(make_timeout_time_ms(1000));
|
||||||
|
#else
|
||||||
|
// if you are not using pico_cyw43_arch_poll, then WiFI driver and lwIP work
|
||||||
|
// is done via interrupt in the background. This sleep is just an example of some (blocking)
|
||||||
|
// work you might be doing.
|
||||||
|
sleep_ms(1000);
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
int err = state->error;
|
||||||
|
free(state);
|
||||||
|
altcp_tls_free_config(tls_config);
|
||||||
|
return err == 0;
|
||||||
|
}
|
||||||
103
pico_w/wifi/tls_client/tls_verify.c
Normal file
103
pico_w/wifi/tls_client/tls_verify.c
Normal file
@@ -0,0 +1,103 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (c) 2023 Raspberry Pi (Trading) Ltd.
|
||||||
|
*
|
||||||
|
* SPDX-License-Identifier: BSD-3-Clause
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include "pico/stdlib.h"
|
||||||
|
#include "pico/cyw43_arch.h"
|
||||||
|
|
||||||
|
// Using this url as we know the root cert won't change for a long time
|
||||||
|
#define TLS_CLIENT_SERVER "fw-download-alias1.raspberrypi.com"
|
||||||
|
#define TLS_CLIENT_HTTP_REQUEST "GET /net_install/boot.sig HTTP/1.1\r\n" \
|
||||||
|
"Host: " TLS_CLIENT_SERVER "\r\n" \
|
||||||
|
"Connection: close\r\n" \
|
||||||
|
"\r\n"
|
||||||
|
#define TLS_CLIENT_TIMEOUT_SECS 15
|
||||||
|
|
||||||
|
// This is the PUBLIC root certificate exported from a browser
|
||||||
|
// Note that the newlines are needed
|
||||||
|
#define TLS_ROOT_CERT_OK "-----BEGIN CERTIFICATE-----\n\
|
||||||
|
MIIC+jCCAn+gAwIBAgICEAAwCgYIKoZIzj0EAwIwgbcxCzAJBgNVBAYTAkdCMRAw\n\
|
||||||
|
DgYDVQQIDAdFbmdsYW5kMRIwEAYDVQQHDAlDYW1icmlkZ2UxHTAbBgNVBAoMFFJh\n\
|
||||||
|
c3BiZXJyeSBQSSBMaW1pdGVkMRwwGgYDVQQLDBNSYXNwYmVycnkgUEkgRUNDIENB\n\
|
||||||
|
MR0wGwYDVQQDDBRSYXNwYmVycnkgUEkgUm9vdCBDQTEmMCQGCSqGSIb3DQEJARYX\n\
|
||||||
|
c3VwcG9ydEByYXNwYmVycnlwaS5jb20wIBcNMjExMjA5MTEzMjU1WhgPMjA3MTEx\n\
|
||||||
|
MjcxMTMyNTVaMIGrMQswCQYDVQQGEwJHQjEQMA4GA1UECAwHRW5nbGFuZDEdMBsG\n\
|
||||||
|
A1UECgwUUmFzcGJlcnJ5IFBJIExpbWl0ZWQxHDAaBgNVBAsME1Jhc3BiZXJyeSBQ\n\
|
||||||
|
SSBFQ0MgQ0ExJTAjBgNVBAMMHFJhc3BiZXJyeSBQSSBJbnRlcm1lZGlhdGUgQ0Ex\n\
|
||||||
|
JjAkBgkqhkiG9w0BCQEWF3N1cHBvcnRAcmFzcGJlcnJ5cGkuY29tMHYwEAYHKoZI\n\
|
||||||
|
zj0CAQYFK4EEACIDYgAEcN9K6Cpv+od3w6yKOnec4EbyHCBzF+X2ldjorc0b2Pq0\n\
|
||||||
|
N+ZvyFHkhFZSgk2qvemsVEWIoPz+K4JSCpgPstz1fEV6WzgjYKfYI71ghELl5TeC\n\
|
||||||
|
byoPY+ee3VZwF1PTy0cco2YwZDAdBgNVHQ4EFgQUJ6YzIqFh4rhQEbmCnEbWmHEo\n\
|
||||||
|
XAUwHwYDVR0jBBgwFoAUIIAVCSiDPXut23NK39LGIyAA7NAwEgYDVR0TAQH/BAgw\n\
|
||||||
|
BgEB/wIBADAOBgNVHQ8BAf8EBAMCAYYwCgYIKoZIzj0EAwIDaQAwZgIxAJYM+wIM\n\
|
||||||
|
PC3wSPqJ1byJKA6D+ZyjKR1aORbiDQVEpDNWRKiQ5QapLg8wbcED0MrRKQIxAKUT\n\
|
||||||
|
v8TJkb/8jC/oBVTmczKlPMkciN+uiaZSXahgYKyYhvKTatCTZb+geSIhc0w/2w==\n\
|
||||||
|
-----END CERTIFICATE-----\n"
|
||||||
|
|
||||||
|
// This is a test certificate
|
||||||
|
#define TLS_ROOT_CERT_BAD "-----BEGIN CERTIFICATE-----\n\
|
||||||
|
MIIDezCCAwGgAwIBAgICEAEwCgYIKoZIzj0EAwIwgasxCzAJBgNVBAYTAkdCMRAw\n\
|
||||||
|
DgYDVQQIDAdFbmdsYW5kMR0wGwYDVQQKDBRSYXNwYmVycnkgUEkgTGltaXRlZDEc\n\
|
||||||
|
MBoGA1UECwwTUmFzcGJlcnJ5IFBJIEVDQyBDQTElMCMGA1UEAwwcUmFzcGJlcnJ5\n\
|
||||||
|
IFBJIEludGVybWVkaWF0ZSBDQTEmMCQGCSqGSIb3DQEJARYXc3VwcG9ydEByYXNw\n\
|
||||||
|
YmVycnlwaS5jb20wHhcNMjExMjA5MTMwMjIyWhcNNDYxMjAzMTMwMjIyWjA6MQsw\n\
|
||||||
|
CQYDVQQGEwJHQjErMCkGA1UEAwwiZnctZG93bmxvYWQtYWxpYXMxLnJhc3BiZXJy\n\
|
||||||
|
eXBpLmNvbTBZMBMGByqGSM49AgEGCCqGSM49AwEHA0IABJ6BQv8YtNiNv7ibLtt4\n\
|
||||||
|
lwpgEr2XD4sOl9wu/l8GnGD5p39YK8jZV0j6HaTNkqi86Nly1H7YklzbxhFy5orM\n\
|
||||||
|
356jggGDMIIBfzAJBgNVHRMEAjAAMBEGCWCGSAGG+EIBAQQEAwIGQDAzBglghkgB\n\
|
||||||
|
hvhCAQ0EJhYkT3BlblNTTCBHZW5lcmF0ZWQgU2VydmVyIENlcnRpZmljYXRlMB0G\n\
|
||||||
|
A1UdDgQWBBRlONP3G2wTERZA9D+VxJABfiaCVTCB5QYDVR0jBIHdMIHagBQnpjMi\n\
|
||||||
|
oWHiuFARuYKcRtaYcShcBaGBvaSBujCBtzELMAkGA1UEBhMCR0IxEDAOBgNVBAgM\n\
|
||||||
|
B0VuZ2xhbmQxEjAQBgNVBAcMCUNhbWJyaWRnZTEdMBsGA1UECgwUUmFzcGJlcnJ5\n\
|
||||||
|
IFBJIExpbWl0ZWQxHDAaBgNVBAsME1Jhc3BiZXJyeSBQSSBFQ0MgQ0ExHTAbBgNV\n\
|
||||||
|
BAMMFFJhc3BiZXJyeSBQSSBSb290IENBMSYwJAYJKoZIhvcNAQkBFhdzdXBwb3J0\n\
|
||||||
|
QHJhc3BiZXJyeXBpLmNvbYICEAAwDgYDVR0PAQH/BAQDAgWgMBMGA1UdJQQMMAoG\n\
|
||||||
|
CCsGAQUFBwMBMAoGCCqGSM49BAMCA2gAMGUCMEHerJRT0WmG5tz4oVLSIxLbCizd\n\
|
||||||
|
//SdJBCP+072zRUKs0mfl5EcO7dXWvBAb386PwIxAL7LrgpJroJYrYJtqeufJ3a9\n\
|
||||||
|
zVi56JFnA3cNTcDYfIzyzy5wUskPAykdrRrCS534ig==\n\
|
||||||
|
-----END CERTIFICATE-----\n"
|
||||||
|
|
||||||
|
extern bool run_tls_client_test(const uint8_t *cert, size_t cert_len, const char *server, const char *request, int timeout);
|
||||||
|
|
||||||
|
int main() {
|
||||||
|
stdio_init_all();
|
||||||
|
|
||||||
|
if (cyw43_arch_init()) {
|
||||||
|
printf("failed to initialise\n");
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
cyw43_arch_enable_sta_mode();
|
||||||
|
|
||||||
|
if (cyw43_arch_wifi_connect_timeout_ms(WIFI_SSID, WIFI_PASSWORD, CYW43_AUTH_WPA2_AES_PSK, 30000)) {
|
||||||
|
printf("failed to connect\n");
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
// This should work
|
||||||
|
const uint8_t cert_ok[] = TLS_ROOT_CERT_OK;
|
||||||
|
bool pass1 = run_tls_client_test(cert_ok, sizeof(cert_ok), TLS_CLIENT_SERVER, TLS_CLIENT_HTTP_REQUEST, TLS_CLIENT_TIMEOUT_SECS);
|
||||||
|
if (pass1) {
|
||||||
|
printf("Test passed\n");
|
||||||
|
} else {
|
||||||
|
printf("Test failed\n");
|
||||||
|
}
|
||||||
|
|
||||||
|
// Repeat the test with the wrong certificate. It should fail
|
||||||
|
const uint8_t cert_bad[] = TLS_ROOT_CERT_BAD;
|
||||||
|
bool pass2 = !run_tls_client_test(cert_bad, sizeof(cert_bad), TLS_CLIENT_SERVER, TLS_CLIENT_HTTP_REQUEST, TLS_CLIENT_TIMEOUT_SECS);
|
||||||
|
if (pass2) {
|
||||||
|
printf("Test passed\n");
|
||||||
|
} else {
|
||||||
|
printf("Test failed\n");
|
||||||
|
}
|
||||||
|
|
||||||
|
/* sleep a bit to let usb stdio write out any buffer to host */
|
||||||
|
sleep_ms(100);
|
||||||
|
|
||||||
|
cyw43_arch_deinit();
|
||||||
|
printf("All done\n");
|
||||||
|
return (pass1 && pass2) ? 0 : 1;
|
||||||
|
}
|
||||||
|
|
||||||
Reference in New Issue
Block a user