czwartek, 17 kwietnia 2025

STM32 - Funkcje bezpieczeństwa - X-CUBE-CRYPTOLIB

W tym wpisie przedstawię, jak zainstalować oraz uruchomić bibliotekę X-CUBE-CRYPTOLIB, przeznaczoną do implementacji algorytmów kryptograficznych na mikrokontrolerach STM32.


X-CUBE-CRYPTOLIB:


Biblioteka X-CUBE-CRYPTOLIB wspiera wszystkie układy STM32 i dostarcza implementacje wielu algorytmów.

Została zoptymalizowana do działania z wykorzystaniem sprzętowego akceleratora CRC, dzięki czemu może być używana na wszystkich układach STM32


Bibliotekę najlepiej pobrać ze strony ST i dołożyć do projektu. 


Rysunek powyżej przedstawia architekturę biblioteki kryptograficznej X-CUBE-CRYPTOLIB. Można na niej wyróżnić trzy główne warstwy:

User application (aplikacja użytkownika) - W tej części następuje implementacja biblioteki przez użytkownika. 

Cryptographic Library Middleware (Pośrednia warstwa kryptograficzna) - Odpowiada za dostarczenie gotowych algorytmów. Biblioteka działa jako warstwa pośrednia pomiędzy aplikacją użytkownika a sprzętem (Development Board) .

Biblioteka została podzielona na kilka kategorii:

CIPHER - szyfrowanie symetryczne
ECC - kryptografia krzywych eliptycznych
MAC - kody uwierzytelniające
RSA - szyfrowanie asymetryczne i podpisy cyfrowe
HASH - funkcje skrótu
DRBG - deterministyczne generowanie liczb losowych.

Development Board - płyta z układem STM32. 

Instalacja biblioteki:


Najlepszym sposobem na uzyskanie biblioteki jest pobranie jej ze strony producenta.

Po pobraniu wystarczy skopiować jej zawartość do folderu projektu (np. Middlewares/ST), a następnie dodać odpowiednie katalogi do ścieżek include i bibliotek w ustawieniach projektu.


 Później w projekcie dokładamy następujące elementy. 


Dodajemy odpowiednią bibliotekę do projektu:


Test AES:

Blokowy diagram AES [link]:


AES pozwala na przetworzenie 128 bitowego bloku danych przy użyciu klucza 128 lub 256 bitów. 

Na samym początku należy uruchomić bibliotekę:

  1. if (cmox_initialize(NULL) != CMOX_INIT_SUCCESS)
  2. {
  3.   Error_Handler();
  4. }

Wybór implementacji AES wykonuje się w pliku cmox_default_config.h:

  1. /**
  2.   * @brief Flag indicating the default implementation of AES
  3.   * @note  Value can be
  4.   *        - @ref CMOX_AES_SMALL
  5.   *        - @ref CMOX_AES_FAST
  6.   */
  7. #define CMOX_AES_IMPLEMENTATION CMOX_AES_FAST
  8.  
  9. /**
  10.   * @brief Flag indicating the fast implementation of AES
  11.   */
  12. #define CMOX_AES_FAST   0x01U
  13.  
  14. /**
  15.   * @brief Flag indicating the small implementation of AES
  16.   */
  17. #define CMOX_AES_SMALL  0x02U

Aby wykonać szyfrowanie AES należy wykorzystać funkcje:

  1. cmox_cipher_retval_t cmox_cipher_encrypt(cmox_cipher_algo_t P_algo,
  2.                                          const uint8_t *P_pInput,
  3.                                          size_t P_inputLen,
  4.                                          const uint8_t *P_pKey,
  5.                                          cmox_cipher_keyLen_t P_keyLen,
  6.                                          const uint8_t *P_pIv,
  7.                                          size_t P_ivLen,
  8.                                          uint8_t *P_pOutput,
  9.                                          size_t *P_pOutputLen);

Przykładowa funkcja wykonująca szyfrowanie może wyglądać w następujący sposób:

  1. uint8_t test_aes_cbc_128_encrypt(void) {
  2.     cmox_cipher_retval_t retVal;
  3.     size_t outputLen;
  4.  
  5.     retVal = cmox_cipher_encrypt(
  6.           CMOX_AES_CBC_ENC_ALGO,      
  7.           textToEncrypt,              
  8.           sizeof(textToEncrypt),        
  9.           key,                          
  10.           sizeof(key),                
  11.           IV,                          
  12.           sizeof(IV),                  
  13.           encBuffer,                    
  14.           &outputLen);                  
  15.  
  16.      if( (retVal != CMOX_CIPHER_SUCCESS) ||
  17.           (outputLen != sizeof(encryptedText)) ||
  18.           (memcmp(encryptedText, encBuffer, outputLen) != 0) ) {
  19.          return 1;
  20.      }
  21.  
  22.      return 0;
  23. }

Do funkcji cmox_cipher_encrypt wprowadza się następujące dane:

  1. retVal = cmox_cipher_encrypt(
  2.     CMOX_AES_CBC_ENC_ALGO, -> Tryb szyfrowania (AES-CBC)
  3.     textToEncrypt,         -> Tekst do zaszyfrowania
  4.     sizeof(textToEncrypt), -> Rozmiar tekstu
  5.     key,                   -> Klucz szyfrujący
  6.     sizeof(key),           -> Rozmiar klucza (16 bajtów)
  7.     IV,                    -> Wektor inicjalizacyjny
  8.     sizeof(IV),            -> Rozmiar IV (16 bajtów)
  9.     encBuffer,             -> Bufor wyjściowy (wynik szyfrowania)
  10.     &outputLen             -> Zmienna do której zostanie zapisany rozmiar wyniku szyfrowania
  11. );

Dane wejściowe muszą być wielokrotnością 16 bajtów. Jeśli tak nie jest to trzeba je dopełnić paddingiem. 

Następnie następuje sprawdzenie wartości zwracanej przez funkcje. Możliwe zwracane wartości są opisane w pliku cmox_cipher_retvals.h:

  1. //@brief Cipher operation successfully performed
  2. #define CMOX_CIPHER_SUCCESS                  ((cmox_cipher_retval_t)0x00010000U)
  3. //@brief Some error happens internally in the cipher module
  4. #define CMOX_CIPHER_ERR_INTERNAL             ((cmox_cipher_retval_t)0x00010001U)
  5. //@brief The function is not implemented for the current algorithm
  6. #define CMOX_CIPHER_ERR_NOT_IMPLEMENTED      ((cmox_cipher_retval_t)0x00010002U)
  7. //@brief One or more parameter has been wrongly passed to the function
  8. #define CMOX_CIPHER_ERR_BAD_PARAMETER        ((cmox_cipher_retval_t)0x00010003U)
  9. //@brief Error on performing the operation
  10. #define CMOX_CIPHER_ERR_BAD_OPERATION        ((cmox_cipher_retval_t)0x00010004U)
  11. //@brief A buffer with a wrong size has been passed to the function
  12. #define CMOX_CIPHER_ERR_BAD_INPUT_SIZE       ((cmox_cipher_retval_t)0x00010005U)
  13. //@brief Authentication of the tag has been successful
  14. #define CMOX_CIPHER_AUTH_SUCCESS             ((cmox_cipher_retval_t)0x0001C726U)
  15. //@brief Authentication of the tag failed
  16. #define CMOX_CIPHER_AUTH_FAIL                ((cmox_cipher_retval_t)0x00016E93U)

Działanie algorytmu najlepiej zweryfikować w oparciu o przykładowe dane np. takie z NIST [link]:

  1. Key 2b7e151628aed2a6abf7158809cf4f3c
  2. IV 000102030405060708090a0b0c0d0e0f
  3.  
  4. Block #1
  5. Plaintext 6bc1bee22e409f96e93d7e117393172a
  6. Input Block 6bc0bce12a459991e134741a7f9e1925
  7. Output Block 7649abac8119b246cee98e9b12e9197d
  8. Ciphertext 7649abac8119b246cee98e9b12e9197d
  9.  
  10. Block #2
  11. Plaintext ae2d8a571e03ac9c9eb76fac45af8e51
  12. Input Block d86421fb9f1a1eda505ee1375746972c
  13. Output Block 5086cb9b507219ee95db113a917678b2
  14. Ciphertext 5086cb9b507219ee95db113a917678b2
  15.  
  16. Block #3
  17. Plaintext 30c81c46a35ce411e5fbc1191a0a52ef
  18. Input Block 604ed7ddf32efdff7020d0238b7c2a5d
  19. Output Block 73bed6b8e3c1743b7116e69e22229516
  20. Ciphertext 73bed6b8e3c1743b7116e69e22229516
  21.  
  22. Block #4
  23. Plaintext f69f2445df4f9b17ad2b417be66c3710
  24. Input Block 8521f2fd3c8eef2cdc3da7e5c44ea206
  25. Output Block 3ff1caa1681fac09120eca307586e1a7

Powyżej widać jak powinien wyglądać każdy z bloków danych.

Wywołanie powyższej funkcji spowoduje wygenerowanie identycznych danych wyjściowych:

  1. AES CBC Encrypt
  2. Block1: 76 49 AB AC 81 19 B2 46 CE E9 8E 9B 12 E9 19 7D
  3. Block2: 50 86 CB 9B 50 72 19 EE 95 DB 11 3A 91 76 78 B2
  4. Block3: 73 BE D6 B8 E3 C1 74 3B 71 16 E6 9E 22 22 95 16
  5. Block4: 3F F1 CA A1 68 1F AC 09 12 0E CA 30 75 86 E1 A7

Funkcja decrypt wygląda następująco:

  1. uint8_t test_aes_cbc_128_decrypt(void) {
  2.     cmox_cipher_retval_t retVal;
  3.     size_t outputLen;
  4.  
  5.     printf("AES CBC Decrypt\r\n");
  6.  
  7.     retVal = cmox_cipher_decrypt(
  8.           CMOX_AES_CBC_DEC_ALGO,
  9.           encryptedText,
  10.           sizeof(encryptedText),
  11.           key,
  12.           sizeof(key),
  13.           IV,
  14.           sizeof(IV),
  15.           decBuffer,
  16.           &outputLen);
  17.  
  18.     //check the result
  19.     if((retVal != CMOX_CIPHER_SUCCESS) ||
  20.        (outputLen != sizeof(plainText)) ||
  21.        (memcmp(plainText, decBuffer, outputLen) != 0) ){
  22.         return 1;
  23.     }
  24.  
  25.     for(size_t i = 0; i < outputLen; i++)
  26.     {
  27.       printf("%02X ", decBuffer[i]);
  28.     }
  29.  
  30.     printf("\r\n");
  31.  
  32.     return 0;
  33. }

Wywołanie funkcji decrypt spowoduje wygenerowanie następujących danych:

  1. AES CBC Decrypt
  2. 6B C1 BE E2 2E 40 9F 96 E9 3D 7E 11 73 93 17 2A
  3. AE 2D 8A 57 1E 03 AC 9C 9E B7 6F AC 45 AF 8E 51
  4. 30 C8 1C 46 A3 5C E4 11 E5 FB C1 19 1A 0A 52 EF
  5. F6 9F 24 45 DF 4F 9B 17 AD 2B 41 7B E6 6C 37 10