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
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:
Blokowy diagram AES [link]:
Na samym początku należy uruchomić bibliotekę:
- if (cmox_initialize(NULL) != CMOX_INIT_SUCCESS)
- {
- Error_Handler();
- }
Wybór implementacji AES wykonuje się w pliku cmox_default_config.h:
- /**
- * @brief Flag indicating the default implementation of AES
- * @note Value can be
- * - @ref CMOX_AES_SMALL
- * - @ref CMOX_AES_FAST
- */
- #define CMOX_AES_IMPLEMENTATION CMOX_AES_FAST
- /**
- * @brief Flag indicating the fast implementation of AES
- */
- #define CMOX_AES_FAST 0x01U
- /**
- * @brief Flag indicating the small implementation of AES
- */
- #define CMOX_AES_SMALL 0x02U
Aby wykonać szyfrowanie AES należy wykorzystać funkcje:
- cmox_cipher_retval_t cmox_cipher_encrypt(cmox_cipher_algo_t P_algo,
- const uint8_t *P_pInput,
- size_t P_inputLen,
- const uint8_t *P_pKey,
- cmox_cipher_keyLen_t P_keyLen,
- const uint8_t *P_pIv,
- size_t P_ivLen,
- uint8_t *P_pOutput,
- size_t *P_pOutputLen);
Przykładowa funkcja wykonująca szyfrowanie może wyglądać w następujący sposób:
- uint8_t test_aes_cbc_128_encrypt(void) {
- cmox_cipher_retval_t retVal;
- size_t outputLen;
- retVal = cmox_cipher_encrypt(
- CMOX_AES_CBC_ENC_ALGO,
- textToEncrypt,
- sizeof(textToEncrypt),
- key,
- sizeof(key),
- IV,
- sizeof(IV),
- encBuffer,
- &outputLen);
- if( (retVal != CMOX_CIPHER_SUCCESS) ||
- (outputLen != sizeof(encryptedText)) ||
- (memcmp(encryptedText, encBuffer, outputLen) != 0) ) {
- return 1;
- }
- return 0;
- }
Do funkcji cmox_cipher_encrypt wprowadza się następujące dane:
- retVal = cmox_cipher_encrypt(
- CMOX_AES_CBC_ENC_ALGO, -> Tryb szyfrowania (AES-CBC)
- textToEncrypt, -> Tekst do zaszyfrowania
- sizeof(textToEncrypt), -> Rozmiar tekstu
- key, -> Klucz szyfrujący
- sizeof(key), -> Rozmiar klucza (16 bajtów)
- IV, -> Wektor inicjalizacyjny
- sizeof(IV), -> Rozmiar IV (16 bajtów)
- encBuffer, -> Bufor wyjściowy (wynik szyfrowania)
- &outputLen -> Zmienna do której zostanie zapisany rozmiar wyniku szyfrowania
- );
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:
- //@brief Cipher operation successfully performed
- #define CMOX_CIPHER_SUCCESS ((cmox_cipher_retval_t)0x00010000U)
- //@brief Some error happens internally in the cipher module
- #define CMOX_CIPHER_ERR_INTERNAL ((cmox_cipher_retval_t)0x00010001U)
- //@brief The function is not implemented for the current algorithm
- #define CMOX_CIPHER_ERR_NOT_IMPLEMENTED ((cmox_cipher_retval_t)0x00010002U)
- //@brief One or more parameter has been wrongly passed to the function
- #define CMOX_CIPHER_ERR_BAD_PARAMETER ((cmox_cipher_retval_t)0x00010003U)
- //@brief Error on performing the operation
- #define CMOX_CIPHER_ERR_BAD_OPERATION ((cmox_cipher_retval_t)0x00010004U)
- //@brief A buffer with a wrong size has been passed to the function
- #define CMOX_CIPHER_ERR_BAD_INPUT_SIZE ((cmox_cipher_retval_t)0x00010005U)
- //@brief Authentication of the tag has been successful
- #define CMOX_CIPHER_AUTH_SUCCESS ((cmox_cipher_retval_t)0x0001C726U)
- //@brief Authentication of the tag failed
- #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]:
- Key 2b7e151628aed2a6abf7158809cf4f3c
- IV 000102030405060708090a0b0c0d0e0f
- Block #1
- Plaintext 6bc1bee22e409f96e93d7e117393172a
- Input Block 6bc0bce12a459991e134741a7f9e1925
- Output Block 7649abac8119b246cee98e9b12e9197d
- Ciphertext 7649abac8119b246cee98e9b12e9197d
- Block #2
- Plaintext ae2d8a571e03ac9c9eb76fac45af8e51
- Input Block d86421fb9f1a1eda505ee1375746972c
- Output Block 5086cb9b507219ee95db113a917678b2
- Ciphertext 5086cb9b507219ee95db113a917678b2
- Block #3
- Plaintext 30c81c46a35ce411e5fbc1191a0a52ef
- Input Block 604ed7ddf32efdff7020d0238b7c2a5d
- Output Block 73bed6b8e3c1743b7116e69e22229516
- Ciphertext 73bed6b8e3c1743b7116e69e22229516
- Block #4
- Plaintext f69f2445df4f9b17ad2b417be66c3710
- Input Block 8521f2fd3c8eef2cdc3da7e5c44ea206
- 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:
- AES CBC Encrypt
- Block1: 76 49 AB AC 81 19 B2 46 CE E9 8E 9B 12 E9 19 7D
- Block2: 50 86 CB 9B 50 72 19 EE 95 DB 11 3A 91 76 78 B2
- Block3: 73 BE D6 B8 E3 C1 74 3B 71 16 E6 9E 22 22 95 16
- Block4: 3F F1 CA A1 68 1F AC 09 12 0E CA 30 75 86 E1 A7
Funkcja decrypt wygląda następująco:
- uint8_t test_aes_cbc_128_decrypt(void) {
- cmox_cipher_retval_t retVal;
- size_t outputLen;
- printf("AES CBC Decrypt\r\n");
- retVal = cmox_cipher_decrypt(
- CMOX_AES_CBC_DEC_ALGO,
- encryptedText,
- sizeof(encryptedText),
- key,
- sizeof(key),
- IV,
- sizeof(IV),
- decBuffer,
- &outputLen);
- //check the result
- if((retVal != CMOX_CIPHER_SUCCESS) ||
- (outputLen != sizeof(plainText)) ||
- (memcmp(plainText, decBuffer, outputLen) != 0) ){
- return 1;
- }
- for(size_t i = 0; i < outputLen; i++)
- {
- printf("%02X ", decBuffer[i]);
- }
- printf("\r\n");
- return 0;
- }
Wywołanie funkcji decrypt spowoduje wygenerowanie następujących danych:
- AES CBC Decrypt
- 6B C1 BE E2 2E 40 9F 96 E9 3D 7E 11 73 93 17 2A
- AE 2D 8A 57 1E 03 AC 9C 9E B7 6F AC 45 AF 8E 51
- 30 C8 1C 46 A3 5C E4 11 E5 FB C1 19 1A 0A 52 EF
- F6 9F 24 45 DF 4F 9B 17 AD 2B 41 7B E6 6C 37 10