From cf08819d8e1222690ca7ca16cf5d473dc8ed3177 Mon Sep 17 00:00:00 2001 From: David Garske Date: Tue, 30 Dec 2025 16:47:02 -0800 Subject: [PATCH] Support for CUSTOM_ENCRYPT_KEY that allows customer to supply their own implementation --- docs/Targets.md | 15 ++-- docs/encrypted_partitions.md | 49 ++++++++++++ hal/mpfs250.c | 4 +- options.mk | 3 + src/libwolfboot.c | 66 ++++++++++------ src/update_disk.c | 145 +++++++++++++++++++++++++++++------ tools/keytools/sign.c | 3 + 7 files changed, 229 insertions(+), 56 deletions(-) diff --git a/docs/Targets.md b/docs/Targets.md index e3c5a70c..0149a14b 100644 --- a/docs/Targets.md +++ b/docs/Targets.md @@ -1047,23 +1047,20 @@ int wolfBoot_get_encrypt_key(uint8_t *key, uint8_t *nonce); int wolfBoot_erase_encrypt_key(void); /* called automatically by wolfBoot_success() */ ``` +To use your own implementation for getting the encryption key use `CUSTOM_ENCRYPT_KEY` and `OBJS_EXTRA=src/my_custom_encrypt_key.o`. Then provide your own implementation of `int wolfBoot_get_encrypt_key(uint8_t *key, uint8_t *nonce);` + To sign and encrypt an image, create a key file with the concatenated key and nonce, then use the sign tool: ```sh -# Create key file (32-byte key + 16-byte IV for AES-256) -echo -n "0123456789abcdef0123456789abcdef0123456789abcdef" > enc_key.der +# Create key file (32-byte key + 16-byte nonce for AES-256) +printf "0123456789abcdef0123456789abcdef0123456789abcdef" > /tmp/enc_key.der # Sign and encrypt -./tools/keytools/sign --ecc384 --sha384 --aes256 --encrypt enc_key.der \ +./tools/keytools/sign --ecc384 --sha384 --aes256 --encrypt /tmp/enc_key.der \ fitImage wolfboot_signing_private_key.der 1 ``` -In your application, set the encryption key before triggering an update: - -```c -wolfBoot_set_encrypt_key(enc_key, enc_iv); -wolfBoot_update_trigger(); -``` +The result is `fitImage_v1_signed_and_encrypted.bin`, which gets placed into your OFP_A or OFP_B partitions. During boot, wolfBoot decrypts the image headers from disk to select the best candidate, loads and decrypts the full image to RAM, then verifies integrity and authenticity before booting. On successful boot, `wolfBoot_success()` clears the key from RAM. diff --git a/docs/encrypted_partitions.md b/docs/encrypted_partitions.md index 5ac13d20..c977a95f 100644 --- a/docs/encrypted_partitions.md +++ b/docs/encrypted_partitions.md @@ -36,6 +36,55 @@ wolfBoot upon next boot. Aside from setting the temporary key, the update mechanism remains the same for distributing, uploading and installing firmware updates through wolfBoot. +### Custom encryption key storage + +You can use the `CUSTOM_ENCRYPT_KEY` option to implement your own functions for: +`wolfBoot_get_encrypt_key`, `wolfBoot_set_encrypt_key` and +`wolfBoot_erase_encrypt_key`. + +To enable: + +1) Add `CUSTOM_ENCRYPT_KEY=1` to your `.config` +2) Add your own .c file using `OBJS_EXTRA`. For example, for your own + `src/custom_encrypt_key.c` add this to your `.config`: + `OBJS_EXTRA=src/custom_encrypt_key.o` + +Your custom implementation must provide these functions: + +```c +int wolfBoot_set_encrypt_key(const uint8_t *key, const uint8_t *nonce); +int wolfBoot_get_encrypt_key(uint8_t *key, uint8_t *nonce); +int wolfBoot_erase_encrypt_key(void); +``` + +Example custom function for testing: + +```c +#include "wolfboot/wolfboot.h" +#include "image.h" + +int RAMFUNCTION wolfBoot_get_encrypt_key(uint8_t *key, uint8_t *nonce) +{ + int i; + /* Test key: "0123456789abcdef0123456789abcdef" (32 bytes for AES-256) */ + const char test_key[] = "0123456789abcdef0123456789abcdef"; + /* Test nonce: "0123456789abcdef" (16 bytes) */ + const char test_nonce[] = "0123456789abcdef"; + + for (i = 0; i < ENCRYPT_KEY_SIZE && i < (int)sizeof(test_key); i++) { + key[i] = (uint8_t)test_key[i]; + } + for (i = 0; i < ENCRYPT_NONCE_SIZE && i < (int)sizeof(test_nonce); i++) { + nonce[i] = (uint8_t)test_nonce[i]; + } + return 0; +} +``` + +Note: On platforms that use the src/update_disk.c loader it only reads from a +GPT partition and with ENCRYPT=1 it only needs `wolfBoot_get_encrypt_key` implemented. + + ### Libwolfboot API The API to communicate with the bootloader from the application is expanded when this feature is enabled, diff --git a/hal/mpfs250.c b/hal/mpfs250.c index 4a264a8c..f81cb28c 100644 --- a/hal/mpfs250.c +++ b/hal/mpfs250.c @@ -1605,7 +1605,7 @@ int disk_read(int drv, uint64_t start, uint32_t count, uint8_t *buf) uint32_t start_offset = (start % EMMC_SD_BLOCK_SIZE); (void)drv; /* only one drive supported */ -#if 1 //def DEBUG_MMC +#ifdef DEBUG_MMC wolfBoot_printf("disk_read: drv:%d, start:%llu, count:%d, dst:%p\n", drv, start, count, buf); #endif @@ -1657,7 +1657,7 @@ int disk_write(int drv, uint64_t start, uint32_t count, const uint8_t *buf) uint32_t start_offset = (start % EMMC_SD_BLOCK_SIZE); (void)drv; /* only one drive supported */ -#if 1 //def DEBUG_MMC +#ifdef DEBUG_MMC wolfBoot_printf("disk_write: drv:%d, start:%llu, count:%d, src:%p\n", drv, start, count, buf); #endif diff --git a/options.mk b/options.mk index 6af4fa66..9cd1b84a 100644 --- a/options.mk +++ b/options.mk @@ -563,6 +563,9 @@ ifeq ($(ENCRYPT),1) endif endif endif + ifeq ($(CUSTOM_ENCRYPT_KEY),1) + CFLAGS+=-D"CUSTOM_ENCRYPT_KEY" + endif endif ifeq ($(EXT_FLASH),1) diff --git a/src/libwolfboot.c b/src/libwolfboot.c index d3808486..6f573d82 100644 --- a/src/libwolfboot.c +++ b/src/libwolfboot.c @@ -1491,6 +1491,7 @@ static int RAMFUNCTION hal_set_key(const uint8_t *k, const uint8_t *nonce) return ret; #endif } +#ifndef CUSTOM_ENCRYPT_KEY /** * @brief Set the encryption key. * @@ -1545,7 +1546,8 @@ int RAMFUNCTION wolfBoot_get_encrypt_key(uint8_t *k, uint8_t *nonce) #endif return 0; } -#endif +#endif /* UNIT_TEST */ + /** * @brief Erase the encryption key. * @@ -1575,6 +1577,7 @@ int RAMFUNCTION wolfBoot_erase_encrypt_key(void) #endif return 0; } +#endif /* !CUSTOM_ENCRYPT_KEY */ #if defined(__WOLFBOOT) || defined(UNIT_TEST) @@ -1585,20 +1588,31 @@ ChaCha chacha; int RAMFUNCTION chacha_init(void) { -#if defined(MMU) || defined(UNIT_TEST) - const uint8_t *key = ENCRYPT_KEY; +#ifdef CUSTOM_ENCRYPT_KEY + uint8_t stored_nonce[ENCRYPT_NONCE_SIZE]; + uint8_t key[ENCRYPT_KEY_SIZE]; #else - const uint8_t *key = (uint8_t *)(WOLFBOOT_PARTITION_BOOT_ADDRESS + - ENCRYPT_TMP_SECRET_OFFSET); + const uint8_t* stored_nonce; + uint8_t *key; #endif uint8_t ff[ENCRYPT_KEY_SIZE]; - const uint8_t* stored_nonce; - -#ifdef NVM_FLASH_WRITEONCE - key -= WOLFBOOT_SECTOR_SIZE * nvm_select_fresh_sector(PART_BOOT); -#endif +#ifdef CUSTOM_ENCRYPT_KEY + int ret = wolfBoot_get_encrypt_key(key, stored_nonce); + if (ret != 0) + return ret; +#else + #if defined(MMU) || defined(UNIT_TEST) + key = ENCRYPT_KEY; + #else + key = (uint8_t *)(WOLFBOOT_PARTITION_BOOT_ADDRESS + + ENCRYPT_TMP_SECRET_OFFSET); + #endif + #ifdef NVM_FLASH_WRITEONCE + key -= WOLFBOOT_SECTOR_SIZE * nvm_select_fresh_sector(PART_BOOT); + #endif stored_nonce = key + ENCRYPT_KEY_SIZE; +#endif XMEMSET(&chacha, 0, sizeof(chacha)); @@ -1632,9 +1646,14 @@ Aes aes_dec, aes_enc; */ int aes_init(void) { - int devId; + int devId = INVALID_DEVID; +#if defined(CUSTOM_ENCRYPT_KEY) && !defined(WOLFBOOT_RENESAS_TSIP) + uint8_t stored_nonce[ENCRYPT_NONCE_SIZE]; + uint8_t key[ENCRYPT_KEY_SIZE]; +#else uint8_t *stored_nonce; uint8_t *key; +#endif uint8_t ff[ENCRYPT_KEY_SIZE]; #ifdef WOLFBOOT_RENESAS_TSIP @@ -1645,19 +1664,20 @@ int aes_init(void) key = enc_key->encrypted_user_key; stored_nonce = enc_key->initial_vector; wolfCrypt_Init(); /* required to setup the crypto callback defaults */ -#else /* non TSIP */ - devId = INVALID_DEVID; -#if defined(MMU) || defined(UNIT_TEST) - key = ENCRYPT_KEY; +#elif defined(CUSTOM_ENCRYPT_KEY) + wolfBoot_get_encrypt_key(key, stored_nonce); #else - key = (uint8_t*)(WOLFBOOT_PARTITION_BOOT_ADDRESS + - ENCRYPT_TMP_SECRET_OFFSET); -#endif -#ifdef NVM_FLASH_WRITEONCE - key -= WOLFBOOT_SECTOR_SIZE * nvm_select_fresh_sector(PART_BOOT); -#endif - stored_nonce = key + ENCRYPT_KEY_SIZE; -#endif /* WOLFBOOT_RENESAS_TSIP */ + #if defined(MMU) || defined(UNIT_TEST) + key = ENCRYPT_KEY; + #else + key = (uint8_t*)(WOLFBOOT_PARTITION_BOOT_ADDRESS + + ENCRYPT_TMP_SECRET_OFFSET); + #endif + #ifdef NVM_FLASH_WRITEONCE + key -= WOLFBOOT_SECTOR_SIZE * nvm_select_fresh_sector(PART_BOOT); + #endif + stored_nonce = key + ENCRYPT_KEY_SIZE; +#endif /* non TSIP */ XMEMSET(&aes_enc, 0, sizeof(aes_enc)); XMEMSET(&aes_dec, 0, sizeof(aes_dec)); diff --git a/src/update_disk.c b/src/update_disk.c index dbc83235..614eedf0 100644 --- a/src/update_disk.c +++ b/src/update_disk.c @@ -52,6 +52,9 @@ defined(ENCRYPT_WITH_CHACHA) #define DISK_ENCRYPT #include "encrypt.h" + +/* Module-level storage for encryption nonce */ +static uint8_t disk_encrypt_nonce[ENCRYPT_NONCE_SIZE]; #endif #include @@ -91,6 +94,92 @@ #endif #ifdef DISK_ENCRYPT + +/* Module-level storage for encryption key */ +static uint8_t disk_encrypt_key[ENCRYPT_KEY_SIZE]; + +/** + * @brief Get the version from an already-decrypted header. + * + * This function extracts the version from a decrypted header blob + * without calling wolfBoot_get_blob_version, which might try to + * decrypt again if EXT_ENCRYPTED && MMU is defined. + * + * @param hdr Pointer to the decrypted header. + * + * @return The version number, or 0 if not found. + */ +static uint32_t get_decrypted_blob_version(uint8_t *hdr) +{ + uint32_t *magic = (uint32_t *)hdr; + uint16_t tlv_type, tlv_len; + uint8_t *p = hdr + IMAGE_HEADER_OFFSET; + uint8_t *max_p = hdr + IMAGE_HEADER_SIZE; + + if (*magic != WOLFBOOT_MAGIC) + return 0; + + /* Search for version TLV */ + while (p + 4 < max_p) { + tlv_type = *((uint16_t*)p); + tlv_len = *((uint16_t*)(p + 2)); + + if (tlv_type == 0 || tlv_type == 0xFFFF) + break; + + /* Skip padding bytes */ + if ((p[0] == 0xFF) || ((((uintptr_t)p) & 0x01) != 0)) { + p++; + continue; + } + + if (tlv_type == HDR_VERSION && tlv_len == 4) { + uint32_t ver = *((uint32_t*)(p + 4)); + return ver; + } + + p += 4 + tlv_len; + } + return 0; +} + +/** + * @brief Set up decryption context with IV at specified block offset. + * + * This function sets up the AES/ChaCha context with the IV positioned + * at the specified block offset. It matches how sign.c sets up encryption. + * + * @param block_offset Block offset for IV counter (0 = start of image). + */ +static void disk_crypto_set_iv(uint32_t block_offset) +{ +#if defined(ENCRYPT_WITH_CHACHA) + wc_Chacha_SetIV(&chacha, disk_encrypt_nonce, block_offset); +#elif defined(ENCRYPT_WITH_AES128) || defined(ENCRYPT_WITH_AES256) + /* For AES CTR, we need to construct the IV with the counter. + * The sign tool uses the IV directly without byte-reversal, + * so we must match that behavior here. */ + uint8_t iv[ENCRYPT_BLOCK_SIZE]; + uint32_t ctr; + + /* Copy nonce/IV (first 12 bytes for CTR nonce, last 4 for counter) */ + memcpy(iv, disk_encrypt_nonce, ENCRYPT_NONCE_SIZE); + + /* Add block offset to the counter portion (last 4 bytes, big-endian) */ + /* The IV from sign.c is already in the correct format, we just need + * to add the block offset to the counter portion */ + ctr = ((uint32_t)iv[12] << 24) | ((uint32_t)iv[13] << 16) | + ((uint32_t)iv[14] << 8) | (uint32_t)iv[15]; + ctr += block_offset; + iv[12] = (uint8_t)(ctr >> 24); + iv[13] = (uint8_t)(ctr >> 16); + iv[14] = (uint8_t)(ctr >> 8); + iv[15] = (uint8_t)(ctr); + + wc_AesSetIV(&aes_dec, iv); +#endif +} + /** * @brief Decrypt an image header in RAM. * @@ -104,13 +193,14 @@ */ static int decrypt_header(const uint8_t *src, uint8_t *dst) { - uint32_t i; uint32_t magic; - for (i = 0; i < IMAGE_HEADER_SIZE; i += ENCRYPT_BLOCK_SIZE) { - wolfBoot_crypto_set_iv(NULL, i / ENCRYPT_BLOCK_SIZE); - crypto_decrypt(dst + i, src + i, ENCRYPT_BLOCK_SIZE); - } + /* Reset IV to start of image (block 0) */ + disk_crypto_set_iv(0); + + /* Decrypt header - CTR mode handles counter increment internally */ + crypto_decrypt(dst, src, IMAGE_HEADER_SIZE); + magic = *((uint32_t*)dst); if (magic != WOLFBOOT_MAGIC) return -1; @@ -130,22 +220,12 @@ static int decrypt_header(const uint8_t *src, uint8_t *dst) */ static int decrypt_image(uint8_t *data, uint32_t size) { - uint32_t iv_counter = 0; - uint32_t offset = 0; - uint8_t dec_block[ENCRYPT_BLOCK_SIZE]; + /* Reset IV to start of image (block 0) */ + disk_crypto_set_iv(0); - while (offset < size) { - uint32_t chunk = size - offset; - if (chunk > ENCRYPT_BLOCK_SIZE) - chunk = ENCRYPT_BLOCK_SIZE; + /* Decrypt entire image - CTR mode handles counter increment internally */ + crypto_decrypt(data, data, size); - wolfBoot_crypto_set_iv(NULL, iv_counter); - crypto_decrypt(dec_block, data + offset, chunk); - memcpy(data + offset, dec_block, chunk); - - offset += ENCRYPT_BLOCK_SIZE; - iv_counter++; - } return 0; } #endif /* DISK_ENCRYPT */ @@ -189,11 +269,17 @@ void RAMFUNCTION wolfBoot_start(void) uint64_t start_us, elapsed_ms; #ifdef DISK_ENCRYPT - /* Initialize encryption */ + /* Initialize encryption - this sets up the cipher with key from storage */ if (wolfBoot_initialize_encryption() != 0) { wolfBoot_printf("Error initializing encryption\r\n"); wolfBoot_panic(); } + /* Retrieve encryption key and nonce for disk decryption */ + if (wolfBoot_get_encrypt_key(disk_encrypt_key, disk_encrypt_nonce) != 0) { + wolfBoot_printf("Error getting encryption key\r\n"); + wolfBoot_panic(); + } + wolfBoot_printf("Disk encryption enabled\r\n"); #endif ret = disk_init(BOOT_DISK); @@ -212,7 +298,9 @@ void RAMFUNCTION wolfBoot_start(void) == IMAGE_HEADER_SIZE) { #ifdef DISK_ENCRYPT if (decrypt_header(p_hdr, dec_hdr) == 0) { - pA_ver = wolfBoot_get_blob_version(dec_hdr); + /* Use local version parser to avoid double-decryption issue + * when EXT_ENCRYPTED && MMU is also defined */ + pA_ver = get_decrypted_blob_version(dec_hdr); } #else pA_ver = wolfBoot_get_blob_version((uint8_t*)p_hdr); @@ -225,7 +313,9 @@ void RAMFUNCTION wolfBoot_start(void) == IMAGE_HEADER_SIZE) { #ifdef DISK_ENCRYPT if (decrypt_header(p_hdr, dec_hdr) == 0) { - pB_ver = wolfBoot_get_blob_version(dec_hdr); + /* Use local version parser to avoid double-decryption issue + * when EXT_ENCRYPTED && MMU is also defined */ + pB_ver = get_decrypted_blob_version(dec_hdr); } #else pB_ver = wolfBoot_get_blob_version((uint8_t*)p_hdr); @@ -275,8 +365,19 @@ void RAMFUNCTION wolfBoot_start(void) continue; } +#ifdef DISK_ENCRYPT + /* Decrypt header to parse image size */ + if (decrypt_header(p_hdr, dec_hdr) != 0) { + wolfBoot_printf("Error decrypting header for %s\r\n", part_name); + selected ^= 1; + continue; + } + memset(&os_image, 0, sizeof(os_image)); + ret = wolfBoot_open_image_address(&os_image, (void*)dec_hdr); +#else memset(&os_image, 0, sizeof(os_image)); ret = wolfBoot_open_image_address(&os_image, (void*)p_hdr); +#endif if (ret < 0) { wolfBoot_printf("Error parsing loaded image\r\n"); selected ^= 1; diff --git a/tools/keytools/sign.c b/tools/keytools/sign.c index 712b1c8b..b888fc59 100644 --- a/tools/keytools/sign.c +++ b/tools/keytools/sign.c @@ -1808,6 +1808,8 @@ static int make_header_ex(int is_diff, uint8_t *pubkey, uint32_t pubkey_sz, fsize = ftell(f); fseek(f, 0, SEEK_SET); /* restart the _signed file from 0 */ + printf("Encrypting %u bytes...\n", fsize); + if (CMD.encrypt == ENC_CHACHA) { ChaCha cha; #ifndef HAVE_CHACHA @@ -1845,6 +1847,7 @@ static int make_header_ex(int is_diff, uint8_t *pubkey, uint32_t pubkey_sz, } } fclose(fef); + printf("Encryption complete.\n"); } printf("Output image(s) successfully created.\n"); ret = 0;