Skip to content

Commit 0e6a62c

Browse files
committed
Merge branch 'op_hardware'
2 parents a80573d + bafd8c1 commit 0e6a62c

File tree

2 files changed

+147
-0
lines changed

2 files changed

+147
-0
lines changed

api/bootloader/device.go

Lines changed: 49 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
// Copyright 2018-2019 Shift Cryptosecurity AG
2+
// Copyright 2025 Shift Crypto AG
23
//
34
// Licensed under the Apache License, Version 2.0 (the "License");
45
// you may not use this file except in compliance with the License.
@@ -72,6 +73,7 @@ func toByte(b bool) byte {
7273
// Device provides the API to communicate with the BitBox02 bootloader.
7374
type Device struct {
7475
communication Communication
76+
version *semver.SemVer
7577
product common.Product
7678
status *Status
7779
onStatusChanged func(*Status)
@@ -86,6 +88,7 @@ func NewDevice(
8688
) *Device {
8789
return &Device{
8890
communication: communication,
91+
version: version,
8992
product: product,
9093
status: &Status{},
9194
onStatusChanged: onStatusChanged,
@@ -183,6 +186,52 @@ func (device *Device) ScreenRotate() error {
183186
return err
184187
}
185188

189+
// SecureChipModel enumerates the secure chip models in use.
190+
type SecureChipModel string
191+
192+
const (
193+
// SecureChipModelATECC refers to the ATECC chips (e.g. ATECC608A, ATECC608B).
194+
SecureChipModelATECC SecureChipModel = "ATECC"
195+
// SecureChipModelOptiga refers to the Optiga chip (e.g. Optiga Trust M V3).
196+
SecureChipModelOptiga SecureChipModel = "Optiga"
197+
)
198+
199+
// Hardware contains hardware info, returned by `Hardware()`.
200+
type Hardware struct {
201+
// SecureChipModel contains which securechip model is on the device.
202+
SecureChipModel SecureChipModel
203+
}
204+
205+
// Hardware returns hardware info.
206+
func (device *Device) Hardware() (*Hardware, error) {
207+
// OP_HARDWARE was introduced in v1.1.0.
208+
if !device.version.AtLeast(semver.NewSemVer(1, 1, 0)) {
209+
return &Hardware{
210+
SecureChipModel: SecureChipModelATECC,
211+
}, nil
212+
}
213+
response, err := device.query('W', nil)
214+
if err != nil {
215+
return nil, err
216+
}
217+
if len(response) < 1 {
218+
return nil, errp.New("unexpected response")
219+
}
220+
221+
var securechipModel SecureChipModel
222+
switch response[0] {
223+
case 0x00:
224+
securechipModel = SecureChipModelATECC
225+
case 0x01:
226+
securechipModel = SecureChipModelOptiga
227+
default:
228+
return nil, errp.Newf("Unrecognized securechip model: %d", response[0])
229+
}
230+
return &Hardware{
231+
SecureChipModel: securechipModel,
232+
}, nil
233+
}
234+
186235
func (device *Device) erase(firmwareNumChunks uint8) error {
187236
_, err := device.query('e', []byte{firmwareNumChunks})
188237
return err

cmd/bootloader/main.go

Lines changed: 98 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,98 @@
1+
// Copyright 2025 Shift Crypto AG
2+
//
3+
// Licensed under the Apache License, Version 2.0 (the "License");
4+
// you may not use this file except in compliance with the License.
5+
// You may obtain a copy of the License at
6+
//
7+
// http://www.apache.org/licenses/LICENSE-2.0
8+
//
9+
// Unless required by applicable law or agreed to in writing, software
10+
// distributed under the License is distributed on an "AS IS" BASIS,
11+
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12+
// See the License for the specific language governing permissions and
13+
// limitations under the License.
14+
15+
// Package main is a playground for devs to interact with a live device.
16+
package main
17+
18+
import (
19+
"fmt"
20+
"log"
21+
"regexp"
22+
23+
"github.com/BitBoxSwiss/bitbox02-api-go/api/bootloader"
24+
"github.com/BitBoxSwiss/bitbox02-api-go/api/common"
25+
"github.com/BitBoxSwiss/bitbox02-api-go/communication/u2fhid"
26+
"github.com/BitBoxSwiss/bitbox02-api-go/util/errp"
27+
"github.com/BitBoxSwiss/bitbox02-api-go/util/semver"
28+
"github.com/karalabe/hid"
29+
)
30+
31+
const (
32+
bitbox02VendorID = 0x03eb
33+
bitbox02ProductID = 0x2403
34+
35+
HARDENED = 0x80000000
36+
)
37+
38+
func errpanic(err error) {
39+
if err != nil {
40+
log.Fatalf("%+v", err)
41+
}
42+
}
43+
44+
func isBitBox02Bootloader(deviceInfo *hid.DeviceInfo) bool {
45+
return (deviceInfo.Product == common.BootloaderHIDProductStringStandard ||
46+
deviceInfo.Product == common.BootloaderHIDProductStringBTCOnly) &&
47+
deviceInfo.VendorID == bitbox02VendorID &&
48+
deviceInfo.ProductID == bitbox02ProductID &&
49+
(deviceInfo.UsagePage == 0xffff || deviceInfo.Interface == 0)
50+
}
51+
52+
func parseVersion(serial string) (*semver.SemVer, error) {
53+
match := regexp.MustCompile(`v([0-9]+\.[0-9]+\.[0-9]+)`).FindStringSubmatch(serial)
54+
if len(match) != 2 {
55+
return nil, errp.Newf("Could not find the version in '%s'.", serial)
56+
}
57+
version, err := semver.NewSemVerFromString(match[1])
58+
if err != nil {
59+
return nil, err
60+
}
61+
return version, err
62+
}
63+
64+
func main() {
65+
deviceInfo := func() *hid.DeviceInfo {
66+
infos, err := hid.Enumerate(0, 0)
67+
errpanic(err)
68+
for idx := range infos {
69+
di := &infos[idx]
70+
if di.Serial == "" || di.Product == "" {
71+
continue
72+
}
73+
if isBitBox02Bootloader(di) {
74+
return di
75+
}
76+
}
77+
panic("could no find a bitbox02")
78+
79+
}()
80+
81+
hidDevice, err := deviceInfo.Open()
82+
errpanic(err)
83+
const bitbox02BootloaderCMD = 0x80 + 0x40 + 0x03
84+
comm := u2fhid.NewCommunication(hidDevice, bitbox02BootloaderCMD)
85+
version, err := parseVersion(deviceInfo.Serial)
86+
errpanic(err)
87+
product, err := common.ProductFromHIDProductString(deviceInfo.Product)
88+
errpanic(err)
89+
device := bootloader.NewDevice(version, product, comm, func(*bootloader.Status) {})
90+
firmwareVersion, signingPubkeysVersion, err := device.Versions()
91+
errpanic(err)
92+
fmt.Println("Firmware monotonic version:", firmwareVersion)
93+
fmt.Println("Signing pubkeys monotonic version:", signingPubkeysVersion)
94+
fmt.Println("Product:", device.Product())
95+
hardware, err := device.Hardware()
96+
errpanic(err)
97+
fmt.Printf("Hardware: %+v\n", hardware)
98+
}

0 commit comments

Comments
 (0)