Skip to content

Commit

Permalink
Merge PR #601
Browse files Browse the repository at this point in the history
  • Loading branch information
dainnilsson committed Mar 22, 2024
2 parents 1772db5 + 69941d9 commit 2e892ef
Show file tree
Hide file tree
Showing 2 changed files with 42 additions and 17 deletions.
7 changes: 6 additions & 1 deletion ykman/diagnostics.py
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@
from .openpgp import get_openpgp_info
from .hsmauth import get_hsmauth_info

from yubikit.core import Tlv
from yubikit.core.smartcard import SmartCardConnection
from yubikit.core.fido import FidoConnection
from yubikit.core.otp import OtpConnection
Expand Down Expand Up @@ -51,9 +52,13 @@ def sys_info():
def mgmt_info(pid, conn):
data: List[Any] = []
try:
m = ManagementSession(conn)
raw_info = m.backend.read_config()
if Tlv.parse_dict(raw_info[1:]).get(0x10) == b"\1":
raw_info += m.backend.read_config(1)
data.append(
{
"Raw Info": ManagementSession(conn).backend.read_config(),
"Raw Info": raw_info,
}
)
except Exception as e:
Expand Down
52 changes: 36 additions & 16 deletions yubikit/management.py
Original file line number Diff line number Diff line change
Expand Up @@ -49,7 +49,7 @@
from fido2.hid import CAPABILITY as CTAP_CAPABILITY

from enum import IntEnum, IntFlag, unique
from dataclasses import dataclass
from dataclasses import dataclass, field
from typing import Optional, Union, Mapping
import abc
import struct
Expand Down Expand Up @@ -168,16 +168,17 @@ class DEVICE_FLAG(IntFlag):
TAG_REBOOT = 0x0C
TAG_NFC_SUPPORTED = 0x0D
TAG_NFC_ENABLED = 0x0E
TAG_MORE_DATA = 0x10


@dataclass
class DeviceConfig:
"""Management settings for YubiKey which can be configured by the user."""

enabled_capabilities: Mapping[TRANSPORT, CAPABILITY]
auto_eject_timeout: Optional[int]
challenge_response_timeout: Optional[int]
device_flags: Optional[DEVICE_FLAG]
enabled_capabilities: Mapping[TRANSPORT, CAPABILITY] = field(default_factory=dict)
auto_eject_timeout: Optional[int] = None
challenge_response_timeout: Optional[int] = None
device_flags: Optional[DEVICE_FLAG] = None

def get_bytes(
self,
Expand Down Expand Up @@ -229,7 +230,12 @@ def has_transport(self, transport: TRANSPORT) -> bool:
def parse(cls, encoded: bytes, default_version: Version) -> "DeviceInfo":
if len(encoded) - 1 != encoded[0]:
raise BadResponseError("Invalid length")
data = Tlv.parse_dict(encoded[1:])
return cls.parse_tlvs(Tlv.parse_dict(encoded[1:]), default_version)

@classmethod
def parse_tlvs(
cls, data: Mapping[int, bytes], default_version: Version
) -> "DeviceInfo":
locked = data.get(TAG_CONFIG_LOCK) == b"\1"
serial = bytes2int(data.get(TAG_SERIAL, b"\0")) or None
ff_value = bytes2int(data.get(TAG_FORM_FACTOR, b"\0"))
Expand Down Expand Up @@ -324,7 +330,7 @@ def set_mode(self, data: bytes) -> None:
...

@abc.abstractmethod
def read_config(self) -> bytes:
def read_config(self, page: int = 0) -> bytes:
...

@abc.abstractmethod
Expand All @@ -351,8 +357,10 @@ def set_mode(self, data):
return # ProgSeq isn't updated by set mode when empty
raise

def read_config(self):
response = self.protocol.send_and_receive(SLOT_YK4_CAPABILITIES)
def read_config(self, page: int = 0):
response = self.protocol.send_and_receive(
SLOT_YK4_CAPABILITIES, int2bytes(page)
)
r_len = response[0]
if check_crc(response[: r_len + 1 + 2]):
return response[: r_len + 1]
Expand Down Expand Up @@ -401,8 +409,8 @@ def set_mode(self, data):
else:
self.protocol.send_apdu(0, INS_SET_MODE, P1_DEVICE_CONFIG, 0, data)

def read_config(self):
return self.protocol.send_apdu(0, INS_READ_CONFIG, 0, 0)
def read_config(self, page: int = 0):
return self.protocol.send_apdu(0, INS_READ_CONFIG, page, 0)

def write_config(self, config):
self.protocol.send_apdu(0, INS_WRITE_CONFIG, 0, 0, config)
Expand Down Expand Up @@ -434,8 +442,8 @@ def close(self):
def set_mode(self, data):
self.ctap.call(CTAP_YUBIKEY_DEVICE_CONFIG, data)

def read_config(self):
return self.ctap.call(CTAP_READ_CONFIG)
def read_config(self, page: int = 0):
return self.ctap.call(CTAP_READ_CONFIG, int2bytes(page))

def write_config(self, config):
self.ctap.call(CTAP_WRITE_CONFIG, config)
Expand Down Expand Up @@ -468,7 +476,20 @@ def version(self) -> Version:
def read_device_info(self) -> DeviceInfo:
"""Get detailed information about the YubiKey."""
require_version(self.version, (4, 1, 0))
return DeviceInfo.parse(self.backend.read_config(), self.version)
more_data = True
tlvs = {}
page = 0
while more_data:
logger.debug(f"Reading DeviceInfo page: {page}")
encoded = self.backend.read_config(page)
if len(encoded) - 1 != encoded[0]:
raise BadResponseError("Invalid length")
data = Tlv.parse_dict(encoded[1:])
more_data = data.pop(TAG_MORE_DATA, 0) == b"\1"
tlvs.update(data)
page += 1

return DeviceInfo.parse_tlvs(tlvs, self.version)

def write_device_config(
self,
Expand All @@ -489,7 +510,7 @@ def write_device_config(
raise ValueError("Lock code must be 16 bytes")
if new_lock_code is not None and len(new_lock_code) != 16:
raise ValueError("Lock code must be 16 bytes")
config = config or DeviceConfig({}, None, None, None)
config = config or DeviceConfig()
logger.debug(
f"Writing device config: {config}, reboot: {reboot}, "
f"current lock code: {cur_lock_code is not None}, "
Expand Down Expand Up @@ -534,7 +555,6 @@ def set_mode(
{TRANSPORT.USB: usb_enabled},
auto_eject_timeout,
chalresp_timeout,
None,
)
)
else:
Expand Down

0 comments on commit 2e892ef

Please sign in to comment.