diff --git a/.github/workflows/test-units.yml b/.github/workflows/test-units.yml index 25fc97d36..f951595b1 100644 --- a/.github/workflows/test-units.yml +++ b/.github/workflows/test-units.yml @@ -22,27 +22,10 @@ jobs: run: | make keysclean && make -C tools/keytools clean && rm -f include/target.h - - name: Build wolfboot and test footprint + - name: Build unit tests run: | make -C tools/unit-tests - - - name: Run manifest header parser unit tests - run: | - ./tools/unit-tests/unit-parser - - - name: Run non-encrypted ext_flash unit tests - run: | - ./tools/unit-tests/unit-extflash - - - name: Run aes128 ext_flash unit tests - run: | - ./tools/unit-tests/unit-aes128 - - - name: Run aes256 ext_flash unit tests - run: | - ./tools/unit-tests/unit-aes256 - - - name: Run chacha20 ext_flash unit tests + - name: Run unit tests run: | - ./tools/unit-tests/unit-chacha20 + make -C tools/unit-tests run diff --git a/.gitignore b/.gitignore index 04c7d732e..7c6b8a0ec 100644 --- a/.gitignore +++ b/.gitignore @@ -134,4 +134,10 @@ stage1/loader_stage1.ld debug/lauterbach #cland cache -.cache/* \ No newline at end of file +.cache/* + +#gcov files +*.gcov +*.gcda +*.gcno +coverage.* diff --git a/src/libwolfboot.c b/src/libwolfboot.c index 1cdf25fe2..f14cb0742 100644 --- a/src/libwolfboot.c +++ b/src/libwolfboot.c @@ -398,6 +398,7 @@ static int RAMFUNCTION partition_magic_write(uint8_t part, uintptr_t addr) (void*)&wolfboot_magic_trail, sizeof(uint32_t)); #endif +#ifndef MOCK_PARTITION_TRAILER #ifdef EXT_FLASH /** @@ -558,6 +559,7 @@ static void RAMFUNCTION set_partition_magic(uint8_t part) } } #endif /* EXT_FLASH */ +#endif /* MOCK_PARTITION_TRAILER */ @@ -626,6 +628,8 @@ int RAMFUNCTION wolfBoot_set_partition_state(uint8_t part, uint8_t newst) { uint32_t *magic; uint8_t *state; + if (part == PART_NONE) + return -1; magic = get_partition_magic(part); if (*magic != WOLFBOOT_MAGIC_TRAIL) set_partition_magic(part); @@ -669,6 +673,8 @@ int RAMFUNCTION wolfBoot_get_partition_state(uint8_t part, uint8_t *st) { uint32_t *magic; uint8_t *state; + if (part == PART_NONE) + return -1; magic = get_partition_magic(part); if (*magic != WOLFBOOT_MAGIC_TRAIL) return -1; diff --git a/tools/unit-tests/Makefile b/tools/unit-tests/Makefile index 83c771be2..f6902cdd6 100644 --- a/tools/unit-tests/Makefile +++ b/tools/unit-tests/Makefile @@ -7,15 +7,50 @@ endif CFLAGS=-I. -I../../src -I../../include -I../../lib/wolfssl CFLAGS+=-g -ggdb +CFLAGS+=-fprofile-arcs +CFLAGS+=-ftest-coverage +CFLAGS+=--coverage +LDFLAGS+=-fprofile-arcs +LDFLAGS+=-ftest-coverage +WOLFCRYPT=../../lib/wolfssl/ -all: unit-parser unit-extflash unit-aes128 unit-aes256 unit-chacha20 +TESTS:=unit-parser unit-extflash unit-aes128 unit-aes256 unit-chacha20 unit-pci \ + unit-mock-state unit-sectorflags unit-image + +all: $(TESTS) + +cov: + gcovr -f "^\.\.\/\.\.\/src.*\.c" -r ../.. --verbose \ + --merge-mode-functions merge-use-line-0 \ + --html-medium-threshold 60 \ + --html-high-threshold 80 \ + --html-details coverage.html + firefox coverage.html + +run: $(TESTS) + for unit in $(TESTS); do \ + ./$$unit || exit 1; \ + done unit-aes128:CFLAGS+=-DEXT_ENCRYPTED -DENCRYPT_WITH_AES128 unit-aes256:CFLAGS+=-DEXT_ENCRYPTED -DENCRYPT_WITH_AES256 unit-chacha20:CFLAGS+=-DEXT_ENCRYPTED -DENCRYPT_WITH_CHACHA +unit-parser:CFLAGS+=-DNVM_FLASH_WRITEONCE + + +WOLFCRYPT_SRC:=$(WOLFCRYPT)/wolfcrypt/src/sha.c \ + $(WOLFCRYPT)/wolfcrypt/src/sha256.c \ + $(WOLFCRYPT)/wolfcrypt/src/sp_int.c \ + $(WOLFCRYPT)/wolfcrypt/src/sp_c64.c \ + $(WOLFCRYPT)/wolfcrypt/src/random.c \ + $(WOLFCRYPT)/wolfcrypt/src/memory.c + +WOLFCRYPT_CFLAGS+=-DWOLFSSL_USER_SETTINGS -DWOLFBOOT_SIGN_ECC256 -DWOLFBOOT_SIGN_ECC256 -DHAVE_ECC_KEY_IMPORT -DWOLFBOOT_HASH_SHA256 -D__WOLFBOOT + + ../../include/target.h: FORCE cp -f target.h $@ @@ -24,7 +59,7 @@ unit-extflash.o: FORCE rm -f $@ gcc -c -o $@ unit-extflash.c $(CFLAGS) -unit-parser: ../../include/target.h unit-parser.o +unit-parser: ../../include/target.h unit-parser.c gcc -o $@ $^ $(CFLAGS) $(LDFLAGS) unit-extflash: ../../include/target.h unit-extflash.c @@ -42,10 +77,23 @@ unit-chacha20: ../../include/target.h unit-extflash.c unit-pci: unit-pci.c ../../src/pci.c gcc -o $@ $< $(CFLAGS) $(LDFLAGS) +unit-mock-state: ../../include/target.h unit-mock-state.c + gcc -o $@ $^ $(CFLAGS) $(LDFLAGS) + +unit-sectorflags: ../../include/target.h unit-sectorflags.c + gcc -o $@ $^ $(CFLAGS) $(LDFLAGS) + +unit-image: unit-image.c unit-common.c $(WOLFCRYPT_SRC) + gcc -o $@ $^ $(CFLAGS) $(WOLFCRYPT_CFLAGS) $(LDFLAGS) + %.o:%.c gcc -c -o $@ $^ $(CFLAGS) -clean: - rm -f unit-parser unit-extflash *.o + +covclean: + rm -f *.gcov *.gcno *.gcda coverage.* + +clean: covclean + rm -f $(TESTS) *.o *.gcno *.gcda coverage.* .PHONY: FORCE diff --git a/tools/unit-tests/unit-common.c b/tools/unit-tests/unit-common.c new file mode 100644 index 000000000..eea418b65 --- /dev/null +++ b/tools/unit-tests/unit-common.c @@ -0,0 +1,106 @@ +/* unit-common.c + * + * Unit test common tools + * + * + * Copyright (C) 2023 wolfSSL Inc. + * + * This file is part of wolfBoot. + * + * wolfBoot is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * wolfBoot is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1335, USA + */ +#include +#include +#include +#include +#include "target.h" +#define FLASH_SIZE (33 * 1024) + +/* Emulation of external flash with a static buffer of 32KB (update) + 1KB (swap) */ +uint8_t flash[FLASH_SIZE]; + +/* Flash lock/unlock counter */ +static int elocked = 0; + +#ifndef BACKUP_MOCKED + +uint8_t image_backup(uint8_t part_id) +{ + printf("Called image_backup\n"); + return 0xFF; /* PART_NONE */ +} +#endif + +#ifndef EXT_MOCKED + +/* Mocks for ext_flash_read, ext_flash_write, and ext_flash_erase functions */ +int ext_flash_read(uintptr_t address, uint8_t *data, int len) { + printf("Called ext_flash_read %p %p %d\n", address, data, len); + + /* Check that the read address and size are within the bounds of the flash memory */ + ck_assert_int_le(address + len, FLASH_SIZE); + + /* Copy the data from the flash memory to the output buffer */ + memcpy(data, &flash[address], len); + + printf("Return value: %d\n", len); + return len; +} + +int ext_flash_write(uintptr_t address, const uint8_t *data, int len) { + printf("Called ext_flash_write %p %p %d\n", address, data, len); + + + /* Check that the write address and size are within the bounds of the flash memory */ + ck_assert_int_le(address + len, FLASH_SIZE); + + /* Copy the data from the input buffer to the flash memory */ + memcpy(&flash[address], data, len); + + return 0; +} + +int ext_flash_erase(uintptr_t address, int len) { + /* Check that the erase address and size are within the bounds of the flash memory */ + ck_assert_int_le(address + len, FLASH_SIZE); + + /* Check that address is aligned to WOLFBOOT_SECTOR_SIZE */ + ck_assert_int_eq(address, address & ~(WOLFBOOT_SECTOR_SIZE - 1)); + + /* Check that len is aligned to WOLFBOOT_SECTOR_SIZE */ + ck_assert_int_eq(len, len & ~(WOLFBOOT_SECTOR_SIZE - 1)); + + + /* Erase the flash memory by setting each byte to 0xFF, WOLFBOOT_SECTOR_SIZE bytes at a time */ + uint32_t i; + for (i = address; i < address + len; i += WOLFBOOT_SECTOR_SIZE) { + memset(&flash[i], 0xFF, WOLFBOOT_SECTOR_SIZE); + } + + return 0; +} + +void ext_flash_unlock(void) +{ + fail_unless(elocked, "Double ext unlock detected\n"); + elocked--; +} +void ext_flash_lock(void) +{ + fail_if(elocked, "Double ext lock detected\n"); + elocked++; +} + +#endif /* EXT_MOCKED */ diff --git a/tools/unit-tests/unit-image.c b/tools/unit-tests/unit-image.c new file mode 100644 index 000000000..e1dac0e5d --- /dev/null +++ b/tools/unit-tests/unit-image.c @@ -0,0 +1,607 @@ +/* unit-image.c + * + * Unit test for parser functions in image.c + * + * + * Copyright (C) 2023 wolfSSL Inc. + * + * This file is part of wolfBoot. + * + * wolfBoot is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * wolfBoot is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1335, USA + */ + +/* Option to enable sign tool debugging */ +/* Must also define DEBUG_WOLFSSL in user_settings.h */ +#define WOLFBOOT_HASH_SHA256 +#define EXT_FLASH +#define PART_UPDATE_EXT +#define NVM_FLASH_WRITEONCE + +#if defined(ENCRYPT_WITH_AES256) || defined(ENCRYPT_WITH_AES128) + #define WOLFSSL_AES_COUNTER + #define WOLFSSL_AES_DIRECT +#endif +#if defined(ENCRYPT_WITH_AES256) + #define WOLFSSL_AES_256 +#endif +#if defined(ENCRYPT_WITH_CHACHA) + #define HAVE_CHACHA +#endif +//#define WC_NO_HARDEN +#define ECC_TIMING_RESISTANT + +#define WOLFSSL_USER_SETTINGS +#define ENCRYPT_KEY "123456789abcdef0123456789abcdef0123456789abcdef" +#define UNIT_TEST +#define WOLFBOOT_SIGN_ECC256 +#define KEYSTORE_PUBKEY_SIZE KEYSTORE_PUBKEY_SIZE_ECC256 +#define __WOLFBOOT + +#include +#include +#include +#include +#include +#include "user_settings.h" +#include "wolfssl/wolfcrypt/sha.h" +#include "wolfboot/wolfboot.h" + +#include "unit-keystore.c" + +#include "image.c" + +const uint8_t a; + +static int ecc_init_fail = 1; +static int ecc_import_fail = 1; + +static int verify_called = 0; + +static int find_header_fail = 0; +static int find_header_called = 0; +static int find_header_mocked = 1; + +static const unsigned char pubkey_digest[SHA256_DIGEST_SIZE] = { + 0x17, 0x20, 0xa5, 0x9b, 0xe0, 0x9b, 0x80, 0x0c, 0xaa, 0xc4, 0xf5, 0x3f, + 0xae, 0xe5, 0x72, 0x4f, 0xf2, 0x1f, 0x33, 0x53, 0xd1, 0xd4, 0xcd, 0x8b, + 0x5c, 0xc3, 0x4e, 0xda, 0xea, 0xc8, 0x4a, 0x68 +}; + + +static const unsigned char test_img_v200000000_signed_bin[] = { + 0x57, 0x4f, 0x4c, 0x46, 0x13, 0x00, 0x00, 0x00, 0x01, 0x00, 0x04, 0x00, + 0x00, 0xc2, 0xeb, 0x0b, 0xff, 0xff, 0xff, 0xff, 0x02, 0x00, 0x08, 0x00, + 0x77, 0x33, 0x29, 0x65, 0x00, 0x00, 0x00, 0x00, 0x04, 0x00, 0x02, 0x00, + 0x01, 0x02, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x03, 0x00, 0x20, 0x00, + 0xda, 0x9c, 0xee, 0x7e, 0x12, 0xcf, 0xa0, 0xe1, 0xda, 0xa1, 0xb4, 0x23, + 0xbf, 0x31, 0xe5, 0xdd, 0x6f, 0x58, 0xfe, 0xd9, 0x8d, 0xb7, 0x7b, 0x31, + 0x6f, 0x7b, 0x01, 0x84, 0xe0, 0x63, 0x5e, 0xe9, 0x10, 0x00, 0x20, 0x00, + 0x17, 0x20, 0xa5, 0x9b, 0xe0, 0x9b, 0x80, 0x0c, 0xaa, 0xc4, 0xf5, 0x3f, + 0xae, 0xe5, 0x72, 0x4f, 0xf2, 0x1f, 0x33, 0x53, 0xd1, 0xd4, 0xcd, 0x8b, + 0x5c, 0xc3, 0x4e, 0xda, 0xea, 0xc8, 0x4a, 0x68, 0x20, 0x00, 0x40, 0x00, + 0xb0, 0x22, 0xb3, 0x91, 0xf7, 0x4e, 0xe1, 0x37, 0x6c, 0xb5, 0x64, 0x2e, + 0xe6, 0x80, 0x4b, 0xcb, 0xa7, 0x1d, 0xa1, 0xa7, 0x16, 0x2e, 0x4b, 0xa5, + 0xee, 0x67, 0xd2, 0x02, 0xff, 0x1b, 0xd3, 0x4c, 0xc6, 0x09, 0x62, 0x66, + 0x08, 0x4c, 0xfc, 0x32, 0x4b, 0x47, 0x56, 0xe0, 0x9b, 0x98, 0xd9, 0xa4, + 0x2a, 0x5e, 0x53, 0xd3, 0xb4, 0xde, 0x80, 0xe1, 0x9a, 0x95, 0x2a, 0x58, + 0xc9, 0xd6, 0x9a, 0x2a, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0x54, 0x65, 0x73, 0x74, 0x20, 0x69, 0x6d, 0x61, + 0x67, 0x65, 0x20, 0x63, 0x6f, 0x6e, 0x74, 0x65, 0x6e, 0x74, 0x0a +}; + + +static const unsigned char test_img_v200000000_wrong_signature_bin[] = { + 0x57, 0x4f, 0x4c, 0x46, 0x13, 0x00, 0x00, 0x00, 0x01, 0x00, 0x04, 0x00, + 0x00, 0xc2, 0xeb, 0x0b, 0xff, 0xff, 0xff, 0xff, 0x02, 0x00, 0x08, 0x00, + 0x77, 0x33, 0x29, 0x65, 0x00, 0x00, 0x00, 0x00, 0x04, 0x00, 0x02, 0x00, + 0x01, 0x02, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x03, 0x00, 0x20, 0x00, + 0xda, 0x9c, 0xee, 0x7e, 0x12, 0xcf, 0xa0, 0xe1, 0xda, 0xa1, 0xb4, 0x23, + 0xbf, 0x31, 0xe5, 0xdd, 0x6f, 0x58, 0xfe, 0xd9, 0x8d, 0xb7, 0x7b, 0x31, + 0x6f, 0x7b, 0x01, 0x84, 0xe0, 0x63, 0x5e, 0xe9, 0x10, 0x00, 0x20, 0x00, + 0x17, 0x20, 0xa5, 0x9b, 0xe0, 0x9b, 0x80, 0x0c, 0xaa, 0xc4, 0xf5, 0x3f, + 0xae, 0xe5, 0x72, 0x4f, 0xf2, 0x1f, 0x33, 0x53, 0xd1, 0xd4, 0xcd, 0x8b, + 0x5c, 0xc3, 0x4e, 0xda, 0xea, 0xc8, 0x4a, 0x68, 0x20, 0x00, 0x40, 0x00, + 0xb0, 0x22, 0xb3, 0x91, 0xf7, 0x4e, 0xe1, 0x37, 0x6c, 0xb5, 0x64, 0x2f, + 0xe6, 0x80, 0x4b, 0xcb, 0xa7, 0x1d, 0xa1, 0xa7, 0x16, 0x2e, 0x4b, 0xa5, + 0xee, 0x67, 0xd2, 0x02, 0xff, 0x1b, 0xd3, 0x4c, 0xc6, 0x09, 0x62, 0x66, + 0x08, 0x4c, 0xfc, 0x32, 0x4b, 0x47, 0x56, 0xe0, 0x9b, 0x98, 0xd9, 0xa4, + 0x2a, 0x5e, 0x53, 0xd3, 0xb4, 0xde, 0x80, 0xe1, 0x9a, 0x95, 0x2a, 0x58, + 0xc9, 0xd6, 0x9a, 0x2a, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0x54, 0x65, 0x73, 0x74, 0x20, 0x69, 0x6d, 0x61, + 0x67, 0x65, 0x20, 0x63, 0x6f, 0x6e, 0x74, 0x65, 0x6e, 0x74, 0x0a +}; + +static const unsigned char test_img_v200000000_wrong_pubkey_bin[] = { + 0x57, 0x4f, 0x4c, 0x46, 0x13, 0x00, 0x00, 0x00, 0x01, 0x00, 0x04, 0x00, + 0x00, 0xc2, 0xeb, 0x0b, 0xff, 0xff, 0xff, 0xff, 0x02, 0x00, 0x08, 0x00, + 0x77, 0x33, 0x29, 0x65, 0x00, 0x00, 0x00, 0x00, 0x04, 0x00, 0x02, 0x00, + 0x01, 0x02, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x03, 0x00, 0x20, 0x00, + 0xda, 0x9c, 0xee, 0x7e, 0x12, 0xcf, 0xa0, 0xe1, 0xda, 0xa1, 0xb4, 0x24, + 0xbf, 0x31, 0xe5, 0xdd, 0x6f, 0x58, 0xfe, 0xd9, 0x8d, 0xb7, 0x7b, 0x31, + 0x6f, 0x7b, 0x01, 0x84, 0xe0, 0x63, 0x5e, 0xe9, 0x10, 0x00, 0x20, 0x00, + 0x17, 0x20, 0xa5, 0x9b, 0xe0, 0x9b, 0x80, 0x0c, 0xaa, 0xc4, 0xf5, 0x3f, + 0xae, 0xe5, 0x72, 0x4f, 0xf2, 0x1f, 0x33, 0x53, 0xd1, 0xd4, 0xcd, 0x8b, + 0x5c, 0xc3, 0x4e, 0xda, 0xea, 0xc8, 0x4a, 0x68, 0x20, 0x00, 0x40, 0x00, + 0xb0, 0x22, 0xb3, 0x91, 0xf7, 0x4e, 0xe1, 0x37, 0x6c, 0xb5, 0x64, 0x2e, + 0xe6, 0x80, 0x4b, 0xcb, 0xa7, 0x1d, 0xa1, 0xa7, 0x16, 0x2e, 0x4b, 0xa5, + 0xee, 0x67, 0xd2, 0x02, 0xff, 0x1b, 0xd3, 0x4c, 0xc6, 0x09, 0x62, 0x66, + 0x08, 0x4c, 0xfc, 0x32, 0x4b, 0x47, 0x56, 0xe0, 0x9b, 0x98, 0xd9, 0xa4, + 0x2a, 0x5e, 0x53, 0xd3, 0xb4, 0xde, 0x80, 0xe1, 0x9a, 0x95, 0x2a, 0x58, + 0xc9, 0xd6, 0x9a, 0x2a, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0x54, 0x65, 0x73, 0x74, 0x20, 0x69, 0x6d, 0x61, + 0x67, 0x65, 0x20, 0x63, 0x6f, 0x6e, 0x74, 0x65, 0x6e, 0x74, 0x0a +}; +static const unsigned int test_img_len = 275; + + +unsigned char test_img_v123_signed_bin[] = { + 0x57, 0x4f, 0x4c, 0x46, 0x13, 0x00, 0x00, 0x00, 0x01, 0x00, 0x04, 0x00, + 0x7b, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0x02, 0x00, 0x08, 0x00, + 0x77, 0x33, 0x29, 0x65, 0x00, 0x00, 0x00, 0x00, 0x04, 0x00, 0x02, 0x00, + 0x01, 0x02, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x03, 0x00, 0x20, 0x00, + 0x89, 0xbd, 0x89, 0x01, 0xb9, 0xaf, 0xa9, 0xbd, 0x78, 0x88, 0xba, 0xd1, + 0x97, 0xc1, 0x6f, 0xd0, 0x7f, 0x11, 0xbd, 0x29, 0x97, 0x4a, 0x10, 0x27, + 0xa0, 0x53, 0x8c, 0x32, 0x3d, 0xfc, 0xc9, 0x9b, 0x10, 0x00, 0x20, 0x00, + 0x17, 0x20, 0xa5, 0x9b, 0xe0, 0x9b, 0x80, 0x0c, 0xaa, 0xc4, 0xf5, 0x3f, + 0xae, 0xe5, 0x72, 0x4f, 0xf2, 0x1f, 0x33, 0x53, 0xd1, 0xd4, 0xcd, 0x8b, + 0x5c, 0xc3, 0x4e, 0xda, 0xea, 0xc8, 0x4a, 0x68, 0x20, 0x00, 0x40, 0x00, + 0xfc, 0x1d, 0x02, 0x10, 0xb7, 0x60, 0x63, 0x7b, 0x55, 0xe0, 0x0e, 0xd5, + 0xb0, 0x64, 0xcd, 0x14, 0x9c, 0x1c, 0x80, 0x5f, 0x02, 0xb5, 0x54, 0x67, + 0x54, 0x93, 0x6d, 0xaf, 0x72, 0x74, 0x7b, 0x96, 0x94, 0x5c, 0x62, 0xb2, + 0x6d, 0x0f, 0xc9, 0xf4, 0x9f, 0x82, 0xa7, 0xd4, 0x28, 0xb9, 0x4c, 0x64, + 0x01, 0x5d, 0x03, 0x0f, 0x81, 0x05, 0x13, 0xf1, 0xe0, 0xbd, 0xdc, 0xe2, + 0x17, 0x84, 0xa3, 0x25, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0x54, 0x65, 0x73, 0x74, 0x20, 0x69, 0x6d, 0x61, + 0x67, 0x65, 0x20, 0x63, 0x6f, 0x6e, 0x74, 0x65, 0x6e, 0x74, 0x0a +}; +unsigned int test_img_v123_signed_bin_len = 275; + + +static uint16_t _find_header(uint8_t *haystack, uint16_t type, uint8_t **ptr) +{ + uint8_t *p = haystack; + uint16_t len; + const volatile uint8_t *max_p = (haystack - IMAGE_HEADER_OFFSET) + + IMAGE_HEADER_SIZE; + *ptr = NULL; + if (p > max_p) { + printf("Illegal address (too high)\n"); + return 0; + } + while ((p + 4) < max_p) { + if ((p[0] == 0) && (p[1] == 0)) { + printf("Explicit end of options reached\n"); + break; + } + if (*p == HDR_PADDING) { + /* Padding byte (skip one position) */ + p++; + continue; + } + /* Sanity check to prevent dereferencing unaligned half-words */ + if ((((unsigned long)p) & 0x01) != 0) { + p++; + continue; + } + len = p[2] | (p[3] << 8); + if ((4 + len) > (uint16_t)(IMAGE_HEADER_SIZE - IMAGE_HEADER_OFFSET)) { + printf("This field is too large (bigger than the space available " + "in the current header)\n"); + printf("%d %d %d\n", len, IMAGE_HEADER_SIZE, IMAGE_HEADER_OFFSET); + break; + } + if (p + 4 + len > max_p) { + printf("This field is too large and would overflow the image " + "header\n"); + break; + } + if ((p[0] | (p[1] << 8)) == type) { + *ptr = (p + 4); + return len; + } + p += 4 + len; + } + return 0; +} + +uint32_t wolfBoot_get_blob_type(uint8_t *addr) +{ + return HDR_IMG_TYPE_APP; +} + +uint16_t wolfBoot_find_header(uint8_t *haystack, uint16_t type, uint8_t **ptr) +{ + find_header_called++; + if (find_header_mocked) { + if (find_header_fail) { + return -1; + } else { + return sizeof(uint16_t); + } + } else { + return _find_header(haystack, type, ptr); + } +} + + +int wc_ecc_init(ecc_key* key) { + if (ecc_init_fail) + return -1; + return 0; +} + +int wc_ecc_free(ecc_key *key) { + return 0; +} + +int wc_ecc_import_unsigned(ecc_key* key, const byte* qx, const byte* qy, + const byte* d, int curve_id) +{ + if (ecc_import_fail) + return -1; + + key->type = ECC_PUBLICKEY; + return 0; +} + + +int wc_ecc_verify_hash_ex(mp_int *r, mp_int *s, const byte* hash, + word32 hashlen, int* res, ecc_key* key) +{ + verify_called++; + *res = 1; + return 0; +} + +START_TEST(test_verify_signature) +{ + uint8_t pubkey[32]; + struct wolfBoot_image test_img; + + test_img.part = PART_UPDATE; + test_img.fw_size = test_img_len; + test_img.fw_base = 0; + + wolfBoot_verify_signature(0, NULL, NULL); + ck_assert_int_eq(verify_called, 0); + + ecc_init_fail = 1; + wolfBoot_verify_signature(0, NULL, pubkey); + ck_assert_int_eq(verify_called, 0); + + ecc_init_fail = 0; + verify_called = 0; + ecc_import_fail = 1; + wolfBoot_verify_signature(0, NULL, pubkey); + ck_assert_int_eq(verify_called, 0); + + ecc_init_fail = 0; + ecc_import_fail = 0; + verify_called = 0; + find_header_mocked = 0; + ext_flash_erase(0, 2 * WOLFBOOT_SECTOR_SIZE); + ext_flash_write(0, test_img_v200000000_signed_bin, + test_img_len); + wolfBoot_verify_signature(0, &test_img, pubkey); + ck_assert_int_eq(verify_called, 1); +} +END_TEST + + +START_TEST(test_sha_ops) +{ + uint8_t hash[SHA256_DIGEST_SIZE]; + static uint8_t FlashImg[32 * 1024]; + uint8_t *retp = NULL; + struct wolfBoot_image test_img; + uint32_t offset; + uint32_t sz = 0; + find_header_mocked = 1; + memset(&test_img, 0, sizeof(struct wolfBoot_image)); + test_img.part = PART_BOOT; + test_img.fw_size = 0x1000; + test_img.fw_base = FlashImg; + + /* Test get_sha_block */ + offset = 0x2000; + retp = get_sha_block(&test_img, offset); + ck_assert_ptr_null(retp); + + offset = 0x100; + retp = get_sha_block(&test_img, offset); + ck_assert_ptr_eq(retp, FlashImg + offset); + + test_img.part = PART_UPDATE; + test_img.fw_size = 0x1000; + test_img.fw_base = 0x0000; + + offset = 0x2000; + retp = get_sha_block(&test_img, offset); + ck_assert_ptr_null(retp); + + offset = 0x100; + retp = get_sha_block(&test_img, offset); + ck_assert_ptr_eq(retp, ext_hash_block); + + /* Test wolfBoot_peek_image */ + hdr_cpy_done = 0; + offset = 0x100; + retp = get_sha_block(&test_img, offset); + ck_assert_ptr_eq(retp, ext_hash_block); + retp = wolfBoot_peek_image(&test_img, offset, NULL); + ck_assert_ptr_eq(retp, ext_hash_block); + retp = wolfBoot_peek_image(&test_img, offset, &sz); + ck_assert_ptr_eq(retp, ext_hash_block); + ck_assert_uint_eq(sz, WOLFBOOT_SHA_BLOCK_SIZE); + + /* Test image_sha256 */ + + /* NULL img */ + ck_assert_int_lt(image_sha256(NULL, hash), 0); + + /* Too short, internal partition field */ + test_img.part = PART_BOOT; + test_img.fw_size = 0x1000; + ck_assert_int_lt(image_sha256(&test_img, hash), 0); + + /* Ext partition with a valid SHA */ + find_header_mocked = 0; + find_header_fail = 0; + hdr_cpy_done = 0; + ext_flash_write(0, test_img_v200000000_signed_bin, + test_img_len); + test_img.part = PART_UPDATE; + test_img.fw_base = 0; + test_img.fw_size = test_img_len; + ck_assert_int_eq(image_sha256(&test_img, hash), 0); + + /* key_sha256 */ + key_sha256(0, hash); + ck_assert_mem_eq(hash, pubkey_digest, SHA256_DIGEST_SIZE); +} +END_TEST + +START_TEST(test_headers) +{ + struct wolfBoot_image img; + uint16_t type; + void *ptr; + uint16_t ret; + uint32_t sz; + memset(&img, 0, sizeof(struct wolfBoot_image)); + + + /* Test get_header() */ + img.part = PART_BOOT; + find_header_fail = 1; + find_header_called = 0; + ret = get_header(&img, type, &ptr); + ck_assert_uint_eq(ret, 0xFFFF); + ck_assert_int_eq(find_header_called, 1); + + img.part = PART_BOOT; + find_header_fail = 0; + find_header_called = 0; + ret = get_header(&img, type, &ptr); + ck_assert_uint_ne(ret, 0xFFFF); + ck_assert_int_eq(find_header_called, 1); + + img.part = PART_UPDATE; + find_header_fail = 1; + find_header_called = 0; + ret = get_header(&img, type, &ptr); + ck_assert_uint_eq(ret, 0xFFFF); + ck_assert_int_eq(find_header_called, 1); + + img.part = PART_UPDATE; + find_header_fail = 0; + find_header_called = 0; + ret = get_header(&img, type, &ptr); + ck_assert_uint_ne(ret, 0xFFFF); + ck_assert_int_eq(find_header_called, 1); + + /* Test get_img_hdr */ + img.part = PART_BOOT; + img.hdr = (void *)0xAABBCCDD; + ptr = get_img_hdr(&img); + ck_assert_ptr_eq(ptr, img.hdr); + + img.part = PART_UPDATE; + img.hdr = 0; + ptr = get_img_hdr(&img); + ck_assert_ptr_eq(ptr, hdr_cpy); + + /* Test image_size */ + sz = wolfBoot_image_size(test_img_v200000000_signed_bin); + ck_assert_uint_eq(sz, test_img_len - 256); +} + +START_TEST(test_verify_authenticity) +{ + struct wolfBoot_image test_img; + int ret; + memset(&test_img, 0, sizeof(struct wolfBoot_image)); + test_img.part = PART_UPDATE; + + /* Wrong sha field */ + find_header_mocked = 1; + ret = wolfBoot_verify_authenticity(&test_img); + ck_assert_int_eq(ret, -1); + + /* Wrong pubkey */ + find_header_mocked = 0; + hdr_cpy_done = 0; + ext_flash_write(0, test_img_v200000000_wrong_pubkey_bin, + test_img_len); + ret = wolfBoot_verify_authenticity(&test_img); + ck_assert_int_lt(ret, 0); + + /* Wrong signature */ + find_header_mocked = 0; + find_header_fail = 0; + hdr_cpy_done = 0; + ext_flash_write(0, test_img_v200000000_wrong_signature_bin, + test_img_len); + ret = wolfBoot_verify_authenticity(&test_img); + ck_assert_int_lt(ret, 0); + + /* Correct image */ + find_header_mocked = 0; + ecc_import_fail = 0; + ecc_init_fail = 0; + hdr_cpy_done = 0; + ext_flash_erase(0, 2 * WOLFBOOT_SECTOR_SIZE); + ext_flash_write(0, test_img_v123_signed_bin, + test_img_v123_signed_bin_len); + test_img.signature_ok = 1; /* mock for VERIFY_FN */ + ret = wolfBoot_verify_authenticity(&test_img); + ck_assert_int_eq(ret, 0); + +} +END_TEST + +START_TEST(test_verify_integrity) +{ + struct wolfBoot_image test_img; + int ret; + /* Wrong sha field */ + find_header_mocked = 1; + ret = wolfBoot_verify_integrity(&test_img); + ck_assert_int_eq(ret, -1); + + /* Correct image */ + find_header_mocked = 0; + find_header_fail = 0; + hdr_cpy_done = 0; + ecc_import_fail = 0; + ecc_init_fail = 0; + memset(&test_img, 0, sizeof(struct wolfBoot_image)); + ext_flash_erase(WOLFBOOT_PARTITION_UPDATE_ADDRESS, WOLFBOOT_SECTOR_SIZE); + ext_flash_write(WOLFBOOT_PARTITION_UPDATE_ADDRESS, + test_img_v123_signed_bin, + test_img_v123_signed_bin_len); + ret = wolfBoot_open_image(&test_img, PART_UPDATE); + ck_assert_int_eq(ret, 0); + ck_assert_uint_eq(test_img.hdr, WOLFBOOT_PARTITION_UPDATE_ADDRESS); + ret = wolfBoot_verify_integrity(&test_img); + ck_assert_int_eq(ret, 0); +} +END_TEST + +START_TEST(test_open_image) +{ + struct wolfBoot_image img; + int ret; + + + /* invalid argument */ + ret = wolfBoot_open_image(NULL, PART_UPDATE); + ck_assert_int_eq(ret, -1); + + /* Empty flash */ + find_header_mocked = 0; + hdr_cpy_done = 0; + ext_flash_erase(0, WOLFBOOT_SECTOR_SIZE); + ret = wolfBoot_open_image(&img, PART_UPDATE); + ck_assert_int_eq(ret, -1); + + /* Swap partition */ + ret = wolfBoot_open_image(&img, PART_SWAP); + ck_assert_uint_eq(img.hdr_ok, 1); + ck_assert_ptr_eq(img.hdr, WOLFBOOT_PARTITION_SWAP_ADDRESS); + ck_assert_ptr_eq(img.hdr, img.fw_base); + ck_assert_uint_eq(img.fw_size, WOLFBOOT_SECTOR_SIZE); + + /* Valid image */ + hdr_cpy_done = 0; + ext_flash_write(0, test_img_v200000000_signed_bin, + test_img_len); + ret = wolfBoot_open_image(&img, PART_UPDATE); + ck_assert_int_eq(ret, 0); + ck_assert_uint_eq(img.hdr_ok, 1); + ck_assert_ptr_eq(img.hdr, WOLFBOOT_PARTITION_UPDATE_ADDRESS); + ck_assert_ptr_eq(img.fw_base, WOLFBOOT_PARTITION_UPDATE_ADDRESS + 256); + +} +END_TEST + + +Suite *wolfboot_suite(void) +{ + /* Suite initialization */ + Suite *s = suite_create("wolfBoot"); + + TCase* tcase_verify_signature = tcase_create("verify_signature"); + tcase_set_timeout(tcase_verify_signature, 20); + tcase_add_test(tcase_verify_signature, test_verify_signature); + suite_add_tcase(s, tcase_verify_signature); + + TCase* tcase_sha_ops = tcase_create("sha_ops"); + tcase_set_timeout(tcase_sha_ops, 20); + tcase_add_test(tcase_sha_ops, test_sha_ops); + suite_add_tcase(s, tcase_sha_ops); + + TCase* tcase_headers = tcase_create("headers"); + tcase_set_timeout(tcase_headers, 20); + tcase_add_test(tcase_headers, test_headers); + suite_add_tcase(s, tcase_headers); + + TCase* tcase_verify_authenticity = tcase_create("verify_authenticity"); + tcase_set_timeout(tcase_verify_authenticity, 20); + tcase_add_test(tcase_verify_authenticity, test_verify_authenticity); + suite_add_tcase(s, tcase_verify_authenticity); + + TCase* tcase_verify_integrity = tcase_create("verify_integrity"); + tcase_set_timeout(tcase_verify_integrity, 20); + tcase_add_test(tcase_verify_integrity, test_verify_integrity); + suite_add_tcase(s, tcase_verify_integrity); + + TCase* tcase_open_image = tcase_create("open_image"); + tcase_set_timeout(tcase_open_image, 20); + tcase_add_test(tcase_open_image, test_open_image); + suite_add_tcase(s, tcase_open_image); + return s; +} + +int main(void) +{ + int fails; + Suite *s = wolfboot_suite(); + SRunner *sr = srunner_create(s); + srunner_run_all(sr, CK_NORMAL); + fails = srunner_ntests_failed(sr); + srunner_free(sr); + return fails; +} diff --git a/tools/unit-tests/unit-keystore.c b/tools/unit-tests/unit-keystore.c new file mode 100644 index 000000000..430fcd0e3 --- /dev/null +++ b/tools/unit-tests/unit-keystore.c @@ -0,0 +1,98 @@ +/* unit-keystore.c + * + * example keystore used for image.c unit tests + * + * + * Copyright (C) 2023 wolfSSL Inc. + * + * This file is part of wolfBoot. + * + * wolfBoot is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * wolfBoot is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1335, USA + */ +#include +#include "wolfboot/wolfboot.h" +#include "keystore.h" +#ifdef WOLFBOOT_NO_SIGN + #define NUM_PUBKEYS 0 +#else + +#if !defined(KEYSTORE_ANY) && (KEYSTORE_PUBKEY_SIZE != KEYSTORE_PUBKEY_SIZE_ECC256) + #error Key algorithm mismatch. Remove old keys via 'make keysclean' +#else + +#if defined(__APPLE__) && defined(__MACH__) +#define KEYSTORE_SECTION __attribute__((section ("__KEYSTORE,__keystore"))) +#else +#define KEYSTORE_SECTION __attribute__((section (".keystore"))) +#endif + +#define NUM_PUBKEYS 1 +const KEYSTORE_SECTION struct keystore_slot PubKeys[NUM_PUBKEYS] = { + + /* Key associated to file 'wolfboot_signing_private_key.der' */ + { + .slot_id = 0, + .key_type = AUTH_KEY_ECC256, + .part_id_mask = 0xFFFFFFFF, + .pubkey_size = KEYSTORE_PUBKEY_SIZE_ECC256, + .pubkey = { + + 0xc5, 0x7d, 0xbf, 0xfb, 0x23, 0x79, 0xba, 0xb6, + 0x31, 0x8f, 0x7b, 0x8d, 0xfe, 0xc9, 0x5d, 0x46, + 0xf5, 0x95, 0xb4, 0xa8, 0xbd, 0x45, 0xb7, 0x46, + 0xf3, 0x6c, 0x1b, 0x86, 0x28, 0x7b, 0x23, 0xd1, + 0x83, 0xf3, 0x27, 0x5c, 0x08, 0x1f, 0x9d, 0x9e, + 0x6c, 0xca, 0xee, 0xb3, 0x0d, 0x5c, 0x01, 0xb2, + 0xc5, 0x98, 0xf3, 0x85, 0x6c, 0xdd, 0x42, 0x54, + 0xef, 0x44, 0x94, 0x59, 0xf3, 0x08, 0x3d, 0xcd + }, + }, + + +}; + +int keystore_num_pubkeys(void) +{ + return NUM_PUBKEYS; +} + +uint8_t *keystore_get_buffer(int id) +{ + if (id >= keystore_num_pubkeys()) + return (uint8_t *)0; + return (uint8_t *)PubKeys[id].pubkey; +} + +int keystore_get_size(int id) +{ + if (id >= keystore_num_pubkeys()) + return -1; + return (int)PubKeys[id].pubkey_size; +} + +uint32_t keystore_get_mask(int id) +{ + if (id >= keystore_num_pubkeys()) + return -1; + return (int)PubKeys[id].part_id_mask; +} + +uint32_t keystore_get_key_type(int id) +{ + return PubKeys[id].key_type; +} + +#endif /* Keystore public key size check */ +#endif /* WOLFBOOT_NO_SIGN */ diff --git a/tools/unit-tests/unit-mock-state.c b/tools/unit-tests/unit-mock-state.c new file mode 100644 index 000000000..149aabcfd --- /dev/null +++ b/tools/unit-tests/unit-mock-state.c @@ -0,0 +1,398 @@ +/* unit-mock-state.c + * + * Unit test for parser functions in libwolfboot.c + * + * + * Copyright (C) 2023 wolfSSL Inc. + * + * This file is part of wolfBoot. + * + * wolfBoot is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * wolfBoot is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1335, USA + */ + +/* Option to enable sign tool debugging */ +/* Must also define DEBUG_WOLFSSL in user_settings.h */ +#define WOLFBOOT_HASH_SHA256 + +#if defined(ENCRYPT_WITH_AES256) || defined(ENCRYPT_WITH_AES128) + #define WOLFSSL_AES_COUNTER + #define WOLFSSL_AES_DIRECT +#endif +#if defined(ENCRYPT_WITH_AES256) + #define WOLFSSL_AES_256 +#endif +#if defined(ENCRYPT_WITH_CHACHA) + #define HAVE_CHACHA +#endif +#define WC_NO_HARDEN +#define NVM_FLASH_WRITEONCE + +#define WOLFSSL_USER_SETTINGS +#define UNIT_TEST +#define MOCK_PARTITION_TRAILER +#define MOCK_BLOB_TYPE +#include +#include +#include +#include +#include +#include "user_settings.h" +#include "wolfboot/wolfboot.h" +static uint8_t* get_trailer_at(uint8_t part, uint32_t at); +static void set_trailer_at(uint8_t part, uint32_t at, uint8_t val); +static void set_partition_magic(uint8_t part); + +static uint8_t current_backup_part = 1; +uint8_t image_backup(uint8_t part_id) +{ + printf("Called image_backup\n"); + return current_backup_part; +} + +#ifndef PART_TOTAL_IDS +# define PART_TOTAL_IDS 3 +#endif + +#include "libwolfboot.c" + +/* Mocks */ + +static int locked = 0; +static int hal_flash_write_mock_called = 0; +static uintptr_t hal_flash_write_mock_address = 0U; +static uint8_t *hal_flash_write_mock_data = NULL; +static int hal_flash_write_mock_len = 0; + +static uintptr_t hal_flash_erase_mock_address = 0; +static int hal_flash_erase_mock_len = 0; + +static int hal_flash_erase_mock_called = 0; + +static void hal_flash_write_mock_reset(void) +{ + hal_flash_write_mock_called = 0; + hal_flash_write_mock_address = 0U; + hal_flash_write_mock_data = NULL; + hal_flash_write_mock_len = 0; +} + +static void hal_flash_erase_mock_reset(void) +{ + hal_flash_erase_mock_called = 0; + hal_flash_erase_mock_address = 0U; + hal_flash_erase_mock_len = 0; +} + +void hal_init(void) +{ +} +int hal_flash_write(uint32_t address, const uint8_t *data, int len) +{ + printf("Called hal_flash_write\r\n"); + hal_flash_write_mock_called++; + hal_flash_write_mock_address = address; + hal_flash_write_mock_data = data; + hal_flash_write_mock_len = len; + return 0; +} +int hal_flash_erase(uint32_t address, int len) +{ + printf("Called hal_flash_erase\r\n"); + hal_flash_erase_mock_called++; + hal_flash_erase_mock_address = address; + hal_flash_erase_mock_len = len; + return 0; +} +void hal_flash_unlock(void) +{ +} +void hal_flash_lock(void) +{ +} + +void hal_prepare_boot(void) +{ +} + +static int ext_locked = 1; +static int ext_flash_write_mock_called = 0; +static uintptr_t ext_flash_write_mock_address = 0U; +static uint8_t *ext_flash_write_mock_data = NULL; +static int ext_flash_write_mock_len = 0; + +static int ext_flash_read_mock_called = 0; +static uint32_t ext_flash_read_mock_address = 0U; +static uint8_t *ext_flash_read_mock_data = NULL; +static int ext_flash_read_mock_len = 0; + +static uintptr_t ext_flash_erase_mock_address = 0U; +static int ext_flash_erase_mock_len = 0; + +static int ext_flash_erase_mock_called = 0; + +static void ext_flash_write_mock_reset(void) +{ + ext_flash_write_mock_called = 0; + ext_flash_write_mock_address = 0U; + ext_flash_write_mock_data = NULL; + ext_flash_write_mock_len = 0; +} + +static void ext_flash_erase_mock_reset(void) +{ + ext_flash_erase_mock_called = 0; + ext_flash_erase_mock_address = 0U; + ext_flash_erase_mock_len = 0; +} + +void ext_init(void) +{ +} +int ext_flash_read(uintptr_t address, uint8_t *data, int len) +{ + printf("Called ext_flash_read\r\n"); + ext_flash_read_mock_called++; + ext_flash_read_mock_address = address; + ext_flash_read_mock_data = data; + ext_flash_read_mock_len = len; + return 0; +} +int ext_flash_write(uintptr_t address, const uint8_t *data, int len) +{ + printf("Called ext_flash_write\r\n"); + ext_flash_write_mock_called++; + ext_flash_write_mock_address = address; + ext_flash_write_mock_data = data; + ext_flash_write_mock_len = len; + return 0; +} +int ext_flash_erase(uintptr_t address, int len) +{ + printf("Called ext_flash_erase\r\n"); + ext_flash_erase_mock_called++; + ext_flash_erase_mock_address = address; + ext_flash_erase_mock_len = len; + return 0; +} +void ext_flash_unlock(void) +{ + fail_unless(ext_locked, "Double unlock detected (ext)\n"); + ext_locked--; +} +void ext_flash_lock(void) +{ + fail_if(ext_locked, "Double lock detected(ext)\n"); + ext_locked++; +} + + +static uint8_t test_buffer[512] = { + 'W', 'O', 'L', 'F', 0x00, 0x00, 0x01, 0x00, + 0x01, 0x00, 0x04, 0x00, 0x0d, 0x0c, 0x0b, 0x0a, + 0xFF, 0xFF, 0xFF, 0xFF, 0x02, 0x00, 0x08, 0x00, + 0x07, 0x06, 0x05, 0x04, 0x03, 0x02, 0x01, 0x00, + 0xFF, 0xFF, 0xFF, 0xFF, 0x03, 0x00, 0x20, 0x00, + 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, + 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f, + 0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17, + 0x18, 0x19, 0x1a, 0x1b, 0x1c, 0x1d, 0x1e, 0x1f, + 0x00, 0x00, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, /*<-- end of options */ + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + /* End HDR */ + 0x13, 0x13, 0x13, 0x13, 0x13, 0x13, 0x13, 0x13, +}; + + +static uint8_t mock_partition_state; +static int mock_partition_state_retval; +static uint8_t mock_partition_state_arg_part; +static int mock_get_partition_state_called = 0; + +struct mock_state { + uint8_t part; + uint8_t state; + int retval; + int getstate_called; + int setstate_called; +}; + +static struct mock_state mock_state[PART_TOTAL_IDS] = { + { PART_BOOT, IMG_STATE_NEW, -1, 0, 0 }, + { PART_UPDATE, IMG_STATE_NEW, -1, 0, 0 }, + { PART_SWAP, IMG_STATE_NEW, -1, 0, 0 } +}; + +static void mock_set_initial_partition_state(uint8_t part, uint8_t st) +{ + mock_state[part].retval = 0; + mock_state[part].state = st; +} + +static void mock_reset_partition_states(void) +{ + int i; + for (i = 0; i < PART_TOTAL_IDS; i++) { + mock_state[i].retval = -1; + mock_state[i].state = IMG_STATE_NEW; + mock_state[i].getstate_called = 0; + mock_state[i].setstate_called = 0; + } +} + +static uint8_t magic_trailer[4] = { 'B','O','O','T' }; +static uint8_t erased_trailer[4] = { 0xFF, 0xFF, 0xFF, 0xFF }; + +static uint8_t* get_trailer_at(uint8_t part, uint32_t at) +{ + //ck_assert_uint_lt(part, PART_TOTAL_IDS); + if (part >= PART_TOTAL_IDS) + return NULL; + if (at == 1) + mock_state[part].getstate_called++; + if ((at == 0) && (mock_state[part].retval == 0)) { + return magic_trailer; + } else if ((at == 1) && (mock_state[part].retval == 0)) { + return &mock_state[part].state; + } else { + return erased_trailer; + } +} + +static void set_trailer_at(uint8_t part, uint32_t at, uint8_t val) +{ + ck_assert_uint_lt(part, PART_TOTAL_IDS); + if (at == 1) { + printf("Setting part %d state %02x\n", part, val); + mock_state[part].setstate_called++; + mock_state[part].state = val; + } +} + +static void set_partition_magic(uint8_t part) +{ + ck_assert_uint_lt(part, PART_TOTAL_IDS); + mock_state[part].retval = 0; +} + +/* End Mocks */ + +START_TEST(test_wolfBoot_set_partition_state) +{ + int i; + uint8_t st = 0x0D; + /* Corner cases: PART_NONE should have no effect */ + mock_reset_partition_states(); + wolfBoot_set_partition_state(PART_NONE, IMG_STATE_SUCCESS); + ck_assert_uint_eq(mock_state[PART_BOOT].state, IMG_STATE_NEW); + ck_assert_uint_eq(mock_state[PART_BOOT].getstate_called, 0); + ck_assert_uint_eq(mock_state[PART_UPDATE].state, IMG_STATE_NEW); + ck_assert_uint_eq(mock_state[PART_UPDATE].getstate_called, 0); + + /* Ensure 'get_partition_state()' with 'PART_NONE' is invalid and + * has no side effects + */ + ck_assert_int_eq(wolfBoot_get_partition_state(PART_NONE, &st), -1); + for (i = 0; i < PART_TOTAL_IDS - 1; i++) { + ck_assert_uint_eq(mock_state[i].state, IMG_STATE_NEW); + ck_assert_int_eq(mock_state[i].retval, -1); + ck_assert_uint_eq(mock_state[i].getstate_called, 0); + ck_assert_uint_eq(mock_state[i].setstate_called, 0); + } + + /* Sunny day set state change */ + mock_reset_partition_states(); + wolfBoot_set_partition_state(PART_UPDATE, IMG_STATE_TESTING); + ck_assert_uint_eq(mock_state[PART_UPDATE].state, IMG_STATE_TESTING); + ck_assert_int_eq(mock_state[PART_UPDATE].retval, 0); + ck_assert_uint_ge(mock_state[PART_UPDATE].getstate_called, 1); + ck_assert_uint_ge(mock_state[PART_UPDATE].setstate_called, 1); + +} + +END_TEST + +START_TEST(test_wolfBoot_misc_utils) +{ + uint16_t word2 = 0xA0B1; + uint32_t word4 = 0xA0B1C2D3; + uint8_t *hdr_cpy_ptr = NULL; + ext_flash_erase_mock_reset(); + mock_reset_partition_states(); + ck_assert_uint_eq(wb_reverse_word32(word4), 0xD3C2B1A0); + + ck_assert_uint_eq(im2n(word4), word4); + ck_assert_uint_eq(im2ns(word2), word2); + + ck_assert_ptr_eq(wolfBoot_get_image_from_part(PART_BOOT), (void *)WOLFBOOT_PARTITION_BOOT_ADDRESS); + ck_assert_ptr_eq(wolfBoot_get_image_from_part(PART_UPDATE), (void *)WOLFBOOT_PARTITION_UPDATE_ADDRESS); + + +} +END_TEST + + + + +Suite *wolfboot_suite(void) +{ + + /* Suite initialization */ + Suite *s = suite_create("wolfBoot"); + + TCase* tcase_wolfBoot_set_partition_state = tcase_create("wolfBoot_set_partition_state"); + tcase_set_timeout(tcase_wolfBoot_set_partition_state, 20); + tcase_add_test(tcase_wolfBoot_set_partition_state, test_wolfBoot_set_partition_state); + suite_add_tcase(s, tcase_wolfBoot_set_partition_state); + + TCase* tcase_wolfBoot_misc_utils = tcase_create("wolfBoot_misc_utils"); + tcase_set_timeout(tcase_wolfBoot_misc_utils, 20); + tcase_add_test(tcase_wolfBoot_misc_utils, test_wolfBoot_misc_utils); + suite_add_tcase(s, tcase_wolfBoot_misc_utils); + + return s; +} + + +int main(void) +{ + int fails; + Suite *s = wolfboot_suite(); + SRunner *sr = srunner_create(s); + srunner_run_all(sr, CK_NORMAL); + fails = srunner_ntests_failed(sr); + srunner_free(sr); + return fails; +} diff --git a/tools/unit-tests/unit-pci.c b/tools/unit-tests/unit-pci.c index b48065655..fc5afd6ac 100644 --- a/tools/unit-tests/unit-pci.c +++ b/tools/unit-tests/unit-pci.c @@ -1,3 +1,26 @@ +/* unit-pci.c + * + * Unit test for pci functions + * + * + * Copyright (C) 2023 wolfSSL Inc. + * + * This file is part of wolfBoot. + * + * wolfBoot is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * wolfBoot is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1335, USA + */ #include #include #include diff --git a/tools/unit-tests/unit-sectorflags.c b/tools/unit-tests/unit-sectorflags.c new file mode 100644 index 000000000..0c8057d76 --- /dev/null +++ b/tools/unit-tests/unit-sectorflags.c @@ -0,0 +1,258 @@ +/* unit-sectorflags.c + * + * Unit test for sector flags functions in libwolfboot.c + * + * + * Copyright (C) 2023 wolfSSL Inc. + * + * This file is part of wolfBoot. + * + * wolfBoot is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * wolfBoot is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1335, USA + */ + +/* Option to enable sign tool debugging */ +/* Must also define DEBUG_WOLFSSL in user_settings.h */ +#define FLASH_SIZE (33 * 1024) +#define WOLFBOOT_HASH_SHA256 +#define IMAGE_HEADER_SIZE 256 +#define EXT_FLASH 1 +#define PART_UPDATE_EXT 1 +#define PART_SWAP_EXT 1 + + +#define WC_NO_HARDEN + +#define WOLFSSL_USER_SETTINGS +#define ENCRYPT_KEY "123456789abcdef0123456789abcdef0123456789abcdef" +#define UNIT_TEST +#include +#include +#include +#include +#include +#include "user_settings.h" + +uint8_t *ut_get_endpart(void); +#include "libwolfboot.c" + +/* Mocks */ + +static int locked = 0; +static int ext_locked = 0; + +void hal_init(void) +{ +} +int hal_flash_write(uint32_t address, const uint8_t *data, int len) +{ + return 0; +} +int hal_flash_erase(uint32_t address, int len) +{ + return 0; +} +void hal_flash_unlock(void) +{ + fail_unless(locked, "Double unlock detected\n"); + locked--; +} +void hal_flash_lock(void) +{ + fail_if(locked, "Double lock detected\n"); + locked++; +} + +void ext_flash_unlock(void) +{ + //fail_unless(ext_locked, "Double unlock detected\n"); + ext_locked--; +} +void ext_flash_lock(void) +{ + //fail_if(ext_locked, "Double lock detected\n"); + ext_locked++; +} + +void hal_prepare_boot(void) +{ +} + +/* Emulation of external flash with a static buffer of 32KB (update) + 1KB (swap) */ +uint8_t flash[FLASH_SIZE]; + +uint8_t *ut_get_endpart(void) +{ + return flash + WOLFBOOT_PARTITION_SIZE; +} + +/* Mocks for ext_flash_read, ext_flash_write, and ext_flash_erase functions */ +int ext_flash_read(uintptr_t address, uint8_t *data, int len) { + printf("Called ext_flash_read %p %p %d\n", address, data, len); + + /* Check that the read address and size are within the bounds of the flash memory */ + ck_assert_int_le(address + len, FLASH_SIZE); + + /* Copy the data from the flash memory to the output buffer */ + memcpy(data, &flash[address], len); + + return len; +} + +int ext_flash_write(uintptr_t address, const uint8_t *data, int len) { + printf("Called ext_flash_write %p %p %d\n", address, data, len); + /* Check that the write address and size are within the bounds of the flash memory */ + ck_assert_int_le(address + len, FLASH_SIZE); + + /* Copy the data from the input buffer to the flash memory */ + memcpy(&flash[address], data, len); + + return 0; +} + +int ext_flash_erase(uintptr_t address, int len) { + printf("Called ext_flash_erase %p %d\n", address, len); + /* Check that the erase address and size are within the bounds of the flash memory */ + ck_assert_int_le(address + len, FLASH_SIZE); + + /* Erase the flash memory by setting each byte to 0xFF, WOLFBOOT_SECTOR_SIZE bytes at a time */ + uint32_t i; + for (i = address; i < address + len; i += WOLFBOOT_SECTOR_SIZE) { + memset(&flash[i], 0xFF, WOLFBOOT_SECTOR_SIZE); + } + + return 0; +} + +static uint8_t test_buffer[512] = { + 'W', 'O', 'L', 'F', 0x00, 0x00, 0x01, 0x00, + 0x01, 0x00, 0x04, 0x00, 0x0d, 0x0c, 0x0b, 0x0a, + 0xFF, 0xFF, 0xFF, 0xFF, 0x02, 0x00, 0x08, 0x00, + 0x07, 0x06, 0x05, 0x04, 0x03, 0x02, 0x01, 0x00, + 0xFF, 0xFF, 0xFF, 0xFF, 0x03, 0x00, 0x20, 0x00, + 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, + 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f, + 0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17, + 0x18, 0x19, 0x1a, 0x1b, 0x1c, 0x1d, 0x1e, 0x1f, + 0x00, 0x00, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, /*<-- end of options */ + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + /* End HDR */ + 0x13, 0x13, 0x13, 0x13, 0x13, 0x13, 0x13, 0x13, +}; + + +START_TEST(test_partition_flags) { + uint32_t address = 0; + uint32_t size = 512; + uint8_t data[2 * WOLFBOOT_SECTOR_SIZE]; + uint8_t empty_sector[WOLFBOOT_SECTOR_SIZE]; + int rres, wres, eres; + uint8_t st; + + memset(empty_sector, 0xFF, WOLFBOOT_SECTOR_SIZE); + + /* Write data to the flash memory */ + wres = ext_flash_write(address, test_buffer, size); + ck_assert_int_eq(wres, 0); + + /* Read data from the flash memory */ + rres = ext_flash_read(address, data, size); + ck_assert_int_eq(rres, size); + + /* Check that the data read from the flash memory matches the data that was written */ + ck_assert_mem_eq(data, test_buffer, size); + + /* Set partition to updating state */ + wolfBoot_update_trigger(); + + /* Get partition state */ + wolfBoot_get_partition_state(PART_UPDATE, &st); + ck_assert_int_eq(st, IMG_STATE_UPDATING); + + /* Change to IMG_STATE_TESTING */ + st = IMG_STATE_TESTING; + wolfBoot_set_partition_state(PART_UPDATE, st); + wolfBoot_get_partition_state(PART_UPDATE, &st); + ck_assert_int_eq(st, IMG_STATE_TESTING); + + /* Change to IMG_STATE_SUCCESS */ + st = IMG_STATE_SUCCESS; + wolfBoot_set_partition_state(PART_UPDATE, st); + wolfBoot_get_partition_state(PART_UPDATE, &st); + ck_assert_int_eq(st, IMG_STATE_SUCCESS); +} +END_TEST + +START_TEST(test_sector_flags) { + +} +END_TEST + + +/* End Mocks */ + +Suite *wolfboot_suite(void) +{ + + /* Suite initialization */ + Suite *s = suite_create("wolfBoot"); + + /* Test cases */ + TCase *partition_flags = tcase_create("External flash operations: partition flags"); + TCase *sector_flags = tcase_create("External encrypted flash operations"); + + /* Set parameters + add to suite */ + tcase_add_test(partition_flags, test_partition_flags); + tcase_add_test(sector_flags, test_sector_flags); + + tcase_set_timeout(partition_flags, 20); + tcase_set_timeout(sector_flags, 20); + suite_add_tcase(s, partition_flags); + suite_add_tcase(s, sector_flags); + + return s; +} + + +int main(void) +{ + int fails; + Suite *s = wolfboot_suite(); + SRunner *sr = srunner_create(s); + srunner_run_all(sr, CK_NORMAL); + fails = srunner_ntests_failed(sr); + srunner_free(sr); + return fails; +}