diff --git a/.github/workflows/test-library.yml b/.github/workflows/test-library.yml index 2b9323f232..53f6b2c439 100644 --- a/.github/workflows/test-library.yml +++ b/.github/workflows/test-library.yml @@ -20,7 +20,7 @@ jobs: fail-fast: false # Limit concurrent jobs for scheduling problem on GitHub's hosted runner pool. - max-parallel: 12 + max-parallel: 10 matrix: math: @@ -31,7 +31,7 @@ jobs: - "SPMATH=0 SPMATHALL=0 WOLFBOOT_SMALL_STACK=0" - "SPMATH=0 SPMATHALL=0 WOLFBOOT_SMALL_STACK=1" asym: [ed25519, ecc256, ecc384, ecc521, rsa2048, rsa3072, rsa4096, ed448] - hash: [sha256, sha384, sha3] + hash: [sha256, sha384, sha3] # --sha256 for commandline, SHA256 for make # See https://github.com/wolfSSL/wolfBoot/issues/614 regarding exclusions: exclude: @@ -41,23 +41,38 @@ jobs: steps: - uses: actions/checkout@v4 with: + clean: true submodules: true - - name: make clean + - name: dist clean run: | - make keysclean && make -C tools/keytools clean && rm -f include/target.h + # Ensure parallel build did not leave behind any debris + make distclean - name: Build test-lib env: shell: bash - ASYM: ${{ matrix.asym }} + ASYM: ${{ matrix.asym }} # MAKE_SIGN in upper case HASH: ${{ matrix.hash }} MATH: ${{ matrix.math }} run: | + # Test various library parameters + + export MAKE_SIGN="${ASYM^^}" + export MAKE_HASH="${HASH^^}" + export MAKE_MATH='${{ matrix.math }}' # e.g., "SPMATH=1 WOLFBOOT_SMALL_STACK=1" + export PRIVATE_KEY="wolfboot_signing_private_key.der" + + echo "This MAKE_SIGN=$MAKE_SIGN" + echo "This MAKE_HASH=$MAKE_HASH" + echo "This MAKE_MATH=$MAKE_MATH" + # Sample build build_once() { # Convert asym and hash to upper case, optionally add additional param - make -j test-lib SIGN=${ASYM^^} HASH=${HASH^^} ${MATH} "$@" + echo "Build test-lib..." + echo "make -j1 test-lib SIGN=${MAKE_SIGN} HASH=${MAKE_HASH} ${MATH} \"$@\"" + make -j1 test-lib SIGN=${MAKE_SIGN} HASH=${MAKE_HASH} ${MATH} "$@" } set -euo pipefail @@ -65,17 +80,65 @@ jobs: # Get the reference config cp config/examples/library.config .config + # peek + echo "Existing files?" + if [ -f "src/keystore.c" ]; then + echo "WARNING: Found unexpected src/keystore.c" + fi + if [ -f "include/target.h" ]; then + echo "WARNING: Found unexpected include/target.h" + fi + if [ -f "keystore.der" ]; then + echo "WARNING: Found unexpected keystore.der" + fi + if [ -f "wolfboot_signing_private_key.der" ]; then + echo "WARNING: Found unexpected wolfboot_signing_private_key.der" + fi + if [ -f "./tools/keytools/keystore.der" ]; then + echo "WARNING: Found unexpected ./tools/keytools/keystore.der" + fi + if [ -f "./tools/keytools/wolfboot_signing_private_key.der" ]; then + echo "WARNING: Found unexpected ./tools/keytools/wolfboot_signing_private_key.der" + fi + # Keytools - make keytools - ./tools/keytools/keygen --${ASYM} -g wolfboot_signing_private_key.der + echo "" + echo "make -j1 keytools SIGN=\"${MAKE_SIGN}\" HASH=\"${MAKE_HASH}\" $MATH" + make -j1 keytools SIGN="${MAKE_SIGN}" HASH="${MAKE_HASH}" $MATH + + # Generate keys + echo "" + echo "./tools/keytools/keygen --${ASYM} -g wolfboot_signing_private_key.der" + ./tools/keytools/keygen --${ASYM} -g wolfboot_signing_private_key.der + + # Force fresh files + # peek + echo "Existing files?" + if [ -f "src/keystore.c" ]; then + echo "Found unexpected src/keystore.c" + fi + if [ -f "include/target.h" ]; then + echo "Found unexpected include/target.h" + fi + if [ -f "keystore.der" ]; then + echo "Found unexpected keystore.der" + fi + if [ -f "wolfboot_signing_private_key.der" ]; then + echo "Found unexpected wolfboot_signing_private_key.der" + fi # Sign + echo "" echo "Test" > test.bin - ./tools/keytools/sign --${ASYM} --${HASH} test.bin wolfboot_signing_private_key.der 1 + echo "Sign test.bin" + echo "./tools/keytools/sign --${ASYM} --${HASH} test.bin wolfboot_signing_private_key.der 1" + ./tools/keytools/sign --${ASYM} --${HASH} test.bin wolfboot_signing_private_key.der 1 # First attempt if build_once >build.out 2>build.err; then echo "Success on first attempt, WOLFBOOT_HUGE_STACK not applied." + cat build.out + cat build.err exit 0 fi @@ -90,12 +153,16 @@ jobs: build_once WOLFBOOT_HUGE_STACK=1 else echo "Build failed for another reason:" + cat build.out cat build.err exit 1 fi - name: Run test-lib run: | + # Check test_v1_signed.bin + + echo "./test-lib test_v1_signed.bin" ./test-lib test_v1_signed.bin ./test-lib test_v1_signed.bin 2>&1 | grep "Firmware Valid" @@ -104,5 +171,25 @@ jobs: # Corrupt signed binary truncate -s -1 test_v1_signed.bin echo "A" >> test_v1_signed.bin - ./test-lib test_v1_signed.bin - ./test-lib test_v1_signed.bin 2>&1 | grep "Failure" + + # Run once, capture output and status + set +e + output=$(./test-lib test_v1_signed.bin 2>&1) + status=$? + set -e + + echo "$output" + + # Must have failed (non-zero exit) + if [ $status -eq 0 ]; then + echo "Expected failure, but exit code was 0" + exit 1 + fi + + # Must include the expected Failure message + echo "$output" | grep -F "Failure" >/dev/null || { + echo "Expected 'Failure' not found in output" + exit 1 + } + + echo "Got expected non-zero exit and 'Failure' message." diff --git a/.github/workflows/test-sunnyday-simulator.yml b/.github/workflows/test-sunnyday-simulator.yml index 9f5a38b6eb..04091e6e7e 100644 --- a/.github/workflows/test-sunnyday-simulator.yml +++ b/.github/workflows/test-sunnyday-simulator.yml @@ -90,6 +90,26 @@ jobs: run: | tools/scripts/sim-sunnyday-update.sh + - name: Cleanup before dualbank simulator test + run: | + make keysclean + + - name: Build wolfboot.elf (dualbank simulator) + run: | + make clean + mv .config .config.orig + cp config/examples/sim-dualbank.config .config + make test-sim-internal-flash-with-update + + - name: Run dualbank swap simulation + run: | + tools/scripts/sim-dualbank-swap-update.sh + + - name: Cleanup before WOLFBOOT_SMALL_STACK test + run: | + make keysclean + mv .config.orig .config + - name: Build wolfboot.elf (ECC256, WOLFBOOT_SMALL_STACK) run: | make clean && make test-sim-internal-flash-with-update SIGN=ECC256 WOLFBOOT_SMALL_STACK=1 SPMATH=1 diff --git a/config/examples/sim-dualbank.config b/config/examples/sim-dualbank.config new file mode 100644 index 0000000000..805ba4b634 --- /dev/null +++ b/config/examples/sim-dualbank.config @@ -0,0 +1,18 @@ +ARCH=sim +TARGET=sim +SIGN?=ED25519 +HASH?=SHA256 +WOLFBOOT_SMALL_STACK?=0 +SPI_FLASH=0 +DEBUG=1 +DUALBANK_SWAP=1 + +# sizes should be multiple of system page size +WOLFBOOT_PARTITION_SIZE=0x40000 +WOLFBOOT_SECTOR_SIZE=0x1000 +WOLFBOOT_PARTITION_BOOT_ADDRESS=0x80000 +WOLFBOOT_PARTITION_UPDATE_ADDRESS=0x100000 +WOLFBOOT_PARTITION_SWAP_ADDRESS=0x180000 + +# required for keytools +WOLFBOOT_FIXED_PARTITIONS=1 diff --git a/hal/library.c b/hal/library.c index ca7f83827d..02f1b4d652 100644 --- a/hal/library.c +++ b/hal/library.c @@ -46,6 +46,12 @@ #undef NO_FILESYSTEM #endif +#ifdef WOLFBOOT_KEYTOOLS + /* this code needs to use the Use ./include/user_settings.h file */ + #error "The wrong user_settings.h has been included." +#endif + + /* HAL Stubs */ void hal_init(void) { @@ -124,14 +130,17 @@ int wolfBoot_start(void) os_image.hdr = (uint8_t*)gImage; if ((ret = wolfBoot_open_image_address(&os_image, (uint8_t*)gImage)) < 0) { + wolfBoot_printf("Failed to open image address.\n"); goto exit; } if ((ret = wolfBoot_verify_integrity(&os_image)) < 0) { + wolfBoot_printf("Failed to verify integrity.\n"); goto exit; } if ((ret = wolfBoot_verify_authenticity(&os_image)) < 0) { + wolfBoot_printf("Failed to verify authenticity.\n"); goto exit; } @@ -142,11 +151,13 @@ int wolfBoot_start(void) exit: if (ret < 0) { wolfBoot_printf("Failure %d: Hdr %d, Hash %d, Sig %d\n", ret, - (int)os_image.hdr_ok, (int)os_image.sha_ok, - (int)os_image.signature_ok); + os_image.hdr_ok, os_image.sha_ok, os_image.signature_ok); + return -1; + } + else { + return 0; } - return 0; } @@ -155,39 +166,62 @@ int main(int argc, const char* argv[]) int ret = 0; #ifdef NO_FILESYSTEM + wolfBoot_printf("NO_FILESYSTEM is defined, looking at test_img"); gImage = (uintptr_t)test_img; #else - if (argc > 1) { - size_t sz = 0, bread; + if (argc == 2) { + size_t sz = 0, bread = 0; FILE* img = fopen(argv[1], "rb"); if (img == NULL) { - wolfBoot_printf("failed to open %s!\n", argv[1]); + wolfBoot_printf("Failed to open file: %s!\n\n", argv[1]); + wolfBoot_printf("Usage: %s image_file.bin\n", argv[0]); return -3; } - fseek(img, 0, SEEK_END); - sz = ftell(img); - fseek(img, 0, SEEK_SET); + else { + wolfBoot_printf("Looking at image file: %s\n", argv[1]); + fseek(img, 0, SEEK_END); + sz = ftell(img); + fseek(img, 0, SEEK_SET); + + gImage = (uintptr_t)malloc(sz); + } - gImage = (uintptr_t)malloc(sz); if (((void*)gImage) == NULL) { - wolfBoot_printf("failed to malloc %zu bytes for image\n", sz); + wolfBoot_printf("Failed to malloc %zu bytes for image.\n", sz); ret = -1; } + else { + /* check the image */ + bread = fread((void*)gImage, 1, sz, img); + } - bread = fread((void*)gImage, 1, sz, img); - if (bread != sz) { + if (bread == sz) { + wolfBoot_printf("Confirmed expected size: %zu bytes.\n", bread); + } + else { ret = -2; - wolfBoot_printf("read %zu of %zu bytes from %s\n", bread, sz, argv[1]); + wolfBoot_printf("Read %zu of %zu bytes from %s\n", bread, sz, argv[1]); } fclose(img); - } else { + } + else { wolfBoot_printf("usage: %s image_file.bin\n", argv[0]); ret = 255; } #endif if (ret == 0) { + wolfBoot_printf("Checking image... "); ret = wolfBoot_start(); } + if (ret == 0) { + wolfBoot_printf("Success!\n"); + } + else { + if (ret != 255) { + /* Only show error if we actually processed file, not missing params */ + wolfBoot_printf("Failed to verify with wolfBoot_start\n"); + } + } #ifndef NO_FILESYSTEM if ((void*)gImage != NULL) diff --git a/hal/sim.c b/hal/sim.c index bc312c96f6..4df7f8d524 100644 --- a/hal/sim.c +++ b/hal/sim.c @@ -73,6 +73,15 @@ int extFlashLocked = 1; #define INTERNAL_FLASH_FILE "./internal_flash.dd" #define EXTERNAL_FLASH_FILE "./external_flash.dd" +#ifdef DUALBANK_SWAP +#define SIM_REGISTER_FILE "./sim_registers.dd" +#define SIM_FLASH_OPTR_SWAP_BANK (1U << 20) +static uint32_t sim_flash_optr; +static void sim_dualbank_register_load(void); +static void sim_dualbank_register_store(void); +uint32_t hal_sim_get_dualbank_state(void); +#endif + /* global used to store command line arguments to forward to the test * application */ char **main_argv; @@ -224,6 +233,57 @@ static int mmap_file(const char *path, uint8_t *address, uint8_t** ret_address) return 0; } +#ifdef DUALBANK_SWAP +static void sim_dualbank_register_store(void) +{ + int fd = open(SIM_REGISTER_FILE, O_RDWR | O_CREAT, 0644); + if (fd == -1) { + wolfBoot_printf("Failed to open %s: %s\n", SIM_REGISTER_FILE, strerror(errno)); + return; + } + + if (pwrite(fd, &sim_flash_optr, sizeof(sim_flash_optr), 0) != + (ssize_t)sizeof(sim_flash_optr)) { + wolfBoot_printf("Failed to store dualbank swap state: %s\n", + strerror(errno)); + } + + close(fd); +} + +static void sim_dualbank_register_load(void) +{ + int fd = open(SIM_REGISTER_FILE, O_RDWR | O_CREAT, 0644); + uint32_t value = 0; + int rd; + if (fd == -1) { + wolfBoot_printf("Failed to open %s: %s\n", SIM_REGISTER_FILE, + strerror(errno)); + exit(-1); + } + + rd = pread(fd, &value, sizeof(value), 0); + + if (rd == (int)sizeof(value)) { + sim_flash_optr = value; + } else { + sim_flash_optr = 0; + if (pwrite(fd, &sim_flash_optr, sizeof(sim_flash_optr), 0) != + sizeof(sim_flash_optr)) { + wolfBoot_printf("Failed to initialize dualbank swap state: %s\n", + strerror(errno)); + } + } + + close(fd); +} + +uint32_t hal_sim_get_dualbank_state(void) +{ + return (sim_flash_optr & SIM_FLASH_OPTR_SWAP_BANK) ? 1U : 0U; +} +#endif + void hal_flash_unlock(void) { flashLocked = 0; @@ -234,6 +294,46 @@ void hal_flash_lock(void) flashLocked = 1; } +#ifdef DUALBANK_SWAP +void hal_flash_dualbank_swap(void) +{ + uint8_t *boot = (uint8_t *)WOLFBOOT_PARTITION_BOOT_ADDRESS; + uint8_t *update = (uint8_t *)WOLFBOOT_PARTITION_UPDATE_ADDRESS; + uint8_t *buffer; + int was_locked = flashLocked; + + buffer = (uint8_t *)malloc(WOLFBOOT_PARTITION_SIZE); + if (buffer == NULL) { + wolfBoot_printf("Simulator dualbank swap failed: out of memory\n"); + exit(-1); + } + + if (was_locked) + hal_flash_unlock(); + + memcpy(buffer, boot, WOLFBOOT_PARTITION_SIZE); + memcpy(boot, update, WOLFBOOT_PARTITION_SIZE); + memcpy(update, buffer, WOLFBOOT_PARTITION_SIZE); + + if (msync(boot, WOLFBOOT_PARTITION_SIZE, MS_SYNC) != 0) { + wolfBoot_printf("msync boot partition failed: %s\n", strerror(errno)); + } + if (msync(update, WOLFBOOT_PARTITION_SIZE, MS_SYNC) != 0) { + wolfBoot_printf("msync update partition failed: %s\n", strerror(errno)); + } + + free(buffer); + + sim_flash_optr ^= SIM_FLASH_OPTR_SWAP_BANK; + sim_dualbank_register_store(); + wolfBoot_printf("Simulator dualbank swap complete, register=%u\n", + hal_sim_get_dualbank_state()); + + if (was_locked) + hal_flash_lock(); +} +#endif + void hal_prepare_boot(void) { /* no op */ @@ -312,6 +412,10 @@ void hal_init(void) } #endif /* EXT_FLASH */ +#ifdef DUALBANK_SWAP + sim_dualbank_register_load(); +#endif + for (i = 1; i < main_argc; i++) { if (strcmp(main_argv[i], "powerfail") == 0) { erasefail_address = strtol(main_argv[++i], NULL, 16); @@ -480,6 +584,7 @@ void do_boot(const uint32_t *app_offset) #endif #endif +#if !defined(WOLFBOOT_DUALBOOT) int wolfBoot_fallback_is_possible(void) { return 0; @@ -489,6 +594,7 @@ int wolfBoot_dualboot_candidate(void) { return 0; } +#endif #ifdef WOLFBOOT_ENABLE_WOLFHSM_CLIENT diff --git a/hal/stm32c0.c b/hal/stm32c0.c index 7a0a9f53fa..ceb8231fc2 100644 --- a/hal/stm32c0.c +++ b/hal/stm32c0.c @@ -95,8 +95,8 @@ #define FLASH_CR_PG (1 << 0) /* RM0490 - 3.7.5 - FLASH_CR */ #define FLASH_CR_SEC_PROT (1 << 28) /* RM0490 - 3.7.5 - FLASH_CR */ -#define FLASH_CR_PNB_SHIFT 3 /* RM0490 - 3.7.5 - FLASH_CR - PNB bits 8:3 */ -#define FLASH_CR_PNB_MASK 0x3f /* RM0490 - 3.7.5 - FLASH_CR - PNB bits 8:3 - 6 bits */ +#define FLASH_CR_PNB_SHIFT 3 /* RM0490 - 4.7.5 - FLASH_CR - PNB bits 9:3 */ +#define FLASH_CR_PNB_MASK 0x7f /* RM0490 - 4.7.5 - FLASH_CR - PNB bits 9:3 - 7 bits */ #define FLASH_SECR_SEC_SIZE_POS (0U) #define FLASH_SECR_SEC_SIZE_MASK (0xFF) diff --git a/hal/stm32g0.c b/hal/stm32g0.c index d26436cc63..d4e7b31668 100644 --- a/hal/stm32g0.c +++ b/hal/stm32g0.c @@ -87,8 +87,8 @@ #define FLASH_CR_PG (1 << 0) /* RM0444 - 3.7.5 - FLASH_CR */ #define FLASH_CR_SEC_PROT (1 << 28) /* RM0444 - 3.7.5 - FLASH_CR */ -#define FLASH_CR_PNB_SHIFT 3 /* RM0444 - 3.7.5 - FLASH_CR - PNB bits 8:3 */ -#define FLASH_CR_PNB_MASK 0x3f /* RM0444 - 3.7.5 - FLASH_CR - PNB bits 8:3 - 6 bits */ +#define FLASH_CR_PNB_SHIFT 3 /* RM0444 - 3.7.5 - FLASH_CR - PNB bits 9:3 */ +#define FLASH_CR_PNB_MASK 0x7f /* RM0444 - 3.7.5 - FLASH_CR - PNB bits 9:3 - 7 bits */ #define FLASH_SECR_SEC_SIZE_POS (0U) #define FLASH_SECR_SEC_SIZE_MASK (0xFF) diff --git a/hal/stm32h5.h b/hal/stm32h5.h index 88dbf11fd0..e33a91894e 100644 --- a/hal/stm32h5.h +++ b/hal/stm32h5.h @@ -302,6 +302,10 @@ #define FLASH_CR_BER (1 << 3) #define FLASH_CR_FW (1 << 4) #define FLASH_CR_STRT (1 << 5) +/* Page number selection: + * Up to 31 pages: H523/33xx + * Up to 127 pages: All others + */ #define FLASH_CR_PNB_SHIFT 6 #define FLASH_CR_PNB_MASK 0x7F #define FLASH_CR_MER (1 << 15) diff --git a/hal/stm32l5.h b/hal/stm32l5.h index b080d89b12..0981ef29ab 100644 --- a/hal/stm32l5.h +++ b/hal/stm32l5.h @@ -203,7 +203,7 @@ #define FLASH_CR_PER (1 << 1) #define FLASH_CR_MER1 (1 << 2) #define FLASH_CR_PNB_SHIFT 3 -#define FLASH_CR_PNB_MASK 0x7F +#define FLASH_CR_PNB_MASK 0x7F /* up to 127 pages */ #define FLASH_CR_BKER (1 << 11) #define FLASH_CR_MER2 (1 << 15) #define FLASH_CR_STRT (1 << 16) diff --git a/hal/stm32u5.h b/hal/stm32u5.h index 8578938bf1..b26975dd25 100644 --- a/hal/stm32u5.h +++ b/hal/stm32u5.h @@ -209,8 +209,13 @@ #define FLASH_CR_PG (1 << 0) #define FLASH_CR_PER (1 << 1) #define FLASH_CR_MER1 (1 << 2) +/* Page number selection: + * Up to 31 pages: U535/U545 + * Up to 127 pages: U575/U585 + * Up to 255 pages: U59x/5Ax/5Fx/5Gx + */ #define FLASH_CR_PNB_SHIFT 3 -#define FLASH_CR_PNB_MASK 0x7F +#define FLASH_CR_PNB_MASK 0xFF /* support up to 255 pages */ #define FLASH_CR_BKER (1 << 11) #define FLASH_CR_MER2 (1 << 15) #define FLASH_CR_STRT (1 << 16) diff --git a/include/printf.h b/include/printf.h index 860774d355..cf3d98f561 100644 --- a/include/printf.h +++ b/include/printf.h @@ -66,6 +66,9 @@ # elif defined(WOLFBOOT_LOG_PRINTF) /* allow output to stdout */ # define wolfBoot_printf(_f_, ...) printf(_f_, ##__VA_ARGS__) +# elif defined(_MSC_VER) +# include +# define wolfBoot_printf(...) do { fprintf(stderr, __VA_ARGS__); } while (0) # else /* use stderr by default */ # define wolfBoot_printf(_f_, ...) fprintf(stderr, _f_, ##__VA_ARGS__) diff --git a/src/image.c b/src/image.c index 16312dcc8a..2f31b80893 100644 --- a/src/image.c +++ b/src/image.c @@ -30,6 +30,11 @@ #include #endif #include /* for wolfCrypt hash/sign routines */ +#ifdef WOLFBOOT_KEYTOOLS + /* this code needs to use the Use ./include/user_settings.h, not keytools */ + #error "The wrong user_settings.h has been included." +#endif + #include #include @@ -941,7 +946,7 @@ static int header_sha256(wc_Sha256 *sha256_ctx, struct wolfBoot_image *img) while (p < end_sha) { blksz = WOLFBOOT_SHA_BLOCK_SIZE; if (end_sha - p < blksz) - blksz = end_sha - p; + blksz = (int)(end_sha - p); wc_Sha256Update(sha256_ctx, p, blksz); p += blksz; } @@ -988,20 +993,28 @@ static int image_sha256(struct wolfBoot_image *img, uint8_t *hash) * @param key_slot The key slot ID to calculate the hash for. * @param hash A pointer to store the resulting SHA256 hash. */ -static void key_sha256(uint8_t key_slot, uint8_t *hash) +static int key_sha256(uint8_t key_slot, uint8_t *hash) { uint8_t *pubkey = keystore_get_buffer(key_slot); int pubkey_sz = keystore_get_size(key_slot); wc_Sha256 sha256_ctx; + int ret = 0; + + if (!pubkey || (pubkey_sz < 0)) { + return -1; + } memset(hash, 0, SHA256_DIGEST_SIZE); - if (!pubkey || (pubkey_sz < 0)) - return; - wc_InitSha256(&sha256_ctx); - wc_Sha256Update(&sha256_ctx, pubkey, (word32)pubkey_sz); - wc_Sha256Final(&sha256_ctx, hash); + ret = wc_InitSha256(&sha256_ctx); + if (ret == 0) { + ret = wc_Sha256Update(&sha256_ctx, pubkey, (word32)pubkey_sz); + } + if (ret == 0) { + ret = wc_Sha256Final(&sha256_ctx, hash); + } wc_Sha256Free(&sha256_ctx); + return ret; } #endif /* WOLFBOOT_NO_SIGN */ #endif /* SHA2-256 */ @@ -2200,18 +2213,36 @@ uint8_t* wolfBoot_peek_image(struct wolfBoot_image *img, uint32_t offset, * * @param hint The SHA hash of the public key to search for. * @return The key slot ID if found, -1 if the key was not found. + * Other negative values if the key_hash function failed. */ + int keyslot_id_by_sha(const uint8_t *hint) { int id; + int ret = 0; /* TODO default to failure until proven otherwise */ + int ct = 0; + if (hint == NULL) { + return -1; + } for (id = 0; id < keystore_num_pubkeys(); id++) { + ct++; + + /* TODO: return values for key_hash */ key_hash(id, digest); - if (memcmp(digest, hint, WOLFBOOT_SHA_DIGEST_SIZE) == 0) { + if ((ret == 0) && memcmp(digest, hint, WOLFBOOT_SHA_DIGEST_SIZE) == 0) { + wolfBoot_printf("Found matching digest in slot %d\n", id); return id; } } - return -1; + + if (ret == 0) { + /* Calls to key_hash were successful, but we did not find one. Fail: */ + wolfBoot_printf("No matching key hash found. Looked at %d slot(s)", ct); + ret = -1; + } + /* Reminder: zero based slot array. */ + return ret; } #endif /* !WOLFBOOT_NO_SIGN && !WOLFBOOT_RENESAS_SCEPROTECT */ diff --git a/test-app/app_sim.c b/test-app/app_sim.c index 5565857d79..ae8ae7dbbd 100644 --- a/test-app/app_sim.c +++ b/test-app/app_sim.c @@ -30,6 +30,10 @@ #include "wolfboot/wolfboot.h" +#ifdef DUALBANK_SWAP +uint32_t hal_sim_get_dualbank_state(void); +#endif + #ifdef TARGET_sim /* Matches all keys: @@ -72,6 +76,12 @@ int do_cmd(const char *cmd) wolfBoot_success(); return 0; } +#ifdef DUALBANK_SWAP + if (strcmp(cmd, "get_swap_state") == 0) { + printf("%u\n", hal_sim_get_dualbank_state()); + return 0; + } +#endif if (strcmp(cmd, "update_trigger") == 0) { #if EXT_ENCRYPTED wolfBoot_set_encrypt_key((uint8_t *)enc_key,(uint8_t *)(enc_key + 32)); diff --git a/tools/keytools/keygen.c b/tools/keytools/keygen.c index 615b0edbca..19a7d1d50b 100644 --- a/tools/keytools/keygen.c +++ b/tools/keytools/keygen.c @@ -503,10 +503,10 @@ void keystore_add(uint32_t ktype, uint8_t *key, uint32_t sz, const char *keyfile } fprintf(fpub, Pubkey_footer); fprintf(fpub, Slot_footer); - printf("Associated key file: %s\n", keyfile); + printf("Associated key file: %s\n", keyfile); printf("Partition ids mask: %08x\n", id_mask); - printf("Key type : %s\n", KName[ktype]); - printf("Public key slot: %u\n", id_slot); + printf("Key type: %s\n", KName[ktype]); + printf("Public key slot: %u\n", id_slot); if (noLocalKeys) { printf("WARNING: --nolocalkeys flag used, keystore.c public key is zeroed\n"); } @@ -1089,7 +1089,7 @@ static void keygen_ml_dsa(const char *priv_fname, uint32_t id_mask) if (exportPubKey) { if (saveAsDer) { uint8_t* pubDer; - size_t pubDerSz; + word32 pubDerSz; int pubOutLen; const int WITH_ALG_SPKI = 1; @@ -1349,10 +1349,11 @@ int main(int argc, char** argv) uint32_t n_pubkeys = 0; uint32_t part_id_mask = 0xFFFFFFFF; /* Default: key verify all */ + printf("wolfBoot KeyGen\n"); #ifdef DEBUG_SIGNTOOL wolfSSL_Debugging_ON(); #endif - printf("Keystore size: %lu\n", (unsigned long)sizeof(struct keystore_slot)); + printf("Keystore size: %lu\n", (unsigned long)sizeof(struct keystore_slot)); /* Check arguments and print usage */ if (argc < 2) @@ -1436,6 +1437,7 @@ int main(int argc, char** argv) i++; sprintf(pubkeyfile,"%s%s", argv[i], "/keystore.c"); sprintf(pubkeyimg, "%s%s", argv[i], "/keystore.der"); + printf("Saving keystore file: %s\n", pubkeyfile); i++; continue; } @@ -1453,20 +1455,23 @@ int main(int argc, char** argv) usage(argv[0]); } } - printf("Keytype: %s\n", KName[keytype]); - if (keytype == 0) + printf("Selected Keytype: %s\n", KName[keytype]); + if (keytype == 0) { + fprintf(stderr, "No keytype, exiting..."); exit(0); + } fpub = fopen(pubkeyfile, "rb"); if (!force && (fpub != NULL)) { char reply[40]; int replySz; - printf("** Warning: keystore already exists! Are you sure you want to generate a new key and overwrite the existing key? [Type 'Yes']: "); + printf("** Warning: keystore file already exists! %s\n", pubkeyfile); + printf("Are you sure you want to generate a new key and overwrite the existing key ? [Type 'Yes'] : "); fflush(stdout); replySz = scanf("%s", reply); printf("Reply is [%s]\n", reply); fclose(fpub); if (replySz < 0 || strcmp(reply, "Yes") != 0) { - printf("Operation aborted by user."); + printf("Operation aborted by user.\n"); exit(5); } else { unlink(pubkeyfile); diff --git a/tools/keytools/sign.c b/tools/keytools/sign.c index 712b1c8bac..d4f6e6ebac 100644 --- a/tools/keytools/sign.c +++ b/tools/keytools/sign.c @@ -58,7 +58,7 @@ #include #define HAVE_MMAP 0 #define ftruncate(fd, len) _chsize(fd, len) -static inline int fp_truncate(FILE *f, size_t len) +static inline int fp_truncate(FILE *f, long len) { int fd; if (f == NULL) @@ -953,7 +953,7 @@ static int sign_digest(int sign, int hash_algo, { int ret; WC_RNG rng; - printf("Sign: %02x\n", sign >> 8); + printf("Sign: 0x%02x\n", (unsigned)sign >> 8); (void)secondary; if ((ret = wc_InitRng(&rng)) != 0) { @@ -2401,57 +2401,59 @@ static void set_signature_sizes(int secondary) printf("Manifest header size: %u\n", CMD.header_sz); } -int main(int argc, char** argv) -{ - int ret = 0; - int i; +static int process_args(int argc, char** argv) { + uint8_t buf[PATH_MAX - 32]; /* leave room to avoid "directive output may be truncated" */ + static const int REQUIRED_PARAM_CT = 2; /* image name, key name */ + static const int OPTIONAL_PARAM_CT = 2; /* version = 1, output = "[image name]_v1_signed.bin" */ char* tmpstr; const char* sign_str = "AUTO"; const char* hash_str = "SHA256"; const char* secondary_sign_str = "NONE"; - uint8_t buf[PATH_MAX-32]; /* leave room to avoid "directive output may be truncated" */ - uint8_t *pubkey = NULL; - uint32_t pubkey_sz = 0; - uint8_t *kbuf=NULL, *key_buffer, *key_buffer2; - uint32_t key_buffer_sz, key_buffer_sz2; - #ifdef DEBUG_SIGNTOOL - wolfSSL_Debugging_ON(); + int param_ct = 0; /* params are all non-setting values (image name, key, version, output) */ #endif + int ret = 0; + int i; - printf("wolfBoot KeyTools (Compiled C version)\n"); - printf("wolfBoot version %X\n", WOLFBOOT_VERSION); - - /* Check arguments and print usage */ - if (argc < 4 || argc > 14) { - printf("Usage: %s [options] image key version\n", argv[0]); + /* Usage requires at least 2 params: + * sign [OPTIONS] IMAGE.BIN KEY.DER */ + if (argc < 3 || argc > 14) { + printf("Usage: %s [options] image key [version] [output name]\n", argv[0]); printf("For full usage manual, see 'docs/Signing.md'\n"); exit(1); } - /* Set initial manifest header size to a minimum default value */ - CMD.header_sz = 256; - /* Parse Arguments */ - for (i=1; i= (argc - (REQUIRED_PARAM_CT + OPTIONAL_PARAM_CT))) && (i <= (argc - REQUIRED_PARAM_CT))) + { + /* Looks like we have good parameters */ +#ifdef DEBUG_SIGNTOOL + if (param_ct == 0) { + param_ct = argc - i; + printf("Detected %d positional arguments.\n", param_ct); + printf("Using:\n"); + } + else { + if ((i == argc - param_ct)) { + /* param #1 is essential */ + printf(" Image: %s\n", argv[i + 0]); + } + if ((i == argc - param_ct + 1)) { + /* param #2 is essential */ + printf(" Key: %s\n", argv[i + 1]); + } + if ((i == argc - param_ct + 2)) { + /* param #3 is optional, version number */ + printf(" Version: %s\n", argv[i + 2]); + } + if ((i == argc - param_ct + 3)) { + /* param #4 is optional, output name*/ + printf(" Output: %s\n", argv[i + 3]); + } + } +#endif + } + else { + printf("Error: expected exactly 2, 3 or 4 positional arguments after options, got %d.\n", argc - i); + printf("Usage: %s [OPTIONS] IMAGE.BIN KEY.DER [VERSION] [output]\n", argv[0]); + + exit(1); + } i--; break; } @@ -2774,39 +2829,41 @@ int main(int argc, char** argv) if (CMD.sign != NO_SIGN) { if (CMD.hybrid) { printf("Parsing arguments in hybrid mode\n"); - CMD.image_file = argv[i+1]; - CMD.key_file = argv[i+2]; - CMD.secondary_key_file = argv[i+3]; - CMD.fw_version = argv[i+4]; + CMD.image_file = argv[i + 1]; + CMD.key_file = argv[i + 2]; + CMD.secondary_key_file = argv[i + 3]; + CMD.fw_version = argv[i + 4]; if (CMD.manual_sign) { - CMD.signature_file = argv[i+5]; + CMD.signature_file = argv[i + 5]; } printf("Secondary private key: %s\n", CMD.secondary_key_file); printf("Secondary cipher: %s\n", secondary_sign_str); printf("Version: %s\n", CMD.fw_version); - } else { - CMD.image_file = argv[i+1]; - CMD.key_file = argv[i+2]; - CMD.fw_version = argv[i+3]; + } + else { + CMD.image_file = argv[i + 1]; + CMD.key_file = argv[i + 2]; + CMD.fw_version = argv[i + 3]; if (CMD.manual_sign) { - CMD.signature_file = argv[i+4]; + CMD.signature_file = argv[i + 4]; } } - } else { - CMD.image_file = argv[i+1]; + } + else { + CMD.image_file = argv[i + 1]; CMD.key_file = NULL; - CMD.fw_version = argv[i+2]; + CMD.fw_version = argv[i + 2]; } memset(buf, 0, sizeof(buf)); - strncpy((char*)buf, CMD.image_file, sizeof(buf)-1); + strncpy((char*)buf, CMD.image_file, sizeof(buf) - 1); tmpstr = strrchr((char*)buf, '.'); if (tmpstr) { *tmpstr = '\0'; /* null terminate at last "." */ } snprintf(CMD.output_image_file, sizeof(CMD.output_image_file) - 1, - "%s_v%s_%s.bin", (char*)buf, CMD.fw_version, - CMD.sha_only ? "digest" : "signed"); + "%s_v%s_%s.bin", (char*)buf, CMD.fw_version, + CMD.sha_only ? "digest" : "signed"); snprintf(CMD.output_encrypted_image_file, sizeof(CMD.output_encrypted_image_file), @@ -2830,9 +2887,9 @@ int main(int argc, char** argv) } printf("Input image: %s\n", CMD.image_file); printf("Selected cipher: %s\n", sign_str); - printf("Selected hash : %s\n", hash_str); + printf("Selected hash: %s\n", hash_str); if (CMD.sign != NO_SIGN) { - printf("Private key: %s\n", CMD.key_file); + printf("Private key: %s\n", CMD.key_file); } if (CMD.hybrid) { printf("Secondary cipher: %s\n", secondary_sign_str); @@ -2853,7 +2910,7 @@ int main(int argc, char** argv) if (CMD.encrypt) { printf("Encrypted output: %s\n", CMD.output_encrypted_image_file); } - printf("Target partition id : %hu ", CMD.partition_id); + printf("Target partition id: %hu ", CMD.partition_id); if (CMD.partition_id == HDR_IMG_TYPE_WOLFBOOT) printf("(bootloader)"); printf("\n"); @@ -2885,11 +2942,65 @@ int main(int argc, char** argv) set_signature_sizes(1); } - if (((CMD.sign != NO_SIGN) && (CMD.signature_sz == 0)) || - CMD.header_sz == 0) { + if (CMD.header_sz == 0) { + printf("Header size cannot be zero"); + ret = 2; + } +#ifdef DEBUG_SIGNTOOL + else { + printf("Command header size: %d\n", CMD.header_sz); + } +#endif + + if (CMD.sign == HDR_IMG_TYPE_AUTH_NONE) { + printf("Warning: image signing auth set to NONE"); + } + + if (CMD.sign == NO_SIGN) { + printf("Warning: not signing"); + } + + if ((CMD.sign != NO_SIGN) && (CMD.signature_sz == 0)) { + printf("ERROR: When signing (CMD.sign = %d), signature size cannot be zero.\n", CMD.sign); + ret = 2; + } +#ifdef DEBUG_SIGNTOOL + else { + printf("Signature size: %d\n", CMD.signature_sz); + } +#endif + + if (ret != 0) { printf("Invalid hash or signature type! %d, %d, %d\n", CMD.sign, - CMD.signature_sz, CMD.header_sz); - exit(2); + CMD.signature_sz, CMD.header_sz); + exit(ret); + } + + return ret; +} + +int main(int argc, char** argv) +{ + int ret = 0; + uint8_t *pubkey = NULL; + uint32_t pubkey_sz = 0; + uint8_t *kbuf=NULL, *key_buffer, *key_buffer2; + uint32_t key_buffer_sz, key_buffer_sz2; + + /* Set initial manifest header size to a minimum default value */ + CMD.header_sz = 256; + +#ifdef DEBUG_SIGNTOOL + wolfSSL_Debugging_ON(); +#endif + + printf("wolfBoot KeyTools (Compiled C version)\n"); + printf("wolfBoot version %X\n", WOLFBOOT_VERSION); + + /* Check arguments and print usage */ + ret = process_args(argc, argv); + if (ret != 0) { + exit(ret); } if (CMD.sign == NO_SIGN) { @@ -2899,6 +3010,7 @@ int main(int argc, char** argv) } else { kbuf = load_key(&key_buffer, &key_buffer_sz, &pubkey, &pubkey_sz, 0); if (!kbuf) { + printf("Failed to load key"); exit(1); } } /* CMD.sign != NO_SIGN */ @@ -2915,28 +3027,39 @@ int main(int argc, char** argv) DEBUG_PRINT("Signature size: %u\n", CMD.signature_sz); DEBUG_PRINT("Secondary signature size: %u\n", CMD.secondary_signature_sz); DEBUG_PRINT("Header size: %u\n", CMD.header_sz); - if (kbuf2) + if (kbuf2) { free(kbuf2); - if (pubkey2) + } + if (pubkey2) { free(pubkey2); - } else { + } + } + else { make_header(pubkey, pubkey_sz, CMD.image_file, CMD.output_image_file); } if (CMD.delta) { - if (CMD.encrypt) + if (CMD.encrypt) { ret = base_diff(CMD.delta_base_file, pubkey, pubkey_sz, 64); - else + } + else { ret = base_diff(CMD.delta_base_file, pubkey, pubkey_sz, 16); + } + + if (ret != 0) { + printf("Warning: base_diff returned %d\n", ret); + } } /* Add pubkey cleanup */ - if (pubkey) + if (pubkey) { free(pubkey); - - if (kbuf) + } + if (kbuf) { free(kbuf); + } + if (CMD.sign == SIGN_ED25519) { wc_ed25519_free(&key.ed); } diff --git a/tools/scripts/sim-dualbank-swap-update.sh b/tools/scripts/sim-dualbank-swap-update.sh new file mode 100755 index 0000000000..eba1ac23f6 --- /dev/null +++ b/tools/scripts/sim-dualbank-swap-update.sh @@ -0,0 +1,42 @@ +#!/bin/bash +set -euo pipefail + +if [ ! -f ".config" ]; then + echo "Missing .config. Run make config first." >&2 + exit 1 +fi + +if ! grep -Eq '^(DUALBANK_SWAP(\?|)=1)' .config; then + echo "DUALBANK_SWAP=1 is required for this simulation." >&2 + exit 1 +fi + +if [ ! -x "./wolfboot.elf" ]; then + echo "wolfboot.elf not found. Build the simulator first." >&2 + exit 1 +fi + +rm -f sim_registers.dd + +mapfile -t lines < <(./wolfboot.elf get_swap_state get_version 2>/dev/null) +if [ "${#lines[@]}" -ne 2 ] || [ "${lines[0]}" != "1" ] || [ "${lines[1]}" != "2" ]; then + echo "Unexpected output on first boot (got: ${lines[*]-})" >&2 + exit 1 +fi +echo "dualbank: first boot reports swap=${lines[0]} active_version=${lines[1]}" + +mapfile -t lines < <(./wolfboot.elf success get_swap_state get_version 2>/dev/null) +if [ "${#lines[@]}" -ne 2 ] || [ "${lines[0]}" != "1" ] || [ "${lines[1]}" != "2" ]; then + echo "Unexpected output while confirming update (got: ${lines[*]-})" >&2 + exit 1 +fi +echo "dualbank: after wolfBoot_success swap=${lines[0]} active_version=${lines[1]}" + +mapfile -t lines < <(./wolfboot.elf get_swap_state get_version 2>/dev/null) +if [ "${#lines[@]}" -ne 2 ] || [ "${lines[0]}" != "1" ] || [ "${lines[1]}" != "2" ]; then + echo "Unexpected output after confirmation (got: ${lines[*]-})" >&2 + exit 1 +fi +echo "dualbank: persistent swap state confirmed swap=${lines[0]} active_version=${lines[1]}" + +echo "Dualbank swap simulation successful." diff --git a/tools/test.mk b/tools/test.mk index ac5bb95026..4ada0a2969 100644 --- a/tools/test.mk +++ b/tools/test.mk @@ -963,37 +963,37 @@ test-all: clean test-size-all: make test-size SIGN=NONE LIMIT=4896 NO_ARM_ASM=1 make keysclean - make test-size SIGN=ED25519 LIMIT=11548 NO_ARM_ASM=1 + make test-size SIGN=ED25519 LIMIT=11556 NO_ARM_ASM=1 make keysclean make test-size SIGN=ECC256 LIMIT=18656 NO_ARM_ASM=1 make clean - make test-size SIGN=ECC256 NO_ASM=1 LIMIT=13700 NO_ARM_ASM=1 + make test-size SIGN=ECC256 NO_ASM=1 LIMIT=13712 NO_ARM_ASM=1 make keysclean - make test-size SIGN=RSA2048 LIMIT=11396 NO_ARM_ASM=1 + make test-size SIGN=RSA2048 LIMIT=11408 NO_ARM_ASM=1 make clean - make test-size SIGN=RSA2048 NO_ASM=1 LIMIT=11976 NO_ARM_ASM=1 + make test-size SIGN=RSA2048 NO_ASM=1 LIMIT=11988 NO_ARM_ASM=1 make keysclean - make test-size SIGN=RSA4096 LIMIT=11680 NO_ARM_ASM=1 + make test-size SIGN=RSA4096 LIMIT=11692 NO_ARM_ASM=1 make clean - make test-size SIGN=RSA4096 NO_ASM=1 LIMIT=12260 NO_ARM_ASM=1 + make test-size SIGN=RSA4096 NO_ASM=1 LIMIT=12272 NO_ARM_ASM=1 make keysclean make test-size SIGN=ECC384 LIMIT=18616 NO_ARM_ASM=1 make clean - make test-size SIGN=ECC384 NO_ASM=1 LIMIT=15076 NO_ARM_ASM=1 + make test-size SIGN=ECC384 NO_ASM=1 LIMIT=15088 NO_ARM_ASM=1 make keysclean - make test-size SIGN=ED448 LIMIT=13616 NO_ARM_ASM=1 + make test-size SIGN=ED448 LIMIT=13624 NO_ARM_ASM=1 make keysclean - make test-size SIGN=RSA3072 LIMIT=11536 NO_ARM_ASM=1 + make test-size SIGN=RSA3072 LIMIT=11548 NO_ARM_ASM=1 make clean - make test-size SIGN=RSA3072 NO_ASM=1 LIMIT=12080 NO_ARM_ASM=1 + make test-size SIGN=RSA3072 NO_ASM=1 LIMIT=12092 NO_ARM_ASM=1 make keysclean make test-size SIGN=LMS LMS_LEVELS=2 LMS_HEIGHT=5 LMS_WINTERNITZ=8 \ WOLFBOOT_SMALL_STACK=0 IMAGE_SIGNATURE_SIZE=2644 \ - IMAGE_HEADER_SIZE?=5288 LIMIT=7620 NO_ARM_ASM=1 + IMAGE_HEADER_SIZE?=5288 LIMIT=7632 NO_ARM_ASM=1 make keysclean make test-size SIGN=XMSS XMSS_PARAMS='XMSS-SHA2_10_256' \ IMAGE_SIGNATURE_SIZE=2500 IMAGE_HEADER_SIZE?=4096 \ - LIMIT=8408 NO_ARM_ASM=1 + LIMIT=8420 NO_ARM_ASM=1 make keysclean make clean make test-size SIGN=ML_DSA ML_DSA_LEVEL=2 LIMIT=19246 \ diff --git a/tools/unit-tests/unit-keystore.c b/tools/unit-tests/unit-keystore.c index bbf811c4dd..0ed048eb7c 100644 --- a/tools/unit-tests/unit-keystore.c +++ b/tools/unit-tests/unit-keystore.c @@ -34,6 +34,10 @@ #if defined(__APPLE__) && defined(__MACH__) #define KEYSTORE_SECTION __attribute__((section ("__KEYSTORE,__keystore"))) +#elif defined(_MSC_VER) +/* Create a RW data section named .keystore */ +#pragma section(".keystore", read, write) +#define KEYSTORE_SECTION __declspec(allocate(".keystore")) #else #define KEYSTORE_SECTION __attribute__((section (".keystore"))) #endif @@ -48,7 +52,7 @@ const KEYSTORE_SECTION struct keystore_slot PubKeys[NUM_PUBKEYS] = { .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,