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 scheme
linux-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
BootROM
andrustBoot
.- rustBoot assumes that the target device contains a
root of trust
with 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 theUPDATE
partition. The image is marked for update and should replace the current image inBOOT
.STATE_TESTING (0x10):
Only valid in theBOOT
partition. 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 theBOOT
partition. The image stored inBOOT
has been successfully staged at least once, and the update is now complete.
partition sector flags:
- When an update is triggered, the contents of
UPDATE
andBOOT
are swapped one sector at a time.SWAP
is 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
UPDATE
partition. - Each
sector-swap
operation 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-update
i.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_status
and 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.txt
file must (always) contain an active and a passive component.- The active component must contain the fields -
image_name
andimage_version
.- The passive component may contain optional fields such as
image_name
,image_version
andupdate_status
- Example: for what constitutes a
valid config file
, please see theupdt.txt
in the rpi4 example.
Here's how it works:
- rustBoot loads the
updt.txt
file 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_name
andimage_version
fields? - has the passive component's
ready_for_update
field been set to true? - if yes, have the
passive_name
,passive_version
fields been set and haspassive_status
been set to eitherupdating
orsuccess
? 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-number
fromupdt.txt
matches the fit-image's timestamp (to prevent rollback or downgrade attacks). The fit's version number is retrieved from rustBoot'supdt.txt
file.- 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
.itb
as 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