Merge pull request #651 from Foundation-Devices/firmware-update-validation-hardening
What changed, and why it matters
This change tightens how Passport's bootloader validates a new firmware update. Previously, if the currently-running firmware failed validation, the bootloader would skip signature checks on the incoming update and jump straight to installing it. The patch moves the signature verification outside that 'only if current firmware is valid' branch, so the new firmware is always signature-checked before installation. It also moves a check about whether the current firmware was user-signed so it is computed earlier. In short: the bootloader now refuses to install unsigned updates even when the existing firmware looks broken.
Treat this as a security hardening fix for a latent bootloader bypass. Review whether any prior firmware version allowed unsigned updates when current firmware was invalid, and consider issuing a security note or advisory if an exploitable chain exists (e.g., bricking/glitched device accepting malicious update). Users should update to the patched bootloader/firmware.
Security signals we found
Missing signature verification on firmware update path when current firmware is invalid
Control-flow change that moves verify_signature() outside conditional branch
Bootloader trust-boundary issue: current-firmware state influenced validation of new firmware
Potential downgrade/rollback protection remains conditional on current firmware validity
Evidence from the diff
In ports/stm32/boards/Passport/bootloader/update.c, the function update_firmware() previously called verify_current_firmware(true) and, only on SEC_TRUE, performed calculate_spi_hash(), verify_signature(), and SE board-hash/timestamp checks. If the current firmware was invalid, control fell through to do_update() without verifying the incoming firmware’s signature. The patch restructures the flow: it computes is_spi_fw_user_signed before the current-firmware check, captures the result of verify_current_firmware(true), then unconditionally performs calculate_spi_hash() and verify_signature() regardless of current_firmware_result. The SE hash/timestamp downgrade/rollback checks remain gated on current_firmware_result == SEC_TRUE. This prevents installation of an improperly signed update when the running firmware is corrupt or unverified.
Changed components
ports/stm32/boards/Passport/bootloader/update.cupdate_firmware()verify_signature()verify_current_firmware()do_update()Inspect captured patch +30 / −26
### ports/stm32/boards/Passport/bootloader/update.c
@@ -338,6 +338,9 @@ void update_firmware(void) {
}
}
+ bool is_spi_fw_user_signed = spihdr.signature.pubkey1 == FW_USER_KEY;
+ secresult current_firmware_result;
+
// Handle the firmware hash update
get_current_board_hash(current_board_hash);
@@ -353,13 +356,12 @@ void update_firmware(void) {
#ifdef DEBUG_PRINT_UPDATE_HASH
printf("Verifying current firmware before update\r\n");
#endif
- if (verify_current_firmware(true) == SEC_TRUE) {
+ current_firmware_result = verify_current_firmware(true);
+ if (current_firmware_result == SEC_TRUE) {
#ifdef DEBUG_PRINT_UPDATE_HASH
printf(" Current firmware is valid, so doing more checks.\r\n");
#endif
- bool is_spi_fw_user_signed = spihdr.signature.pubkey1 == FW_USER_KEY;
-
#ifdef PRODUCTION_BUILD
uint32_t current_firmware_timestamp = se_get_firmware_timestamp(current_board_hash);
if (current_firmware_timestamp == 0) {
@@ -393,29 +395,36 @@ void update_firmware(void) {
}
}
#endif
+ }
+#ifdef DEBUG_PRINT_UPDATE_HASH
+ else {
+ printf(" Current firmware is INVALID - fall through to try the update.\r\n");
+ }
+#endif
- calculate_spi_hash(&spihdr, spi_fw_hash, sizeof(spi_fw_hash));
+ calculate_spi_hash(&spihdr, spi_fw_hash, sizeof(spi_fw_hash));
#ifdef DEBUG_PRINT_UPDATE_HASH
- printf(">>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>\r\n");
- bytes_to_hex_str((void*)&spihdr, sizeof(spihdr), str_buf, 64, "\r\n");
- printf("spihdr\r\n%s\r\n", str_buf);
- bytes_to_hex_str((void*)spi_fw_hash, sizeof(spi_fw_hash), str_buf, 64, "\r\n");
- printf("spi_fw_hash\r\n%s\r\n", str_buf);
- printf(">>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>\r\n");
+ printf(">>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>\r\n");
+ bytes_to_hex_str((void*)&spihdr, sizeof(spihdr), str_buf, 64, "\r\n");
+ printf("spihdr\r\n%s\r\n", str_buf);
+ bytes_to_hex_str((void*)spi_fw_hash, sizeof(spi_fw_hash), str_buf, 64, "\r\n");
+ printf("spi_fw_hash\r\n%s\r\n", str_buf);
+ printf(">>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>\r\n");
#endif
- // Verify the signature and bail if it fails
- if (verify_signature(&spihdr, spi_fw_hash, sizeof(spi_fw_hash)) == SEC_FALSE) {
- error4:
- if (ui_show_error("PASSPORT", "Update Error",
- "The firmware update is not properly signed and will not be installed.", &ICON_SHUTDOWN,
- &ICON_CHECKMARK, true) == KEY_RIGHT_SELECT) {
- goto out;
- } else {
- ui_ask_shutdown();
- goto error4;
- }
+ // Verify the incoming firmware signature before attempting to install it.
+ if (verify_signature(&spihdr, spi_fw_hash, sizeof(spi_fw_hash)) == SEC_FALSE) {
+ error4:
+ if (ui_show_error("PASSPORT", "Update Error",
+ "The firmware update is not properly signed and will not be installed.", &ICON_SHUTDOWN,
+ &ICON_CHECKMARK, true) == KEY_RIGHT_SELECT) {
+ goto out;
+ } else {
+ ui_ask_shutdown();
+ goto error4;
}
+ }
+ if (current_firmware_result == SEC_TRUE) {
/*
* Calculate a new board hash based on the SPI firmware and then
* reprogram the board hash in the SE. If the update fails it
@@ -456,11 +465,6 @@ void update_firmware(void) {
}
}
}
-#ifdef DEBUG_PRINT_UPDATE_HASH
- else {
- printf(" Current firmware is INVALID - fall through to try the update.\r\n");
- }
-#endif
rc = do_update(FW_HEADER_SIZE + spihdr.info.fwlength);
if (rc < 0) {Why this scored 61/100
Community notes
Notes can correct, qualify, or add evidence to the AI analysis. Every note shown here has been validated by a human moderator.
The AI analysis stands alone for now. Submit a note if you can add evidence or important context.