Skip to content

Commit f3f27ae

Browse files
committed
Fixed ChaCha20 IV usage/setting. Test/docs updated.
1 parent 8612593 commit f3f27ae

7 files changed

Lines changed: 104 additions & 56 deletions

File tree

docs/encrypted_partitions.md

Lines changed: 50 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@
33
wolfBoot offers the possibility to encrypt the content of the entire UPDATE partition,
44
by using a pre-shared symmetric key which can be temporarily stored in a safer non-volatile memory area.
55

6-
SWAP partition is also temporarily encrypted using the same key, so a dump of the external flash won't reveal
6+
SWAP partition is also temporarily encrypted using the same key, so a dump of the external flash won't reveal
77
any content of the firmware update packages.
88

99
### Rationale
@@ -30,7 +30,7 @@ Alternatively, more secure mechanisms are available to store the temporary key i
3030

3131
The temporary key can be set at run time by the application, and will be used exactly once by the bootloader
3232
to verify and install the next update. The key can be for example received from a back-end during the update
33-
process using secure communication, and set by the application, using `libwolfboot` API, to be used by
33+
process using secure communication, and set by the application, using `libwolfboot` API, to be used by
3434
wolfBoot upon next boot.
3535

3636
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.
4444
The functions
4545

4646
```
47-
int wolfBoot_set_encrypt_key(const uint8_t *key, int len);
47+
int wolfBoot_set_encrypt_key(const uint8_t *key, const uint8_t *nonce);
4848
int wolfBoot_erase_encrypt_key(void);
4949
```
5050

@@ -58,6 +58,52 @@ reboot.
5858
### Symmetric encryption algorithm
5959

6060
The algorithm currently used to encrypt and decrypt data in external partitions
61-
is Chacha20-256. The expected key to provide to `wolfBoot_set_encrypt_key()` must be exactly 32 Bytes long.
61+
is Chacha20-256.
62+
63+
- The `key` provided to `wolfBoot_set_encrypt_key()` must be exactly 32 Bytes long.
64+
- The `nonce` argument must be a 96-bit (12 Bytes) randomly generated buffer, to be used as IV for encryption and decription.
65+
66+
## Example usage
67+
68+
### Signing and encrypting the update bundle
69+
70+
The `sign.py` tool can sign and encrypt the image with a single command.
71+
The encryption secret is provided in a binary file that should contain a concatenation of
72+
a 32B ChaCha-256 key and a 12B nonce.
73+
74+
In the examples provided, the test application uses the following parameters:
75+
76+
```
77+
key = "0123456789abcdef0123456789abcdef"
78+
nonce = "0123456789ab"
79+
```
80+
81+
So it is easy to prepare the encryption secret in the test scripts or from the command line using:
82+
83+
```
84+
echo -n "0123456789abcdef0123456789abcdef0123456789ab" > enc_key.der
85+
```
86+
87+
The `sign.py` script can now be invoked to produce a signed+encrypted image, by using the extra argument `--encrypt` followed by the
88+
secret file:
89+
90+
```
91+
./tools/keytools/sign.py --encrypt enc_key.der test-app/image.bin ecc256.der 24
92+
93+
```
94+
95+
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.
96+
97+
98+
### API usage in the application
99+
100+
When transferring the image, the application can still use the libwolfboot API functions to store the encrypted firmware. When called from the application,
101+
the function `ext_flash_write` will store the payload unencrypted.
102+
103+
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`.
104+
105+
An example of encrypted update trigger can be found in the [stm32wb test application source code](test-app/app_stm32wb.c).
106+
107+
62108

63109

include/wolfboot/wolfboot.h

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -120,8 +120,9 @@ int wolfBoot_dualboot_candidate(void);
120120

121121
/* Encryption support */
122122
#define ENCRYPT_BLOCK_SIZE 16
123-
#define ENCRYPT_KEY_SIZE 32 /* Chacha20-256 */
124-
int wolfBoot_set_encrypt_key(const uint8_t *key, int len);
123+
#define ENCRYPT_KEY_SIZE 32 /* Chacha20 - 256bit */
124+
#define ENCRYPT_NONCE_SIZE 12 /* 96 bit*/
125+
126+
int wolfBoot_set_encrypt_key(const uint8_t *key, const uint8_t *nonce);
125127
int wolfBoot_erase_encrypt_key(void);
126-
int wolfBoot_set_encrypt_password(const uint8_t *pwd, int len);
127128
#endif /* !WOLFBOOT_H */

src/libwolfboot.c

Lines changed: 41 additions & 43 deletions
Original file line numberDiff line numberDiff line change
@@ -497,7 +497,7 @@ int wolfBoot_fallback_is_possible(void)
497497
#error option EXT_ENCRYPTED requires EXT_FLASH
498498
#endif
499499

500-
#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)))
500+
#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)))
501501

502502

503503
#ifdef NVM_FLASH_WRITEONCE
@@ -507,7 +507,7 @@ static uint8_t ENCRYPT_CACHE[NVM_CACHE_SIZE] __attribute__((aligned(32)));
507507
#endif
508508

509509

510-
static int RAMFUNCTION hal_set_key(const uint8_t *k)
510+
static int RAMFUNCTION hal_set_key(const uint8_t *k, const uint8_t *nonce)
511511
{
512512
uint32_t addr = ENCRYPT_TMP_SECRET_OFFSET + WOLFBOOT_PARTITION_BOOT_ADDRESS;
513513
uint32_t addr_align = addr & (~(WOLFBOOT_SECTOR_SIZE - 1));
@@ -519,49 +519,48 @@ static int RAMFUNCTION hal_set_key(const uint8_t *k)
519519
if (ret != 0)
520520
return ret;
521521
XMEMCPY(ENCRYPT_CACHE + addr_off, k, ENCRYPT_KEY_SIZE);
522+
XMEMCPY(ENCRYPT_CACHE + addr_off + ENCRYPT_KEY_SIZE, nonce, ENCRYPT_NONCE_SIZE);
522523
ret = hal_flash_write(addr_align, ENCRYPT_CACHE, WOLFBOOT_SECTOR_SIZE);
523524
hal_flash_lock();
524525
return ret;
525526
}
526527

527-
int RAMFUNCTION wolfBoot_set_encrypt_key(const uint8_t *key, int len)
528+
int RAMFUNCTION wolfBoot_set_encrypt_key(const uint8_t *key, const uint8_t *nonce)
528529
{
529-
if (len != ENCRYPT_KEY_SIZE)
530-
return -1;
531-
hal_set_key(key);
530+
hal_set_key(key, nonce);
532531
return 0;
533532
}
534533

535534
int RAMFUNCTION wolfBoot_erase_encrypt_key(void)
536535
{
537-
uint8_t ff[ENCRYPT_KEY_SIZE];
536+
uint8_t ff[ENCRYPT_KEY_SIZE + ENCRYPT_NONCE_SIZE];
538537
int i;
539-
XMEMSET(ff, 0xFF, ENCRYPT_KEY_SIZE);
540-
hal_set_key(ff);
538+
XMEMSET(ff, 0xFF, ENCRYPT_KEY_SIZE + ENCRYPT_NONCE_SIZE);
539+
hal_set_key(ff, ff + ENCRYPT_KEY_SIZE);
541540
return 0;
542541
}
543542

544-
int RAMFUNCTION wolfBoot_set_encrypt_password(const uint8_t *pwd, int len)
545-
{
546-
/* TODO */
547-
return -1;
548-
}
549-
550543
#ifdef __WOLFBOOT
551544

552545
static ChaCha chacha;
553546
static int chacha_initialized = 0;
547+
static uint8_t chacha_iv_nonce[ENCRYPT_NONCE_SIZE];
554548

555549
static int chacha_init(void)
556550
{
557551
uint8_t *key = (uint8_t *)(WOLFBOOT_PARTITION_BOOT_ADDRESS + ENCRYPT_TMP_SECRET_OFFSET);
558552
uint8_t ff[ENCRYPT_KEY_SIZE];
553+
uint8_t *stored_nonce = key + ENCRYPT_KEY_SIZE;
554+
555+
/* Check against 'all 0xff' or 'all zero' cases */
559556
XMEMSET(ff, 0xFF, ENCRYPT_KEY_SIZE);
560557
if (XMEMCMP(key, ff, ENCRYPT_KEY_SIZE) == 0)
561558
return -1;
562-
XMEMSET(ff, 0xFF, ENCRYPT_KEY_SIZE);
559+
XMEMSET(ff, 0x00, ENCRYPT_KEY_SIZE);
563560
if (XMEMCMP(key, ff, ENCRYPT_KEY_SIZE) == 0)
564561
return -1;
562+
563+
XMEMCPY(chacha_iv_nonce, stored_nonce, ENCRYPT_NONCE_SIZE);
565564
wc_Chacha_SetKey(&chacha, key, ENCRYPT_KEY_SIZE);
566565
chacha_initialized = 1;
567566
return 0;
@@ -570,7 +569,6 @@ static int chacha_init(void)
570569

571570
static inline uint8_t part_address(uintptr_t a)
572571
{
573-
574572
if ( 1 &&
575573
#if WOLFBOOT_PARTITION_UPDATE_ADDRESS != 0
576574
(a >= WOLFBOOT_PARTITION_UPDATE_ADDRESS) &&
@@ -590,10 +588,9 @@ static uint32_t swap_counter = 0;
590588

591589
int ext_flash_encrypt_write(uintptr_t address, const uint8_t *data, int len)
592590
{
593-
uint32_t iv[ENCRYPT_BLOCK_SIZE / sizeof(uint32_t)];
591+
uint32_t iv_counter;
594592
uint8_t block[ENCRYPT_BLOCK_SIZE];
595593
uint8_t part;
596-
uint32_t row_number;
597594
int sz = len;
598595
uint32_t row_address = address, row_offset;
599596
int i;
@@ -609,20 +606,22 @@ int ext_flash_encrypt_write(uintptr_t address, const uint8_t *data, int len)
609606
if (!chacha_initialized)
610607
if (chacha_init() < 0)
611608
return -1;
612-
XMEMSET(iv, 0, ENCRYPT_BLOCK_SIZE);
613609
part = part_address(address);
614610
switch(part) {
615611
case PART_UPDATE:
616-
row_number = (address - WOLFBOOT_PARTITION_UPDATE_ADDRESS) / ENCRYPT_BLOCK_SIZE;
612+
iv_counter = (address - WOLFBOOT_PARTITION_UPDATE_ADDRESS) / ENCRYPT_BLOCK_SIZE;
617613
/* Do not encrypt last sector */
618-
if (row_number == (WOLFBOOT_PARTITION_SIZE - 1) / ENCRYPT_BLOCK_SIZE) {
614+
if (iv_counter == (WOLFBOOT_PARTITION_SIZE - 1) / ENCRYPT_BLOCK_SIZE) {
619615
return ext_flash_write(address, data, len);
620616
}
621617
break;
622618
case PART_SWAP:
623-
row_number = (address - WOLFBOOT_PARTITION_UPDATE_ADDRESS) / ENCRYPT_BLOCK_SIZE;
624-
iv[1] = swap_counter++;
625-
break;
619+
{
620+
uint32_t row_number;
621+
row_number = (address - WOLFBOOT_PARTITION_SWAP_ADDRESS) / ENCRYPT_BLOCK_SIZE;
622+
iv_counter = ((swap_counter++) << 8) + row_number;
623+
break;
624+
}
626625
default:
627626
return -1;
628627
}
@@ -631,30 +630,28 @@ int ext_flash_encrypt_write(uintptr_t address, const uint8_t *data, int len)
631630
if (ext_flash_read(row_address, block, ENCRYPT_BLOCK_SIZE) != ENCRYPT_BLOCK_SIZE)
632631
return -1;
633632
XMEMCPY(block + row_offset, data, step);
634-
iv[0] = row_number;
635-
wc_Chacha_SetIV(&chacha, (byte *)iv, ENCRYPT_BLOCK_SIZE);
633+
wc_Chacha_SetIV(&chacha, chacha_iv_nonce, iv_counter);
636634
wc_Chacha_Process(&chacha, enc_block, block, ENCRYPT_BLOCK_SIZE);
637635
ext_flash_write(row_address, enc_block, ENCRYPT_BLOCK_SIZE);
638636
address += step;
639637
data += step;
640638
sz -= step;
639+
iv_counter++;
641640
}
642641
for (i = 0; i < sz / ENCRYPT_BLOCK_SIZE; i++) {
643-
iv[0] = row_number;
644-
wc_Chacha_SetIV(&chacha, (byte *)iv, ENCRYPT_BLOCK_SIZE);
642+
wc_Chacha_SetIV(&chacha, chacha_iv_nonce, iv_counter);
645643
XMEMCPY(block, data + (ENCRYPT_BLOCK_SIZE * i), ENCRYPT_BLOCK_SIZE);
646644
wc_Chacha_Process(&chacha, ENCRYPT_CACHE + (ENCRYPT_BLOCK_SIZE * i), block, ENCRYPT_BLOCK_SIZE);
647-
row_number++;
645+
iv_counter++;
648646
}
649647
return ext_flash_write(address, ENCRYPT_CACHE, len);
650648
}
651649

652650
int ext_flash_decrypt_read(uintptr_t address, uint8_t *data, int len)
653651
{
654-
uint32_t iv[ENCRYPT_BLOCK_SIZE / sizeof(uint32_t)];
652+
uint32_t iv_counter = 0;
655653
uint8_t block[ENCRYPT_BLOCK_SIZE];
656654
uint8_t part;
657-
uint32_t row_number;
658655
int sz = len;
659656
uint32_t row_address = address, row_offset;
660657
int i;
@@ -671,19 +668,21 @@ int ext_flash_decrypt_read(uintptr_t address, uint8_t *data, int len)
671668
if (chacha_init() < 0)
672669
return -1;
673670
part = part_address(row_address);
674-
XMEMSET(iv, 0, ENCRYPT_BLOCK_SIZE);
675671
switch(part) {
676672
case PART_UPDATE:
677-
row_number = (address - WOLFBOOT_PARTITION_UPDATE_ADDRESS) / ENCRYPT_BLOCK_SIZE;
673+
iv_counter = (address - WOLFBOOT_PARTITION_UPDATE_ADDRESS) / ENCRYPT_BLOCK_SIZE;
678674
/* Do not decrypt last sector */
679-
if (row_number == (WOLFBOOT_PARTITION_SIZE - 1) / ENCRYPT_BLOCK_SIZE) {
675+
if (iv_counter == (WOLFBOOT_PARTITION_SIZE - 1) / ENCRYPT_BLOCK_SIZE) {
680676
return ext_flash_read(address, data, len);
681677
}
682678
break;
683679
case PART_SWAP:
684-
row_number = (address - WOLFBOOT_PARTITION_UPDATE_ADDRESS) / ENCRYPT_BLOCK_SIZE;
685-
iv[1] = swap_counter;
686-
break;
680+
{
681+
uint32_t row_number;
682+
row_number = (address - WOLFBOOT_PARTITION_UPDATE_ADDRESS) / ENCRYPT_BLOCK_SIZE;
683+
iv_counter = (swap_counter << 8) + row_number;
684+
break;
685+
}
687686
default:
688687
return -1;
689688
}
@@ -692,22 +691,21 @@ int ext_flash_decrypt_read(uintptr_t address, uint8_t *data, int len)
692691
int step = ENCRYPT_BLOCK_SIZE - row_offset;
693692
if (ext_flash_read(row_address, block, ENCRYPT_BLOCK_SIZE) != ENCRYPT_BLOCK_SIZE)
694693
return -1;
695-
iv[0] = row_number;
696-
wc_Chacha_SetIV(&chacha, (byte *)iv, ENCRYPT_BLOCK_SIZE);
694+
wc_Chacha_SetIV(&chacha, chacha_iv_nonce, iv_counter);
697695
wc_Chacha_Process(&chacha, dec_block, block, ENCRYPT_BLOCK_SIZE);
698696
XMEMCPY(data, dec_block + row_offset, step);
699697
address += step;
700698
data += step;
701699
sz -= step;
700+
iv_counter++;
702701
}
703702
if (ext_flash_read(address, data, sz) != sz)
704703
return -1;
705704
for (i = 0; i < sz / ENCRYPT_BLOCK_SIZE; i++) {
706-
iv[0] = row_number;
707-
wc_Chacha_SetIV(&chacha, (byte *)iv, ENCRYPT_BLOCK_SIZE);
705+
wc_Chacha_SetIV(&chacha, chacha_iv_nonce, iv_counter);
708706
XMEMCPY(block, data + (ENCRYPT_BLOCK_SIZE * i), ENCRYPT_BLOCK_SIZE);
709707
wc_Chacha_Process(&chacha, data + (ENCRYPT_BLOCK_SIZE * i), block, ENCRYPT_BLOCK_SIZE);
710-
row_number++;
708+
iv_counter++;
711709
}
712710
return len;
713711
}

test-app/app_stm32wb.c

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -30,7 +30,8 @@
3030
#include "update.c"
3131

3232
#ifdef PLATFORM_stm32wb
33-
char enc_key[33] = "0123456789abcdef0123456789abcdef";
33+
char enc_key[] = "0123456789abcdef0123456789abcdef" /* ChaCha key (256 bit) */
34+
"0123456789ab"; /* IV nonce (96 bit) */
3435

3536
volatile uint32_t time_elapsed = 0;
3637
void main(void) {
@@ -48,7 +49,7 @@ void main(void) {
4849
uint32_t sz;
4950
boot_led_off();
5051
#if EXT_ENCRYPTED
51-
wolfBoot_set_encrypt_key((uint8_t *)enc_key, 32);
52+
wolfBoot_set_encrypt_key((uint8_t *)enc_key,(uint8_t *)(enc_key + 32));
5253
#endif
5354
wolfBoot_update_trigger();
5455
boot_led_on();

tools/keytools/sign.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -390,11 +390,11 @@
390390
outfile = open(output_image_file, 'rb')
391391
ekeyfile = open(encrypt_key_file, 'rb')
392392
key = ekeyfile.read(32)
393+
iv_nonce = ekeyfile.read(12)
393394
enc_outfile = open(encrypted_output_image_file, 'wb')
394395
cha = ciphers.ChaCha(key, 32)
395396
while(True):
396-
iv = struct.pack('<LLLL', off, 0, 0, 0)
397-
cha.set_iv(iv)
397+
cha.set_iv(iv_nonce, off)
398398
buf = outfile.read(16)
399399
if len(buf) == 0:
400400
break

tools/scripts/prepare_encrypted_update.sh

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@ VERSION=8
55
APP=test-app/image_v"$VERSION"_signed_and_encrypted.bin
66

77
# Create test key
8-
echo -n "0123456789abcdef0123456789abcdef" > enc_key.der
8+
echo -n "0123456789abcdef0123456789abcdef0123456789ab" > enc_key.der
99

1010
./tools/keytools/sign.py --encrypt enc_key.der test-app/image.bin ecc256.der $VERSION
1111
dd if=/dev/zero bs=$SIZE count=1 2>/dev/null | tr "\000" "\377" > update.bin

tools/test-enc.mk

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,7 @@ tools/uart-flash-server/ufserver: FORCE
1818
@rm -f src/libwolfboot.o
1919

2020
test-enc-update: factory.bin test-app/image.bin .config tools/uart-flash-server/ufserver FORCE
21-
@echo -n "0123456789abcdef0123456789abcdef" > /tmp/enc_key.der
21+
@echo -n "0123456789abcdef0123456789abcdef0123456789ab" > /tmp/enc_key.der
2222
@$(SIGN_TOOL) $(SIGN_ARGS) test-app/image.bin $(PRIVATE_KEY) $(ENC_TEST_UPDATE_VERSION)
2323
@$(SIGN_TOOL) $(SIGN_ENC_ARGS) test-app/image.bin $(PRIVATE_KEY) $(ENC_TEST_UPDATE_VERSION)
2424
@st-flash write factory.bin 0x08000000
@@ -28,6 +28,8 @@ test-enc-update: factory.bin test-app/image.bin .config tools/uart-flash-server/
2828
@sleep 5
2929
@st-flash reset
3030
@sleep $(TIMEOUT)
31+
@st-flash reset
32+
@sleep 1
3133
@sudo killall ufserver
3234
@st-flash read boot.bin 0x08010000 0x1000
3335
@dd if=test-app/image_v$(ENC_TEST_UPDATE_VERSION)_signed.bin of=boot_compare.bin bs=4096 count=1

0 commit comments

Comments
 (0)