Skip to content

Commit

Permalink
Improve handling of PIN/PUK/RESET for Bio MPE
Browse files Browse the repository at this point in the history
  • Loading branch information
dainnilsson committed Jun 19, 2024
1 parent 1d265f5 commit c9cc568
Show file tree
Hide file tree
Showing 3 changed files with 50 additions and 26 deletions.
27 changes: 18 additions & 9 deletions ykman/_cli/config.py
Original file line number Diff line number Diff line change
Expand Up @@ -35,7 +35,6 @@
CAPABILITY,
USB_INTERFACE,
DEVICE_FLAG,
FORM_FACTOR,
Mode,
)
from .util import (
Expand Down Expand Up @@ -123,15 +122,17 @@ def reset(ctx, force):
This action will wipe all data and restore factory settings for
all applications on the YubiKey.
"""
transport = ctx.obj["device"].transport
info = ctx.obj["info"]
is_bio = info.form_factor in (FORM_FACTOR.USB_A_BIO, FORM_FACTOR.USB_C_BIO)
has_piv = CAPABILITY.PIV in info.supported_capabilities.get(transport)
if not (is_bio and has_piv):
raise CliFail(
"Full device reset is not supported on this YubiKey, "
"refer to reset commands for specific applications instead."
)
# reset_blocked is a sure indicator of the command
if not info.reset_blocked:
# No reset blocked, we can still check for Bio MPE
transport = ctx.obj["device"].transport
has_piv = CAPABILITY.PIV in info.supported_capabilities.get(transport)
if not (info._is_bio and has_piv):
raise CliFail(
"Full device reset is not supported on this YubiKey, "
"refer to reset commands for specific applications instead."
)

force or click.confirm(
"WARNING! This will delete all stored data and restore factory "
Expand Down Expand Up @@ -250,6 +251,14 @@ def _configure_applications(
force,
):
info = ctx.obj["info"]

# If any app reset is blocked, we will not be able to toggle applications
if info.reset_blocked:
raise CliFail(
"This YubiKey must be in a newly reset state before applications can be "
"toggled."
)

supported = info.supported_capabilities.get(transport)
enabled = info.config.enabled_capabilities.get(transport)

Expand Down
17 changes: 16 additions & 1 deletion ykman/_cli/piv.py
Original file line number Diff line number Diff line change
Expand Up @@ -287,6 +287,12 @@ def set_pin_retries(ctx, management_key, pin, pin_retries, puk_retries, force):
"Retry attempts must be set before PIN/PUK have been changed."
)

try: # Can't change retries on Bio MPE
session.get_bio_metadata()
raise CliFail("PIN/PUK retries cannot be changed on this YubiKey.")
except NotSupportedError:
pass

_ensure_authenticated(
ctx, pin, management_key, require_pin_and_key=True, no_prompt=force
)
Expand All @@ -304,7 +310,7 @@ def set_pin_retries(ctx, management_key, pin, pin_retries, puk_retries, force):
click.echo("\tPIN:\t123456")
click.echo("\tPUK:\t12345678")
except Exception:
raise CliFail("Setting pin retries failed.")
raise CliFail("Setting PIN retries failed.")


def _do_change_pin_puk(pin_complexity, name, current, new, fn):
Expand Down Expand Up @@ -347,6 +353,9 @@ def change_pin(ctx, pin, new_pin):
info = ctx.obj["info"]
session = ctx.obj["session"]

if not session.get_pin_attempts():
raise CliFail("PIN is blocked.")

if not pin:
pin = _prompt_pin("Enter the current PIN")
if not new_pin:
Expand Down Expand Up @@ -382,6 +391,12 @@ def change_puk(ctx, puk, new_puk):
info = ctx.obj["info"]
session = ctx.obj["session"]

try:
if not session.get_puk_metadata().attempts_remaining:
raise CliFail("PUK is blocked.")
except NotSupportedError:
pass

if not puk:
puk = _prompt_pin("Enter the current PUK")
if not new_puk:
Expand Down
32 changes: 16 additions & 16 deletions ykman/piv.py
Original file line number Diff line number Diff line change
Expand Up @@ -526,22 +526,8 @@ def get_piv_info(session: PivSession):
tries = session.get_pin_attempts()
tries_str = "15 or more" if tries == 15 else str(tries)
info["PIN tries remaining"] = tries_str
try:
puk_data = session.get_puk_metadata()
if puk_data.attempts_remaining == 0:
lines.append("PUK is blocked")
elif puk_data.default_value:
lines.append("WARNING: Using default PUK!")
tries_str = "%d/%d" % (
puk_data.attempts_remaining,
puk_data.total_attempts,
)
info["PUK tries remaining"] = tries_str
except NotSupportedError:
if pivman.puk_blocked:
lines.append("PUK is blocked")

try:
try: # Bio metadata
bio = session.get_bio_metadata()
if bio.configured:
info[
Expand All @@ -550,7 +536,21 @@ def get_piv_info(session: PivSession):
else:
info["Biometrics"] = "Not configured"
except NotSupportedError:
pass
try: # PUK metadata (on non-bio)
puk_data = session.get_puk_metadata()
if puk_data.attempts_remaining == 0:
lines.append("PUK is blocked")
elif puk_data.default_value:
lines.append("WARNING: Using default PUK!")
tries_str = "%d/%d" % (
puk_data.attempts_remaining,
puk_data.total_attempts,
)
info["PUK tries remaining"] = tries_str
except NotSupportedError:
# YK < 5.3
if pivman.puk_blocked:
lines.append("PUK is blocked")

try:
metadata = session.get_management_key_metadata()
Expand Down

0 comments on commit c9cc568

Please sign in to comment.