Fixed ChaCha20 IV usage/setting. Test/docs updated.

pull/62/head
Daniele Lacamera 2020-06-18 11:12:01 +02:00
parent 861259316f
commit f3f27ae2cb
7 changed files with 104 additions and 56 deletions

View File

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

View File

@ -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 */

View File

@ -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++;
{
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:
{
uint32_t row_number;
row_number = (address - WOLFBOOT_PARTITION_UPDATE_ADDRESS) / ENCRYPT_BLOCK_SIZE;
iv[1] = swap_counter;
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;
}

View File

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

View File

@ -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('<LLLL', off, 0, 0, 0)
cha.set_iv(iv)
cha.set_iv(iv_nonce, off)
buf = outfile.read(16)
if len(buf) == 0:
break

View File

@ -5,7 +5,7 @@ VERSION=8
APP=test-app/image_v"$VERSION"_signed_and_encrypted.bin
# Create test key
echo -n "0123456789abcdef0123456789abcdef" > 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

View File

@ -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