Skip to content
Draft
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
33 changes: 25 additions & 8 deletions nitrokeyapp/device_data.py
Original file line number Diff line number Diff line change
@@ -1,9 +1,10 @@
import logging
from typing import List, Optional

from nitrokey import nk3
from nitrokey import nk3, nkpk
from nitrokey.nk3 import NK3, NK3Bootloader
from nitrokey.trussed import TrussedBase, TrussedDevice, Uuid, Version
from nitrokey.nkpk import NKPK, NKPKBootloader
from nitrokey.trussed import Model, TrussedBase, TrussedDevice, Uuid, Version
from nitrokey.trussed.admin_app import Status

from nitrokeyapp.update import Nk3Context, UpdateGUI, UpdateResult, UpdateStatus
Expand All @@ -14,6 +15,7 @@
class DeviceData:
def __init__(self, device: TrussedBase) -> None:
self.path = device.path
self.model = device.model
self.updating = False

self._status: Optional[Status] = None
Expand All @@ -35,18 +37,28 @@ def __repr__(self) -> str:

@classmethod
def list(cls) -> List["DeviceData"]:
return [cls(dev) for dev in nk3.list()]
nk3_devices = [cls(dev) for dev in nk3.list()]
nkpk_devices = [cls(dev) for dev in nkpk.list()]
return nk3_devices + nkpk_devices

@property
def name(self) -> str:
if self.is_bootloader:
# desc = self.path.split("/")[-1]
return "Nitrokey 3 (BL)"
return f"Nitrokey 3: {self.uuid_prefix}"
return "Nitrokey (BL)"
elif self.model == Model.NK3:
return f"{self.model}: {self.uuid_prefix}"
elif self.model == Model.NKPK:
return f"{self.model}: {self.uuid_prefix}"

@property
def is_bootloader(self) -> bool:
return isinstance(self._device, NK3Bootloader)
if isinstance(self._device, NK3Bootloader) or isinstance(
self._device, NKPKBootloader
):
return True
else:
return False

@property
def is_too_old(self) -> bool:
Expand Down Expand Up @@ -94,8 +106,13 @@ def uuid_prefix(self) -> str:
assert isinstance(self._device, TrussedDevice)
return str(self.uuid)[:5]

def open(self) -> NK3:
device = NK3.open(self.path)
def open(self) -> NK3 | NKPK:
device: Optional[NK3 | NKPK] = None
if self.model == Model.NK3:
device = NK3.open(self.path)
elif self.model == Model.NKPK:
device = NKPK.open(self.path)

if device:
return device
else:
Expand Down
9 changes: 7 additions & 2 deletions nitrokeyapp/gui.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@
from types import TracebackType
from typing import Dict, Optional, Type

from nitrokey.trussed import Model
from PySide6 import QtWidgets
from PySide6.QtCore import QEvent, Qt, Signal, Slot
from PySide6.QtGui import QCursor
Expand Down Expand Up @@ -261,8 +262,12 @@ def show_device(self, data: DeviceData) -> None:
self.tabs.setTabEnabled(1, False)
self.tabs.setTabEnabled(2, False)
else:
self.tabs.setTabEnabled(1, True)
self.tabs.setTabEnabled(2, True)
if not self.selected_device.model == Model.NKPK:
self.tabs.setTabEnabled(1, True)
self.tabs.setTabEnabled(2, True)
else:
self.tabs.setTabEnabled(1, False)
self.tabs.setTabEnabled(2, True)

self.show_navigation()
self.welcome_widget.hide()
Expand Down
26 changes: 20 additions & 6 deletions nitrokeyapp/overview_tab/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@
import shutil
from typing import Optional

from nitrokey.trussed import Model
from nitrokey.trussed.admin_app import InitStatus
from PySide6.QtCore import QThread, Signal, Slot
from PySide6.QtWidgets import QFileDialog, QWidget
Expand Down Expand Up @@ -106,7 +107,7 @@ def refresh(self, data: DeviceData, force: bool = False) -> None:
self.ui.status_label.hide()
self.ui.nk3_status.hide()
self.ui.more_info.hide()
self.ui.nk3_label.setText("Nitrokey 3 Bootloader")
self.ui.nk3_label.setText("Nitrokey Bootloader")
self.status_error(InitStatus(0))

else:
Expand All @@ -118,7 +119,10 @@ def refresh(self, data: DeviceData, force: bool = False) -> None:
str(data.status.variant.name),
str(data.status.init_status),
)
self.ui.nk3_label.setText("Nitrokey 3")
if data.model == Model.NK3:
self.ui.nk3_label.setText(f"{data.model}")
if data.model == Model.NKPK:
self.ui.nk3_label.setText(f"{data.model}")
if data.status.init_status is None:
self.ui.status_label.hide()
self.ui.nk3_status.hide()
Expand Down Expand Up @@ -160,10 +164,20 @@ def set_update_enabled(self, enabled: bool) -> None:
)
tooltip = "Please remove all Nitrokey 3 devices except the one you want to update."

self.ui.btn_update.setEnabled(enabled)
self.ui.btn_update.setToolTip(tooltip)
self.ui.btn_more_options.setEnabled(enabled)
self.ui.btn_more_options.setToolTip(tooltip)
if self.data and self.data.model == Model.NK3:
self.ui.btn_update.setEnabled(enabled)
self.ui.btn_update.setToolTip(tooltip)
self.ui.btn_more_options.setEnabled(enabled)
self.ui.btn_more_options.setToolTip(tooltip)
elif self.data and self.data.model == Model.NKPK:
self.ui.btn_update.setEnabled(False)
self.ui.btn_update.setToolTip(
"Nitrokey Passkeys can't be updated with this version of the Nitrokey App."
)
self.ui.btn_more_options.setEnabled(False)
self.ui.btn_more_options.setToolTip(
"Nitrokey Passkeys can't be updated with this version of the Nitrokey App."
)

def update_btns_during_update(self, enabled: bool) -> None:
tooltip = ""
Expand Down
25 changes: 25 additions & 0 deletions nitrokeyapp/secrets_tab/worker.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
from datetime import datetime
from typing import Dict, Optional

from nitrokey.nk3 import NK3
from nitrokey.nk3.secrets_app import SecretsApp, SecretsAppException
from nitrokey.trussed import Uuid
from PySide6.QtCore import QObject, Signal, Slot
Expand Down Expand Up @@ -68,6 +69,8 @@ def run(self) -> None:
compatible = False
try:
with self.data.open() as device:
if not isinstance(device, NK3):
return
secrets = SecretsApp(device)
try:
compatible = secrets._semver_equal_or_newer("4.11.0")
Expand Down Expand Up @@ -118,6 +121,8 @@ def cleanup(self) -> None:

def run(self) -> None:
with self.data.open() as device:
if not isinstance(device, NK3):
return
secrets = SecretsApp(device)
select = secrets.select()

Expand All @@ -135,6 +140,8 @@ def run(self) -> None:
@Slot(str)
def pin_queried(self, pin: str) -> None:
with self.data.open() as device:
if not isinstance(device, NK3):
return
secrets = SecretsApp(device)
try:
with self.touch_prompt():
Expand All @@ -151,6 +158,8 @@ def pin_queried(self, pin: str) -> None:
@Slot(str)
def pin_chosen(self, pin: str) -> None:
with self.data.open() as device:
if not isinstance(device, NK3):
return
secrets = SecretsApp(device)
with self.touch_prompt():
secrets.set_pin_raw(pin)
Expand Down Expand Up @@ -309,6 +318,8 @@ def temp_rename_credential(self, from_cred_id: bytes) -> bytes:
new_cred_id += b"_"

with self.data.open() as device:
if not isinstance(device, NK3):
return from_cred_id
secrets = SecretsApp(device)
with self.touch_prompt():
secrets.update_credential(cred_id=from_cred_id, new_name=new_cred_id)
Expand All @@ -318,6 +329,8 @@ def temp_rename_credential(self, from_cred_id: bytes) -> bytes:
@Slot()
def edit_credential_final(self) -> None:
with self.data.open() as device:
if not isinstance(device, NK3):
return
secrets = SecretsApp(device)
with self.touch_prompt():

Expand Down Expand Up @@ -405,6 +418,8 @@ def add_credential(self, successful: bool = True) -> None:
return

with self.data.open() as device:
if not isinstance(device, NK3):
return
secrets = SecretsApp(device)
with self.touch_prompt():

Expand Down Expand Up @@ -474,6 +489,8 @@ def run(self) -> None:
@Slot()
def delete_credential(self) -> None:
with self.data.open() as device:
if not isinstance(device, NK3):
return
secrets = SecretsApp(device)
try:
secrets.delete(self.credential.id)
Expand Down Expand Up @@ -522,6 +539,8 @@ def run(self) -> None:
@Slot()
def generate_otp(self) -> None:
with self.data.open() as device:
if not isinstance(device, NK3):
return
secrets = SecretsApp(device)

challenge = None
Expand Down Expand Up @@ -580,6 +599,8 @@ def run(self) -> None:
self.spawn(verify_pin_job)
else:
with self.data.open() as device:
if not isinstance(device, NK3):
return
secrets = SecretsApp(device)
credentials = Credential.list(secrets)
self.credentials_listed.emit(credentials)
Expand All @@ -591,6 +612,8 @@ def list_protected_credentials(self, successful: bool) -> None:
self.uncheck_checkbox.emit(True)

with self.data.open() as device:
if not isinstance(device, NK3):
return
secrets = SecretsApp(device)
for credential in Credential.list(secrets):
credentials.append(credential)
Expand Down Expand Up @@ -635,6 +658,8 @@ def run(self) -> None:
@Slot()
def get_credential(self) -> None:
with self.data.open() as device:
if not isinstance(device, NK3):
return
secrets = SecretsApp(device)
try:
pse = secrets.get_credential(self.credential.id)
Expand Down
11 changes: 10 additions & 1 deletion nitrokeyapp/settings_tab/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@

from fido2.ctap2.base import Info
from nitrokey.nk3.secrets_app import SelectResponse
from nitrokey.trussed import Model
from PySide6.QtCore import QThread, Signal, Slot
from PySide6.QtWidgets import QLineEdit, QTreeWidgetItem, QWidget

Expand Down Expand Up @@ -189,6 +190,14 @@ def refresh(self, data: DeviceData, force: bool = False) -> None:
return
self.reset()
self.data = data
if self.data.model == Model.NKPK:
self.items[State.Passwords].setDisabled(True)
self.items[State.PasswordsPin].setDisabled(True)
self.items[State.PasswordsReset].setDisabled(True)
else:
self.items[State.Passwords].setDisabled(False)
self.items[State.PasswordsPin].setDisabled(False)
self.items[State.PasswordsReset].setDisabled(False)

def field_btn(self) -> None:
icon_visibility = self.get_qicon("visibility_off.svg")
Expand Down Expand Up @@ -239,7 +248,7 @@ def field_btn(self) -> None:

def show_widget(self, item: Optional[QTreeWidgetItem]) -> None:
self.active_item = item
if item is None:
if item is None or item.isDisabled():
self.view_settings_empty()
return

Expand Down
9 changes: 9 additions & 0 deletions nitrokeyapp/settings_tab/worker.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@

from fido2.ctap2.base import Ctap2, Info
from fido2.ctap2.pin import ClientPin
from nitrokey.nk3 import NK3
from nitrokey.nk3.secrets_app import (
SecretsApp,
SecretsAppException,
Expand Down Expand Up @@ -62,6 +63,8 @@ def __init__(
def run(self) -> None:
pin_status: bool = False
with self.data.open() as device:
if not isinstance(device, NK3):
return
secrets = SecretsApp(device)
status = secrets.select()
if status.pin_attempt_counter is not None:
Expand Down Expand Up @@ -135,6 +138,8 @@ def __init__(
def check(self) -> bool:
pin_status: bool = False
with self.data.open() as device:
if not isinstance(device, NK3):
return pin_status
secrets = SecretsApp(device)
status = secrets.select()
if status.pin_attempt_counter is not None:
Expand All @@ -147,6 +152,8 @@ def run(self) -> None:
passwords_state = self.check()
with self.touch_prompt():
with self.data.open() as device:
if not isinstance(device, NK3):
return
secrets = SecretsApp(device)
try:
if passwords_state:
Expand Down Expand Up @@ -213,6 +220,8 @@ def __init__(

def run(self) -> None:
with self.data.open() as device:
if not isinstance(device, NK3):
return
secrets = SecretsApp(device)
try:
with self.touch_prompt():
Expand Down