diff --git a/docs/encrypted_partitions.md b/docs/encrypted_partitions.md index 8b4aa2e2..0223623d 100644 --- a/docs/encrypted_partitions.md +++ b/docs/encrypted_partitions.md @@ -3,7 +3,7 @@ wolfBoot offers the possibility to encrypt the content of the entire UPDATE partition, by using a pre-shared symmetric key which can be temporarily stored in a safer non-volatile memory area. -SWAP partition is also temporarily encrypted using the same key, so a dump of the external flash won't reveal +SWAP partition is also temporarily encrypted using the same key, so a dump of the external flash won't reveal any content of the firmware update packages. ### Rationale @@ -30,7 +30,7 @@ Alternatively, more secure mechanisms are available to store the temporary key i The temporary key can be set at run time by the application, and will be used exactly once by the bootloader to verify and install the next update. The key can be for example received from a back-end during the update -process using secure communication, and set by the application, using `libwolfboot` API, to be used by +process using secure communication, and set by the application, using `libwolfboot` API, to be used by wolfBoot upon next boot. Aside from setting the temporary key, the update mechanism remains the same for distrubuting, uploading and @@ -44,7 +44,7 @@ to allow setting a temporary key to process the next update. The functions ``` -int wolfBoot_set_encrypt_key(const uint8_t *key, int len); +int wolfBoot_set_encrypt_key(const uint8_t *key, const uint8_t *nonce); int wolfBoot_erase_encrypt_key(void); ``` @@ -58,6 +58,52 @@ reboot. ### Symmetric encryption algorithm The algorithm currently used to encrypt and decrypt data in external partitions -is Chacha20-256. The expected key to provide to `wolfBoot_set_encrypt_key()` must be exactly 32 Bytes long. +is Chacha20-256. + + - The `key` provided to `wolfBoot_set_encrypt_key()` must be exactly 32 Bytes long. + - The `nonce` argument must be a 96-bit (12 Bytes) randomly generated buffer, to be used as IV for encryption and decription. + +## Example usage + +### Signing and encrypting the update bundle + +The `sign.py` tool can sign and encrypt the image with a single command. +The encryption secret is provided in a binary file that should contain a concatenation of +a 32B ChaCha-256 key and a 12B nonce. + +In the examples provided, the test application uses the following parameters: + +``` +key = "0123456789abcdef0123456789abcdef" +nonce = "0123456789ab" +``` + +So it is easy to prepare the encryption secret in the test scripts or from the command line using: + +``` +echo -n "0123456789abcdef0123456789abcdef0123456789ab" > enc_key.der +``` + +The `sign.py` script can now be invoked to produce a signed+encrypted image, by using the extra argument `--encrypt` followed by the +secret file: + +``` +./tools/keytools/sign.py --encrypt enc_key.der test-app/image.bin ecc256.der 24 + +``` + +which will produce as output the file `test-app/image_v24_signed_and_encrypted.bin`, that can be transferred to the target's external device. + + +### API usage in the application + +When transferring the image, the application can still use the libwolfboot API functions to store the encrypted firmware. When called from the application, +the function `ext_flash_write` will store the payload unencrypted. + +In order to trigger an update, before calling `wolfBoot_update_trigger` it is necessary to set the temporary key used by the bootloader by calling `wolfBoot_set_encrypt_key`. + +An example of encrypted update trigger can be found in the [stm32wb test application source code](test-app/app_stm32wb.c). + + diff --git a/include/wolfboot/wolfboot.h b/include/wolfboot/wolfboot.h index e8db4167..7dbe0957 100644 --- a/include/wolfboot/wolfboot.h +++ b/include/wolfboot/wolfboot.h @@ -120,8 +120,9 @@ int wolfBoot_dualboot_candidate(void); /* Encryption support */ #define ENCRYPT_BLOCK_SIZE 16 -#define ENCRYPT_KEY_SIZE 32 /* Chacha20-256 */ -int wolfBoot_set_encrypt_key(const uint8_t *key, int len); +#define ENCRYPT_KEY_SIZE 32 /* Chacha20 - 256bit */ +#define ENCRYPT_NONCE_SIZE 12 /* 96 bit*/ + +int wolfBoot_set_encrypt_key(const uint8_t *key, const uint8_t *nonce); int wolfBoot_erase_encrypt_key(void); -int wolfBoot_set_encrypt_password(const uint8_t *pwd, int len); #endif /* !WOLFBOOT_H */ diff --git a/src/libwolfboot.c b/src/libwolfboot.c index b69ac408..6872469f 100644 --- a/src/libwolfboot.c +++ b/src/libwolfboot.c @@ -497,7 +497,7 @@ int wolfBoot_fallback_is_possible(void) #error option EXT_ENCRYPTED requires EXT_FLASH #endif -#define ENCRYPT_TMP_SECRET_OFFSET (WOLFBOOT_PARTITION_SIZE - (TRAILER_SKIP + (sizeof(uint32_t) + 1 + ((1 + WOLFBOOT_PARTITION_SIZE) / (WOLFBOOT_SECTOR_SIZE * 8)) + ENCRYPT_KEY_SIZE))) +#define ENCRYPT_TMP_SECRET_OFFSET (WOLFBOOT_PARTITION_SIZE - (TRAILER_SKIP + (sizeof(uint32_t) + 1 + ((1 + WOLFBOOT_PARTITION_SIZE) / (WOLFBOOT_SECTOR_SIZE * 8)) + ENCRYPT_KEY_SIZE + ENCRYPT_NONCE_SIZE))) #ifdef NVM_FLASH_WRITEONCE @@ -507,7 +507,7 @@ static uint8_t ENCRYPT_CACHE[NVM_CACHE_SIZE] __attribute__((aligned(32))); #endif -static int RAMFUNCTION hal_set_key(const uint8_t *k) +static int RAMFUNCTION hal_set_key(const uint8_t *k, const uint8_t *nonce) { uint32_t addr = ENCRYPT_TMP_SECRET_OFFSET + WOLFBOOT_PARTITION_BOOT_ADDRESS; uint32_t addr_align = addr & (~(WOLFBOOT_SECTOR_SIZE - 1)); @@ -519,49 +519,48 @@ static int RAMFUNCTION hal_set_key(const uint8_t *k) if (ret != 0) return ret; XMEMCPY(ENCRYPT_CACHE + addr_off, k, ENCRYPT_KEY_SIZE); + XMEMCPY(ENCRYPT_CACHE + addr_off + ENCRYPT_KEY_SIZE, nonce, ENCRYPT_NONCE_SIZE); ret = hal_flash_write(addr_align, ENCRYPT_CACHE, WOLFBOOT_SECTOR_SIZE); hal_flash_lock(); return ret; } -int RAMFUNCTION wolfBoot_set_encrypt_key(const uint8_t *key, int len) +int RAMFUNCTION wolfBoot_set_encrypt_key(const uint8_t *key, const uint8_t *nonce) { - if (len != ENCRYPT_KEY_SIZE) - return -1; - hal_set_key(key); + hal_set_key(key, nonce); return 0; } int RAMFUNCTION wolfBoot_erase_encrypt_key(void) { - uint8_t ff[ENCRYPT_KEY_SIZE]; + uint8_t ff[ENCRYPT_KEY_SIZE + ENCRYPT_NONCE_SIZE]; int i; - XMEMSET(ff, 0xFF, ENCRYPT_KEY_SIZE); - hal_set_key(ff); + XMEMSET(ff, 0xFF, ENCRYPT_KEY_SIZE + ENCRYPT_NONCE_SIZE); + hal_set_key(ff, ff + ENCRYPT_KEY_SIZE); return 0; } -int RAMFUNCTION wolfBoot_set_encrypt_password(const uint8_t *pwd, int len) -{ - /* TODO */ - return -1; -} - #ifdef __WOLFBOOT static ChaCha chacha; static int chacha_initialized = 0; +static uint8_t chacha_iv_nonce[ENCRYPT_NONCE_SIZE]; static int chacha_init(void) { uint8_t *key = (uint8_t *)(WOLFBOOT_PARTITION_BOOT_ADDRESS + ENCRYPT_TMP_SECRET_OFFSET); uint8_t ff[ENCRYPT_KEY_SIZE]; + uint8_t *stored_nonce = key + ENCRYPT_KEY_SIZE; + + /* Check against 'all 0xff' or 'all zero' cases */ XMEMSET(ff, 0xFF, ENCRYPT_KEY_SIZE); if (XMEMCMP(key, ff, ENCRYPT_KEY_SIZE) == 0) return -1; - XMEMSET(ff, 0xFF, ENCRYPT_KEY_SIZE); + XMEMSET(ff, 0x00, ENCRYPT_KEY_SIZE); if (XMEMCMP(key, ff, ENCRYPT_KEY_SIZE) == 0) return -1; + + XMEMCPY(chacha_iv_nonce, stored_nonce, ENCRYPT_NONCE_SIZE); wc_Chacha_SetKey(&chacha, key, ENCRYPT_KEY_SIZE); chacha_initialized = 1; return 0; @@ -570,7 +569,6 @@ static int chacha_init(void) static inline uint8_t part_address(uintptr_t a) { - if ( 1 && #if WOLFBOOT_PARTITION_UPDATE_ADDRESS != 0 (a >= WOLFBOOT_PARTITION_UPDATE_ADDRESS) && @@ -590,10 +588,9 @@ static uint32_t swap_counter = 0; int ext_flash_encrypt_write(uintptr_t address, const uint8_t *data, int len) { - uint32_t iv[ENCRYPT_BLOCK_SIZE / sizeof(uint32_t)]; + uint32_t iv_counter; uint8_t block[ENCRYPT_BLOCK_SIZE]; uint8_t part; - uint32_t row_number; int sz = len; uint32_t row_address = address, row_offset; int i; @@ -609,20 +606,22 @@ int ext_flash_encrypt_write(uintptr_t address, const uint8_t *data, int len) if (!chacha_initialized) if (chacha_init() < 0) return -1; - XMEMSET(iv, 0, ENCRYPT_BLOCK_SIZE); part = part_address(address); switch(part) { case PART_UPDATE: - row_number = (address - WOLFBOOT_PARTITION_UPDATE_ADDRESS) / ENCRYPT_BLOCK_SIZE; + iv_counter = (address - WOLFBOOT_PARTITION_UPDATE_ADDRESS) / ENCRYPT_BLOCK_SIZE; /* Do not encrypt last sector */ - if (row_number == (WOLFBOOT_PARTITION_SIZE - 1) / ENCRYPT_BLOCK_SIZE) { + if (iv_counter == (WOLFBOOT_PARTITION_SIZE - 1) / ENCRYPT_BLOCK_SIZE) { return ext_flash_write(address, data, len); } break; case PART_SWAP: - row_number = (address - WOLFBOOT_PARTITION_UPDATE_ADDRESS) / ENCRYPT_BLOCK_SIZE; - iv[1] = swap_counter++; - break; + { + uint32_t row_number; + row_number = (address - WOLFBOOT_PARTITION_SWAP_ADDRESS) / ENCRYPT_BLOCK_SIZE; + iv_counter = ((swap_counter++) << 8) + row_number; + break; + } default: return -1; } @@ -631,30 +630,28 @@ int ext_flash_encrypt_write(uintptr_t address, const uint8_t *data, int len) if (ext_flash_read(row_address, block, ENCRYPT_BLOCK_SIZE) != ENCRYPT_BLOCK_SIZE) return -1; XMEMCPY(block + row_offset, data, step); - iv[0] = row_number; - wc_Chacha_SetIV(&chacha, (byte *)iv, ENCRYPT_BLOCK_SIZE); + wc_Chacha_SetIV(&chacha, chacha_iv_nonce, iv_counter); wc_Chacha_Process(&chacha, enc_block, block, ENCRYPT_BLOCK_SIZE); ext_flash_write(row_address, enc_block, ENCRYPT_BLOCK_SIZE); address += step; data += step; sz -= step; + iv_counter++; } for (i = 0; i < sz / ENCRYPT_BLOCK_SIZE; i++) { - iv[0] = row_number; - wc_Chacha_SetIV(&chacha, (byte *)iv, ENCRYPT_BLOCK_SIZE); + wc_Chacha_SetIV(&chacha, chacha_iv_nonce, iv_counter); XMEMCPY(block, data + (ENCRYPT_BLOCK_SIZE * i), ENCRYPT_BLOCK_SIZE); wc_Chacha_Process(&chacha, ENCRYPT_CACHE + (ENCRYPT_BLOCK_SIZE * i), block, ENCRYPT_BLOCK_SIZE); - row_number++; + iv_counter++; } return ext_flash_write(address, ENCRYPT_CACHE, len); } int ext_flash_decrypt_read(uintptr_t address, uint8_t *data, int len) { - uint32_t iv[ENCRYPT_BLOCK_SIZE / sizeof(uint32_t)]; + uint32_t iv_counter = 0; uint8_t block[ENCRYPT_BLOCK_SIZE]; uint8_t part; - uint32_t row_number; int sz = len; uint32_t row_address = address, row_offset; int i; @@ -671,19 +668,21 @@ int ext_flash_decrypt_read(uintptr_t address, uint8_t *data, int len) if (chacha_init() < 0) return -1; part = part_address(row_address); - XMEMSET(iv, 0, ENCRYPT_BLOCK_SIZE); switch(part) { case PART_UPDATE: - row_number = (address - WOLFBOOT_PARTITION_UPDATE_ADDRESS) / ENCRYPT_BLOCK_SIZE; + iv_counter = (address - WOLFBOOT_PARTITION_UPDATE_ADDRESS) / ENCRYPT_BLOCK_SIZE; /* Do not decrypt last sector */ - if (row_number == (WOLFBOOT_PARTITION_SIZE - 1) / ENCRYPT_BLOCK_SIZE) { + if (iv_counter == (WOLFBOOT_PARTITION_SIZE - 1) / ENCRYPT_BLOCK_SIZE) { return ext_flash_read(address, data, len); } break; case PART_SWAP: - row_number = (address - WOLFBOOT_PARTITION_UPDATE_ADDRESS) / ENCRYPT_BLOCK_SIZE; - iv[1] = swap_counter; - break; + { + uint32_t row_number; + row_number = (address - WOLFBOOT_PARTITION_UPDATE_ADDRESS) / ENCRYPT_BLOCK_SIZE; + iv_counter = (swap_counter << 8) + row_number; + break; + } default: return -1; } @@ -692,22 +691,21 @@ int ext_flash_decrypt_read(uintptr_t address, uint8_t *data, int len) int step = ENCRYPT_BLOCK_SIZE - row_offset; if (ext_flash_read(row_address, block, ENCRYPT_BLOCK_SIZE) != ENCRYPT_BLOCK_SIZE) return -1; - iv[0] = row_number; - wc_Chacha_SetIV(&chacha, (byte *)iv, ENCRYPT_BLOCK_SIZE); + wc_Chacha_SetIV(&chacha, chacha_iv_nonce, iv_counter); wc_Chacha_Process(&chacha, dec_block, block, ENCRYPT_BLOCK_SIZE); XMEMCPY(data, dec_block + row_offset, step); address += step; data += step; sz -= step; + iv_counter++; } if (ext_flash_read(address, data, sz) != sz) return -1; for (i = 0; i < sz / ENCRYPT_BLOCK_SIZE; i++) { - iv[0] = row_number; - wc_Chacha_SetIV(&chacha, (byte *)iv, ENCRYPT_BLOCK_SIZE); + wc_Chacha_SetIV(&chacha, chacha_iv_nonce, iv_counter); XMEMCPY(block, data + (ENCRYPT_BLOCK_SIZE * i), ENCRYPT_BLOCK_SIZE); wc_Chacha_Process(&chacha, data + (ENCRYPT_BLOCK_SIZE * i), block, ENCRYPT_BLOCK_SIZE); - row_number++; + iv_counter++; } return len; } diff --git a/test-app/app_stm32wb.c b/test-app/app_stm32wb.c index ee406d71..c896dde1 100644 --- a/test-app/app_stm32wb.c +++ b/test-app/app_stm32wb.c @@ -30,7 +30,8 @@ #include "update.c" #ifdef PLATFORM_stm32wb -char enc_key[33] = "0123456789abcdef0123456789abcdef"; +char enc_key[] = "0123456789abcdef0123456789abcdef" /* ChaCha key (256 bit) */ + "0123456789ab"; /* IV nonce (96 bit) */ volatile uint32_t time_elapsed = 0; void main(void) { @@ -48,7 +49,7 @@ void main(void) { uint32_t sz; boot_led_off(); #if EXT_ENCRYPTED - wolfBoot_set_encrypt_key((uint8_t *)enc_key, 32); + wolfBoot_set_encrypt_key((uint8_t *)enc_key,(uint8_t *)(enc_key + 32)); #endif wolfBoot_update_trigger(); boot_led_on(); diff --git a/tools/keytools/sign.py b/tools/keytools/sign.py index cfb4a3d1..389c3edb 100755 --- a/tools/keytools/sign.py +++ b/tools/keytools/sign.py @@ -390,11 +390,11 @@ if (encrypt): outfile = open(output_image_file, 'rb') ekeyfile = open(encrypt_key_file, 'rb') key = ekeyfile.read(32) + iv_nonce = ekeyfile.read(12) enc_outfile = open(encrypted_output_image_file, 'wb') cha = ciphers.ChaCha(key, 32) while(True): - iv = struct.pack(' enc_key.der +echo -n "0123456789abcdef0123456789abcdef0123456789ab" > enc_key.der ./tools/keytools/sign.py --encrypt enc_key.der test-app/image.bin ecc256.der $VERSION dd if=/dev/zero bs=$SIZE count=1 2>/dev/null | tr "\000" "\377" > update.bin diff --git a/tools/test-enc.mk b/tools/test-enc.mk index 92018245..94aac5b5 100644 --- a/tools/test-enc.mk +++ b/tools/test-enc.mk @@ -18,7 +18,7 @@ tools/uart-flash-server/ufserver: FORCE @rm -f src/libwolfboot.o test-enc-update: factory.bin test-app/image.bin .config tools/uart-flash-server/ufserver FORCE - @echo -n "0123456789abcdef0123456789abcdef" > /tmp/enc_key.der + @echo -n "0123456789abcdef0123456789abcdef0123456789ab" > /tmp/enc_key.der @$(SIGN_TOOL) $(SIGN_ARGS) test-app/image.bin $(PRIVATE_KEY) $(ENC_TEST_UPDATE_VERSION) @$(SIGN_TOOL) $(SIGN_ENC_ARGS) test-app/image.bin $(PRIVATE_KEY) $(ENC_TEST_UPDATE_VERSION) @st-flash write factory.bin 0x08000000 @@ -28,6 +28,8 @@ test-enc-update: factory.bin test-app/image.bin .config tools/uart-flash-server/ @sleep 5 @st-flash reset @sleep $(TIMEOUT) + @st-flash reset + @sleep 1 @sudo killall ufserver @st-flash read boot.bin 0x08010000 0x1000 @dd if=test-app/image_v$(ENC_TEST_UPDATE_VERSION)_signed.bin of=boot_compare.bin bs=4096 count=1