W tym poście chciałbym opisać sposób obsługi szyfru blokowego Blowfish z wykorzystaniem biblioteki mbedtls.
Projekt był testowany na płytce Nucleo z układem STM32H723ZG. Natomiast działanie biblioteki będzie takie samo dla innych układów.
Poniższe funkcje wykorzystują tryb działania ECB (Electronic Code Block). Czyli każdy blok danych szyfrowany jest za pomocą jednego klucza. Dane wejściowe są zapisane w postaci bloku.
Funkcja kodująca dane z wykorzystaniem ECB:
- static void blowfish_encrypt_ecb(const unsigned char *key_buffer, unsigned char *src_buffer, unsigned char * des_buffer) {
- unsigned char output[8];
- int setkey_result = 0;
- mbedtls_blowfish_context ctx;
- for(uint8_t i=0; i<8; i++) { output[i] = 0x00; }
- mbedtls_blowfish_init(&ctx);
- setkey_result = mbedtls_blowfish_setkey(&ctx, key_buffer, 8*8);
- if(setkey_result == 0) {
- mbedtls_blowfish_crypt_ecb(&ctx, MBEDTLS_BLOWFISH_ENCRYPT, src_buffer, output);
- }
- for(uint8_t i=0; i<8; i++) { *(des_buffer + i) = output[i]; }
- mbedtls_blowfish_free(&ctx);
- }
Funkcja dekodująca dane z wykorzystaniem ECB:
- static void blowfish_decrypt_ecb(const unsigned char * key_buffer, unsigned char * src_buffer, unsigned char * des_buffer)
- {
- unsigned char output[8];
- int setkey_result = 0;
- mbedtls_blowfish_context ctx;
- for(uint8_t i=0; i<8; i++) { output[i] = 0x00; }
- mbedtls_blowfish_init(&ctx);
- setkey_result = mbedtls_blowfish_setkey(&ctx, key_buffer, 8*8);
- if(setkey_result == 0) {
- mbedtls_blowfish_crypt_ecb(&ctx, MBEDTLS_BLOWFISH_DECRYPT, src_buffer, output);
- }
- for(uint8_t i=0; i<8; i++) { *(des_buffer + i) = output[i]; }
- mbedtls_blowfish_free(&ctx);
- }
Funkcja mbedtls_blowfish_crypt_ecb przyjmuje następujące parametry:
- Wskaźnik na strukturę z danymi dla Blowfish. Klucz musi być do niej wprowadzony przed wywołaniem funkcji szyfrującej.
- Rodzaj operacji czyli szyfrowanie lub odszyfrowanie wiadomości.
- Wskaźnik na dane wejściowe o rozmiarze 8 bajtów.
- Wskaźnik na dane wyjściowe.
Funkcja wywołująca operacje kodowania/dekowania funkcji:
- uint8_t Test_Decrypt_Buffer[16] = {
- 0x9D, 0x67, 0xE8, 0x3B, 0x98, 0x25, 0x03, 0x7F, 0x2A, 0x7B, 0x68, 0x73, 0x23, 0x91, 0x65, 0xEF
- };
- uint8_t Test_Encrypt_Buffer[16] = {
- 0x7f, 0x4c, 0x63, 0x02, 0x64, 0x97, 0xdc, 0x55, 0xf0, 0x1b, 0x51, 0x32, 0xe3, 0xb5, 0x31, 0x9a
- };
- uint8_t Test_Password[8] = {
- 0x8F, 0x7E, 0x60, 0x82, 0x74, 0x95, 0x27, 0x96
- };
- static void blowfish_encrypt_ecb(const unsigned char *key_buffer, unsigned char *src_buffer, unsigned char * des_buffer);
- static void blowfish_decrypt_ecb(const unsigned char * key_buffer, unsigned char * src_buffer, unsigned char * des_buffer);
- void test_encrypt_data(void)
- {
- uint8_t output_encrypt_buffer[16] = {0x00};
- uint8_t output_decrypt_buffer[16] = {0x00};
- blowfish_encrypt_ecb(&Test_Password[0], &Test_Decrypt_Buffer[0], &output_encrypt_buffer[0]);
- blowfish_encrypt_ecb(&Test_Password[0], &Test_Decrypt_Buffer[8], &output_encrypt_buffer[8]);
- blowfish_decrypt_ecb(&Test_Password[0], &output_encrypt_buffer[0], &output_decrypt_buffer[0]);
- blowfish_decrypt_ecb(&Test_Password[0], &output_encrypt_buffer[8], &output_decrypt_buffer[8]);
- }
Drugi sposób to CBC gdzie bloki danych zależą od poprzednich bloków. Na końcu szyfrowanie następuje za pomocą zwykłego klucza.
- static uint8_t blowfish_encrypt_cbc(const unsigned char * key_buffer, unsigned char * iv_str,
- unsigned char * src_buffer, unsigned char * des_buffer, uint16_t src_buffer_size)
- {
- unsigned char output[32];
- int operation_status = 0;
- for(uint8_t i=0; i<32; i++) { output[i] = 0x00; }
- mbedtls_blowfish_context ctx;
- mbedtls_blowfish_init( &ctx );
- operation_status = mbedtls_blowfish_setkey(&ctx, key_buffer, 8*8);
- if(operation_status == 0) {
- operation_status = mbedtls_blowfish_crypt_cbc(&ctx, MBEDTLS_BLOWFISH_ENCRYPT, src_buffer_size , iv_str, src_buffer, output);
- }
- for(uint8_t i=0; i<32; i++) { *(des_buffer + i) = output[i]; }
- mbedtls_blowfish_free(&ctx);
- }
- static uint8_t blowfish_decrypt_cbc(const unsigned char * key_buffer, unsigned char * iv_buffer,
- unsigned char * src_buffer, unsigned char * des_buffer, uint16_t src_buffer_size)
- {
- unsigned char output[32];
- int operation_status = 0;
- for(uint8_t i=0; i<32; i++) { output[i] = 0x00; }
- mbedtls_blowfish_context ctx;
- mbedtls_blowfish_init( &ctx );
- operation_status = mbedtls_blowfish_setkey(&ctx, key_buffer, 8*8);
- if(operation_status == 0) {
- operation_status = mbedtls_blowfish_crypt_cbc(&ctx, MBEDTLS_BLOWFISH_DECRYPT, src_buffer_size , iv_buffer, src_buffer, output);
- }
- for(uint8_t i=0; i<32; i++) { *(des_buffer + i) = output[i]; }
- mbedtls_blowfish_free(&ctx);
- }
Funkcja mbedtls_blowfish_crypt_ecb przyjmuje następujące parametry:
- Wskaźnik na strukturę z danymi dla Blowfish. Klucz musi być do niej wprowadzony przed wywołaniem funkcji szyfrującej.
- Rodzaj operacji czyli szyfrowanie lub odszyfrowanie wiadomości.
- Ilość danych wejściowych. Rozmiar tablicy musi być wielokrotnością liczby 8.
- Tzw. Initialization Vector. (8 bajtów). Jest on aktualizowany w tej funkcji w celu użytku wykonywania wielu operacji na kilku blokach danych. W celu odszyfrowania danych potrzebny jest blok oryginalny. W związku z tym musi on zostać zapisany przed szyfrowaniem danych.
- Wskaźnik na dane wejściowe. Rozmiar tablicy musi być wielokrotnością liczby 8.
- Wskaźnik na dane wyjściowe.
Wywołanie funkcji wygląda następująco:
- uint8_t cbc_src_buffer[32] = {
- 0x9D, 0x67, 0xE8, 0x3B, 0x98, 0x25, 0x03, 0x7F, 0x2A, 0x7B, 0x68, 0x73, 0x23, 0x91, 0x65, 0xEF,
- 0x9D, 0x67, 0xE8, 0x3B, 0x56, 0x25, 0x03, 0x7F, 0x2A, 0xFE, 0x68, 0xCB, 0x23, 0x91, 0x65, 0xEF
- };
- uint8_t cbc_password[8] = {
- 0x8F, 0x7E, 0x60, 0x82, 0x74, 0x95, 0x27, 0x96
- };
- uint8_t iv_buffer[8] = {
- 0x55, 0x34, 0x87, 0x34, 0x98, 0x47, 0xAE, 0x8B
- };
- uint8_t iv_buffer_read[8] = {
- 0x55, 0x34, 0x87, 0x34, 0x98, 0x47, 0xAE, 0x8B
- };
- uint8_t cbc_encrypt_buffer[32] = { 0x00 };
- uint8_t cbc_decrypt_buffer[32] = { 0x00 };
- static uint8_t blowfish_encrypt_cbc(const unsigned char * key_buffer, unsigned char * iv_str,
- unsigned char * src_buffer, unsigned char * des_buffer, uint16_t src_buffer_size);
- static uint8_t blowfish_decrypt_cbc(const unsigned char * key_buffer, unsigned char * iv_buffer,
- unsigned char * src_buffer, unsigned char * des_buffer, uint16_t src_buffer_size);
- void test_encrypt_data(void)
- {
- blowfish_encrypt_cbc(&cbc_password[0], &iv_buffer[0], &cbc_src_buffer[0], &cbc_encrypt_buffer[0], 32);
- blowfish_decrypt_cbc(&cbc_password[0], &iv_buffer_read[0], &cbc_encrypt_buffer[0], &cbc_decrypt_buffer[0], 32);
- }
Porównanie danych wejściowych i wyjściowych można zrobić korzystając np. z takiego narzędzia online. Pozwoli to na zweryfikowanie czy operacje wykonywane w programie działają poprawnie.
Biblioteka mbedtls pozwala także na wywołanie innych trybów działania szyfrowania Blowfish np. cfb64.