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"
)