Introduction
In this tutorial, we will check how to decipher data with AES-128 in ECB mode, on the Arduino core running on the ESP32.
In this previous tutorial we have already checked how to cipher data with this algorithm, so now we will see how to decipher it. Note that most of the functions we will use here were already covered in the previous tutorial, so my recommendation is that you check it first.
We will use the mbed TLS libraries, which are available in the Arduino core and contain an implementation of the AES algorithm, amongst many other cryptographic functionalities.
The tests were performed using a DFRobot’s ESP32 module integrated in a ESP32 development board.
The code
In order to get access to the AES related functionality, we will first include the mbedtls/aes.h file.
#include "mbedtls/aes.h"
In order to keep things organized, we will declare a function to encrypt the data and another to decrypt it.
We will start by the encryption function, which we will call encrypt. This function will receive as input the plain text to encrypt, the encryption key and a byte buffer to store the output of the operation.
Remember from the previous post that AES operates on 16 bytes data blocks. In the case of the mbed TLS implementation, the encryption function supports only a single block of 16 bytes in ECB mode.
Since we are using AES-128, then the key needs to also have a length of 16 bytes. Finally, the output data buffer also needs to have a length of 16 bytes, so we need to make sure to pass to this function a buffer with enough size when calling it later.
Our function will return void since the actual output of the encryption will be copied to the buffer we pass to the function.
void encrypt(char * plainText, char * key, unsigned char * outputBuffer){ // Encryption code }
Now for the function implementation, the first thing we need to do is declaring a variable of type mbedtls_aes_context, which will hold the context of the algorithm during the procedure.
mbedtls_aes_context aes;
After declaring the context, we need to initialize it by calling the mbedtls_aes_init function and passing as input a pointer to the context.
mbedtls_aes_init(&aes);
Next we need to set the encryption key by calling the mbedtls_aes_setkey_enc function. As first argument, it receives a pointer to the AES context, as second the encryption key (remember that we receive it as parameter of our function) and finally the size of the key, in bits.
Note that the key needs a cast to const unsigned char *.
mbedtls_aes_setkey_enc( &aes, (const unsigned char*) key, strlen(key) * 8 );
Once the key is set, we apply the actual encryption by calling the mbedtls_aes_crypt_ecb function.
As before, it receives a pointer to the AES context as first argument. As second argument, it receives the constant MBEDTLS_AES_ENCRYPT, which indicates that we want to encrypt data.
As third argument, it receives the plain text to encrypt (also an input of our function) and finally, as fourth argument, it receives the output buffer, to which the result of the operation will be copied. Note that the plain text needs a cast to const unsigned char *.
mbedtls_aes_crypt_ecb( &aes, MBEDTLS_AES_ENCRYPT, (const unsigned char*)plainText, outputBuffer);
To finalize the implementation of the function, we free the AES context we used before by calling the mbedtls_aes_free and passing again as input a pointer to the context.
mbedtls_aes_free( &aes );
The final encrypt function can be seen below.
void encrypt(char * plainText, char * key, unsigned char * outputBuffer){ mbedtls_aes_context aes; mbedtls_aes_init( &aes ); mbedtls_aes_setkey_enc( &aes, (const unsigned char*) key, strlen(key) * 8 ); mbedtls_aes_crypt_ecb( &aes, MBEDTLS_AES_ENCRYPT, (const unsigned char*)plainText, outputBuffer); mbedtls_aes_free( &aes ); }
Now we need to define the function to decrypt the data. We will call it decrypt and both its signature and implementation will be very similar to the previous one.
So, as input, it will receive the cipher text to decrypt, the encryption key and an output buffer. As before, the function will return void since the result will be copied to the buffer supplied.
The cipher text will have 16 bytes, which was the length of the output of the encryption we have mentioned before. The key needs to be exactly the same for us to be able to recover the plain text, so it also has a length of 16 bytes bits.
Finally, the output needs to match the original plain text input, which also had a length of 16 bits.
void decrypt(unsigned char * chipherText, char * key, unsigned char * outputBuffer){ // Decryption code }
In terms of function calls, the API exposed by mbed TLS is similar for both encryption and decryption, and shares many of the function calls.
As before, we need to declare an AES context and initialize it.
mbedtls_aes_context aes; mbedtls_aes_init( &aes );
Then we need to set the decryption key. The function used basically receives the same inputs as when setting the encryption key, but is named mbedtls_aes_setkey_dec.
mbedtls_aes_setkey_enc( &aes, (const unsigned char*) key, strlen(key) * 8 );
Next, to perform the decryption, we call the mbedtls_aes_crypt_ecb again.
As before, it receives as first input the pointer to the AES context. As second, it now receives a different value, more precisely the MBEDTLS_AES_DECRYPT constant, which specifies the mode of operation as decryption.
As third argument, it receives the encrypted text to decipher and as fourth the output buffer, to which the deciphered text will be copied.
mbedtls_aes_crypt_ecb(&aes, MBEDTLS_AES_DECRYPT, (const unsigned char*)chipherText, outputBuffer);
Finally, as we did before, we need to free the AES context previously used.
mbedtls_aes_free( &aes );
The complete decrypt function code can be seen below.
void decrypt(unsigned char * chipherText, char * key, unsigned char * outputBuffer){ mbedtls_aes_context aes; mbedtls_aes_init( &aes ); mbedtls_aes_setkey_dec( &aes, (const unsigned char*) key, strlen(key) * 8 ); mbedtls_aes_crypt_ecb(&aes, MBEDTLS_AES_DECRYPT, (const unsigned char*)chipherText, outputBuffer); mbedtls_aes_free( &aes ); }
The final source code can be seen below.
#include "mbedtls/aes.h" void encrypt(char * plainText, char * key, unsigned char * outputBuffer){ mbedtls_aes_context aes; mbedtls_aes_init( &aes ); mbedtls_aes_setkey_enc( &aes, (const unsigned char*) key, strlen(key) * 8 ); mbedtls_aes_crypt_ecb( &aes, MBEDTLS_AES_ENCRYPT, (const unsigned char*)plainText, outputBuffer); mbedtls_aes_free( &aes ); } void decrypt(unsigned char * chipherText, char * key, unsigned char * outputBuffer){ mbedtls_aes_context aes; mbedtls_aes_init( &aes ); mbedtls_aes_setkey_dec( &aes, (const unsigned char*) key, strlen(key) * 8 ); mbedtls_aes_crypt_ecb(&aes, MBEDTLS_AES_DECRYPT, (const unsigned char*)chipherText, outputBuffer); mbedtls_aes_free( &aes ); } void setup() { Serial.begin(115200); char * key = "abcdefghijklmnop"; char *plainText = "Tech tutorials x"; unsigned char cipherTextOutput[16]; unsigned char decipheredTextOutput[16]; encrypt(plainText, key, cipherTextOutput); decrypt(cipherTextOutput, key, decipheredTextOutput); Serial.println("\nOriginal plain text:"); Serial.println(plainText); Serial.println("\nCiphered text:"); for (int i = 0; i < 16; i++) { char str[3]; sprintf(str, "%02x", (int)cipherTextOutput[i]); Serial.print(str); } Serial.println("\n\nDeciphered text:"); for (int i = 0; i < 16; i++) { Serial.print((char)decipheredTextOutput[i]); } } void loop() {}
Testing the code
You should get an output similar to figure 1.
Just finished figuring out for myself the AES MBEDTLS library, this was about a week late for me…. but its very nice tutorial, so thanks!
RichardS
i want this library please
I really wish you’d used some other mode like CBC which is actually secure; I hate to think of someone following this tutorial and then trying to use the code for a real-world applications! We have enough broken IoT security as it is. ECB may be the simplest mode but is notoriously weak:
“Because ECB encrypts identical plaintext blocks into identical ciphertext blocks, it does not hide data patterns well. In some senses, it doesn’t provide serious message confidentiality, and it is not recommended for use in cryptographic protocols at all.”
—Wikipedia
https://en.wikipedia.org/wiki/Block_cipher_mode_of_operation#Electronic_Codebook_(ECB)
The example following that quote is a pretty graphic example of ECB’s weakness.
Thanks for the heads up, guess it all comes down to how secure do you need it? 10 devices on the net or 1,000,000.. 🙂
mbedtls/aes.h: No such file or directory
please help me out
please tell me the path of this library file
I’m using Arduino 1.8.12
i have a same probleme ……..
Clear and helpful, well explained:)