Skip to content
This repository has been archived by the owner on Feb 25, 2023. It is now read-only.

Commit

Permalink
fixup! hsm: add possibility to update HSM firmware from middleware
Browse files Browse the repository at this point in the history
  • Loading branch information
Tomasvrba committed Dec 19, 2019
1 parent a772c34 commit f7311a4
Show file tree
Hide file tree
Showing 5 changed files with 91 additions and 68 deletions.
39 changes: 8 additions & 31 deletions middleware/cmd/middleware/main.go
Original file line number Diff line number Diff line change
Expand Up @@ -31,25 +31,20 @@ func main() {
imageUpdateInfoURL := flag.String("updateinfourl", "https://shiftcrypto.ch/updates/base.json", "URL to query information about Base image updates from")
notificationNamedPipePath := flag.String("notificationNamedPipePath", "/tmp/middleware-notification.pipe", "Path where the Middleware creates a named pipe to receive notifications from other processes on the BitBoxBase")
hsmSerialPort := flag.String("hsmserialport", "/dev/ttyS0", "Serial port used to communicate with the HSM")
// hsmFirmwareFile and upgradeHSMFirmware options are to be used only for manually installing the hsm firmare
// Otherwise firmare is upgraded automatically on boot when new version is specified in Redis hsm:firmware:version
hsmFirmwareFile := flag.String("hsmfirmwarefile", "/opt/shift/hsm/firmware-bitboxbase.signed.bin", "Location of the signed HSM firmware binary")
updateHSMFirmware := flag.Bool("updatehsmfirmware", false, "Set to true to force HSM firmware update")
upgradeHSMFirmware := flag.Bool("upgradehsmfirmware", false, "Set to true to force HSM firmware upgrade")
flag.Parse()

hsm := hsm.NewHSM(*hsmSerialPort)
if *updateHSMFirmware {
err := hsm.UpdateFirmware(*hsmFirmwareFile)
if *upgradeHSMFirmware {
err := hsm.UpgradeFirmware(*hsmFirmwareFile)
if err != nil {
log.Printf("Failed to update HSM firmware: %s", err)
log.Printf("Failed to upgrade HSM firmware: %s", err)
}
}

hsmFirmware, err := hsm.WaitForFirmware()
if err != nil {
log.Printf("Failed to connect to the HSM firmware: %v. Continuing without HSM.", err)
} else {
log.Printf("HSM serial port connected.")
}

config := configuration.NewConfiguration(
configuration.Args{
BBBCmdScript: *bbbCmdScript,
Expand All @@ -64,6 +59,7 @@ func main() {
PrometheusURL: *prometheusURL,
RedisMock: *redisMock,
RedisPort: *redisPort,
HsmFirmwareFile: *hsmFirmwareFile,
},
)

Expand All @@ -77,31 +73,12 @@ func main() {
}
defer logBeforeExit()

middleware, err := middleware.NewMiddleware(config, hsmFirmware)
middleware, err := middleware.NewMiddleware(config, hsm)
if err != nil {
log.Fatalf("error starting the middleware: %s . Is redis connected? \nIf you are running the middleware outside of the base consider setting the redis mock flag to true: '-redismock true' .", err.Error())
}
log.Println("--------------- Started middleware --------------")

// Check for available hsm firmware update on middleware boot and update if available
if hsmFirmware != nil {
updateAvailable, err := middleware.HSMUpdateAvailable(hsm)
if err != nil {
log.Printf("Failed to fetch HSM version/update information: %s. Proceeding normally", err)
}
if updateAvailable {
err = hsm.UpdateFirmware(*hsmFirmwareFile)
if err != nil {
log.Printf("Failed to update HSM firmware: %s", err)
} else {
err = middleware.BootHSMFirmware(hsm)
if err != nil {
log.Printf("Error: Failed to reboot back into HSM firmware. Base restart may be required: %s", err)
}
}
}
}

handlers := handlers.NewHandlers(middleware, *dataDir)
log.Printf("Binding middleware api to port %s\n", *middlewarePort)

Expand Down
8 changes: 8 additions & 0 deletions middleware/src/configuration/configuration.go
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@ type Args struct {
PrometheusURL string
RedisMock bool
RedisPort string
HsmFirmwareFile string
}

// Configuration holds the configuration options for the Middleware.
Expand All @@ -46,6 +47,7 @@ type Configuration struct {
prometheusURL string
redisMock bool
redisPort string
hsmFirmwareFile string
}

// NewConfiguration returns a new Configuration instance.
Expand All @@ -66,6 +68,7 @@ func NewConfiguration(args Args) Configuration {
prometheusURL: args.PrometheusURL,
redisMock: args.RedisMock,
redisPort: args.RedisPort,
hsmFirmwareFile: args.HsmFirmwareFile,
}
return config
}
Expand Down Expand Up @@ -130,3 +133,8 @@ func (config *Configuration) GetNetwork() string {
func (config *Configuration) GetElectrsRPCPort() string {
return config.electrsRPCPort
}

// GetHsmFirmwareFile is a getter for the location of the HSM firmware file.
func (config *Configuration) GetHsmFirmwareFile() string {
return config.hsmFirmwareFile
}
9 changes: 3 additions & 6 deletions middleware/src/hsm/hsm.go
Original file line number Diff line number Diff line change
Expand Up @@ -205,17 +205,14 @@ func (hsm *HSM) InteractWithBootloader(f func(*bb02bootloader.Device)) error {
return nil
}

// UpdateFirmware reboots into Bootloader and executes the firmware update
func (hsm *HSM) UpdateFirmware(hsmFirmwareFile string) error {
// UpgradeFirmware reboots into Bootloader and executes the firmware upgrade
func (hsm *HSM) UpgradeFirmware(hsmFirmwareFile string) error {
hsmFirmwareBinary, err := ioutil.ReadFile(hsmFirmwareFile)
if err != nil {
return err
}
err = hsm.InteractWithBootloader(func(bootloader *bb02bootloader.Device) {
err = bootloader.UpgradeFirmware(hsmFirmwareBinary)
})
if err != nil {
return err
}
return nil
return err
}
39 changes: 8 additions & 31 deletions middleware/src/middleware.go
Original file line number Diff line number Diff line change
Expand Up @@ -49,6 +49,7 @@ type Middleware struct {
isMiddlewarePasswordSet bool
isBaseSetupDone bool

hsm *hsm.HSM
hsmFirmware *firmware.Device
}

Expand All @@ -61,7 +62,7 @@ func (middleware *Middleware) GetMiddlewareVersion() string {
//
// hsmFirmware let's you talk to the HSM. NOTE: it the HSM could not be connected, this is nil. The
// middleware must be able to run and serve RPC calls without the HSM present.
func NewMiddleware(config configuration.Configuration, hsmFirmware *firmware.Device) (*Middleware, error) {
func NewMiddleware(config configuration.Configuration, hsm *hsm.HSM) (*Middleware, error) {
middleware := &Middleware{
config: config,
//TODO(TheCharlatan) find a better way to increase the channel size
Expand All @@ -78,7 +79,7 @@ func NewMiddleware(config configuration.Configuration, hsmFirmware *firmware.Dev
UpdateAvailable: false,
},
baseVersion: semver.NewSemVer(0, 0, 0),
hsmFirmware: hsmFirmware,
hsm: hsm,
}

middleware.prometheusClient = prometheus.NewClient(middleware.config.GetPrometheusURL())
Expand All @@ -89,6 +90,11 @@ func NewMiddleware(config configuration.Configuration, hsmFirmware *firmware.Dev
middleware.redisClient = redis.NewMockClient("")
}

// Initialize the HSM firmware connection and install firmware upgrade if available
if hsm != nil {
middleware.initHSM()
}

err := middleware.checkMiddlewareSetup()
if err != nil {
log.Println("failed to update the middleware password set flag")
Expand Down Expand Up @@ -1172,32 +1178,3 @@ func (middleware *Middleware) VerifyAppMiddlewarePairing(channelHash []byte) (bo
}
return true, nil
}

// HSMUpdateAvailable checks the AvailableHSMVersion Redis and compares it to the running FW version
func (middleware *Middleware) HSMUpdateAvailable(hsm *hsm.HSM) (bool, error) {
availableHSMVersion, err := middleware.redisClient.GetString(redis.AvailableHSMVersion)
if err != nil {
return false, err
}
availableSemver, err := semver.NewSemVerFromString(availableHSMVersion)
if err != nil {
return false, err
}
currentVersion := middleware.hsmFirmware.Version()
if !currentVersion.AtLeast(availableSemver) {
log.Printf("BitBoxBase HSM update available from version: %s to version: %s", currentVersion, availableHSMVersion)
return true, nil
}
log.Printf("BitBoxBase HSM is up to date: %s", currentVersion)
return false, nil
}

// BootHSMFirmware boots into the HSM firmware, e.g., after a firmware udpate
func (middleware *Middleware) BootHSMFirmware(hsm *hsm.HSM) error {
var err error
middleware.hsmFirmware, err = hsm.WaitForFirmware()
if err != nil {
return err
}
return nil
}
64 changes: 64 additions & 0 deletions middleware/src/util.go
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ import (
"github.com/digitalbitbox/bitbox-base/middleware/src/redis"
"github.com/digitalbitbox/bitbox-base/middleware/src/rpcmessages"
"github.com/digitalbitbox/bitbox02-api-go/api/firmware/messages"
"github.com/digitalbitbox/bitbox02-api-go/util/semver"
)

// The util.go file includes utility functions for the Middleware.
Expand Down Expand Up @@ -411,3 +412,66 @@ func (middleware *Middleware) setHSMConfig() error {
}
return nil
}

// upgradeFirmware upgrades the HSM firmare from the middleware
func (middleware *Middleware) upgradeFirmware() error {
err := middleware.hsm.UpgradeFirmware(middleware.config.GetHsmFirmwareFile())
if err != nil {
return err
}
middleware.hsmFirmware, err = middleware.hsm.WaitForFirmware()
if err != nil {
return err
}
return nil
}

// hsmUpgradeAvailable checks the AvailableHSMVersion Redis and compares it to the running FW version
func (middleware *Middleware) hsmUpgradeAvailable() (bool, error) {
availableHSMVersion, err := middleware.redisClient.GetString(redis.AvailableHSMVersion)
if err != nil {
return false, err
}
availableSemver, err := semver.NewSemVerFromString(availableHSMVersion)
if err != nil {
return false, err
}
currentVersion := middleware.hsmFirmware.Version()
if !currentVersion.AtLeast(availableSemver) {
log.Printf("BitBoxBase HSM upgrade available from version: %s to version: %s", currentVersion, availableHSMVersion)
return true, nil
}
log.Printf("BitBoxBase HSM is up to date: %s", currentVersion)
return false, nil
}

// initHSM tries to connect to the HSM firmware. On success, it checks if a firmware upgrade is available
// and tries to apply it.
// On failing to connect to the firmware, it tries to connect to the bootloader and re-install the firmware.
// If that also fails, we continue without the HSM
func (middleware *Middleware) initHSM() {
var err error
middleware.hsmFirmware, err = middleware.hsm.WaitForFirmware()
if err != nil {
// Failing to connect to the firmware may mean a FW upgrade had failed and we are in the bootloader
// Try to re-do the upgrade via the bootloader before continuing without the HSM
err = middleware.upgradeFirmware()
if err != nil {
log.Printf("Failed to connect to the HSM: %v. Continuing without HSM.", err)
}
} else {
log.Printf("HSM serial port connected.")
}
if middleware.hsmFirmware != nil {
upgradeAvailable, err := middleware.hsmUpgradeAvailable()
if err != nil {
log.Printf("Failed to fetch HSM version/upgrade information: %s. Proceeding normally", err)
}
if upgradeAvailable {
err = middleware.upgradeFirmware()
if err != nil {
log.Printf("Failed to upgrade HSM firmware: %s", err)
}
}
}
}

0 comments on commit f7311a4

Please sign in to comment.