Skip to content
5 changes: 5 additions & 0 deletions .github/workflows/test-hooks-simulator.yml
Original file line number Diff line number Diff line change
Expand Up @@ -88,6 +88,11 @@ jobs:
WOLFBOOT_HOOK_BOOT=1 \
WOLFBOOT_HOOK_PANIC=1

- name: Run dualbank rollback denial simulation
if: matrix.mechanism == 'dualbank'
run: |
tools/scripts/sim-dualbank-rollback-denied.sh

- name: Clear hook log
run: |
rm -f /tmp/wolfboot_hooks.log
Expand Down
4 changes: 4 additions & 0 deletions .github/workflows/test-sunnyday-simulator.yml
Original file line number Diff line number Diff line change
Expand Up @@ -54,6 +54,10 @@ jobs:
cp config/examples/sim-dualbank.config .config
make test-sim-internal-flash-with-update

- name: Run dualbank rollback denial simulation
run: |
tools/scripts/sim-dualbank-rollback-denied.sh
Comment thread
danielinux marked this conversation as resolved.

- name: Run dualbank swap simulation
run: |
tools/scripts/sim-dualbank-swap-update.sh
Expand Down
10 changes: 10 additions & 0 deletions src/libwolfboot.c
Original file line number Diff line number Diff line change
Expand Up @@ -1395,6 +1395,16 @@ int wolfBoot_dualboot_candidate(void)
(wolfBoot_get_partition_state(candidate, &p_state) == 0) &&
(p_state == IMG_STATE_TESTING))
{
#ifndef ALLOW_DOWNGRADE
uint32_t candidate_v = (candidate == PART_BOOT) ? boot_v : update_v;
uint32_t fallback_v = (candidate == PART_BOOT) ? update_v : boot_v;

if (fallback_v < candidate_v) {
wolfBoot_printf("Rollback to lower version not allowed\n");
Comment thread
danielinux marked this conversation as resolved.
wolfBoot_erase_partition(candidate);
return -1;
Comment thread
danielinux marked this conversation as resolved.
Outdated
}
Comment thread
danielinux marked this conversation as resolved.
#endif
wolfBoot_erase_partition(candidate);
candidate ^= 1; /* switch to other partition if available */
}
Expand Down
11 changes: 11 additions & 0 deletions src/update_disk.c
Original file line number Diff line number Diff line change
Expand Up @@ -259,6 +259,7 @@ void RAMFUNCTION wolfBoot_start(void)
uint32_t *load_address;
int failures = 0;
uint32_t load_off;
uint32_t max_ver;
const uint8_t *hdr_ptr = NULL;
#ifdef MMU
uint8_t *dts_addr = NULL;
Expand Down Expand Up @@ -346,6 +347,7 @@ void RAMFUNCTION wolfBoot_start(void)
}

wolfBoot_printf("Versions, A:%u B:%u\r\n", pA_ver, pB_ver);
max_ver = (pB_ver > pA_ver) ? (uint32_t)pB_ver : (uint32_t)pA_ver;
Comment thread
danielinux marked this conversation as resolved.
Outdated

/* Choose partition with higher version */
selected = (pB_ver > pA_ver) ? 1: 0;
Expand All @@ -368,6 +370,15 @@ void RAMFUNCTION wolfBoot_start(void)
cur_part = BOOT_PART_B;
else
cur_part = BOOT_PART_A;
#ifndef ALLOW_DOWNGRADE
{
uint32_t cur_ver = selected ? (uint32_t)pB_ver : (uint32_t)pA_ver;
if ((max_ver > 0U) && (cur_ver < max_ver)) {
wolfBoot_printf("Rollback to lower version not allowed\r\n");
break;
Comment thread
danielinux marked this conversation as resolved.
Outdated
}
}
#endif

part_name[2] = 'A' + selected;

Comment thread
danielinux marked this conversation as resolved.
Expand Down
14 changes: 14 additions & 0 deletions src/update_flash_hwswap.c
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,7 @@
#include "hooks.h"
#include "spi_flash.h"
#include "wolfboot/wolfboot.h"
#include "printf.h"
#ifdef SECURE_PKCS11
int WP11_Library_Init(void);
#endif
Expand All @@ -45,12 +46,25 @@ void RAMFUNCTION wolfBoot_start(void)
int active;
struct wolfBoot_image fw_image;
uint8_t p_state;
#ifndef ALLOW_DOWNGRADE
uint32_t boot_v = wolfBoot_current_firmware_version();
uint32_t update_v = wolfBoot_update_firmware_version();
uint32_t max_v = (boot_v > update_v) ? boot_v : update_v;
Comment thread
danielinux marked this conversation as resolved.
Outdated
#endif
Comment thread
danielinux marked this conversation as resolved.
active = wolfBoot_dualboot_candidate();

if (active < 0) /* panic if no images available */
boot_panic();

for (;;) {
#ifndef ALLOW_DOWNGRADE
uint32_t active_v = (active == PART_UPDATE) ? update_v : boot_v;
if ((max_v > 0U) && (active_v < max_v)) {
wolfBoot_printf("Rollback to lower version not allowed\n");
boot_panic();
Comment thread
danielinux marked this conversation as resolved.
Comment thread
danielinux marked this conversation as resolved.
continue;
Comment thread
danielinux marked this conversation as resolved.
Outdated
}
#endif
if ((wolfBoot_open_image(&fw_image, active) < 0)
#ifndef WOLFBOOT_SKIP_BOOT_VERIFY
|| (wolfBoot_verify_integrity(&fw_image) < 0)
Expand Down
15 changes: 15 additions & 0 deletions src/update_ram.c
Original file line number Diff line number Diff line change
Expand Up @@ -138,6 +138,11 @@ void RAMFUNCTION wolfBoot_start(void)
uint8_t *dts_addr = NULL;
uint32_t dts_size = 0;
#endif
#ifndef ALLOW_DOWNGRADE
uint32_t boot_v = wolfBoot_current_firmware_version();
uint32_t update_v = wolfBoot_update_firmware_version();
uint32_t max_v = (boot_v > update_v) ? boot_v : update_v;
Comment thread
danielinux marked this conversation as resolved.
Outdated
Comment thread
danielinux marked this conversation as resolved.
Outdated
#endif
Comment thread
danielinux marked this conversation as resolved.
Outdated

memset(&os_image, 0, sizeof(struct wolfBoot_image));

Expand All @@ -162,6 +167,16 @@ void RAMFUNCTION wolfBoot_start(void)
wolfBoot_panic();
break;
}
#ifndef ALLOW_DOWNGRADE
{
uint32_t active_v = (active == PART_UPDATE) ? update_v : boot_v;
if ((max_v > 0U) && (active_v < max_v)) {
wolfBoot_printf("Rollback to lower version not allowed\n");
wolfBoot_panic();
break;
}
Comment thread
danielinux marked this conversation as resolved.
}
#endif
Comment thread
danielinux marked this conversation as resolved.
Outdated

#if defined(WOLFBOOT_DUALBOOT) && defined(WOLFBOOT_FIXED_PARTITIONS)
wolfBoot_printf("Trying %s partition at %p\n",
Expand Down
1 change: 1 addition & 0 deletions tools/keytools/keygen.c
Original file line number Diff line number Diff line change
Expand Up @@ -948,6 +948,7 @@ static void keygen_lms(const char *priv_fname, uint32_t id_mask)
keystore_add(AUTH_KEY_LMS, lms_pub, KEYSTORE_PUBKEY_SIZE_LMS, priv_fname, id_mask);

wc_LmsKey_Free(&key);
wc_ForceZero(&key, sizeof(key));
}

#include "../xmss/xmss_common.h"
Expand Down
65 changes: 65 additions & 0 deletions tools/scripts/sim-dualbank-rollback-denied.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,65 @@
#!/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

if [ ! -f "./internal_flash.dd" ]; then
echo "internal_flash.dd not found. Build test-sim-internal-flash-with-update first." >&2
exit 1
fi

backup_image="$(mktemp ./internal_flash.rollback.XXXXXX)"
cp ./internal_flash.dd "$backup_image"
trap 'cp "$backup_image" ./internal_flash.dd; rm -f "$backup_image" sim_registers.dd' EXIT

rm -f sim_registers.dd

update_addr_hex="$(grep '^WOLFBOOT_PARTITION_UPDATE_ADDRESS=' .config | cut -d= -f2)"
if [ -z "${update_addr_hex}" ]; then
echo "WOLFBOOT_PARTITION_UPDATE_ADDRESS is not set in .config." >&2
exit 1
fi

update_addr=$((update_addr_hex))

# Corrupt UPDATE payload bytes so version metadata remains intact but
# image verification fails and boot logic attempts fallback.
printf '\x00\x00\x00\x00\x00\x00\x00\x00' | \
dd of=./internal_flash.dd bs=1 seek="$((update_addr + 0x120))" conv=notrunc status=none

set +e
rollback_output="$(timeout 3s ./wolfboot.elf get_version 2>&1)"
rollback_rc=$?
set -e

if [ "$rollback_rc" -eq 0 ]; then
echo "Expected rollback denial, but boot continued normally." >&2
exit 1
fi

if [ "$rollback_rc" -ne 124 ] && [ "$rollback_rc" -ne 80 ]; then
echo "Unexpected exit code while checking rollback denial: $rollback_rc" >&2
echo "$rollback_output" >&2
exit 1
fi

if ! printf '%s\n' "$rollback_output" | grep -q "Rollback to lower version not allowed"; then
echo "Rollback denial message not found in output." >&2
echo "$rollback_output" >&2
exit 1
fi

echo "Dualbank rollback-to-older-version denial verified."
2 changes: 1 addition & 1 deletion tools/tpm/policy_create.c
Original file line number Diff line number Diff line change
Expand Up @@ -54,7 +54,7 @@ int writeBin(const char* filename, const uint8_t*buf, word32 bufSz)
XFILE fp = NULL;
size_t fileSz = 0;

fp = XFOPEN(filename, "wt");
fp = XFOPEN(filename, "wb");
if (fp != XBADFILE) {
fileSz = XFWRITE(buf, 1, bufSz, fp);
/* sanity check */
Expand Down
7 changes: 5 additions & 2 deletions tools/tpm/policy_sign.c
Original file line number Diff line number Diff line change
Expand Up @@ -159,7 +159,10 @@ static int PolicySign(int alg, const char* keyFile, byte* hash, word32 hashSz,
rc = BAD_FUNC_ARG;
}

XFREE(buf, NULL, DYNAMIC_TYPE_TMP_BUFFER);
if (buf != NULL) {
wc_ForceZero(buf, bufSz);
XFREE(buf, NULL, DYNAMIC_TYPE_TMP_BUFFER);
}
wc_FreeRng(&rng);

if (rc != 0) {
Expand Down Expand Up @@ -221,7 +224,7 @@ static int writeBin(const char* filename, const byte *buf, word32 bufSz)
FILE *fp = NULL;
size_t fileSz = 0;

fp = fopen(filename, "wt");
fp = fopen(filename, "wb");
if (fp != NULL) {
fileSz = fwrite(buf, 1, bufSz, fp);
/* sanity check */
Expand Down
18 changes: 18 additions & 0 deletions tools/unit-tests/unit-update-disk.c
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,7 @@ static int mock_disk_init_ret;
static int mock_disk_close_called;
static int mock_do_boot_called;
static const uint32_t *mock_boot_address;
static int mock_fail_payload_part;

ChaCha chacha;

Expand Down Expand Up @@ -72,6 +73,7 @@ static void reset_mocks(void)
mock_disk_close_called = 0;
mock_do_boot_called = 0;
mock_boot_address = NULL;
mock_fail_payload_part = -1;
wolfBoot_panicked = 0;
}

Expand Down Expand Up @@ -140,6 +142,8 @@ int disk_part_read(int drv, int part, uint64_t off, uint64_t sz, uint8_t *buf)

(void)drv;
image = (part == BOOT_PART_B) ? part_b_image : part_a_image;
if ((mock_fail_payload_part == part) && (off >= IMAGE_HEADER_SIZE))
return -1;
if ((off > max) || (sz > (max - off)))
return -1;
memcpy(buf, image + off, (size_t)sz);
Expand Down Expand Up @@ -288,6 +292,19 @@ START_TEST(test_get_decrypted_blob_version_rejects_truncated_version_tlv)
}
END_TEST

START_TEST(test_update_disk_rejects_rollback_after_higher_image_failure)
{
reset_mocks();
build_image(part_a_image, 7, 0xA1);
build_image(part_b_image, 5, 0xB2);
mock_fail_payload_part = BOOT_PART_A;

wolfBoot_start();

ck_assert_int_eq(wolfBoot_panicked, 1);
}
END_TEST

Suite *wolfboot_suite(void)
{
Suite *s = suite_create("wolfBoot");
Comment thread
danielinux marked this conversation as resolved.
Expand All @@ -297,6 +314,7 @@ Suite *wolfboot_suite(void)
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);
tcase_add_test(tc, test_update_disk_rejects_rollback_after_higher_image_failure);
suite_add_tcase(s, tc);

return s;
Expand Down
9 changes: 5 additions & 4 deletions tools/unit-tests/unit-update-ram-nofixed.c
Original file line number Diff line number Diff line change
Expand Up @@ -191,7 +191,7 @@ static int add_payload(uint8_t part, uint32_t version, uint32_t size)
return 0;
}

START_TEST(test_invalid_update_falls_back_to_boot_without_reselect_loop)
START_TEST(test_invalid_update_rollback_to_older_boot_is_denied)
{
uint8_t bad_digest[SHA256_DIGEST_SIZE];

Expand All @@ -208,8 +208,9 @@ START_TEST(test_invalid_update_falls_back_to_boot_without_reselect_loop)

wolfBoot_start();

ck_assert_int_eq(wolfBoot_staged_ok, 1);
ck_assert_ptr_eq(wolfBoot_stage_address, (const uint32_t *)WOLFBOOT_LOAD_ADDRESS);
ck_assert_int_eq(wolfBoot_staged_ok, 0);
Comment thread
danielinux marked this conversation as resolved.
Outdated
ck_assert_int_eq(wolfBoot_panicked, 1);
ck_assert_ptr_eq((void *)wolfBoot_stage_address, (void *)0xFFFFFFFF);
Comment thread
danielinux marked this conversation as resolved.
Outdated
cleanup_flash();
}
END_TEST
Expand All @@ -219,7 +220,7 @@ static Suite *wolfboot_suite(void)
Suite *s = suite_create("wolfboot-update-ram-nofixed");
TCase *tc = tcase_create("fallback");

tcase_add_test(tc, test_invalid_update_falls_back_to_boot_without_reselect_loop);
tcase_add_test(tc, test_invalid_update_rollback_to_older_boot_is_denied);
tcase_set_timeout(tc, 5);
suite_add_tcase(s, tc);

Expand Down
21 changes: 11 additions & 10 deletions tools/unit-tests/unit-update-ram.c
Original file line number Diff line number Diff line change
Expand Up @@ -378,8 +378,9 @@ START_TEST (test_invalid_update_type) {
ext_flash_lock();
wolfBoot_update_trigger();
wolfBoot_start();
ck_assert(wolfBoot_staged_ok);
ck_assert(get_version_ramloaded() == 1);
ck_assert(!wolfBoot_staged_ok);
ck_assert_int_eq(wolfBoot_panicked, 1);
Comment thread
danielinux marked this conversation as resolved.
ck_assert_int_eq(get_version_ramloaded(), 2);
cleanup_flash();
}

Expand All @@ -396,8 +397,8 @@ START_TEST (test_update_toolarge) {

wolfBoot_update_trigger();
wolfBoot_start();
ck_assert(wolfBoot_staged_ok);
ck_assert(get_version_ramloaded() == 1);
ck_assert(!wolfBoot_staged_ok);
ck_assert_int_eq(wolfBoot_panicked, 1);
cleanup_flash();
}

Expand All @@ -414,12 +415,12 @@ START_TEST (test_invalid_sha) {
ext_flash_lock();
wolfBoot_update_trigger();
wolfBoot_start();
ck_assert(wolfBoot_staged_ok);
ck_assert(get_version_ramloaded() == 1);
ck_assert(!wolfBoot_staged_ok);
ck_assert_int_eq(wolfBoot_panicked, 1);
cleanup_flash();
}

START_TEST (test_emergency_rollback) {
START_TEST (test_emergency_rollback_to_older_version_denied) {
uint8_t testing_flags[5] = { IMG_STATE_TESTING, 'B', 'O', 'O', 'T' };
reset_mock_stats();
prepare_flash();
Expand All @@ -432,8 +433,8 @@ START_TEST (test_emergency_rollback) {
ext_flash_lock();

wolfBoot_start();
ck_assert(wolfBoot_staged_ok);
ck_assert(get_version_ramloaded() == 1);
ck_assert(!wolfBoot_staged_ok);
ck_assert_int_eq(wolfBoot_panicked, 1);
cleanup_flash();
}

Expand Down Expand Up @@ -532,7 +533,7 @@ Suite *wolfboot_suite(void)
tcase_add_test(invalid_update_type, test_invalid_update_type);
tcase_add_test(update_toolarge, test_update_toolarge);
tcase_add_test(invalid_sha, test_invalid_sha);
tcase_add_test(emergency_rollback, test_emergency_rollback);
tcase_add_test(emergency_rollback, test_emergency_rollback_to_older_version_denied);
tcase_add_test(emergency_rollback_failure_due_to_bad_update, test_emergency_rollback_failure_due_to_bad_update);
tcase_add_test(empty_boot_partition_update, test_empty_boot_partition_update);
tcase_add_test(empty_boot_but_update_sha_corrupted_denied, test_empty_boot_but_update_sha_corrupted_denied);
Expand Down
Loading