Secure Boot & Update
rustBoot supports the following boot and update schemes, depending on the underlying device-type.
mcu updates:to boot and update bare-metal mcu-firmware using a simplepartitioning schemelinux-system updates:to boot and update a linux system.
mcu updates:
rustBoot implements a small state machine leveraging rust's type-system to enforce compile-time state transition checks. Additionally, it uses the sealed-trait pattern to declare and seal (all possible) valid states and partitions.
The image below captures rustBoot's state transitions.
Upon supplying power to a system, execution usually starts in an mcu's BootROM. The BootROM is a piece of immutable code, also referred to as the root of trust. BootROM(s) are programmed into an mcu's ROM in the factory and may support secure-boot i.e. the ability to cryptographically verify (and pass control to) a 2nd stage executable/binary.
In our case, rustBoot is the 2nd stage. It checks the status of BOOT and UPDATE partitions and drives the state machine forward, as shown in the image below.
Note:
- there may be many intermediate stages/executables between
BootROMandrustBoot.- rustBoot assumes that the target device contains a
root of trustwith support for digitally verifying cryptographic signatures.- rustBoot will check for firmware integrity and authenticity at appropriate stages during boot/update/rollback.
- booting, updating and rolling back are determined by the the state machine.
partition status:
rustBoot partitions use a status byte to track the status of firmware in each partition. The status byte is a 1-byte field stored at the end of each partition.
Possible states are:
STATE_NEW (0xFF):The image was never staged for boot, or triggered for an update. If an image is present, no flags are active.STATE_UPDATING (0x70):Only valid in theUPDATEpartition. The image is marked for update and should replace the current image inBOOT.STATE_TESTING (0x10):Only valid in theBOOTpartition. The image has been just updated, and never completed its boot. If present after reboot, it means that the updated image failed to boot, despite being correctly verified. This particular situation triggers a rollback.STATE_SUCCESS (0x00):Only valid in theBOOTpartition. The image stored inBOOThas been successfully staged at least once, and the update is now complete.
partition sector flags:
- When an update is triggered, the contents of
UPDATEandBOOTare swapped one sector at a time.SWAPis used to temporarily hold a sector during the swap. This ensures that we always store a backup of the original firmware. - rustBoot keeps track of the state of each sector (during a swap), using 4 bits per sector at the end of the
UPDATEpartition. - Each
sector-swapoperation corresponds to a different flag-value for a sector in the sector flags area. This means if a swap operation is interrupted, it can be resumed upon reboot.
linux-system updates:
linux-system updates rely on an updt.txt file. updt.txt as the name suggests is an update-configuration file containing active and passive image components.
- active component: refers to the rustBoot compliant fit-image that's currently in-use. This also implies that the active image has been successfully verified and booted at least once.
- passive component: refers to the rustBoot compliant fit-image that's been marked
ready-for-updatei.e. the active image has downloaded an update and set the corresponding passive component field inupdt.txt. Passive component(s) have an additional field calledupdate_statusand can be assigned one of the following values.- updating: The image is marked for update and should replace the current
active-image. - testing: The image has been just updated, and never completed its boot. If present after reboot, it means that the updated image failed to boot, despite being correctly verified. This particular situation triggers a rollback.
- success: The update has been successfully staged at least once, and the update is now complete.
- updating: The image is marked for update and should replace the current
Notes:
- A valid
updt.txtfile must (always) contain an active and a passive component.- The active component must contain the fields -
image_nameandimage_version.- The passive component may contain optional fields such as
image_name,image_versionandupdate_status- Example: for what constitutes a
valid config file, please see theupdt.txtin the rpi4 example.
Here's how it works:
- rustBoot loads the
updt.txtfile from the root directory, parses it and retrieves the active and passive components. - it then performs the following checks, assuming an update is available
- does the active component include valid
image_nameandimage_versionfields? - has the passive component's
ready_for_updatefield been set to true? - if yes, have the
passive_name,passive_versionfields been set and haspassive_statusbeen set to eitherupdatingorsuccess? i.e. update has been marked as ready (on the next reboot). - is the passive_version field greater than the active? A valid update must have a version greater than the active version.
- does the active component include valid
- if all the above checks pass, we attempt to load the update (fit-image) and if any one of above checks fail, we simply load the currently active-image.
- lastly, rustBoot verifies the loaded fit-image's cryptographic digital signature and additionally checks whether the
version-numberfromupdt.txtmatches the fit-image's timestamp (to prevent rollback or downgrade attacks). The fit's version number is retrieved from rustBoot'supdt.txtfile.- if a version mismatch arises in the last step, rustBoot attempts to rollback to the currently active-image
A note on valid updt.txt configs. They must contain
- an image name that ends with
.itbas its file extension - a valid unix epoch timestamp as its version number and it must start with the
ts_prefix. - config keys that are recognized by rustBoot -
[active],[passive] - field names that are recognized by rustBoot. -
image_name,image_version,ready_for_update_flag,update_status