czwartek, 9 kwietnia 2026

STM32H7 - TLS komunikacja V2

Tym razem chciałem umieścić tutaj aktualizację dotyczącą komunikacji TLS. Program testowałem na płytce z układem STM32H725. 


Zajmę się tutaj poprawą wcześniej udostępnionej komunikacji SSL. Tak aby połączenie oraz jego ponowne nawiązywanie było bardziej stabilne. 

Zacznijmy od potrzebnych definicji:

  1. #define MBEDTLS_SSL_PROTO_TLS1_2    //Obsługa TLS1.2
  2. #define MBEDTLS_CTR_DRBG_C          //Uruchomienie generatora liczb losowych
  3. #define MBEDTLS_ENTROPY_C           //Źródło entropii dla DRBG
  4. #define MBEDTLS_NET_C          
  5. #define MBEDTLS_SSL_CLI_C           //Włączony tryb klienta
  6. #define MBEDTLS_SSL_SRV_C           //Włączony tryb serwera
  7. #define MBEDTLS_SSL_TLS_C
  8. #define MBEDTLS_X509_USE_C
  9. #define MBEDTLS_X509_CRT_PARSE_C    //Parsowanie certyfikatów X.509


Należy także zdefiniować dozwolone szyfry. Tak aby połączenie korzystało z możliwie najnowszych:

  1. #define MBEDTLS_SSL_CIPHERSUITES \
  2.     MBEDTLS_TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384, \
  3.     MBEDTLS_TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256, \
  4.     MBEDTLS_TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA384, \
  5.     MBEDTLS_TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA256, \
  6.     MBEDTLS_TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA, \
  7.     MBEDTLS_TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA

Ta lista będzie przedstawiana w podczas handshak'a. Serwer wybierze jeden z nich do komunikacji. Warto także pamiętać, że nowsze szyfry z reguły potrzebują mniej miejsca w pamięci na ich wykonanie. 

Dobrze to przechodzimy do nawiązywania sesji komunikacyjnej. Rozpoczynamy od inicjalizacji danych:

  1. static void Initialize_SessionData(void)
  2. {
  3.     //Przygotowanie gniazda TCP
  4.     mbedtls_net_init(&server_fd);
  5.     //Inicjalizacja pustego kontekstu TLS
  6.     mbedtls_ssl_init(&ssl);\
  7.     //Inicjalizacja obiektu konfig handshake i szyfrowania         
  8.     mbedtls_ssl_config_init(&conf);    
  9.     //przygotowanie struktur certyfikatów
  10.     mbedtls_x509_crt_init(&cacert);
  11.     mbedtls_x509_crt_init(&server_cert);
  12.     //Przygotowanie generatora liczb losowych oraz strukture klucze prywatnego
  13.     mbedtls_ctr_drbg_init(&ctr_drbg);
  14.     mbedtls_pk_init(&pkey);
  15.     mbedtls_entropy_init(&entropy);
  16. }

Powyżej przygotowywane są wszystkie obiekty mbedTLS. 

Teraz przygotowywany jest seed dla generatora losowego. 

  1. static int Drbg_Seed(void)
  2. {
  3.     int opStatus = 0;
  4.     if((opStatus = mbedtls_ctr_drbg_seed(&ctr_drbg,
  5.                                          mbedtls_entropy_func,
  6.                                          &entropy,
  7.                                          (const unsigned char*) pers,
  8.                                          strlen(pers))) != 0) {
  9.         return opStatus;
  10.     }
  11.     return opStatus;
  12. }

Łączone jest DRBG z źródłem entropii. W parametrze pers przekazywany jest tzw. prejonizacyjny string. Jest to dodatkowy identyfikator dla instancji generatora. Musi to być poprawnie zainicjalizowane i uruchomione aby handshake mógł przejść bez problemów. Na podstawie wygenerowanych danych tworzone są losowe dane do kluczy sesji. Dzięki temu bezpieczeństwo sesji nie opiera się na stałych danych. 

Następnym krokiem jest załadowanie certyfikatów. 

  1. static int InitializeCertificate(void)
  2. {
  3.     int opStatus = 0;
  4.  
  5.     opStatus = mbedtls_pk_parse_key(
  6.         &pkey,
  7.         (const unsigned char *)mbedtls_certificate_key,
  8.         mbedtls_certificate_key_len + 1,
  9.         (const unsigned char *)mbedtls_certificate_key_pass,
  10.         mbedtls_certificate_key_pass_len
  11.     );
  12.     if (opStatus < 0) return opStatus;
  13.  
  14.     opStatus = mbedtls_x509_crt_parse(
  15.         &cacert,
  16.         (const unsigned char *)mbedtls_client_certificate,
  17.         mbedtls_client_certificate_len + 1
  18.     );
  19.     if (opStatus < 0) return opStatus;
  20.  
  21.     opStatus = mbedtls_x509_crt_parse(
  22.         &server_cert,
  23.         (const unsigned char *)mbedtls_server_certificate,
  24.         strlen(mbedtls_server_certificate) + 1
  25.     );
  26.     if (opStatus < 0) return opStatus;
  27.  
  28.     opStatus = mbedtls_pk_check_pair(&cacert.pk, &pkey);
  29.     if (opStatus < 0) return opStatus;
  30.  
  31.     return 0;

Funkcja mbedtls_pl_parse_key parsuje klucz prywatny klienta z formatu PEM. Następnie w kolejnej funkcji jest on ładowany. Dalej przetwarzany jest certyfikat serwera. Na końcu sprawdzana jest poprawność klucza prywatnego z certyfikatem klienta. 

Ponieważ do projektu wykorzystane zostały dwa certyfikaty, To komunikacja może się odbywać w tzw. mTLS. Gdzie zarówno serwer jak i klient muszą przedstawić swój certyfikat. Oczywiście aby to faktycznie zadziałało to serwer musi wymagać od klienta tego certyfikatu. Bez niego uwierzytelnia się tylko serwer. Takie połączenie określa się jako TLS. 

Poniżej funkcja konfigurująca połączenie po TLS 1.2. 

  1. static uint8_t LoadDefaultConfiguration(void)
  2. {
  3.     int opStatus = 0;
  4.  
  5.     if((opStatus = mbedtls_ssl_config_defaults(&conf,
  6.                                                MBEDTLS_SSL_IS_CLIENT,
  7.                                                MBEDTLS_SSL_TRANSPORT_STREAM,
  8.                                                MBEDTLS_SSL_PRESET_DEFAULT)) != 0) {
  9.         return opStatus;
  10.     }
  11.  
  12.     return opStatus;
  13. }

Parametr MBEDTLS_SSL_TRANSPORT_STREAM działa nad strumieniem TCP. Dla projektu wykorzystującego DTLS należy użyć protokołu UDP:

  1. #define MBEDTLS_SSL_TRANSPORT_STREAM            0   /*!< TLS      */
  2. #define MBEDTLS_SSL_TRANSPORT_DATAGRAM          1   /*!< DTLS     */

Połączenie zaczyna się od TCP. Po jego ustanowieniu biblioteka mbedTLS dokłada do niego wartstwę TLS. Pozwala to na zaszyfrowanie strumienia danych. 

  1. static uint8_t ConnectionSSL_Configuration(void)
  2. {
  3.     int opStatus = 0;
  4.  
  5.     mbedtls_ssl_conf_authmode(&conf, MBEDTLS_SSL_VERIFY_OPTIONAL);
  6.     mbedtls_ssl_conf_ca_chain(&conf, &server_cert, NULL);
  7.     mbedtls_ssl_conf_rng(&conf, mbedtls_ctr_drbg_random, &ctr_drbg);
  8.  
  9.     mbedtls_ssl_conf_min_version(&conf,
  10.                                  MBEDTLS_SSL_MAJOR_VERSION_3,
  11.                                  MBEDTLS_SSL_MINOR_VERSION_3);
  12.     mbedtls_ssl_conf_max_version(&conf,
  13.                                  MBEDTLS_SSL_MAJOR_VERSION_3,
  14.                                  MBEDTLS_SSL_MINOR_VERSION_3);
  15.  
  16.     mbedtls_ssl_conf_read_timeout(&conf, 10000);
  17.  
  18.     opStatus = mbedtls_ssl_conf_own_cert(&conf, &cacert, &pkey);
  19.     if (opStatus < 0) return opStatus;
  20.  
  21.     opStatus = mbedtls_ssl_setup(&ssl, &conf);
  22.     if (opStatus != 0) return opStatus;
  23.  
  24.     return 0;
  25. }

Walidacja certyfikatu serwera obejmuje sprawdzenie jego poprawności czy zgodności z oczekiwanym hostem. Funkcja mbedtls_ssl_set_hostname() ustawia nazwę, z którą biblioteka porównuje dane certyfikatu. 

Przy pełnym i poprawnym certyfikacie warto zmienić opcję MBEDTLS_SSL_VERIFY_OPTIONAL na MBEDTLS_SSL_VERIFY_REQUIRED. 

Funkcje zastosowane powyżej wykonują następujące zadania:
  • mbedtls_ssl_config_defaults - przygotowuje bazową konfiguracje klienta SSL przez TCP
  • mbedtls_ssl_conf_authmode - ustawienie polityki weryfikacji certyfikatu serwera.
  • mbedtls_ssl_conf_ca_chain - przekazujemy który certyfikat będzie wykorzystywany do weryfikacji.
  • mbedtls_ssl_conf_rng - podłącza generator liczb losowych.
  • mbedtls_ssl_conf_min_version (max_version) - ustawia wersję TLS na 1.2.
  • mbedtls_ssl_conf_own_cert - podpięcie certyfikatu i klucza klienta.
  • mbedtls_ssl_setup - podłączenie sesji ssl z certyfikatem.
TLS 1.2. ciągle jest popularnym protokołem w systemach wbudowanych. Głównie ze względu na szerokie wsparcie i dobre biblioteki. Handshake w nim jest także bardziej rozbudowany niż w wersji 1.3. Przekłada się to na większą liczbę kroków w negocjacji czyli dłuższy czas potrzebny do zestawienia połączenia. 

Podłączenie transportu i handshak'a:

  1. static uint8_t SetHostname_BioCallbacks(void)
  2. {
  3.     int opStatus = 0;
  4.  
  5.     //ustawia nazwe hosta do weryfikacji certyfikatu.
  6.     if((opStatus = mbedtls_ssl_set_hostname(&ssl, "name")) != 0) {
  7.         return opStatus;
  8.     }
  9.    
  10.     //laczy TLS z gniazdem TCP oraz `send` i `recv`.
  11.     mbedtls_ssl_set_bio(&ssl, &server_fd, mbedtls_net_send, mbedtls_net_recv, NULL);
  12.     return opStatus;
  13. }

Teraz pora na handshake:

  1. static int Handshake_timeout(uint32_t timeout_ms)
  2. {
  3.     int ret;
  4.     TickType_t start = xTaskGetTickCount();
  5.     TickType_t timeout_ticks = pdMS_TO_TICKS(timeout_ms);
  6.  
  7.     //wykonanie wszystkie kroki negocjacji TLS.
  8.     while ((ret = mbedtls_ssl_handshake(&ssl)) != 0)
  9.     {
  10.         //tutaj bez bledów krytycznych tylko konieczność,
  11.         //ponownego wywolania.
  12.         if (ret == MBEDTLS_ERR_SSL_WANT_READ ||
  13.             ret == MBEDTLS_ERR_SSL_WANT_WRITE ||
  14.             ret == MBEDTLS_ERR_SSL_CRYPTO_IN_PROGRESS)
  15.         {
  16.             if ((xTaskGetTickCount() - start) > timeout_ticks)
  17.                 return MBEDTLS_ERR_SSL_TIMEOUT;
  18.  
  19.             osDelay(2);
  20.             continue;
  21.         }
  22.  
  23.         return ret;
  24.     }
  25.  
  26.     return 0;
  27. }

W tej funkcji dodatkowo jest zaimplementowany timeout, party o tick FreeRTOS. Tak aby handshake nie wisiał w nieskończoność z powodu błędów. 

Dla standardowego scenariusza TLS 1.2 wymiana pakietów wygląda następująco:
  • ClientHello - klient wysyła wersję TLS, listę ciphersuite, dane losowe. 
  • ServerHello - serwer przekazuje wybrane wersje protokołu i ciphersuite.
  • Certificate - serwer przesyła swój certyfikat X.509
  • ServerKeyExchange - serwer przekazuje dane potrzebne do wymiany klucza. 
  • ServerHelloDone - koniec odpowiedzi od serwera.
  • ClientKeyExchange - przesyłanie danych do ustanowienia sesji oraz certyfikat klienta jeśli jest wymaganay.
  • ChangeCipherSpec - przejście na klucze sesyjne,
  • Finished - potwierdzenie poprawnej negocjacji.
Ten etap jest konieczny. Są na nim uzgadniane parametry sesji. Dopiero po jego poprawnym zakończeniu aplikacje mogą przejść do wymiany danych użytkowników.  

Zapis i odczyt danych odbywa się przez następujące funkcje:

  1. ret = mbedtls_ssl_write(&ssl, frameToSend + sent, numberOfDataToSend - sent);
  2. ret = mbedtls_ssl_read(&ssl, (unsigned char *)buf, len);

Operują one oczywiście na warstwie TLS. Czyli dane są odczyta szyfrowane/odszyfrowane i przekazywane dalej. 

Ważnym elementem jest sterowanie stanem sesji TLS. Jest ono niezależne od stanu samego TCP. Do takich operacji wykorzystuje się alerty. Jednym z nich jest close_notify. Odpowiada on za kontrolowane zamknięcie kanału szyfrowanego. Czyli nie tylko należy zamknąć socket ale także wysłać odpowiedni alert:

  1. do {
  2.     ret = mbedtls_ssl_close_notify(&ssl);
  3. } while(ret == MBEDTLS_ERR_SSL_WANT_WRITE);
  4.  
  5. mbedtls_ssl_session_reset(&ssl);
  6. mbedtls_net_free(&server_fd);

Dzięki takiemu zakończeniu sesji wiadomo, że druga strona musi zamknąć połączenie oraz, że nie doszło do żadnego błędu. 

Należ także zadbać o czyszczenie danych sesji TLS oraz przygotowaniu danych do kolejnego handshaka.

  1. ret = mbedtls_ssl_session_reset(&ssl);
  2. if (ret != 0) {
  3.     mbedtls_printf_mac("ERROR - mbedtls_ssl_session_reset returned %d\r\n \r\n ", ret);
  4.  
  5.     // twardy reset ssl jeśli reset sesji nie wyszedł
  6.     mbedtls_ssl_free(&ssl);
  7.     mbedtls_ssl_init(&ssl);
  8.  
  9.     // setup ponownie z już skonfigurowanym conf
  10.     ret = mbedtls_ssl_setup(&ssl, &conf);
  11.     if (ret != 0) {
  12.         mbedtls_printf_mac("ERROR - mbedtls_ssl_setup(re) returned %d\r\n \r\n ", ret);
  13.         mbedtls_net_free(&server_fd);
  14.         osDelay(30000);
  15.         continue;
  16.     }
  17.  
  18.     // hostname trzeba ponownie po re-init ssl
  19.     ret = mbedtls_ssl_set_hostname(&ssl, "name");
  20.     if (ret != 0) {
  21.         mbedtls_printf_mac("ERROR - mbedtls_ssl_set_hostname(re) returned %d\r\n \r\n ", ret);
  22.         mbedtls_net_free(&server_fd);
  23.         osDelay(30000);
  24.         continue;
  25.     }
  26. }

Powyżej widać, że dodatkowo czyszczone są wewnętrzne stany TLS. Tak aby kolejny hanshake mógł zacząć od zera i odziedziczył żadnych danych z poprzedniej sesji 

Kolejnym nie mniej istotnym elementem jest obsługa stanów pośrednich zwracanych przez TLS. 

  1. //Wysyłanie danych
  2. while (sent < numberOfDataToSend)
  3. {
  4.     ret = mbedtls_ssl_write(&ssl,
  5.                             frameToSend + sent,
  6.                             numberOfDataToSend - sent);
  7.  
  8.     if (ret > 0)
  9.     {
  10.         sent += (uint16_t)ret;
  11.         continue;
  12.     }
  13.  
  14.     if (ret == MBEDTLS_ERR_SSL_WANT_READ ||
  15.         ret == MBEDTLS_ERR_SSL_WANT_WRITE)
  16.     {
  17.         osDelay(2);
  18.         continue;
  19.     }
  20.  
  21.     return -1;
  22. }
  23.  
  24. //Odczyt danych
  25. ret = mbedtls_ssl_read(&ssl, (unsigned char *)buf, len);
  26.  
  27. if (ret == MBEDTLS_ERR_SSL_TIMEOUT)
  28. {
  29.     sslReadTimeoutCounter++;
  30.  
  31.     if (sslReadTimeoutCounter >= 7)
  32.     {
  33.         break;
  34.     }
  35.  
  36.     osDelay(10);
  37.     continue;
  38. }

Cyklicznie ponawiane są operacje odczytu, zapisu oraz handshake w zależności od stanu i timeout'u.  

Dla całej konfiguracji należy pamiętać, że TLS oprócz samego miejsca na biblioteki mbedTLS potrzebuje mieć jeszcze przydzielone miejsce do buforów, certyfikatów, kluczy, stanu sesji czy wymiany danych w handshak'u.  

W celu weryfikacji ile miejsca jest zajętych i jeśli się wywala to na których elementach warto korzystać z funkcji debugowania:

  1. static void my_debug(void *ctx, int level, const char *file, int line, const char *str)
  2. {
  3.   ((void) level);
  4.  
  5.   mbedtls_fprintf((FILE*) ctx, "%s:%04d: %s", file, line, str);
  6.   fflush((FILE*) ctx);
  7. }

  1. //Podłączenie funkcji debug
  2. mbedtls_ssl_conf_dbg(&conf, my_debug, stdout);
Do określenia zajętości pamięci dobrze sprawdzi się coś takiego:

  1. //Bufor odbiorczy
  2. char buf[3072] = {0x00};
  3. oraz diagnostykę wolnej pamięci:
  4.  
  5. printf("[SSL][HEAP] free=%u min=%u\r\n",
  6.        (unsigned)xPortGetFreeHeapSize(),
  7.        (unsigned)xPortGetMinimumEverFreeHeapSize());
  8.  
  9. //Monitorowanie stosu
  10. static void PrintTaskWatermark(const char *tag)
  11. {
  12.     printf("[STACK] %s watermark=%u\r\n",
  13.            tag, (unsigned)uxTaskGetStackHighWaterMark(NULL));
  14. }

Główna funkcja uruchamiająca TLS oraz przetwarzająca dane wygląda następująco:

  1. #define DEBUG_CLIENT_TASK
  2. void StartSSLClientTask(void const *argument)
  3. {
  4.     int ret = 1;
  5.     int len = 0;
  6.    
  7.     //Semafor ustawiony po uruchomieniu LWIP
  8.     while (xSemaphoreTake(sslTaskSemaphore, portMAX_DELAY) == 0) {
  9.         osDelay(3000);
  10.     }
  11.    
  12.     #ifdef DEBUG_CLIENT_TASK
  13.     printf("[StartSSLClientTask] SSL START\r\n");
  14.     PrintTaskWatermark("ssl start");
  15.     fflush(stdout);
  16.     #endif
  17.  
  18.     Get_Port_String_Data(&serverPort[0]);
  19.     Get_Server_Ip_String_Data(&serverIp[0]);
  20.     Get_Board_Ip_String_Data(&boardIp[0]);
  21.  
  22. #if defined(MBEDTLS_DEBUG_C)
  23.     mbedtls_debug_set_threshold(DEBUG_LEVEL);
  24. #endif
  25.  
  26.     Initialize_SessionData();
  27.  
  28.     ret = Drbg_Seed();
  29.     if (ret != 0) { goto exit_task; }
  30.     #ifdef DEBUG_CLIENT_TASK
  31.     PrintTaskWatermark("after Drbg_Seed");
  32.     #endif
  33.  
  34.     ret = InitializeCertificate();
  35.     if (ret != 0) { goto exit_task; }
  36.     #ifdef DEBUG_CLIENT_TASK
  37.     PrintTaskWatermark("after InitializeCertificate");
  38.     #endif
  39.  
  40.     ValidateIpAddress();
  41.  
  42.     mbedtls_printf_mac("MBEDTLS LDC\r\n");
  43.     if (LoadDefaultConfiguration() != 0) { goto exit_task; }
  44.     #ifdef DEBUG_CLIENT_TASK
  45.     PrintTaskWatermark("after LoadDefaultConfiguration");
  46.     #endif
  47.  
  48.     if (ConnectionSSL_Configuration() != 0) { goto exit_task; }
  49.     #ifdef DEBUG_CLIENT_TASK
  50.     PrintTaskWatermark("after ConnectionSSL_Configuration");
  51.     #endif
  52.  
  53.     if ((ret = mbedtls_ssl_set_hostname(&ssl, "name")) != 0) {
  54.         mbedtls_printf_mac("ERROR - mbedtls_ssl_set_hostname returned %d\r\n \r\n ", ret);
  55.         goto exit_task;
  56.     }
  57.  
  58.     while (1)
  59.     {
  60.         //nowy socket
  61.         mbedtls_net_init(&server_fd);
  62.  
  63.         #ifdef DEBUG_CLIENT_TASK
  64.         mbedtls_printf_mac("ConnectToServer\r\n");
  65.         #endif
  66.  
  67.         ConnectingToServerFlag = 1;
  68.  
  69.         if (ConnectToServerTcp() != 0) {
  70.             mbedtls_printf_mac("ConnectToServerTcp failed\r\n");
  71.             osDelay(5000);
  72.             continue;
  73.         }
  74.  
  75.         #ifdef DEBUG_CLIENT_TASK
  76.         mbedtls_printf_mac("ConnectOK\r\n");
  77.         #endif
  78.  
  79.         sslTaskTimerCounter = 1;
  80.  
  81.         ret = mbedtls_ssl_session_reset(&ssl);
  82.         if (ret != 0) {
  83.             mbedtls_printf_mac("ERROR - mbedtls_ssl_session_reset returned %d\r\n \r\n ", ret);
  84.  
  85.             mbedtls_ssl_free(&ssl);
  86.             mbedtls_ssl_init(&ssl);
  87.  
  88.             ret = mbedtls_ssl_setup(&ssl, &conf);
  89.             if (ret != 0) {
  90.                 mbedtls_printf_mac("ERROR - mbedtls_ssl_setup(re) returned %d\r\n \r\n ", ret);
  91.                 mbedtls_net_free(&server_fd);
  92.                 osDelay(30000);
  93.                 continue;
  94.             }
  95.  
  96.             ret = mbedtls_ssl_set_hostname(&ssl, "rcpkd");
  97.             if (ret != 0) {
  98.                 mbedtls_printf_mac("ERROR - mbedtls_ssl_set_hostname(re) returned %d\r\n \r\n ", ret);
  99.                 mbedtls_net_free(&server_fd);
  100.                 osDelay(30000);
  101.                 continue;
  102.             }
  103.         }
  104.  
  105.         //Po każdym zerwaniu połączenia czy reset należy przypiąć nowe BIO
  106.         mbedtls_ssl_set_bio(&ssl, &server_fd, mbedtls_net_send, mbedtls_net_recv, mbedtls_net_recv_timeout);
  107.  
  108.         handshakeTimeoutCount = 0;
  109.         HandshakeStart = 2;
  110.        
  111.         #ifdef DEBUG_CLIENT_TASK
  112.         mbedtls_printf_mac("HS Start\r\n");
  113.         #endif
  114.  
  115.         #ifdef DEBUG_CLIENT_TASK
  116.         PrintTaskWatermark("before handshake");
  117.         #endif
  118.  
  119.         ret = Handshake_timeout(20000); // 20s
  120.         #ifdef DEBUG_CLIENT_TASK
  121.         PrintTaskWatermark("after handshake");
  122.         #endif
  123.         if (ret != 0) {
  124.             HandshakeStart = 0;
  125.             handshakeTimeoutCount = 0;
  126.             goto cleanup_session;
  127.         }
  128.  
  129.         ConnectingToServerFlag = 0;
  130.  
  131.         HandshakeStart = 1;
  132.         handshakeTimeoutCount = 0;
  133.  
  134.         #ifdef DEBUG_CLIENT_TASK
  135.         mbedtls_printf_mac("[StartSSLClientTask] HAND\r\n");
  136.         #endif
  137.         VarifyCertificate();
  138.         #ifdef DEBUG_CLIENT_TASK
  139.         mbedtls_printf_mac("[StartSSLClientTask] VERCERT\r\n");
  140.         #endif
  141.        
  142.         #ifdef DEBUG_CLIENT_TASK
  143.         printf("[StartSSLClientTask] before auth cmd\r\n");
  144.         printf("[StartSSLClientTask][HEAP] free=%u min=%u\r\n",
  145.                (unsigned)xPortGetFreeHeapSize(),
  146.                (unsigned)xPortGetMinimumEverFreeHeapSize());
  147.         #endif
  148.  
  149.         PrepareAndSendAuthenticationCommandToServer();
  150.  
  151.         #ifdef DEBUG_CLIENT_TASK
  152.         printf("[StartSSLClientTask] after auth cmd\r\n");
  153.         printf("[StartSSLClientTask][HEAP] free=%u min=%u\r\n",
  154.                (unsigned)xPortGetFreeHeapSize(),
  155.                (unsigned)xPortGetMinimumEverFreeHeapSize());
  156.         #endif
  157.  
  158.         while (1)
  159.         {
  160.             len = sizeof(buf) - 1;
  161.             memset(buf, 0, sizeof(buf));
  162.  
  163.             #ifdef DEBUG_CLIENT_TASK
  164.             printf("[StartSSLClientTask] before first read\r\n");
  165.             printf("[StartSSLClientTask][HEAP] free=%u min=%u\r\n",
  166.                    (unsigned)xPortGetFreeHeapSize(),
  167.                    (unsigned)xPortGetMinimumEverFreeHeapSize());
  168.             #endif
  169.  
  170.             ret = mbedtls_ssl_read(&ssl, (unsigned char *)buf, len);
  171.            
  172.             #ifdef DEBUG_CLIENT_TASK
  173.             printf("[SSL] read ret=%d\r\n", ret);
  174.             #endif
  175.  
  176.             if (ret == MBEDTLS_ERR_SSL_WANT_READ ||
  177.                 ret == MBEDTLS_ERR_SSL_WANT_WRITE)
  178.             {
  179.                 osDelay(10);
  180.                 continue;
  181.             }
  182.  
  183.             if (ret == MBEDTLS_ERR_SSL_TIMEOUT)
  184.             {
  185.                 sslReadTimeoutCounter++;
  186.                 printf("[SSL] read timeout count=%u\r\n", sslReadTimeoutCounter);
  187.  
  188.                 if (sslReadTimeoutCounter >= 7)
  189.                 {
  190.                     printf("[SSL] too many read timeouts -> reconnect\r\n");
  191.                     break;
  192.                 }
  193.  
  194.                 osDelay(10);
  195.                 continue;
  196.             }
  197.  
  198.             if (ret == MBEDTLS_ERR_SSL_WANT_READ || ret == MBEDTLS_ERR_SSL_WANT_WRITE)
  199.             {
  200.                 osDelay(10);
  201.                 continue;
  202.             }
  203.            
  204.             //Poprawne zamknięcie połączenia przez serwer
  205.             if (ret == MBEDTLS_ERR_SSL_PEER_CLOSE_NOTIFY)
  206.             {
  207.                 break;
  208.             }
  209.  
  210.             if (ret < 0)
  211.             {
  212.                 printf("SSL read error: %d\n", ret); // prawdziwy błąd
  213.                 break;
  214.             }
  215.  
  216.             if (ret == 0)
  217.             {
  218.                 // EOF / TCP close
  219.                 break;
  220.             }
  221.  
  222.             counterSSLTimeout = 0;
  223.             sslTaskTimerCounter = 1;
  224.             sslReadTimeoutCounter = 0;
  225.  
  226.             Analize_Msg(buf, ret);
  227.         }
  228.         //Cleanup oraz reconnect
  229. cleanup_session:
  230.         mbedtls_printf_mac("close session...\r\n \r\n ");
  231.  
  232.         do {
  233.             ret = mbedtls_ssl_close_notify(&ssl);
  234.         } while (ret == MBEDTLS_ERR_SSL_WANT_WRITE);
  235.  
  236.         (void)mbedtls_ssl_session_reset(&ssl);
  237.  
  238.         mbedtls_net_free(&server_fd);
  239.  
  240.         flagSSLTask = 0;
  241.  
  242.         osDelay(20000);
  243.         continue;
  244.     }
  245.  
  246. exit_task:
  247.     mbedtls_printf_mac("error on mbedtls...\r\n \r\n ");
  248.     mbedtls_net_free(&server_fd);
  249.     mbedtls_x509_crt_free(&cacert);
  250.     mbedtls_x509_crt_free(&server_cert);
  251.     mbedtls_ssl_free(&ssl);
  252.     mbedtls_ssl_config_free(&conf);
  253.     mbedtls_ctr_drbg_free(&ctr_drbg);
  254.     mbedtls_entropy_free(&entropy);
  255.  
  256. #if defined(MBEDTLS_MEMORY_BUFFER_ALLOC_C)
  257.     mbedtls_memory_buffer_alloc_free();
  258. #endif
  259.     SSLClient_FreeMemmory();
  260.     mbedtls_printf_mac("Task delete\r\n \r\n ");
  261.     vTaskDelete(NULL);
  262. }

Powyższa implementacja pokazuje dosyć stabilne zestawienie i utrzymanie połączenia TLS 1.2 w systemach wbudowanych, które bazują na bibliotece mbedTLS.