Skip to content
Open
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
15 changes: 15 additions & 0 deletions .github/workflows/ci.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -55,6 +55,21 @@ jobs:
run: make install
- name: Check code static typing
run: make check-typing
lint-typing-ty:
name: Check static typing with ty
runs-on: ubuntu-latest
container: python:3.10-slim
steps:
- name: Checkout repository
uses: actions/checkout@v4
- name: Install required packages
run: apt update && apt install -y ${REQUIRED_PACKAGES}
- name: Install Poetry
run: pip install "${POETRY_SPEC}"
- name: Create virtual environment
run: make install
- name: Check code static typing
run: poetry run ty check
lint-poetry:
name: Check poetry configuration
runs-on: ubuntu-latest
Expand Down
29 changes: 28 additions & 1 deletion poetry.lock
Original file line number Diff line number Diff line change
Expand Up @@ -1320,6 +1320,33 @@ files = [
{file = "tomli-2.2.1.tar.gz", hash = "sha256:cd45e1dc79c835ce60f7404ec8119f2eb06d38b1deba146f07ced3bbc44505ff"},
]

[[package]]
name = "ty"
version = "0.0.16"
description = "An extremely fast Python type checker, written in Rust."
optional = false
python-versions = ">=3.8"
groups = ["dev"]
files = [
{file = "ty-0.0.16-py3-none-linux_armv6l.whl", hash = "sha256:6d8833b86396ed742f2b34028f51c0e98dbf010b13ae4b79d1126749dc9dab15"},
{file = "ty-0.0.16-py3-none-macosx_10_12_x86_64.whl", hash = "sha256:934c0055d3b7f1cf3c8eab78c6c127ef7f347ff00443cef69614bda6f1502377"},
{file = "ty-0.0.16-py3-none-macosx_11_0_arm64.whl", hash = "sha256:b55e8e8733b416d914003cd22e831e139f034681b05afed7e951cc1a5ea1b8d4"},
{file = "ty-0.0.16-py3-none-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:feccae8f4abd6657de111353bd604f36e164844466346eb81ffee2c2b06ea0f0"},
{file = "ty-0.0.16-py3-none-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:1cad5e29d8765b92db5fa284940ac57149561f3f89470b363b9aab8a6ce553b0"},
{file = "ty-0.0.16-py3-none-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:86f28797c7dc06f081238270b533bf4fc8e93852f34df49fb660e0b58a5cda9a"},
{file = "ty-0.0.16-py3-none-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:be971a3b42bcae44d0e5787f88156ed2102ad07558c05a5ae4bfd32a99118e66"},
{file = "ty-0.0.16-py3-none-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:3c9f982b7c4250eb91af66933f436b3a2363c24b6353e94992eab6551166c8b7"},
{file = "ty-0.0.16-py3-none-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:d122edf85ce7bdf6f85d19158c991d858fc835677bd31ca46319c4913043dc84"},
{file = "ty-0.0.16-py3-none-musllinux_1_2_aarch64.whl", hash = "sha256:497ebdddbb0e35c7758ded5aa4c6245e8696a69d531d5c9b0c1a28a075374241"},
{file = "ty-0.0.16-py3-none-musllinux_1_2_armv7l.whl", hash = "sha256:e1e0ac0837bde634b030243aeba8499383c0487e08f22e80f5abdacb5b0bd8ce"},
{file = "ty-0.0.16-py3-none-musllinux_1_2_i686.whl", hash = "sha256:1216c9bcca551d9f89f47a817ebc80e88ac37683d71504e5509a6445f24fd024"},
{file = "ty-0.0.16-py3-none-musllinux_1_2_x86_64.whl", hash = "sha256:221bbdd2c6ee558452c96916ab67fcc465b86967cf0482e19571d18f9c831828"},
{file = "ty-0.0.16-py3-none-win32.whl", hash = "sha256:d52c4eb786be878e7514cab637200af607216fcc5539a06d26573ea496b26512"},
{file = "ty-0.0.16-py3-none-win_amd64.whl", hash = "sha256:f572c216aa8ecf79e86589c6e6d4bebc01f1f3cb3be765c0febd942013e1e73a"},
{file = "ty-0.0.16-py3-none-win_arm64.whl", hash = "sha256:430eadeb1c0de0c31ef7bef9d002bdbb5f25a31e3aad546f1714d76cd8da0a87"},
{file = "ty-0.0.16.tar.gz", hash = "sha256:a999b0db6aed7d6294d036ebe43301105681e0c821a19989be7c145805d7351c"},
]

[[package]]
name = "typer"
version = "0.16.0"
Expand Down Expand Up @@ -1502,4 +1529,4 @@ files = [
[metadata]
lock-version = "2.1"
python-versions = ">=3.10, <4"
content-hash = "1a20a18c696c423e7b3333b9218c5ca13d6f16ac69168fed5c72a6e0fc362674"
content-hash = "446cf7daba9ad1c2a5e272db9ece7d87dd7bf4af460144b1fce8f530cdd529d8"
8 changes: 8 additions & 0 deletions pyproject.toml
Original file line number Diff line number Diff line change
Expand Up @@ -50,6 +50,7 @@ mypy = "^1.4"
rstcheck = { version = "^6", extras = ["sphinx"] }
ruff = "^0.14"
sphinx = "^7"
ty = "^0.0.16"
types-protobuf = ">=5.26, <7"
types-requests = "^2.16"
typing-extensions = "^4.1"
Expand Down Expand Up @@ -97,3 +98,10 @@ split-on-trailing-comma = false

[tool.ruff.lint.mccabe]
max-complexity = 30

[tool.ty.environment]
extra-paths = ["stubs"]

[tool.ty.rules]
# some type: ignore comments are only needed for mypy so ty should not report them as unused
unused-type-ignore-comment = "ignore"
8 changes: 4 additions & 4 deletions src/nitrokey/trussed/_bootloader/lpc55.py
Original file line number Diff line number Diff line change
Expand Up @@ -100,7 +100,7 @@ def _list_vid_pid(cls: type[T], vid: int, pid: int) -> list[T]:
try:
devices.append(cls(device))
except ValueError:
logger.warn(
logger.warning(
f"Invalid Nitrokey 3 LPC55 bootloader returned by enumeration: {device}"
)
return devices
Expand All @@ -109,16 +109,16 @@ def _list_vid_pid(cls: type[T], vid: int, pid: int) -> list[T]:
def _open(cls: type[T], path: str) -> Optional[T]:
devices = UsbDevice.enumerate(path=path)
if len(devices) == 0:
logger.warn(f"No HID device at {path}")
logger.warning(f"No HID device at {path}")
return None
if len(devices) > 1:
logger.warn(f"Multiple HID devices at {path}: {devices}")
logger.warning(f"Multiple HID devices at {path}: {devices}")
return None

try:
return cls(devices[0])
except ValueError:
logger.warn(f"No Nitrokey 3 bootloader at path {path}", exc_info=sys.exc_info())
logger.warning(f"No Nitrokey 3 bootloader at path {path}", exc_info=sys.exc_info())
return None


Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -470,10 +470,10 @@ class AvailableCommandsValue(PropertyValueBase):
__slots__ = ("value",)

@property
def tags(self) -> List[str]:
def tags(self) -> List[int]:
"""List of tags representing Available commands."""
return [
cmd_tag.tag # type: ignore
cmd_tag.tag
for cmd_tag in CommandTag
if cmd_tag.tag > 0 and (1 << cmd_tag.tag - 1) & self.value
]
Expand All @@ -492,11 +492,13 @@ def __contains__(self, item: int) -> bool:

def to_str(self) -> str:
"""Get stringified property representation."""
return [
cmd_tag.label # type: ignore
for cmd_tag in CommandTag
if cmd_tag.tag > 0 and (1 << cmd_tag.tag - 1) & self.value
]
return ", ".join(
[
cmd_tag.label
for cmd_tag in CommandTag
if cmd_tag.tag > 0 and (1 << cmd_tag.tag - 1) & self.value
]
)


class IrqNotifierPinValue(PropertyValueBase):
Expand Down
14 changes: 8 additions & 6 deletions src/nitrokey/trussed/_bootloader/nrf52.py
Original file line number Diff line number Diff line change
Expand Up @@ -141,25 +141,25 @@ def reboot(self) -> bool:
def uuid(self) -> Optional[Uuid]:
return Uuid(self._uuid)

def update(self, data: bytes, callback: Optional[ProgressCallback] = None) -> None:
def update(self, image: bytes, callback: Optional[ProgressCallback] = None) -> None:
# based on https://github.com/NordicSemiconductor/pc-nrfutil/blob/1caa347b1cca3896f4695823f48abba15fbef76b/nordicsemi/dfu/dfu.py
# we have to implement this ourselves because we want to read the files
# from memory, not from the filesystem

image = Image.parse(data, self._signature_keys)
parsed_image = Image.parse(image, self._signature_keys)

time.sleep(3)

dfu = DfuTransportSerial(self.path)

if callback:
total = len(image.firmware_bin)
total = len(parsed_image.firmware_bin)
callback(0, total)
dfu.register_events_callback(DfuEvent.PROGRESS_EVENT, CallbackWrapper(callback, total))

dfu.open()
dfu.send_init_packet(image.firmware_dat)
dfu.send_firmware(image.firmware_bin)
dfu.send_init_packet(parsed_image.firmware_dat)
dfu.send_firmware(parsed_image.firmware_bin)
dfu.close()

@classmethod
Expand Down Expand Up @@ -192,7 +192,9 @@ def _list_ports(vid: int, pid: int) -> list[tuple[str, int]]:
product_id = int(device.product_id, base=16)
assert device.com_ports
if len(device.com_ports) > 1:
logger.warn(f"Nitrokey 3 NRF52 bootloader has multiple com ports: {device.com_ports}")
logger.warning(
f"Nitrokey 3 NRF52 bootloader has multiple com ports: {device.com_ports}"
)
if vendor_id == vid and product_id == pid:
port = device.com_ports[0]
serial = int(device.serial_number, base=16)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -136,7 +136,10 @@ def get_serial_serial_no(

hkey_path = "SYSTEM\\CurrentControlSet\\Enum\\USB\\VID_{}&PID_{}".format(vendor_id, product_id)
try:
vendor_product_hkey = winreg.OpenKeyEx(winreg.HKEY_LOCAL_MACHINE, hkey_path)
vendor_product_hkey = winreg.OpenKeyEx(
winreg.HKEY_LOCAL_MACHINE, # ty: ignore[possibly-missing-attribute]
hkey_path,
)
except EnvironmentError:
return None

Expand All @@ -153,7 +156,10 @@ def get_serial_serial_no(
)

try:
device_hkey = winreg.OpenKeyEx(winreg.HKEY_LOCAL_MACHINE, hkey_path)
device_hkey = winreg.OpenKeyEx(
winreg.HKEY_LOCAL_MACHINE, # ty: ignore[possibly-missing-attribute]
hkey_path,
)
except EnvironmentError:
continue

Expand Down Expand Up @@ -185,7 +191,10 @@ def get_serial_serial_no(
def com_port_is_open(port: str) -> bool:
hkey_path = "HARDWARE\\DEVICEMAP\\SERIALCOMM"
try:
device_hkey = winreg.OpenKeyEx(winreg.HKEY_LOCAL_MACHINE, hkey_path)
device_hkey = winreg.OpenKeyEx(
winreg.HKEY_LOCAL_MACHINE, # ty: ignore[possibly-missing-attribute]
hkey_path,
)
except EnvironmentError:
# Unable to check enumerated serialports. Assume open.
return True
Expand All @@ -211,7 +220,10 @@ def list_all_com_ports(vendor_id: str, product_id: str, serial_number: str) -> l
)

try:
device_hkey = winreg.OpenKeyEx(winreg.HKEY_LOCAL_MACHINE, hkey_path)
device_hkey = winreg.OpenKeyEx(
winreg.HKEY_LOCAL_MACHINE, # ty: ignore[possibly-missing-attribute]
hkey_path,
)
except EnvironmentError:
return ports

Expand All @@ -227,7 +239,10 @@ def list_all_com_ports(vendor_id: str, product_id: str, serial_number: str) -> l
vendor_id, product_id, serial_number
)
try:
device_hkey = winreg.OpenKeyEx(winreg.HKEY_LOCAL_MACHINE, hkey_path)
device_hkey = winreg.OpenKeyEx(
winreg.HKEY_LOCAL_MACHINE, # ty: ignore[possibly-missing-attribute]
hkey_path,
)
try:
COM_port = winreg.QueryValueEx(device_hkey, "PortName")[0]
ports.append(COM_port)
Expand All @@ -253,7 +268,10 @@ def list_all_com_ports(vendor_id: str, product_id: str, serial_number: str) -> l
)
iface_id += 1
try:
device_hkey = winreg.OpenKeyEx(winreg.HKEY_LOCAL_MACHINE, hkey_path)
device_hkey = winreg.OpenKeyEx(
winreg.HKEY_LOCAL_MACHINE, # ty: ignore[possibly-missing-attribute]
hkey_path,
)
except EnvironmentError:
break

Expand Down
4 changes: 2 additions & 2 deletions src/nitrokey/trussed/_device.py
Original file line number Diff line number Diff line change
Expand Up @@ -90,12 +90,12 @@ def open(cls: type[T], path: str) -> Optional[T]:
else:
device = open_device(path)
except Exception:
logger.warn(f"No CTAPHID device at path {path}", exc_info=sys.exc_info())
logger.warning(f"No CTAPHID device at path {path}", exc_info=sys.exc_info())
return None
try:
return cls.from_device(device)
except ValueError:
logger.warn(f"No Nitrokey device at path {path}", exc_info=sys.exc_info())
logger.warning(f"No Nitrokey device at path {path}", exc_info=sys.exc_info())
return None

@classmethod
Expand Down
6 changes: 6 additions & 0 deletions src/nitrokey/trussed/admin_app.py
Original file line number Diff line number Diff line change
Expand Up @@ -176,12 +176,18 @@ def is_valid(self, value: str) -> bool:
return False
except ValueError:
return False
else:
# TODO: use typing.assert_never from Python 3.11
raise ValueError(self)

def __str__(self) -> str:
if self == ConfigFieldType.BOOL:
return "Bool"
elif self == ConfigFieldType.U8:
return "u8"
else:
# TODO: use typing.assert_never from Python 3.11
raise ValueError(self)


@dataclass
Expand Down