Skip to content

tooling: Bump python tools to latest version #1454

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 1 commit into from
May 14, 2025
Merged
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
2 changes: 1 addition & 1 deletion .containerversion
Original file line number Diff line number Diff line change
@@ -1 +1 @@
47
48
2 changes: 1 addition & 1 deletion .pylintrc
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ docstring-min-length=10

[MESSAGES CONTROL]
# Black/pylint mismatch on code formatting
disable=bad-continuation, fixme, too-few-public-methods, duplicate-code, line-too-long, consider-using-f-string, too-many-locals, raise-missing-from, super-init-not-called, too-many-lines
disable=fixme, too-few-public-methods, duplicate-code, line-too-long, consider-using-f-string, too-many-locals, raise-missing-from, super-init-not-called, too-many-lines, broad-exception-raised, too-many-positional-arguments
good-names=i,j,tx

[TYPECHECK]
Expand Down
10 changes: 5 additions & 5 deletions Dockerfile
Original file line number Diff line number Diff line change
Expand Up @@ -95,11 +95,11 @@ RUN rm /tmp/requirements.txt

# Python modules for CI
RUN python3 -m pip install --upgrade \
pylint==2.13.9 \
pylint-protobuf==0.20.2 \
black==22.3.0 \
mypy==0.960 \
mypy-protobuf==3.2.0
pylint==3.3.7 \
pylint-protobuf==0.22.0 \
black==25.1.0 \
mypy==1.15.0 \
mypy-protobuf==3.6.0

# Python modules for packaging
RUN python3 -m pip install --upgrade \
Expand Down
2 changes: 1 addition & 1 deletion py/bitbox02/bitbox02/bitbox02/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
""" Library to interact with a BitBox02 device. """
"""Library to interact with a BitBox02 device."""

from __future__ import print_function
import sys
Expand Down
6 changes: 3 additions & 3 deletions py/bitbox02/bitbox02/bitbox02/bootloader.py
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
""" Interact with a BitBox02 bootloader. """
"""Interact with a BitBox02 bootloader."""

import struct
import typing
Expand Down Expand Up @@ -160,7 +160,7 @@ def show_firmware_hash_enabled(self) -> bool:
"""
Returns whether the bootloader will automatically show the firmware hash on boot.
"""
return bool(self._query(b"H\xFF")[0])
return bool(self._query(b"H\xff")[0])

def set_show_firmware_hash(self, enable: bool) -> None:
"""
Expand Down Expand Up @@ -233,7 +233,7 @@ def erased(self) -> bool:
# We check by comparing the device reported firmware hash.
# If erased, the firmware is all '\xFF'.
firmware_v, _ = self.versions()
empty_firmware = struct.pack("<I", firmware_v) + b"\xFF" * MAX_FIRMWARE_SIZE
empty_firmware = struct.pack("<I", firmware_v) + b"\xff" * MAX_FIRMWARE_SIZE
empty_firmware_hash = hashlib.sha256(hashlib.sha256(empty_firmware).digest()).digest()
reported_firmware_hash, _ = self.get_hashes()
return empty_firmware_hash == reported_firmware_hash
Expand Down
4 changes: 3 additions & 1 deletion py/bitbox02/bitbox02/communication/bitbox_api_protocol.py
Original file line number Diff line number Diff line change
Expand Up @@ -240,7 +240,7 @@ def __init__(self, need_atleast: semver.VersionInfo):
class BitBoxNoiseConfig:
"""Stores Functions required setup a noise connection"""

# pylint: disable=no-self-use,unused-argument
# pylint: disable=unused-argument
def show_pairing(self, code: str, device_response: Callable[[], bool]) -> bool:
"""
Returns True if the user confirms the pairing (both device and host).
Expand Down Expand Up @@ -546,6 +546,8 @@ def __init__(
edition = BitBox02Edition.MULTI
elif device_info["product_string"] in (BITBOX02BTC, BITBOX02PLUS_BTC):
edition = BitBox02Edition.BTCONLY
else:
raise Exception("Invalid product string")
else:
version, _, edition, _, _ = self.get_info(transport)

Expand Down
22 changes: 8 additions & 14 deletions py/bitbox02/bitbox02/communication/communication.py
Original file line number Diff line number Diff line change
Expand Up @@ -26,31 +26,25 @@ class TransportLayer(Protocol):
transmitting byte strings.
"""

# pylint: disable=unused-argument,no-self-use
# pylint: disable=unused-argument
def write(self, data: bytes, endpoint: int, cid: int) -> None:
"""Sends a frame of data to the specified endpoint"""

def read(self, endpoint: int, cid: int) -> bytes:
...
def read(self, endpoint: int, cid: int) -> bytes: ...

def query(self, data: bytes, endpoint: int, cid: int) -> bytes:
self.write(data, endpoint, cid)
return self.read(endpoint, cid)

def generate_cid(self) -> int:
...
def generate_cid(self) -> int: ...

def close(self) -> None:
...
def close(self) -> None: ...


class PhysicalLayer(Protocol):
# pylint: disable=unused-argument,no-self-use
def write(self, data: bytes) -> None:
...
# pylint: disable=unused-argument
def write(self, data: bytes) -> None: ...

def read(self, size: int, timeout_ms: int) -> bytes:
...
def read(self, size: int, timeout_ms: int) -> bytes: ...

def close(self) -> None:
...
def close(self) -> None: ...
4 changes: 2 additions & 2 deletions py/bitbox02/bitbox02/communication/u2fhid/u2fhid.py
Original file line number Diff line number Diff line change
Expand Up @@ -101,7 +101,7 @@ def write(self, data: bytes, endpoint: int, cid: int) -> None:
b"\0"
+ struct.pack(">IBH", cid, endpoint, data_len)
+ buf
+ b"\xEE" * (USB_REPORT_SIZE - 7 - len(buf))
+ b"\xee" * (USB_REPORT_SIZE - 7 - len(buf))
)
else:
# CONT frame
Expand All @@ -110,7 +110,7 @@ def write(self, data: bytes, endpoint: int, cid: int) -> None:
b"\0"
+ struct.pack(">IB", cid, seq)
+ buf
+ b"\xEE" * (USB_REPORT_SIZE - 5 - len(buf))
+ b"\xee" * (USB_REPORT_SIZE - 5 - len(buf))
)
seq += 1
idx += len(buf)
Expand Down
3 changes: 2 additions & 1 deletion py/requirements.txt
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
# Modules needed to run scripts in this directory
tzlocal>=1.5,<2.0
# until bitbox02 is released it must be installed from py/bitbox02
types-tzlocal
bitbox02
requests
types-requests
118 changes: 61 additions & 57 deletions py/send_message.py
Original file line number Diff line number Diff line change
Expand Up @@ -27,10 +27,10 @@
import textwrap
import json

import requests # type: ignore
import requests
import hid
import semver
from tzlocal import get_localzone # type: ignore
from tzlocal import get_localzone

from bitbox02 import util
from bitbox02 import bitbox02
Expand Down Expand Up @@ -58,11 +58,11 @@ def eprint(*args: Any, **kwargs: Any) -> None:


def ask_user(
choices: Sequence[Tuple[str, Callable[[], None]]]
choices: Sequence[Tuple[str, Callable[[], None]]],
) -> Union[Callable[[], None], bool, None]:
"""Ask user to choose one of the choices, q quits"""
print("What would you like to do?")
for (idx, choice) in enumerate(choices):
for idx, choice in enumerate(choices):
print(f"- ({idx+1}) {choice[0]}")
print("- (q) Quit")
ans_str = input("")
Expand Down Expand Up @@ -253,7 +253,7 @@ def _print_backups(self, backups: Optional[Sequence[bitbox02.Backup]] = None) ->
print("No backups found.")
return
fmt = "%Y-%m-%d %H:%M:%S %z"
for (i, (backup_id, backup_name, date)) in enumerate(backups):
for i, (backup_id, backup_name, date) in enumerate(backups):
date = local_timezone.localize(date)
date_str = date.strftime(fmt)
print(f"[{i+1}] Backup Name: {backup_name}, Time: {date_str}, ID: {backup_id}")
Expand Down Expand Up @@ -758,7 +758,7 @@ def _sign_btc_policy(self) -> None:
bip44_account: int = 0 + HARDENED
account_keypath = [48 + HARDENED, 1 + HARDENED, bip44_account, 3 + HARDENED]
inputs, outputs = _btc_demo_inputs_outputs(bip44_account)
for (i, inp) in enumerate(inputs):
for i, inp in enumerate(inputs):
inp["keypath"] = account_keypath + [0, i]
inp["script_config_index"] = 0
assert isinstance(outputs[0], bitbox02.BTCOutputInternal)
Expand Down Expand Up @@ -789,7 +789,10 @@ def _sign_btc_tx_from_raw(self) -> None:

def get(tx_id: str) -> Any:
return requests.get(
"https://api.blockchair.com/bitcoin/testnet/dashboards/transaction/{}".format(tx_id)
"https://api.blockchair.com/bitcoin/testnet/dashboards/transaction/{}".format(
tx_id
),
timeout=30,
).json()["data"][tx_id]

tx_id = input("Paste a btc testnet tx ID: ").strip()
Expand Down Expand Up @@ -1711,6 +1714,9 @@ def read(self, size: int, timeout_ms: int) -> bytes:
print(f"Read from the simulator:\n{res.hex()}")
return res

def close(self) -> None:
return None

def __del__(self) -> None:
print("Simulator quit")
self.client_socket.close()
Expand Down Expand Up @@ -1749,67 +1755,65 @@ def connect_to_usb_bitbox(debug: bool, use_cache: bool) -> int:
except devices.NoneFoundException:
print("Neither bitbox nor bootloader found.")
return 1
else:
hid_device = hid.device()
hid_device.open_path(bootloader["path"])
bootloader_connection = bitbox02.Bootloader(u2fhid.U2FHid(hid_device), bootloader)
boot_app = SendMessageBootloader(bootloader_connection)
return boot_app.run()
else:
hid_device = hid.device()
hid_device.open_path(bootloader["path"])
bootloader_connection = bitbox02.Bootloader(u2fhid.U2FHid(hid_device), bootloader)
boot_app = SendMessageBootloader(bootloader_connection)
return boot_app.run()

def show_pairing(code: str, device_response: Callable[[], bool]) -> bool:
print("Please compare and confirm the pairing code on your BitBox02:")
print(code)
if not device_response():
return False
return input("Accept pairing? [y]/n: ").strip() != "n"
def show_pairing(code: str, device_response: Callable[[], bool]) -> bool:
print("Please compare and confirm the pairing code on your BitBox02:")
print(code)
if not device_response():
return False
return input("Accept pairing? [y]/n: ").strip() != "n"

class NoiseConfig(util.NoiseConfigUserCache):
"""NoiseConfig extends NoiseConfigUserCache"""
class NoiseConfig(util.NoiseConfigUserCache):
"""NoiseConfig extends NoiseConfigUserCache"""

def __init__(self) -> None:
super().__init__("shift/send_message")
def __init__(self) -> None:
super().__init__("shift/send_message")

def show_pairing(self, code: str, device_response: Callable[[], bool]) -> bool:
return show_pairing(code, device_response)
def show_pairing(self, code: str, device_response: Callable[[], bool]) -> bool:
return show_pairing(code, device_response)

def attestation_check(self, result: bool) -> None:
if result:
print("Device attestation PASSED")
else:
print("Device attestation FAILED")
def attestation_check(self, result: bool) -> None:
if result:
print("Device attestation PASSED")
else:
print("Device attestation FAILED")

class NoiseConfigNoCache(bitbox_api_protocol.BitBoxNoiseConfig):
"""NoiseConfig extends BitBoxNoiseConfig"""
class NoiseConfigNoCache(bitbox_api_protocol.BitBoxNoiseConfig):
"""NoiseConfig extends BitBoxNoiseConfig"""

def show_pairing(self, code: str, device_response: Callable[[], bool]) -> bool:
return show_pairing(code, device_response)
def show_pairing(self, code: str, device_response: Callable[[], bool]) -> bool:
return show_pairing(code, device_response)

def attestation_check(self, result: bool) -> None:
if result:
print("Device attestation PASSED")
else:
print("Device attestation FAILED")
def attestation_check(self, result: bool) -> None:
if result:
print("Device attestation PASSED")
else:
print("Device attestation FAILED")

if use_cache:
config: bitbox_api_protocol.BitBoxNoiseConfig = NoiseConfig()
else:
config = NoiseConfigNoCache()
if use_cache:
config: bitbox_api_protocol.BitBoxNoiseConfig = NoiseConfig()
else:
config = NoiseConfigNoCache()

hid_device = hid.device()
hid_device.open_path(bitbox["path"])
bitbox_connection = bitbox02.BitBox02(
transport=u2fhid.U2FHid(hid_device), device_info=bitbox, noise_config=config
)
try:
bitbox_connection.check_min_version()
except FirmwareVersionOutdatedException as exc:
print("WARNING: ", exc)
hid_device = hid.device()
hid_device.open_path(bitbox["path"])
bitbox_connection = bitbox02.BitBox02(
transport=u2fhid.U2FHid(hid_device), device_info=bitbox, noise_config=config
)
try:
bitbox_connection.check_min_version()
except FirmwareVersionOutdatedException as exc:
print("WARNING: ", exc)

if debug:
print("Device Info:")
pprint.pprint(bitbox)
return SendMessage(bitbox_connection, debug).run()
if debug:
print("Device Info:")
pprint.pprint(bitbox)
return SendMessage(bitbox_connection, debug).run()


def main() -> int:
Expand Down
11 changes: 6 additions & 5 deletions py/u2f/u2f.py
Original file line number Diff line number Diff line change
Expand Up @@ -114,9 +114,8 @@ def _der_to_sig(der: bytes) -> Tuple[int, bytes]:


class U2FSender(Protocol):
# pylint: disable=unused-argument,no-self-use
def u2fhid_msg(self, msg: bytes) -> bytes:
...
# pylint: disable=unused-argument
def u2fhid_msg(self, msg: bytes) -> bytes: ...


class APDU:
Expand Down Expand Up @@ -192,6 +191,8 @@ def _get_cert(msg: bytes) -> Tuple[int, bytes]:
elif msg[1] == 0x82:
cert_len = msg[2] * 256 + msg[3]
header_len = 4
else:
raise Exception("invalid magic number")

return (cert_len + header_len, bytes(msg[: cert_len + header_len]))

Expand Down Expand Up @@ -257,7 +258,7 @@ def __init__(self, app_id: str, bogus: str = ""):
self._apdu = APDU(U2F_REGISTER, 0x00, 0x00, len(data), data)

def send(self, bitbox: U2FSender) -> RegistrationResponse:
response_bytes = bitbox.u2fhid_msg(self._apdu.__bytes__())
response_bytes = bitbox.u2fhid_msg(bytes(self._apdu))
_status_code_to_exception(response_bytes[-2:])

return RegistrationResponse(response_bytes, self._challenge_parameter, self._app_parameter)
Expand Down Expand Up @@ -323,7 +324,7 @@ def __init__(self, app_id: str, key_handle: bytes):
self._apdu = APDU(U2F_AUTHENTICATE, 0x03, 0x00, len(data), data)

def send(self, bitbox: U2FSender) -> AuthenticationResponse:
response_bytes = bitbox.u2fhid_msg(self._apdu.__bytes__())
response_bytes = bitbox.u2fhid_msg(bytes(self._apdu))
_status_code_to_exception(response_bytes[-2:])
return AuthenticationResponse(
response_bytes, self._app_parameter, self._challenge_parameter
Expand Down
2 changes: 1 addition & 1 deletion scripts/lint-python
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@
# Run pylint on a subset of the code, alternatively pass files as arguments.

# Exit on error
set -e
set -xe

# Exit on pipe fail
set -o pipefail
Expand Down