Created
November 7, 2018 19:50
-
-
Save cnlohr/96128ef4126bcc878b1b5a7586c624ef to your computer and use it in GitHub Desktop.
Example of using hardware AES 256 Crypto in CBC mode on the ESP32 using ESP-IDF
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#include <string.h> | |
#include <stdio.h> | |
#include <hwcrypto/aes.h> | |
/* | |
For Encryption time: 1802.40us (9.09 MB/s) at 16kB blocks. | |
*/ | |
static inline int32_t _getCycleCount(void) { | |
int32_t ccount; | |
__asm__ __volatile__("rsr %0,ccount":"=a" (ccount)); | |
return ccount; | |
} | |
char plaintext[16384]; | |
char encrypted[16384]; | |
int encodetest() | |
{ | |
uint8_t key[32]; | |
uint8_t iv[16]; | |
//If you have cryptographically random data in the start of your payload, you do not need | |
//an IV. If you start a plaintext payload, you will need an IV. | |
memset( iv, 0, sizeof( iv ) ); | |
//Right now, I am using a key of all zeroes. This should change. You should fill the key | |
//out with actual data. | |
memset( key, 0, sizeof( key ) ); | |
memset( plaintext, 0, sizeof( plaintext ) ); | |
strcpy( plaintext, "Hello, world, how are you doing today?" ); | |
//Just FYI - you must be encrypting/decrypting data that is in BLOCKSIZE chunks!!! | |
esp_aes_context ctx; | |
esp_aes_init( &ctx ); | |
esp_aes_setkey( &ctx, key, 256 ); | |
int32_t start = _getCycleCount(); | |
esp_aes_crypt_cbc( &ctx, ESP_AES_ENCRYPT, sizeof(plaintext), iv, (uint8_t*)plaintext, (uint8_t*)encrypted ); | |
int32_t end = _getCycleCount(); | |
float enctime = (end-start)/240.0; | |
printf( "Encryption time: %.2fus (%f MB/s)\n", enctime, (sizeof(plaintext)*1.0)/enctime ); | |
//See encrypted payload, and wipe out plaintext. | |
memset( plaintext, 0, sizeof( plaintext ) ); | |
int i; | |
for( i = 0; i < 128; i++ ) | |
{ | |
printf( "%02x[%c]%c", encrypted[i], (encrypted[i]>31)?encrypted[i]:' ', ((i&0xf)!=0xf)?' ':'\n' ); | |
} | |
printf( "\n" ); | |
//Must reset IV. | |
//XXX TODO: Research further: I found out if you don't reset the IV, the first block will fail | |
//but subsequent blocks will pass. Is there some strange cryptoalgebra going on that permits this? | |
printf( "IV: %02x %02x\n", iv[0], iv[1] ); | |
memset( iv, 0, sizeof( iv ) ); | |
//Use the ESP32 to decrypt the CBC block. | |
esp_aes_crypt_cbc( &ctx, ESP_AES_DECRYPT, sizeof(encrypted), iv, (uint8_t*)encrypted, (uint8_t*)plaintext ); | |
//Verify output | |
for( i = 0; i < 128; i++ ) | |
{ | |
printf( "%02x[%c]%c", plaintext[i], (plaintext[i]>31)?plaintext[i]:' ', ((i&0xf)!=0xf)?' ':'\n' ); | |
} | |
printf( "\n" ); | |
esp_aes_free( &ctx ); | |
} |
Regarding your:
//XXX TODO: Research further: I found out if you don't reset the IV, the first block will fail
//but subsequent blocks will pass. Is there some strange cryptoalgebra going on that permits this?
The function esp_aes_crypt_cbc
updates the IV variable for further operation. It seems to be designed for continious operation in one mode only.
For use like in your script you need an exact copy of the iv for decryption and pass it there, so it get's updated independently. Then it works like a charm. =)
My changes so far:
// Replaced <hwcrypto/aes.h> as it moved
#include <aes/esp_aes.h>
uint8_t key[32] = {AES_KEY};
uint8_t encryptionIV[16] = {AES_IV};
uint8_t decryptionIV[16] = {AES_IV};
.
esp_aes_crypt_cbc( &ctx, ESP_AES_ENCRYPT, sizeof(plaintext), encryptionIV, (uint8_t*)plaintext, (uint8_t*)encrypted );
.
// Removed the reset of the IV as it is not necessary anymore
.
esp_aes_crypt_cbc( &ctx, ESP_AES_DECRYPT, sizeof(encrypted), decryptionIV, (uint8_t*)encrypted, (uint8_t*)plaintext );
Thank you for this, here is my final version:
#include <string.h>
#include <aes/esp_aes.h>
// AES-256 Key => replace with your own
#define AES_KEY 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, \
0x08, 0x09, 0x0A, 0x0B, 0x0C, 0x0D, 0x0E, 0x0F, \
0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17, \
0x18, 0x19, 0x1A, 0x1B, 0x1C, 0x1D, 0x1E, 0x1F
// IV => should be a real random value
#define AES_IV 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, \
0x08, 0x09, 0x0A, 0x0B, 0x0C, 0x0D, 0x0E, 0x0F
/*
For Encryption time: 1802.40us (9.09 MB/s) at 16kB blocks.
*/
static inline int32_t _getCycleCount(void) {
int32_t ccount;
__asm__ __volatile__("rsr %0,ccount":"=a" (ccount));
return ccount;
}
char plaintext[16];
char encrypted[16];
void encodetest()
{
uint8_t key[32] = {AES_KEY};
uint8_t encryptionIV[16] = {AES_IV};
uint8_t decryptionIV[16] = {AES_IV};
memset( plaintext, 0, sizeof( plaintext ) );
strcpy( plaintext, "Hello, ESP32!" );
esp_aes_context ctx;
esp_aes_init( &ctx );
esp_aes_setkey( &ctx, key, 256 );
int32_t start = _getCycleCount();
esp_aes_crypt_cbc( &ctx, ESP_AES_ENCRYPT, sizeof(plaintext), encryptionIV, (uint8_t*)plaintext, (uint8_t*)encrypted );
int32_t end = _getCycleCount();
float enctime = (end-start)/240.0;
xprintf( "Encryption time: %.2fus (%f MB/s)\n", enctime, (sizeof(plaintext)*1.0)/enctime );
//See encrypted payload, and wipe out plaintext.
memset( plaintext, 0, sizeof( plaintext ) );
int i;
for( i = 0; i < 16; i++ )
{
xprintf( "%02x[%c]%c", encrypted[i], (encrypted[i]>31)?encrypted[i]:' ', ((i&0xf)!=0xf)?' ':'\n' );
}
xprintf( "\n" );
//Use the ESP32 to decrypt the CBC block.
esp_aes_crypt_cbc( &ctx, ESP_AES_DECRYPT, sizeof(encrypted), decryptionIV, (uint8_t*)encrypted, (uint8_t*)plaintext );
//Verify output
for( i = 0; i < 16; i++ )
{
xprintf( "%02x[%c]%c", plaintext[i], (plaintext[i]>31)?plaintext[i]:' ', ((i&0xf)!=0xf)?' ':'\n' );
}
xprintf( "\n" );
esp_aes_free( &ctx );
}
void xprintf(const char *format, ...)
{
char buffer[256]; // or smaller or static &c.
va_list args;
va_start(args, format);
vsprintf(buffer, format, args);
va_end(args);
Serial.print(buffer);
}
void setup() {
Serial.begin(115200);
// wait for console to start
delay(1000);
encodetest();
}
void loop() {
}
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Note that the header file has moved and changed its name. It is now "aes/esp_aes.h"
One could also use "mbedtls/aes.h" though the names of the functions are a little different. This version of functions allows for a separate encrypt and decrypt key to be used in case you are mixing between these (so you don't have to reset the key each time) though the mbedtls code for AES is slightly larger code (~40 bytes more).