Skip to content

Commit

Permalink
Update FIDO2 certificate hash for nkpk
Browse files Browse the repository at this point in the history
  • Loading branch information
robin-nitrokey committed Feb 2, 2024
1 parent 6f5c2c0 commit 4d4775b
Show file tree
Hide file tree
Showing 8 changed files with 69 additions and 92 deletions.
17 changes: 0 additions & 17 deletions .github/workflows/ci.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -77,20 +77,3 @@ jobs:
run: |
. venv/bin/activate
make check-typing
test-documentation:
name: Run documentation tests
runs-on: ubuntu-latest
container: python:3.9-slim
steps:
- name: Checkout repository
uses: actions/checkout@v3
- name: Install required packages
run: |
apt update
apt install -y gcc libpcsclite-dev make swig git
- name: Create virtual environment
run: make init
- name: Check code static typing
run: |
. venv/bin/activate
make check-doctest
5 changes: 1 addition & 4 deletions Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -46,10 +46,7 @@ check-typing:
@echo "Note: run semi-clean target in case this fails without any proper reason"
$(PYTHON3_VENV) -m mypy $(PACKAGE_NAME)/

check-doctest:
$(PYTHON3_VENV) -m doctest $(PACKAGE_NAME)/nk3/utils.py

check: check-format check-import-sorting check-style check-typing check-doctest
check: check-format check-import-sorting check-style check-typing

# automatic code fixes
fix:
Expand Down
5 changes: 2 additions & 3 deletions pynitrokey/cli/trussed/tests.py
Original file line number Diff line number Diff line change
Expand Up @@ -19,10 +19,9 @@
from pynitrokey.cli.trussed.test import TestContext, TestResult, TestStatus, test_case
from pynitrokey.fido2.client import NKFido2Client
from pynitrokey.helpers import local_print
from pynitrokey.nk3.utils import Fido2Certs
from pynitrokey.trussed.base import NitrokeyTrussedBase
from pynitrokey.trussed.device import NitrokeyTrussedDevice
from pynitrokey.trussed.utils import Uuid, Version
from pynitrokey.trussed.utils import Fido2Certs, Uuid, Version

logger = logging.getLogger(__name__)

Expand Down Expand Up @@ -400,7 +399,7 @@ def request_uv(self, permissions: Any, rd_id: Any) -> bool:

firmware_version = ctx.firmware_version or device.admin.version()
if firmware_version:
expected_certs = Fido2Certs.get(firmware_version)
expected_certs = Fido2Certs.get(device.fido2_certs, firmware_version)
if expected_certs and cert_hash not in expected_certs.hashes:
return TestResult(
TestStatus.FAILURE,
Expand Down
24 changes: 23 additions & 1 deletion pynitrokey/nk3/device.py
Original file line number Diff line number Diff line change
Expand Up @@ -10,13 +10,31 @@
from fido2.hid import CtapHidDevice

from pynitrokey.trussed.device import NitrokeyTrussedDevice
from pynitrokey.trussed.utils import Fido2Certs, Version

FIDO2_CERTS = [
Fido2Certs(
start=Version(0, 1, 0),
hashes=[
"ad8fd1d16f59104b9e06ef323cc03f777ed5303cd421a101c9cb00bb3fdf722d",
],
),
Fido2Certs(
start=Version(1, 0, 3),
hashes=[
"aa1cb760c2879530e7d7fed3da75345d25774be9cfdbbcbd36fdee767025f34b", # NK3xN/lpc55
"4c331d7af869fd1d8217198b917a33d1fa503e9778da7638504a64a438661ae0", # NK3AM/nrf52
"f1ed1aba24b16e8e3fabcda72b10cbfa54488d3b778bda552162d60c6dd7b4fa", # NK3AM/nrf52 test
],
),
]


class Nitrokey3Device(NitrokeyTrussedDevice):
"""A Nitrokey 3 device running the firmware."""

def __init__(self, device: CtapHidDevice) -> None:
super().__init__(device)
super().__init__(device, FIDO2_CERTS)

@property
def pid(self) -> int:
Expand All @@ -27,3 +45,7 @@ def pid(self) -> int:
@property
def name(self) -> str:
return "Nitrokey 3"

@classmethod
def from_device(cls, device: CtapHidDevice) -> "Nitrokey3Device":
return cls(device)
60 changes: 0 additions & 60 deletions pynitrokey/nk3/utils.py

This file was deleted.

16 changes: 15 additions & 1 deletion pynitrokey/nkpk.py
Original file line number Diff line number Diff line change
Expand Up @@ -15,14 +15,24 @@
from pynitrokey.trussed.base import NitrokeyTrussedBase
from pynitrokey.trussed.bootloader.nrf52 import NitrokeyTrussedBootloaderNrf52
from pynitrokey.trussed.device import NitrokeyTrussedDevice
from pynitrokey.trussed.utils import Fido2Certs, Version

PID_NITROKEY_PASSKEY_DEVICE = 0x42F3
PID_NITROKEY_PASSKEY_BOOTLOADER = 0x42F4

FIDO2_CERTS = [
Fido2Certs(
start=Version(0, 1, 0),
hashes=[
"",
],
),
]


class NitrokeyPasskeyDevice(NitrokeyTrussedDevice):
def __init__(self, device: CtapHidDevice) -> None:
super().__init__(device)
super().__init__(device, FIDO2_CERTS)

@property
def pid(self) -> int:
Expand All @@ -32,6 +42,10 @@ def pid(self) -> int:
def name(self) -> str:
return "Nitrokey Passkey"

@classmethod
def from_device(cls, device: CtapHidDevice) -> "NitrokeyPasskeyDevice":
return cls(device)


class NitrokeyPasskeyBootloader(NitrokeyTrussedBootloaderNrf52):
@property
Expand Down
18 changes: 13 additions & 5 deletions pynitrokey/trussed/device.py
Original file line number Diff line number Diff line change
Expand Up @@ -13,14 +13,14 @@
import sys
from abc import abstractmethod
from enum import Enum
from typing import Optional, TypeVar
from typing import Optional, Sequence, TypeVar

from fido2.hid import CtapHidDevice, open_device

from pynitrokey.fido2 import device_path_to_str

from .base import NitrokeyTrussedBase
from .utils import Uuid, Version
from .utils import Fido2Certs, Uuid, Version

T = TypeVar("T", bound="NitrokeyTrussedDevice")

Expand All @@ -37,10 +37,13 @@ class App(Enum):


class NitrokeyTrussedDevice(NitrokeyTrussedBase):
def __init__(self, device: CtapHidDevice) -> None:
def __init__(
self, device: CtapHidDevice, fido2_certs: Sequence[Fido2Certs]
) -> None:
self.validate_vid_pid(device.descriptor.vid, device.descriptor.pid)

self.device = device
self.fido2_certs = fido2_certs
self._path = device_path_to_str(device.descriptor.path)
self.logger = logger.getChild(self._path)

Expand Down Expand Up @@ -90,6 +93,11 @@ def _call_app(
) -> bytes:
return self._call(app.value, app.name, response_len, data)

@classmethod
@abstractmethod
def from_device(cls: type[T], device: CtapHidDevice) -> T:
...

@classmethod
def open(cls: type[T], path: str) -> Optional[T]:
try:
Expand All @@ -101,7 +109,7 @@ def open(cls: type[T], path: str) -> Optional[T]:
logger.warn(f"No CTAPHID device at path {path}", exc_info=sys.exc_info())
return None
try:
return cls(device)
return cls.from_device(device)
except ValueError:
logger.warn(f"No Nitrokey device at path {path}", exc_info=sys.exc_info())
return None
Expand All @@ -111,7 +119,7 @@ def list(cls: type[T]) -> list[T]:
devices = []
for device in CtapHidDevice.list_devices():
try:
devices.append(cls(device))
devices.append(cls.from_device(device))
except ValueError:
# not the correct device type, skip
pass
Expand Down
16 changes: 15 additions & 1 deletion pynitrokey/trussed/utils.py
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@
import dataclasses
from dataclasses import dataclass, field
from functools import total_ordering
from typing import Optional
from typing import Optional, Sequence

from spsdk.sbfile.misc import BcdVersion3

Expand Down Expand Up @@ -231,3 +231,17 @@ def from_v_str(cls, s: str) -> "Version":
@classmethod
def from_bcd_version(cls, version: BcdVersion3) -> "Version":
return cls(major=version.major, minor=version.minor, patch=version.service)


@dataclass
class Fido2Certs:
start: Version
hashes: list[str]

@staticmethod
def get(certs: Sequence["Fido2Certs"], version: Version) -> Optional["Fido2Certs"]:
matching_certs = [c for c in certs if version >= c.start]
if matching_certs:
return max(matching_certs, key=lambda c: c.start)
else:
return None

0 comments on commit 4d4775b

Please sign in to comment.