prettyprint

2023年11月16日 星期四

Raspberry Pi || NODE_RED || Pico W || TLS: Ep1. Getting Started - setup

本文章紀錄Raspberry Pi PicoW TLS Client連線,Server端建制NODE-RED在Raspberry Pi 3B+,並建立TLS TCP Flow來測試。


詳細步驟:
  • 使用rpi-imager建立Raspberry Pi 3+作業系統,使用不含Desktop。
    設定WiFi連線與enable SSH。
  • 安裝NODE-RED:
    ~$ sudo apt install build-essential git curl
    ~$ bash <(curl -sL https://raw.githubusercontent.com/node-red/linux-installers/master/deb/update-nodejs-and-nodered)
  • 安裝self signed CA certificates:
    1.產生一組CA key:
     openssl genrsa -des3 -out ca.key 2048

    2.製作一組CA 憑證:
     openssl req -new -x509 -days 3650 -key ca.key -out ca.crt

    3.製作 server key:
    openssl genrsa -out node_red_tls.key 2048

    4. 使用server key製作server憑證需求(certificate request):
     openssl req -new -out node_red_tls.csr -key node_red_tls.key

    5.製作server憑證:
    openssl x509 -req -in node_red_tls.csr -CA ca.crt -CAkey ca.key -CAcreateserial -out node_red_tls.crt -days 360
  • 建立NODE-RED flow:

    輸入剛剛建立的憑證。
  • PicoW Client端:
    在lwipopts.h加入啟用mbedtls:

    加入ca.crt憑證。

/* TCP WND must be at least 16 kb to match TLS record size
   or you will get a warning "altcp_tls: TCP_WND is smaller than the RX decrypion buffer, connection RX might stall!" */
// tls
#undef TCP_WND
#define TCP_WND  16384

#define LWIP_ALTCP               1
#define LWIP_ALTCP_TLS           1
#define LWIP_ALTCP_TLS_MBEDTLS   1

#define LWIP_DEBUG 1
#define ALTCP_MBEDTLS_DEBUG  LWIP_DBG_ON

#define ALTCP_MBEDTLS_AUTHMODE MBEDTLS_SSL_VERIFY_REQUIRED

詳細步驟請參閱下列影片:


程式碼:

  • picow_tls_client.c

#include < stdio.h >
#include "pico/stdlib.h"
#include "pico/cyw43_arch.h"
#include "picow_tls_client.h"
#include "picow_tls_cert.h"

#define TCP_POLL_INTERVAL   10
#define LED_RED_PIN         13
#define LED_GREEN_PIN       14
#define LED_BLUE_PIN        15

#define WIFI_SSID           "your-ssid"
#define WIFI_PASSWORD       "your-password"
#define SERVER_IP           "your-server-ip"
#define SERVER_PORT         20000

static struct altcp_tls_config *tls_config = NULL;

err_t tcp_connect_close(TLS_CLIENT_T * tls_client) {
    tls_client->connected=false;
    tls_client->dns_found=false;
    return altcp_close(tls_client->tpcb);
}

static int32_t buff_remain_len=0;

err_t tcp_recv_cb(void *arg, struct altcp_pcb *tpcb, struct pbuf *p, err_t err) {
    TLS_CLIENT_T *tls_client = (TLS_CLIENT_T*)arg;
    if (!p) {
        printf("pbuf null\n");
        return tcp_connect_close(tls_client);
    }
    
    if (p->tot_len > 0) {
        // for test only
        int32_t len;
        int32_t offset=0;
        buff_remain_len = p->tot_len;
        while (buff_remain_len > 0) {
            len = buff_remain_len > RECV_BUFF_MAX_LEN ? RECV_BUFF_MAX_LEN : buff_remain_len;
            pbuf_copy_partial(p, tls_client->recv_buff, len,offset);
            tls_client->recv_buff[len]=0;
            printf("%s\n", tls_client->recv_buff);
            buff_remain_len -= len;
            offset+=len;
        } 
        
       altcp_recved(tpcb, p->tot_len);
    }
    gpio_put(LED_GREEN_PIN, !gpio_get(LED_GREEN_PIN));
    pbuf_free(p);
    return ERR_OK;
}

err_t tcp_sent_cb(void *arg, struct altcp_pcb *tpcb, u16_t len) {
    return ERR_OK;
}

err_t tcp_poll_cb(void *arg, struct altcp_pcb *tpcb) {
    TLS_CLIENT_T *tls_client = (TLS_CLIENT_T*)arg;
    return tcp_connect_close(tls_client);
}

void tcp_err_cb(void *arg, err_t err) {
    printf("tcp error:%d\n");
    tcp_connect_close(arg);
}

err_t tcp_connected_cb(void *arg, struct altcp_pcb *tpcb, err_t err) {
    TLS_CLIENT_T *tls_client = (TLS_CLIENT_T*)arg;
    if (err == ERR_OK) {
        printf("tls server connected \n");
        tls_client->connected = true;
    }
    return err;
}

void tls_client_dns_found(const char *name, const ip_addr_t *ipaddr, void *callback_arg) {
    TLS_CLIENT_T *tls_client = (TLS_CLIENT_T*)callback_arg;
    if (ipaddr)
    {
        printf("DNS resolving complete\n");
        tls_client->dns_found=true;
        tls_client->server_addr = *ipaddr;
    }
    else
    {
        printf("error resolving hostname %s\n", name);
        tls_client->dns_found=false;
    }
}

int main()
{
    
    int wifi_stat;
    err_t tcp_stat;
    stdio_init_all();
    printf("Starting \n");

    if (cyw43_arch_init()) {
        printf("cyw43 init error\n");
        return 0;
    }
    gpio_init_mask(1 << LED_BLUE_PIN | 1 << LED_GREEN_PIN | 1 << LED_RED_PIN);
    gpio_set_dir_out_masked(1 << LED_BLUE_PIN | 1 << LED_GREEN_PIN | 1 << LED_RED_PIN);
    
    tls_config = altcp_tls_create_config_client(ca_cert, sizeof(ca_cert));
    //tls_config = altcp_tls_create_config_client(NULL, 0);
    assert(tls_config);

    //led: red
    gpio_put_masked(1 << LED_BLUE_PIN | 1 << LED_GREEN_PIN | 1 << LED_RED_PIN, 0);
    gpio_put(LED_RED_PIN, true);

    // connect to WiFi
    cyw43_arch_enable_sta_mode();
    wifi_stat = cyw43_arch_wifi_connect_timeout_ms(WIFI_SSID, WIFI_PASSWORD, CYW43_AUTH_WPA2_AES_PSK, 30000);
    if (wifi_stat) {
        printf("wifi connect error:%d\n", wifi_stat);
        return 0;
    }
    printf("Wifi connected\n");
    //led: blue
    gpio_put_masked(1 << LED_BLUE_PIN | 1 << LED_GREEN_PIN | 1 << LED_RED_PIN, 0);
    gpio_put(LED_BLUE_PIN, true);

    // connect to tcp server
    TLS_CLIENT_T* tls_client = (TLS_CLIENT_T*)calloc(1, sizeof(TLS_CLIENT_T));
    // set callback
    tls_client->tpcb = altcp_tls_new(tls_config, IPADDR_TYPE_ANY);
    altcp_arg(tls_client->tpcb, tls_client);
    altcp_sent(tls_client->tpcb, tcp_sent_cb);
    altcp_recv(tls_client->tpcb, tcp_recv_cb);
    altcp_poll(tls_client->tpcb, tcp_poll_cb, TCP_POLL_INTERVAL);
    altcp_err(tls_client->tpcb, tcp_err_cb);
    /*
    // if DNS query
    mbedtls_ssl_set_hostname(altcp_tls_context(tcp_client.tpcb), SERVER_NAME);
    tls_client->dns_found=false;
    cyw43_arch_lwip_begin();
    tcp_stat = dns_gethostbyname(SERVER_NAME, &tls_client->server_addr, tls_client_dns_found, &tls_client);     
    while(!tls_client->dns_found) {
         cyw43_arch_poll();
        sleep_ms(10);
    }
    */
   // if not by dns
   ipaddr_aton(SERVER_IP, &tls_client->server_addr);
   
   // connect to server;
    tls_client->connected = false;
    tcp_stat = altcp_connect(tls_client->tpcb, &tls_client->server_addr, SERVER_PORT, tcp_connected_cb);
    
    if (tcp_stat != ERR_OK) {
        printf("connect TCP server error:%d\n", tcp_stat);
        return 0;
    }
    absolute_time_t timeout = make_timeout_time_ms(20000);
    while(!tls_client->connected && absolute_time_diff_us(get_absolute_time(), timeout)>0){
        printf(".");
        cyw43_arch_poll();
        sleep_ms(10);
    } 
    printf("\n");
    if (!tls_client->connected) {
        printf("connection timeout\n");
        free(tls_client);
        return 0;
    }
    // led : green
    gpio_put_masked(1 << LED_BLUE_PIN | 1 << LED_GREEN_PIN | 1 << LED_RED_PIN, 0);
    gpio_put(LED_GREEN_PIN, true);

    uint8_t buff[100];
    uint32_t idx=0;
    while(1) {
        sprintf(buff, "test from PicoW:%020d", idx++);
        altcp_write(tls_client->tpcb, buff, strlen(buff), TCP_WRITE_FLAG_COPY);
        cyw43_arch_poll();
        cyw43_arch_wait_for_work_until(make_timeout_time_ms(1000));
        //sleep_ms(50);
    }
    altcp_close(tls_client->tpcb);
    free(tls_client);
    cyw43_arch_deinit();

    return 0;
}

  • CMakeLists.txt

# Generated Cmake Pico project file

cmake_minimum_required(VERSION 3.13)

set(CMAKE_C_STANDARD 11)
set(CMAKE_CXX_STANDARD 17)

# Initialise pico_sdk from installed location
# (note this can come from environment, CMake cache etc)
set(PICO_SDK_PATH "/home/duser/pico/pico-sdk")

set(PICO_BOARD pico_w CACHE STRING "Board type")

# Pull in Raspberry Pi Pico SDK (must be before project)
include(pico_sdk_import.cmake)

if (PICO_SDK_VERSION_STRING VERSION_LESS "1.4.0")
  message(FATAL_ERROR "Raspberry Pi Pico SDK version 1.4.0 (or later) required. Your version is ${PICO_SDK_VERSION_STRING}")
endif()

project(picow_tls_client C CXX ASM)

# Initialise the Raspberry Pi Pico SDK
pico_sdk_init()

# Add executable. Default name is the project name, version 0.1

add_executable(picow_tls_client picow_tls_client.c )

pico_set_program_name(picow_tls_client "picow_tls_client")
pico_set_program_version(picow_tls_client "0.1")

pico_enable_stdio_uart(picow_tls_client 1)
pico_enable_stdio_usb(picow_tls_client 0)

# Add the standard library to the build
target_link_libraries(picow_tls_client
        pico_stdlib)

# Add the standard include files to the build
target_include_directories(picow_tls_client PRIVATE
  ${CMAKE_CURRENT_LIST_DIR}
  ${CMAKE_CURRENT_LIST_DIR}/.. # for our common lwipopts or any other standard includes, if required
)

# Add any user requested libraries
target_link_libraries(picow_tls_client 
        pico_cyw43_arch_lwip_poll
        pico_lwip_mbedtls
        pico_mbedtls
        )

pico_add_extra_outputs(picow_tls_client)

  • picow_tls_client.h

#ifndef __PICOW_TLS_CLIENT_H__
#define __PICOW_TLS_CLIENT_H__
#include "lwip/pbuf.h"
#include "lwip/altcp_tcp.h"
#include "lwip/altcp_tcp.h"
#include "lwip/altcp_tls.h"
#include "lwip/dns.h"
#define RECV_BUFF_MAX_LEN   1024
typedef struct {
    struct altcp_pcb *tpcb;
    ip_addr_t server_addr;
    bool connected;
    bool dns_found;
    uint8_t recv_buff[RECV_BUFF_MAX_LEN+1];
} TLS_CLIENT_T;


#endif
  • lwipopts.h

#ifndef __LWIPOPTS_H__
#define __LWIPOPTS_H__


// Common settings used in most of the pico_w examples
// (see https://www.nongnu.org/lwip/2_1_x/group__lwip__opts.html for details)

// allow override in some examples
#ifndef NO_SYS
#define NO_SYS                      1
#endif
// allow override in some examples
#ifndef LWIP_SOCKET
#define LWIP_SOCKET                 0
#endif
#if PICO_CYW43_ARCH_POLL
#define MEM_LIBC_MALLOC             1
#else
// MEM_LIBC_MALLOC is incompatible with non polling versions
#define MEM_LIBC_MALLOC             0
#endif
#define MEM_ALIGNMENT               4
#define MEM_SIZE                    4000
#define MEMP_NUM_TCP_SEG            32
#define MEMP_NUM_ARP_QUEUE          10
#define PBUF_POOL_SIZE              24
#define LWIP_ARP                    1
#define LWIP_ETHERNET               1
#define LWIP_ICMP                   1
#define LWIP_RAW                    1
#define TCP_WND                     (8 * TCP_MSS)
#define TCP_MSS                     1460
#define TCP_SND_BUF                 (8 * TCP_MSS)
#define TCP_SND_QUEUELEN            ((4 * (TCP_SND_BUF) + (TCP_MSS - 1)) / (TCP_MSS))
#define LWIP_NETIF_STATUS_CALLBACK  1
#define LWIP_NETIF_LINK_CALLBACK    1
#define LWIP_NETIF_HOSTNAME         1
#define LWIP_NETCONN                0
#define MEM_STATS                   0
#define SYS_STATS                   0
#define MEMP_STATS                  0
#define LINK_STATS                  0
// #define ETH_PAD_SIZE                2
#define LWIP_CHKSUM_ALGORITHM       3
#define LWIP_DHCP                   1
#define LWIP_IPV4                   1
#define LWIP_TCP                    1
#define LWIP_UDP                    1
#define LWIP_DNS                    1
#define LWIP_TCP_KEEPALIVE          1
#define LWIP_NETIF_TX_SINGLE_PBUF   1
#define DHCP_DOES_ARP_CHECK         0
#define LWIP_DHCP_DOES_ACD_CHECK    0

#ifndef NDEBUG
#define LWIP_DEBUG                  1
#define LWIP_STATS                  1
#define LWIP_STATS_DISPLAY          1
#endif

#define ETHARP_DEBUG                LWIP_DBG_OFF
#define NETIF_DEBUG                 LWIP_DBG_OFF
#define PBUF_DEBUG                  LWIP_DBG_OFF
#define API_LIB_DEBUG               LWIP_DBG_OFF
#define API_MSG_DEBUG               LWIP_DBG_OFF
#define SOCKETS_DEBUG               LWIP_DBG_OFF
#define ICMP_DEBUG                  LWIP_DBG_OFF
#define INET_DEBUG                  LWIP_DBG_OFF
#define IP_DEBUG                    LWIP_DBG_OFF
#define IP_REASS_DEBUG              LWIP_DBG_OFF
#define RAW_DEBUG                   LWIP_DBG_OFF
#define MEM_DEBUG                   LWIP_DBG_OFF
#define MEMP_DEBUG                  LWIP_DBG_OFF
#define SYS_DEBUG                   LWIP_DBG_OFF
#define TCP_DEBUG                   LWIP_DBG_OFF
#define TCP_INPUT_DEBUG             LWIP_DBG_OFF
#define TCP_OUTPUT_DEBUG            LWIP_DBG_OFF
#define TCP_RTO_DEBUG               LWIP_DBG_OFF
#define TCP_CWND_DEBUG              LWIP_DBG_OFF
#define TCP_WND_DEBUG               LWIP_DBG_OFF
#define TCP_FR_DEBUG                LWIP_DBG_OFF
#define TCP_QLEN_DEBUG              LWIP_DBG_OFF
#define TCP_RST_DEBUG               LWIP_DBG_OFF
#define UDP_DEBUG                   LWIP_DBG_OFF
#define TCPIP_DEBUG                 LWIP_DBG_OFF
#define PPP_DEBUG                   LWIP_DBG_OFF
#define SLIP_DEBUG                  LWIP_DBG_OFF
#define DHCP_DEBUG                  LWIP_DBG_OFF

/* TCP WND must be at least 16 kb to match TLS record size
   or you will get a warning "altcp_tls: TCP_WND is smaller than the RX decrypion buffer, connection RX might stall!" */
// tls
#undef TCP_WND
#define TCP_WND  16384

#define LWIP_ALTCP               1
#define LWIP_ALTCP_TLS           1
#define LWIP_ALTCP_TLS_MBEDTLS   1

#define LWIP_DEBUG 1
#define ALTCP_MBEDTLS_DEBUG  LWIP_DBG_ON

#define ALTCP_MBEDTLS_AUTHMODE MBEDTLS_SSL_VERIFY_REQUIRED

#endif /* __LWIPOPTS_H__ */

  • mbedtls_config.h

/* Workaround for some mbedtls source files using INT_MAX without including limits.h */
#include < limits.h >

#define MBEDTLS_NO_PLATFORM_ENTROPY
#define MBEDTLS_ENTROPY_HARDWARE_ALT

#define MBEDTLS_SSL_OUT_CONTENT_LEN    2048

#define MBEDTLS_ALLOW_PRIVATE_ACCESS
#define MBEDTLS_HAVE_TIME

#define MBEDTLS_CIPHER_MODE_CBC
#define MBEDTLS_ECP_DP_SECP192R1_ENABLED
#define MBEDTLS_ECP_DP_SECP224R1_ENABLED
#define MBEDTLS_ECP_DP_SECP256R1_ENABLED
#define MBEDTLS_ECP_DP_SECP384R1_ENABLED
#define MBEDTLS_ECP_DP_SECP521R1_ENABLED
#define MBEDTLS_ECP_DP_SECP192K1_ENABLED
#define MBEDTLS_ECP_DP_SECP224K1_ENABLED
#define MBEDTLS_ECP_DP_SECP256K1_ENABLED
#define MBEDTLS_ECP_DP_BP256R1_ENABLED
#define MBEDTLS_ECP_DP_BP384R1_ENABLED
#define MBEDTLS_ECP_DP_BP512R1_ENABLED
#define MBEDTLS_ECP_DP_CURVE25519_ENABLED
#define MBEDTLS_KEY_EXCHANGE_RSA_ENABLED
#define MBEDTLS_PKCS1_V15
#define MBEDTLS_SHA256_SMALLER
#define MBEDTLS_SSL_SERVER_NAME_INDICATION
#define MBEDTLS_AES_C
#define MBEDTLS_ASN1_PARSE_C
#define MBEDTLS_BIGNUM_C
#define MBEDTLS_CIPHER_C
#define MBEDTLS_CTR_DRBG_C
#define MBEDTLS_ENTROPY_C
#define MBEDTLS_ERROR_C
#define MBEDTLS_MD_C
#define MBEDTLS_MD5_C
#define MBEDTLS_OID_C
#define MBEDTLS_PKCS5_C
#define MBEDTLS_PK_C
#define MBEDTLS_PK_PARSE_C
#define MBEDTLS_PLATFORM_C
#define MBEDTLS_RSA_C
#define MBEDTLS_SHA1_C
#define MBEDTLS_SHA224_C
#define MBEDTLS_SHA256_C
#define MBEDTLS_SHA512_C
#define MBEDTLS_SSL_CLI_C
#define MBEDTLS_SSL_SRV_C
#define MBEDTLS_SSL_TLS_C
#define MBEDTLS_X509_CRT_PARSE_C
#define MBEDTLS_X509_USE_C
#define MBEDTLS_AES_FEWER_TABLES

/* TLS 1.2 */
#define MBEDTLS_SSL_PROTO_TLS1_2
#define MBEDTLS_KEY_EXCHANGE_ECDHE_ECDSA_ENABLED
#define MBEDTLS_GCM_C
#define MBEDTLS_ECDH_C
#define MBEDTLS_ECP_C
#define MBEDTLS_ECDSA_C
#define MBEDTLS_ASN1_WRITE_C

// The following is needed to parse a certificate
#define MBEDTLS_PEM_PARSE_C
#define MBEDTLS_BASE64_C

  • picow_tls_cert.h

#define ca_cert "-----BEGIN CERTIFICATE-----\n\
.
.
.
-----END CERTIFICATE-----\n"


沒有留言:

張貼留言