diff --git a/.gitignore b/.gitignore index 05d4922f20..6c7bad8e78 100644 --- a/.gitignore +++ b/.gitignore @@ -86,6 +86,7 @@ tools/keytools/Debug tools/keytools/Release tools/keytools/otp/otp-keystore-primer + # delta binaries tools/delta/bmdiff tools/delta/bmpatch @@ -176,6 +177,10 @@ tools/unit-tests/unit-hal-otp tools/unit-tests/unit-rot-auth tools/unit-tests/unit-sdhci-response-bits tools/unit-tests/unit-tpm-check-rot-auth +tools/unit-tests/unit-policy-create +tools/unit-tests/unit-sign-encrypted-output +tools/unit-tests/unit-update-flash-delta +tools/unit-tests/unit-update-flash-self-update diff --git a/CMakeLists.txt b/CMakeLists.txt index 65f0e6c70e..88756f5754 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -747,7 +747,20 @@ if(ARCH STREQUAL "AARCH64") endif() +if(NOT DEFINED WOLFBOOT_ORIGIN) + set(WOLFBOOT_ORIGIN ${ARCH_FLASH_OFFSET}) +endif() + +if(NOT DEFINED BOOTLOADER_PARTITION_SIZE) + math(EXPR BOOTLOADER_PARTITION_SIZE + "${WOLFBOOT_PARTITION_BOOT_ADDRESS} - ${ARCH_FLASH_OFFSET}" + OUTPUT_FORMAT HEXADECIMAL) +endif() + list(APPEND WOLFBOOT_DEFS ARCH_FLASH_OFFSET=${ARCH_FLASH_OFFSET}) +list(APPEND WOLFBOOT_DEFS + WOLFBOOT_ORIGIN=${WOLFBOOT_ORIGIN} + BOOTLOADER_PARTITION_SIZE=${BOOTLOADER_PARTITION_SIZE}) if(${WOLFBOOT_TARGET} STREQUAL "x86_64_efi") if(NOT DEFINED GNU_EFI_LIB_PATH) @@ -1139,7 +1152,7 @@ if(TZEN) endif() endif() -target_sources(wolfboothal PRIVATE include/hal.h hal/${WOLFBOOT_TARGET}.c ${WOLFBOOT_FLASH_SOURCES} +target_sources(wolfboothal PRIVATE include/hal.h hal/hal.c hal/${WOLFBOOT_TARGET}.c ${WOLFBOOT_FLASH_SOURCES} ${PARTITION_SOURCE} ${WOLFBOOT_TZ_HAL_SOURCES}) diff --git a/docs/compile.md b/docs/compile.md index cf0d566ac0..8da2b093c5 100644 --- a/docs/compile.md +++ b/docs/compile.md @@ -193,7 +193,9 @@ To circumvent the compile-time checks on the maximum allowed stack size, use `WO Optionally, it is possible to disable the backup copy of the current running firmware upon the installation of the update. This implies that no fall-back mechanism is protecting the target from a faulty firmware installation, but may be useful -in some cases where it is not possible to write on the update partition from the bootloader. +in some cases where it is not possible to write on the update partition from the bootloader. This also removes the +power-fail-safe swap behavior: if power is lost while the update is being copied into the BOOT partition, the original +firmware may already be partially overwritten and the device can be left unrecoverable. The associated compile-time option is `DISABLE_BACKUP=1` diff --git a/hal/hal.c b/hal/hal.c index 4fb243fe51..6c14c63ed9 100644 --- a/hal/hal.c +++ b/hal/hal.c @@ -281,6 +281,13 @@ int hal_flash_test_dualbank(void) #endif /* TEST_FLASH */ +WEAKFUNCTION int RAMFUNCTION hal_flash_protect(haladdr_t address, int len) +{ + (void)address; + (void)len; + return 0; +} + WEAKFUNCTION int hal_uds_derive_key(uint8_t *out, size_t out_len) { (void)out; diff --git a/hal/nrf5340.c b/hal/nrf5340.c index f1772a4b03..2c0a3f6fbc 100644 --- a/hal/nrf5340.c +++ b/hal/nrf5340.c @@ -827,7 +827,7 @@ void hal_init(void) #ifdef __WOLFBOOT /* enable write protection for the region of flash specified */ -int hal_flash_protect(uint32_t start, uint32_t len) +int RAMFUNCTION hal_flash_protect(haladdr_t start, int len) { /* only application core supports SPU */ #ifdef TARGET_nrf5340_app @@ -884,14 +884,6 @@ static void periph_unsecure() void hal_prepare_boot(void) { - /* Write protect bootloader region of flash. - * Not needed in TrustZone configs because the application - * runs in non-secure mode and the bootloader partition is marked as - * secure. */ -#ifndef TZEN - hal_flash_protect(WOLFBOOT_ORIGIN, BOOTLOADER_PARTITION_SIZE); -#endif - if (enableShm) { #ifdef TARGET_nrf5340_net if (doUpdateNet) { diff --git a/hal/skeleton.c b/hal/skeleton.c index bdd3597e05..e6858a1641 100644 --- a/hal/skeleton.c +++ b/hal/skeleton.c @@ -34,6 +34,12 @@ void hal_init(void) void hal_prepare_boot(void) { + /* wolfBoot calls hal_flash_protect() before this hook. + * Override int hal_flash_protect(haladdr_t address, int len) to lock + * the bootloader region on targets that support runtime write + * protection. Return 0 on success or a negative value on failure, and + * use this hook only for any remaining platform-specific handoff work. + */ } #endif @@ -55,4 +61,3 @@ int RAMFUNCTION hal_flash_erase(uint32_t address, int len) { return 0; /* on success. */ } - diff --git a/include/hal.h b/include/hal.h index 5649019b30..fac45ef034 100644 --- a/include/hal.h +++ b/include/hal.h @@ -87,6 +87,11 @@ uint64_t hal_get_timer_us(void); #endif void hal_flash_unlock(void); void hal_flash_lock(void); +/* + * Lock the flash region [address, address + len) against writes. + * Return 0 on success, or a negative value on failure. + */ +int hal_flash_protect(haladdr_t address, int len); void hal_prepare_boot(void); #ifdef DUALBANK_SWAP diff --git a/include/image.h b/include/image.h index c9e7fcba10..8ebb2145ce 100644 --- a/include/image.h +++ b/include/image.h @@ -166,7 +166,7 @@ struct wolfBoot_image { * With ARMORED setup, the flag is redundant, and the information is wrapped in * between canary variables, to mitigate attacks based on memory corruptions. */ -static void __attribute__((noinline)) wolfBoot_image_confirm_signature_ok( +static void NOINLINEFUNCTION wolfBoot_image_confirm_signature_ok( struct wolfBoot_image *img) { img->canary_FEED4567 = 0xFEED4567UL; @@ -176,7 +176,7 @@ static void __attribute__((noinline)) wolfBoot_image_confirm_signature_ok( img->canary_FEED89AB = 0xFEED89ABUL; } -static void __attribute__((noinline)) wolfBoot_image_clear_signature_ok( +static void NOINLINEFUNCTION wolfBoot_image_clear_signature_ok( struct wolfBoot_image *img) { img->canary_FEED4567 = 0xFEED4567UL; @@ -424,7 +424,8 @@ static void __attribute__((noinline)) wolfBoot_image_clear_signature_ok( asm volatile("mov r0, #50":::"r0"); \ asm volatile("mov r0, #50":::"r0"); \ asm volatile("mov r0, #50":::"r0"); \ - compare_res = XMEMCMP(digest, img->sha_hash, WOLFBOOT_SHA_DIGEST_SIZE); \ + compare_res = image_CT_compare(digest, img->sha_hash, \ + WOLFBOOT_SHA_DIGEST_SIZE); \ /* Redundant checks that ensure the function actually returned 0 */ \ asm volatile("cmp r0, #0":::"cc"); \ asm volatile("cmp r0, #0":::"cc"); \ @@ -442,8 +443,9 @@ static void __attribute__((noinline)) wolfBoot_image_clear_signature_ok( asm volatile("cmp r0, #0":::"cc"); \ asm volatile("cmp r0, #0":::"cc"); \ asm volatile("bne hnope"); \ - /* Repeat memcmp call */ \ - compare_res = XMEMCMP(digest, img->sha_hash, WOLFBOOT_SHA_DIGEST_SIZE); \ + /* Repeat comparison call */ \ + compare_res = image_CT_compare(digest, img->sha_hash, \ + WOLFBOOT_SHA_DIGEST_SIZE); \ compare_res; \ /* Redundant checks that ensure the function actually returned 0 */ \ asm volatile("cmp r0, #0":::"cc"); \ @@ -1234,7 +1236,7 @@ static void UNUSEDFUNCTION wolfBoot_image_clear_signature_ok( ret = fn(__VA_ARGS__); #define RSA_VERIFY_HASH(img,digest) \ - if (XMEMCMP(img->sha_hash, digest, WOLFBOOT_SHA_DIGEST_SIZE) == 0) \ + if (image_CT_compare(img->sha_hash, digest, WOLFBOOT_SHA_DIGEST_SIZE) == 0) \ wolfBoot_image_confirm_signature_ok(img); #define PART_SANITY_CHECK(p) \ @@ -1250,6 +1252,8 @@ static void UNUSEDFUNCTION wolfBoot_image_clear_signature_ok( #endif /* Defined in image.c */ +int image_CT_compare(const uint8_t *expected, const uint8_t *actual, + uint32_t len); int wolfBoot_open_image(struct wolfBoot_image *img, uint8_t part); #ifdef EXT_FLASH int wolfBoot_open_image_external(struct wolfBoot_image* img, uint8_t part, uint8_t* addr); diff --git a/include/tpm.h b/include/tpm.h index fb788914c9..50b68adf41 100644 --- a/include/tpm.h +++ b/include/tpm.h @@ -79,6 +79,10 @@ int wolfBoot_load_pubkey(const uint8_t* pubkey_hint, WOLFTPM2_KEY* pubKey, TPM_ALG_ID* pAlg); #endif +#if defined(WOLFBOOT_TPM_KEYSTORE) || defined(WOLFBOOT_TPM_SEAL) +int wolfBoot_constant_compare(const uint8_t* a, const uint8_t* b, uint32_t len); +#endif + #ifdef WOLFBOOT_TPM_KEYSTORE int wolfBoot_check_rot(int key_slot, uint8_t* pubkey_hint); #endif diff --git a/include/wolfboot/wolfboot.h b/include/wolfboot/wolfboot.h index a6e79d708d..6075de6f46 100644 --- a/include/wolfboot/wolfboot.h +++ b/include/wolfboot/wolfboot.h @@ -83,6 +83,20 @@ extern "C" { # endif #endif +#ifndef NOINLINEFUNCTION +# if defined(__has_attribute) +# if __has_attribute(noinline) +# define NOINLINEFUNCTION __attribute__((noinline)) +# else +# define NOINLINEFUNCTION +# endif +# elif defined(__GNUC__) || defined(__CC_ARM) +# define NOINLINEFUNCTION __attribute__((noinline)) +# else +# define NOINLINEFUNCTION +# endif +#endif + /* Helpers for memory alignment */ #ifndef XALIGNED @@ -185,6 +199,15 @@ extern "C" { #endif #endif /* WOLFBOOT_SELF_HEADER */ +#if defined(WOLFBOOT_SKIP_BOOT_VERIFY) && !defined(WOLFBOOT_SELF_HEADER) +#error "WOLFBOOT_SKIP_BOOT_VERIFY requires WOLFBOOT_SELF_HEADER" +#endif + +#if defined(WOLFBOOT_SKIP_BOOT_VERIFY) && \ + !defined(WOLFBOOT_SELF_UPDATE_MONOLITHIC) +#error "WOLFBOOT_SKIP_BOOT_VERIFY requires WOLFBOOT_SELF_UPDATE_MONOLITHIC" +#endif + #ifdef BIG_ENDIAN_ORDER # define WOLFBOOT_MAGIC 0x574F4C46 /* WOLF */ # define WOLFBOOT_MAGIC_TRAIL 0x424F4F54 /* BOOT */ diff --git a/options.mk b/options.mk index ff8df2c4ad..f9307e5b29 100644 --- a/options.mk +++ b/options.mk @@ -90,6 +90,9 @@ endif ## Monolithic self-update: erase covers fw_size so the payload can span ## the bootloader region into the contiguous boot partition. +ifeq ($(WOLFBOOT_SELF_UPDATE_MONOLITHIC),1) + SELF_UPDATE_MONOLITHIC:=1 +endif ifeq ($(SELF_UPDATE_MONOLITHIC),1) CFLAGS+=-DWOLFBOOT_SELF_UPDATE_MONOLITHIC endif @@ -710,6 +713,12 @@ ifeq ($(ALLOW_DOWNGRADE),1) endif ifeq ($(WOLFBOOT_SKIP_BOOT_VERIFY),1) + ifneq ($(WOLFBOOT_SELF_HEADER),1) + $(error WOLFBOOT_SKIP_BOOT_VERIFY=1 requires WOLFBOOT_SELF_HEADER=1) + endif + ifneq ($(SELF_UPDATE_MONOLITHIC),1) + $(error WOLFBOOT_SKIP_BOOT_VERIFY=1 requires WOLFBOOT_SELF_UPDATE_MONOLITHIC (set SELF_UPDATE_MONOLITHIC=1)) + endif CFLAGS+=-D"WOLFBOOT_SKIP_BOOT_VERIFY" endif @@ -718,6 +727,7 @@ ifeq ($(NVM_FLASH_WRITEONCE),1) endif ifeq ($(DISABLE_BACKUP),1) + $(warning DISABLE_BACKUP=1 disables power-fail-safe updates; losing power during an update can leave BOOT partially written and unrecoverable) CFLAGS+= -D"DISABLE_BACKUP" endif diff --git a/src/delta.c b/src/delta.c index c1f2e3be12..fd7674ee3e 100644 --- a/src/delta.c +++ b/src/delta.c @@ -40,6 +40,7 @@ struct BLOCK_HDR_PACKED block_hdr { }; #define BLOCK_HDR_SIZE (sizeof (struct block_hdr)) +#define BLOCK_OFF_MAX 0xFFFFFFU #if defined(EXT_ENCRYPTED) && defined(__WOLFBOOT) #include "image.h" @@ -300,6 +301,8 @@ int wb_diff(WB_DIFF_CTX *ctx, uint8_t *patch, uint32_t len) */ match_len = BLOCK_HDR_SIZE; blk_start = pa - ctx->src_a; + if (blk_start > BLOCK_OFF_MAX) + return -1; b_start = ctx->off_b; pa+= BLOCK_HDR_SIZE; ctx->off_b += BLOCK_HDR_SIZE; @@ -362,6 +365,8 @@ int wb_diff(WB_DIFF_CTX *ctx, uint8_t *patch, uint32_t len) */ match_len = BLOCK_HDR_SIZE; blk_start = pb - ctx->src_b; + if (blk_start > BLOCK_OFF_MAX) + return -1; pb+= BLOCK_HDR_SIZE; ctx->off_b += BLOCK_HDR_SIZE; while ((pb < pb_limit) && diff --git a/src/image.c b/src/image.c index d5ff5f3e1a..cf434066f3 100644 --- a/src/image.c +++ b/src/image.c @@ -58,8 +58,8 @@ /* Globals */ static uint8_t digest[WOLFBOOT_SHA_DIGEST_SIZE] XALIGNED(4); -static int image_CT_compare(const uint8_t *expected, const uint8_t *actual, - uint32_t len) +int NOINLINEFUNCTION image_CT_compare( + const uint8_t *expected, const uint8_t *actual, uint32_t len) { uint8_t diff = 0; uint32_t i; @@ -68,7 +68,7 @@ static int image_CT_compare(const uint8_t *expected, const uint8_t *actual, diff |= expected[i] ^ actual[i]; } - return diff == 0; + return diff; } #if defined(WOLFBOOT_CERT_CHAIN_VERIFY) && \ @@ -1551,7 +1551,7 @@ int wolfBoot_verify_integrity(struct wolfBoot_image *img) return -1; if (image_hash(img, digest) != 0) return -1; - if (!image_CT_compare(digest, stored_sha, stored_sha_len)) + if (image_CT_compare(digest, stored_sha, stored_sha_len) != 0) return -1; img->sha_ok = 1; img->sha_hash = stored_sha; @@ -1990,7 +1990,7 @@ int wolfBoot_check_flash_image_elf(uint8_t part, unsigned long* entry_out) /* Finalize SHA calculation */ final_hash(&ctx, calc_digest); - if (!image_CT_compare(exp_digest, calc_digest, WOLFBOOT_SHA_DIGEST_SIZE)) { + if (image_CT_compare(exp_digest, calc_digest, WOLFBOOT_SHA_DIGEST_SIZE) != 0) { wolfBoot_printf("ELF: [CHECK] SHA verification FAILED\n"); wolfBoot_printf( "ELF: [CHECK] Expected %02x%02x%02x%02x%02x%02x%02x%02x\n", diff --git a/src/libwolfboot.c b/src/libwolfboot.c index 658cf6aeea..8d52712ae9 100644 --- a/src/libwolfboot.c +++ b/src/libwolfboot.c @@ -62,6 +62,19 @@ #include /* for size_t */ +#ifdef EXT_ENCRYPTED +static int encrypt_key_is_erased(const uint8_t *key, uint32_t len) +{ + uint8_t diff = 0; + uint32_t i; + + for (i = 0; i < len; i++) + diff |= key[i] ^ FLASH_BYTE_ERASED; + + return diff == 0; +} +#endif + #if defined(EXT_ENCRYPTED) && (defined(__WOLFBOOT) || defined(UNIT_TEST) || defined(MMU)) #include "encrypt.h" static int encrypt_initialized = 0; @@ -70,6 +83,20 @@ static uint8_t encrypt_iv_nonce[ENCRYPT_NONCE_SIZE] XALIGNED(4); static uint32_t encrypt_iv_offset = 0; static int fallback_iv_forced = 0; +static int encrypt_key_is_valid(const uint8_t *key, uint32_t len) +{ + uint8_t has_one = 0; + uint8_t has_zero = 0; + uint32_t i; + + for (i = 0; i < len; i++) { + has_one |= key[i]; + has_zero |= (uint8_t)~key[i]; + } + + return (has_one != 0) && (has_zero != 0); +} + #define FALLBACK_IV_OFFSET 0x00100000U #if !defined(XMEMSET) #include @@ -1549,7 +1576,7 @@ static int RAMFUNCTION hal_set_key(const uint8_t *k, const uint8_t *nonce) /* erase the old key */ ret = hal_flash_erase(addr_align, WOLFBOOT_SECTOR_SIZE); if (ret != 0) - return ret; + goto exit_lock; #endif /* Populate key + nonce in the cache */ @@ -1574,7 +1601,7 @@ static int RAMFUNCTION hal_set_key(const uint8_t *k, const uint8_t *nonce) ret = hal_flash_write(addr_align, ENCRYPT_CACHE, WOLFBOOT_SECTOR_SIZE); #ifdef NVM_FLASH_WRITEONCE if (ret != 0) - return ret; + goto exit_lock; /* Erasing original sector "sel_sec", * same one returned from by nvm_select. */ @@ -1582,6 +1609,7 @@ static int RAMFUNCTION hal_set_key(const uint8_t *k, const uint8_t *nonce) addr_align -= (sel_sec * WOLFBOOT_SECTOR_SIZE); ret = hal_flash_erase(addr_align, WOLFBOOT_SECTOR_SIZE); #endif +exit_lock: hal_flash_lock(); return ret; #endif @@ -1596,14 +1624,13 @@ static int RAMFUNCTION hal_set_key(const uint8_t *k, const uint8_t *nonce) * @param key Pointer to the encryption key. * @param nonce Pointer to the encryption nonce. * - * @return 0 if successful. + * @return 0 on success, or the underlying flash error code on failure. * */ int RAMFUNCTION wolfBoot_set_encrypt_key(const uint8_t *key, const uint8_t *nonce) { - hal_set_key(key, nonce); - return 0; + return hal_set_key(key, nonce); } #ifndef UNIT_TEST @@ -1649,7 +1676,7 @@ int RAMFUNCTION wolfBoot_get_encrypt_key(uint8_t *k, uint8_t *nonce) * This function erases the encryption key and nonce, resetting them to all 0xFF * bytes.It ensures that the key and nonce cannot be accessed after erasure. * - * @return 0 if successful. + * @return 0 on success, or the underlying flash error code on failure. * */ int RAMFUNCTION wolfBoot_erase_encrypt_key(void) @@ -1659,6 +1686,7 @@ int RAMFUNCTION wolfBoot_erase_encrypt_key(void) #elif defined(MMU) ForceZero(ENCRYPT_KEY, ENCRYPT_KEY_SIZE + ENCRYPT_NONCE_SIZE); #else + int ret = 0; uint8_t ff[ENCRYPT_KEY_SIZE + ENCRYPT_NONCE_SIZE]; uint8_t *mem = (uint8_t *)ENCRYPT_TMP_SECRET_OFFSET + WOLFBOOT_PARTITION_BOOT_ADDRESS; @@ -1667,8 +1695,9 @@ int RAMFUNCTION wolfBoot_erase_encrypt_key(void) mem -= (sel_sec * WOLFBOOT_SECTOR_SIZE); #endif XMEMSET(ff, FLASH_BYTE_ERASED, ENCRYPT_KEY_SIZE + ENCRYPT_NONCE_SIZE); - if (XMEMCMP(mem, ff, ENCRYPT_KEY_SIZE + ENCRYPT_NONCE_SIZE) != 0) - hal_set_key(ff, ff + ENCRYPT_KEY_SIZE); + if (!encrypt_key_is_erased(mem, ENCRYPT_KEY_SIZE + ENCRYPT_NONCE_SIZE)) + ret = hal_set_key(ff, ff + ENCRYPT_KEY_SIZE); + return ret; #endif return 0; } @@ -1683,6 +1712,7 @@ ChaCha chacha; int RAMFUNCTION chacha_init(void) { + int ret = 0; #ifdef CUSTOM_ENCRYPT_KEY uint8_t stored_nonce[ENCRYPT_NONCE_SIZE]; uint8_t key[ENCRYPT_KEY_SIZE]; @@ -1690,12 +1720,10 @@ int RAMFUNCTION chacha_init(void) const uint8_t* stored_nonce; uint8_t *key; #endif - uint8_t ff[ENCRYPT_KEY_SIZE]; - #ifdef CUSTOM_ENCRYPT_KEY - int ret = wolfBoot_get_encrypt_key(key, stored_nonce); + ret = wolfBoot_get_encrypt_key(key, stored_nonce); if (ret != 0) - return ret; + goto exit; #else #if defined(MMU) || defined(UNIT_TEST) key = ENCRYPT_KEY; @@ -1711,19 +1739,21 @@ int RAMFUNCTION chacha_init(void) XMEMSET(&chacha, 0, sizeof(chacha)); - /* 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, 0x00, ENCRYPT_KEY_SIZE); - if (XMEMCMP(key, ff, ENCRYPT_KEY_SIZE) == 0) - return -1; + if (!encrypt_key_is_valid(key, ENCRYPT_KEY_SIZE)) { + ret = -1; + goto exit; + } XMEMCPY(encrypt_iv_nonce, stored_nonce, ENCRYPT_NONCE_SIZE); wc_Chacha_SetKey(&chacha, key, ENCRYPT_KEY_SIZE); encrypt_initialized = 1; - return 0; +exit: +#ifdef CUSTOM_ENCRYPT_KEY + ForceZero(key, sizeof(key)); + ForceZero(stored_nonce, sizeof(stored_nonce)); +#endif + return ret; } #elif defined(ENCRYPT_WITH_AES128) || defined(ENCRYPT_WITH_AES256) @@ -1742,6 +1772,7 @@ Aes aes_dec, aes_enc; int aes_init(void) { int devId = INVALID_DEVID; + int ret = 0; #if defined(CUSTOM_ENCRYPT_KEY) && !defined(WOLFBOOT_RENESAS_TSIP) uint8_t stored_nonce[ENCRYPT_NONCE_SIZE]; uint8_t key[ENCRYPT_KEY_SIZE]; @@ -1749,10 +1780,7 @@ int aes_init(void) uint8_t *stored_nonce; uint8_t *key; #endif - uint8_t ff[ENCRYPT_KEY_SIZE]; - #ifdef WOLFBOOT_RENESAS_TSIP - int ret; wrap_enc_key_t* enc_key; devId = RENESAS_DEVID + 1; enc_key =(wrap_enc_key_t*)RENESAS_TSIP_INSTALLEDENCKEY_ADDR; @@ -1760,7 +1788,9 @@ int aes_init(void) stored_nonce = enc_key->initial_vector; wolfCrypt_Init(); /* required to setup the crypto callback defaults */ #elif defined(CUSTOM_ENCRYPT_KEY) - wolfBoot_get_encrypt_key(key, stored_nonce); + ret = wolfBoot_get_encrypt_key(key, stored_nonce); + if (ret != 0) + goto exit; #else #if defined(MMU) || defined(UNIT_TEST) key = ENCRYPT_KEY; @@ -1779,13 +1809,10 @@ int aes_init(void) wc_AesInit(&aes_enc, NULL, devId); wc_AesInit(&aes_dec, NULL, devId); - /* 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, 0x00, ENCRYPT_KEY_SIZE); - if (XMEMCMP(key, ff, ENCRYPT_KEY_SIZE) == 0) - return -1; + if (!encrypt_key_is_valid(key, ENCRYPT_KEY_SIZE)) { + ret = -1; + goto exit; + } #ifdef WOLFBOOT_RENESAS_TSIP /* Unwrap key and get key index */ @@ -1797,7 +1824,8 @@ int aes_init(void) enc_key->encrypted_user_key, &aes_enc.ctx.tsip_keyIdx); #endif if (ret != TSIP_SUCCESS) { - return -1; + ret = -1; + goto exit; } /* set encryption key size */ aes_enc.ctx.keySize = ENCRYPT_KEY_SIZE; @@ -1818,7 +1846,12 @@ int aes_init(void) XMEMCPY(encrypt_iv_nonce, stored_nonce, ENCRYPT_NONCE_SIZE); encrypt_initialized = 1; - return 0; +exit: +#if defined(CUSTOM_ENCRYPT_KEY) && !defined(WOLFBOOT_RENESAS_TSIP) + ForceZero(key, sizeof(key)); + ForceZero(stored_nonce, sizeof(stored_nonce)); +#endif + return ret; } /** diff --git a/src/string.c b/src/string.c index 7f65ebda18..cc94e4e9a8 100644 --- a/src/string.c +++ b/src/string.c @@ -27,9 +27,6 @@ #endif #include -#if defined(_RENESAS_RA_) -#include -#endif #if !defined(TARGET_library) && defined(__STDC_HOSTED__) && __STDC_HOSTED__ \ && !defined(__CCRX__) #include @@ -288,15 +285,30 @@ void RAMFUNCTION *memcpy(void *dst, const void *src, size_t n) !defined(__CCRX__) void *memmove(void *dst, const void *src, size_t n) { - int i; + size_t i; if (dst == src) return dst; if (src < dst) { const char *s = (const char *)src; char *d = (char *)dst; - for (i = n - 1; i >= 0; i--) { - d[i] = s[i]; + size_t aligned_n = 0; +#ifdef FAST_MEMCPY + if (((size_t)dst & (sizeof(unsigned long)-1)) == 0 && + ((size_t)src & (sizeof(unsigned long)-1)) == 0) + { + aligned_n = n & ~(sizeof(unsigned long) - 1); + } +#endif + for (i = n; i > aligned_n; i--) { + d[i - 1] = s[i - 1]; + } +#ifdef FAST_MEMCPY + while (aligned_n > 0) { + aligned_n -= sizeof(unsigned long); + *(unsigned long*)(d + aligned_n) = + *(const unsigned long*)(s + aligned_n); } +#endif return dst; } else { return memcpy(dst, src, n); diff --git a/src/tpm.c b/src/tpm.c index 682ab51c60..23b3e05dd3 100644 --- a/src/tpm.c +++ b/src/tpm.c @@ -44,7 +44,7 @@ WOLFTPM2_KEY wolftpm_srk; #endif #if defined(WOLFBOOT_TPM_SEAL) || defined(WOLFBOOT_TPM_KEYSTORE) -static int wolfBoot_constant_compare(const uint8_t* a, const uint8_t* b, +int wolfBoot_constant_compare(const uint8_t* a, const uint8_t* b, uint32_t len) { uint32_t i; diff --git a/src/update_disk.c b/src/update_disk.c index 6d1b7d0514..7e52feadd7 100644 --- a/src/update_disk.c +++ b/src/update_disk.c @@ -547,6 +547,12 @@ void RAMFUNCTION wolfBoot_start(void) (void)hal_hsm_disconnect(); #elif defined(WOLFBOOT_ENABLE_WOLFHSM_SERVER) (void)hal_hsm_server_cleanup(); +#endif +#ifndef TZEN + if (hal_flash_protect(WOLFBOOT_ORIGIN, BOOTLOADER_PARTITION_SIZE) < 0) { + wolfBoot_printf("Error protecting bootloader flash region\r\n"); + wolfBoot_panic(); + } #endif hal_prepare_boot(); diff --git a/src/update_flash.c b/src/update_flash.c index 2ead2448be..10b9196e1c 100644 --- a/src/update_flash.c +++ b/src/update_flash.c @@ -33,8 +33,31 @@ #include "delta.h" #include "printf.h" +static void wolfBoot_zeroize(void *ptr, size_t len) +{ + volatile uint8_t *p = (volatile uint8_t *)ptr; + + while (len-- > 0) { + *p++ = 0; + } +} + +static int wolfBoot_local_constant_compare(const uint8_t* a, const uint8_t* b, + uint32_t len) +{ + uint32_t i; + uint8_t diff = 0; + + for (i = 0; i < len; i++) { + diff |= a[i] ^ b[i]; + } + + return diff; +} + #ifdef EXT_ENCRYPTED int wolfBoot_force_fallback_iv(int enable); +#include "encrypt.h" #endif #ifdef WOLFBOOT_TPM #include "tpm.h" @@ -43,19 +66,6 @@ int wolfBoot_force_fallback_iv(int enable); int WP11_Library_Init(void); #endif -#ifdef EXT_ENCRYPTED -#include "encrypt.h" - -static void wolfBoot_zeroize(void *ptr, size_t len) -{ - volatile uint8_t *p = (volatile uint8_t *)ptr; - - while (len-- > 0) { - *p++ = 0; - } -} -#endif /* EXT_ENCRYPTED */ - #ifdef MMU #error "MMU is not yet supported for update_flash.c, please consider update_ram.c instead" #endif @@ -437,6 +447,7 @@ static int RAMFUNCTION wolfBoot_swap_and_final_erase(int resume) uintptr_t tmpBootPos = WOLFBOOT_PARTITION_SIZE - eraseLen - WOLFBOOT_SECTOR_SIZE; uint32_t tmpBuffer[TRAILER_OFFSET_WORDS + 1]; + int ret = 0; /* open partitions (ignore failure) */ wolfBoot_open_image(boot, PART_BOOT); @@ -485,8 +496,16 @@ static int RAMFUNCTION wolfBoot_swap_and_final_erase(int resume) wolfBoot_printf("In function wolfBoot_final_swap: swapDone = %d\n", swapDone); if (swapDone == 0) { /* For encrypted images: Get the encryption key and IV */ - wolfBoot_get_encrypt_key((uint8_t*)tmpBuffer, - (uint8_t*)&tmpBuffer[ENCRYPT_KEY_SIZE/sizeof(uint32_t)]); + ret = wolfBoot_get_encrypt_key((uint8_t*)tmpBuffer, + (uint8_t*)&tmpBuffer[ENCRYPT_KEY_SIZE / sizeof(uint32_t)]); + if (ret != 0) { +#ifdef EXT_FLASH + ext_flash_lock(); +#endif + hal_flash_lock(); + wolfBoot_zeroize(tmpBuffer, sizeof(tmpBuffer)); + return ret; + } /* Set the magic trailer in the buffer and write it to the staging sector */ tmpBuffer[TRAILER_OFFSET_WORDS] = WOLFBOOT_MAGIC_TRAIL; @@ -499,8 +518,16 @@ static int RAMFUNCTION wolfBoot_swap_and_final_erase(int resume) #ifdef EXT_ENCRYPTED /* Initialize encryption with the saved key */ - wolfBoot_set_encrypt_key((uint8_t*)tmpBuffer, - (uint8_t*)&tmpBuffer[ENCRYPT_KEY_SIZE/sizeof(uint32_t)]); + ret = wolfBoot_set_encrypt_key((uint8_t*)tmpBuffer, + (uint8_t*)&tmpBuffer[ENCRYPT_KEY_SIZE / sizeof(uint32_t)]); + if (ret != 0) { +#ifdef EXT_FLASH + ext_flash_lock(); +#endif + hal_flash_lock(); + wolfBoot_zeroize(tmpBuffer, sizeof(tmpBuffer)); + return ret; + } /* wolfBoot_set_encrypt_key calls hal_flash_unlock, need to unlock again */ hal_flash_unlock(); #endif @@ -525,6 +552,8 @@ static int RAMFUNCTION wolfBoot_swap_and_final_erase(int resume) #endif hal_flash_lock(); + wolfBoot_zeroize(tmpBuffer, sizeof(tmpBuffer)); + (void)ret; return 0; } #ifdef __CCRX__ @@ -562,8 +591,13 @@ static int wolfBoot_delta_update(struct wolfBoot_image *boot, uint8_t *base_hash; if (boot->fw_size == 0) { - /* Resume after powerfail can leave boot header erased; bound by partition size. */ - boot->fw_size = WOLFBOOT_PARTITION_SIZE - IMAGE_HEADER_SIZE; + if ((boot->hdr != NULL) && + (*((uint32_t *)boot->hdr) != WOLFBOOT_MAGIC)) { + /* Resume after powerfail can leave the boot header erased. */ + boot->fw_size = WOLFBOOT_PARTITION_SIZE - IMAGE_HEADER_SIZE; + } else { + return -1; + } } /* Use biggest size for the swap */ @@ -633,7 +667,7 @@ static int wolfBoot_delta_update(struct wolfBoot_image *boot, cur_v, delta_base_v); ret = -1; } else if (!resume && delta_base_hash && - memcmp(base_hash, delta_base_hash, base_hash_sz) != 0) { + image_CT_compare(base_hash, delta_base_hash, base_hash_sz) != 0) { /* Wrong base image digest, cannot apply delta patch */ wolfBoot_printf("Delta Base hash mismatch\n"); ret = -1; @@ -781,7 +815,7 @@ static int wolfBoot_delta_update(struct wolfBoot_image *boot, #ifdef __CCRX__ #pragma section FRAM #endif -static int wolfBoot_get_total_size(struct wolfBoot_image* boot, +static uint32_t wolfBoot_get_total_size(struct wolfBoot_image* boot, struct wolfBoot_image* update) { uint32_t total_size = 0; @@ -799,6 +833,7 @@ static int RAMFUNCTION wolfBoot_update(int fallback_allowed) uint32_t total_size = 0; const uint32_t sector_size = WOLFBOOT_SECTOR_SIZE; uint32_t sector = 0; + int ret = 0; /* we need to pre-set flag to SECT_FLAG_NEW in case magic hasn't been set * on the update partition as part of the delta update direction check. if * magic has not been set flag will have an un-determined value when we go @@ -868,7 +903,7 @@ static int RAMFUNCTION wolfBoot_update(int fallback_allowed) /* get total size */ total_size = wolfBoot_get_total_size(&boot, &update); if (total_size <= IMAGE_HEADER_SIZE) { - wolfBoot_printf("Image total size %u too large!\n", total_size); + wolfBoot_printf("Image total size %u invalid!\n", total_size); return -1; } /* In case this is a new update, do the required @@ -1119,7 +1154,9 @@ static int RAMFUNCTION wolfBoot_update(int fallback_allowed) #if !defined(CUSTOM_PARTITION_TRAILER) /* start re-entrant final erase, return code is only for resumption in * wolfBoot_start */ - wolfBoot_swap_and_final_erase(0); + ret = wolfBoot_swap_and_final_erase(0); + if (ret != 0) + return ret; #ifndef DISABLE_BACKUP if (rollback_needed) { hal_flash_unlock(); @@ -1196,14 +1233,18 @@ static int RAMFUNCTION wolfBoot_update(int fallback_allowed) /* Save the encryption key after swapping */ #ifdef EXT_ENCRYPTED - wolfBoot_set_encrypt_key(key, nonce); + ret = wolfBoot_set_encrypt_key(key, nonce); + wolfBoot_zeroize(key, sizeof(key)); + wolfBoot_zeroize(nonce, sizeof(nonce)); + if (ret != 0) + return ret; #endif #endif /* DISABLE_BACKUP */ #ifdef EXT_ENCRYPTED /* Make sure we leave the global IV offset in its normal state. */ wolfBoot_enable_fallback_iv(0); #endif - return 0; + return ret; } #ifdef __CCRX__ #pragma section @@ -1265,7 +1306,8 @@ int wolfBoot_unlock_disk(void) secretCheck, &secretCheckSz); if (ret == 0) { if (secretSz != secretCheckSz || - memcmp(secret, secretCheck, secretSz) != 0) + wolfBoot_local_constant_compare(secret, secretCheck, + (uint32_t)secretSz) != 0) { wolfBoot_printf("secret check mismatch!\n"); ret = -1; @@ -1475,6 +1517,12 @@ void RAMFUNCTION wolfBoot_start(void) (void)hal_hsm_server_cleanup(); #endif +#ifndef TZEN + if (hal_flash_protect(WOLFBOOT_ORIGIN, BOOTLOADER_PARTITION_SIZE) < 0) { + wolfBoot_printf("Error protecting bootloader flash region\n"); + wolfBoot_panic(); + } +#endif hal_prepare_boot(); #ifdef WOLFBOOT_HOOK_BOOT diff --git a/src/update_flash_hwswap.c b/src/update_flash_hwswap.c index ef1db99514..07c95862b7 100644 --- a/src/update_flash_hwswap.c +++ b/src/update_flash_hwswap.c @@ -105,6 +105,10 @@ void RAMFUNCTION wolfBoot_start(void) (void)hal_hsm_disconnect(); #elif defined(WOLFBOOT_ENABLE_WOLFHSM_SERVER) (void)hal_hsm_server_cleanup(); +#endif +#ifndef TZEN + if (hal_flash_protect(WOLFBOOT_ORIGIN, BOOTLOADER_PARTITION_SIZE) < 0) + boot_panic(); #endif hal_prepare_boot(); #ifdef WOLFBOOT_HOOK_BOOT diff --git a/src/update_ram.c b/src/update_ram.c index 7f2beb5a73..b2f0d77212 100644 --- a/src/update_ram.c +++ b/src/update_ram.c @@ -390,6 +390,12 @@ void RAMFUNCTION wolfBoot_start(void) (void)hal_hsm_server_cleanup(); #endif +#ifndef TZEN + if (hal_flash_protect(WOLFBOOT_ORIGIN, BOOTLOADER_PARTITION_SIZE) < 0) { + wolfBoot_printf("Error protecting bootloader flash region\n"); + wolfBoot_panic(); + } +#endif hal_prepare_boot(); #ifdef WOLFBOOT_HOOK_BOOT diff --git a/src/x86/ahci.c b/src/x86/ahci.c index ff5a7a42a2..45d97c4207 100644 --- a/src/x86/ahci.c +++ b/src/x86/ahci.c @@ -109,6 +109,19 @@ __attribute__((aligned(HBA_TBL_ALIGN))); #define AHCI_DEBUG_PRINTF(...) do {} while(0) #endif /* DEBUG_AHCI */ +static int wolfBoot_local_constant_compare(const uint8_t* a, const uint8_t* b, + uint32_t len) +{ + uint32_t i; + uint8_t diff = 0; + + for (i = 0; i < len; i++) { + diff |= a[i] ^ b[i]; + } + + return diff; +} + /** * @brief Sets the AHCI Base Address Register (ABAR) for the given device. * @@ -296,7 +309,8 @@ static int sata_create_and_seal_unlock_secret(const uint8_t *pubkey_hint, secret_check, &secret_check_sz); if (ret == 0) { if (*secret_size != secret_check_sz || - memcmp(secret, secret_check, secret_check_sz) != 0) + wolfBoot_local_constant_compare(secret, secret_check, + (uint32_t)secret_check_sz) != 0) { wolfBoot_printf("secret check mismatch!\n"); ret = -1; diff --git a/tools/delta/bmdiff.c b/tools/delta/bmdiff.c index 4ae336da5a..adeafb3d9b 100644 --- a/tools/delta/bmdiff.c +++ b/tools/delta/bmdiff.c @@ -97,6 +97,10 @@ int main(int argc, char *argv[]) exit(3); } len2 = st.st_size; + if (len2 > MAX_SRC_SIZE) { + printf("%s: file too large\n", argv[2]); + exit(3); + } buffer = mmap(NULL, len2, PROT_READ, MAP_SHARED, fd2, 0); if (buffer == (void *)(-1)) { perror("mmap"); diff --git a/tools/keytools/keygen.c b/tools/keytools/keygen.c index 96104fa962..7e4515cad5 100644 --- a/tools/keytools/keygen.c +++ b/tools/keytools/keygen.c @@ -539,39 +539,51 @@ static void keygen_rsa(const char *keyfile, int kbits, uint32_t id_mask) uint8_t priv_der[4096], pub_der[2048]; int privlen, publen; FILE *fpriv; + int ret = 0; + int exit_code = 0; + int rsa_init = 0; - if (wc_InitRsaKey(&k, NULL) != 0) { + ret = wc_InitRsaKey(&k, NULL); + if (ret != 0) { fprintf(stderr, "Unable to initialize RSA%d key\n", kbits); exit(1); } + rsa_init = 1; - if (wc_MakeRsaKey(&k, kbits, 65537, &rng) != 0) { + ret = wc_MakeRsaKey(&k, kbits, 65537, &rng); + if (ret != 0) { fprintf(stderr, "Unable to create RSA%d key\n", kbits); - exit(1); + exit_code = 1; + goto cleanup; } privlen = wc_RsaKeyToDer(&k, priv_der, kbits); if (privlen <= 0) { fprintf(stderr, "Unable to export private key to DER\n"); - exit(2); + exit_code = 2; + goto cleanup; } publen = wc_RsaKeyToPublicDer(&k, pub_der, kbits); if (publen <= 0) { fprintf(stderr, "Unable to export public key\n"); - exit(3); + exit_code = 3; + goto cleanup; } printf("RSA public key len: %d bytes\n", publen); fpriv = fopen(keyfile, "wb"); if (fpriv == NULL) { fprintf(stderr, "Unable to open file '%s' for writing: %s", keyfile, strerror(errno)); - exit(4); + exit_code = 4; + goto cleanup; } fwrite(priv_der, privlen, 1, fpriv); fclose(fpriv); + fpriv = NULL; if (exportPubKey) { if (export_pubkey_file(keyfile, pub_der, publen) != 0) { fprintf(stderr, "Unable to export public key to file\n"); - exit(5); + exit_code = 5; + goto cleanup; } } @@ -581,6 +593,13 @@ static void keygen_rsa(const char *keyfile, int kbits, uint32_t id_mask) keystore_add(AUTH_KEY_RSA3072, pub_der, publen, keyfile, id_mask); else if (kbits == 4096) keystore_add(AUTH_KEY_RSA4096, pub_der, publen, keyfile, id_mask); + +cleanup: + wc_ForceZero(priv_der, sizeof(priv_der)); + if (rsa_init) + wc_FreeRsaKey(&k); + if (exit_code != 0) + exit(exit_code); } #define MAX_ECC_KEY_SIZE 66 @@ -597,19 +616,24 @@ static void keygen_ecc(const char *priv_fname, uint16_t ecc_key_size, uint8_t priv_der[ECC_BUFSIZE]; int privlen; uint8_t k_buffer[2 * MAX_ECC_KEY_SIZE]; - FILE *fpriv; + FILE *fpriv = NULL; + int exit_code = 0; + int key_init = 0; wc_ecc_init(&k); + key_init = 1; if (wc_ecc_make_key(&rng, ecc_key_size, &k) != 0) { fprintf(stderr, "Unable to create ecc key\n"); - exit(1); + exit_code = 1; + goto cleanup; } ret = wc_EccKeyToDer(&k, priv_der, (word32)sizeof(priv_der)); if (ret <= 0) { fprintf(stderr, "Unable to export private key to DER\n"); - exit(2); + exit_code = 2; + goto cleanup; } privlen = ret; ret = 0; @@ -617,20 +641,23 @@ static void keygen_ecc(const char *priv_fname, uint16_t ecc_key_size, if (wc_ecc_export_private_raw(&k, Qx, &qxsize, Qy, &qysize, d, &dsize) != 0) { fprintf(stderr, "Unable to export private key to raw\n"); - exit(2); + exit_code = 2; + goto cleanup; } if (wc_ecc_export_public_raw(&k, Qx, &qxsize, Qy, &qysize ) != 0) { fprintf(stderr, "Unable to export public key\n"); - exit(3); + exit_code = 3; + goto cleanup; } fpriv = fopen(priv_fname, "wb"); if (fpriv == NULL) { fprintf(stderr, "Unable to open file '%s' for writing: %s", priv_fname, strerror(errno)); - exit(3); + exit_code = 3; + goto cleanup; } if (saveAsDer) { @@ -644,11 +671,12 @@ static void keygen_ecc(const char *priv_fname, uint16_t ecc_key_size, fwrite(d, dsize, 1, fpriv); } fclose(fpriv); + fpriv = NULL; if (exportPubKey) { int pubOutLen; /* Reuse priv_der buffer for public key */ - memset(priv_der, 0, sizeof(priv_der)); + wc_ForceZero(priv_der, sizeof(priv_der)); if (saveAsDer) { /* If you want public key also exported as a DER file and not as RAW @@ -665,16 +693,27 @@ static void keygen_ecc(const char *priv_fname, uint16_t ecc_key_size, if (ret < 0) { fprintf(stderr, "Unable to export public key to DER, ret=%d\n", ret); - exit(4); + exit_code = 4; + goto cleanup; } if (export_pubkey_file(priv_fname, priv_der, pubOutLen) != 0) { fprintf(stderr, "Unable to export public key to file\n"); - exit(4); + exit_code = 4; + goto cleanup; } ret = 0; } - wc_ecc_free(&k); +cleanup: + if (fpriv != NULL) + fclose(fpriv); + if (key_init) + wc_ecc_free(&k); + wc_ForceZero(d, sizeof(d)); + wc_ForceZero(priv_der, sizeof(priv_der)); + + if (exit_code != 0) + exit(exit_code); memcpy(k_buffer, Qx, ecc_key_size); memcpy(k_buffer + ecc_key_size, Qy, ecc_key_size); @@ -694,22 +733,37 @@ static void keygen_ed25519(const char *privkey, uint32_t id_mask) uint8_t priv[32], pub[32]; FILE *fpriv; uint32_t outlen = ED25519_KEY_SIZE; + int exit_code = 0; + int key_init = 0; + + key_init = wc_ed25519_init(&k); + if (key_init != 0) { + fprintf(stderr, "Unable to initialize ed25519 key\n"); + exit_code = 1; + goto cleanup; + } + if (wc_ed25519_make_key(&rng, ED25519_KEY_SIZE, &k) != 0) { fprintf(stderr, "Unable to create ed25519 key\n"); - exit(1); + exit_code = 1; + goto cleanup; } if (wc_ed25519_export_private_only(&k, priv, &outlen) != 0) { fprintf(stderr, "Unable to export ed25519 private key\n"); - exit(2); + exit_code = 2; + goto cleanup; } + outlen = ED25519_PUB_KEY_SIZE; if (wc_ed25519_export_public(&k, pub, &outlen) != 0) { fprintf(stderr, "Unable to export ed25519 public key\n"); - exit(2); + exit_code = 2; + goto cleanup; } fpriv = fopen(privkey, "wb"); if (fpriv == NULL) { fprintf(stderr, "Unable to open file '%s' for writing: %s", privkey, strerror(errno)); - exit(3); + exit_code = 3; + goto cleanup; } fwrite(priv, 32, 1, fpriv); fwrite(pub, 32, 1, fpriv); @@ -718,11 +772,19 @@ static void keygen_ed25519(const char *privkey, uint32_t id_mask) if (exportPubKey) { if (export_pubkey_file(privkey, pub, ED25519_PUB_KEY_SIZE) != 0) { fprintf(stderr, "Unable to export public key to file\n"); - exit(4); + exit_code = 4; + goto cleanup; } } keystore_add(AUTH_KEY_ED25519, pub, ED25519_PUB_KEY_SIZE, privkey, id_mask); + +cleanup: + wc_ForceZero(priv, sizeof(priv)); + if (key_init == 0) + wc_ed25519_free(&k); + if (exit_code != 0) + exit(exit_code); } static void keygen_ed448(const char *privkey, uint32_t id_mask) @@ -731,22 +793,37 @@ static void keygen_ed448(const char *privkey, uint32_t id_mask) uint8_t priv[ED448_KEY_SIZE], pub[ED448_PUB_KEY_SIZE]; FILE *fpriv; uint32_t outlen = ED448_KEY_SIZE; + int exit_code = 0; + int key_init = 0; + + key_init = wc_ed448_init(&k); + if (key_init != 0) { + fprintf(stderr, "Unable to initialize ed448 key\n"); + exit_code = 1; + goto cleanup; + } + if (wc_ed448_make_key(&rng, ED448_KEY_SIZE, &k) != 0) { fprintf(stderr, "Unable to create ed448 key\n"); - exit(1); + exit_code = 1; + goto cleanup; } if (wc_ed448_export_private_only(&k, priv, &outlen) != 0) { fprintf(stderr, "Unable to export ed448 private key\n"); - exit(2); + exit_code = 2; + goto cleanup; } + outlen = ED448_PUB_KEY_SIZE; if (wc_ed448_export_public(&k, pub, &outlen) != 0) { fprintf(stderr, "Unable to export ed448 public key\n"); - exit(2); + exit_code = 2; + goto cleanup; } fpriv = fopen(privkey, "wb"); if (fpriv == NULL) { fprintf(stderr, "Unable to open file 'ed448.der' for writing: %s", strerror(errno)); - exit(3); + exit_code = 3; + goto cleanup; } fwrite(priv, ED448_KEY_SIZE, 1, fpriv); fwrite(pub, ED448_PUB_KEY_SIZE, 1, fpriv); @@ -755,11 +832,19 @@ static void keygen_ed448(const char *privkey, uint32_t id_mask) if (exportPubKey) { if (export_pubkey_file(privkey, pub, ED448_PUB_KEY_SIZE) != 0) { fprintf(stderr, "Unable to export public key to file\n"); - exit(4); + exit_code = 4; + goto cleanup; } } keystore_add(AUTH_KEY_ED448, pub, ED448_PUB_KEY_SIZE, privkey, id_mask); + +cleanup: + wc_ForceZero(priv, sizeof(priv)); + if (key_init == 0) + wc_ed448_free(&k); + if (exit_code != 0) + exit(exit_code); } #include "../lms/lms_common.h" @@ -978,6 +1063,8 @@ static void keygen_ml_dsa(const char *priv_fname, uint32_t id_mask) int ml_dsa_priv_len = 0; int ml_dsa_pub_len = 0; int ml_dsa_level = ML_DSA_LEVEL; + int exit_code = 0; + int key_init = 0; char * env_ml_dsa_level = getenv("ML_DSA_LEVEL"); if (env_ml_dsa_level != NULL) { ml_dsa_level = atoi(env_ml_dsa_level); @@ -988,21 +1075,25 @@ static void keygen_ml_dsa(const char *priv_fname, uint32_t id_mask) ret = wc_MlDsaKey_Init(&key, NULL, INVALID_DEVID); if (ret != 0) { fprintf(stderr, "error: wc_MlDsaKey_Init returned %d\n", ret); - exit(1); + exit_code = 1; + goto cleanup; } + key_init = 1; ret = wc_MlDsaKey_SetParams(&key, ml_dsa_level); if (ret != 0) { fprintf(stderr, "error: wc_MlDsaKey_SetParams(%d) returned %d\n", ml_dsa_level, ret); - exit(1); + exit_code = 1; + goto cleanup; } /* Make the key pair. */ ret = wc_MlDsaKey_MakeKey(&key, &rng); if (ret != 0) { fprintf(stderr, "error: wc_MlDsaKey_MakeKey returned %d\n", ret); - exit(1); + exit_code = 1; + goto cleanup; } /* Get the ML-DSA public key length. */ @@ -1010,7 +1101,8 @@ static void keygen_ml_dsa(const char *priv_fname, uint32_t id_mask) if (ret != 0 || ml_dsa_pub_len <= 0) { printf("error: wc_MlDsaKey_GetPrivLen returned %d\n", ret); - exit(1); + exit_code = 1; + goto cleanup; } printf("info: ml-dsa public key length: %d\n", ml_dsa_pub_len); @@ -1020,14 +1112,16 @@ static void keygen_ml_dsa(const char *priv_fname, uint32_t id_mask) if (ret != 0 || ml_dsa_priv_len <= 0) { printf("error: wc_MlDsaKey_GetPrivLen returned %d\n", ret); - exit(1); + exit_code = 1; + goto cleanup; } printf("info: ml-dsa private key length: %d\n", ml_dsa_priv_len); if (ml_dsa_priv_len <= ml_dsa_pub_len) { printf("error: ml-dsa: unexpected key lengths: %d, %d", ml_dsa_priv_len, ml_dsa_pub_len); - exit(1); + exit_code = 1; + goto cleanup; } else { ml_dsa_priv_len -= ml_dsa_pub_len; @@ -1036,7 +1130,8 @@ static void keygen_ml_dsa(const char *priv_fname, uint32_t id_mask) priv = malloc(ml_dsa_priv_len); if (priv == NULL) { fprintf(stderr, "error: malloc(%d) failed\n", ml_dsa_priv_len); - exit(1); + exit_code = 1; + goto cleanup; } /* Set the expected key lengths. */ @@ -1046,25 +1141,29 @@ static void keygen_ml_dsa(const char *priv_fname, uint32_t id_mask) ret = wc_MlDsaKey_ExportPubRaw(&key, pub, &pub_len); if (ret != 0) { fprintf(stderr, "error: wc_MlDsaKey_ExportPubRaw returned %d\n", ret); - exit(1); + exit_code = 1; + goto cleanup; } ret = wc_MlDsaKey_ExportPrivRaw(&key, priv, &priv_len); if (ret != 0) { fprintf(stderr, "error: wc_MlDsaKey_ExportPrivRaw returned %d\n", ret); - exit(1); + exit_code = 1; + goto cleanup; } if ((int)pub_len != ml_dsa_pub_len) { fprintf(stderr, "error: wc_MlDsaKey_ExportPubRaw returned pub_len=%d, " \ "expected %d\n", pub_len, ml_dsa_pub_len); - exit(1); + exit_code = 1; + goto cleanup; } if ((int) priv_len != ml_dsa_priv_len) { fprintf(stderr, "error: ml_dsa priv key mismatch: got %d " \ "bytes, expected %d\n", priv_len, ml_dsa_priv_len); - exit(1); + exit_code = 1; + goto cleanup; } fpriv = fopen(priv_fname, "wb"); @@ -1072,12 +1171,14 @@ static void keygen_ml_dsa(const char *priv_fname, uint32_t id_mask) if (fpriv == NULL) { fprintf(stderr, "error: fopen(%s) failed: %s", priv_fname, strerror(errno)); - exit(1); + exit_code = 1; + goto cleanup; } fwrite(priv, priv_len, 1, fpriv); fwrite(pub, pub_len, 1, fpriv); fclose(fpriv); + fpriv = NULL; if (exportPubKey) { if (saveAsDer) { @@ -1099,14 +1200,16 @@ static void keygen_ml_dsa(const char *priv_fname, uint32_t id_mask) break; default: fprintf(stderr, "Error: Unsupported ML DSA level\n"); - exit(1); + exit_code = 1; + goto cleanup; break; } pubDer = (uint8_t*)malloc(pubDerSz); if (pubDer == NULL) { fprintf(stderr, "Error: Failed to allocate memory for DER export\n"); - exit(1); + exit_code = 1; + goto cleanup; } /* Export public key in DER format */ @@ -1116,12 +1219,16 @@ static void keygen_ml_dsa(const char *priv_fname, uint32_t id_mask) if (pubOutLen < 0) { fprintf(stderr, "Unable to export public key to DER, ret=%d\n", pubOutLen); - exit(1); + free(pubDer); + exit_code = 1; + goto cleanup; } if (export_pubkey_file(priv_fname, pubDer, pubOutLen) != 0) { fprintf(stderr, "Unable to export public key to file\n"); - exit(1); + free(pubDer); + exit_code = 1; + goto cleanup; } free(pubDer); @@ -1131,16 +1238,26 @@ static void keygen_ml_dsa(const char *priv_fname, uint32_t id_mask) if (export_pubkey_file(priv_fname, pub, KEYSTORE_PUBKEY_SIZE_ML_DSA) != 0) { fprintf(stderr, "Unable to export public key to file\n"); - exit(1); + exit_code = 1; + goto cleanup; } } } keystore_add(AUTH_KEY_ML_DSA, pub, pub_len, priv_fname, id_mask); - wc_MlDsaKey_Free(&key); - free(priv); - priv = NULL; +cleanup: + if (fpriv != NULL) + fclose(fpriv); + if (key_init) + wc_MlDsaKey_Free(&key); + if (priv != NULL) { + wc_ForceZero(priv, priv_len); + free(priv); + priv = NULL; + } + if (exit_code != 0) + exit(exit_code); } static void key_gen_check(const char *kfilename) diff --git a/tools/keytools/sign.c b/tools/keytools/sign.c index 5109c9d9a2..8e1217b6e6 100644 --- a/tools/keytools/sign.c +++ b/tools/keytools/sign.c @@ -221,9 +221,23 @@ static void header_append_u16(uint8_t* header, uint32_t* idx, uint16_t tmp16) memcpy(&header[*idx], &tmp16, sizeof(tmp16)); *idx += sizeof(tmp16); } + +static uint32_t header_append_limit(void); + static void header_append_tag(uint8_t* header, uint32_t* idx, uint16_t tag, - uint16_t len, void* data) + uint16_t len, const void* data) { + const uint32_t append_sz = (uint32_t)(sizeof(tag) + sizeof(len)) + len; + const uint32_t header_sz = header_append_limit(); + + if ((*idx > header_sz) || (append_sz > (header_sz - *idx))) { + fprintf(stderr, + "Header overflow while appending tag 0x%04x " + "(offset=%u, size=%u, header=%u)\n", + tag, *idx, append_sz, header_sz); + exit(1); + } + header_append_u16(header, idx, tag); header_append_u16(header, idx, len); memcpy(&header[*idx], data, len); @@ -296,6 +310,21 @@ static struct cmd_options CMD = { .hybrid = 0 }; +static uint32_t header_append_limit(void) +{ + return CMD.header_sz; +} + +static void zero_and_free(uint8_t *buf, uint32_t len) +{ + if (buf == NULL) { + return; + } + + wc_ForceZero(buf, len); + free(buf); +} + static uint16_t sign_tool_find_header(uint8_t *haystack, uint16_t type, uint8_t **ptr) { uint8_t *p = haystack; @@ -575,6 +604,7 @@ static uint8_t *load_key(uint8_t **key_buffer, uint32_t *key_buffer_sz, } } fclose(f); + f = NULL; if (*key_buffer == NULL) { printf("Key buffer malloc error!\n"); goto failure; @@ -942,7 +972,7 @@ static uint8_t *load_key(uint8_t **key_buffer, uint32_t *key_buffer_sz, failure: if (*key_buffer != NULL) { - free(*key_buffer); + zero_and_free(*key_buffer, *key_buffer_sz); *key_buffer = NULL; } return NULL; @@ -1107,6 +1137,119 @@ static int sign_digest(int sign, int hash_algo, #define ALIGN_8(x) while ((x % 8) != 4) { x++; } #define ALIGN_4(x) while ((x % 4) != 0) { x++; } +static void header_size_align_8(uint32_t *idx) +{ + while ((*idx % 8U) != 4U) { + (*idx)++; + } +} + +static void header_size_align_4(uint32_t *idx) +{ + while ((*idx % 4U) != 0U) { + (*idx)++; + } +} + +static void header_size_append_tag(uint32_t *idx, uint32_t len) +{ + *idx += 4U + len; +} + +static uint32_t header_digest_size(int hash_algo) +{ + switch (hash_algo) { + case HASH_SHA256: + return HDR_SHA256_LEN; + case HASH_SHA384: + return HDR_SHA384_LEN; + case HASH_SHA3: + return HDR_SHA3_384_LEN; + default: + return 0; + } +} + +static uint32_t header_required_size(int is_diff, uint32_t cert_chain_sz, + uint32_t secondary_key_sz) +{ + uint32_t idx = 0; + uint32_t digest_sz = header_digest_size(CMD.hash_algo); + uint32_t i; + + idx += 2U * sizeof(uint32_t); + header_size_append_tag(&idx, HDR_VERSION_LEN); + header_size_align_8(&idx); + + if (!CMD.no_ts) { + header_size_append_tag(&idx, HDR_TIMESTAMP_LEN); + } + + header_size_append_tag(&idx, HDR_IMG_TYPE_LEN); + + if (is_diff) { + header_size_align_4(&idx); + header_size_append_tag(&idx, 4); + header_size_append_tag(&idx, 4); + header_size_align_4(&idx); + header_size_append_tag(&idx, 4); + header_size_append_tag(&idx, 4); + + if (!CMD.no_base_sha && digest_sz > 0U) { + header_size_align_8(&idx); + header_size_append_tag(&idx, digest_sz); + } + } + + for (i = 0; i < CMD.custom_tlvs; i++) { + header_size_align_8(&idx); + header_size_append_tag(&idx, CMD.custom_tlv[i].len); + } + + if (cert_chain_sz > 0U) { + header_size_align_8(&idx); + header_size_append_tag(&idx, cert_chain_sz); + } + + if (digest_sz > 0U) { + header_size_align_8(&idx); + header_size_append_tag(&idx, digest_sz); + header_size_align_8(&idx); + + if (CMD.hybrid && secondary_key_sz > 0U) { + header_size_append_tag(&idx, 2); + if (CMD.hash_algo == HASH_SHA256) + header_size_align_8(&idx); + header_size_append_tag(&idx, digest_sz); + header_size_align_8(&idx); + } + + header_size_append_tag(&idx, digest_sz); + } + + if (CMD.sign != NO_SIGN) { + header_size_align_8(&idx); + header_size_append_tag(&idx, CMD.signature_sz); + + if (CMD.hybrid) { + header_size_align_8(&idx); + header_size_append_tag(&idx, CMD.secondary_signature_sz); + } + + if (CMD.policy_sign) { + uint32_t policy_sig_sz = CMD.policy_sz; + if (policy_sig_sz == 0U) { + policy_sig_sz = CMD.signature_sz; + } + header_size_align_8(&idx); + header_size_append_tag(&idx, + policy_sig_sz + (uint32_t)sizeof(uint32_t)); + } + } + + return idx; +} + static int make_header_ex(int is_diff, uint8_t *pubkey, uint32_t pubkey_sz, const char *image_file, const char *outfile, uint32_t delta_base_version, uint32_t patch_len, uint32_t patch_inv_off, @@ -1125,6 +1268,8 @@ static int make_header_ex(int is_diff, uint8_t *pubkey, uint32_t pubkey_sz, int ret = -1; uint8_t buf[4096]; uint8_t second_buf[4096]; + uint8_t key[ENC_MAX_KEY_SZ]; + uint8_t iv[ENC_MAX_IV_SZ]; uint32_t read_sz, pos; uint8_t digest[48]; /* max digest */ uint32_t digest_sz = 0; @@ -1133,6 +1278,9 @@ static int make_header_ex(int is_diff, uint8_t *pubkey, uint32_t pubkey_sz, uint8_t* cert_chain = NULL; uint32_t cert_chain_sz = 0; + XMEMSET(key, 0, sizeof(key)); + XMEMSET(iv, 0, sizeof(iv)); + /* Check certificate chain file size before allocating header, and adjust * header size if needed */ if (CMD.cert_chain_file != NULL) { @@ -1140,14 +1288,8 @@ static int make_header_ex(int is_diff, uint8_t *pubkey, uint32_t pubkey_sz, /* Get the file size */ if (stat(CMD.cert_chain_file, &file_stat) == 0) { - /* 2 bytes for tag + 2 bytes for length field */ - const uint32_t tag_len_size = 4; - /* Maximum alignment padding that might be needed */ - const uint32_t max_alignment = 8; - /* Required space = tag(2) + length(2) + data + potential alignment - * * padding */ - const uint32_t required_space = - tag_len_size + file_stat.st_size + max_alignment; + const uint32_t required_space = header_required_size(is_diff, + (uint32_t)file_stat.st_size, secondary_key_sz); /* If the current header size is too small, increase it */ if (CMD.header_sz < required_space) { @@ -1302,6 +1444,7 @@ static int make_header_ex(int is_diff, uint8_t *pubkey, uint32_t pubkey_sz, printf("Could not get certificate chain file size: %s\n", strerror(errno)); fclose(f); + f = NULL; goto failure; } @@ -1316,6 +1459,7 @@ static int make_header_ex(int is_diff, uint8_t *pubkey, uint32_t pubkey_sz, cert_chain_sz), CMD.header_sz); fclose(f); + f = NULL; goto failure; } @@ -1323,12 +1467,14 @@ static int make_header_ex(int is_diff, uint8_t *pubkey, uint32_t pubkey_sz, if (cert_chain == NULL) { printf("Certificate chain buffer malloc error!\n"); fclose(f); + f = NULL; goto failure; } /* Read the entire file into the buffer */ io_sz = (int)fread(cert_chain, 1, cert_chain_sz, f); fclose(f); + f = NULL; if (io_sz != (int)cert_chain_sz) { printf("Error reading certificate chain file: %s\n", @@ -1400,6 +1546,12 @@ static int make_header_ex(int is_diff, uint8_t *pubkey, uint32_t pubkey_sz, /* Hash image file */ f = fopen(image_file, "rb"); + if (f == NULL) { + printf("Open image file %s failed\n", image_file); + ret = -1; + wc_Sha256Free(&sha); + goto failure; + } pos = 0; while (ret == 0 && pos < image_sz) { read_sz = image_sz - pos; @@ -1469,6 +1621,12 @@ static int make_header_ex(int is_diff, uint8_t *pubkey, uint32_t pubkey_sz, /* Hash image file */ f = fopen(image_file, "rb"); + if (f == NULL) { + printf("Open image file %s failed\n", image_file); + ret = -1; + wc_Sha384Free(&sha); + goto failure; + } pos = 0; while (ret == 0 && pos < image_sz) { read_sz = image_sz - pos; @@ -1536,6 +1694,12 @@ static int make_header_ex(int is_diff, uint8_t *pubkey, uint32_t pubkey_sz, /* Hash image file */ f = fopen(image_file, "rb"); + if (f == NULL) { + printf("Open image file %s failed\n", image_file); + ret = -1; + wc_Sha3_384_Free(&sha); + goto failure; + } pos = 0; while (ret == 0 && pos < image_sz) { read_sz = image_sz - pos; @@ -1615,6 +1779,7 @@ static int make_header_ex(int is_diff, uint8_t *pubkey, uint32_t pubkey_sz, } io_sz = (int)fread(signature, 1, CMD.signature_sz, f); fclose(f); + f = NULL; if (io_sz <= 0) { printf("Error reading file %s\n", CMD.signature_file); goto failure; @@ -1662,6 +1827,7 @@ static int make_header_ex(int is_diff, uint8_t *pubkey, uint32_t pubkey_sz, if (io_sz != sizeof(uint32_t)) { printf("Error reading file %s\n", CMD.policy_file); fclose(f); + f = NULL; goto failure; } @@ -1669,6 +1835,7 @@ static int make_header_ex(int is_diff, uint8_t *pubkey, uint32_t pubkey_sz, /* in normal sign mode PCR digest (32 bytes) */ io_sz = (int)fread(digest, 1, digest_sz, f); fclose(f); + f = NULL; if (io_sz != (int)digest_sz) { printf("Error reading file %s\n", CMD.policy_file); goto failure; @@ -1691,6 +1858,7 @@ static int make_header_ex(int is_diff, uint8_t *pubkey, uint32_t pubkey_sz, /* in manual mode remainder is PCR signature */ io_sz = (int)fread(policy, 1, CMD.policy_sz, f); fclose(f); + f = NULL; if (io_sz <= 0) { printf("Error reading file %s\n", CMD.policy_file); goto failure; @@ -1705,6 +1873,7 @@ static int make_header_ex(int is_diff, uint8_t *pubkey, uint32_t pubkey_sz, if (f != NULL) { fwrite(policy, 1, CMD.policy_sz + sizeof(uint32_t), f); fclose(f); + f = NULL; } } @@ -1836,7 +2005,6 @@ static int make_header_ex(int is_diff, uint8_t *pubkey, uint32_t pubkey_sz, } if (!CMD.header_only && (CMD.encrypt != ENC_OFF) && CMD.encrypt_key_file) { - uint8_t key[ENC_MAX_KEY_SZ], iv[ENC_MAX_IV_SZ]; uint8_t enc_buf[ENC_MAX_BLOCK_SZ]; int ivSz, keySz, encBlockSz; uint32_t fsize = 0; @@ -1865,24 +2033,28 @@ static int make_header_ex(int is_diff, uint8_t *pubkey, uint32_t pubkey_sz, if (fek == NULL) { fprintf(stderr, "Open encryption key file %s: %s\n", CMD.encrypt_key_file, strerror(errno)); - exit(1); + goto failure; } ret = (int)fread(key, 1, keySz, fek); if (ret != keySz) { fprintf(stderr, "Error reading key from %s\n", CMD.encrypt_key_file); - exit(1); + ret = -1; + goto failure; } ret = (int)fread(iv, 1, ivSz, fek); if (ret != ivSz) { fprintf(stderr, "Error reading IV from %s\n", CMD.encrypt_key_file); - exit(1); + ret = -1; + goto failure; } fclose(fek); + fek = NULL; fef = fopen(CMD.output_encrypted_image_file, "wb"); if (!fef) { fprintf(stderr, "Open encrypted output file %s: %s\n", - CMD.encrypt_key_file, strerror(errno)); + CMD.output_encrypted_image_file, strerror(errno)); + goto failure; } fsize = ftell(f); fseek(f, 0, SEEK_SET); /* restart the _signed file from 0 */ @@ -1894,7 +2066,8 @@ static int make_header_ex(int is_diff, uint8_t *pubkey, uint32_t pubkey_sz, #ifndef HAVE_CHACHA fprintf(stderr, "Encryption not supported: chacha support not found" "in wolfssl configuration.\n"); - exit(100); + ret = 100; + goto failure; #endif wc_Chacha_SetKey(&cha, key, sizeof(key)); wc_Chacha_SetIV(&cha, iv, 0); @@ -1926,17 +2099,30 @@ static int make_header_ex(int is_diff, uint8_t *pubkey, uint32_t pubkey_sz, } } fclose(fef); + fef = NULL; printf("Encryption complete.\n"); } printf("Output image(s) successfully created.\n"); ret = 0; if (f2) { fclose(f2); + f2 = NULL; } if (f) { fclose(f); + f = NULL; } failure: + wc_ForceZero(key, sizeof(key)); + wc_ForceZero(iv, sizeof(iv)); + if (f) + fclose(f); + if (f2) + fclose(f2); + if (fek) + fclose(fek); + if (fef) + fclose(fef); if (cert_chain) free(cert_chain); if (policy) @@ -2083,6 +2269,10 @@ static int base_diff(const char *f_base, uint8_t *pubkey, uint32_t pubkey_sz, in goto cleanup; } len2 = st.st_size; + if (len2 > MAX_SRC_SIZE) { + printf("%s: file too large\n", CMD.output_image_file); + goto cleanup; + } buffer = mmap(NULL, len2, PROT_READ, MAP_SHARED, fd2, 0); if (buffer == (void *)(-1)) { perror("mmap"); @@ -2118,6 +2308,10 @@ static int base_diff(const char *f_base, uint8_t *pubkey, uint32_t pubkey_sz, in fseek(f2, 0L, SEEK_END); len2 = ftell(f2); fseek(f2, 0L, SEEK_SET); + if (len2 > MAX_SRC_SIZE) { + printf("%s: file too large\n", CMD.output_image_file); + goto cleanup; + } buffer = malloc(len2); if (buffer == NULL) { fprintf(stderr, "Error malloc for buffer %d\n", len2); @@ -3009,7 +3203,7 @@ int main(int argc, char** argv) DEBUG_PRINT("Secondary signature size: %u\n", CMD.secondary_signature_sz); DEBUG_PRINT("Header size: %u\n", CMD.header_sz); if (kbuf2) - free(kbuf2); + zero_and_free(kbuf2, key_buffer_sz2); if (pubkey2) free(pubkey2); } else { @@ -3029,7 +3223,7 @@ int main(int argc, char** argv) free(pubkey); if (kbuf) - free(kbuf); + zero_and_free(kbuf, key_buffer_sz); if (CMD.sign == SIGN_ED25519) { wc_ed25519_free(&key.ed); } diff --git a/tools/test.mk b/tools/test.mk index 0217ea21d1..fdc19c01d1 100644 --- a/tools/test.mk +++ b/tools/test.mk @@ -1164,7 +1164,7 @@ test-size-all: make clean make test-size SIGN=ECC384 NO_ASM=1 LIMIT=15290 NO_ARM_ASM=1 make keysclean - make test-size SIGN=ED448 LIMIT=13862 NO_ARM_ASM=1 + make test-size SIGN=ED448 LIMIT=13864 NO_ARM_ASM=1 make keysclean make test-size SIGN=RSA3072 LIMIT=12056 NO_ARM_ASM=1 make clean diff --git a/tools/tpm/Makefile b/tools/tpm/Makefile index 3f2d2bd22d..e9bf9f37cc 100644 --- a/tools/tpm/Makefile +++ b/tools/tpm/Makefile @@ -91,6 +91,12 @@ debug: all swtpm:CFLAGS+=-DWOLFTPM_SWTPM swtpm:all +# The TPM host tools consume the generated root keystore just like the +# bootloader build. In a clean CI workspace, regenerate it through the +# top-level makefile before compiling the local keystore object. +$(WOLFBOOTDIR)/src/keystore.c: + $(Q)$(MAKE) -C $(WOLFBOOTDIR) src/keystore.c + # build objects $(OBJDIR)/%.o: %.c $(Q)$(CC) $(CFLAGS) -c -o $@ $< @@ -103,6 +109,8 @@ $(OBJDIR)/%.o: $(WOLFBOOT_LIB_WOLFTPM)/src/%.c $(OBJDIR)/%.o: $(WOLFBOOT_LIB_WOLFTPM)/hal/%.c $(Q)$(CC) $(CFLAGS) -c -o $@ $< +$(OBJDIR)/keystore.o: $(WOLFBOOTDIR)/src/keystore.c + # build templates rot: $(OBJS_VIRT) rot.o @echo "Building Root of Trust (ROT) tool" diff --git a/tools/tpm/policy_create.c b/tools/tpm/policy_create.c index 0ea6ce3663..15b8555547 100644 --- a/tools/tpm/policy_create.c +++ b/tools/tpm/policy_create.c @@ -241,7 +241,7 @@ int main(int argc, char *argv[]) pcrDigestSz = -1; else pcrDigestSz = hexToByte(hashHexStr, pcrDigest, hashHexStrLen); - if (pcrDigestSz <= 0) { + if ((int)pcrDigestSz <= 0) { fprintf(stderr, "Invalid PCR hash length\n"); usage(); return -1; diff --git a/tools/unit-tests/Makefile b/tools/unit-tests/Makefile index 4edf38319e..47c6bb9d21 100644 --- a/tools/unit-tests/Makefile +++ b/tools/unit-tests/Makefile @@ -29,6 +29,7 @@ CFLAGS+=-g -ggdb CFLAGS+=-fprofile-arcs CFLAGS+=-ftest-coverage CFLAGS+=--coverage +CFLAGS+=-DUNIT_TEST_COVERAGE CFLAGS+=-DUNIT_TEST -DWOLFSSL_USER_SETTINGS LDFLAGS+=-fprofile-arcs LDFLAGS+=-ftest-coverage @@ -45,17 +46,25 @@ endif TESTS:=unit-parser unit-fdt unit-extflash unit-string unit-spi-flash unit-aes128 \ unit-aes256 unit-chacha20 unit-pci unit-mock-state unit-sectorflags \ unit-image unit-image-rsa unit-nvm unit-nvm-flagshome unit-enc-nvm \ - unit-enc-nvm-flagshome unit-delta unit-update-flash \ + unit-enc-nvm-flagshome unit-delta unit-update-flash unit-update-flash-delta \ + unit-update-flash-self-update \ unit-update-flash-enc unit-update-ram unit-pkcs11_store unit-psa_store unit-disk \ unit-update-disk unit-multiboot unit-boot-x86-fsp unit-qspi-flash unit-tpm-rsa-exp \ unit-image-nopart unit-image-sha384 unit-image-sha3-384 unit-store-sbrk \ - unit-tpm-blob unit-policy-sign unit-rot-auth unit-sdhci-response-bits + unit-tpm-blob unit-policy-create unit-policy-sign unit-rot-auth unit-sdhci-response-bits \ + unit-sign-encrypted-output TESTS+=unit-tpm-check-rot-auth +include unit-sign-encrypted-output.mkfrag + all: $(TESTS) cov: + rm -f unit-sign-encrypted-output-* gcovr -f "^\.\.\/\.\.\/src.*\.c" -r ../.. --verbose \ + --exclude-directories 'tools/keytools$$' \ + --gcov-exclude '.*tools/keytools/.*' \ + --gcov-exclude '.*unit-sign-encrypted-output-.*' \ --merge-mode-functions merge-use-line-0 \ --html-medium-threshold 60 \ --html-high-threshold 80 \ @@ -92,10 +101,23 @@ unit-delta:CFLAGS+=-DNVM_FLASH_WRITEONCE -DMOCK_PARTITIONS -DDELTA_UPDATES -DDEL unit-pkcs11_store:CFLAGS+=-I$(WOLFBOOT_LIB_WOLFPKCS11) -DMOCK_PARTITIONS -DMOCK_KEYVAULT -DSECURE_PKCS11 -DWOLFPKCS11_USER_SETTINGS unit-psa_store:CFLAGS+=-I$(WOLFBOOT_LIB_WOLFPSA) -DMOCK_PARTITIONS -DMOCK_KEYVAULT -DWOLFCRYPT_TZ_PSA unit-update-flash:CFLAGS+=-DMOCK_PARTITIONS -DWOLFBOOT_NO_SIGN -DUNIT_TEST_AUTH \ - -DWOLFBOOT_HASH_SHA256 -DPRINTF_ENABLED -DEXT_FLASH -DPART_UPDATE_EXT -DPART_SWAP_EXT + -DWOLFBOOT_HASH_SHA256 -DPRINTF_ENABLED -DEXT_FLASH -DPART_UPDATE_EXT -DPART_SWAP_EXT \ + -DWOLFBOOT_ORIGIN=MOCK_ADDRESS_BOOT -DBOOTLOADER_PARTITION_SIZE=WOLFBOOT_PARTITION_SIZE +unit-update-flash-delta:CFLAGS+=-DMOCK_PARTITIONS -DWOLFBOOT_NO_SIGN -DUNIT_TEST_AUTH \ + -DWOLFBOOT_HASH_SHA256 -DPRINTF_ENABLED -DEXT_FLASH -DPART_UPDATE_EXT -DPART_SWAP_EXT \ + -DDELTA_UPDATES -DDELTA_BLOCK_SIZE=512 -D__WOLFBOOT \ + -DWOLFBOOT_ORIGIN=MOCK_ADDRESS_BOOT -DBOOTLOADER_PARTITION_SIZE=WOLFBOOT_PARTITION_SIZE +unit-update-flash-self-update:CFLAGS+=-DMOCK_PARTITIONS -DWOLFBOOT_NO_SIGN -DUNIT_TEST_AUTH \ + -DWOLFBOOT_HASH_SHA256 -DPRINTF_ENABLED -DEXT_FLASH -DPART_UPDATE_EXT -DPART_SWAP_EXT \ + -DRAM_CODE -DARCH_SIM -DUNIT_TEST_SELF_UPDATE_ONLY \ + -DARCH_FLASH_OFFSET=MOCK_ADDRESS_BOOT -DWOLFBOOT_VERSION=7 \ + -DWOLFBOOT_ORIGIN=MOCK_ADDRESS_BOOT -DBOOTLOADER_PARTITION_SIZE=WOLFBOOT_PARTITION_SIZE unit-update-ram:CFLAGS+=-DMOCK_PARTITIONS -DWOLFBOOT_NO_SIGN -DUNIT_TEST_AUTH \ -DWOLFBOOT_HASH_SHA256 -DPRINTF_ENABLED -DEXT_FLASH -DPART_UPDATE_EXT \ - -DPART_SWAP_EXT -DPART_BOOT_EXT -DWOLFBOOT_DUALBOOT -DNO_XIP + -DPART_SWAP_EXT -DPART_BOOT_EXT -DWOLFBOOT_DUALBOOT -DNO_XIP \ + -DWOLFBOOT_ORIGIN=MOCK_ADDRESS_BOOT -DBOOTLOADER_PARTITION_SIZE=WOLFBOOT_PARTITION_SIZE +unit-update-disk:CFLAGS+=-DMOCK_PARTITIONS -DPRINTF_ENABLED \ + -DWOLFBOOT_ORIGIN=MOCK_ADDRESS_BOOT -DBOOTLOADER_PARTITION_SIZE=WOLFBOOT_PARTITION_SIZE unit-string:CFLAGS+=-fno-builtin @@ -126,13 +148,13 @@ unit-spi-flash: ../../include/target.h unit-spi-flash.c unit-qspi-flash: ../../include/target.h unit-qspi-flash.c gcc -o $@ $^ $(CFLAGS) $(LDFLAGS) -unit-tpm-rsa-exp: ../../include/target.h unit-tpm-rsa-exp.c +unit-tpm-rsa-exp: ../../include/target.h unit-tpm-rsa-exp.c ../../src/string.c gcc -o $@ $^ $(CFLAGS) -I$(WOLFBOOT_LIB_WOLFTPM) -DWOLFBOOT_TPM \ -DWOLFTPM_USER_SETTINGS -DWOLFBOOT_TPM_VERIFY -DWOLFBOOT_SIGN_RSA2048 \ -DWOLFBOOT_HASH_SHA256 \ -ffunction-sections -fdata-sections $(LDFLAGS) -Wl,--gc-sections -unit-tpm-check-rot-auth: ../../include/target.h unit-tpm-check-rot-auth.c +unit-tpm-check-rot-auth: ../../include/target.h unit-tpm-check-rot-auth.c ../../src/string.c gcc -o $@ $^ $(CFLAGS) -I$(WOLFBOOT_LIB_WOLFTPM) -DWOLFBOOT_TPM \ -DWOLFTPM_USER_SETTINGS -DWOLFBOOT_TPM_VERIFY -DWOLFBOOT_SIGN_RSA2048 \ -DWOLFBOOT_HASH_SHA256 \ @@ -144,6 +166,12 @@ unit-tpm-blob: ../../include/target.h unit-tpm-blob.c -DWOLFBOOT_HASH_SHA256 \ -ffunction-sections -fdata-sections $(LDFLAGS) -Wl,--gc-sections +unit-policy-create: ../../include/target.h unit-policy-create.c \ + $(WOLFBOOT_LIB_WOLFSSL)/wolfcrypt/src/memory.c + gcc -o $@ $^ -I../tpm $(CFLAGS) -I$(WOLFBOOT_LIB_WOLFTPM) -DWOLFBOOT_TPM \ + -DWOLFTPM_USER_SETTINGS -DWOLFBOOT_SIGN_ECC256 -DWOLFBOOT_HASH_SHA256 \ + -ffunction-sections -fdata-sections $(LDFLAGS) -Wl,--gc-sections + unit-policy-sign: ../../include/target.h unit-policy-sign.c \ $(WOLFBOOT_LIB_WOLFSSL)/wolfcrypt/src/memory.c gcc -o $@ $^ -I../tpm $(CFLAGS) -I$(WOLFBOOT_LIB_WOLFTPM) -DWOLFBOOT_TPM \ @@ -151,6 +179,13 @@ unit-policy-sign: ../../include/target.h unit-policy-sign.c \ -DHAVE_ECC_KEY_IMPORT \ -ffunction-sections -fdata-sections $(LDFLAGS) -Wl,--gc-sections +unit-sign-encrypted-output: ../../include/target.h unit-sign-encrypted-output.c \ + $(KEYTOOLS_SIGN_SRCS) + gcc -o $@ $^ -I../keytools $(CFLAGS) -DML_DSA_LEVEL=2 \ + -D"LMS_LEVELS=1" -D"LMS_HEIGHT=10" -D"LMS_WINTERNITZ=8" \ + -DWOLFBOOT_XMSS_PARAMS=\"XMSS-SHA2_10_256\" \ + -ffunction-sections -fdata-sections \ + $(LDFLAGS) -Wl,--gc-sections unit-rot-auth: ../../include/target.h unit-rot-auth.c \ $(WOLFBOOT_LIB_WOLFSSL)/wolfcrypt/src/memory.c gcc -o $@ $^ -I../tpm $(CFLAGS) -I$(WOLFBOOT_LIB_WOLFTPM) -DWOLFBOOT_TPM \ @@ -163,6 +198,11 @@ unit-store-sbrk: unit-store-sbrk.c ../../src/store_sbrk.c unit-string: ../../include/target.h unit-string.c gcc -o $@ $^ $(CFLAGS) -DDEBUG_UART -DPRINTF_ENABLED $(LDFLAGS) +unit-update-flash-self-update: ../../include/target.h unit-update-flash.c + gcc -o $@ unit-update-flash.c ../../src/image.c \ + $(WOLFBOOT_LIB_WOLFSSL)/wolfcrypt/src/sha256.c \ + $(CFLAGS) $(LDFLAGS) + unit-sdhci-response-bits: ../../include/target.h unit-sdhci-response-bits.c gcc -o $@ $^ $(CFLAGS) -ffunction-sections -fdata-sections $(LDFLAGS) \ -Wl,--gc-sections @@ -233,10 +273,15 @@ unit-delta: ../../include/target.h unit-delta.c unit-update-flash: ../../include/target.h unit-update-flash.c gcc -o $@ unit-update-flash.c ../../src/image.c $(WOLFBOOT_LIB_WOLFSSL)/wolfcrypt/src/sha256.c $(CFLAGS) $(LDFLAGS) +unit-update-flash-delta: ../../include/target.h unit-update-flash.c + gcc -o $@ unit-update-flash.c ../../src/image.c ../../src/delta.c \ + $(WOLFBOOT_LIB_WOLFSSL)/wolfcrypt/src/sha256.c $(CFLAGS) $(LDFLAGS) + unit-update-flash-enc:CFLAGS+=-DMOCK_PARTITIONS -DWOLFBOOT_NO_SIGN -DUNIT_TEST_AUTH \ -DWOLFBOOT_HASH_SHA256 -DPRINTF_ENABLED -DEXT_FLASH -DPART_UPDATE_EXT \ -DPART_SWAP_EXT -DEXT_ENCRYPTED -DENCRYPT_WITH_CHACHA -DHAVE_CHACHA \ - -DCUSTOM_ENCRYPT_KEY -DUNIT_TEST_FALLBACK_ONLY + -DCUSTOM_ENCRYPT_KEY -DUNIT_TEST_FALLBACK_ONLY \ + -DWOLFBOOT_ORIGIN=MOCK_ADDRESS_BOOT -DBOOTLOADER_PARTITION_SIZE=WOLFBOOT_PARTITION_SIZE unit-update-flash-enc: ../../include/target.h unit-update-flash.c gcc -o $@ unit-update-flash.c ../../src/image.c \ $(WOLFBOOT_LIB_WOLFSSL)/wolfcrypt/src/sha256.c \ @@ -276,6 +321,7 @@ unit-multiboot: unit-multiboot.c covclean: rm -f *.gcov *.gcno *.gcda coverage.* + rm -f unit-sign-encrypted-output-* clean: covclean rm -f $(TESTS) *.o *.gcno *.gcda coverage.* diff --git a/tools/unit-tests/unit-delta.c b/tools/unit-tests/unit-delta.c index d648e39841..060f77bc97 100644 --- a/tools/unit-tests/unit-delta.c +++ b/tools/unit-tests/unit-delta.c @@ -34,6 +34,7 @@ #define PATCH_SIZE 8192 #define DST_SIZE 4096 #define DIFF_SIZE 8192 +#define DELTA_OFFSET_LIMIT (1U << 24) START_TEST(test_wb_patch_init_invalid) @@ -238,6 +239,31 @@ START_TEST(test_wb_diff_preserves_main_loop_header_margin_for_escape) } END_TEST +START_TEST(test_wb_diff_rejects_match_offsets_beyond_24_bits) +{ + WB_DIFF_CTX diff_ctx; + uint8_t *src_a; + uint8_t src_b[BLOCK_HDR_SIZE + 1] = {0}; + uint8_t patch[DELTA_BLOCK_SIZE] = {0}; + size_t src_a_size = DELTA_OFFSET_LIMIT + BLOCK_HDR_SIZE; + int ret; + + src_a = calloc(1, src_a_size); + ck_assert_ptr_nonnull(src_a); + + memset(src_a + DELTA_OFFSET_LIMIT, 0x5a, BLOCK_HDR_SIZE); + memset(src_b, 0x5a, BLOCK_HDR_SIZE); + + ret = wb_diff_init(&diff_ctx, src_a, src_a_size, src_b, sizeof(src_b)); + ck_assert_int_eq(ret, 0); + + ret = wb_diff(&diff_ctx, patch, sizeof(patch)); + ck_assert_int_eq(ret, -1); + + free(src_a); +} +END_TEST + static void initialize_buffers(uint8_t *src_a, uint8_t *src_b, size_t size) { uint32_t pseudo_rand = 0; @@ -276,54 +302,213 @@ static void initialize_buffers(uint8_t *src_a, uint8_t *src_b, size_t size) } -START_TEST(test_wb_patch_and_diff) +static uint8_t pattern_byte(uint32_t seed, size_t index) +{ + uint32_t value = seed ^ (uint32_t)index; + + value *= 1664525U; + value += 1013904223U; + value ^= (uint32_t)(index >> 8); + value ^= (uint32_t)(index >> 16); + + if ((uint8_t)value == ESC) { + value ^= 0x55U; + } + return (uint8_t)value; +} + +static void fill_pattern(uint8_t *dst, size_t size, uint32_t seed) +{ + size_t i; + + for (i = 0; i < size; ++i) { + dst[i] = pattern_byte(seed, i); + } +} + +static uint32_t run_roundtrip_case(const uint8_t *src_a, uint32_t size_a, + const uint8_t *src_b, uint32_t size_b, uint32_t patch_capacity) { WB_DIFF_CTX diff_ctx; WB_PATCH_CTX patch_ctx; - uint8_t src_a[SRC_SIZE]; - uint8_t src_b[SRC_SIZE]; - uint8_t patch[PATCH_SIZE]; - uint8_t patched_dst[DST_SIZE]; - int ret; - int i; + uint8_t *src_a_copy; + uint8_t *patch; + uint8_t *patched_dst; + uint8_t *new_patch; + uint8_t block[DELTA_BLOCK_SIZE] = {0}; uint32_t p_written = 0; + uint32_t dst_written = 0; + int ret; + src_a_copy = malloc(size_a); + patch = malloc(patch_capacity); + patched_dst = malloc(size_b); + ck_assert_ptr_nonnull(src_a_copy); + ck_assert_ptr_nonnull(patch); + ck_assert_ptr_nonnull(patched_dst); - initialize_buffers(src_a, src_b, SRC_SIZE); + memcpy(src_a_copy, src_a, size_a); - ret = wb_diff_init(&diff_ctx, src_a, SRC_SIZE, src_b, SRC_SIZE); + ret = wb_diff_init(&diff_ctx, src_a_copy, size_a, (uint8_t *)src_b, size_b); ck_assert_int_eq(ret, 0); - /* Create the patch */ - for (i = 0; i < SRC_SIZE; i += DELTA_BLOCK_SIZE) { - ret = wb_diff(&diff_ctx, patch + p_written, DELTA_BLOCK_SIZE); - ck_assert_int_ge(ret, 0); /* Should not be 0 until patch is over*/ - if (ret == 0) + for (;;) { + uint32_t remaining = patch_capacity - p_written; + + ck_assert_uint_ge(remaining, BLOCK_HDR_SIZE); + ret = wb_diff(&diff_ctx, patch + p_written, + remaining > DELTA_BLOCK_SIZE ? DELTA_BLOCK_SIZE : remaining); + ck_assert_int_ge(ret, 0); + if (ret == 0) { + if (diff_ctx.off_b < size_b) { + patch_capacity += DELTA_BLOCK_SIZE; + new_patch = realloc(patch, patch_capacity); + ck_assert_ptr_nonnull(new_patch); + patch = new_patch; + continue; + } break; - p_written += ret; + } + p_written += (uint32_t)ret; + ck_assert_uint_le(p_written, patch_capacity); } - ck_assert_int_gt(p_written, 0); /* Should not be 0 */ - printf("patch size: %u\n", p_written); - ret = wb_patch_init(&patch_ctx, src_a, SRC_SIZE, patch, p_written); + ck_assert_uint_eq(diff_ctx.off_b, size_b); + ck_assert_uint_gt(p_written, 0); + + ret = wb_patch_init(&patch_ctx, src_a_copy, size_a, patch, p_written); ck_assert_int_eq(ret, 0); - /* Apply the patch */ - for (i = 0; i < SRC_SIZE;) - { - ret = wb_patch(&patch_ctx, patched_dst + i, DELTA_BLOCK_SIZE); - ck_assert_int_ge(ret, 0); /* Should not be 0 until patch is over*/ - if (ret == 0) + for (;;) { + ret = wb_patch(&patch_ctx, block, sizeof(block)); + ck_assert_int_ge(ret, 0); + if (ret == 0) { break; - i += ret; + } + ck_assert_uint_le(dst_written + (uint32_t)ret, size_b); + memcpy(patched_dst + dst_written, block, (uint32_t)ret); + dst_written += (uint32_t)ret; } - ck_assert_int_gt(i, 0); /* Should not be 0 */ - ck_assert_int_eq(i, SRC_SIZE); // The patched length should match the buffer size - /* Verify that the patched destination matches src_b */ - for (i = 0; i < SRC_SIZE; ++i) { - ck_assert_uint_eq(patched_dst[i], src_b[i]); - } + ck_assert_uint_eq(dst_written, size_b); + ck_assert_int_eq(memcmp(patched_dst, src_b, size_b), 0); + + free(patched_dst); + free(patch); + free(src_a_copy); + return p_written; +} + +START_TEST(test_wb_patch_and_diff) +{ + uint8_t src_a[SRC_SIZE]; + uint8_t src_b[SRC_SIZE]; + uint32_t p_written = 0; + + initialize_buffers(src_a, src_b, SRC_SIZE); + + p_written = run_roundtrip_case(src_a, SRC_SIZE, src_b, SRC_SIZE, PATCH_SIZE); + printf("patch size: %u\n", p_written); +} +END_TEST + +START_TEST(test_wb_patch_and_diff_identical_images) +{ + uint8_t src_a[SRC_SIZE]; + uint32_t p_written; + + fill_pattern(src_a, sizeof(src_a), 0x12345678U); + + p_written = run_roundtrip_case(src_a, sizeof(src_a), src_a, sizeof(src_a), + PATCH_SIZE); + ck_assert_uint_lt(p_written, 64); +} +END_TEST + +START_TEST(test_wb_patch_and_diff_completely_different_images) +{ + uint8_t src_a[SRC_SIZE]; + uint8_t src_b[SRC_SIZE]; + + fill_pattern(src_a, sizeof(src_a), 0x11111111U); + fill_pattern(src_b, sizeof(src_b), 0x22222222U); + + (void)run_roundtrip_case(src_a, sizeof(src_a), src_b, sizeof(src_b), + sizeof(src_b) + DELTA_BLOCK_SIZE); +} +END_TEST + +START_TEST(test_wb_patch_and_diff_all_escape_images) +{ + uint8_t src_a[SRC_SIZE]; + uint8_t src_b[SRC_SIZE]; + + memset(src_a, ESC, sizeof(src_a)); + memset(src_b, ESC, sizeof(src_b)); + + (void)run_roundtrip_case(src_a, sizeof(src_a), src_b, sizeof(src_b), + PATCH_SIZE); +} +END_TEST + +START_TEST(test_wb_patch_and_diff_multi_sector_images) +{ + int sector_size_ret; + uint32_t size; + uint8_t *src_a; + uint8_t *src_b; + + sector_size_ret = wb_diff_get_sector_size(); + ck_assert_int_gt(sector_size_ret, BLOCK_HDR_SIZE); + size = (uint32_t)(3 * sector_size_ret + 37); + + src_a = malloc(size); + src_b = malloc(size); + ck_assert_ptr_nonnull(src_a); + ck_assert_ptr_nonnull(src_b); + + fill_pattern(src_a, size, 0xA5A5A5A5U); + memcpy(src_b, src_a, size); + src_b[sector_size_ret - 1] ^= 0x11; + src_b[sector_size_ret] ^= 0x22; + src_b[(2 * sector_size_ret) + 5] = ESC; + + (void)run_roundtrip_case(src_a, size, src_b, size, size + DELTA_BLOCK_SIZE); + + free(src_b); + free(src_a); +} +END_TEST + +START_TEST(test_wb_patch_and_diff_size_changing_update) +{ + uint8_t src_a[2048]; + uint8_t src_b[3077]; + + fill_pattern(src_a, sizeof(src_a), 0x31415926U); + fill_pattern(src_b, sizeof(src_b), 0x27182818U); + memcpy(src_b + 512, src_a, sizeof(src_a)); + src_b[0] = ESC; + src_b[sizeof(src_b) - 1] ^= 0x5A; + + (void)run_roundtrip_case(src_a, sizeof(src_a), src_b, sizeof(src_b), + sizeof(src_b) + DELTA_BLOCK_SIZE); +} +END_TEST + +START_TEST(test_wb_patch_and_diff_single_byte_difference) +{ + uint8_t src_a[SRC_SIZE]; + uint8_t src_b[SRC_SIZE]; + uint32_t p_written; + + fill_pattern(src_a, sizeof(src_a), 0x0BADB002U); + memcpy(src_b, src_a, sizeof(src_a)); + src_b[1537] ^= 0x01; + + p_written = run_roundtrip_case(src_a, sizeof(src_a), src_b, sizeof(src_b), + PATCH_SIZE); + ck_assert_uint_lt(p_written, 64); } END_TEST @@ -348,7 +533,14 @@ Suite *patch_diff_suite(void) tcase_add_test(tc_wolfboot_delta, test_wb_diff_self_match_extends_to_src_b_end); tcase_add_test(tc_wolfboot_delta, test_wb_diff_preserves_trailing_header_margin_for_escape); tcase_add_test(tc_wolfboot_delta, test_wb_diff_preserves_main_loop_header_margin_for_escape); + tcase_add_test(tc_wolfboot_delta, test_wb_diff_rejects_match_offsets_beyond_24_bits); tcase_add_test(tc_wolfboot_delta, test_wb_patch_and_diff); + tcase_add_test(tc_wolfboot_delta, test_wb_patch_and_diff_identical_images); + tcase_add_test(tc_wolfboot_delta, test_wb_patch_and_diff_completely_different_images); + tcase_add_test(tc_wolfboot_delta, test_wb_patch_and_diff_all_escape_images); + tcase_add_test(tc_wolfboot_delta, test_wb_patch_and_diff_multi_sector_images); + tcase_add_test(tc_wolfboot_delta, test_wb_patch_and_diff_size_changing_update); + tcase_add_test(tc_wolfboot_delta, test_wb_patch_and_diff_single_byte_difference); suite_add_tcase(s, tc_wolfboot_delta); return s; diff --git a/tools/unit-tests/unit-enc-nvm.c b/tools/unit-tests/unit-enc-nvm.c index ce98c88ae6..a4811c3ff4 100644 --- a/tools/unit-tests/unit-enc-nvm.c +++ b/tools/unit-tests/unit-enc-nvm.c @@ -291,6 +291,65 @@ START_TEST (test_nvm_update_with_encryption) } END_TEST +START_TEST(test_set_encrypt_key_propagates_flash_write_error) +{ + int ret; + static const uint8_t key[ENCRYPT_KEY_SIZE] = { 0x11 }; + static const uint8_t nonce[ENCRYPT_NONCE_SIZE] = { 0x22 }; + + ret = mmap_file("/tmp/wolfboot-unit-enc-key.bin", (void *)MOCK_ADDRESS, + WOLFBOOT_PARTITION_SIZE, NULL); + ck_assert(ret >= 0); + ret = mmap_file("/tmp/wolfboot-unit-enc-key-int.bin", + (void *)MOCK_ADDRESS_BOOT, WOLFBOOT_PARTITION_SIZE, NULL); + ck_assert(ret >= 0); + ret = mmap_file("/tmp/wolfboot-unit-enc-key-swap.bin", (void *)MOCK_ADDRESS_SWAP, + WOLFBOOT_SECTOR_SIZE, NULL); + ck_assert(ret >= 0); + + hal_flash_unlock(); + wolfBoot_erase_partition(PART_BOOT); + hal_flash_lock(); + + hal_flash_write_fail = 1; + ret = wolfBoot_set_encrypt_key(key, nonce); + + ck_assert_int_eq(ret, -1); + ck_assert_msg(locked, "The FLASH was left unlocked.\n"); +} +END_TEST + +START_TEST(test_erase_encrypt_key_propagates_flash_write_error) +{ + int ret; + static const uint8_t key[ENCRYPT_KEY_SIZE] = { 0x33 }; + static const uint8_t nonce[ENCRYPT_NONCE_SIZE] = { 0x44 }; + + ret = mmap_file("/tmp/wolfboot-unit-enc-erase.bin", (void *)MOCK_ADDRESS, + WOLFBOOT_PARTITION_SIZE, NULL); + ck_assert(ret >= 0); + ret = mmap_file("/tmp/wolfboot-unit-enc-erase-int.bin", + (void *)MOCK_ADDRESS_BOOT, WOLFBOOT_PARTITION_SIZE, NULL); + ck_assert(ret >= 0); + ret = mmap_file("/tmp/wolfboot-unit-enc-erase-swap.bin", + (void *)MOCK_ADDRESS_SWAP, WOLFBOOT_SECTOR_SIZE, NULL); + ck_assert(ret >= 0); + + hal_flash_unlock(); + wolfBoot_erase_partition(PART_BOOT); + hal_flash_lock(); + + ret = wolfBoot_set_encrypt_key(key, nonce); + ck_assert_int_eq(ret, 0); + + hal_flash_write_fail = 1; + ret = wolfBoot_erase_encrypt_key(); + + ck_assert_int_eq(ret, -1); + ck_assert_msg(locked, "The FLASH was left unlocked.\n"); +} +END_TEST + Suite *wolfboot_suite(void) { @@ -300,6 +359,10 @@ Suite *wolfboot_suite(void) /* Test cases */ TCase *nvm_update_with_encryption = tcase_create("NVM update with encryption"); tcase_add_test(nvm_update_with_encryption, test_nvm_update_with_encryption); + tcase_add_test(nvm_update_with_encryption, + test_set_encrypt_key_propagates_flash_write_error); + tcase_add_test(nvm_update_with_encryption, + test_erase_encrypt_key_propagates_flash_write_error); suite_add_tcase(s, nvm_update_with_encryption); return s; diff --git a/tools/unit-tests/unit-image.c b/tools/unit-tests/unit-image.c index b20611182c..fef59d2c95 100644 --- a/tools/unit-tests/unit-image.c +++ b/tools/unit-tests/unit-image.c @@ -186,7 +186,7 @@ static const unsigned char test_img_v200000000_wrong_pubkey_bin[] = { static uint16_t _find_header(uint8_t *haystack, uint16_t type, uint8_t **ptr); -static void patch_pubkey_hint(uint8_t *img, uint32_t img_len) +static void patch_pubkey_hint_slot(uint8_t *img, uint32_t img_len, uint8_t slot) { uint8_t *ptr = NULL; uint16_t len; @@ -195,10 +195,15 @@ static void patch_pubkey_hint(uint8_t *img, uint32_t img_len) (void)img_len; len = _find_header(img + IMAGE_HEADER_OFFSET, HDR_PUBKEY, &ptr); ck_assert_int_eq(len, WOLFBOOT_SHA_DIGEST_SIZE); - key_hash(0, hash); + key_hash(slot, hash); memcpy(ptr, hash, WOLFBOOT_SHA_DIGEST_SIZE); } +static void patch_pubkey_hint(uint8_t *img, uint32_t img_len) +{ + patch_pubkey_hint_slot(img, img_len, 0); +} + static void patch_signature_len(uint8_t *img, uint32_t img_len, uint16_t new_len) { uint8_t *ptr = NULL; @@ -225,6 +230,39 @@ static void patch_image_type_auth(uint8_t *img, uint32_t img_len) ptr[0] = (uint8_t)(type & 0xFF); ptr[1] = (uint8_t)(type >> 8); } + +static void patch_image_type_auth_value(uint8_t *img, uint32_t img_len, + uint16_t auth) +{ + uint8_t *ptr = NULL; + uint16_t len; + uint16_t type; + + (void)img_len; + len = _find_header(img + IMAGE_HEADER_OFFSET, HDR_IMG_TYPE, &ptr); + ck_assert_int_eq(len, sizeof(uint16_t)); + type = (uint16_t)(ptr[0] | (ptr[1] << 8)); + type = (uint16_t)((type & ~HDR_IMG_TYPE_AUTH_MASK) | + (auth & HDR_IMG_TYPE_AUTH_MASK)); + ptr[0] = (uint8_t)(type & 0xFF); + ptr[1] = (uint8_t)(type >> 8); +} + +static void patch_image_type_part(uint8_t *img, uint32_t img_len, uint16_t part) +{ + uint8_t *ptr = NULL; + uint16_t len; + uint16_t type; + + (void)img_len; + len = _find_header(img + IMAGE_HEADER_OFFSET, HDR_IMG_TYPE, &ptr); + ck_assert_int_eq(len, sizeof(uint16_t)); + type = (uint16_t)(ptr[0] | (ptr[1] << 8)); + type = (uint16_t)((type & ~HDR_IMG_TYPE_PART_MASK) | + (part & HDR_IMG_TYPE_PART_MASK)); + ptr[0] = (uint8_t)(type & 0xFF); + ptr[1] = (uint8_t)(type >> 8); +} static const unsigned int test_img_len = 275; @@ -672,6 +710,80 @@ START_TEST(test_verify_authenticity_bad_siglen) ck_assert_int_eq(ret, -1); } END_TEST + +START_TEST(test_verify_authenticity_rejects_mismatched_auth_type) +{ + struct wolfBoot_image test_img; + uint8_t buf[sizeof(test_img_v200000000_signed_bin)]; + int ret; + + memcpy(buf, test_img_v200000000_signed_bin, sizeof(buf)); + patch_image_type_auth_value(buf, sizeof(buf), HDR_IMG_TYPE_AUTH_RSA2048); + patch_pubkey_hint(buf, sizeof(buf)); + + find_header_mocked = 0; + find_header_fail = 0; + hdr_cpy_done = 0; + ext_flash_write(0, buf, sizeof(buf)); + + memset(&test_img, 0, sizeof(struct wolfBoot_image)); + test_img.part = PART_UPDATE; + test_img.signature_ok = 1; + ret = wolfBoot_verify_authenticity(&test_img); + ck_assert_int_eq(ret, -1); +} +END_TEST + +START_TEST(test_verify_authenticity_rejects_disallowed_key_mask) +{ + struct wolfBoot_image test_img; + uint8_t buf[sizeof(test_img_v200000000_signed_bin)]; + int ret; + + memcpy(buf, test_img_v200000000_signed_bin, sizeof(buf)); + patch_image_type_auth(buf, sizeof(buf)); + patch_pubkey_hint_slot(buf, sizeof(buf), 1); + patch_image_type_part(buf, sizeof(buf), HDR_IMG_TYPE_WOLFBOOT); + + find_header_mocked = 0; + find_header_fail = 0; + hdr_cpy_done = 0; + ext_flash_write(0, buf, sizeof(buf)); + + memset(&test_img, 0, sizeof(struct wolfBoot_image)); + test_img.part = PART_UPDATE; + test_img.signature_ok = 1; + ret = wolfBoot_verify_authenticity(&test_img); + ck_assert_int_eq(ret, -1); +} +END_TEST + +START_TEST(test_verify_authenticity_allows_permitted_key_mask) +{ + struct wolfBoot_image test_img; + uint8_t buf[sizeof(test_img_v200000000_signed_bin)]; + int ret; + + memcpy(buf, test_img_v200000000_signed_bin, sizeof(buf)); + patch_image_type_auth(buf, sizeof(buf)); + patch_pubkey_hint_slot(buf, sizeof(buf), 1); + patch_image_type_part(buf, sizeof(buf), HDR_IMG_TYPE_APP); + + find_header_mocked = 0; + find_header_fail = 0; + hdr_cpy_done = 0; + ecc_import_fail = 0; + ecc_init_fail = 0; + ext_flash_erase(0, 2 * WOLFBOOT_SECTOR_SIZE); + ext_flash_write(0, buf, sizeof(buf)); + + memset(&test_img, 0, sizeof(struct wolfBoot_image)); + test_img.part = PART_UPDATE; + test_img.signature_ok = 1; + ret = wolfBoot_verify_authenticity(&test_img); + ck_assert_int_eq(ret, 0); +} +END_TEST #endif #ifdef WOLFBOOT_FIXED_PARTITIONS @@ -826,6 +938,12 @@ Suite *wolfboot_suite(void) tcase_set_timeout(tcase_verify_authenticity, 20); tcase_add_test(tcase_verify_authenticity, test_verify_authenticity); tcase_add_test(tcase_verify_authenticity, test_verify_authenticity_bad_siglen); + tcase_add_test(tcase_verify_authenticity, + test_verify_authenticity_rejects_mismatched_auth_type); + tcase_add_test(tcase_verify_authenticity, + test_verify_authenticity_rejects_disallowed_key_mask); + tcase_add_test(tcase_verify_authenticity, + test_verify_authenticity_allows_permitted_key_mask); suite_add_tcase(s, tcase_verify_authenticity); #endif diff --git a/tools/unit-tests/unit-keystore.c b/tools/unit-tests/unit-keystore.c index 27856dc6a9..7b9371eb6d 100644 --- a/tools/unit-tests/unit-keystore.c +++ b/tools/unit-tests/unit-keystore.c @@ -117,7 +117,7 @@ const KEYSTORE_SECTION struct keystore_slot PubKeys[NUM_PUBKEYS] = { { .slot_id = 1, .key_type = UNIT_KEY_TYPE, - .part_id_mask = 0xFFFFFFFF, + .part_id_mask = KEY_VERIFY_APP_ONLY, .pubkey_size = UNIT_PUBKEY_SIZE, .pubkey = { 0x00 }, }, diff --git a/tools/unit-tests/unit-policy-create.c b/tools/unit-tests/unit-policy-create.c new file mode 100644 index 0000000000..d00638cd0d --- /dev/null +++ b/tools/unit-tests/unit-policy-create.c @@ -0,0 +1,169 @@ +#include +#include +#include +#include +#include + +#include +#include +#include +#include "tpm.h" + +static int policy_pcr_make_calls; +static int policy_ref_make_calls; +static int hash_digest_size_calls; + +#define TPM2_IoCb NULL +#define XSTRTOL strtol + +int wolfTPM2_PolicyPCRMake(TPM_ALG_ID pcrAlg, byte* pcrArray, word32 pcrArraySz, + const byte* pcrDigest, word32 pcrDigestSz, byte* digest, word32* digestSz) +{ + (void)pcrAlg; + (void)pcrArray; + (void)pcrArraySz; + (void)pcrDigest; + (void)pcrDigestSz; + (void)digest; + (void)digestSz; + policy_pcr_make_calls++; + return -200; +} + +int wolfTPM2_PolicyRefMake(TPM_ALG_ID pcrAlg, byte* digest, word32* digestSz, + const byte* policyRef, word32 policyRefSz) +{ + (void)pcrAlg; + (void)digest; + (void)digestSz; + (void)policyRef; + (void)policyRefSz; + policy_ref_make_calls++; + return -201; +} + +int wolfTPM2_Init(WOLFTPM2_DEV* dev, TPM2HalIoCb ioCb, void* userCtx) +{ + (void)dev; + (void)ioCb; + (void)userCtx; + return 0; +} + +int wolfTPM2_PCRGetDigest(WOLFTPM2_DEV* dev, TPM_ALG_ID pcrAlg, byte* pcrArray, + word32 pcrArraySz, byte* pcrDigest, word32* pcrDigestSz) +{ + (void)dev; + (void)pcrAlg; + (void)pcrArray; + (void)pcrArraySz; + (void)pcrDigest; + (void)pcrDigestSz; + return -202; +} + +int wolfTPM2_Cleanup(WOLFTPM2_DEV* dev) +{ + (void)dev; + return 0; +} + +int TPM2_GetHashDigestSize(TPMI_ALG_HASH hashAlg) +{ + (void)hashAlg; + hash_digest_size_calls++; + return 32; +} + +const char* TPM2_GetAlgName(TPM_ALG_ID alg) +{ + (void)alg; + return "SHA256"; +} + +const char* wolfTPM2_GetRCString(int rc) +{ + (void)rc; + return "stub"; +} + +#define main policy_create_tool_main +#include "../tpm/policy_create.c" +#undef main + +static void setup(void) +{ + policy_pcr_make_calls = 0; + policy_ref_make_calls = 0; + hash_digest_size_calls = 0; +} + +static void make_oversized_hex_arg(char* dst, size_t dst_sz, const char* prefix) +{ + size_t prefix_len = strlen(prefix); + size_t hex_len = (WC_MAX_DIGEST_SIZE * 2U) + 2U; + + ck_assert_uint_gt(dst_sz, prefix_len + hex_len); + memcpy(dst, prefix, prefix_len); + memset(dst + prefix_len, 'a', hex_len); + dst[prefix_len + hex_len] = '\0'; +} + +START_TEST(test_policy_create_rejects_oversized_pcr_digest) +{ + char arg[sizeof("-pcrdigest=") + (WC_MAX_DIGEST_SIZE * 2) + 3]; + char* argv[] = { (char*)"policy_create", arg }; + int rc; + + make_oversized_hex_arg(arg, sizeof(arg), "-pcrdigest="); + rc = policy_create_tool_main(2, argv); + + ck_assert_int_eq(rc, -1); + ck_assert_int_eq(hash_digest_size_calls, 0); + ck_assert_int_eq(policy_pcr_make_calls, 0); + ck_assert_int_eq(policy_ref_make_calls, 0); +} +END_TEST + +START_TEST(test_policy_create_rejects_invalid_pcr_digest_hex) +{ + char arg[] = "-pcrdigest=zz"; + char* argv[] = { (char*)"policy_create", arg }; + int rc; + + rc = policy_create_tool_main(2, argv); + + ck_assert_int_eq(rc, -1); + ck_assert_int_eq(hash_digest_size_calls, 0); + ck_assert_int_eq(policy_pcr_make_calls, 0); + ck_assert_int_eq(policy_ref_make_calls, 0); +} +END_TEST + +static Suite* policy_create_suite(void) +{ + Suite* s; + TCase* tc; + + s = suite_create("policy_create"); + tc = tcase_create("argument_validation"); + tcase_add_checked_fixture(tc, setup, NULL); + tcase_add_test(tc, test_policy_create_rejects_oversized_pcr_digest); + tcase_add_test(tc, test_policy_create_rejects_invalid_pcr_digest_hex); + suite_add_tcase(s, tc); + return s; +} + +int main(void) +{ + Suite* s; + SRunner* sr; + int failed; + + s = policy_create_suite(); + sr = srunner_create(s); + srunner_run_all(sr, CK_NORMAL); + failed = srunner_ntests_failed(sr); + srunner_free(sr); + return failed == 0 ? 0 : 1; +} diff --git a/tools/unit-tests/unit-sign-encrypted-output.c b/tools/unit-tests/unit-sign-encrypted-output.c new file mode 100644 index 0000000000..0741ef0ef5 --- /dev/null +++ b/tools/unit-tests/unit-sign-encrypted-output.c @@ -0,0 +1,639 @@ +/* unit-sign-encrypted-output.c + * + * Unit test for sign tool encrypted output error handling. + */ + +#include +#include +#include +#include +#include +#include +#include +#include + +#define WOLFBOOT_HASH_SHA256 +#define IMAGE_HEADER_SIZE 512 + +static const char *mock_fail_open_path; +static int mock_fail_open_on_call; +static int mock_open_call_count; +static int mock_null_fwrite_calls; +static int mock_null_fread_calls; +static int mock_null_fclose_calls; +static char mock_last_error[512]; + +static FILE *mock_fopen(const char *path, const char *mode); +static size_t mock_fread(void *ptr, size_t size, size_t nmemb, FILE *stream); +static size_t mock_fwrite(const void *ptr, size_t size, size_t nmemb, FILE *stream); +static int mock_fclose(FILE *stream); +static int mock_fprintf(FILE *stream, const char *format, ...); + +#define main wolfboot_sign_main +#define fopen mock_fopen +#define fread mock_fread +#define fwrite mock_fwrite +#define fclose mock_fclose +#define fprintf mock_fprintf +#include "../keytools/sign.c" +#undef fprintf +#undef fclose +#undef fwrite +#undef fread +#undef fopen +#undef main + +#include "../../src/libwolfboot.c" + +static int locked; + +void hal_init(void) +{ +} + +int hal_flash_write(haladdr_t address, const uint8_t *data, int len) +{ + (void)address; + (void)data; + (void)len; + return 0; +} + +int hal_flash_erase(haladdr_t address, int len) +{ + (void)address; + (void)len; + return 0; +} + +void hal_flash_unlock(void) +{ + ck_assert_msg(locked, "Double unlock detected\n"); + locked--; +} + +void hal_flash_lock(void) +{ + ck_assert_msg(!locked, "Double lock detected\n"); + locked++; +} + +void hal_prepare_boot(void) +{ +} + +static void reset_mocks(const char *fail_open_path, int fail_open_on_call) +{ + mock_fail_open_path = fail_open_path; + mock_fail_open_on_call = fail_open_on_call; + mock_open_call_count = 0; + mock_null_fwrite_calls = 0; + mock_null_fread_calls = 0; + mock_null_fclose_calls = 0; + mock_last_error[0] = '\0'; +} + +static FILE *mock_fopen(const char *path, const char *mode) +{ + if (mock_fail_open_path != NULL && strcmp(path, mock_fail_open_path) == 0) { + mock_open_call_count++; + } + + if (mock_fail_open_path != NULL && strcmp(path, mock_fail_open_path) == 0 && + mock_open_call_count == mock_fail_open_on_call) { + errno = EACCES; + return NULL; + } + + return fopen(path, mode); +} + +static size_t mock_fread(void *ptr, size_t size, size_t nmemb, FILE *stream) +{ + if (stream == NULL) { + mock_null_fread_calls++; + return 0; + } + + return fread(ptr, size, nmemb, stream); +} + +static size_t mock_fwrite(const void *ptr, size_t size, size_t nmemb, FILE *stream) +{ + if (stream == NULL) { + mock_null_fwrite_calls++; + return 0; + } + + return fwrite(ptr, size, nmemb, stream); +} + +static int mock_fclose(FILE *stream) +{ + if (stream == NULL) { + mock_null_fclose_calls++; + return EOF; + } + + return fclose(stream); +} + +static int mock_fprintf(FILE *stream, const char *format, ...) +{ + int ret; + va_list args; + + va_start(args, format); + ret = vsnprintf(mock_last_error, sizeof(mock_last_error), format, args); + va_end(args); + + va_start(args, format); + ret = vfprintf(stream, format, args); + va_end(args); + + return ret; +} + +static int write_file(const char *path, const void *buf, size_t len) +{ + FILE *f = fopen(path, "wb"); + size_t written; + + if (f == NULL) { + return -1; + } + + written = fwrite(buf, 1, len, f); + fclose(f); + + return written == len ? 0 : -1; +} + +static int read_file(const char *path, uint8_t **buf, size_t *len) +{ + FILE *f; + struct stat st; + size_t read_len; + + if (stat(path, &st) != 0) { + return -1; + } + + *buf = malloc((size_t)st.st_size); + if (*buf == NULL) { + return -1; + } + + f = fopen(path, "rb"); + if (f == NULL) { + free(*buf); + *buf = NULL; + return -1; + } + + read_len = fread(*buf, 1, (size_t)st.st_size, f); + fclose(f); + if (read_len != (size_t)st.st_size) { + free(*buf); + *buf = NULL; + return -1; + } + + *len = read_len; + return 0; +} + +static void reset_cmd_defaults(void) +{ + memset(&CMD, 0, sizeof(CMD)); + CMD.sign = NO_SIGN; + CMD.hash_algo = HASH_SHA256; + CMD.partition_id = HDR_IMG_TYPE_APP; + CMD.header_sz = IMAGE_HEADER_SIZE; + CMD.fw_version = "7"; + CMD.no_ts = 1; +} + +static void free_custom_tlv_buffers(void) +{ + uint32_t i; + + for (i = 0; i < MAX_CUSTOM_TLVS; i++) { + free(CMD.custom_tlv[i].buffer); + CMD.custom_tlv[i].buffer = NULL; + } +} + +static void assert_header_bytes(const uint8_t *image, uint16_t tag, + const uint8_t *expected, uint16_t expected_len) +{ + uint8_t *value = NULL; + uint16_t len = wolfBoot_find_header((uint8_t *)image + IMAGE_HEADER_OFFSET, + tag, &value); + + ck_assert_uint_eq(len, expected_len); + ck_assert_ptr_nonnull(value); + ck_assert_msg(memcmp(value, expected, expected_len) == 0, + "Tag 0x%04x mismatch", tag); +} + +static uint16_t find_exact_fill_custom_len(void) +{ + uint16_t len; + + for (len = 1; len < IMAGE_HEADER_SIZE; len++) { + CMD.custom_tlvs = 1; + CMD.custom_tlv[0].len = len; + if (header_required_size(0, 0, 0) == CMD.header_sz) { + return len; + } + } + + return 0; +} + +static uint32_t find_cert_chain_len_for_required_size(int hash_algo, + uint32_t required_size, uint32_t secondary_key_sz) +{ + uint32_t len; + + reset_cmd_defaults(); + CMD.hash_algo = hash_algo; + CMD.hybrid = 1; + CMD.secondary_sign = SIGN_ED25519; + + for (len = 1; len < IMAGE_HEADER_SIZE; len++) { + if (header_required_size(0, len, secondary_key_sz) == required_size) { + return len; + } + } + + return 0; +} + +START_TEST(test_make_header_ex_fails_when_encrypted_output_open_fails) +{ + char tempdir[] = "/tmp/wolfboot-sign-XXXXXX"; + char image_path[PATH_MAX]; + char key_path[PATH_MAX]; + char output_path[PATH_MAX]; + char encrypted_output_path[PATH_MAX]; + uint8_t image_buf[] = { 0x01, 0x02, 0x03, 0x04 }; + uint8_t key_buf[ENCRYPT_KEY_SIZE_AES128 + ENCRYPT_NONCE_SIZE_AES]; + uint8_t pubkey[] = { 0xA5 }; + int ret; + + ck_assert_ptr_nonnull(mkdtemp(tempdir)); + + snprintf(image_path, sizeof(image_path), "%s/image.bin", tempdir); + snprintf(key_path, sizeof(key_path), "%s/encrypt.key", tempdir); + snprintf(output_path, sizeof(output_path), "%s/output.bin", tempdir); + snprintf(encrypted_output_path, sizeof(encrypted_output_path), + "%s/encrypted-output.bin", tempdir); + + memset(key_buf, 0x5A, sizeof(key_buf)); + ck_assert_int_eq(write_file(image_path, image_buf, sizeof(image_buf)), 0); + ck_assert_int_eq(write_file(key_path, key_buf, sizeof(key_buf)), 0); + + reset_cmd_defaults(); + CMD.encrypt = ENC_AES128; + CMD.header_sz = 256; + CMD.encrypt_key_file = key_path; + snprintf(CMD.output_encrypted_image_file, + sizeof(CMD.output_encrypted_image_file), "%s", encrypted_output_path); + + reset_mocks(encrypted_output_path, 1); + ret = make_header_ex(0, pubkey, sizeof(pubkey), image_path, output_path, + 0, 0, 0, 0, NULL, 0, NULL, 0); + + ck_assert_int_ne(ret, 0); + ck_assert_int_eq(mock_null_fwrite_calls, 0); + ck_assert_int_eq(mock_null_fread_calls, 0); + ck_assert_int_eq(mock_null_fclose_calls, 0); + ck_assert_ptr_nonnull(strstr(mock_last_error, encrypted_output_path)); + ck_assert_ptr_null(strstr(mock_last_error, key_path)); + + unlink(output_path); + unlink(key_path); + unlink(image_path); + rmdir(tempdir); +} +END_TEST + +START_TEST(test_make_header_ex_fails_when_image_reopen_fails) +{ + char tempdir[] = "/tmp/wolfboot-sign-XXXXXX"; + char image_path[PATH_MAX]; + char output_path[PATH_MAX]; + uint8_t image_buf[] = { 0x01, 0x02, 0x03, 0x04 }; + uint8_t pubkey[] = { 0xA5 }; + int ret; + + ck_assert_ptr_nonnull(mkdtemp(tempdir)); + + snprintf(image_path, sizeof(image_path), "%s/image.bin", tempdir); + snprintf(output_path, sizeof(output_path), "%s/output.bin", tempdir); + + ck_assert_int_eq(write_file(image_path, image_buf, sizeof(image_buf)), 0); + + reset_cmd_defaults(); + CMD.header_sz = 256; + + reset_mocks(image_path, 2); + ret = make_header_ex(0, pubkey, sizeof(pubkey), image_path, output_path, + 0, 0, 0, 0, NULL, 0, NULL, 0); + + ck_assert_int_ne(ret, 0); + ck_assert_int_eq(mock_null_fwrite_calls, 0); + ck_assert_int_eq(mock_null_fread_calls, 0); + ck_assert_int_eq(mock_null_fclose_calls, 0); + + unlink(output_path); + unlink(image_path); + rmdir(tempdir); +} +END_TEST + +START_TEST(test_make_header_ex_grows_header_for_cert_chain_and_digest_tlvs) +{ + char tempdir[] = "/tmp/wolfboot-sign-XXXXXX"; + char image_path[PATH_MAX]; + char output_path[PATH_MAX]; + char cert_chain_path[PATH_MAX]; + uint8_t *output_buf = NULL; + uint8_t *value = NULL; + uint8_t image_buf[] = { 0x01, 0x02, 0x03, 0x04 }; + uint8_t cert_chain_buf[200]; + uint8_t pubkey[] = { 0xA5 }; + struct stat st; + size_t output_len; + int ret; + + ck_assert_ptr_nonnull(mkdtemp(tempdir)); + + snprintf(image_path, sizeof(image_path), "%s/image.bin", tempdir); + snprintf(output_path, sizeof(output_path), "%s/output.bin", tempdir); + snprintf(cert_chain_path, sizeof(cert_chain_path), "%s/cert-chain.bin", + tempdir); + + memset(cert_chain_buf, 0xC3, sizeof(cert_chain_buf)); + ck_assert_int_eq(write_file(image_path, image_buf, sizeof(image_buf)), 0); + ck_assert_int_eq(write_file(cert_chain_path, cert_chain_buf, + sizeof(cert_chain_buf)), 0); + + reset_cmd_defaults(); + CMD.header_sz = 256; + CMD.cert_chain_file = cert_chain_path; + + reset_mocks(NULL, 0); + ret = make_header_ex(0, pubkey, sizeof(pubkey), image_path, output_path, + 0, 0, 0, 0, NULL, 0, NULL, 0); + + ck_assert_int_eq(ret, 0); + ck_assert_uint_eq(CMD.header_sz, 512); + ck_assert_int_eq(stat(output_path, &st), 0); + ck_assert_uint_eq((uint32_t)st.st_size, CMD.header_sz + sizeof(image_buf)); + ck_assert_int_eq(mock_null_fwrite_calls, 0); + ck_assert_int_eq(mock_null_fread_calls, 0); + ck_assert_int_eq(mock_null_fclose_calls, 0); + ck_assert_int_eq(read_file(output_path, &output_buf, &output_len), 0); + ck_assert_uint_eq(output_len, CMD.header_sz + sizeof(image_buf)); + assert_header_bytes(output_buf, HDR_CERT_CHAIN, cert_chain_buf, + sizeof(cert_chain_buf)); + ck_assert_uint_eq(wolfBoot_find_header(output_buf + IMAGE_HEADER_OFFSET, + HDR_PUBKEY, &value), HDR_SHA256_LEN); + ck_assert_uint_eq(wolfBoot_find_header(output_buf + IMAGE_HEADER_OFFSET, + HDR_SHA256, &value), HDR_SHA256_LEN); + + free(output_buf); + unlink(output_path); + unlink(cert_chain_path); + unlink(image_path); + rmdir(tempdir); +} +END_TEST + +START_TEST(test_make_header_ex_roundtrip_custom_tlvs_via_wolfboot_parser) +{ + char tempdir[] = "/tmp/wolfboot-sign-XXXXXX"; + char image_path[PATH_MAX]; + char output_path[PATH_MAX]; + uint8_t *output_buf = NULL; + uint8_t *value = NULL; + uint8_t image_buf[] = { 0x10, 0x20, 0x30, 0x40, 0x50 }; + uint8_t pubkey[] = { 0xA5, 0x5A, 0x33, 0xCC }; + uint8_t tlv_one[] = { 0xAB }; + uint8_t tlv_two[] = { 0x10, 0x11, 0x12, 0x13, 0x14 }; + uint8_t tlv_three[] = { 0x21, 0x22, 0x23, 0x24, 0x25, 0x26, 0x27, 0x28 }; + uint16_t image_type; + uint32_t version = 7; + size_t output_len; + int ret; + + ck_assert_ptr_nonnull(mkdtemp(tempdir)); + + snprintf(image_path, sizeof(image_path), "%s/image.bin", tempdir); + snprintf(output_path, sizeof(output_path), "%s/output.bin", tempdir); + ck_assert_int_eq(write_file(image_path, image_buf, sizeof(image_buf)), 0); + + reset_cmd_defaults(); + image_type = (uint16_t)((CMD.sign & HDR_IMG_TYPE_AUTH_MASK) | + CMD.partition_id); + CMD.custom_tlvs = 3; + CMD.custom_tlv[0].tag = 0x30; + CMD.custom_tlv[0].len = sizeof(tlv_one); + CMD.custom_tlv[0].buffer = malloc(sizeof(tlv_one)); + memcpy(CMD.custom_tlv[0].buffer, tlv_one, sizeof(tlv_one)); + CMD.custom_tlv[1].tag = 0x31; + CMD.custom_tlv[1].len = sizeof(tlv_two); + CMD.custom_tlv[1].buffer = malloc(sizeof(tlv_two)); + memcpy(CMD.custom_tlv[1].buffer, tlv_two, sizeof(tlv_two)); + CMD.custom_tlv[2].tag = 0x32; + CMD.custom_tlv[2].len = sizeof(tlv_three); + CMD.custom_tlv[2].buffer = malloc(sizeof(tlv_three)); + memcpy(CMD.custom_tlv[2].buffer, tlv_three, sizeof(tlv_three)); + + reset_mocks(NULL, 0); + ret = make_header_ex(0, pubkey, sizeof(pubkey), image_path, output_path, + 0, 0, 0, 0, NULL, 0, NULL, 0); + + ck_assert_int_eq(ret, 0); + ck_assert_int_eq(read_file(output_path, &output_buf, &output_len), 0); + ck_assert_uint_eq(output_len, CMD.header_sz + sizeof(image_buf)); + assert_header_bytes(output_buf, HDR_VERSION, (uint8_t *)&version, + sizeof(version)); + assert_header_bytes(output_buf, HDR_IMG_TYPE, (uint8_t *)&image_type, + sizeof(image_type)); + assert_header_bytes(output_buf, 0x30, tlv_one, sizeof(tlv_one)); + assert_header_bytes(output_buf, 0x31, tlv_two, sizeof(tlv_two)); + assert_header_bytes(output_buf, 0x32, tlv_three, sizeof(tlv_three)); + ck_assert_uint_eq(wolfBoot_find_header(output_buf + IMAGE_HEADER_OFFSET, + HDR_PUBKEY, &value), HDR_SHA256_LEN); + ck_assert_ptr_nonnull(value); + ck_assert_uint_eq((uintptr_t)value % 8U, 0); + ck_assert_uint_eq(wolfBoot_find_header(output_buf + IMAGE_HEADER_OFFSET, + 0x30, &value), sizeof(tlv_one)); + ck_assert_uint_eq((uintptr_t)value % 8U, 0); + ck_assert_uint_eq(wolfBoot_find_header(output_buf + IMAGE_HEADER_OFFSET, + 0x31, &value), sizeof(tlv_two)); + ck_assert_uint_eq((uintptr_t)value % 8U, 0); + ck_assert_uint_eq(wolfBoot_find_header(output_buf + IMAGE_HEADER_OFFSET, + 0x32, &value), sizeof(tlv_three)); + ck_assert_uint_eq((uintptr_t)value % 8U, 0); + ck_assert_uint_eq(wolfBoot_find_header(output_buf + IMAGE_HEADER_OFFSET, + HDR_SHA256, &value), HDR_SHA256_LEN); + + free(output_buf); + free_custom_tlv_buffers(); + unlink(output_path); + unlink(image_path); + rmdir(tempdir); +} +END_TEST + +START_TEST(test_make_header_ex_roundtrip_finds_tlv_that_exactly_fills_header) +{ + char tempdir[] = "/tmp/wolfboot-sign-XXXXXX"; + char image_path[PATH_MAX]; + char output_path[PATH_MAX]; + uint8_t *output_buf = NULL; + uint8_t image_buf[] = { 0x61, 0x62, 0x63, 0x64 }; + uint8_t pubkey[] = { 0x01, 0x02 }; + uint8_t *custom_buf = NULL; + uint8_t *value = NULL; + size_t output_len; + uint16_t exact_len; + int ret; + + ck_assert_ptr_nonnull(mkdtemp(tempdir)); + + snprintf(image_path, sizeof(image_path), "%s/image.bin", tempdir); + snprintf(output_path, sizeof(output_path), "%s/output.bin", tempdir); + ck_assert_int_eq(write_file(image_path, image_buf, sizeof(image_buf)), 0); + + reset_cmd_defaults(); + exact_len = find_exact_fill_custom_len(); + ck_assert_uint_ne(exact_len, 0); + custom_buf = malloc(exact_len); + ck_assert_ptr_nonnull(custom_buf); + memset(custom_buf, 0x6C, exact_len); + + CMD.custom_tlvs = 1; + CMD.custom_tlv[0].tag = 0x40; + CMD.custom_tlv[0].len = exact_len; + CMD.custom_tlv[0].buffer = custom_buf; + ck_assert_uint_eq(header_required_size(0, 0, 0), CMD.header_sz); + + reset_mocks(NULL, 0); + ret = make_header_ex(0, pubkey, sizeof(pubkey), image_path, output_path, + 0, 0, 0, 0, NULL, 0, NULL, 0); + + ck_assert_int_eq(ret, 0); + ck_assert_int_eq(read_file(output_path, &output_buf, &output_len), 0); + ck_assert_uint_eq(output_len, CMD.header_sz + sizeof(image_buf)); + ck_assert_uint_eq(wolfBoot_find_header(output_buf + IMAGE_HEADER_OFFSET, + 0x40, &value), exact_len); + ck_assert_ptr_nonnull(value); + ck_assert_msg(memcmp(value, custom_buf, exact_len) == 0, + "Exact-fit TLV did not roundtrip"); + + free(output_buf); + free_custom_tlv_buffers(); + unlink(output_path); + unlink(image_path); + rmdir(tempdir); +} +END_TEST + +START_TEST(test_make_header_ex_keeps_boundary_header_for_sha384_sha3_hybrid_cert_chain) +{ + static const int hash_algos[] = { HASH_SHA384, HASH_SHA3 }; + char tempdir[] = "/tmp/wolfboot-sign-XXXXXX"; + char image_path[PATH_MAX]; + char output_path[PATH_MAX]; + char cert_chain_path[PATH_MAX]; + uint8_t image_buf[] = { 0x71, 0x72, 0x73, 0x74 }; + uint8_t pubkey[] = { 0xA5, 0x5A, 0x33, 0xCC }; + uint8_t secondary_key[] = { 0x11, 0x22, 0x33, 0x44 }; + uint8_t *cert_chain_buf = NULL; + struct stat st; + size_t i; + int ret; + + ck_assert_ptr_nonnull(mkdtemp(tempdir)); + + snprintf(image_path, sizeof(image_path), "%s/image.bin", tempdir); + snprintf(output_path, sizeof(output_path), "%s/output.bin", tempdir); + snprintf(cert_chain_path, sizeof(cert_chain_path), "%s/cert-chain.bin", + tempdir); + ck_assert_int_eq(write_file(image_path, image_buf, sizeof(image_buf)), 0); + + for (i = 0; i < sizeof(hash_algos) / sizeof(hash_algos[0]); i++) { + uint32_t cert_chain_len = find_cert_chain_len_for_required_size( + hash_algos[i], IMAGE_HEADER_SIZE, sizeof(secondary_key)); + + ck_assert_uint_ne(cert_chain_len, 0); + cert_chain_buf = realloc(cert_chain_buf, cert_chain_len); + ck_assert_ptr_nonnull(cert_chain_buf); + memset(cert_chain_buf, 0xC3 + (int)i, cert_chain_len); + ck_assert_int_eq(write_file(cert_chain_path, cert_chain_buf, + cert_chain_len), 0); + + reset_cmd_defaults(); + CMD.hash_algo = hash_algos[i]; + CMD.hybrid = 1; + CMD.secondary_sign = SIGN_ED25519; + CMD.header_sz = IMAGE_HEADER_SIZE; + CMD.cert_chain_file = cert_chain_path; + + reset_mocks(NULL, 0); + ret = make_header_ex(0, pubkey, sizeof(pubkey), image_path, output_path, + 0, 0, 0, 0, secondary_key, sizeof(secondary_key), NULL, 0); + + ck_assert_int_eq(ret, 0); + ck_assert_uint_eq(CMD.header_sz, IMAGE_HEADER_SIZE); + ck_assert_int_eq(stat(output_path, &st), 0); + ck_assert_uint_eq((uint32_t)st.st_size, + IMAGE_HEADER_SIZE + sizeof(image_buf)); + unlink(output_path); + unlink(cert_chain_path); + } + + free(cert_chain_buf); + unlink(image_path); + rmdir(tempdir); +} +END_TEST + +Suite *wolfboot_suite(void) +{ + Suite *s = suite_create("sign-encrypted-output"); + TCase *tcase = tcase_create("make-header-ex"); + + tcase_add_test(tcase, test_make_header_ex_fails_when_encrypted_output_open_fails); + tcase_add_test(tcase, test_make_header_ex_fails_when_image_reopen_fails); + tcase_add_test(tcase, + test_make_header_ex_grows_header_for_cert_chain_and_digest_tlvs); + tcase_add_test(tcase, + test_make_header_ex_roundtrip_custom_tlvs_via_wolfboot_parser); + tcase_add_test(tcase, + test_make_header_ex_roundtrip_finds_tlv_that_exactly_fills_header); + tcase_add_test(tcase, + test_make_header_ex_keeps_boundary_header_for_sha384_sha3_hybrid_cert_chain); + suite_add_tcase(s, tcase); + + return s; +} + +int main(void) +{ + int failed; + Suite *s = wolfboot_suite(); + SRunner *runner = srunner_create(s); + + srunner_run_all(runner, CK_NORMAL); + failed = srunner_ntests_failed(runner); + srunner_free(runner); + + return failed == 0 ? 0 : 1; +} diff --git a/tools/unit-tests/unit-sign-encrypted-output.mkfrag b/tools/unit-tests/unit-sign-encrypted-output.mkfrag new file mode 100644 index 0000000000..69da996def --- /dev/null +++ b/tools/unit-tests/unit-sign-encrypted-output.mkfrag @@ -0,0 +1,30 @@ +KEYTOOLS_SIGN_SRCS=$(WOLFBOOT_LIB_WOLFSSL)/wolfcrypt/src/asn.c \ + $(WOLFBOOT_LIB_WOLFSSL)/wolfcrypt/src/aes.c \ + $(WOLFBOOT_LIB_WOLFSSL)/wolfcrypt/src/ecc.c \ + $(WOLFBOOT_LIB_WOLFSSL)/wolfcrypt/src/coding.c \ + $(WOLFBOOT_LIB_WOLFSSL)/wolfcrypt/src/chacha.c \ + $(WOLFBOOT_LIB_WOLFSSL)/wolfcrypt/src/ed25519.c \ + $(WOLFBOOT_LIB_WOLFSSL)/wolfcrypt/src/ed448.c \ + $(WOLFBOOT_LIB_WOLFSSL)/wolfcrypt/src/fe_operations.c \ + $(WOLFBOOT_LIB_WOLFSSL)/wolfcrypt/src/ge_operations.c \ + $(WOLFBOOT_LIB_WOLFSSL)/wolfcrypt/src/fe_448.c \ + $(WOLFBOOT_LIB_WOLFSSL)/wolfcrypt/src/ge_448.c \ + $(WOLFBOOT_LIB_WOLFSSL)/wolfcrypt/src/hash.c \ + $(WOLFBOOT_LIB_WOLFSSL)/wolfcrypt/src/memory.c \ + $(WOLFBOOT_LIB_WOLFSSL)/wolfcrypt/src/random.c \ + $(WOLFBOOT_LIB_WOLFSSL)/wolfcrypt/src/rsa.c \ + $(WOLFBOOT_LIB_WOLFSSL)/wolfcrypt/src/sp_int.c \ + $(WOLFBOOT_LIB_WOLFSSL)/wolfcrypt/src/sp_c32.c \ + $(WOLFBOOT_LIB_WOLFSSL)/wolfcrypt/src/sp_c64.c \ + $(WOLFBOOT_LIB_WOLFSSL)/wolfcrypt/src/sha3.c \ + $(WOLFBOOT_LIB_WOLFSSL)/wolfcrypt/src/sha256.c \ + $(WOLFBOOT_LIB_WOLFSSL)/wolfcrypt/src/sha512.c \ + $(WOLFBOOT_LIB_WOLFSSL)/wolfcrypt/src/tfm.c \ + $(WOLFBOOT_LIB_WOLFSSL)/wolfcrypt/src/wc_port.c \ + $(WOLFBOOT_LIB_WOLFSSL)/wolfcrypt/src/wolfmath.c \ + $(WOLFBOOT_LIB_WOLFSSL)/wolfcrypt/src/wc_lms.c \ + $(WOLFBOOT_LIB_WOLFSSL)/wolfcrypt/src/wc_lms_impl.c \ + $(WOLFBOOT_LIB_WOLFSSL)/wolfcrypt/src/wc_xmss.c \ + $(WOLFBOOT_LIB_WOLFSSL)/wolfcrypt/src/wc_xmss_impl.c \ + $(WOLFBOOT_LIB_WOLFSSL)/wolfcrypt/src/dilithium.c \ + ../../src/delta.c diff --git a/tools/unit-tests/unit-string.c b/tools/unit-tests/unit-string.c index 604a6eccfe..f98076fa9a 100644 --- a/tools/unit-tests/unit-string.c +++ b/tools/unit-tests/unit-string.c @@ -25,8 +25,12 @@ #define FAST_MEMCPY #include +#include #include #include +#ifdef __linux__ +#include +#endif #include "string.c" @@ -321,10 +325,43 @@ START_TEST(test_memcpy_memmove) ck_assert_int_eq(p[0], 0); ck_assert_int_eq(p[1], 1); + for (i = 0; i < 24; i++) { + p[i] = (unsigned char)i; + } + memmove(p + sizeof(unsigned long), p, sizeof(unsigned long) + 2); + for (i = 0; i < (int)(sizeof(unsigned long) + 2); i++) { + ck_assert_int_eq(p[sizeof(unsigned long) + i], i); + } + ck_assert_ptr_eq(memmove(p, p, 4), p); } END_TEST +#if defined(__linux__) && (SIZE_MAX > INT_MAX) && !defined(UNIT_TEST_COVERAGE) +START_TEST(test_memmove_large_overlap_length) +{ + size_t n = (size_t)INT_MAX + 2U; + size_t len = n + 1U; + unsigned char *region = mmap(NULL, len, PROT_READ | PROT_WRITE, + MAP_PRIVATE | MAP_ANONYMOUS, -1, 0); + + ck_assert_ptr_ne(region, MAP_FAILED); + + region[0] = 0x11; + region[1] = 0x22; + region[n - 1] = 0x33; + region[n] = 0x44; + + memmove(region + 1, region, n); + + ck_assert_uint_eq(region[1], 0x11); + ck_assert_uint_eq(region[n], 0x33); + + ck_assert_int_eq(munmap(region, len), 0); +} +END_TEST +#endif + START_TEST(test_memcpy_aligned_buffers) { union { @@ -443,6 +480,11 @@ Suite *string_suite(void) tcase_add_test(tcase_misc, test_strcpy_strncpy_strcat_strncat); tcase_add_test(tcase_misc, test_strncmp); tcase_add_test(tcase_misc, test_memcpy_memmove); +#if defined(__linux__) && (SIZE_MAX > INT_MAX) +#if !defined(UNIT_TEST_COVERAGE) + tcase_add_test(tcase_misc, test_memmove_large_overlap_length); +#endif +#endif tcase_add_test(tcase_misc, test_memcpy_aligned_buffers); tcase_add_test(tcase_misc, test_uart_writenum_basic); tcase_add_test(tcase_misc, test_uart_printf_formats); diff --git a/tools/unit-tests/unit-update-disk.c b/tools/unit-tests/unit-update-disk.c index 6c051be11f..2e7831ded3 100644 --- a/tools/unit-tests/unit-update-disk.c +++ b/tools/unit-tests/unit-update-disk.c @@ -1,17 +1,21 @@ #define WOLFBOOT_UPDATE_DISK #define WOLFBOOT_SKIP_BOOT_VERIFY +#define WOLFBOOT_SELF_UPDATE_MONOLITHIC +#define WOLFBOOT_SELF_HEADER #define EXT_ENCRYPTED #define ENCRYPT_WITH_CHACHA #define HAVE_CHACHA #define IMAGE_HEADER_SIZE 256 #define BOOT_PART_A 0 #define BOOT_PART_B 1 +#define MOCK_ADDRESS_BOOT 0xCD000000 #include #include #include #include +#include "hal.h" #include "target.h" #include "wolfboot/wolfboot.h" #include "image.h" @@ -188,6 +192,13 @@ void do_boot(const uint32_t *address) mock_boot_address = address; } +int hal_flash_protect(haladdr_t address, int len) +{ + (void)address; + (void)len; + return 0; +} + #include "update_disk.c" START_TEST(test_update_disk_zeroizes_key_material_on_panic) @@ -230,6 +241,24 @@ START_TEST(test_update_disk_zeroizes_key_material_before_boot) } END_TEST +START_TEST(test_update_disk_prefers_primary_partition_when_versions_equal) +{ + reset_mocks(); + build_image(part_a_image, 7, 0xA1); + build_image(part_b_image, 7, 0xB2); + + wolfBoot_start(); + + ck_assert_int_eq(wolfBoot_panicked, 0); + ck_assert_int_eq(mock_do_boot_called, 1); + ck_assert_ptr_eq(mock_boot_address, (const uint32_t *)WOLFBOOT_LOAD_ADDRESS); + ck_assert_int_eq(memcmp(load_buffer, part_a_image + IMAGE_HEADER_SIZE, + TEST_PAYLOAD_SIZE), 0); + ck_assert_int_ne(memcmp(load_buffer, part_b_image + IMAGE_HEADER_SIZE, + TEST_PAYLOAD_SIZE), 0); +} +END_TEST + START_TEST(test_get_decrypted_blob_version_rejects_truncated_version_tlv) { uint8_t hdr[IMAGE_HEADER_SIZE + 2]; @@ -266,6 +295,7 @@ Suite *wolfboot_suite(void) tcase_add_test(tc, test_update_disk_zeroizes_key_material_on_panic); tcase_add_test(tc, test_update_disk_zeroizes_key_material_before_boot); + tcase_add_test(tc, test_update_disk_prefers_primary_partition_when_versions_equal); tcase_add_test(tc, test_get_decrypted_blob_version_rejects_truncated_version_tlv); suite_add_tcase(s, tc); diff --git a/tools/unit-tests/unit-update-flash.c b/tools/unit-tests/unit-update-flash.c index 4272fe005d..0d1de87c1b 100644 --- a/tools/unit-tests/unit-update-flash.c +++ b/tools/unit-tests/unit-update-flash.c @@ -34,6 +34,7 @@ #include #include +#include #include "user_settings.h" #include "wolfboot/wolfboot.h" #include "libwolfboot.c" @@ -51,11 +52,19 @@ const char *argv0; static void reset_mock_stats(void); static void prepare_flash(void); static void cleanup_flash(void); +static int add_payload_type(uint8_t part, uint32_t version, uint32_t size, + uint16_t img_type); #ifdef CUSTOM_ENCRYPT_KEY +static int mock_get_encrypt_key_ret = 0; +static int mock_set_encrypt_key_ret = 0; +static int mock_set_encrypt_key_calls = 0; + int wolfBoot_get_encrypt_key(uint8_t *k, uint8_t *nonce) { int i; + if (mock_get_encrypt_key_ret != 0) + return mock_get_encrypt_key_ret; for (i = 0; i < ENCRYPT_KEY_SIZE; i++) { k[i] = (uint8_t)(i + 1); } @@ -69,7 +78,8 @@ int wolfBoot_set_encrypt_key(const uint8_t *key, const uint8_t *nonce) { (void)key; (void)nonce; - return 0; + mock_set_encrypt_key_calls++; + return mock_set_encrypt_key_ret; } int wolfBoot_erase_encrypt_key(void) @@ -78,6 +88,7 @@ int wolfBoot_erase_encrypt_key(void) } #endif +#ifndef UNIT_TEST_SELF_UPDATE_ONLY START_TEST (test_boot_success_sets_state) { uint8_t state = 0; @@ -96,27 +107,67 @@ START_TEST (test_boot_success_sets_state) cleanup_flash(); } END_TEST +#endif Suite *wolfboot_suite(void); int wolfBoot_staged_ok = 0; const uint32_t *wolfBoot_stage_address = (uint32_t *) 0xFFFFFFFF; +#ifdef RAM_CODE +static int arch_reboot_called = 0; +unsigned int _start_text = MOCK_ADDRESS_BOOT; +#endif void do_boot(const uint32_t *address) { /* Mock of do_boot */ +#ifndef ARCH_SIM if (wolfBoot_panicked) return; +#endif wolfBoot_staged_ok++; wolfBoot_stage_address = address; printf("Called do_boot with address %p\n", address); } +int hal_flash_protect(haladdr_t address, int len) +{ + (void)address; + (void)len; + return 0; +} + static void reset_mock_stats(void) { wolfBoot_staged_ok = 0; +#ifdef CUSTOM_ENCRYPT_KEY + mock_get_encrypt_key_ret = 0; + mock_set_encrypt_key_ret = 0; + mock_set_encrypt_key_calls = 0; +#endif +#ifndef ARCH_SIM wolfBoot_panicked = 0; +#endif + erased_boot = 0; + erased_update = 0; + erased_swap = 0; + erased_nvm_bank0 = 0; + erased_nvm_bank1 = 0; + erased_vault = 0; ext_flash_reset_lock(); +#ifdef RAM_CODE + arch_reboot_called = 0; +#endif +} + +static void clear_erase_stats(void) +{ + erased_boot = 0; + erased_update = 0; + erased_swap = 0; + erased_nvm_bank0 = 0; + erased_nvm_bank1 = 0; + erased_vault = 0; } @@ -148,9 +199,15 @@ static void cleanup_flash(void) #define DIGEST_TLV_OFF_IN_HDR 28 static int add_payload(uint8_t part, uint32_t version, uint32_t size) +{ + return add_payload_type(part, version, size, + HDR_IMG_TYPE_AUTH_NONE | HDR_IMG_TYPE_APP); +} + +static int add_payload_type(uint8_t part, uint32_t version, uint32_t size, + uint16_t img_type) { uint32_t word; - uint16_t word16; int i; uint8_t *base = (uint8_t *)(uintptr_t)WOLFBOOT_PARTITION_BOOT_ADDRESS; int ret; @@ -182,9 +239,8 @@ static int add_payload(uint8_t part, uint32_t version, uint32_t size) word = 2 << 16 | HDR_IMG_TYPE; hal_flash_write((uintptr_t)base + 16, (void *)&word, 4); - word16 = HDR_IMG_TYPE_AUTH_NONE | HDR_IMG_TYPE_APP; - hal_flash_write((uintptr_t)base + 20, (void *)&word16, 2); - printf("Written img_type: %04X\n", word16); + hal_flash_write((uintptr_t)base + 20, (void *)&img_type, 2); + printf("Written img_type: %04X\n", img_type); /* Add 28B header to sha calculation */ ret = wc_Sha256Update(&sha, base, DIGEST_TLV_OFF_IN_HDR); @@ -225,6 +281,104 @@ static int add_payload(uint8_t part, uint32_t version, uint32_t size) } +#ifdef RAM_CODE +void arch_reboot(void) +{ + arch_reboot_called++; +} + +START_TEST (test_self_update_sameversion_erased) +{ + reset_mock_stats(); + prepare_flash(); + clear_erase_stats(); + add_payload_type(PART_UPDATE, WOLFBOOT_VERSION, TEST_SIZE_SMALL, + HDR_IMG_TYPE_WOLFBOOT | HDR_IMG_TYPE_AUTH); + ext_flash_unlock(); + wolfBoot_set_partition_state(PART_UPDATE, IMG_STATE_UPDATING); + ext_flash_lock(); + + wolfBoot_check_self_update(); + + ck_assert_int_eq(arch_reboot_called, 0); + ck_assert_int_ge(erased_update, 1); + ck_assert_uint_eq(*(uint32_t *)(uintptr_t)WOLFBOOT_PARTITION_UPDATE_ADDRESS, + 0xFFFFFFFFu); + cleanup_flash(); +} +END_TEST + +START_TEST (test_self_update_oldversion_erased) +{ + reset_mock_stats(); + prepare_flash(); + clear_erase_stats(); + add_payload_type(PART_UPDATE, WOLFBOOT_VERSION - 1, TEST_SIZE_SMALL, + HDR_IMG_TYPE_WOLFBOOT | HDR_IMG_TYPE_AUTH); + ext_flash_unlock(); + wolfBoot_set_partition_state(PART_UPDATE, IMG_STATE_UPDATING); + ext_flash_lock(); + + wolfBoot_check_self_update(); + + ck_assert_int_eq(arch_reboot_called, 0); + ck_assert_int_ge(erased_update, 1); + ck_assert_uint_eq(*(uint32_t *)(uintptr_t)WOLFBOOT_PARTITION_UPDATE_ADDRESS, + 0xFFFFFFFFu); + cleanup_flash(); +} +END_TEST + +START_TEST (test_self_update_newversion_invalid_integrity_denied) +{ + uint8_t bad_digest[SHA256_DIGEST_SIZE]; + + reset_mock_stats(); + prepare_flash(); + clear_erase_stats(); + add_payload_type(PART_UPDATE, WOLFBOOT_VERSION + 1, TEST_SIZE_SMALL, + HDR_IMG_TYPE_WOLFBOOT | HDR_IMG_TYPE_AUTH); + memset(bad_digest, 0xBA, sizeof(bad_digest)); + ext_flash_unlock(); + ext_flash_write(WOLFBOOT_PARTITION_UPDATE_ADDRESS + DIGEST_TLV_OFF_IN_HDR + 4, + bad_digest, sizeof(bad_digest)); + wolfBoot_set_partition_state(PART_UPDATE, IMG_STATE_UPDATING); + ext_flash_lock(); + + wolfBoot_check_self_update(); + + ck_assert_int_eq(arch_reboot_called, 0); + ck_assert_int_eq(erased_update, 0); + ck_assert_uint_eq(*(uint32_t *)(uintptr_t)WOLFBOOT_PARTITION_BOOT_ADDRESS, + 0xFFFFFFFFu); + cleanup_flash(); +} +END_TEST + +START_TEST (test_self_update_newversion_copies_and_reboots) +{ + reset_mock_stats(); + prepare_flash(); + clear_erase_stats(); + add_payload_type(PART_UPDATE, WOLFBOOT_VERSION + 1, TEST_SIZE_SMALL, + HDR_IMG_TYPE_WOLFBOOT | HDR_IMG_TYPE_AUTH); + ext_flash_unlock(); + wolfBoot_set_partition_state(PART_UPDATE, IMG_STATE_UPDATING); + ext_flash_lock(); + + wolfBoot_check_self_update(); + + ck_assert_int_eq(arch_reboot_called, 1); + ck_assert_int_ge(erased_boot, 1); + ck_assert_mem_eq((const void *)(uintptr_t)WOLFBOOT_PARTITION_BOOT_ADDRESS, + (const void *)(uintptr_t)(WOLFBOOT_PARTITION_UPDATE_ADDRESS + IMAGE_HEADER_SIZE), + FLASHBUFFER_SIZE); + cleanup_flash(); +} +END_TEST +#endif + +#ifndef UNIT_TEST_SELF_UPDATE_ONLY #ifdef EXT_ENCRYPTED static int build_image_buffer(uint8_t part, uint32_t version, uint32_t size, uint8_t *buf, uint32_t buf_sz) @@ -378,6 +532,58 @@ START_TEST (test_fallback_image_verification_rejects_corruption) cleanup_flash(); } END_TEST + +START_TEST (test_final_swap_propagates_encrypt_key_persist_failure) +{ + int ret; + int erase_len = WOLFBOOT_SECTOR_SIZE; + uintptr_t tmp_boot_pos = WOLFBOOT_PARTITION_SIZE - erase_len - + WOLFBOOT_SECTOR_SIZE; + uint32_t tmp_buffer[TRAILER_OFFSET_WORDS + 1]; + + reset_mock_stats(); + prepare_flash(); + + add_payload(PART_BOOT, 1, TEST_SIZE_SMALL); + add_payload(PART_UPDATE, 2, TEST_SIZE_SMALL); + + memset(tmp_buffer, 0, sizeof(tmp_buffer)); + tmp_buffer[TRAILER_OFFSET_WORDS] = WOLFBOOT_MAGIC_TRAIL; + + hal_flash_unlock(); + hal_flash_write(WOLFBOOT_PARTITION_BOOT_ADDRESS + tmp_boot_pos, + (const uint8_t *)tmp_buffer, sizeof(tmp_buffer)); + hal_flash_lock(); + + mock_set_encrypt_key_ret = -5; + ret = wolfBoot_swap_and_final_erase(1); + + ck_assert_int_eq(ret, -5); + ck_assert_int_eq(mock_set_encrypt_key_calls, 1); + + cleanup_flash(); +} +END_TEST + +START_TEST (test_final_swap_propagates_encrypt_key_read_failure) +{ + int ret; + + reset_mock_stats(); + prepare_flash(); + + add_payload(PART_BOOT, 1, TEST_SIZE_SMALL); + add_payload(PART_UPDATE, 2, TEST_SIZE_SMALL); + + mock_get_encrypt_key_ret = -7; + ret = wolfBoot_swap_and_final_erase(0); + + ck_assert_int_eq(ret, -7); + ck_assert_int_eq(mock_set_encrypt_key_calls, 0); + + cleanup_flash(); +} +END_TEST #endif START_TEST (test_sunnyday_noupdate) @@ -495,6 +701,23 @@ START_TEST (test_invalid_update_type) { cleanup_flash(); } +START_TEST (test_invalid_update_auth_type) { + reset_mock_stats(); + prepare_flash(); + uint16_t word16 = HDR_IMG_TYPE_AUTH_ECC256 | HDR_IMG_TYPE_APP; + add_payload(PART_BOOT, 1, TEST_SIZE_SMALL); + add_payload(PART_UPDATE, 2, TEST_SIZE_SMALL); + ext_flash_unlock(); + ext_flash_write(WOLFBOOT_PARTITION_UPDATE_ADDRESS + 20, (void *)&word16, 2); + ext_flash_lock(); + wolfBoot_update_trigger(); + wolfBoot_start(); + ck_assert(!wolfBoot_panicked); + ck_assert(wolfBoot_staged_ok); + ck_assert(wolfBoot_current_firmware_version() == 1); + cleanup_flash(); +} + START_TEST (test_update_toolarge) { uint32_t very_large = WOLFBOOT_PARTITION_SIZE; reset_mock_stats(); @@ -514,6 +737,22 @@ START_TEST (test_update_toolarge) { cleanup_flash(); } +START_TEST (test_zero_size_update_rejected) +{ + int ret; + + reset_mock_stats(); + prepare_flash(); + add_payload(PART_BOOT, 1, 0); + add_payload(PART_UPDATE, 2, 0); + + ret = wolfBoot_update(1); + ck_assert_int_eq(ret, -1); + + cleanup_flash(); +} +END_TEST + START_TEST (test_invalid_sha) { uint8_t bad_digest[SHA256_DIGEST_SIZE]; reset_mock_stats(); @@ -660,6 +899,70 @@ START_TEST (test_diffbase_version_reads) } END_TEST +START_TEST (test_get_total_size_preserves_uint32_range) +{ + struct wolfBoot_image boot; + struct wolfBoot_image update; + uint32_t total_size; + + memset(&boot, 0, sizeof(boot)); + memset(&update, 0, sizeof(update)); + + boot.fw_size = (uint32_t)INT_MAX - IMAGE_HEADER_SIZE + 1u; + update.fw_size = boot.fw_size + 7u; + + total_size = wolfBoot_get_total_size(&boot, &update); + + ck_assert_uint_eq(total_size, update.fw_size + IMAGE_HEADER_SIZE); + ck_assert(total_size > (uint32_t)INT_MAX); +} +END_TEST + +#ifdef DELTA_UPDATES +START_TEST (test_delta_zero_size_valid_header_rejected_without_recovery_heuristic) +{ + struct wolfBoot_image boot, update, swap; + int ret; + + reset_mock_stats(); + prepare_flash(); + add_payload(PART_BOOT, 1, 0); + + ck_assert_int_eq(wolfBoot_open_image(&boot, PART_BOOT), 0); + memset(&update, 0, sizeof(update)); + memset(&swap, 0, sizeof(swap)); + + ret = wolfBoot_delta_update(&boot, &update, &swap, 0, 0); + ck_assert_int_eq(ret, -1); + ck_assert_uint_eq(boot.fw_size, 0); + + cleanup_flash(); +} +END_TEST + +START_TEST (test_delta_zero_size_erased_header_uses_recovery_heuristic) +{ + struct wolfBoot_image boot, update, swap; + int ret; + + reset_mock_stats(); + prepare_flash(); + + ck_assert_int_eq(wolfBoot_open_image(&boot, PART_BOOT), -1); + memset(&update, 0, sizeof(update)); + memset(&swap, 0, sizeof(swap)); + + ret = wolfBoot_delta_update(&boot, &update, &swap, 0, 0); + ck_assert_int_eq(ret, -1); + ck_assert_uint_eq(boot.fw_size, + WOLFBOOT_PARTITION_SIZE - IMAGE_HEADER_SIZE); + + cleanup_flash(); +} +END_TEST +#endif +#endif + Suite *wolfboot_suite(void) { @@ -667,6 +970,25 @@ Suite *wolfboot_suite(void) Suite *s = suite_create("wolfboot"); /* Test cases */ +#ifdef UNIT_TEST_SELF_UPDATE_ONLY +#ifdef RAM_CODE + TCase *self_update_sameversion = tcase_create("Self update same version erased"); + TCase *self_update_oldversion = tcase_create("Self update older version erased"); + TCase *self_update_invalid_integrity = tcase_create("Self update invalid integrity denied"); + TCase *self_update_success = tcase_create("Self update success"); + + tcase_add_test(self_update_sameversion, test_self_update_sameversion_erased); + tcase_add_test(self_update_oldversion, test_self_update_oldversion_erased); + tcase_add_test(self_update_invalid_integrity, test_self_update_newversion_invalid_integrity_denied); + tcase_add_test(self_update_success, test_self_update_newversion_copies_and_reboots); + + suite_add_tcase(s, self_update_sameversion); + suite_add_tcase(s, self_update_oldversion); + suite_add_tcase(s, self_update_invalid_integrity); + suite_add_tcase(s, self_update_success); +#endif + return s; +#else #ifdef UNIT_TEST_FALLBACK_ONLY TCase *fallback_verify = tcase_create("Fallback verify"); #else @@ -684,7 +1006,10 @@ Suite *wolfboot_suite(void) tcase_create("Update to older version denied"); TCase *invalid_update_type = tcase_create("Invalid update type"); + TCase *invalid_update_auth_type = + tcase_create("Invalid update auth type"); TCase *update_toolarge = tcase_create("Update too large"); + TCase *zero_size_update = tcase_create("Zero size update"); TCase *invalid_sha = tcase_create("Invalid SHA digest"); TCase *emergency_rollback = tcase_create("Emergency rollback"); TCase *emergency_rollback_failure_due_to_bad_update = tcase_create("Emergency rollback failure due to bad update"); @@ -692,7 +1017,17 @@ Suite *wolfboot_suite(void) TCase *empty_boot_but_update_sha_corrupted_denied = tcase_create("Empty boot partition but update SHA corrupted"); TCase *swap_resume = tcase_create("Swap resume noop"); TCase *diffbase_version = tcase_create("Diffbase version lookup"); + TCase *get_total_size = tcase_create("Total size range"); TCase *boot_success = tcase_create("Boot success state"); +#ifdef DELTA_UPDATES + TCase *delta_zero_size = tcase_create("Delta zero size"); +#endif +#ifdef RAM_CODE + TCase *self_update_sameversion = tcase_create("Self update same version erased"); + TCase *self_update_oldversion = tcase_create("Self update older version erased"); + TCase *self_update_invalid_integrity = tcase_create("Self update invalid integrity denied"); + TCase *self_update_success = tcase_create("Self update success"); +#endif #ifdef EXT_ENCRYPTED TCase *fallback_verify = tcase_create("Fallback verify"); #endif @@ -702,6 +1037,8 @@ Suite *wolfboot_suite(void) #ifdef UNIT_TEST_FALLBACK_ONLY #ifdef EXT_ENCRYPTED tcase_add_test(fallback_verify, test_fallback_image_verification_rejects_corruption); + tcase_add_test(fallback_verify, test_final_swap_propagates_encrypt_key_read_failure); + tcase_add_test(fallback_verify, test_final_swap_propagates_encrypt_key_persist_failure); suite_add_tcase(s, fallback_verify); #endif return s; @@ -714,7 +1051,9 @@ Suite *wolfboot_suite(void) tcase_add_test(forward_update_sameversion_denied, test_forward_update_sameversion_denied); tcase_add_test(update_oldversion_denied, test_update_oldversion_denied); tcase_add_test(invalid_update_type, test_invalid_update_type); + tcase_add_test(invalid_update_auth_type, test_invalid_update_auth_type); tcase_add_test(update_toolarge, test_update_toolarge); + tcase_add_test(zero_size_update, test_zero_size_update_rejected); tcase_add_test(invalid_sha, test_invalid_sha); tcase_add_test(emergency_rollback, test_emergency_rollback); tcase_add_test(emergency_rollback_failure_due_to_bad_update, test_emergency_rollback_failure_due_to_bad_update); @@ -722,9 +1061,22 @@ Suite *wolfboot_suite(void) tcase_add_test(empty_boot_but_update_sha_corrupted_denied, test_empty_boot_but_update_sha_corrupted_denied); tcase_add_test(swap_resume, test_swap_resume_noop); tcase_add_test(diffbase_version, test_diffbase_version_reads); + tcase_add_test(get_total_size, test_get_total_size_preserves_uint32_range); tcase_add_test(boot_success, test_boot_success_sets_state); +#ifdef DELTA_UPDATES + tcase_add_test(delta_zero_size, test_delta_zero_size_valid_header_rejected_without_recovery_heuristic); + tcase_add_test(delta_zero_size, test_delta_zero_size_erased_header_uses_recovery_heuristic); +#endif +#ifdef RAM_CODE + tcase_add_test(self_update_sameversion, test_self_update_sameversion_erased); + tcase_add_test(self_update_oldversion, test_self_update_oldversion_erased); + tcase_add_test(self_update_invalid_integrity, test_self_update_newversion_invalid_integrity_denied); + tcase_add_test(self_update_success, test_self_update_newversion_copies_and_reboots); +#endif #ifdef EXT_ENCRYPTED tcase_add_test(fallback_verify, test_fallback_image_verification_rejects_corruption); + tcase_add_test(fallback_verify, test_final_swap_propagates_encrypt_key_read_failure); + tcase_add_test(fallback_verify, test_final_swap_propagates_encrypt_key_persist_failure); #endif suite_add_tcase(s, empty_panic); @@ -735,7 +1087,9 @@ Suite *wolfboot_suite(void) suite_add_tcase(s, forward_update_sameversion_denied); suite_add_tcase(s, update_oldversion_denied); suite_add_tcase(s, invalid_update_type); + suite_add_tcase(s, invalid_update_auth_type); suite_add_tcase(s, update_toolarge); + suite_add_tcase(s, zero_size_update); suite_add_tcase(s, invalid_sha); suite_add_tcase(s, emergency_rollback); suite_add_tcase(s, emergency_rollback_failure_due_to_bad_update); @@ -743,7 +1097,17 @@ Suite *wolfboot_suite(void) suite_add_tcase(s, empty_boot_but_update_sha_corrupted_denied); suite_add_tcase(s, swap_resume); suite_add_tcase(s, diffbase_version); + suite_add_tcase(s, get_total_size); suite_add_tcase(s, boot_success); +#ifdef DELTA_UPDATES + suite_add_tcase(s, delta_zero_size); +#endif +#ifdef RAM_CODE + suite_add_tcase(s, self_update_sameversion); + suite_add_tcase(s, self_update_oldversion); + suite_add_tcase(s, self_update_invalid_integrity); + suite_add_tcase(s, self_update_success); +#endif #ifdef EXT_ENCRYPTED suite_add_tcase(s, fallback_verify); #endif @@ -752,6 +1116,7 @@ Suite *wolfboot_suite(void) return s; +#endif } diff --git a/tools/unit-tests/unit-update-ram.c b/tools/unit-tests/unit-update-ram.c index 4550f59419..fecac50540 100644 --- a/tools/unit-tests/unit-update-ram.c +++ b/tools/unit-tests/unit-update-ram.c @@ -84,6 +84,13 @@ static void reset_mock_stats(void) wolfBoot_staged_ok = 0; } +int hal_flash_protect(haladdr_t address, int len) +{ + (void)address; + (void)len; + return 0; +} + uint32_t get_version_ramloaded(void) { return wolfBoot_get_blob_version(wolfboot_ram); @@ -341,6 +348,7 @@ START_TEST (test_forward_update_sameversion_denied) { wolfBoot_start(); ck_assert(wolfBoot_staged_ok); ck_assert(get_version_ramloaded() == 1); + ck_assert_uint_eq(*(uint32_t *)(wolfboot_ram + 4), TEST_SIZE_SMALL); ck_assert(*(uint32_t *)(WOLFBOOT_PARTITION_BOOT_ADDRESS + 4) == TEST_SIZE_SMALL); cleanup_flash(); }