@@ -114,6 +114,19 @@ type DeviceInfo struct {
114114 SecurechipModel string `json:"securechipModel"`
115115}
116116
117+ // DeviceInfoREQ_INFO is the data returned from the REQ_INFO api call.
118+ type DeviceInfoREQ_INFO struct {
119+ // Device firmware version. REQ_INFO is supported since v4.3.0 which means this field will
120+ // always be at least v4.3.0.
121+ version * semver.SemVer
122+ // Device Platform/Edition e.g. "bitbox02-btconly".
123+ product common.Product
124+ // Device unlocked status, true if device is unlocked.
125+ unlocked bool
126+ // Device initialized status, true if device is seeded and backup has been stored.
127+ initialized * bool
128+ }
129+
117130// NewDevice creates a new instance of Device.
118131// version:
119132//
@@ -143,26 +156,26 @@ func NewDevice(
143156 }
144157}
145158
146- // info uses the opInfo api endpoint to learn about the version, platform/edition, and unlock
147- // status (true if unlocked).
148- func (device * Device ) info () (* semver. SemVer , common. Product , bool , error ) {
159+ // info uses the opInfo api endpoint to learn about the version, platform/edition, unlock
160+ // status (true if unlocked), and initialized status (true if device can be unlocked/is unlocked) .
161+ func (device * Device ) info () (* DeviceInfoREQ_INFO , error ) {
149162
150163 // CAREFUL: hwwInfo is called on the raw transport, not on device.rawQuery, which behaves
151164 // differently depending on the firmware version. Reason: the version is not
152165 // available (this call is used to get the version), so it must work for all firmware versions.
153166 response , err := device .communication .Query ([]byte (hwwInfo ))
154167 if err != nil {
155- return nil , "" , false , err
168+ return nil , err
156169 }
157170
158- if len (response ) < 4 {
159- return nil , "" , false , errp .New ("unexpected response" )
171+ if len (response ) < 5 {
172+ return nil , errp .New ("unexpected response" )
160173 }
161174 versionStrLen , response := int (response [0 ]), response [1 :]
162175 versionBytes , response := response [:versionStrLen ], response [versionStrLen :]
163176 version , err := semver .NewSemVerFromString (string (versionBytes ))
164177 if err != nil {
165- return nil , "" , false , err
178+ return nil , err
166179 }
167180 platformByte , response := response [0 ], response [1 :]
168181 editionByte , response := response [0 ], response [1 :]
@@ -175,24 +188,40 @@ func (device *Device) info() (*semver.SemVer, common.Product, bool, error) {
175188 }
176189 editions , ok := products [platformByte ]
177190 if ! ok {
178- return nil , "" , false , errp .Newf ("unrecognized platform: %v" , platformByte )
191+ return nil , errp .Newf ("unrecognized platform: %v" , platformByte )
179192 }
180193 product , ok := editions [editionByte ]
181194 if ! ok {
182- return nil , "" , false , errp .Newf ("unrecognized platform/edition: %v/%v" , platformByte , editionByte )
195+ return nil , errp .Newf ("unrecognized platform/edition: %v/%v" , platformByte , editionByte )
183196 }
184197
185198 var unlocked bool
186- unlockedByte := response [0 ]
199+ unlockedByte , response := response [0 ], response [ 1 : ]
187200 switch unlockedByte {
188201 case 0x00 :
189202 unlocked = false
190203 case 0x01 :
191204 unlocked = true
192205 default :
193- return nil , "" , false , errp .New ("unexpected reply" )
206+ return nil , errp .New ("unexpected reply" )
207+ }
208+
209+ deviceInfo := DeviceInfoREQ_INFO {
210+ version : version ,
211+ product : product ,
212+ unlocked : unlocked ,
194213 }
195- return version , product , unlocked , nil
214+
215+ // Since 9.20.0 REQ_INFO responds with a byte for the initialized status.
216+ if version .AtLeast (semver .NewSemVer (9 , 20 , 0 )) {
217+ initialized := response [0 ] == 0x01
218+ if response [0 ] != 0x00 && response [0 ] != 0x01 {
219+ return nil , errp .New ("unexpected reply" )
220+ }
221+ deviceInfo .initialized = & initialized
222+ }
223+
224+ return & deviceInfo , nil
196225}
197226
198227// Version returns the firmware version.
@@ -206,25 +235,13 @@ func (device *Device) Version() *semver.SemVer {
206235// inferVersionAndProduct either sets the version and product by using OP_INFO if they were not
207236// provided. In this case, the firmware is assumed to be >=v4.3.0, before that OP_INFO was not
208237// available.
209- func (device * Device ) inferVersionAndProduct () error {
238+ func (device * Device ) inferVersionAndProduct (version * semver. SemVer , product common. Product ) {
210239 // The version has not been provided, so we try to get it from OP_INFO.
211240 if device .version == nil {
212- version , product , _ , err := device .info ()
213- if err != nil {
214- return errp .New (
215- "OP_INFO unavailable; need to provide version and product via the USB HID descriptor" )
216- }
217241 device .log .Info (fmt .Sprintf ("OP_INFO: version=%s, product=%s" , version , product ))
218-
219- // sanity check
220- if ! version .AtLeast (semver .NewSemVer (4 , 3 , 0 )) {
221- return errp .New ("OP_INFO is not supposed to exist below v4.3.0" )
222- }
223-
224242 device .version = version
225243 device .product = & product
226244 }
227- return nil
228245}
229246
230247// Init initializes the device. It changes the status to StatusRequireAppUpgrade if needed,
@@ -241,11 +258,23 @@ func (device *Device) Init() error {
241258 device .channelHashDeviceVerified = false
242259 device .sendCipher = nil
243260 device .receiveCipher = nil
244- device .changeStatus (StatusConnected )
245261
246- if err := device .inferVersionAndProduct (); err != nil {
247- return err
262+ if device .version == nil || device .version .AtLeast (semver .NewSemVer (4 , 3 , 0 )) {
263+ deviceInfo , err := device .info ()
264+ if err != nil {
265+ return errp .New (
266+ "OP_INFO unavailable; need to provide version and product via the USB HID descriptor" )
267+ }
268+ // Does nothing if device.version == nil
269+ device .inferVersionAndProduct (deviceInfo .version , deviceInfo .product )
270+
271+ if deviceInfo .unlocked || (deviceInfo .initialized != nil && * deviceInfo .initialized ) {
272+ device .changeStatus (StatusInitialized )
273+ } else {
274+ device .changeStatus (StatusUninitialized )
275+ }
248276 }
277+
249278 if device .version .AtLeast (lowestNonSupportedFirmwareVersion ) {
250279 device .changeStatus (StatusRequireAppUpgrade )
251280 return nil
0 commit comments