Page 1 of 1

Problem with credential setup for ESP32 in Zephyr RTOS

Posted: Wed Apr 19, 2023 1:03 am
by dqtran
Hi, I'm facing a problem after TLS credential setup on Zephyr RTOS. I have posted my problem onto Zephyr discussion board on Github for nearly 2 weeks but no response at all, so hopefully I can seek some help here. The socket I created cannot be connected, it throws the error number 22 - Invalid Argument which I have no idea where it comes from. My purpose with this code is to send a POST request to http host login.microsoftonline.com to get OAuth2.0 access token. Without TLS credential setup, it works perfectly fine but of course I would get redirect URL response due to missing credential to talk with port 443. But when I try setting TLS, it has error at zsock_connect function. This is my sample code, WiFi is already setup and connected.
  1. #include <stddef.h>
  2. #include <errno.h>
  3. #include <stdint.h>
  4. #include <stdbool.h>
  5. #include <stdlib.h>
  6. #include <stdio.h>
  7. #include <string.h>
  8.  
  9. #include <zephyr/kernel.h>
  10. #include <zephyr/logging/log.h>
  11. #include <zephyr/net/socket.h>
  12. #include <zephyr/net/http/client.h>
  13. #include <zephyr/net/tls_credentials.h>
  14.  
  15. #include "ca_certificate.h"
  16.  
  17. #define HTTP_HOST "login.microsoftonline.com"
  18. #define HTTP_PROTOCOL "HTTP/1.1"
  19. #if defined(CONFIG_NET_SOCKETS_SOCKOPT_TLS)
  20. #define HTTP_PORT "443"
  21. #else
  22. #define HTTP_PORT "80"
  23. #endif
  24.  
  25. LOG_MODULE_REGISTER(esp32_get_token);
  26.  
  27. static void dump_addrinfo(const struct zsock_addrinfo *ai)
  28. {
  29.     LOG_INF("Address info @%p", ai);
  30.     LOG_INF("Address info Family: %s", ai->ai_family ? "IPv4" : "IPv6");
  31.     LOG_INF("Socket type: %d", ai->ai_socktype);
  32.     LOG_INF("Socket address Family: %s", ai->ai_addr->sa_family ? "IPv4" : "IPv6");
  33.     LOG_INF("Port: %" PRIu16, ((struct sockaddr_in *)ai->ai_addr)->sin_port);
  34.  
  35.     char peer_addr[INET6_ADDRSTRLEN];
  36.     struct sockaddr *sa = ai->ai_addr;
  37.     inet_ntop(sa->sa_family, (void *)&((struct sockaddr_in *)sa)->sin_addr,
  38.               peer_addr, INET6_ADDRSTRLEN);
  39.     LOG_INF("Peer address: %s", peer_addr);
  40. }
  41.  
  42. void get_token(void)
  43. {
  44.     LOG_INF("Setting up socket...");
  45.     int ret, sock;
  46.     static struct zsock_addrinfo hints =
  47.         {
  48.             .ai_flags = AI_NUMERICSERV,
  49.             .ai_family = AF_INET,
  50.             .ai_socktype = SOCK_STREAM,
  51.             .ai_next = NULL,
  52.         };
  53.     struct zsock_addrinfo *res;
  54.     ret = zsock_getaddrinfo(HTTP_HOST, HTTP_PORT, &hints, &res);
  55.     if (ret != 0)
  56.     {
  57.         LOG_ERR("Get address info failed (%d)", ret);
  58.         return;
  59.     }
  60.     LOG_INF("Got address info");
  61.     dump_addrinfo(res);
  62.  
  63. #if defined(CONFIG_NET_SOCKETS_SOCKOPT_TLS)
  64.     sock = zsock_socket(res->ai_family, res->ai_socktype, IPPROTO_TLS_1_2);
  65. #else
  66.     sock = zsock_socket(res->ai_family, res->ai_socktype, res->ai_protocol);
  67. #endif
  68.     if (sock < 0)
  69.     {
  70.         LOG_ERR("Failed to create HTTP socket (%d)", ret);
  71.         return;
  72.     }
  73.     LOG_INF("Created HTTP socket");
  74.  
  75. #if defined(CONFIG_NET_SOCKETS_SOCKOPT_TLS)
  76.     sec_tag_t sec_tag_list[] = {
  77.         CA_CERTIFICATE_TAG,
  78.     };
  79.     ret = tls_credential_add(CA_CERTIFICATE_TAG, TLS_CREDENTIAL_CA_CERTIFICATE,
  80.                              ca_cert, sizeof(ca_cert));
  81.     if (ret < 0)
  82.     {
  83.         LOG_ERR("Failed to add TLS credential (%d)", -errno);
  84.         return;
  85.     }
  86.     LOG_INF("TLS credential added at tag %d", sec_tag_list[0]);
  87.  
  88.     ret = zsock_setsockopt(sock, SOL_TLS, TLS_SEC_TAG_LIST, sec_tag_list, sizeof(sec_tag_list));
  89.     if (ret < 0)
  90.     {
  91.         LOG_ERR("Failed to set secure option (%d)", -errno);
  92.         return;
  93.     }
  94.     LOG_INF("Set %d secure options succeeded", sizeof(sec_tag_list) / sizeof(sec_tag_list[0]));
  95.  
  96.     ret = zsock_setsockopt(sock, SOL_TLS, TLS_HOSTNAME, TLS_PEER_HOST, strlen(TLS_PEER_HOST));
  97.     if (ret < 0)
  98.     {
  99.         LOG_ERR("Failed to set %s hostname (%d)", TLS_PEER_HOST, -errno);
  100.         return;
  101.     }
  102.     LOG_INF("Set %s hostname succeeded", TLS_PEER_HOST);
  103.  
  104. #endif
  105.  
  106.     ret = zsock_connect(sock, res->ai_addr, res->ai_addrlen);
  107.     if (ret < 0)
  108.     {
  109.         LOG_ERR("Cannot connect to socket (%d)", -errno);
  110.         return;
  111.     }
  112.     LOG_INF("Connected to socket");
  113.  
  114. //send http POST request ...
  115. }
This is my ca_certificate.h file, the credential certificate I used which I took from login.microsoftonline.com, it is called DigiCert Global Root CA.
  1. #ifndef CA_CERTIFICATE_H
  2. #define CA_CERTIFICATE_H
  3.  
  4. #define CA_CERTIFICATE_TAG 1
  5. #define TLS_PEER_HOST "login.microsoftonline.com"
  6.  
  7. static const unsigned char ca_cert[] = {
  8.     // #include "DigiCertGlobalRootCA.der.inc"
  9.     "-----BEGIN CERTIFICATE-----\
  10. MIIDrzCCApegAwIBAgIQCDvgVpBCRrGhdWrJWZHHSjANBgkqhkiG9w0BAQUFADBh\
  11. MQswCQYDVQQGEwJVUzEVMBMGA1UEChMMRGlnaUNlcnQgSW5jMRkwFwYDVQQLExB3\
  12. d3cuZGlnaWNlcnQuY29tMSAwHgYDVQQDExdEaWdpQ2VydCBHbG9iYWwgUm9vdCBD\
  13. QTAeFw0wNjExMTAwMDAwMDBaFw0zMTExMTAwMDAwMDBaMGExCzAJBgNVBAYTAlVT\
  14. MRUwEwYDVQQKEwxEaWdpQ2VydCBJbmMxGTAXBgNVBAsTEHd3dy5kaWdpY2VydC5j\
  15. b20xIDAeBgNVBAMTF0RpZ2lDZXJ0IEdsb2JhbCBSb290IENBMIIBIjANBgkqhkiG\
  16. 9w0BAQEFAAOCAQ8AMIIBCgKCAQEA4jvhEXLeqKTTo1eqUKKPC3eQyaKl7hLOllsB\
  17. CSDMAZOnTjC3U/dDxGkAV53ijSLdhwZAAIEJzs4bg7/fzTtxRuLWZscFs3YnFo97\
  18. nh6Vfe63SKMI2tavegw5BmV/Sl0fvBf4q77uKNd0f3p4mVmFaG5cIzJLv07A6Fpt\
  19. 43C/dxC//AH2hdmoRBBYMql1GNXRor5H4idq9Joz+EkIYIvUX7Q6hL+hqkpMfT7P\
  20. T19sdl6gSzeRntwi5m3OFBqOasv+zbMUZBfHWymeMr/y7vrTC0LUq7dBMtoM1O/4\
  21. gdW7jVg/tRvoSSiicNoxBN33shbyTApOB6jtSj1etX+jkMOvJwIDAQABo2MwYTAO\
  22. BgNVHQ8BAf8EBAMCAYYwDwYDVR0TAQH/BAUwAwEB/zAdBgNVHQ4EFgQUA95QNVbR\
  23. TLtm8KPiGxvDl7I90VUwHwYDVR0jBBgwFoAUA95QNVbRTLtm8KPiGxvDl7I90VUw\
  24. DQYJKoZIhvcNAQEFBQADggEBAMucN6pIExIK+t1EnE9SsPTfrgT1eXkIoyQY/Esr\
  25. hMAtudXH/vTBH1jLuG2cenTnmCmrEbXjcKChzUyImZOMkXDiqw8cvpOp/2PV5Adg\
  26. 06O/nVsJ8dWO41P0jmP6P6fbtGbfYmbW0W5BjfIttep3Sp+dWOIrWcBAI+0tKIJF\
  27. PnlUkiaY4IBIqDfv8NZ5YBberOgOzW6sRBc4L0na4UU+Krk2U886UAb3LujEV0ls\
  28. YSEY1QSteDwsOoBrp+uvFRTp2InBuThs4pFsiv9kuXclVzDAGySj4dzp30d8tbQk\
  29. CAUw7C29C79Fv1C5qfPrmAESrciIxpg0X40KPMbp1ZWVbd4=\
  30. -----END CERTIFICATE-----"};
  31.  
  32. #endif
This is my prj.conf:
  1. CONFIG_MAIN_STACK_SIZE=7168
  2. CONFIG_INIT_STACKS=y
  3. CONFIG_SYSTEM_WORKQUEUE_STACK_SIZE=2048
  4. CONFIG_LOG=y
  5.  
  6. CONFIG_MP_NUM_CPUS=2
  7. CONFIG_MP_MAX_NUM_CPUS=3
  8. CONFIG_ASSERT=y
  9. CONFIG_SETTINGS=y
  10. CONFIG_ASSERT_ON_ERRORS=y
  11. CONFIG_ESP_HEAP_MEM_POOL_REGION_1_SIZE=19456
  12. CONFIG_HEAP_MEM_POOL_SIZE=20480
  13. CONFIG_MINIMAL_LIBC_MALLOC=y
  14. CONFIG_MINIMAL_LIBC=y
  15. CONFIG_MINIMAL_LIBC_MALLOC_ARENA_SIZE=4096
  16.  
  17. CONFIG_WIFI=y
  18. CONFIG_WIFI_ESP32=y
  19. CONFIG_NETWORKING=y
  20. CONFIG_NET_MGMT=y
  21. CONFIG_NET_MGMT_EVENT=y
  22. CONFIG_NET_MGMT_EVENT_INFO=y
  23. CONFIG_NET_IPV4=y
  24. CONFIG_NET_IPV6=y
  25. CONFIG_NET_TCP=y
  26. CONFIG_NET_SOCKETS=y
  27. CONFIG_NET_SOCKETS_POSIX_NAMES=y
  28. CONFIG_NET_DHCPV4=y
  29. CONFIG_NET_TX_STACK_SIZE=2048
  30. CONFIG_NET_RX_STACK_SIZE=2048
  31. CONFIG_NET_L2_ETHERNET=y
  32. CONFIG_HTTP_CLIENT=y
  33. CONFIG_NET_TCP_MAX_RECV_WINDOW_SIZE=5120
  34.  
  35. # CONFIG_NET_HTTP_LOG_LEVEL_DBG=y
  36. # CONFIG_NET_SOCKETS_LOG_LEVEL_DBG=y
  37. # CONFIG_NET_CONTEXT_LOG_LEVEL_DBG=y
  38. # CONFIG_NET_TCP_LOG_LEVEL_DBG=y
  39.  
  40. CONFIG_MBEDTLS=y
  41. CONFIG_MBEDTLS_BUILTIN=y
  42. CONFIG_MBEDTLS_SSL_ALPN=y
  43. CONFIG_MBEDTLS_PEM_CERTIFICATE_FORMAT=y
  44. CONFIG_MBEDTLS_TLS_VERSION_1_2=y
  45. CONFIG_MBEDTLS_ENABLE_HEAP=y
  46. CONFIG_MBEDTLS_HEAP_SIZE=34816
  47. CONFIG_MBEDTLS_SSL_MAX_CONTENT_LEN=2048
  48.  
  49. CONFIG_NET_SOCKETS_SOCKOPT_TLS=y
  50. CONFIG_NET_SOCKETS_TLS_MAX_CONTEXTS=6
I have digged very deep in the source code of zephyr subsystem to find the source of error, turn on debug log, place serveral printk() to track the progress. I dig to TCP module, no problem or log error at all, it can send and receive data perfectly. But when I track the sockets.c module in zephyrproject/zephyr/subsys/net/lib/sockets, I place a printk("%s %d %d\n", __func__, ret, errno) inside VTABLE_CALL(fn, sock, ...) macro function like this:
  1. #define VTABLE_CALL(fn, sock, ...)                                                                 \
  2.     do {                                                                                       \
  3.         const struct socket_op_vtable *vtable;                                             \
  4.         struct k_mutex *lock;                                                              \
  5.         void *obj;                                                                         \
  6.         int ret;                                                                           \
  7.                                                                                                    \
  8.         obj = get_sock_vtable(sock, &vtable, &lock);                                       \
  9.         if (obj == NULL) {                                                                 \
  10.             errno = EBADF;                                                             \
  11.             return -1;                                                                 \
  12.         }                                                                                  \
  13.                                                                                                    \
  14.         if (vtable->fn == NULL) {                                                          \
  15.             errno = EOPNOTSUPP;                                                        \
  16.             return -1;                                                                 \
  17.         }                                                                                  \
  18.                                                                                                    \
  19.         (void)k_mutex_lock(lock, K_FOREVER);                                               \
  20.                                                                                                    \
  21.         ret = vtable->fn(obj, __VA_ARGS__);                                                \
  22.                 printk("%s %d %d\n", __func__, ret, errno);                                                  \
  23.         k_mutex_unlock(lock);                                                              \
  24.                                                                                                    \
  25.         return ret;                                                                        \
  26.                                                                                                    \
  27.     } while (0)
I saw this z_impl_zsock_connect function run twice, the first attempt works perfectly with the ret = 0 from sock_connect_vmeth as well as zsock_connect_ctx and have no problem at all in the deeper nest of subsystem code, at this point, I'm 99% sure the socket is connected and ready to send the http client request. However, the errno = 95 at this point. Then the second attempt pops up with ret = -1 without any deeper execution, and errno = 22 from nowhere, no log error, none of my printk() appears. I saw that before I first called sock = zsock_socket(res->ai_family, res->ai_socktype, IPPROTO_TLS_1_2);, errno = 0 but then errno = 95 once I called it.

Can anyone explain this situation. I'm really confused, I even tried swap to google.com host and the corresponding TLS certificate but still the same error. I'm extremely appriciate if anyone can help.