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

Commit cd0fcd3

Browse files
committed
fixup! hsm: add possibility to update HSM firmware from middleware
1 parent a772c34 commit cd0fcd3

5 files changed

Lines changed: 89 additions & 68 deletions

File tree

middleware/cmd/middleware/main.go

Lines changed: 8 additions & 31 deletions
Original file line numberDiff line numberDiff line change
@@ -31,25 +31,20 @@ func main() {
3131
imageUpdateInfoURL := flag.String("updateinfourl", "https://shiftcrypto.ch/updates/base.json", "URL to query information about Base image updates from")
3232
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")
3333
hsmSerialPort := flag.String("hsmserialport", "/dev/ttyS0", "Serial port used to communicate with the HSM")
34+
// hsmFirmwareFile and upgradeHSMFirmware options are to be used only for manually installing the hsm firmare
35+
// Otherwise firmare is upgraded automatically on boot when new version is specified in Redis hsm:firmware:version
3436
hsmFirmwareFile := flag.String("hsmfirmwarefile", "/opt/shift/hsm/firmware-bitboxbase.signed.bin", "Location of the signed HSM firmware binary")
35-
updateHSMFirmware := flag.Bool("updatehsmfirmware", false, "Set to true to force HSM firmware update")
37+
upgradeHSMFirmware := flag.Bool("upgradehsmfirmware", false, "Set to true to force HSM firmware upgrade")
3638
flag.Parse()
3739

3840
hsm := hsm.NewHSM(*hsmSerialPort)
39-
if *updateHSMFirmware {
40-
err := hsm.UpdateFirmware(*hsmFirmwareFile)
41+
if *upgradeHSMFirmware {
42+
err := hsm.UpgradeFirmware(*hsmFirmwareFile)
4143
if err != nil {
42-
log.Printf("Failed to update HSM firmware: %s", err)
44+
log.Printf("Failed to upgrade HSM firmware: %s", err)
4345
}
4446
}
4547

46-
hsmFirmware, err := hsm.WaitForFirmware()
47-
if err != nil {
48-
log.Printf("Failed to connect to the HSM firmware: %v. Continuing without HSM.", err)
49-
} else {
50-
log.Printf("HSM serial port connected.")
51-
}
52-
5348
config := configuration.NewConfiguration(
5449
configuration.Args{
5550
BBBCmdScript: *bbbCmdScript,
@@ -64,6 +59,7 @@ func main() {
6459
PrometheusURL: *prometheusURL,
6560
RedisMock: *redisMock,
6661
RedisPort: *redisPort,
62+
HsmFirmwareFile: *hsmFirmwareFile,
6763
},
6864
)
6965

@@ -77,31 +73,12 @@ func main() {
7773
}
7874
defer logBeforeExit()
7975

80-
middleware, err := middleware.NewMiddleware(config, hsmFirmware)
76+
middleware, err := middleware.NewMiddleware(config, hsm)
8177
if err != nil {
8278
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())
8379
}
8480
log.Println("--------------- Started middleware --------------")
8581

86-
// Check for available hsm firmware update on middleware boot and update if available
87-
if hsmFirmware != nil {
88-
updateAvailable, err := middleware.HSMUpdateAvailable(hsm)
89-
if err != nil {
90-
log.Printf("Failed to fetch HSM version/update information: %s. Proceeding normally", err)
91-
}
92-
if updateAvailable {
93-
err = hsm.UpdateFirmware(*hsmFirmwareFile)
94-
if err != nil {
95-
log.Printf("Failed to update HSM firmware: %s", err)
96-
} else {
97-
err = middleware.BootHSMFirmware(hsm)
98-
if err != nil {
99-
log.Printf("Error: Failed to reboot back into HSM firmware. Base restart may be required: %s", err)
100-
}
101-
}
102-
}
103-
}
104-
10582
handlers := handlers.NewHandlers(middleware, *dataDir)
10683
log.Printf("Binding middleware api to port %s\n", *middlewarePort)
10784

middleware/src/configuration/configuration.go

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,7 @@ type Args struct {
2727
PrometheusURL string
2828
RedisMock bool
2929
RedisPort string
30+
HsmFirmwareFile string
3031
}
3132

3233
// Configuration holds the configuration options for the Middleware.
@@ -46,6 +47,7 @@ type Configuration struct {
4647
prometheusURL string
4748
redisMock bool
4849
redisPort string
50+
hsmFirmwareFile string
4951
}
5052

5153
// NewConfiguration returns a new Configuration instance.
@@ -66,6 +68,7 @@ func NewConfiguration(args Args) Configuration {
6668
prometheusURL: args.PrometheusURL,
6769
redisMock: args.RedisMock,
6870
redisPort: args.RedisPort,
71+
hsmFirmwareFile: args.HsmFirmwareFile,
6972
}
7073
return config
7174
}
@@ -130,3 +133,8 @@ func (config *Configuration) GetNetwork() string {
130133
func (config *Configuration) GetElectrsRPCPort() string {
131134
return config.electrsRPCPort
132135
}
136+
137+
// GetHsmFirmwareFile is a getter for the location of the HSM firmware file.
138+
func (config *Configuration) GetHsmFirmwareFile() string {
139+
return config.hsmFirmwareFile
140+
}

middleware/src/hsm/hsm.go

Lines changed: 3 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -205,17 +205,14 @@ func (hsm *HSM) InteractWithBootloader(f func(*bb02bootloader.Device)) error {
205205
return nil
206206
}
207207

208-
// UpdateFirmware reboots into Bootloader and executes the firmware update
209-
func (hsm *HSM) UpdateFirmware(hsmFirmwareFile string) error {
208+
// UpgradeFirmware reboots into Bootloader and executes the firmware upgrade
209+
func (hsm *HSM) UpgradeFirmware(hsmFirmwareFile string) error {
210210
hsmFirmwareBinary, err := ioutil.ReadFile(hsmFirmwareFile)
211211
if err != nil {
212212
return err
213213
}
214214
err = hsm.InteractWithBootloader(func(bootloader *bb02bootloader.Device) {
215215
err = bootloader.UpgradeFirmware(hsmFirmwareBinary)
216216
})
217-
if err != nil {
218-
return err
219-
}
220-
return nil
217+
return err
221218
}

middleware/src/middleware.go

Lines changed: 6 additions & 31 deletions
Original file line numberDiff line numberDiff line change
@@ -49,6 +49,7 @@ type Middleware struct {
4949
isMiddlewarePasswordSet bool
5050
isBaseSetupDone bool
5151

52+
hsm *hsm.HSM
5253
hsmFirmware *firmware.Device
5354
}
5455

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

8485
middleware.prometheusClient = prometheus.NewClient(middleware.config.GetPrometheusURL())
@@ -89,6 +90,9 @@ func NewMiddleware(config configuration.Configuration, hsmFirmware *firmware.Dev
8990
middleware.redisClient = redis.NewMockClient("")
9091
}
9192

93+
// Initialize the HSM firmware connection and install firmware upgrade if available
94+
middleware.initHSM()
95+
9296
err := middleware.checkMiddlewareSetup()
9397
if err != nil {
9498
log.Println("failed to update the middleware password set flag")
@@ -1172,32 +1176,3 @@ func (middleware *Middleware) VerifyAppMiddlewarePairing(channelHash []byte) (bo
11721176
}
11731177
return true, nil
11741178
}
1175-
1176-
// HSMUpdateAvailable checks the AvailableHSMVersion Redis and compares it to the running FW version
1177-
func (middleware *Middleware) HSMUpdateAvailable(hsm *hsm.HSM) (bool, error) {
1178-
availableHSMVersion, err := middleware.redisClient.GetString(redis.AvailableHSMVersion)
1179-
if err != nil {
1180-
return false, err
1181-
}
1182-
availableSemver, err := semver.NewSemVerFromString(availableHSMVersion)
1183-
if err != nil {
1184-
return false, err
1185-
}
1186-
currentVersion := middleware.hsmFirmware.Version()
1187-
if !currentVersion.AtLeast(availableSemver) {
1188-
log.Printf("BitBoxBase HSM update available from version: %s to version: %s", currentVersion, availableHSMVersion)
1189-
return true, nil
1190-
}
1191-
log.Printf("BitBoxBase HSM is up to date: %s", currentVersion)
1192-
return false, nil
1193-
}
1194-
1195-
// BootHSMFirmware boots into the HSM firmware, e.g., after a firmware udpate
1196-
func (middleware *Middleware) BootHSMFirmware(hsm *hsm.HSM) error {
1197-
var err error
1198-
middleware.hsmFirmware, err = hsm.WaitForFirmware()
1199-
if err != nil {
1200-
return err
1201-
}
1202-
return nil
1203-
}

middleware/src/util.go

Lines changed: 64 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@ import (
1818
"github.com/digitalbitbox/bitbox-base/middleware/src/redis"
1919
"github.com/digitalbitbox/bitbox-base/middleware/src/rpcmessages"
2020
"github.com/digitalbitbox/bitbox02-api-go/api/firmware/messages"
21+
"github.com/digitalbitbox/bitbox02-api-go/util/semver"
2122
)
2223

2324
// The util.go file includes utility functions for the Middleware.
@@ -411,3 +412,66 @@ func (middleware *Middleware) setHSMConfig() error {
411412
}
412413
return nil
413414
}
415+
416+
// upgradeFirmware upgrades the HSM firmare from the middleware
417+
func (middleware *Middleware) upgradeFirmware() error {
418+
err := middleware.hsm.UpgradeFirmware(middleware.config.GetHsmFirmwareFile())
419+
if err != nil {
420+
return err
421+
}
422+
middleware.hsmFirmware, err = middleware.hsm.WaitForFirmware()
423+
if err != nil {
424+
return err
425+
}
426+
return nil
427+
}
428+
429+
// hsmUpgradeAvailable checks the AvailableHSMVersion Redis and compares it to the running FW version
430+
func (middleware *Middleware) hsmUpgradeAvailable() (bool, error) {
431+
availableHSMVersion, err := middleware.redisClient.GetString(redis.AvailableHSMVersion)
432+
if err != nil {
433+
return false, err
434+
}
435+
availableSemver, err := semver.NewSemVerFromString(availableHSMVersion)
436+
if err != nil {
437+
return false, err
438+
}
439+
currentVersion := middleware.hsmFirmware.Version()
440+
if !currentVersion.AtLeast(availableSemver) {
441+
log.Printf("BitBoxBase HSM upgrade available from version: %s to version: %s", currentVersion, availableHSMVersion)
442+
return true, nil
443+
}
444+
log.Printf("BitBoxBase HSM is up to date: %s", currentVersion)
445+
return false, nil
446+
}
447+
448+
// initHSM tries to connect to the HSM firmware. On success, it checks if a firmware upgrade is available
449+
// and tries to apply it.
450+
// On failing to connect to the firmare, it tries to connect to the bootloader and re-install the firmware.
451+
// If that also fails, we continue without the HSM
452+
func (middleware *Middleware) initHSM() {
453+
var err error
454+
middleware.hsmFirmware, err = middleware.hsm.WaitForFirmware()
455+
if err != nil {
456+
// Failing to connect to the firmware may mean a FW upgrade had failed and we are in the bootloader
457+
// Try to re-do the upgrade via the bootloader before continuing without the HSM
458+
err = middleware.upgradeFirmware()
459+
if err != nil {
460+
log.Printf("Failed to connect to the HSM: %v. Continuing without HSM.", err)
461+
}
462+
} else {
463+
log.Printf("HSM serial port connected.")
464+
}
465+
if middleware.hsmFirmware != nil {
466+
upgradeAvailable, err := middleware.hsmUpgradeAvailable()
467+
if err != nil {
468+
log.Printf("Failed to fetch HSM version/upgrade information: %s. Proceeding normally", err)
469+
}
470+
if upgradeAvailable {
471+
err = middleware.upgradeFirmware()
472+
if err != nil {
473+
log.Printf("Failed to upgrade HSM firmware: %s", err)
474+
}
475+
}
476+
}
477+
}

0 commit comments

Comments
 (0)