Skip to content

Commit

Permalink
Add documentation for yubikit and ykman
Browse files Browse the repository at this point in the history
  • Loading branch information
elibon99 committed Aug 16, 2023
1 parent 021e6ae commit 852f9a7
Show file tree
Hide file tree
Showing 23 changed files with 806 additions and 79 deletions.
2 changes: 2 additions & 0 deletions docs/index.rst
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,8 @@ Welcome to yubikey-manager's documentation!
:maxdepth: 2
:caption: Contents:

rst/packages

Indices and tables
==================

Expand Down
12 changes: 12 additions & 0 deletions docs/rst/packages.rst
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
yubikey-manager
===============

.. toctree::
:maxdepth: 4

yubikit

.. toctree::
:maxdepth: 4

ykman
77 changes: 77 additions & 0 deletions docs/rst/ykman.rst
Original file line number Diff line number Diff line change
@@ -0,0 +1,77 @@
ykman package
=============

Submodules
----------

ykman.base module
-----------------

.. automodule:: ykman.base
:members:
:undoc-members:
:show-inheritance:

ykman.device module
-------------------

.. automodule:: ykman.device
:members:
:undoc-members:
:show-inheritance:

ykman.fido module
-----------------

.. automodule:: ykman.fido
:members:
:undoc-members:
:show-inheritance:

ykman.hsmauth module
--------------------

.. automodule:: ykman.hsmauth
:members:
:undoc-members:
:show-inheritance:

ykman.oath module
-----------------

.. automodule:: ykman.oath
:members:
:undoc-members:
:show-inheritance:

ykman.openpgp module
--------------------

.. automodule:: ykman.openpgp
:members:
:undoc-members:
:show-inheritance:

ykman.piv module
-----------------

.. automodule:: ykman.piv
:members:
:undoc-members:
:show-inheritance:

ykman.scripting module
----------------------

.. automodule:: ykman.scripting
:members:
:undoc-members:
:show-inheritance:

Module contents
---------------

.. automodule:: ykman
:members:
:undoc-members:
:show-inheritance:
2 changes: 1 addition & 1 deletion docs/rst/yubikit.rst
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ Subpackages
-----------

.. toctree::
:maxdepth: 4
:maxdepth: 1

yubikit.core

Expand Down
12 changes: 8 additions & 4 deletions ykman/device.py
Original file line number Diff line number Diff line change
Expand Up @@ -85,16 +85,19 @@ def inner():
EstablishContextException,
)
def list_ccid_devices():
"""List CCID devices."""
return _list_ccid_devices()


@_warn_once("No CTAP HID backend available. FIDO protocols will not function.")
def list_ctap_devices():
"""List CTAP devices."""
return _list_ctap_devices()


@_warn_once("No OTP HID backend available. OTP protocols will not function.")
def list_otp_devices():
"""List OTP devices."""
return _list_otp_devices()


Expand All @@ -108,8 +111,8 @@ def list_otp_devices():
def scan_devices() -> Tuple[Mapping[PID, int], int]:
"""Scan USB for attached YubiKeys, without opening any connections.
Returns a dict mapping PID to device count, and a state object which can be used to
detect changes in attached devices.
:return: A dict mapping PID to device count, and a state object which can be used to
detect changes in attached devices.
"""
fingerprints = set()
merged: Dict[PID, int] = {}
Expand Down Expand Up @@ -270,9 +273,10 @@ def open_connection(self, connection_type):
def list_all_devices(
connection_types: Iterable[Type[Connection]] = _CONNECTION_LIST_MAPPING.keys(),
) -> List[Tuple[YkmanDevice, DeviceInfo]]:
"""Connects to all attached YubiKeys and reads device info from them.
"""Connect to all attached YubiKeys and read device info from them.
Returns a list of (device, info) tuples for each connected device.
:param connection_types: An iterable of YubiKey connection types.
:return: A list of (device, info) tuples for each connected device.
"""
groups: Dict[PID, _PidGroup] = {}

Expand Down
17 changes: 15 additions & 2 deletions ykman/fido.py
Original file line number Diff line number Diff line change
Expand Up @@ -44,7 +44,10 @@


def is_in_fips_mode(fido_connection: FidoConnection) -> bool:
"""Check if a YubiKey FIPS is in FIPS approved mode."""
"""Check if a YubiKey FIPS is in FIPS approved mode.
:param fido_connection: A FIDO connection.
"""
try:
ctap = Ctap1(fido_connection)
ctap.send_apdu(ins=INS_FIPS_VERIFY_FIPS_MODE)
Expand All @@ -62,6 +65,10 @@ def fips_change_pin(
"""Change the PIN on a YubiKey FIPS.
If no PIN is set, pass None or an empty string as old_pin.
:param fido_connection: A FIDO connection.
:param old_pin: The old PIN.
:param new_pin: The new PIN.
"""
ctap = Ctap1(fido_connection)

Expand All @@ -75,7 +82,11 @@ def fips_change_pin(


def fips_verify_pin(fido_connection: FidoConnection, pin: str):
"""Unlock the YubiKey FIPS U2F module for credential creation."""
"""Unlock the YubiKey FIPS U2F module for credential creation.
:param fido_connection: A FIDO connection.
:param pin: The FIDO PIN.
"""
ctap = Ctap1(fido_connection)
ctap.send_apdu(ins=INS_FIPS_VERIFY_PIN, data=pin.encode())

Expand All @@ -86,6 +97,8 @@ def fips_reset(fido_connection: FidoConnection):
Note: This action is only permitted immediately after YubiKey FIPS power-up. It
also requires the user to touch the flashing button on the YubiKey, and will halt
until that happens, or the command times out.
:param fido_connection: A FIDO connection.
"""
ctap = Ctap1(fido_connection)
while True:
Expand Down
1 change: 1 addition & 0 deletions ykman/hsmauth.py
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,7 @@


def get_hsmauth_info(session: HsmAuthSession):
"""Get information about the YubiHSM Auth application."""
retries = session.get_management_key_retries()
info = {
"YubiHSM Auth version": session.version,
Expand Down
4 changes: 4 additions & 0 deletions ykman/oath.py
Original file line number Diff line number Diff line change
Expand Up @@ -34,14 +34,17 @@


def is_hidden(credential):
"""Check if OATH credential is hidden."""
return credential.issuer == "_hidden"


def is_steam(credential):
"""Check if OATH credential is steam."""
return credential.oath_type == OATH_TYPE.TOTP and credential.issuer == "Steam"


def calculate_steam(app, credential, timestamp=None):
"""Calculate steam codes."""
timestamp = int(timestamp or time())
resp = app.calculate(credential.id, struct.pack(">q", timestamp // 30))
offset = resp[-1] & 0x0F
Expand All @@ -54,4 +57,5 @@ def calculate_steam(app, credential, timestamp=None):


def is_in_fips_mode(app):
"""Check if OATH application is in FIPS mode."""
return app.locked
5 changes: 4 additions & 1 deletion ykman/openpgp.py
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,10 @@


def get_openpgp_info(session: OpenPgpSession):
"""Get human readable information about the OpenPGP configuration."""
"""Get human readable information about the OpenPGP configuration.
:param session: The OpenPGP session.
"""
data = session.get_application_related_data()
discretionary = data.discretionary
retries = discretionary.pw_status
Expand Down
38 changes: 32 additions & 6 deletions ykman/otp.py
Original file line number Diff line number Diff line change
Expand Up @@ -144,7 +144,10 @@ def _prepare_upload_key(


def is_in_fips_mode(session: YubiOtpSession) -> bool:
"""Check if the OTP application of a FIPS YubiKey is in FIPS approved mode."""
"""Check if the OTP application of a FIPS YubiKey is in FIPS approved mode.
:param session: The YubiOTP session.
"""
return session.backend.send_and_receive(0x14, b"", 1) == b"\1" # type: ignore


Expand All @@ -156,29 +159,45 @@ def generate_static_pw(
keyboard_layout: KEYBOARD_LAYOUT = KEYBOARD_LAYOUT.MODHEX,
blocklist: Iterable[str] = DEFAULT_PW_CHAR_BLOCKLIST,
) -> str:
"""Generate a random password."""
"""Generate a random password.
:param length: The length of the password.
:param keyboard_layout: The keyboard layout.
:param blocklist: The list of characters to block.
"""
chars = [k for k in keyboard_layout.value.keys() if k not in blocklist]
sr = random.SystemRandom()
return "".join([sr.choice(chars) for _ in range(length)])


def parse_oath_key(val: str) -> bytes:
"""Parse a secret key encoded as either Hex or Base32."""
"""Parse a secret key encoded as either Hex or Base32.
:param val: The secret key.
"""
try:
return bytes.fromhex(val)
except ValueError:
return parse_b32_key(val)


def format_oath_code(response: bytes, digits: int = 6) -> str:
"""Formats an OATH code from a hash response."""
"""Format an OATH code from a hash response.
:param response: The response.
:param digits: The number of digits in the OATH code.
"""
offs = response[-1] & 0xF
code = struct.unpack_from(">I", response[offs:])[0] & 0x7FFFFFFF
return ("%%0%dd" % digits) % (code % 10**digits)


def time_challenge(timestamp: int, period: int = 30) -> bytes:
"""Formats a HMAC-SHA1 challenge based on an OATH timestamp and period."""
"""Format a HMAC-SHA1 challenge based on an OATH timestamp and period.
:param timestamp: The timestamp.
:param period: The period.
"""
return struct.pack(">q", int(timestamp // period))


Expand All @@ -190,7 +209,14 @@ def format_csv(
access_code: Optional[bytes] = None,
timestamp: Optional[datetime] = None,
) -> str:
"""Produces a CSV line in the "Yubico" format."""
"""Produce a CSV line in the "Yubico" format.
:param serial: The serial number.
:param public_id: The public ID.
:param private_id: The private ID.
:param key: The secret key.
:param access_code: The access code.
"""
ts = timestamp or datetime.now()
return ",".join(
[
Expand Down
Loading

0 comments on commit 852f9a7

Please sign in to comment.