Script 'mail_helper' called by obssrc
Hello community,

here is the log from the commit of package yubikey-manager for openSUSE:Factory 
checked in at 2021-10-15 23:04:12
++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Comparing /work/SRC/openSUSE:Factory/yubikey-manager (Old)
 and      /work/SRC/openSUSE:Factory/.yubikey-manager.new.1890 (New)
++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++

Package is "yubikey-manager"

Fri Oct 15 23:04:12 2021 rev:17 rq:925393 version:4.0.7

Changes:
--------
--- /work/SRC/openSUSE:Factory/yubikey-manager/yubikey-manager.changes  
2021-05-19 17:49:27.225516110 +0200
+++ 
/work/SRC/openSUSE:Factory/.yubikey-manager.new.1890/yubikey-manager.changes    
    2021-10-15 23:04:48.214133394 +0200
@@ -1,0 +2,19 @@
+Thu Oct 14 07:03:48 UTC 2021 - [email protected]
+
+- version update to 4.0.7
+  * Version 4.0.7 (released 2021-09-08)
+   ** Bugfix release: Fix broken naming for "YubiKey 4", and a small OATH 
issue with
+      touch Steam credentials.
+  * Version 4.0.6 (released 2021-09-08)
+   ** Improve handling of YubiKey device reboots.
+   ** More consistently mask PIN/password input in prompts.
+   ** Support switching mode over CCID for YubiKey Edge.
+   ** Run pkill from PATH instead of fixed location.
+  * Version 4.0.5 (released 2021-07-16)
+   ** Bugfix: Fix PIV feature detection for some YubiKey NEO versions.
+   ** Bugfix: Fix argument short form for --period when adding TOTP 
credentials.
+   ** Bugfix: More strict validation for some arguments, resulting in better 
error messages.
+   ** Bugfix: Correctly handle TOTP credentials using period != 30 AND 
touch_required.
+   ** Bugfix: Fix prompting for access code in the otp settings command (now 
uses "-A -").
+
+-------------------------------------------------------------------

Old:
----
  yubikey-manager-4.0.3.tar.gz
  yubikey-manager-4.0.3.tar.gz.sig

New:
----
  yubikey-manager-4.0.7.tar.gz
  yubikey-manager-4.0.7.tar.gz.sig

++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++

Other differences:
------------------
++++++ yubikey-manager.spec ++++++
--- /var/tmp/diff_new_pack.9tLf66/_old  2021-10-15 23:04:48.738133768 +0200
+++ /var/tmp/diff_new_pack.9tLf66/_new  2021-10-15 23:04:48.742133771 +0200
@@ -17,7 +17,7 @@
 
 
 Name:           yubikey-manager
-Version:        4.0.3
+Version:        4.0.7
 Release:        0
 Summary:        Python 3 library and command line tool for configuring a 
YubiKey
 License:        BSD-2-Clause

++++++ yubikey-manager-4.0.3.tar.gz -> yubikey-manager-4.0.7.tar.gz ++++++
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/yubikey-manager-4.0.3/NEWS 
new/yubikey-manager-4.0.7/NEWS
--- old/yubikey-manager-4.0.3/NEWS      2021-05-17 08:33:54.782452800 +0200
+++ new/yubikey-manager-4.0.7/NEWS      2021-09-08 12:49:30.423568500 +0200
@@ -1,3 +1,20 @@
+* Version 4.0.7 (released 2021-09-08)
+ ** Bugfix release: Fix broken naming for "YubiKey 4", and a small OATH issue 
with
+    touch Steam credentials.
+
+* Version 4.0.6 (released 2021-09-08)
+ ** Improve handling of YubiKey device reboots.
+ ** More consistently mask PIN/password input in prompts.
+ ** Support switching mode over CCID for YubiKey Edge.
+ ** Run pkill from PATH instead of fixed location.
+
+* Version 4.0.5 (released 2021-07-16)
+ ** Bugfix: Fix PIV feature detection for some YubiKey NEO versions.
+ ** Bugfix: Fix argument short form for --period when adding TOTP credentials.
+ ** Bugfix: More strict validation for some arguments, resulting in better 
error messages.
+ ** Bugfix: Correctly handle TOTP credentials using period != 30 AND 
touch_required.
+ ** Bugfix: Fix prompting for access code in the otp settings command (now 
uses "-A -").
+
 * Version 4.0.3 (released 2021-05-17)
  ** Add support for fido reset over NFC.
  ** Bugfix: The --touch argument to piv change-management-key was ignored.
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/yubikey-manager-4.0.3/PKG-INFO 
new/yubikey-manager-4.0.7/PKG-INFO
--- old/yubikey-manager-4.0.3/PKG-INFO  2021-05-17 08:34:07.618134500 +0200
+++ new/yubikey-manager-4.0.7/PKG-INFO  2021-09-08 12:49:53.598999700 +0200
@@ -1,6 +1,6 @@
 Metadata-Version: 2.1
 Name: yubikey-manager
-Version: 4.0.3
+Version: 4.0.7
 Summary: Tool for managing your YubiKey configuration.
 Home-page: https://github.com/Yubico/yubikey-manager
 License: BSD
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/yubikey-manager-4.0.3/README.adoc 
new/yubikey-manager-4.0.7/README.adoc
--- old/yubikey-manager-4.0.3/README.adoc       2021-05-17 08:33:03.311514600 
+0200
+++ new/yubikey-manager-4.0.7/README.adoc       2021-09-08 12:49:22.240559800 
+0200
@@ -86,13 +86,29 @@
   $ sudo apt update
   $ sudo apt install yubikey-manager
 
+==== FreeBSD
+Althought not being officially supported on this platform, YubiKey Manager can 
be
+installed on FreeBSD. It's available via its ports tree or as pre-built 
package.
+Should you opt to install and use YubiKey Manager on this platform, please be 
aware
+that it's **NOT** maintained by Yubico.
+
+For more information about how to install packages or ports on FreeBSD, please 
refer
+to its official documentation: 
https://docs.freebsd.org/en/books/handbook/ports[FreeBSD Handbook].
+
 ==== Source
 To install from source, see the link:doc/Development.adoc[development]
 instructions.
 
-=== Bash completion
+=== Shell completion
+
+Experimental shell completion for the command line tool is available, provided
+by the underlying CLI library (`click`) but it is not enabled by default. To
+enable it, run this command once (for Bash):
+
+  $ source <(_YKMAN_COMPLETE=bash_source ykman | sudo tee 
/etc/bash_completion.d/ykman)
 
-Experimental Bash completion for the command line tool is available, but not
-enabled by default. To enable it, run this command once:
+More information on shell completion is available here:
+https://click.palletsprojects.com/en/8.0.x/shell-completion
 
-  $ source <(_YKMAN_COMPLETE=source ykman | sudo tee 
/etc/bash_completion.d/ykman)
+NOTE: If your version of the Click dependency is older than 8.0 you need to use
+`source_bash` for the variable instead.
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/yubikey-manager-4.0.3/man/ykman.1 
new/yubikey-manager-4.0.7/man/ykman.1
--- old/yubikey-manager-4.0.3/man/ykman.1       2021-05-17 08:33:54.783446800 
+0200
+++ new/yubikey-manager-4.0.7/man/ykman.1       2021-09-08 12:49:40.281981500 
+0200
@@ -1,4 +1,4 @@
-.TH YKMAN "1" "May 2021" "ykman 4.0.3" "User Commands"
+.TH YKMAN "1" "September 2021" "ykman 4.0.7" "User Commands"
 .SH NAME
 ykman \- YubiKey Manager (ykman)
 .SH SYNOPSIS
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/yubikey-manager-4.0.3/pyproject.toml 
new/yubikey-manager-4.0.7/pyproject.toml
--- old/yubikey-manager-4.0.3/pyproject.toml    2021-05-17 08:33:54.784445800 
+0200
+++ new/yubikey-manager-4.0.7/pyproject.toml    2021-09-08 12:49:30.424196500 
+0200
@@ -1,6 +1,6 @@
 [tool.poetry]
 name = "yubikey-manager"
-version = "4.0.3"
+version = "4.0.7"
 description = "Tool for managing your YubiKey configuration."
 authors = ["Dain Nilsson <[email protected]>"]
 license = "BSD"
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/yubikey-manager-4.0.3/setup.py 
new/yubikey-manager-4.0.7/setup.py
--- old/yubikey-manager-4.0.3/setup.py  2021-05-17 08:34:07.617768500 +0200
+++ new/yubikey-manager-4.0.7/setup.py  2021-09-08 12:49:53.598719100 +0200
@@ -28,7 +28,7 @@
 
 setup_kwargs = {
     'name': 'yubikey-manager',
-    'version': '4.0.3',
+    'version': '4.0.7',
     'description': 'Tool for managing your YubiKey configuration.',
     'long_description': None,
     'author': 'Dain Nilsson',
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/yubikey-manager-4.0.3/tests/device/cli/test_otp.py 
new/yubikey-manager-4.0.7/tests/device/cli/test_otp.py
--- old/yubikey-manager-4.0.3/tests/device/cli/test_otp.py      2021-04-12 
09:23:08.093852000 +0200
+++ new/yubikey-manager-4.0.7/tests/device/cli/test_otp.py      2021-09-08 
12:49:22.245025200 +0200
@@ -484,14 +484,14 @@
         self._check_slot_2_does_not_have_access_code(ykman_cli)
 
         ykman_cli(
-            "otp", "settings", "--new-access-code", "", "2", "-f", 
input="111111111111"
+            "otp", "settings", "--new-access-code", "-", "2", "-f", 
input="111111111111"
         )
         self._check_slot_2_has_access_code(ykman_cli)
 
         ykman_cli(
             "otp",
             "--access-code",
-            "",
+            "-",
             "settings",
             "--delete-access-code",
             "2",
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/yubikey-manager-4.0.3/ykman/__init__.py 
new/yubikey-manager-4.0.7/ykman/__init__.py
--- old/yubikey-manager-4.0.3/ykman/__init__.py 2021-05-17 08:33:54.786439400 
+0200
+++ new/yubikey-manager-4.0.7/ykman/__init__.py 2021-09-08 12:49:30.426263000 
+0200
@@ -35,4 +35,4 @@
 )
 
 
-__version__ = "4.0.3"
+__version__ = "4.0.7"
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/yubikey-manager-4.0.3/ykman/base.py 
new/yubikey-manager-4.0.7/ykman/base.py
--- old/yubikey-manager-4.0.3/ykman/base.py     2021-04-12 09:23:08.122652000 
+0200
+++ new/yubikey-manager-4.0.7/ykman/base.py     2021-09-08 12:49:22.247020500 
+0200
@@ -39,7 +39,7 @@
     NEO = "YubiKey NEO"
     SKY = "Security Key by Yubico"
     YKP = "YubiKey Plus"
-    YK4 = "YubiKey 4"  # This includes YubiKey 5
+    YK4 = "YubiKey"  # This includes YubiKey 5
 
     def get_pid(self, interfaces: USB_INTERFACE) -> "PID":
         suffix = "_".join(
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/yubikey-manager-4.0.3/ykman/cli/__main__.py 
new/yubikey-manager-4.0.7/ykman/cli/__main__.py
--- old/yubikey-manager-4.0.3/ykman/cli/__main__.py     2021-05-17 
08:33:03.314506500 +0200
+++ new/yubikey-manager-4.0.7/ykman/cli/__main__.py     2021-09-08 
12:49:22.248622200 +0200
@@ -82,6 +82,15 @@
 )
 
 
+def _scan_changes(state, attempts=10):
+    for _ in range(attempts):
+        time.sleep(0.25)
+        devices, new_state = scan_devices()
+        if new_state != state:
+            return devices, new_state
+    raise TimeoutError("Timed out waiting for state change")
+
+
 def retrying_connect(serial, connections, attempts=10, state=None):
     while True:
         try:
@@ -91,17 +100,11 @@
             raise  # No need to retry
         except Exception as e:
             logger.error("Failed opening connection", exc_info=e)
-            while attempts:
-                attempts -= 1
-                _, new_state = scan_devices()
-                if new_state != state:
-                    state = new_state
-                    logger.debug("State changed, re-try connect...")
-                    break
-                logger.debug("Sleep...")
-                time.sleep(0.5)
-            else:
-                raise
+            try:
+                _, state = _scan_changes(state)
+                logger.debug("State changed, re-try connect...")
+            except TimeoutError:
+                raise e
 
 
 def print_version(ctx, param, value):
@@ -152,11 +155,11 @@
             if len(readers) == 1:
                 dev = readers[0]
                 try:
+                    conn = dev.open_connection(SmartCardConnection)
+                    info = read_info(dev.pid, conn)
                     if cmd == fido.name:
+                        conn.close()
                         conn = dev.open_connection(FidoConnection)
-                    else:
-                        conn = dev.open_connection(SmartCardConnection)
-                    info = read_info(dev.pid, conn)
                     return conn, dev, info
                 except Exception as e:
                     logger.error("Failure connecting to card", exc_info=e)
@@ -172,8 +175,12 @@
     devices, state = scan_devices()
     n_devs = sum(devices.values())
 
-    if n_devs == 0:
-        cli_fail("No YubiKey detected!")
+    if n_devs == 0:  # The device might not yet be ready, wait a bit
+        try:
+            devices, state = _scan_changes(state)
+            n_devs = sum(devices.values())
+        except TimeoutError:
+            cli_fail("No YubiKey detected!")
     if n_devs > 1:
         cli_fail(
             "Multiple YubiKeys detected. Use --device SERIAL to specify "
@@ -184,7 +191,7 @@
     pid = next(iter(devices.keys()))
     for c in connections:
         if USB_INTERFACE_MAPPING[c] & pid.get_interfaces():
-            if WIN_CTAP_RESTRICTED and c == FidoConnection:
+            if WIN_CTAP_RESTRICTED and connections == FidoConnection:
                 # FIDO-only command on Windows without Admin won't work.
                 cli_fail("FIDO access on Windows requires running as 
Administrator.")
             return retrying_connect(None, connections, state=state)
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/yubikey-manager-4.0.3/ykman/cli/config.py 
new/yubikey-manager-4.0.7/ykman/cli/config.py
--- old/yubikey-manager-4.0.3/ykman/cli/config.py       2021-05-17 
08:33:03.315503600 +0200
+++ new/yubikey-manager-4.0.7/ykman/cli/config.py       2021-09-08 
12:49:22.249622300 +0200
@@ -51,11 +51,11 @@
 logger = logging.getLogger(__name__)
 
 
-CLEAR_LOCK_CODE = "0" * 32
+CLEAR_LOCK_CODE = b"\0" * 16
 
 
-def prompt_lock_code(prompt="Enter your lock code"):
-    return click_prompt(prompt, default="", hide_input=True, 
show_default=False)
+def prompt_lock_code():
+    return click_prompt("Enter your lock code", hide_input=True)
 
 
 @click.group()
@@ -124,78 +124,51 @@
     info = ctx.obj["info"]
     app = ctx.obj["controller"]
 
-    def prompt_new_lock_code():
-        return prompt_lock_code(prompt="Enter your new lock code")
-
-    def prompt_current_lock_code():
-        return prompt_lock_code(prompt="Enter your current lock code")
-
-    def change_lock_code(lock_code, new_lock_code):
-        lock_code = _parse_lock_code(ctx, lock_code)
-        new_lock_code = _parse_lock_code(ctx, new_lock_code)
-        try:
-            app.write_device_config(
-                None,
-                False,
-                lock_code,
-                new_lock_code,
-            )
-        except Exception as e:
-            logger.error("Changing the lock code failed", exc_info=e)
-            cli_fail("Failed to change the lock code. Wrong current code?")
-
-    def set_lock_code(new_lock_code):
-        new_lock_code = _parse_lock_code(ctx, new_lock_code)
-        try:
-            app.write_device_config(
-                None,
-                False,
-                None,
-                new_lock_code,
-            )
-        except Exception as e:
-            logger.error("Setting the lock code failed", exc_info=e)
-            cli_fail("Failed to set the lock code.")
-
-    if generate and new_lock_code:
-        ctx.fail("Invalid options: --new-lock-code conflicts with --generate.")
+    if sum(1 for arg in [new_lock_code, generate, clear] if arg) > 1:
+        cli_fail(
+            "Invalid options: Only one of --new-lock-code, --generate, "
+            "and --clear may be used."
+        )
 
+    # Get the new lock code to set
     if clear:
-        new_lock_code = CLEAR_LOCK_CODE
-
-    if generate:
-        new_lock_code = os.urandom(16).hex()
-        click.echo(f"Using a randomly generated lock code: {new_lock_code}")
+        set_code = CLEAR_LOCK_CODE
+    elif generate:
+        set_code = os.urandom(16)
+        click.echo(f"Using a randomly generated lock code: {set_code.hex()}")
         force or click.confirm(
             "Lock configuration with this lock code?", abort=True, err=True
         )
+    else:
+        if not new_lock_code:
+            new_lock_code = click_prompt(
+                "Enter your new lock code", hide_input=True, 
confirmation_prompt=True
+            )
+        set_code = _parse_lock_code(ctx, new_lock_code)
 
+    # Get the current lock code to use
     if info.is_locked:
-        if lock_code:
-            if new_lock_code:
-                change_lock_code(lock_code, new_lock_code)
-            else:
-                new_lock_code = prompt_new_lock_code()
-                change_lock_code(lock_code, new_lock_code)
-        else:
-            if new_lock_code:
-                lock_code = prompt_current_lock_code()
-                change_lock_code(lock_code, new_lock_code)
-            else:
-                lock_code = prompt_current_lock_code()
-                new_lock_code = prompt_new_lock_code()
-                change_lock_code(lock_code, new_lock_code)
+        if not lock_code:
+            lock_code = click_prompt("Enter your current lock code", 
hide_input=True)
+        use_code = _parse_lock_code(ctx, lock_code)
     else:
         if lock_code:
-            cli_fail(
-                "There is no current lock code set. Use --new-lock-code to set 
one."
-            )
-        else:
-            if new_lock_code:
-                set_lock_code(new_lock_code)
-            else:
-                new_lock_code = prompt_new_lock_code()
-                set_lock_code(new_lock_code)
+            cli_fail("No lock code is currently set. Use --new-lock-code to 
set one.")
+        use_code = None
+
+    # Set new lock code
+    try:
+        app.write_device_config(
+            None,
+            False,
+            use_code,
+            set_code,
+        )
+    except Exception as e:
+        logger.error("Setting the lock code failed", exc_info=e)
+        if info.is_locked:
+            cli_fail("Failed to change the lock code. Wrong current code?")
+        cli_fail("Failed to set the lock code.")
 
 
 @config.command()
@@ -289,6 +262,7 @@
     info = ctx.obj["info"]
     usb_supported = info.supported_capabilities[TRANSPORT.USB]
     usb_enabled = info.config.enabled_capabilities[TRANSPORT.USB]
+    usb_interfaces = USB_INTERFACE.for_capabilities(usb_enabled)
     flags = info.config.device_flags
 
     if enable_all:
@@ -323,6 +297,8 @@
 
     ensure_not_all_disabled(ctx, usb_enabled)
 
+    reboot = usb_interfaces != USB_INTERFACE.for_capabilities(usb_enabled)
+
     f_confirm = ""
     if enable:
         f_confirm += f"Enable {', '.join(str(app) for app in enable)}.\n"
@@ -336,6 +312,8 @@
         f_confirm += f"Set autoeject timeout to {autoeject_timeout}.\n"
     if chalresp_timeout:
         f_confirm += f"Set challenge-response timeout to {chalresp_timeout}.\n"
+    if reboot:
+        f_confirm += "This will cause the YubiKey to reboot.\n"
     f_confirm += "Configure USB?"
 
     is_locked = info.is_locked
@@ -362,7 +340,7 @@
                 chalresp_timeout,
                 flags,
             ),
-            True,
+            reboot,
             lock_code,
         )
     except Exception as e:
@@ -512,12 +490,10 @@
         pass  # Not a numeric mode, parse string
 
     try:
-        interfaces = USB_INTERFACE(0)
         if mode[0] in ["+", "-"]:
             info = ctx.obj["info"]
             usb_enabled = info.config.enabled_capabilities[TRANSPORT.USB]
-            my_mode = _mode_from_usb_enabled(usb_enabled)
-            interfaces |= my_mode.interfaces
+            interfaces = USB_INTERFACE.for_capabilities(usb_enabled)
             for mod in re.findall(r"[+-][A-Z]+", mode.upper()):
                 interface = _parse_interface_string(mod[1:])
                 if mod.startswith("+"):
@@ -525,6 +501,7 @@
                 else:
                     interfaces ^= interface
         else:
+            interfaces = USB_INTERFACE(0)
             for t in re.split(r"[+]+", mode.upper()):
                 if t:
                     interfaces |= _parse_interface_string(t)
@@ -534,17 +511,6 @@
     return Mode(interfaces)
 
 
-def _mode_from_usb_enabled(usb_enabled):
-    interfaces = USB_INTERFACE(0)
-    if CAPABILITY.OTP & usb_enabled:
-        interfaces |= USB_INTERFACE.OTP
-    if (CAPABILITY.U2F | CAPABILITY.FIDO2) & usb_enabled:
-        interfaces |= USB_INTERFACE.FIDO
-    if (CAPABILITY.OPENPGP | CAPABILITY.PIV | CAPABILITY.OATH) & usb_enabled:
-        interfaces |= USB_INTERFACE.CCID
-    return Mode(interfaces)
-
-
 @config.command()
 @click.argument("mode", callback=_parse_mode_string)
 @click.option(
@@ -598,9 +564,9 @@
     info = ctx.obj["info"]
     mgmt = ctx.obj["controller"]
     usb_enabled = info.config.enabled_capabilities[TRANSPORT.USB]
-    my_mode = _mode_from_usb_enabled(usb_enabled)
+    my_mode = Mode(USB_INTERFACE.for_capabilities(usb_enabled))
     usb_supported = info.supported_capabilities[TRANSPORT.USB]
-    interfaces_supported = _mode_from_usb_enabled(usb_supported).interfaces
+    interfaces_supported = USB_INTERFACE.for_capabilities(usb_supported)
     pid = ctx.obj["pid"]
     if pid:
         key_type = pid.get_type()
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/yubikey-manager-4.0.3/ykman/cli/fido.py 
new/yubikey-manager-4.0.7/ykman/cli/fido.py
--- old/yubikey-manager-4.0.3/ykman/cli/fido.py 2021-05-17 08:33:03.317499400 
+0200
+++ new/yubikey-manager-4.0.7/ykman/cli/fido.py 2021-09-08 12:49:22.250619400 
+0200
@@ -197,6 +197,10 @@
             cli_fail("Only one YubiKey can be connected to perform a reset.")
         is_fips = is_fips_version(ctx.obj["info"].version)
 
+        ctap2 = ctx.obj.get("ctap2")
+        if not is_fips and not ctap2:
+            cli_fail("This YubiKey does not support FIDO reset.")
+
         def prompt_re_insert():
             click.echo("Remove and re-insert your YubiKey to perform the 
reset...")
 
@@ -317,14 +321,14 @@
         conn = ctx.obj["conn"]
     else:
         ctap2 = ctx.obj.get("ctap2")
+        if not ctap2:
+            cli_fail("PIN is not supported on this YubiKey.")
         client_pin = ClientPin(ctap2)
 
     def prompt_new_pin():
         return click_prompt(
             "Enter your new PIN",
-            default="",
             hide_input=True,
-            show_default=False,
             confirmation_prompt=True,
         )
 
@@ -453,7 +457,7 @@
 
 
 def _prompt_current_pin(prompt="Enter your current PIN"):
-    return click_prompt(prompt, default="", hide_input=True, 
show_default=False)
+    return click_prompt(prompt, hide_input=True)
 
 
 def _fail_if_not_valid_pin(ctx, pin=None, is_fips=False):
@@ -681,8 +685,8 @@
     ID          The ID of the fingerprint to rename (as shown in "list").
     NAME        A short readable name for the fingerprint (eg. "Left thumb").
     """
-    if len(name) >= 16:
-        ctx.fail("Fingerprint name must be a maximum of 15 characters")
+    if len(name.encode()) >= 16:
+        ctx.fail("Fingerprint name must be a maximum of 15 bytes")
 
     bio = _init_bio(ctx, pin)
     enrollments = bio.enumerate_enrollments()
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/yubikey-manager-4.0.3/ykman/cli/oath.py 
new/yubikey-manager-4.0.7/ykman/cli/oath.py
--- old/yubikey-manager-4.0.3/ykman/cli/oath.py 2021-04-12 09:23:08.132443000 
+0200
+++ new/yubikey-manager-4.0.7/ykman/cli/oath.py 2021-09-08 12:49:30.427506000 
+0200
@@ -371,9 +371,9 @@
     default=0,
     help="Initial counter value for HOTP accounts.",
 )
[email protected]("-i", "--issuer", help="Issuer of the account.")
[email protected]("-i", "--issuer", help="Issuer of the account (optional).")
 @click.option(
-    "-p",
+    "-P",
     "--period",
     help="Number of seconds a TOTP code is valid.",
     default=30,
@@ -403,6 +403,10 @@
     Add a new account.
 
     This will add a new OATH account to the YubiKey.
+
+    \b
+    NAME    Human readable name of the account, such as a username or e-mail 
address.
+    SECRET  Base32-encoded secret/key value provided by the server.
     """
 
     digits = int(digits)
@@ -452,7 +456,7 @@
 
     if not data:
         while True:
-            uri = click_prompt("Enter an OATH URI")
+            uri = click_prompt("Enter an OATH URI (otpauth://)")
             try:
                 data = CredentialData.parse_uri(uri)
                 break
@@ -524,7 +528,7 @@
 @click_show_hidden_option
 @click.pass_context
 @click.option("-o", "--oath-type", is_flag=True, help="Display the OATH type.")
[email protected]("-p", "--period", is_flag=True, help="Display the period.")
[email protected]("-P", "--period", is_flag=True, help="Display the period.")
 @click_password_option
 @click_remember_option
 def list(ctx, show_hidden, oath_type, period, password, remember):
@@ -617,10 +621,10 @@
                 code = "[Requires Touch]"
             elif cred.oath_type == OATH_TYPE.HOTP:
                 code = "[HOTP Account]"
+            elif is_steam(cred):
+                code = calculate_steam(session, cred)
             else:
                 code = ""
-            if is_steam(cred):
-                code = calculate_steam(session, cred)
             outputs.append((_string_id(cred), code))
 
         longest_name = max(len(n) for (n, c) in outputs) if outputs else 0
@@ -643,8 +647,8 @@
     Rename an account (Requires YubiKey 5.3 or later).
 
     \b
-    QUERY       A query to match a single account (as shown in "list").
-    NAME        The name of the account (use "<issuer>:<name>" to specify 
issuer).
+    QUERY  A query to match a single account (as shown in "list").
+    NAME   The name of the account (use "<issuer>:<name>" to specify issuer).
     """
 
     _init_session(ctx, password, remember)
@@ -663,7 +667,7 @@
         new_id = _format_cred_id(issuer, name, cred.oath_type, cred.period)
         if any(cred.id == new_id for cred in creds):
             cli_fail(
-                "Another account with ID {new_id.decode()} "
+                f"Another account with ID {new_id.decode()} "
                 "already exists on this YubiKey."
             )
         if force or (
@@ -693,7 +697,9 @@
     Delete an account.
 
     Delete an account from the YubiKey.
-    Provide a query string to match the account to delete.
+
+    \b
+    QUERY  A query to match a single account (as shown in "list").
     """
 
     _init_session(ctx, password, remember)
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/yubikey-manager-4.0.3/ykman/cli/openpgp.py 
new/yubikey-manager-4.0.7/ykman/cli/openpgp.py
--- old/yubikey-manager-4.0.3/ykman/cli/openpgp.py      2021-04-12 
09:23:08.133595200 +0200
+++ new/yubikey-manager-4.0.7/ykman/cli/openpgp.py      2021-09-08 
12:49:22.252615700 +0200
@@ -207,10 +207,10 @@
     \b
     Off (default)   No touch required
     On              Touch required
-    Fixed           Touch required, can't be disabled without a full reset
+    Fixed           Touch required, can't be disabled without deleting the 
private key
     Cached          Touch required, cached for 15s after use
     Cached-Fixed    Touch required, cached for 15s after use, can't be disabled
-                    without a full reset
+                    without deleting the private key
     """
     controller = ctx.obj["controller"]
 
@@ -225,11 +225,15 @@
     if admin_pin is None:
         admin_pin = click_prompt("Enter Admin PIN", hide_input=True)
 
-    if force or click.confirm(
-        f"Set touch policy of {key.value.lower()} key to {policy_name}?",
-        abort=True,
-        err=True,
-    ):
+    prompt = f"Set touch policy of {key.value.lower()} key to {policy_name}?"
+    if policy.is_fixed:
+        prompt = (
+            "WARNING: This touch policy cannot be changed without deleting the 
"
+            + "corresponding key slot!\n"
+            + prompt
+        )
+
+    if force or click.confirm(prompt, abort=True, err=True):
         try:
             controller.verify_admin(admin_pin)
             controller.set_touch(key, policy)
@@ -278,7 +282,7 @@
 @click.pass_context
 @click.option("-P", "--pin", help="PIN code.")
 @click_format_option
[email protected]("key", metavar="KEY", type=EnumChoice(KEY_SLOT))
[email protected]("key", metavar="KEY", type=EnumChoice(KEY_SLOT, 
hidden=[KEY_SLOT.ATT]))
 @click.argument("certificate", type=click.File("wb"), metavar="CERTIFICATE")
 def attest(ctx, key, certificate, pin, format):
     """
@@ -295,7 +299,7 @@
     controller = ctx.obj["controller"]
 
     if not pin:
-        pin = click_prompt("Enter PIN", default="", hide_input=True, 
show_default=False)
+        pin = click_prompt("Enter PIN", hide_input=True)
 
     try:
         cert = controller.read_certificate(key)
@@ -339,6 +343,10 @@
     CERTIFICATE File to write certificate to. Use '-' to use stdout.
     """
     controller = ctx.obj["controller"]
+
+    if controller.version < (5, 2, 0) and key != KEY_SLOT.AUT:
+        cli_fail(f"Certificate slot {key.name} requires YubiKey 5.2.0 or 
later.")
+
     try:
         cert = controller.read_certificate(key)
     except ValueError:
@@ -358,6 +366,10 @@
     KEY         Key slot to delete certificate from (sig, enc, aut, or att).
     """
     controller = ctx.obj["controller"]
+
+    if controller.version < (5, 2, 0) and key != KEY_SLOT.AUT:
+        cli_fail(f"Certificate slot {key.name} requires YubiKey 5.2.0 or 
later.")
+
     if admin_pin is None:
         admin_pin = click_prompt("Enter Admin PIN", hide_input=True)
     try:
@@ -383,6 +395,9 @@
     """
     controller = ctx.obj["controller"]
 
+    if controller.version < (5, 2, 0) and key != KEY_SLOT.AUT:
+        cli_fail(f"Certificate slot {key.name} requires YubiKey 5.2.0 or 
later.")
+
     if admin_pin is None:
         admin_pin = click_prompt("Enter Admin PIN", hide_input=True)
 
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/yubikey-manager-4.0.3/ykman/cli/otp.py 
new/yubikey-manager-4.0.7/ykman/cli/otp.py
--- old/yubikey-manager-4.0.3/ykman/cli/otp.py  2021-05-17 08:33:03.319670700 
+0200
+++ new/yubikey-manager-4.0.7/ykman/cli/otp.py  2021-09-08 12:49:22.253611000 
+0200
@@ -25,6 +25,7 @@
 # ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
 # POSSIBILITY OF SUCH DAMAGE.
 
+from base64 import b32encode
 from yubikit.yubiotp import (
     SLOT,
     YubiOtpSession,
@@ -104,7 +105,7 @@
     logger.error("Failed to write to device", exc_info=exc_info)
     cli_fail(
         "Failed to write to the YubiKey. Make sure the device does not "
-        "have restricted access."
+        'have restricted access (see "ykman otp --help" for more info).'
     )
 
 
@@ -124,7 +125,7 @@
     "--access-code",
     required=False,
     metavar="HEX",
-    help="A 6 byte access code. Set to empty to use a prompt for input.",
+    help='A 6 byte access code. Use "-" as a value to prompt for input.',
 )
 def otp(ctx, access_code):
     """
@@ -136,7 +137,9 @@
     A slot configuration may be write-protected with an access code. This
     prevents the configuration to be overwritten without the access code
     provided. Mode switching the YubiKey is not possible when a slot is
-    configured with an access code.
+    configured with an access code. To provide an access code to commands
+    which require it, use the --access-code option. Note that this option must
+    be given directly after the "otp" command, before any sub-command.
 
     Examples:
 
@@ -155,12 +158,16 @@
     \b
       Program a random 38 characters long static password to slot 2:
       $ ykman otp static --generate 2 --length 38
+
+    \b
+      Remove a currently set access code from slot 2):
+      $ ykman otp --access-code 0123456789ab settings 2 --delete-access-code
     """
 
     ctx.obj["session"] = YubiOtpSession(ctx.obj["conn"])
     if access_code is not None:
-        if access_code == "":
-            access_code = click_prompt("Enter the access code", 
show_default=False)
+        if access_code == "-":
+            access_code = click_prompt("Enter the access code", 
hide_input=True)
 
         try:
             access_code = parse_access_code_hex(access_code)
@@ -542,6 +549,13 @@
                 "No secret key given. Please remove the --force flag, "
                 "set the KEY argument or set the --generate flag."
             )
+        elif generate:
+            key = os.urandom(20)
+            if totp:
+                b32key = b32encode(key).decode()
+                click.echo(f"Using a randomly generated key (Base32): 
{b32key}")
+            else:
+                click.echo(f"Using a randomly generated key: {key.hex()}")
         elif totp:
             while True:
                 key = click_prompt("Enter a secret key (base32)")
@@ -551,12 +565,8 @@
                 except Exception as e:
                     click.echo(e)
         else:
-            if generate:
-                key = os.urandom(20)
-                click.echo(f"Using a randomly generated key: {key.hex()}")
-            else:
-                key = click_prompt("Enter a secret key")
-                key = parse_oath_key(key)
+            key = click_prompt("Enter a secret key")
+            key = parse_oath_key(key)
 
     cred_type = "TOTP" if totp else "challenge-response"
     force or click.confirm(
@@ -697,8 +707,8 @@
     "--new-access-code",
     metavar="HEX",
     required=False,
-    help="Set a new 6 byte access code for the slot. Set to empty to use a "
-    "prompt for input.",
+    help='Set a new 6 byte access code for the slot. Use "-" as a value to 
prompt for '
+    "input.",
 )
 @click.option(
     "--delete-access-code", is_flag=True, help="Remove access code from the 
slot."
@@ -742,9 +752,15 @@
     """
     session = ctx.obj["session"]
 
-    if (new_access_code is not None) and delete_access_code:
+    if new_access_code and delete_access_code:
         ctx.fail("--new-access-code conflicts with --delete-access-code.")
 
+    if delete_access_code and not ctx.obj["access_code"]:
+        cli_fail(
+            "--delete-access-code used without providing an access code "
+            '(see "ykman otp --help" for more info).'
+        )
+
     if not session.get_config_state().is_configured(slot):
         cli_fail("Not possible to update settings on an empty slot.")
 
@@ -752,8 +768,10 @@
         if not delete_access_code:
             new_access_code = ctx.obj["access_code"]
     else:
-        if new_access_code == "":
-            new_access_code = click_prompt("Enter new access code", 
show_default=False)
+        if new_access_code == "-":
+            new_access_code = click_prompt(
+                "Enter new access code", hide_input=True, 
confirmation_prompt=True
+            )
 
         try:
             new_access_code = parse_access_code_hex(new_access_code)
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/yubikey-manager-4.0.3/ykman/device.py 
new/yubikey-manager-4.0.7/ykman/device.py
--- old/yubikey-manager-4.0.3/ykman/device.py   2021-05-17 08:33:03.322661000 
+0200
+++ new/yubikey-manager-4.0.7/ykman/device.py   2021-09-08 12:49:30.428503800 
+0200
@@ -459,9 +459,11 @@
     ):
         usb_enabled = info.supported_capabilities[TRANSPORT.USB]
         if usb_enabled == (CAPABILITY.OTP | CAPABILITY.U2F | 
USB_INTERFACE.CCID):
-            # YubiKey Edge, hide unusable CCID interface
-            usb_enabled = CAPABILITY.OTP | CAPABILITY.U2F
-            info.supported_capabilities = {TRANSPORT.USB: usb_enabled}
+            # YubiKey Edge, hide unusable CCID interface from supported
+            # usb_enabled = CAPABILITY.OTP | CAPABILITY.U2F
+            info.supported_capabilities = {
+                TRANSPORT.USB: CAPABILITY.OTP | CAPABILITY.U2F
+            }
 
         if USB_INTERFACE.OTP not in interfaces:
             usb_enabled &= ~CAPABILITY.OTP
@@ -540,14 +542,22 @@
         if info.has_transport(TRANSPORT.NFC):
             device_name = "Security Key NFC"
     elif key_type == YUBIKEY.YK4:
-        if info.version[0] == 0:
-            return "Yubikey (%d.%d.%d)" % info.version
+        major_version = info.version[0]
+        if major_version < 4:
+            if info.version[0] == 0:
+                return "YubiKey (%d.%d.%d)" % info.version
+            else:
+                return "YubiKey"
+        elif major_version == 4:
+            if is_fips_version(info.version):  # YK4 FIPS
+                device_name = "YubiKey FIPS"
+            elif usb_supported == CAPABILITY.OTP | CAPABILITY.U2F:
+                device_name = "YubiKey Edge"
+            else:
+                device_name = "YubiKey 4"
+
         if _is_preview(info.version):
             device_name = "YubiKey Preview"
-        elif is_fips_version(info.version):  # YK4 FIPS
-            device_name = "YubiKey FIPS"
-        elif usb_supported == CAPABILITY.OTP | CAPABILITY.U2F:
-            device_name = "YubiKey Edge"
         elif info.version >= (5, 1, 0):
             is_nano = info.form_factor in (
                 FORM_FACTOR.USB_A_NANO,
@@ -560,9 +570,12 @@
                 FORM_FACTOR.USB_C_BIO,
             )
 
-            name_parts = ["YubiKey"]
-            if not is_bio:
-                name_parts.append("5")
+            if info.is_sky:
+                name_parts = ["Security Key"]
+            else:
+                name_parts = ["YubiKey"]
+                if not is_bio:
+                    name_parts.append("5")
             if is_c:
                 name_parts.append("C")
             elif info.form_factor == FORM_FACTOR.USB_C_LIGHTNING:
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/yubikey-manager-4.0.3/ykman/openpgp.py 
new/yubikey-manager-4.0.7/ykman/openpgp.py
--- old/yubikey-manager-4.0.3/ykman/openpgp.py  2021-04-12 09:23:08.149151600 
+0200
+++ new/yubikey-manager-4.0.7/ykman/openpgp.py  2021-09-08 12:49:22.256510700 
+0200
@@ -85,6 +85,10 @@
     CACHED = 0x03
     CACHED_FIXED = 0x04
 
+    @property
+    def is_fixed(self):
+        return "FIXED" in self.name
+
     def __str__(self):
         if self == TOUCH_MODE.OFF:
             return "Off"
@@ -335,16 +339,23 @@
         self._app.send_apdu(0, INS.PUT_DATA, do >> 8, do & 0xFF, data)
 
     def _select_certificate(self, key_slot):
-        data: bytes = Tlv(0x60, Tlv(0x5C, b"\x7f\x21"))
-        if self.version <= (5, 4, 3):  # These use a non-standard byte in the 
command.
-            data = b"\x06" + data  # 6 is the length of the data.
-        self._app.send_apdu(
-            0,
-            INS.SELECT_DATA,
-            3 - key_slot.indx,
-            0x04,
-            data,
-        )
+        try:
+            require_version(self.version, (5, 2, 0))
+            data: bytes = Tlv(0x60, Tlv(0x5C, b"\x7f\x21"))
+            if self.version <= (5, 4, 3):
+                # These use a non-standard byte in the command.
+                data = b"\x06" + data  # 6 is the length of the data.
+            self._app.send_apdu(
+                0,
+                INS.SELECT_DATA,
+                3 - key_slot.indx,
+                0x04,
+                data,
+            )
+        except NotSupportedError:
+            if key_slot == KEY_SLOT.AUT:
+                return  # Older version still support AUT, which is the 
default slot.
+            raise
 
     def _read_version(self):
         bcd_hex = self._app.send_apdu(0, INS.GET_VERSION, 0, 0).hex()
@@ -454,8 +465,8 @@
         )
 
     def read_certificate(self, key_slot):
-        require_version(self.version, (5, 2, 0))
         if key_slot == KEY_SLOT.ATT:
+            require_version(self.version, (5, 2, 0))
             data = self._get_data(DO.ATT_CERTIFICATE)
         else:
             self._select_certificate(key_slot)
@@ -466,9 +477,9 @@
 
     def import_certificate(self, key_slot, certificate):
         """Requires Admin PIN verification."""
-        require_version(self.version, (5, 2, 0))
         cert_data = certificate.public_bytes(Encoding.DER)
         if key_slot == KEY_SLOT.ATT:
+            require_version(self.version, (5, 2, 0))
             self._put_data(DO.ATT_CERTIFICATE, cert_data)
         else:
             self._select_certificate(key_slot)
@@ -566,8 +577,8 @@
 
     def delete_certificate(self, key_slot):
         """Requires Admin PIN verification."""
-        require_version(self.version, (5, 2, 0))
         if key_slot == KEY_SLOT.ATT:
+            require_version(self.version, (5, 2, 0))
             self._put_data(DO.ATT_CERTIFICATE, b"")
         else:
             self._select_certificate(key_slot)
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/yubikey-manager-4.0.3/ykman/pcsc/__init__.py 
new/yubikey-manager-4.0.7/ykman/pcsc/__init__.py
--- old/yubikey-manager-4.0.3/ykman/pcsc/__init__.py    2021-04-12 
09:23:08.151450900 +0200
+++ new/yubikey-manager-4.0.7/ykman/pcsc/__init__.py    2021-09-08 
12:49:22.257508300 +0200
@@ -139,7 +139,7 @@
                 killed = True
     except ImportError:
         # Works for Linux and OS X.
-        return_code = subprocess.call(["/usr/bin/pkill", "-9", "scdaemon"])  # 
nosec
+        return_code = subprocess.call(["pkill", "-9", "scdaemon"])  # nosec
         if return_code == 0:
             killed = True
     if killed:
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/yubikey-manager-4.0.3/yubikit/management.py 
new/yubikey-manager-4.0.7/yubikit/management.py
--- old/yubikey-manager-4.0.3/yubikit/management.py     2021-05-17 
08:33:03.328645000 +0200
+++ new/yubikey-manager-4.0.7/yubikit/management.py     2021-09-08 
12:49:22.258505600 +0200
@@ -56,15 +56,6 @@
 
 
 @unique
-class USB_INTERFACE(IntFlag):
-    """YubiKey USB interface identifiers."""
-
-    OTP = 0x01
-    FIDO = 0x02
-    CCID = 0x04
-
-
-@unique
 class CAPABILITY(IntFlag):
     """YubiKey Application identifiers."""
 
@@ -88,6 +79,37 @@
 
 
 @unique
+class USB_INTERFACE(IntFlag):
+    """YubiKey USB interface identifiers."""
+
+    OTP = 0x01
+    FIDO = 0x02
+    CCID = 0x04
+
+    def supports_connection(self, connection_type) -> bool:
+        if issubclass(connection_type, SmartCardConnection):
+            return USB_INTERFACE.CCID in self
+        if issubclass(connection_type, FidoConnection):
+            return USB_INTERFACE.FIDO in self
+        if issubclass(connection_type, OtpConnection):
+            return USB_INTERFACE.OTP in self
+        return False
+
+    @staticmethod
+    def for_capabilities(capabilities: CAPABILITY) -> "USB_INTERFACE":
+        ifaces = USB_INTERFACE(0)
+        if capabilities & CAPABILITY.OTP:
+            ifaces |= USB_INTERFACE.OTP
+        if capabilities & (CAPABILITY.U2F | CAPABILITY.FIDO2):
+            ifaces |= USB_INTERFACE.FIDO
+        if capabilities & (
+            CAPABILITY.OATH | CAPABILITY.PIV | CAPABILITY.OPENPGP | 
CAPABILITY.HSMAUTH
+        ):
+            ifaces |= USB_INTERFACE.CCID
+        return ifaces
+
+
+@unique
 class FORM_FACTOR(IntEnum):
     """YubiKey device form factors."""
 
@@ -200,6 +222,7 @@
     supported_capabilities: Mapping[TRANSPORT, CAPABILITY]
     is_locked: bool
     is_fips: bool = False
+    is_sky: bool = False
 
     def has_transport(self, transport: TRANSPORT) -> bool:
         return transport in self.supported_capabilities
@@ -214,6 +237,7 @@
         ff_value = bytes2int(data.get(TAG_FORM_FACTOR, b"\0"))
         form_factor = FORM_FACTOR.from_code(ff_value)
         fips = bool(ff_value & 0x80)
+        sky = bool(ff_value & 0x40)
         if TAG_VERSION in data:
             version = Version.from_bytes(data[TAG_VERSION])
         else:
@@ -244,6 +268,7 @@
             supported,
             locked,
             fips,
+            sky,
         )
 
 
@@ -345,7 +370,11 @@
 class _ManagementSmartCardBackend(_Backend):
     def __init__(self, smartcard_connection):
         self.protocol = SmartCardProtocol(smartcard_connection)
-        select_str = self.protocol.select(AID.MANAGEMENT).decode()
+        select_bytes = self.protocol.select(AID.MANAGEMENT)
+        if select_bytes[-2:] == b"\x90\x00":
+            # YubiKey Edge incorrectly appends SW twice.
+            select_bytes = select_bytes[:-2]
+        select_str = select_bytes.decode()
         self.version = Version.from_string(select_str)
         # For YubiKey NEO, we use the OTP application for further commands
         if self.version[0] == 3:
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/yubikey-manager-4.0.3/yubikit/oath.py 
new/yubikey-manager-4.0.7/yubikit/oath.py
--- old/yubikey-manager-4.0.3/yubikit/oath.py   2021-04-12 09:23:08.171612300 
+0200
+++ new/yubikey-manager-4.0.7/yubikit/oath.py   2021-09-08 12:49:22.259502400 
+0200
@@ -249,6 +249,7 @@
         self._version, self._salt, self._challenge = _parse_select(
             self.protocol.select(AID.OATH)
         )
+        self._has_key = self._challenge is not None
         self._device_id = _get_device_id(self._salt)
         self.protocol.enable_touch_workaround(self._version)
 
@@ -261,12 +262,17 @@
         return self._device_id
 
     @property
+    def has_key(self) -> bool:
+        return self._has_key
+
+    @property
     def locked(self) -> bool:
         return self._challenge is not None
 
     def reset(self) -> None:
         self.protocol.send_apdu(0, INS_RESET, 0xDE, 0xAD)
         _, self._salt, self._challenge = 
_parse_select(self.protocol.select(AID.OATH))
+        self._has_key = False
         self._device_id = _get_device_id(self._salt)
 
     def derive_key(self, password: str) -> bytes:
@@ -298,9 +304,11 @@
                 + Tlv(TAG_RESPONSE, response)
             ),
         )
+        self._has_key = True
 
     def unset_key(self) -> None:
         self.protocol.send_apdu(0, INS_SET_CODE, 0, 0, Tlv(TAG_KEY))
+        self._has_key = False
 
     def put_credential(
         self, credential_data: CredentialData, touch_required: bool = False
@@ -397,12 +405,12 @@
             )
 
             code = None  # Will be None for HOTP and touch
-            if oath_type == OATH_TYPE.TOTP:
-                if period != DEFAULT_PERIOD:
+            if resp_tag == TAG_TRUNCATED:  # Only TOTP, no-touch here
+                if period == DEFAULT_PERIOD:
+                    code = _format_code(credential, timestamp, tlv.value)
+                else:
                     # Non-standard period, recalculate
                     code = self.calculate_code(credential, timestamp)
-                elif resp_tag == TAG_TRUNCATED:
-                    code = _format_code(credential, timestamp, tlv.value)
             entries[credential] = code
 
         return entries
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/yubikey-manager-4.0.3/yubikit/piv.py 
new/yubikey-manager-4.0.7/yubikit/piv.py
--- old/yubikey-manager-4.0.3/yubikit/piv.py    2021-04-12 09:23:08.172763000 
+0200
+++ new/yubikey-manager-4.0.7/yubikit/piv.py    2021-09-08 12:49:22.259502400 
+0200
@@ -73,7 +73,7 @@
 
 # Don't treat pre 1.0 versions as "developer builds".
 def require_version(my_version: Version, *args, **kwargs):
-    if my_version <= (0, 1, 3):  # Last pre 1.0 release of ykneo-piv
+    if my_version <= (0, 1, 4):  # Last pre 1.0 release of ykneo-piv
         my_version = Version(1, 0, 0)
     _require_version(my_version, *args, **kwargs)
 
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/yubikey-manager-4.0.3/yubikit/yubiotp.py 
new/yubikey-manager-4.0.7/yubikit/yubiotp.py
--- old/yubikey-manager-4.0.3/yubikit/yubiotp.py        2021-04-12 
09:23:08.173915100 +0200
+++ new/yubikey-manager-4.0.7/yubikit/yubiotp.py        2021-09-08 
12:49:22.260499700 +0200
@@ -438,7 +438,7 @@
         return self
 
     def imf(self: Cfg, imf: int) -> Cfg:
-        if not (imf % 16 == 0 or 0 <= imf <= 0xFFFF0):
+        if not (imf % 16 == 0 and 0 <= imf <= 0xFFFF0):
             raise ValueError(
                 f"imf should be between {0} and {1048560}, evenly dividable by 
16"
             )

Reply via email to