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 );
}
@skxo
Copy link

skxo commented Jan 26, 2019

Hi,

how can you compile it please? can you give me the procedure to make it run on esp32?
thanks man

@m0923678421
Copy link

Hi,

how can you compile it please? can you give me the procedure to make it run on esp32?
thanks man

#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;
Serial.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++ )
{
Serial.printf( "%02x[%c]%c", encrypted[i], (encrypted[i]>31)?encrypted[i]:' ', ((i&0xf)!=0xf)?' ':'\n' );
}
Serial.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?
Serial.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++ )
{
Serial.printf( "%02x[%c]%c", plaintext[i], (plaintext[i]>31)?plaintext[i]:' ', ((i&0xf)!=0xf)?' ':'\n' );
}
Serial.printf( "\n" );

esp_aes_free( &ctx );
}
void setup() {
// put your setup code here, to run once:
Serial.begin(115200);
encodetest();
}

void loop() {
// put your main code here, to run repeatedly:

}

@roysG
Copy link

roysG commented Aug 26, 2021

@m0923678421 , can you give example how to generate base64 hash?

@RichFalk
Copy link

RichFalk commented Oct 14, 2021

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

@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