Skip to content

Instantly share code, notes, and snippets.

@cnlohr
Created November 7, 2018 19:50
Show Gist options
  • Save cnlohr/96128ef4126bcc878b1b5a7586c624ef to your computer and use it in GitHub Desktop.
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
#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 );
}
@Ayokas
Copy link

Ayokas commented Oct 19, 2023

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 );

@regisgrison
Copy link

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