Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
18 changes: 14 additions & 4 deletions core/fw_update.c
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@
#include "eos_fw_update.h"
#include "eos_hal.h"
#include "eos_fwsvc.h"
#include "eos_rollback.h"
#include <string.h>

int eos_fw_update_begin(eos_fw_update_ctx_t *ctx, eos_slot_t slot)
Expand Down Expand Up @@ -184,10 +185,19 @@ int eos_fw_update_finalize(eos_fw_update_ctx_t *ctx, eos_upgrade_mode_t mode)
}
}

/* Anti-rollback check */
extern int eos_image_check_rollback(uint32_t candidate_version);
int rb_rc = eos_image_check_rollback(ctx->header.image_version);
if (rb_rc != EOS_OK && rb_rc != EOS_ERR_NOT_SUPPORTED) {
/* Persistent floor: authenticated TLV security counter, not image_version.
* Finalize installs the image; it does not boot it, so the counter is
* verified here but not staged. */
uint32_t img_counter = 0;
int rb_rc = eos_rollback_read_image_counter(ctx->target_addr, &img_counter);
if (rb_rc != EOS_OK) {
ctx->state = EOS_FW_STATE_ERROR;
ctx->last_error = rb_rc;
return rb_rc;
}

rb_rc = eos_rollback_verify(img_counter);
if (rb_rc != EOS_OK) {
ctx->state = EOS_FW_STATE_ERROR;
ctx->last_error = rb_rc;
return rb_rc;
Expand Down
22 changes: 18 additions & 4 deletions stage1/jump_app.c
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@
#include "eos_hal.h"
#include "eos_bootctl.h"
#include "eos_image.h"
#include "eos_rollback.h"
#include "eos_mpu_boot.h"

/* Forward declarations from boot_log */
Expand Down Expand Up @@ -56,13 +57,26 @@ int eboot_jump_to_app(eos_bootctl_t *bctl, eos_slot_t slot)
return rc;
}

/* Anti-rollback check */
extern int eos_image_check_rollback(uint32_t candidate_version);
rc = eos_image_check_rollback(hdr.image_version);
if (rc != EOS_OK && rc != EOS_ERR_NOT_SUPPORTED) {
/* Persistent floor from the device monotonic counter. The value compared
* here is the authenticated EOS_TLV_MIN_SEC_VER, not image_version:
* firmware versions and OTP security counters are different scales, and
* comparing the former to the latter lets a high version / low TLV image
* walk past the floor. Same sequence as eos_secure_boot() step 5b. */
uint32_t img_counter = 0;
rc = eos_rollback_read_image_counter(addr, &img_counter);
if (rc != EOS_OK) {
eos_boot_log_append(EOS_LOG_IMAGE_INVALID, slot, rc);
return rc;
}

rc = eos_rollback_verify(img_counter);
if (rc != EOS_OK) {
eos_boot_log_append(EOS_LOG_IMAGE_INVALID, slot, rc);
return rc;
}

/* Raised only once the image is confirmed good; see eos_bootctl_confirm(). */
eos_rollback_stage(img_counter);

/* Increment boot attempts before jumping */
eos_bootctl_increment_attempts(bctl);
Expand Down
37 changes: 35 additions & 2 deletions tests/unit/test_tlv_auth.c
Original file line number Diff line number Diff line change
Expand Up @@ -290,20 +290,53 @@ TEST(test_oversized_tlv_len_is_rejected)
ASSERT(eos_rollback_read_image_counter(SLOT_A_ADDR, &counter) == EOS_ERR_INVALID);
}

/*
* Regression: the production boot and update paths used to feed
* hdr.image_version into eos_image_check_rollback(), which compares that
* number to the OTP monotonic floor. Firmware versions (0x00MMmmpp) and
* TLV security counters are different scales. An old image with
* image_version = 0x00010000 and authenticated MIN_SEC_VER = 3 therefore
* passed a floor of 9, defeating the anti-rollback gate.
*
* The hardware-floor decision is eos_rollback_verify(tlv_counter).
*/
extern int eos_image_check_rollback(uint32_t candidate_version);

TEST(test_hw_floor_uses_tlv_counter_not_image_version)
{
build_image(3, true, true);

eos_image_header_t hdr;
ASSERT(eos_image_parse_header(SLOT_A_ADDR, &hdr) == EOS_OK);
ASSERT(hdr.image_version == 0x00010000u);

uint32_t counter = 0;
ASSERT(eos_rollback_read_image_counter(SLOT_A_ADDR, &counter) == EOS_OK);
ASSERT(counter == 3);

sim_counter = 9;

/* The old production check: image_version against the HW floor. */
ASSERT(eos_image_check_rollback(hdr.image_version) == EOS_OK);

/* The authenticated counter is below the floor and must be rejected. */
ASSERT(eos_rollback_verify(counter) == EOS_ERR_ANTI_ROLLBACK);
}

int main(void)
{
printf("TLV authentication (anti-rollback counter)\n\n");

tests_run = 8;
run_test_authenticated_tlv_counter_is_read();
run_test_tampered_tlv_counter_is_rejected();
run_test_tamper_is_invisible_to_signature_and_integrity();
run_test_unbound_tlv_area_is_not_trusted();
run_test_image_without_tlv_area_still_reports_zero();
run_test_tlv_binding_fields_are_inside_the_signed_prefix();
run_test_oversized_tlv_len_is_rejected();
run_test_hw_floor_uses_tlv_counter_not_image_version();

tests_run = 7;
tests_run = 8;
printf("\n%d/%d passed\n", tests_passed, tests_run);
return tests_passed == tests_run ? 0 : 1;
}