18
18
import io
19
19
import math
20
20
import hashlib
21
+ import enum
22
+ from typing import TypedDict
21
23
22
24
from bitbox02 .communication import TransportLayer
23
- from bitbox02 .communication .devices import DeviceInfo
25
+
26
+ from bitbox02 .communication .devices import DeviceInfo , parse_device_version
24
27
25
28
BOOTLOADER_CMD = 0x80 + 0x40 + 0x03
26
29
NUM_ROOT_KEYS = 3
44
47
SIGDATA_LEN = SIGNING_PUBKEYS_DATA_LEN + FIRMWARE_DATA_LEN
45
48
46
49
50
+ class SecureChipModel (enum .Enum ):
51
+ """Secure chip model variants for the BitBox02 platform."""
52
+
53
+ ATECC = "ATECC"
54
+ OPTIGA = "Optiga"
55
+
56
+
57
+ class Hardware (TypedDict ):
58
+ """Hardware configuration containing secure chip model information."""
59
+
60
+ secure_chip_model : SecureChipModel
61
+
62
+
47
63
def parse_signed_firmware (firmware : bytes ) -> typing .Tuple [bytes , bytes , bytes ]:
48
64
"""
49
65
Split raw firmware bytes into magic, sigdata and firmware
@@ -75,6 +91,10 @@ def __init__(self, transport: TransportLayer, device_info: DeviceInfo):
75
91
"bb02btc-bootloader" : SIGDATA_MAGIC_BTCONLY ,
76
92
"bitboxbase-bootloader" : SIGDATA_MAGIC_BITBOXBASE_STANDARD ,
77
93
}.get (device_info ["product_string" ])
94
+ self .version = parse_device_version (device_info ["serial_number" ])
95
+ # Delete the prelease part, as it messes with the comparison (e.g. 3.0.0-pre < 3.0.0 is
96
+ # True, but the 3.0.0-pre has already the same API breaking changes like 3.0.0...).
97
+ self .version = self .version .replace (prerelease = None )
78
98
assert self .expected_magic
79
99
80
100
def _query (self , msg : bytes ) -> bytes :
@@ -94,6 +114,25 @@ def versions(self) -> typing.Tuple[int, int]:
94
114
firmware_v , signing_pubkeys_v = struct .unpack ("<II" , response [:8 ])
95
115
return firmware_v , signing_pubkeys_v
96
116
117
+ def hardware (self ) -> Hardware :
118
+ """
119
+ Returns (hardware variant).
120
+ """
121
+ secure_chip : SecureChipModel = SecureChipModel .ATECC
122
+
123
+ # Previous bootloader versions do not support the call and have ATECC SC.
124
+ if self .version >= "1.1.0" :
125
+ response = self ._query (b"W" )
126
+ response_code = response [:1 ]
127
+
128
+ if response_code == b"\x00 " :
129
+ secure_chip = SecureChipModel .ATECC
130
+ elif response_code == b"\x01 " :
131
+ secure_chip = SecureChipModel .OPTIGA
132
+ else :
133
+ raise ValueError (f"Unrecognized securechip model: { response_code !r} " )
134
+ return {"secure_chip_model" : secure_chip }
135
+
97
136
def get_hashes (
98
137
self , display_firmware_hash : bool = False , display_signing_keydata_hash : bool = False
99
138
) -> typing .Tuple [bytes , bytes ]:
0 commit comments