Skip to content

Commit 2cdc1f5

Browse files
authored
Merge pull request #520 from danielinux/delta-base-hash
Delta update: check sha digest of base image
2 parents 6d1adc2 + 3a69b0e commit 2cdc1f5

File tree

11 files changed

+178
-14
lines changed

11 files changed

+178
-14
lines changed

.github/workflows/test-powerfail-simulator.yml

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -235,6 +235,14 @@ jobs:
235235
run: |
236236
tools/scripts/sim-update-powerfail-resume.sh
237237
238+
- name: Rebuild without SHA of base image to test compatibility
239+
run: |
240+
make clean && make test-sim-internal-flash-with-delta-update-no-base-sha
241+
242+
- name: Run sunny day update test (DELTA with no-base-sha)
243+
run: |
244+
tools/scripts/sim-sunnyday-update.sh
245+
238246
- name: Rebuild with wrong delta base version
239247
run: |
240248
make clean && make test-sim-internal-flash-with-wrong-delta-update

config/examples/sim-delta-update.config

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@ WOLFBOOT_SMALL_STACK=1
66
SPI_FLASH=0
77
DEBUG=1
88
DELTA_UPDATES=1
9+
IMAGE_HEADER_SIZE=512
910

1011
# sizes should be multiple of system page size
1112
WOLFBOOT_PARTITION_SIZE=0x40000

config/examples/sim-encrypt-delta-nvm-writeonce-update.config

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@ ENCRYPT_WITH_AES128=1
1010
DEBUG=1
1111
DELTA_UPDATES=1
1212
NVM_FLASH_WRITEONCE=1
13+
IMAGE_HEADER_SIZE=512
1314

1415
# sizes should be multiple of system page size
1516
WOLFBOOT_PARTITION_SIZE=0x40000

config/examples/sim-encrypt-delta-update.config

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@ ENCRYPT=1
99
ENCRYPT_WITH_AES128=1
1010
DEBUG=1
1111
DELTA_UPDATES=1
12+
IMAGE_HEADER_SIZE=512
1213

1314
# sizes should be multiple of system page size
1415
WOLFBOOT_PARTITION_SIZE=0x40000

docs/Signing.md

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -180,6 +180,15 @@ result is stored in a file ending in `_signed_diff.bin`.
180180

181181
The compression scheme used is Bentley–McIlroy.
182182

183+
Options:
184+
* `--no-base-sha` : Avoid adding the sha of the base image to the manifest header.
185+
By default, the sign tool appends the sha of the base image to the manifest header,
186+
so wolfBoot will refuse to start a delta update if the sha does not match the
187+
one of the existing image. However, this takes up 32 to 48 bytes extra in the
188+
manifest header, so this option is available to provide compatibility on
189+
existing installations without this feature, where the header size does not
190+
allow to accommodate the field
191+
183192

184193
#### Policy signing (for sealing/unsealing with a TPM)
185194

include/delta.h

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -67,7 +67,8 @@ int wb_diff_init(WB_DIFF_CTX *ctx, uint8_t *src_a, uint32_t len_a, uint8_t *src_
6767
int wb_diff(WB_DIFF_CTX *ctx, uint8_t *patch, uint32_t len);
6868
int wb_patch_init(WB_PATCH_CTX *bm, uint8_t *src, uint32_t ssz, uint8_t *patch, uint32_t psz);
6969
int wb_patch(WB_PATCH_CTX *ctx, uint8_t *dst, uint32_t len);
70-
int wolfBoot_get_delta_info(uint8_t part, int inverse, uint32_t **img_offset, uint32_t **img_size);
70+
int wolfBoot_get_delta_info(uint8_t part, int inverse, uint32_t **img_offset,
71+
uint32_t **img_size, uint8_t **base_hash, uint16_t *base_hash_size);
7172

7273
#endif
7374

include/wolfboot/wolfboot.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -66,6 +66,7 @@ extern "C" {
6666
#define HDR_IMG_TYPE 0x04
6767
#define HDR_IMG_DELTA_BASE 0x05
6868
#define HDR_IMG_DELTA_SIZE 0x06
69+
#define HDR_IMG_DELTA_BASE_HASH 0x07
6970
#define HDR_PUBKEY 0x10
7071
#define HDR_SECONDARY_CIPHER 0x11
7172
#define HDR_SECONDARY_PUBKEY 0x12

src/libwolfboot.c

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -935,9 +935,8 @@ static inline uint16_t im2ns(uint16_t val)
935935
*
936936
*/
937937
int wolfBoot_get_delta_info(uint8_t part, int inverse, uint32_t **img_offset,
938-
uint16_t **img_size)
938+
uint32_t **img_size, uint8_t **base_hash, uint16_t *base_hash_size)
939939
{
940-
uint32_t *version_field = NULL;
941940
uint32_t *magic = NULL;
942941
uint8_t *image = (uint8_t *)0x00000000;
943942
if (part == PART_UPDATE) {
@@ -986,6 +985,8 @@ int wolfBoot_get_delta_info(uint8_t part, int inverse, uint32_t **img_offset,
986985
return -1;
987986
}
988987
}
988+
*base_hash_size = wolfBoot_find_header((uint8_t *)(image + IMAGE_HEADER_OFFSET),
989+
HDR_IMG_DELTA_BASE_HASH, base_hash);
989990
return 0;
990991
}
991992
#endif

src/update_flash.c

Lines changed: 38 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -313,6 +313,10 @@ static int wolfBoot_delta_update(struct wolfBoot_image *boot,
313313
uint8_t nonce[ENCRYPT_NONCE_SIZE];
314314
uint8_t enc_blk[DELTA_BLOCK_SIZE];
315315
#endif
316+
uint16_t delta_base_hash_sz;
317+
uint8_t *delta_base_hash;
318+
uint16_t base_hash_sz;
319+
uint8_t *base_hash;
316320

317321
/* Use biggest size for the swap */
318322
total_size = boot->fw_size + IMAGE_HEADER_SIZE;
@@ -327,12 +331,39 @@ static int wolfBoot_delta_update(struct wolfBoot_image *boot,
327331
#ifdef EXT_ENCRYPTED
328332
wolfBoot_get_encrypt_key(key, nonce);
329333
#endif
330-
if (wolfBoot_get_delta_info(PART_UPDATE, inverse, &img_offset, &img_size) < 0) {
334+
if (wolfBoot_get_delta_info(PART_UPDATE, inverse, &img_offset, &img_size,
335+
&delta_base_hash, &delta_base_hash_sz) < 0) {
331336
return -1;
332337
}
333338
cur_v = wolfBoot_current_firmware_version();
334339
upd_v = wolfBoot_update_firmware_version();
335340
delta_base_v = wolfBoot_get_diffbase_version(PART_UPDATE);
341+
342+
if (delta_base_hash_sz != WOLFBOOT_SHA_DIGEST_SIZE) {
343+
if (delta_base_hash_sz == 0) {
344+
wolfBoot_printf("Warning: delta update: Base hash not found in image\n");
345+
delta_base_hash = NULL;
346+
} else {
347+
wolfBoot_printf("Error: delta update: Base hash size mismatch"
348+
" (size: %x expected %x)\n", delta_base_hash_sz,
349+
WOLFBOOT_SHA_DIGEST_SIZE);
350+
return -1;
351+
}
352+
}
353+
354+
#if defined(WOLFBOOT_HASH_SHA256)
355+
base_hash_sz = wolfBoot_find_header(boot->hdr + IMAGE_HEADER_OFFSET,
356+
HDR_SHA256, &base_hash);
357+
#elif defined(WOLFBOOT_HASH_SHA384)
358+
base_hash_sz = wolfBoot_find_header(boot->hdr + IMAGE_HEADER_OFFSET,
359+
HDR_SHA384, &base_hash);
360+
#elif defined(WOLFBOOT_HASH_SHA3_384)
361+
base_hash_sz = wolfBoot_find_header(boot->hdr + IMAGE_HEADER_OFFSET,
362+
HDR_SHA3_384, &base_hash);
363+
#else
364+
#error "Delta update: Fatal error, no hash algorithm defined!"
365+
#endif
366+
336367
if (inverse) {
337368
if (((cur_v == upd_v) && (delta_base_v < cur_v)) || resume) {
338369
ret = wb_patch_init(&ctx, boot->hdr, boot->fw_size +
@@ -345,10 +376,15 @@ static int wolfBoot_delta_update(struct wolfBoot_image *boot,
345376
}
346377
} else {
347378
if (!resume && (cur_v != delta_base_v)) {
348-
/* Wrong base image, cannot apply delta patch */
379+
/* Wrong base image version, cannot apply delta patch */
349380
wolfBoot_printf("Delta Base 0x%x != Cur 0x%x\n",
350381
cur_v, delta_base_v);
351382
ret = -1;
383+
} else if (!resume && delta_base_hash &&
384+
memcmp(base_hash, delta_base_hash, base_hash_sz) != 0) {
385+
/* Wrong base image digest, cannot apply delta patch */
386+
wolfBoot_printf("Delta Base hash mismatch\n");
387+
ret = -1;
352388
} else {
353389
ret = wb_patch_init(&ctx, boot->hdr, boot->fw_size + IMAGE_HEADER_SIZE,
354390
update->hdr + IMAGE_HEADER_SIZE, *img_size);

tools/keytools/sign.c

Lines changed: 106 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -48,6 +48,7 @@
4848
#include <delta.h>
4949

5050
#include "wolfboot/version.h"
51+
#include "wolfboot/wolfboot.h"
5152

5253
#ifdef DEBUG_SIGNTOOL
5354
#define DEBUG_PRINT(...) fprintf(stderr, __VA_ARGS__)
@@ -176,6 +177,7 @@ static inline int fp_truncate(FILE *f, size_t len)
176177

177178
#define HDR_IMG_DELTA_BASE 0x05
178179
#define HDR_IMG_DELTA_SIZE 0x06
180+
#define HDR_IMG_DELTA_BASE_HASH 0x07
179181
#define HDR_IMG_DELTA_INVERSE 0x15
180182
#define HDR_IMG_DELTA_INVERSE_SIZE 0x16
181183

@@ -289,6 +291,7 @@ struct cmd_options {
289291
const char *policy_file;
290292
const char *encrypt_key_file;
291293
const char *delta_base_file;
294+
int no_base_sha;
292295
char output_image_file[PATH_MAX];
293296
char output_diff_file[PATH_MAX];
294297
char output_encrypted_image_file[PATH_MAX];
@@ -316,6 +319,57 @@ static struct cmd_options CMD = {
316319
.hybrid = 0
317320
};
318321

322+
static uint16_t sign_tool_find_header(uint8_t *haystack, uint16_t type, uint8_t **ptr)
323+
{
324+
uint8_t *p = haystack;
325+
uint16_t len, htype;
326+
const volatile uint8_t *max_p = (haystack - IMAGE_HEADER_OFFSET) +
327+
IMAGE_HEADER_SIZE;
328+
*ptr = NULL;
329+
if (p > max_p) {
330+
fprintf(stderr, "Illegal address (too high)\n");
331+
return 0;
332+
}
333+
while ((p + 4) < max_p) {
334+
htype = p[0] | (p[1] << 8);
335+
if (htype == 0) {
336+
fprintf(stderr, "Explicit end of options reached\n");
337+
break;
338+
}
339+
/* skip unaligned half-words and padding bytes */
340+
if ((p[0] == HDR_PADDING) || ((((size_t)p) & 0x01) != 0)) {
341+
p++;
342+
continue;
343+
}
344+
345+
len = p[2] | (p[3] << 8);
346+
/* check len */
347+
if ((4 + len) > (uint16_t)(IMAGE_HEADER_SIZE - IMAGE_HEADER_OFFSET)) {
348+
fprintf(stderr, "This field is too large (bigger than the space available "
349+
"in the current header)\n");
350+
//fprintf(stderr, "%d %d %d\n", len, IMAGE_HEADER_SIZE, IMAGE_HEADER_OFFSET);
351+
break;
352+
}
353+
/* check max pointer */
354+
if (p + 4 + len > max_p) {
355+
fprintf(stderr, "This field is too large and would overflow the image "
356+
"header\n");
357+
break;
358+
}
359+
360+
/* skip header [type|len] */
361+
p += 4;
362+
363+
if (htype == type) {
364+
/* found, return pointer to data portion */
365+
*ptr = p;
366+
return len;
367+
}
368+
p += len;
369+
}
370+
return 0;
371+
}
372+
319373
static int load_key_ecc(int sign_type, uint32_t curve_sz, int curve_id,
320374
int header_sz,
321375
uint8_t **key_buffer, uint32_t *key_buffer_sz,
@@ -1063,7 +1117,8 @@ static int sign_digest(int sign, int hash_algo,
10631117
static int make_header_ex(int is_diff, uint8_t *pubkey, uint32_t pubkey_sz,
10641118
const char *image_file, const char *outfile,
10651119
uint32_t delta_base_version, uint32_t patch_len, uint32_t patch_inv_off,
1066-
uint32_t patch_inv_len, const uint8_t *secondary_key, uint32_t secondary_key_sz)
1120+
uint32_t patch_inv_len, const uint8_t *secondary_key, uint32_t secondary_key_sz,
1121+
uint8_t *base_hash, uint32_t base_hash_sz)
10671122
{
10681123
uint32_t header_idx;
10691124
uint8_t *header;
@@ -1141,12 +1196,42 @@ static int make_header_ex(int is_diff, uint8_t *pubkey, uint32_t pubkey_sz,
11411196
&patch_len);
11421197

11431198
/* Append pad bytes, so fields are 4-byte aligned */
1144-
while ((header_idx % 4) != 0)
1145-
header_idx++;
1199+
ALIGN_4(header_idx);
11461200
header_append_tag(header, &header_idx, HDR_IMG_DELTA_INVERSE, 4,
11471201
&patch_inv_off);
11481202
header_append_tag(header, &header_idx, HDR_IMG_DELTA_INVERSE_SIZE, 4,
11491203
&patch_inv_len);
1204+
1205+
if (!CMD.no_base_sha) {
1206+
/* Append pad bytes, so base hash is 8-byte aligned */
1207+
ALIGN_8(header_idx);
1208+
if (!base_hash) {
1209+
fprintf(stderr, "Base hash for delta image not found.\n");
1210+
exit(1);
1211+
}
1212+
if (CMD.hash_algo == HASH_SHA256) {
1213+
if (base_hash_sz != HDR_SHA256_LEN) {
1214+
fprintf(stderr, "Invalid base hash size for SHA256.\n");
1215+
exit(1);
1216+
}
1217+
header_append_tag(header, &header_idx, HDR_IMG_DELTA_BASE_HASH,
1218+
HDR_SHA256_LEN, base_hash);
1219+
} else if (CMD.hash_algo == HASH_SHA384) {
1220+
if (base_hash_sz != HDR_SHA384_LEN) {
1221+
fprintf(stderr, "Invalid base hash size for SHA384.\n");
1222+
exit(1);
1223+
}
1224+
header_append_tag(header, &header_idx, HDR_IMG_DELTA_BASE_HASH,
1225+
HDR_SHA384_LEN, base_hash);
1226+
} else if (CMD.hash_algo == HASH_SHA3) {
1227+
if (base_hash_sz != HDR_SHA3_384_LEN) {
1228+
fprintf(stderr, "Invalid base hash size for SHA3-384.\n");
1229+
exit(1);
1230+
}
1231+
header_append_tag(header, &header_idx, HDR_IMG_DELTA_BASE_HASH,
1232+
HDR_SHA3_384_LEN, base_hash);
1233+
}
1234+
}
11501235
}
11511236

11521237
/* Add custom TLVs */
@@ -1690,26 +1775,27 @@ static int make_header(uint8_t *pubkey, uint32_t pubkey_sz,
16901775
const char *image_file, const char *outfile)
16911776
{
16921777
return make_header_ex(0, pubkey, pubkey_sz, image_file, outfile, 0, 0, 0, 0,
1693-
NULL, 0);
1778+
NULL, 0, NULL, 0);
16941779
}
16951780

16961781
static int make_header_delta(uint8_t *pubkey, uint32_t pubkey_sz,
16971782
const char *image_file, const char *outfile,
16981783
uint32_t delta_base_version, uint32_t patch_len,
1699-
uint32_t patch_inv_off, uint32_t patch_inv_len)
1784+
uint32_t patch_inv_off, uint32_t patch_inv_len,
1785+
uint8_t *base_hash, uint32_t base_hash_sz)
17001786
{
17011787
return make_header_ex(1, pubkey, pubkey_sz, image_file, outfile,
17021788
delta_base_version, patch_len,
17031789
patch_inv_off, patch_inv_len,
1704-
NULL, 0);
1790+
NULL, 0, base_hash, base_hash_sz);
17051791
}
17061792

17071793
static int make_hybrid_header(uint8_t *pubkey, uint32_t pubkey_sz,
17081794
const char *image_file, const char *outfile,
17091795
const uint8_t *secondary_key, uint32_t secondary_key_sz)
17101796
{
17111797
return make_header_ex(0, pubkey, pubkey_sz, image_file, outfile, 0, 0, 0, 0,
1712-
secondary_key, secondary_key_sz);
1798+
secondary_key, secondary_key_sz, NULL, 0);
17131799
}
17141800

17151801
static int base_diff(const char *f_base, uint8_t *pubkey, uint32_t pubkey_sz, int padding)
@@ -1734,6 +1820,8 @@ static int base_diff(const char *f_base, uint8_t *pubkey, uint32_t pubkey_sz, in
17341820
WB_DIFF_CTX diff_ctx;
17351821
int ret = -1;
17361822
int io_sz;
1823+
uint8_t *base_hash = NULL;
1824+
uint32_t base_hash_sz = 0;
17371825

17381826
/* Get source file size */
17391827
if (stat(f_base, &st) < 0) {
@@ -1790,14 +1878,21 @@ static int base_diff(const char *f_base, uint8_t *pubkey, uint32_t pubkey_sz, in
17901878
delta_base_version = (uint32_t)(retval&0xFFFFFFFF);
17911879
}
17921880
}
1793-
17941881
if (delta_base_version == 0) {
17951882
printf("Could not read firmware version from base file %s\n", f_base);
17961883
goto cleanup;
17971884
} else {
17981885
printf("Delta base version: %u\n", delta_base_version);
17991886
}
18001887

1888+
/* Retrieve the hash digest of the base image */
1889+
if (CMD.hash_algo == HASH_SHA256)
1890+
base_hash_sz = sign_tool_find_header(base + 8, HDR_SHA256, &base_hash);
1891+
else if (CMD.hash_algo == HASH_SHA384)
1892+
base_hash_sz = sign_tool_find_header(base + 8, HDR_SHA384, &base_hash);
1893+
else if (CMD.hash_algo == HASH_SHA3)
1894+
base_hash_sz = sign_tool_find_header(base + 8, HDR_SHA3_384, &base_hash);
1895+
18011896
#if HAVE_MMAP
18021897
/* Open second image file */
18031898
fd2 = open(CMD.output_image_file, O_RDONLY);
@@ -1952,7 +2047,7 @@ static int base_diff(const char *f_base, uint8_t *pubkey, uint32_t pubkey_sz, in
19522047
/* Create delta file, with header, from the resulting patch */
19532048

19542049
ret = make_header_delta(pubkey, pubkey_sz, wolfboot_delta_file, CMD.output_diff_file,
1955-
delta_base_version, patch_sz, patch_inv_off, patch_inv_sz);
2050+
delta_base_version, patch_sz, patch_inv_off, patch_inv_sz, base_hash, base_hash_sz);
19562051

19572052
cleanup:
19582053
/* Unlink output file */
@@ -2398,6 +2493,8 @@ int main(int argc, char** argv)
23982493
else if (strcmp(argv[i], "--delta") == 0) {
23992494
CMD.delta = 1;
24002495
CMD.delta_base_file = argv[++i];
2496+
} else if (strcmp(argv[i], "--no-base-sha") == 0) {
2497+
CMD.no_base_sha = 1;
24012498
}
24022499
else if (strcmp(argv[i], "--no-ts") == 0) {
24032500
CMD.no_ts = 1;

0 commit comments

Comments
 (0)