Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

lpc55-rot-startup: Use a better method to generate an FWID. #1500

Merged
merged 2 commits into from
Aug 25, 2023
Merged

Conversation

flihp
Copy link
Contributor

@flihp flihp commented Aug 17, 2023

The current implementation captures all of the RoT software. It does not however include other pages in the RoT image flash range. Expanding our definition for the RoT FWID as not only the expected image, but also any other programmed flash pages in the range of possible pages mitigates a potential persistence mechanism for an attacker.

This commit:

  • adds a new environment variable HUBRIS_FLASH_OUTPUT that holds the config::Output structure for the flash region assigned to the image being built
  • generates a Range from HUBRIS_FLASH_OUTPUT usable by the lpc55-rot-starup code
  • uses this Range to walk through all possible flash pages for the image including each page that's been programmed in the FWID

@flihp flihp requested a review from lzrd August 17, 2023 15:35
@flihp
Copy link
Contributor Author

flihp commented Aug 17, 2023

This can be tested in a few different ways if folks want to reproduce. The easiest is to (assuming we're using imagea on an RoT)

  1. erase the flash range 0x10_000..0x50_000 (consult lpc55 memory.toml for values):
    NOTE: this is not strictly necessary but my dev board had a random flash page programmed well outside of the expected range likely from past work so this may be required
  2. boot the image
  3. copy out all of the flash pages programmed
    NOTE: humility readmem can be used for this but you'll have to read out the flash in 0x10_000 byte blocks and convert the hex dump back to binary. This works but it's easier to use gdb dump binary memory foo.bin 0x10000 0xEND
    1. the start address should be 0x10_000 per above
    2. the end address should be 0x10_000 + sizeof(final.bin) + FLASH_PAGE_SIZE - sizeof(final.bin) % FLASH_PAGE_SIZE
      1. sizeof(final.bin) = 225736
      2. FLASH_PAGE_SIZE = 512
  4. hash the memory dumped by gdb: $ openssl sha3-256 foo.bin
    NOTE the expected digest is 897408be5039aec1fcb581c01c5afdc4d4f433a28a6d19a52c4563235b7493d0
  5. get the FWID calculated by hubris on boot (from this PR) by querying the update server for the rot_boot_info: humility --target rot-carrier --archive-name imagea -- hiffy --call Update.rot_boot_info
    NOTE: the output should look something like:
    Update.rot_boot_info() => RotBootInfo { active: A, persistent_boot_preference: A, pending_persistent_boot_preference: None, transient_boot_preference: None, slot_a_sha3_256_digest: Some([ 0x89, 0x74, 0x8, 0xbe, 0x50, 0x39, 0xae, 0xc1, 0xfc, 0xb5, 0x81, 0xc0, 0x1c, 0x5a, 0xfd, 0xc4, 0xd4, 0xf4, 0x33, 0xa2, 0x8a, 0x6d, 0x19, 0xa5, 0x2c, 0x45, 0x63, 0x23, 0x5b, 0x74, 0x93, 0xd0 ]), slot_b_sha3_256_digest: None }
    

@flihp flihp linked an issue Aug 17, 2023 that may be closed by this pull request
lib/lpc55-rot-startup/build.rs Outdated Show resolved Hide resolved

for start in IMAGE_FLASH.step_by(FLASH_PAGE_SIZE) {
let start: u32 = start.try_into().unwrap_lite();
if lpc55_romapi::validate_programmed(start, PAGE_SIZE) {
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Does this handle partially programmed pages correctly? I need to go review exactly how this would work on trailing pages (aside, I should sit down and finally rip out usage of this ROM API and just use the flash driver)

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

ROM API rip / replace: #1431

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Does this handle partially programmed pages correctly?

I think so, assuming my reading of UM11126 § 8.6.7 is correct. This implementation includes the 0xff trailing data added by the write command in the FWID hash.

@andrewjstone
Copy link
Contributor

@flihp asked me to take a look at this because we plumb the digest all the way up through wicket. AFAICT this looks good and closes a security hole. Approved from my POV, although you may want to build atop @labbott's recent changes that rip out the rom api.

Copy link
Collaborator

@labbott labbott left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I ran the tests you suggested and was able to get a reliable answer. Even longer term we might just want to write to the entire image length space.

pub fn get_fwid(&self) -> [u8; 32] {
let mut hash = Sha3_256::new();

for start in IMAGE_FLASH.step_by(FLASH_PAGE_SIZE) {
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Okay while testing I realize this doesn't actually work. We need to be able to get the base of the other image, otherwise they both end up with the same hash:

$ humility -a target/rot-carrier/dist/a/build-rot-carrier-image-a.zip hiffy --call Update.rot_boot_info
humility: attached via CMSIS-DAP
Update.rot_boot_info() => RotBootInfo { active: A, persistent_boot_preference: A, pending_persistent_boot_preference: None, transient_boot_preference: None, slot_a_sha3_256_digest: Some([ 0x9d, 0xce, 0xc5, 0x7e, 0x78, 0xb8, 0x74, 0xb0, 0xc8, 0x78, 0x71, 0x7e, 0xf1, 0x58, 0x8b, 0x53, 0x88, 0xea, 0xc, 0x7, 0xb6, 0x23, 0x3, 0x69, 0x3b, 0x8f, 0xd4, 0xd8, 0x1d, 0x10, 0x8f, 0x1 ]), slot_b_sha3_256_digest: Some([ 0x9d, 0xce, 0xc5, 0x7e, 0x78, 0xb8, 0x74, 0xb0, 0xc8, 0x78, 0x71, 0x7e, 0xf1, 0x58, 0x8b, 0x53, 0x88, 0xea, 0xc, 0x7, 0xb6, 0x23, 0x3, 0x69, 0x3b, 0x8f, 0xd4, 0xd8, 0x1d, 0x10, 0x8f, 0x1 ]) }

I'll give this a think if there's another option

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I see what you mean. This doesn't become apparent until you've flashed the 'b' image before pulling the rot_boot_info:

(hubris) [flihp@slag hubris]$ humility --target rot-carrier --archive-name imagea -- hiffy --call Update.rot_boot_info
humility: attached to 1fc9:0143:IYJ1NYJRCW2TC via CMSIS-DAP
Update.rot_boot_info() => RotBootInfo { active: B, persistent_boot_preference: A, pending_persistent_boot_preference: None, transient_boot_preference: None, slot_a_sha3_256_digest: Some([ 0x3d, 0x4d, 0x34, 0x69, 0x37, 0x24, 0x8b, 0xef, 0x93, 0xd7, 0x18, 0xd1, 0x25, 0xf3, 0xe0, 0xab, 0x15, 0xbc, 0x15, 0x98, 0x33, 0xaa, 0x46, 0x67, 0x91, 0xa4, 0x26, 0x53, 0xde, 0x53, 0x10, 0x41 ]), slot_b_sha3_256_digest: Some([ 0x3d, 0x4d, 0x34, 0x69, 0x37, 0x24, 0x8b, 0xef, 0x93, 0xd7, 0x18, 0xd1, 0x25, 0xf3, 0xe0, 0xab, 0x15, 0xbc, 0x15, 0x98, 0x33, 0xaa, 0x46, 0x67, 0x91, 0xa4, 0x26, 0x53, 0xde, 0x53, 0x10, 0x41 ]) }

It's pretty much impossible for the two images to have the same hash (and still be functional) so this is definitely wrong. I'll sort out some options and update the PR.

Copy link
Contributor Author

@flihp flihp Aug 23, 2023

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I've just pushed two commits with a new approach. Instead of exporting the range for the image being built we now export all flash ranges for the chip from xtask through the environment. This allows us to use the appropriate range for A or B images. This also duplicates data in the HUBRIS_DICE_MFG env var so I've included another commit removing it.

@flihp
Copy link
Contributor Author

flihp commented Aug 23, 2023

@flihp asked me to take a look at this because we plumb the digest all the way up through wicket. AFAICT this looks good and closes a security hole. Approved from my POV, although you may want to build atop @labbott's recent changes that rip out the rom api.

Those recent changes went in after this PR was opened. I'll definitely rebase before merging but didn't want to do so while folks were actively reviewing.

@flihp
Copy link
Contributor Author

flihp commented Aug 23, 2023

@flihp asked me to take a look at this because we plumb the digest all the way up through wicket. AFAICT this looks good and closes a security hole. Approved from my POV, although you may want to build atop @labbott's recent changes that rip out the rom api.

Those recent changes went in after this PR was opened. I'll definitely rebase before merging but didn't want to do so while folks were actively reviewing.

Ah that PR hasn't been merged yet. Either way there will be rebasing.

@flihp flihp requested a review from labbott August 23, 2023 23:40
@flihp flihp force-pushed the fwid-calc branch 2 times, most recently from 296f6ad to 576c06a Compare August 24, 2023 02:14
Copy link
Collaborator

@labbott labbott left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Good solution!

@flihp
Copy link
Contributor Author

flihp commented Aug 25, 2023

I'm rebasing to clean up history from this review & will merge once those changes pass CI.

@flihp flihp force-pushed the fwid-calc branch 2 times, most recently from dc62478 to 22b3557 Compare August 25, 2023 16:40
The current implementation captures all of the RoT software. It does not
however include other pages in the RoT image flash range. Expanding our
definition for the RoT FWID as not only the expected image, but also any
other programmed flash pages in the range of possible pages mitigates a
potential persistence mechanism for an attacker.

This commit:
- adds a new environment variable HUBRIS_FLASH_OUTPUT that holds all
  config::Output structures for the flash regions assigned to the image
  being built
- generates a Range for each flash region in HUBRIS_FLASH_OUTPUTS for
  use by the lpc55-rot-startup crate
- uses the A & B Ranges to walk through all possible flash pages for the
  given image including each page that's been programmed in the FWID
@flihp flihp merged commit b2b4861 into master Aug 25, 2023
71 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

improve RoT stage0 FWID calculation
3 participants