@@ -114,15 +114,28 @@ type DeviceInfo struct {
114
114
SecurechipModel string `json:"securechipModel"`
115
115
}
116
116
117
+ // info is the data returned from the REQ_INFO api call.
118
+ type 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
+
117
130
// NewDevice creates a new instance of Device.
118
131
// version:
119
132
//
120
133
// Can be given if known at the time of instantiation, e.g. by parsing the USB HID product string.
121
134
// It must be provided if the version could be less than 4.3.0.
122
- // If nil, the version will be queried from the device using the OP_INFO api endpoint. Do this
135
+ // If nil, the version will be queried from the device using the REQ_INFO api endpoint. Do this
123
136
// when you are sure the firmware version is bigger or equal to 4.3.0.
124
137
//
125
- // product: same deal as with the version, after 4.3.0 it can be inferred by OP_INFO .
138
+ // product: same deal as with the version, after 4.3.0 it can be inferred by REQ_INFO .
126
139
func NewDevice (
127
140
version * semver.SemVer ,
128
141
product * common.Product ,
@@ -143,26 +156,26 @@ func NewDevice(
143
156
}
144
157
}
145
158
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 () (* info , error ) {
149
162
150
163
// CAREFUL: hwwInfo is called on the raw transport, not on device.rawQuery, which behaves
151
164
// differently depending on the firmware version. Reason: the version is not
152
165
// available (this call is used to get the version), so it must work for all firmware versions.
153
166
response , err := device .communication .Query ([]byte (hwwInfo ))
154
167
if err != nil {
155
- return nil , "" , false , err
168
+ return nil , err
156
169
}
157
170
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" )
160
173
}
161
174
versionStrLen , response := int (response [0 ]), response [1 :]
162
175
versionBytes , response := response [:versionStrLen ], response [versionStrLen :]
163
176
version , err := semver .NewSemVerFromString (string (versionBytes ))
164
177
if err != nil {
165
- return nil , "" , false , err
178
+ return nil , err
166
179
}
167
180
platformByte , response := response [0 ], response [1 :]
168
181
editionByte , response := response [0 ], response [1 :]
@@ -175,24 +188,40 @@ func (device *Device) info() (*semver.SemVer, common.Product, bool, error) {
175
188
}
176
189
editions , ok := products [platformByte ]
177
190
if ! ok {
178
- return nil , "" , false , errp .Newf ("unrecognized platform: %v" , platformByte )
191
+ return nil , errp .Newf ("unrecognized platform: %v" , platformByte )
179
192
}
180
193
product , ok := editions [editionByte ]
181
194
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 )
183
196
}
184
197
185
198
var unlocked bool
186
- unlockedByte := response [0 ]
199
+ unlockedByte , response := response [0 ], response [ 1 : ]
187
200
switch unlockedByte {
188
201
case 0x00 :
189
202
unlocked = false
190
203
case 0x01 :
191
204
unlocked = true
192
205
default :
193
- return nil , "" , false , errp .New ("unexpected reply" )
206
+ return nil , errp .New ("unexpected reply" )
207
+ }
208
+
209
+ deviceInfo := info {
210
+ version : version ,
211
+ product : product ,
212
+ unlocked : unlocked ,
194
213
}
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
196
225
}
197
226
198
227
// Version returns the firmware version.
@@ -203,30 +232,6 @@ func (device *Device) Version() *semver.SemVer {
203
232
return device .version
204
233
}
205
234
206
- // inferVersionAndProduct either sets the version and product by using OP_INFO if they were not
207
- // provided. In this case, the firmware is assumed to be >=v4.3.0, before that OP_INFO was not
208
- // available.
209
- func (device * Device ) inferVersionAndProduct () error {
210
- // The version has not been provided, so we try to get it from OP_INFO.
211
- 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
- }
217
- 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
-
224
- device .version = version
225
- device .product = & product
226
- }
227
- return nil
228
- }
229
-
230
235
// Init initializes the device. It changes the status to StatusRequireAppUpgrade if needed,
231
236
// otherwise performs the attestation check, unlock, and noise pairing. This call is blocking.
232
237
// After this call finishes, Status() will be either:
@@ -241,11 +246,30 @@ func (device *Device) Init() error {
241
246
device .channelHashDeviceVerified = false
242
247
device .sendCipher = nil
243
248
device .receiveCipher = nil
244
- device .changeStatus (StatusConnected )
245
249
246
- if err := device .inferVersionAndProduct (); err != nil {
247
- return err
250
+ if device .version == nil || device .version .AtLeast (semver .NewSemVer (9 , 2 , 0 )) {
251
+ deviceInfo , err := device .info ()
252
+ if err != nil {
253
+ return errp .New (
254
+ "REQ_INFO unavailable; need to provide version and product via the USB HID descriptor" )
255
+ }
256
+ device .log .Info (fmt .Sprintf ("REQ_INFO: version=%s, product=%s" , deviceInfo .version ,
257
+ deviceInfo .product ))
258
+ device .version = deviceInfo .version
259
+ device .product = & deviceInfo .product
260
+
261
+ if ! deviceInfo .version .AtLeast (semver .NewSemVer (9 , 20 , 0 )) {
262
+ device .changeStatus (StatusConnected )
263
+ } else if deviceInfo .unlocked {
264
+ device .changeStatus (StatusUnlocked )
265
+ } else if * deviceInfo .initialized {
266
+ // deviceInfo.initialized is not nil if version is at least 9.20.0.
267
+ device .changeStatus (StatusConnected )
268
+ } else {
269
+ device .changeStatus (StatusUninitialized )
270
+ }
248
271
}
272
+
249
273
if device .version .AtLeast (lowestNonSupportedFirmwareVersion ) {
250
274
device .changeStatus (StatusRequireAppUpgrade )
251
275
return nil
0 commit comments