mirror of https://github.com/wolfSSL/wolfBoot.git
Fixed ChaCha20 IV usage/setting. Test/docs updated.
parent
861259316f
commit
f3f27ae2cb
|
@ -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).
|
||||
|
||||
|
||||
|
||||
|
||||
|
|
|
@ -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 */
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
|
|
@ -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();
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
Loading…
Reference in New Issue