Last active
February 20, 2025 20:16
-
-
Save Sv443/d591abc5538b9b31b96194add0c9fd06 to your computer and use it in GitHub Desktop.
ESP32 Arduino - secure AES-GCM key and IV generation and storage example
This file contains hidden or 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 <Preferences.h> | |
#include <esp_random.h> | |
#include <mbedtls/sha256.h> | |
#ifndef String | |
#include <Arduino.h> | |
#endif | |
// set to true to clear the NVS before generating and writing a new key and IV: | |
#define CLEAR_ALL false | |
#define AES_KEY_SIZE 16 // key size in bytes (16 for a 128-bit AES key) | |
#define IV_SIZE 12 // recommended IV size for AES-GCM is 12 | |
Preferences prefs; | |
// Store AES Key in Encrypted NVS | |
void storeAESKey(const uint8_t key[AES_KEY_SIZE]) { | |
prefs.begin("secure", false); | |
prefs.putBytes("aes_key", key, AES_KEY_SIZE); | |
prefs.end(); | |
Serial.println("AES key stored securely in NVS."); | |
} | |
// Load AES Key from Encrypted NVS | |
bool loadAESKey(uint8_t key[AES_KEY_SIZE]) { | |
prefs.begin("secure", false); | |
size_t keySize = prefs.getBytes("aes_key", key, AES_KEY_SIZE); | |
prefs.end(); | |
if(keySize == AES_KEY_SIZE) { | |
Serial.println("AES key loaded successfully."); | |
return true; | |
} else { | |
Serial.println("AES key not found in NVS."); | |
return false; | |
} | |
} | |
// Generate a Secure Random AES Key | |
void generateRandomAESKey(uint8_t key[AES_KEY_SIZE]) { | |
for(int i = 0; i < AES_KEY_SIZE; i++) | |
key[i] = esp_random() & 0xFF; | |
} | |
// Store Counter in NVS for IV Generation | |
void storeIVCounter(uint32_t counter) { | |
prefs.begin("secure", false); | |
prefs.putUInt("iv_counter", counter); | |
prefs.end(); | |
} | |
// Load Counter from NVS (or initialize to 0 if not found) | |
uint32_t loadIVCounter() { | |
prefs.begin("secure", false); | |
uint32_t counter = prefs.getUInt("iv_counter", 0); // Default to 0 if missing | |
prefs.end(); | |
return counter; | |
} | |
// Hash the IV using SHA-256 to eliminate leading zeros | |
void hashIV(uint8_t iv[IV_SIZE]) { | |
uint8_t hash[32]; // Full SHA-256 hash output (32 bytes) | |
// Compute SHA-256 hash of the IV | |
mbedtls_sha256_context ctx; | |
mbedtls_sha256_init(&ctx); | |
mbedtls_sha256_starts(&ctx, 0); // 0 = SHA-256 (not SHA-224) | |
mbedtls_sha256_update(&ctx, iv, IV_SIZE); | |
mbedtls_sha256_finish(&ctx, hash); | |
mbedtls_sha256_free(&ctx); | |
// Use the first 12 bytes of the hash as the new IV | |
memcpy(iv, hash, IV_SIZE); | |
} | |
// Generate a Secure IV (Counter + Random Component) | |
void generateIV(uint8_t iv[IV_SIZE]) { | |
// Load counter from NVS | |
uint32_t counter = loadIVCounter(); | |
// Convert counter to bytes (Big-Endian format) | |
iv[0] = (counter >> 24) & 0xFF; | |
iv[1] = (counter >> 16) & 0xFF; | |
iv[2] = (counter >> 8) & 0xFF; | |
iv[3] = counter & 0xFF; | |
// Generate remaining 8 random bytes | |
for(int i = 4; i < IV_SIZE; i++) | |
iv[i] = esp_random() & 0xFF; | |
// Increment and store the updated counter | |
storeIVCounter(counter + 1); | |
} | |
void setup() { | |
Serial.begin(115200); | |
delay(1000); | |
Serial.println("\n"); | |
uint32_t u1 = micros(); | |
#if CLEAR_ALL | |
// Delete AES Key & Counter from NVS (for debugging) | |
prefs.begin("secure", false); | |
prefs.remove("aes_key"); | |
prefs.remove("iv_counter"); | |
prefs.end(); | |
Serial.println("Cleared aes_key & iv_counter preferences\n\n"); | |
#endif | |
uint8_t aesKey[AES_KEY_SIZE]; | |
uint32_t u2_1 = 0; | |
uint32_t u2_2 = 0; | |
// Try to load an existing key | |
if(!loadAESKey(aesKey)) { | |
u2_1 = micros(); | |
Serial.println("Generating a new AES key..."); | |
generateRandomAESKey(aesKey); | |
u2_2 = micros(); | |
storeAESKey(aesKey); | |
} | |
uint32_t u2 = micros(); | |
// Generate IV | |
uint8_t iv[IV_SIZE]; | |
generateIV(iv); | |
uint32_t u3 = micros(); | |
// Hash the IV | |
hashIV(iv); | |
uint32_t u4 = micros(); | |
// Print AES Key (DO NOT PRINT IN PRODUCTION!) | |
Serial.print("AES Key: "); | |
for(int i = 0; i < AES_KEY_SIZE; i++) | |
Serial.printf("%02X ", aesKey[i]); | |
Serial.println(); | |
// Print IV | |
Serial.print("Generated IV: "); | |
for(int i = 0; i < IV_SIZE; i++) | |
Serial.printf("%02X ", iv[i]); | |
Serial.println(); | |
uint32_t u5 = micros(); | |
Serial.println("\nTimings (ms):"); | |
Serial.println(" AES Key:"); | |
if(u2_1 > 0) { | |
Serial.print(" - READ: "); | |
Serial.println(String(1.0 * (u2_1 - u1) / 1000)); | |
Serial.print(" - GENER8: "); | |
Serial.println(String(1.0 * (u2_2 - u2_1) / 1000)); | |
Serial.print(" - WRITE: "); | |
Serial.println(String(1.0 * (u2 - u2_2) / 1000)); | |
} | |
else { | |
Serial.print(" - READ: "); | |
Serial.println(String(1.0 * (u2 - u1) / 1000)); | |
} | |
Serial.println(" IV:"); | |
Serial.print(" - GENER8: "); | |
Serial.println(String(1.0 * (u3 - u2) / 1000)); | |
Serial.print(" - HASH: "); | |
Serial.println(String(1.0 * (u4 - u3) / 1000)); | |
Serial.println(" Print Key:"); | |
Serial.print(" - PRINT: "); | |
Serial.println(String(1.0 * (u5 - u4) / 1000)); | |
Serial.println(" Total: "); | |
Serial.print(" - TOTAL: "); | |
Serial.println(String(1.0 * (u5 - u1) / 1000)); | |
Serial.print(" - NOPRNT: "); | |
Serial.println(String(1.0 * ((u5 - u1) - (u5 - u4)) / 1000)); | |
} | |
void loop() {} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment