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

(Almost) interaction-less firmware update #2919

Closed
Hannsek opened this issue Mar 29, 2023 · 13 comments · Fixed by #3363
Closed

(Almost) interaction-less firmware update #2919

Hannsek opened this issue Mar 29, 2023 · 13 comments · Fixed by #3363
Assignees
Labels
bootloader core Bootloader for Trezor core T2B1 Trezor Safe 3 (F4) T2T1 Trezor Model T

Comments

@Hannsek
Copy link
Contributor

Hannsek commented Mar 29, 2023

#2841 (comment):

while we're thinking about this, we could allow interaction-less upgrade, i.e., skip the "really install firmware by SatoshiLabs version 2.99" dialog, if (a) the vendor is unchanged, and (b) it is a strict upgrade, and (c) click flag is not set in the vendor header i.e.:

  • reinstall of same version still gets a confirmation
  • downgrade gets confirmation
  • vendor change gets confirmation, obviously
  • any operation with devel firmware gets confirmation

then you would, in firmware, click Yes on "proceed to installing firmware?", Trezor screen goes blue, waits for host, straight to progress bar -> reboot -> you're done

Questions:

  • Can we display info about to-be-installed firmware before the installation?

The whole flow could look like this:
image

image
@Hannsek Hannsek added bootloader core Bootloader for Trezor core T2T1 Trezor Model T labels Mar 29, 2023
@Hannsek Hannsek added this to the 🔵 Bootloader update milestone Mar 29, 2023
@Hannsek Hannsek added this to Firmware Mar 29, 2023
@matejcik
Copy link
Contributor

can't really: the host could be lying and send one thing first and another thing after going to bootloader.
we could do that if we allow jump straight to bootloader, but that's something I'm not ready to allow just yet.

@Hannsek Hannsek moved this to 🎯 To do in Firmware Mar 30, 2023
@Hannsek
Copy link
Contributor Author

Hannsek commented Mar 30, 2023

@prusnak @andrewkozlik Do you have any thoughts about this?

@prusnak
Copy link
Member

prusnak commented Mar 30, 2023

I find the proposal in the original comment good. Just one note, there is no devel firmware. What you call a devel firmware is just a firmware with a different vendor (which has the click flag set at the moment).

@matejcik
Copy link
Contributor

interaction-less update RFC:

1. Firmware stage

  1. host sends FirmwareUpdate that contains exactly the header, i.e., concatenated vendor header + firmware header

  2. firmware verifies that:

    • the headers are valid (signatures match etc)
    • vendor matches the currently installed one
    • version is strictly higher than the current one

    if this fails, Failure is returned. (if the operation is actually what the user wants, they will need to go to bootloader to do it)

  3. if it is OK, Trezor displays a prompt:
    "Upgrade firmware to version 2.7.9?"
    (I don't think we need to mention the vendor here, given that it is forced to be the same as current -- but we can)

  4. when the user confirms, firmware sends Success and jumps to bootloader

  5. host must wait until the device disappears from the bus and a bootloader reappears

2. Bootloader stage

  1. bootloader detects that a header was sent from firmware

  2. bootloader verifies that:

    • the headers are valid
    • vendor matches the currently installed one
    • vendor is strictly higher than the current one

    if this fails, an error screen is displayed, asking the user to replug device (?)
    (alternately, we could either (a) continue booting as if nothing happened, or (b) jump to bootloader menu as if normal jump-to-bootloader)

  3. if header is OK, bootloader goes to "waiting for host" screen. this should be rendered in black instead of bootloader blue

  4. host sends FirmwareErase message indicating that it is ready to update firmware

    • if the host sends something else here, like UnlockBootloader or WipeDevice, we can probably go to that flow? it seems to make the impl simpler, and I can't see any problem in allowing that.
  5. bootloader acts as if it already received the header and jumps to verification. we need to inject an automatic response into this step -- this is already done when is_new comes out as sectrue, so that should not be a problem

  6. firmware upgrade process continues as normal (with black progress bar)

For bonus points:

Make the interval between erasing firmware and writing first sector shorter.
The ideal would be:

  1. receive & validate header
  2. erase first sector
  3. write header at start of first sector
  4. erase rest of sectors
  5. proceed to ask for first chunk, then write chunk into rest of sector

this shortens the interval when no valid header is present in flash to minimum (i.e., if we allow recovery from corrupted firmware, the only way to lose the header is if the power goes out at exactly the right moment)

unfortunately I believe it further complicates the logic of chunk handling. but maybe we could untangle and refactor the current code so that this is achievable?

cc @mroz22 for Connect-side review

@Hannsek
Copy link
Contributor Author

Hannsek commented Sep 19, 2023

if this fails, Failure is returned. (if the operation is actually what the user wants, they will need to go to bootloader to do it)

Either, or it can simply follow to old process. Device would jump into the bootloader and user will have to confirm the installation there.

if this fails, an error screen is displayed, asking the user to replug device (?)
(alternately, we could either (a) continue booting as if nothing happened, or (b) jump to bootloader menu as if normal jump-to-bootloader)

If this fails, there is something really wrong, no? Like that host sent different fw to bootloader then to firmware, right?

if header is OK, bootloader goes to "waiting for host" screen. this should be rendered in black instead of bootloader blue

I believe we can drop this screen and goes directly to "installing firmware" screen?

What about displaying fw fingerprint in Firmware stage? Would it be possible?

@matejcik
Copy link
Contributor

if this fails, Failure is returned. (if the operation is actually what the user wants, they will need to go to bootloader to do it)

Either, or it can simply follow to old process. Device would jump into the bootloader and user will have to confirm the installation there.

That's a weird behavior from code standpoint. Host sends message A and device responds as if the message was B. In this case, if the host insists on doing it, it should react to the failure by re-trying the action via message B explicitly:

Host: FirmwareUpdate(...)
Trezor: Failure(DataError)
Host: ah, I see that didn't work. RebootToBootloader()

if this fails, an error screen is displayed, asking the user to replug device (?)
(alternately, we could either (a) continue booting as if nothing happened, or (b) jump to bootloader menu as if normal jump-to-bootloader)

If this fails, there is something really wrong, no? Like that host sent different fw to bootloader then to firmware, right?

This could indicate (a) something wrong with the device, or (b) a broken / malicious firmware. E.g., if a dev firmware tried to initiate upgrade to a signed firmware.

if header is OK, bootloader goes to "waiting for host" screen. this should be rendered in black instead of bootloader blue

I believe we can drop this screen and goes directly to "installing firmware" screen?

We probably could, but it seems nicer to me to insert "waiting for host" inbetween. Consider a situation when Suite initiates the upgrade, but then has trouble recognizing the Trezor in bootloader mode. In this scenario, Trezor is either stuck "waiting for host" (strictly correct -- Suite isn't talking to it) or "updating firmware" (slightly misleading, the update hadn't actually started)

What about displaying fw fingerprint in Firmware stage? Would it be possible?

sure, but what would be the point?

@Hannsek
Copy link
Contributor Author

Hannsek commented Sep 19, 2023

if this fails, Failure is returned. (if the operation is actually what the user wants, they will need to go to bootloader to do it)

Either, or it can simply follow to old process. Device would jump into the bootloader and user will have to confirm the installation there.

That's a weird behavior from code standpoint. Host sends message A and device responds as if the message was B. In this case, if the host insists on doing it, it should react to the failure by re-trying the action via message B explicitly:

Host: FirmwareUpdate(...) Trezor: Failure(DataError) Host: ah, I see that didn't work. RebootToBootloader()

I meant the situation when e.g. the version is the same or lower than the current one; in that case trezor would jump to bootloader mode but instead immediate installation of new fw user would have to confirm the installation once more (current state)

if this fails, an error screen is displayed, asking the user to replug device (?)
(alternately, we could either (a) continue booting as if nothing happened, or (b) jump to bootloader menu as if normal jump-to-bootloader)

If this fails, there is something really wrong, no? Like that host sent different fw to bootloader then to firmware, right?

This could indicate (a) something wrong with the device, or (b) a broken / malicious firmware. E.g., if a dev firmware tried to initiate upgrade to a signed firmware.

So probably put there some "Contact support" screen? It should appear only exceptionally, right?

What about displaying fw fingerprint in Firmware stage? Would it be possible?

sure, but what would be the point?

So that users can check the fingerprint prior to installation.

@matejcik
Copy link
Contributor

I meant the situation when e.g. the version is the same or lower than the current one; in that case trezor would jump to bootloader mode but instead immediate installation of new fw user would have to confirm the installation once more (current state)

sooo you would see what? one confirmation of upgrade in firmware (saying what? upgrade/downgrade, target version?) and then another identical one in bootloader mode?

what is the usecase, as opposed to "go to bootloader" -> "install firmware" -> confirm?

Note that the "strict upgrade" condition is arbitrarily chosen. In terms of hard-security, we could allow anything that has the same vendor. In terms of "UX security", I would be OK with lowering to "upgrade or reinstall". I would still strongly prefer to make the user jump through hoops to do a firmware downgrade, because that always means they could be downgrading to an insecure version.

So probably put there some "Contact support" screen? It should appear only exceptionally, right?

yes, which is why i wouldn't want to create a completely new screen for it and instead reuse the "generic error" one

What about displaying fw fingerprint in Firmware stage? Would it be possible?

sure, but what would be the point?

So that users can check the fingerprint prior to installation.

I mean, the whole fingerprint verification thing is weird. Is Suite even showing a fingerprint for T2xx models?
Even if yes, what is the source of truth here? A subverted Suite will lie about the fingerprint. The user would need to find the fingerprint through another channel (a reproducible build maybe?).
We could take this as a feel-good measure for users, "letting" them do an active step in confirming that the firmware is genuine ... but on the other hand that could be sending a message that an active step is somehow necessary?

Again, this can be done (an (i) icon for the upgrade firmware screen), but in terms of UX I would prefer to leave it out, and let the paranoid ones install through bootloader where the fingerprint can be shown.

@matejcik
Copy link
Contributor

would be OK with lowering to "upgrade or reinstall".

The reason i left out "reinstall" originally is that a reinstall is, by default, a strange condition. Reinstall of the same version of firmware is never required: either the firmware is borked, in which case bootloader won't let you start it -- or the firmware is OK, in which case a reinstall is a no-op.

@Hannsek
Copy link
Contributor Author

Hannsek commented Sep 19, 2023

what is the usecase, as opposed to "go to bootloader" -> "install firmware" -> confirm?

This is the current flow and it is good for the non-happy path. I was probably confused. 🤨
I fully agree with being strict and I'd allow only upgrade. The worst thing that can happen to user is the fallback to the current solution, which is fine.

@Hannsek
Copy link
Contributor Author

Hannsek commented Sep 19, 2023

yes, which is why i wouldn't want to create a completely new screen for it and instead reuse the "generic error" one

yeah, but then support won't know what has happened.

@Hannsek
Copy link
Contributor Author

Hannsek commented Sep 19, 2023

Again, this can be done (an (i) icon for the upgrade firmware screen), but in terms of UX I would prefer to leave it out, and let the paranoid ones install through bootloader where the fingerprint can be shown.

Ok, I am fine with leaving fingerprint only when installing through the bootloader.

@Hannsek
Copy link
Contributor Author

Hannsek commented Oct 11, 2023

@matejkriz @Hermez-cz FYI, this is to be released the next fw release - November.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
bootloader core Bootloader for Trezor core T2B1 Trezor Safe 3 (F4) T2T1 Trezor Model T
Projects
Archived in project
Development

Successfully merging a pull request may close this issue.

4 participants