Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
20 changes: 18 additions & 2 deletions api/firmware/device.go
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@
package firmware

import (
"context"
"fmt"
"sync"

Expand Down Expand Up @@ -60,6 +61,10 @@ const (
// Device provides the API to communicate with the BitBox02.
type Device struct {
communication Communication
// This context represents the device lifetime, so Close can cancel a pending host-input dialog.
ctx context.Context //nolint:containedctx
cancel context.CancelFunc
closeOnce sync.Once
// firmware version.
version *semver.SemVer
product *common.Product
Expand Down Expand Up @@ -149,8 +154,11 @@ func NewDevice(
for _, opt := range opts {
opt(options)
}
ctx, cancel := context.WithCancel(context.Background())
return &Device{
communication: communication,
ctx: ctx,
cancel: cancel,
version: version,
product: product,
config: config,
Expand Down Expand Up @@ -290,7 +298,7 @@ func (device *Device) Init() error {

// Before 2.0.0, unlock was invoked automatically by the device before USB communication
// started.
if device.version.AtLeast(semver.NewSemVer(2, 0, 0)) {
if device.version.AtLeast(semver.NewSemVer(2, 0, 0)) && !device.supportsPairedUnlock() {
_, err := device.rawQuery([]byte(opUnlock))
if err != nil {
// Most likely the device has been unplugged.
Expand All @@ -307,19 +315,27 @@ func (device *Device) Init() error {
}

func (device *Device) changeStatus(status Status) {
device.mu.Lock()
device.status = status
device.mu.Unlock()
device.fireEvent(EventStatusChanged)
}

// Status returns the device state. See the Status* constants.
func (device *Device) Status() Status {
device.mu.RLock()
defer device.mu.RUnlock()
device.log.Debug(fmt.Sprintf("Device status: %v", device.status))
return device.status
}

// Close implements device.Device.
func (device *Device) Close() {
device.communication.Close()
// Disconnect handling and an interrupted unlock can both close the same device.
device.closeOnce.Do(func() {
device.cancel()
device.communication.Close()
})
}

// RootFingerprint returns the keystore's root fingerprint, which is the first 32 bits of the
Expand Down
4 changes: 4 additions & 0 deletions api/firmware/event.go
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,10 @@ const (
// EventAttestationCheckDone is fired when the attestation signature check is completed. In
// case of failure, the user should be alerted, before they enter the password.
EventAttestationCheckDone Event = "attestationCheckDone"

// EventPassphraseEntered is fired when entry on the device finishes, before confirmation.
// Requires firmware 9.28.0 or later.
EventPassphraseEntered Event = "passphraseEntered"
)

// SetOnEvent installs the callback which will be called with various events.
Expand Down
163 changes: 122 additions & 41 deletions api/firmware/messages/hww.pb.go

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

4 changes: 4 additions & 0 deletions api/firmware/messages/hww.proto
Original file line number Diff line number Diff line change
Expand Up @@ -60,6 +60,9 @@ message Request {
BluetoothRequest bluetooth = 29;
ChangePasswordRequest change_password = 30;
BitBoxSyncRequest bitbox_sync = 31;
UnlockRequest unlock = 32;
UnlockContinueRequest unlock_continue = 33;
UnlockHostInfoRequest unlock_host_info = 34;
}
}

Expand All @@ -84,5 +87,6 @@ message Response {
BIP85Response bip85 = 16;
BluetoothResponse bluetooth = 17;
BitBoxSyncResponse bitbox_sync = 18;
UnlockResponse unlock = 19;
}
}
Loading
Loading