From 6876115b0d7dc0c4b4fbcef3499e5d415d82f35e Mon Sep 17 00:00:00 2001 From: Marko Bencun Date: Mon, 13 May 2024 06:49:05 +0200 Subject: [PATCH] backend/bitbox02bootloader: allow fixing broken install Problem: a new device ships with the latest firmware monotonic version. User sees 'Install' because the device is erased (no firmware on it), but during first install of firmware, unplugs. Upon replug, the device will show 'Invalid firmware' and boot into bootloader again. Since the monotonic version of the firmware to be installed is the same as the version stored on the device, and the device is not erased (half of the firmware is on it), the user sees no button to upgrade/instll anymore. This commit fixes this by still showing the upgrade button if the versions are the same, but the device firmware hash does not match the expected firmware hash. --- backend/devices/bitbox02bootloader/device.go | 24 ++++++++++++++++++- .../devices/bitbox02bootloader/firmware.go | 14 +++++++++++ 2 files changed, 37 insertions(+), 1 deletion(-) diff --git a/backend/devices/bitbox02bootloader/device.go b/backend/devices/bitbox02bootloader/device.go index fa58331096..b3daf4d533 100644 --- a/backend/devices/bitbox02bootloader/device.go +++ b/backend/devices/bitbox02bootloader/device.go @@ -17,6 +17,8 @@ package bitbox02bootloader import ( + "bytes" + "encoding/hex" "fmt" "sync" @@ -240,6 +242,11 @@ func (device *Device) VersionInfo() (*VersionInfo, error) { if err != nil { return nil, err } + currentFirmwareHash, _, err := device.Device.GetHashes(false, false) + if err != nil { + return nil, err + } + latestFw, err := bundledFirmware(device.Device.Product()) if err != nil { return nil, err @@ -249,12 +256,27 @@ func (device *Device) VersionInfo() (*VersionInfo, error) { if err != nil { return nil, err } - canUpgrade := erased || latestFirmwareVersion > currentFirmwareVersion + + latestFirmwareHash, err := latestFw.firmwareHash() + if err != nil { + return nil, err + } + + // If the device firmware version is at the latest version but the installed firmware is + // different, we assume it's a broken/interrupted install. This can happen for example when a + // new device is shipped with the latest monotonic version pre-set, and the user interrupts + // their first install. + brokenInstall := latestFirmwareVersion == currentFirmwareVersion && + !bytes.Equal(currentFirmwareHash, latestFirmwareHash) + + canUpgrade := erased || latestFirmwareVersion > currentFirmwareVersion || brokenInstall additionalUpgradeFollows := nextFw.monotonicVersion < latestFirmwareVersion device.log. WithField("latestFirmwareVersion", latestFirmwareVersion). WithField("currentFirmwareVersion", currentFirmwareVersion). + WithField("currentFirmwareHash", hex.EncodeToString(currentFirmwareHash)). WithField("erased", erased). + WithField("brokenInstall", brokenInstall). WithField("canUpgrade", canUpgrade). WithField("additionalUpgradeFollows", additionalUpgradeFollows). Info("VersionInfo") diff --git a/backend/devices/bitbox02bootloader/firmware.go b/backend/devices/bitbox02bootloader/firmware.go index 1bb92629e1..c957e09b1f 100644 --- a/backend/devices/bitbox02bootloader/firmware.go +++ b/backend/devices/bitbox02bootloader/firmware.go @@ -21,6 +21,7 @@ import ( "io" "github.com/digitalbitbox/bitbox-wallet-app/util/errp" + "github.com/digitalbitbox/bitbox02-api-go/api/bootloader" bitbox02common "github.com/digitalbitbox/bitbox02-api-go/api/common" "github.com/digitalbitbox/bitbox02-api-go/util/semver" ) @@ -53,6 +54,19 @@ func (fi firmwareInfo) signedBinary() ([]byte, error) { return io.ReadAll(gz) } +func (fi firmwareInfo) firmwareHash() ([]byte, error) { + signedBinary, err := fi.signedBinary() + if err != nil { + return nil, err + } + + _, _, binary, err := bootloader.ParseSignedFirmware(signedBinary) + if err != nil { + return nil, err + } + return bootloader.HashFirmware(fi.monotonicVersion, binary), nil +} + // The last entry in the slice is the latest firmware update to which one can upgrade. // The other entries are intermediate upgrades that are required before upgrading to the latest one. // Each one has to be flashed and booted before being able to continue upgrading.