Hello community,

here is the log from the commit of package yubikey-manager for openSUSE:Factory 
checked in at 2019-06-30 10:22:09
++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Comparing /work/SRC/openSUSE:Factory/yubikey-manager (Old)
 and      /work/SRC/openSUSE:Factory/.yubikey-manager.new.4615 (New)
++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++

Package is "yubikey-manager"

Sun Jun 30 10:22:09 2019 rev:11 rq:712543 version:3.0.0

Changes:
--------
--- /work/SRC/openSUSE:Factory/yubikey-manager/yubikey-manager.changes  
2019-06-18 14:56:18.253404922 +0200
+++ 
/work/SRC/openSUSE:Factory/.yubikey-manager.new.4615/yubikey-manager.changes    
    2019-06-30 10:22:26.987708605 +0200
@@ -1,0 +2,10 @@
+Sat Jun 29 20:32:49 UTC 2019 - Karol Babioch <kbabi...@suse.com>
+
+- Version 3.0.0 (released 2019-06-24)
+  * Add support for new YubiKey Preview and lightning form factor
+  * FIDO: Support for credential management
+  * OpenPGP: Support for OpenPGP attestation, cardholder certificates and
+    cached touch policies
+  * OTP: Add flag for using numeric keypad when sending digits 
+
+-------------------------------------------------------------------

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

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

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

Other differences:
------------------
++++++ yubikey-manager.spec ++++++
--- /var/tmp/diff_new_pack.t9CyQJ/_old  2019-06-30 10:22:27.447709320 +0200
+++ /var/tmp/diff_new_pack.t9CyQJ/_new  2019-06-30 10:22:27.451709326 +0200
@@ -17,7 +17,7 @@
 
 
 Name:           yubikey-manager
-Version:        2.1.1
+Version:        3.0.0
 Release:        0
 Summary:        Python 3 library and command line tool for configuring a 
YubiKey
 License:        BSD-2-Clause

++++++ yubikey-manager-2.1.1.tar.gz -> yubikey-manager-3.0.0.tar.gz ++++++
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/yubikey-manager-2.1.1/NEWS 
new/yubikey-manager-3.0.0/NEWS
--- old/yubikey-manager-2.1.1/NEWS      2019-05-28 09:04:43.000000000 +0200
+++ new/yubikey-manager-3.0.0/NEWS      2019-06-24 09:07:27.000000000 +0200
@@ -1,3 +1,9 @@
+* Version 3.0.0 (released 2019-06-24)
+ ** Add support for new YubiKey Preview and lightning form factor
+ ** FIDO: Support for credential management
+ ** OpenPGP: Support for OpenPGP attestation, cardholder certificates and 
cached touch policies
+ ** OTP: Add flag for using numeric keypad when sending digits
+
 * Version 2.1.1 (released 2019-05-28)
  ** OTP: Add initial support for uploading Yubico OTP credentials to YubiCloud
  ** Don't automatically select the U2F applet on YubiKey NEO, it might be 
blocked by the OS
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/yubikey-manager-2.1.1/PKG-INFO 
new/yubikey-manager-3.0.0/PKG-INFO
--- old/yubikey-manager-2.1.1/PKG-INFO  2019-05-28 10:05:25.000000000 +0200
+++ new/yubikey-manager-3.0.0/PKG-INFO  2019-06-24 09:12:28.000000000 +0200
@@ -1,13 +1,12 @@
-Metadata-Version: 1.2
+Metadata-Version: 1.1
 Name: yubikey-manager
-Version: 2.1.1
+Version: 3.0.0
 Summary: Tool for managing your YubiKey configuration.
 Home-page: https://github.com/Yubico/yubikey-manager
-Author: Dain Nilsson
-Author-email: d...@yubico.com
-Maintainer: Yubico Open Source Maintainers
-Maintainer-email: ossma...@yubico.com
+Author: Yubico Open Source Maintainers
+Author-email: ossma...@yubico.com
 License: BSD 2 clause
+Description-Content-Type: UNKNOWN
 Description: UNKNOWN
 Platform: UNKNOWN
 Classifier: License :: OSI Approved :: BSD License
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/yubikey-manager-2.1.1/man/ykman.1 
new/yubikey-manager-3.0.0/man/ykman.1
--- old/yubikey-manager-2.1.1/man/ykman.1       2019-05-28 08:53:55.000000000 
+0200
+++ new/yubikey-manager-3.0.0/man/ykman.1       2019-06-24 09:07:27.000000000 
+0200
@@ -1,4 +1,4 @@
-.TH YKMAN "1" "May 2019" "ykman 2.1.1" "User Commands"
+.TH YKMAN "1" "June 2019" "ykman 3.0.0" "User Commands"
 .SH NAME
 ykman \- YubiKey Manager (ykman)
 .SH SYNOPSIS
Binary files old/yubikey-manager-2.1.1/test/__pycache__/__init__.cpython-36.pyc 
and new/yubikey-manager-3.0.0/test/__pycache__/__init__.cpython-36.pyc differ
Binary files 
old/yubikey-manager-2.1.1/test/__pycache__/test_device.cpython-36.pyc and 
new/yubikey-manager-3.0.0/test/__pycache__/test_device.cpython-36.pyc differ
Binary files 
old/yubikey-manager-2.1.1/test/__pycache__/test_external_libs.cpython-36.pyc 
and 
new/yubikey-manager-3.0.0/test/__pycache__/test_external_libs.cpython-36.pyc 
differ
Binary files 
old/yubikey-manager-2.1.1/test/__pycache__/test_oath.cpython-36.pyc and 
new/yubikey-manager-3.0.0/test/__pycache__/test_oath.cpython-36.pyc differ
Binary files old/yubikey-manager-2.1.1/test/__pycache__/test_piv.cpython-36.pyc 
and new/yubikey-manager-3.0.0/test/__pycache__/test_piv.cpython-36.pyc differ
Binary files 
old/yubikey-manager-2.1.1/test/__pycache__/test_scancodes.cpython-36.pyc and 
new/yubikey-manager-3.0.0/test/__pycache__/test_scancodes.cpython-36.pyc differ
Binary files 
old/yubikey-manager-2.1.1/test/__pycache__/test_util.cpython-36.pyc and 
new/yubikey-manager-3.0.0/test/__pycache__/test_util.cpython-36.pyc differ
Binary files old/yubikey-manager-2.1.1/test/__pycache__/util.cpython-36.pyc and 
new/yubikey-manager-3.0.0/test/__pycache__/util.cpython-36.pyc differ
Binary files 
old/yubikey-manager-2.1.1/test/on_yubikey/__pycache__/__init__.cpython-36.pyc 
and 
new/yubikey-manager-3.0.0/test/on_yubikey/__pycache__/__init__.cpython-36.pyc 
differ
Binary files 
old/yubikey-manager-2.1.1/test/on_yubikey/__pycache__/test_cli_config.cpython-36.pyc
 and 
new/yubikey-manager-3.0.0/test/on_yubikey/__pycache__/test_cli_config.cpython-36.pyc
 differ
Binary files 
old/yubikey-manager-2.1.1/test/on_yubikey/__pycache__/test_cli_misc.cpython-36.pyc
 and 
new/yubikey-manager-3.0.0/test/on_yubikey/__pycache__/test_cli_misc.cpython-36.pyc
 differ
Binary files 
old/yubikey-manager-2.1.1/test/on_yubikey/__pycache__/test_cli_oath.cpython-36.pyc
 and 
new/yubikey-manager-3.0.0/test/on_yubikey/__pycache__/test_cli_oath.cpython-36.pyc
 differ
Binary files 
old/yubikey-manager-2.1.1/test/on_yubikey/__pycache__/test_cli_openpgp.cpython-36.pyc
 and 
new/yubikey-manager-3.0.0/test/on_yubikey/__pycache__/test_cli_openpgp.cpython-36.pyc
 differ
Binary files 
old/yubikey-manager-2.1.1/test/on_yubikey/__pycache__/test_cli_otp.cpython-36.pyc
 and 
new/yubikey-manager-3.0.0/test/on_yubikey/__pycache__/test_cli_otp.cpython-36.pyc
 differ
Binary files 
old/yubikey-manager-2.1.1/test/on_yubikey/__pycache__/test_fips_u2f_commands.cpython-36.pyc
 and 
new/yubikey-manager-3.0.0/test/on_yubikey/__pycache__/test_fips_u2f_commands.cpython-36.pyc
 differ
Binary files 
old/yubikey-manager-2.1.1/test/on_yubikey/__pycache__/test_interfaces.cpython-36.pyc
 and 
new/yubikey-manager-3.0.0/test/on_yubikey/__pycache__/test_interfaces.cpython-36.pyc
 differ
Binary files 
old/yubikey-manager-2.1.1/test/on_yubikey/__pycache__/test_piv.cpython-36.pyc 
and 
new/yubikey-manager-3.0.0/test/on_yubikey/__pycache__/test_piv.cpython-36.pyc 
differ
Binary files 
old/yubikey-manager-2.1.1/test/on_yubikey/__pycache__/util.cpython-36.pyc and 
new/yubikey-manager-3.0.0/test/on_yubikey/__pycache__/util.cpython-36.pyc differ
Binary files 
old/yubikey-manager-2.1.1/test/on_yubikey/cli_piv/__pycache__/__init__.cpython-36.pyc
 and 
new/yubikey-manager-3.0.0/test/on_yubikey/cli_piv/__pycache__/__init__.cpython-36.pyc
 differ
Binary files 
old/yubikey-manager-2.1.1/test/on_yubikey/cli_piv/__pycache__/test_fips.cpython-36.pyc
 and 
new/yubikey-manager-3.0.0/test/on_yubikey/cli_piv/__pycache__/test_fips.cpython-36.pyc
 differ
Binary files 
old/yubikey-manager-2.1.1/test/on_yubikey/cli_piv/__pycache__/test_generate_cert_and_csr.cpython-36.pyc
 and 
new/yubikey-manager-3.0.0/test/on_yubikey/cli_piv/__pycache__/test_generate_cert_and_csr.cpython-36.pyc
 differ
Binary files 
old/yubikey-manager-2.1.1/test/on_yubikey/cli_piv/__pycache__/test_key_management.cpython-36.pyc
 and 
new/yubikey-manager-3.0.0/test/on_yubikey/cli_piv/__pycache__/test_key_management.cpython-36.pyc
 differ
Binary files 
old/yubikey-manager-2.1.1/test/on_yubikey/cli_piv/__pycache__/test_management_key.cpython-36.pyc
 and 
new/yubikey-manager-3.0.0/test/on_yubikey/cli_piv/__pycache__/test_management_key.cpython-36.pyc
 differ
Binary files 
old/yubikey-manager-2.1.1/test/on_yubikey/cli_piv/__pycache__/test_misc.cpython-36.pyc
 and 
new/yubikey-manager-3.0.0/test/on_yubikey/cli_piv/__pycache__/test_misc.cpython-36.pyc
 differ
Binary files 
old/yubikey-manager-2.1.1/test/on_yubikey/cli_piv/__pycache__/test_pin_puk.cpython-36.pyc
 and 
new/yubikey-manager-3.0.0/test/on_yubikey/cli_piv/__pycache__/test_pin_puk.cpython-36.pyc
 differ
Binary files 
old/yubikey-manager-2.1.1/test/on_yubikey/cli_piv/__pycache__/util.cpython-36.pyc
 and 
new/yubikey-manager-3.0.0/test/on_yubikey/cli_piv/__pycache__/util.cpython-36.pyc
 differ
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' 
old/yubikey-manager-2.1.1/test/on_yubikey/cli_piv/test_key_management.py 
new/yubikey-manager-3.0.0/test/on_yubikey/cli_piv/test_key_management.py
--- old/yubikey-manager-2.1.1/test/on_yubikey/cli_piv/test_key_management.py    
2019-04-09 09:39:55.000000000 +0200
+++ new/yubikey-manager-3.0.0/test/on_yubikey/cli_piv/test_key_management.py    
2019-06-24 09:07:27.000000000 +0200
@@ -107,7 +107,7 @@
             csr = x509.load_pem_x509_csr(output.encode(), default_backend())
             self.assertTrue(csr.is_signature_valid)
 
-    def test_import_correct_cert_succeeds_with_pin(self):
+    def test_import_verify_correct_cert_succeeds_with_pin(self):
         # Set up a key in the slot and create a certificate for it
         public_key_pem = ykman_cli(
             'piv', 'generate-key', '9a', '-a', 'ECCP256', '-m',
@@ -122,17 +122,20 @@
 
         with self.assertRaises(SystemExit):
             ykman_cli(
-                'piv', 'import-certificate', '9a', '/tmp/test-pub-key.pem',
+                'piv', 'import-certificate', '--verify',
+                '9a', '/tmp/test-pub-key.pem',
                 '-m', DEFAULT_MANAGEMENT_KEY)
 
         ykman_cli(
-            'piv', 'import-certificate', '9a', '/tmp/test-pub-key.pem',
+            'piv', 'import-certificate', '--verify',
+            '9a', '/tmp/test-pub-key.pem',
             '-m', DEFAULT_MANAGEMENT_KEY, '-P', DEFAULT_PIN)
         ykman_cli(
-            'piv', 'import-certificate', '9a', '/tmp/test-pub-key.pem',
+            'piv', 'import-certificate', '--verify',
+            '9a', '/tmp/test-pub-key.pem',
             '-m', DEFAULT_MANAGEMENT_KEY, input=DEFAULT_PIN)
 
-    def test_import_wrong_cert_fails(self):
+    def test_import_verify_wrong_cert_fails(self):
         # Set up a key in the slot and create a certificate for it
         public_key_pem = ykman_cli(
             'piv', 'generate-key', '9a', '-a', 'ECCP256', '-m',
@@ -153,10 +156,10 @@
 
         with self.assertRaises(SystemExit):
             ykman_cli(
-                'piv', 'import-certificate', '9a', '-',
+                'piv', 'import-certificate', '--verify', '9a', '-',
                 '-m', DEFAULT_MANAGEMENT_KEY, '-P', DEFAULT_PIN, 
input=cert_pem)
 
-    def test_import_wrong_cert_can_be_forced(self):
+    def test_import_no_verify_wrong_cert_succeeds(self):
         # Set up a key in the slot and create a certificate for it
         public_key_pem = ykman_cli(
             'piv', 'generate-key', '9a', '-a', 'ECCP256', '-m',
@@ -177,7 +180,7 @@
 
         with self.assertRaises(SystemExit):
             ykman_cli(
-                'piv', 'import-certificate', '9a', '-',
+                'piv', 'import-certificate', '--verify', '9a', '-',
                 '-m', DEFAULT_MANAGEMENT_KEY, '-P', DEFAULT_PIN, 
input=cert_pem)
 
         ykman_cli(
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' 
old/yubikey-manager-2.1.1/test/on_yubikey/cli_piv/test_misc.py 
new/yubikey-manager-3.0.0/test/on_yubikey/cli_piv/test_misc.py
--- old/yubikey-manager-2.1.1/test/on_yubikey/cli_piv/test_misc.py      
2019-04-09 09:39:55.000000000 +0200
+++ new/yubikey-manager-3.0.0/test/on_yubikey/cli_piv/test_misc.py      
2019-06-24 09:07:27.000000000 +0200
@@ -1,6 +1,7 @@
 from ..util import ykman_cli
 from .util import PivTestCase, DEFAULT_MANAGEMENT_KEY
 
+
 class Misc(PivTestCase):
 
     def test_info(self):
@@ -14,7 +15,7 @@
     def test_write_read_object(self):
         ykman_cli(
             'piv', 'write-object',
-            '-m', DEFAULT_MANAGEMENT_KEY,'0x5f0001',
+            '-m', DEFAULT_MANAGEMENT_KEY, '0x5f0001',
             '-', input='test data')
         output = ykman_cli('piv', 'read-object', '0x5f0001')
         self.assertEquals('test data\n', output)
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' 
old/yubikey-manager-2.1.1/test/on_yubikey/test_cli_misc.py 
new/yubikey-manager-3.0.0/test/on_yubikey/test_cli_misc.py
--- old/yubikey-manager-2.1.1/test/on_yubikey/test_cli_misc.py  2019-04-09 
09:39:55.000000000 +0200
+++ new/yubikey-manager-3.0.0/test/on_yubikey/test_cli_misc.py  2019-06-24 
09:07:27.000000000 +0200
@@ -13,7 +13,7 @@
 
     @unittest.skipIf(is_fips(), 'Not applicable to YubiKey FIPS.')
     def test_ykman_info_does_not_report_fips_for_non_fips_device(self):
-        info = ykman_cli('info --check-fips')
+        info = ykman_cli('info', '--check-fips')
         self.assertNotIn('FIPS', info)
 
     @unittest.skipIf(not is_fips(), 'YubiKey FIPS required.')
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/yubikey-manager-2.1.1/test/test_util.py 
new/yubikey-manager-3.0.0/test/test_util.py
--- old/yubikey-manager-2.1.1/test/test_util.py 2019-04-09 09:39:55.000000000 
+0200
+++ new/yubikey-manager-3.0.0/test/test_util.py 2019-06-24 09:07:27.000000000 
+0200
@@ -33,7 +33,7 @@
         for l in range(0, 38):
             self.assertRegex(
                 generate_static_pw(l),
-                '^[cbdefghijklnrtuvCBDEFGHIJKLNRTUV]{' + '{:d}'.format(l) + 
'}$')
+                '^[cbdefghijklnrtuvCBDEFGHIJKLNRTUV]{%d}$' % l)
 
     def test_hmac_shorten_key(self):
         self.assertEqual(b'short', hmac_shorten_key(b'short', 'sha1'))
@@ -146,15 +146,14 @@
         with open_file('rsa_2048_key_cert.pfx') as rsa_2048_key_cert_pfx:
             self.assertFalse(is_pem(rsa_2048_key_cert_pfx.read()))
 
-        with open_file('rsa_2048_cert_metadata.pem') as 
rsa_2048_cert_metadata_pem:
-            self.assertTrue(is_pem(rsa_2048_cert_metadata_pem.read()))
+        with open_file('rsa_2048_cert_metadata.pem') as f:
+            self.assertTrue(is_pem(f.read()))
 
         with open_file(
             'rsa_2048_key_cert_encrypted.pfx') as \
                 rsa_2048_key_cert_encrypted_pfx:
             self.assertFalse(is_pem(rsa_2048_key_cert_encrypted_pfx.read()))
 
-
     def test_form_factor_from_code(self):
         self.assertEqual(FORM_FACTOR.UNKNOWN, FORM_FACTOR.from_code(None))
         with self.assertRaises(ValueError):
@@ -166,4 +165,6 @@
         self.assertEqual(
             FORM_FACTOR.USB_C_KEYCHAIN, FORM_FACTOR.from_code(0x03))
         self.assertEqual(FORM_FACTOR.USB_C_NANO, FORM_FACTOR.from_code(0x04))
-        self.assertEqual(FORM_FACTOR.UNKNOWN, FORM_FACTOR.from_code(0x05))
+        self.assertEqual(FORM_FACTOR.USB_C_LIGHTNING,
+                         FORM_FACTOR.from_code(0x05))
+        self.assertEqual(FORM_FACTOR.UNKNOWN, FORM_FACTOR.from_code(0x06))
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/yubikey-manager-2.1.1/ykman/VERSION 
new/yubikey-manager-3.0.0/ykman/VERSION
--- old/yubikey-manager-2.1.1/ykman/VERSION     2019-05-27 12:35:48.000000000 
+0200
+++ new/yubikey-manager-3.0.0/ykman/VERSION     2019-06-24 09:07:27.000000000 
+0200
@@ -1 +1 @@
-2.1.1
+3.0.0
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/yubikey-manager-2.1.1/ykman/cli/fido.py 
new/yubikey-manager-3.0.0/ykman/cli/fido.py
--- old/yubikey-manager-2.1.1/ykman/cli/fido.py 2019-05-08 09:19:37.000000000 
+0200
+++ new/yubikey-manager-3.0.0/ykman/cli/fido.py 2019-06-24 09:07:27.000000000 
+0200
@@ -102,6 +102,74 @@
             click.echo('PIN is not set.')
 
 
+@fido.command('list')
+@click.pass_context
+@click.option('-P', '--pin', help='PIN code.')
+def list_creds(ctx, pin):
+    """
+    List resident credentials.
+    """
+    controller = ctx.obj['controller']
+
+    if not controller.has_pin:
+        ctx.fail('No PIN set.')
+
+    if controller.has_pin and pin is None:
+        pin = _prompt_current_pin(prompt='Enter your PIN')
+
+    try:
+        for cred in controller.get_resident_credentials(pin):
+            click.echo('{} ({})'.format(cred.user_name, cred.rp_id))
+    except CtapError as e:
+        if e.code == CtapError.ERR.PIN_INVALID:
+            ctx.fail('Wrong PIN.')
+    except Exception as e:
+        logger.debug('Failed to list resident credentials', exc_info=e)
+        ctx.fail('Failed to list resident credentials.')
+
+
+@fido.command()
+@click.pass_context
+@click.argument('query')
+@click.option('-P', '--pin', help='PIN code.')
+@click.option('-f', '--force', is_flag=True,
+              help='Confirm deletion without prompting')
+def delete(ctx, query, pin, force):
+    """
+    Delete a resident credential.
+    """
+    controller = ctx.obj['controller']
+
+    if not controller.has_pin:
+        ctx.fail('No PIN set.')
+
+    if controller.has_pin and pin is None:
+        pin = _prompt_current_pin(prompt='Enter your PIN')
+
+    try:
+        hits = [
+            cred for cred in controller.get_resident_credentials(pin)
+            if query.lower() in cred.user_name or query.lower() in cred.rp_id
+        ]
+        if len(hits) == 0:
+            ctx.fail('No matches, nothing to be done.')
+        elif len(hits) == 1:
+            cred = hits[0]
+            if force or click.confirm(
+                    'Delete credential {} ({})?'.format(
+                        cred.user_name, cred.rp_id)):
+                controller.delete_resident_credential(
+                    cred.credential_id, pin)
+        else:
+            ctx.fail('Multiple matches, make the query more specific.')
+    except CtapError as e:
+        if e.code == CtapError.ERR.PIN_INVALID:
+            ctx.fail('Wrong PIN.')
+    except Exception as e:
+        logger.debug('Failed to delete resident credential', exc_info=e)
+        ctx.fail('Failed to delete resident credential.')
+
+
 @fido.command('set-pin')
 @click.pass_context
 @click.option('-P', '--pin', help='Current PIN code.')
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/yubikey-manager-2.1.1/ykman/cli/opgp.py 
new/yubikey-manager-3.0.0/ykman/cli/opgp.py
--- old/yubikey-manager-2.1.1/ykman/cli/opgp.py 2019-04-09 09:39:55.000000000 
+0200
+++ new/yubikey-manager-3.0.0/ykman/cli/opgp.py 2019-06-24 09:07:27.000000000 
+0200
@@ -29,28 +29,17 @@
 
 import logging
 import click
-from ..util import TRANSPORT
+from ..util import TRANSPORT, parse_certificates, parse_private_key
 from ..opgp import OpgpController, KEY_SLOT, TOUCH_MODE
 from ..driver_ccid import APDUError, SW
-from .util import click_force_option, click_postpone_execution
+from .util import (
+    click_force_option, click_format_option, click_postpone_execution,
+    UpperCaseChoice)
 
 
 logger = logging.getLogger(__name__)
 
 
-KEY_NAMES = dict(
-    sig=KEY_SLOT.SIGNATURE,
-    enc=KEY_SLOT.ENCRYPTION,
-    aut=KEY_SLOT.AUTHENTICATION
-)
-
-MODE_NAMES = dict(
-    off=TOUCH_MODE.OFF,
-    on=TOUCH_MODE.ON,
-    fixed=TOUCH_MODE.FIXED
-)
-
-
 def one_of(data):
     def inner(ctx, param, key):
         if key is not None:
@@ -92,7 +81,7 @@
 
     \b
       Require touch to use the authentication key:
-      $ ykman openpgp touch aut on
+      $ ykman openpgp set-touch aut on
     """
     try:
         ctx.obj['controller'] = OpgpController(ctx.obj['dev'].driver)
@@ -116,17 +105,23 @@
     click.echo('PIN tries remaining: {}'.format(retries.pin))
     click.echo('Reset code tries remaining: {}'.format(retries.reset))
     click.echo('Admin PIN tries remaining: {}'.format(retries.admin))
-    click.echo()
-    click.echo('Touch policies')
-    click.echo(
-        'Signature key           {.name}'.format(
-            controller.get_touch(KEY_SLOT.SIGNATURE)))
-    click.echo(
-        'Encryption key          {.name}'.format(
-            controller.get_touch(KEY_SLOT.ENCRYPTION)))
-    click.echo(
-        'Authentication key      {.name}'.format(
-            controller.get_touch(KEY_SLOT.AUTHENTICATION)))
+    # Touch only available on YK4 and later
+    if controller.version >= (4, 2, 6):
+        click.echo()
+        click.echo('Touch policies')
+        click.echo(
+            'Signature key           {!s}'.format(
+                controller.get_touch(KEY_SLOT.SIGNATURE)))
+        click.echo(
+            'Encryption key          {!s}'.format(
+                controller.get_touch(KEY_SLOT.ENCRYPTION)))
+        click.echo(
+            'Authentication key      {!s}'.format(
+                controller.get_touch(KEY_SLOT.AUTHENTICATION)))
+        if controller.supports_attestation:
+            click.echo(
+                'Attestation key         {!s}'.format(
+                    controller.get_touch(KEY_SLOT.ATTESTATION)))
 
 
 @openpgp.command()
@@ -153,63 +148,242 @@
     click.echo('Admin PIN:   12345678')
 
 
-@openpgp.command()
-@click.argument('key', metavar='KEY', type=click.Choice(sorted(KEY_NAMES)),
-                callback=lambda c, p, k: KEY_NAMES.get(k))
-@click.argument('policy', metavar='POLICY', 
type=click.Choice(sorted(MODE_NAMES)),
-                callback=lambda c, p, k: MODE_NAMES.get(k))
-@click.option('--admin-pin', required=False, metavar='PIN',
-              help='Admin PIN for OpenPGP.')
+@openpgp.command('set-touch')
+@click.argument(
+    'key', metavar='KEY', type=UpperCaseChoice(['AUT', 'ENC', 'SIG', 'ATT']),
+    callback=lambda c, p, v: KEY_SLOT(v))
+@click.argument(
+    'policy', metavar='POLICY',
+    type=UpperCaseChoice(['ON', 'OFF', 'FIXED', 'CACHED', 'CACHED-FIXED']),
+    callback=lambda c, p, v: TOUCH_MODE[v.replace('-', '_')])
+@click.option('-a', '--admin-pin', help='Admin PIN for OpenPGP.')
 @click_force_option
 @click.pass_context
-def touch(ctx, key, policy, admin_pin, force):
+def set_touch(ctx, key, policy, admin_pin, force):
     """
-    Manage touch policy for OpenPGP keys.
+    Set touch policy for OpenPGP keys.
 
     \b
-    KEY     Key slot to set (sig, enc or aut).
-    POLICY  Touch policy to set (on, off or fixed).
+    KEY     Key slot to set (sig, enc, aut or att).
+    POLICY  Touch policy to set (on, off, fixed, cached or cached-fixed).
     """
     controller = ctx.obj['controller']
-    old_policy = controller.get_touch(key)
 
-    if old_policy == TOUCH_MODE.FIXED:
-        ctx.fail('A FIXED policy cannot be changed!')
-
-    force or click.confirm('Set touch policy of {.name} key to 
{.name}?'.format(
-        key, policy), abort=True, err=True)
     if admin_pin is None:
         admin_pin = click.prompt('Enter admin PIN', hide_input=True, err=True)
-    controller.set_touch(key, policy, admin_pin.encode('utf8'))
+
+    policy_name = policy.name.lower().replace('_', '-')
+
+    if policy not in controller.supported_touch_policies:
+        ctx.fail('Touch policy {} not supported.'.format(policy_name))
+
+    if force or click.confirm(
+        'Set touch policy of {} key to {}?'.format(
+            key.name.lower(),
+            policy_name),
+            abort=True, err=True):
+        try:
+            controller.set_touch(key, policy, admin_pin)
+        except APDUError as e:
+            if e.sw == SW.SECURITY_CONDITION_NOT_SATISFIED:
+                ctx.fail('Touch policy not allowed.')
+            logger.debug('Failed to set touch policy', exc_info=e)
+            ctx.fail('Failed to set touch policy.')
 
 
 @openpgp.command('set-pin-retries')
-@click.argument('pw-attempts', nargs=3, type=click.IntRange(1, 99))
-@click.password_option('--admin-pin', metavar='PIN', prompt='Enter admin PIN',
-                       confirmation_prompt=False)
+@click.argument(
+    'pin-retries', type=click.IntRange(1, 99), metavar='PIN-RETRIES')
+@click.argument(
+    'reset-code-retries',
+    type=click.IntRange(1, 99), metavar='RESET-CODE-RETRIES')
+@click.argument(
+    'admin-pin-retries',
+    type=click.IntRange(1, 99), metavar='ADMIN-PIN-RETRIES')
+@click.option('-a', '--admin-pin', help='Admin PIN for OpenPGP.')
 @click_force_option
 @click.pass_context
-def set_pin_retries(ctx, pw_attempts, admin_pin, force):
+def set_pin_retries(
+        ctx, admin_pin, pin_retries,
+        reset_code_retries, admin_pin_retries, force):
     """
-    Manage pin-retries.
-
-    Sets the number of attempts available before locking for each PIN.
-
-    PW_ATTEMPTS should be three integer values corresponding to the number of
-    attempts for the PIN, Reset Code, and Admin PIN, respectively.
+    Set PIN, Reset Code and Admin PIN retries.
     """
     controller = ctx.obj['controller']
+
+    if admin_pin is None:
+        admin_pin = click.prompt('Enter admin PIN', hide_input=True, err=True)
+
     resets_pins = controller.version < (4, 0, 0)
     if resets_pins:
         click.echo('WARNING: Setting PIN retries will reset the values for all 
'
                    '3 PINs!')
-    force or click.confirm('Set PIN retry counters to: {} {} {}?'.format(
-        *pw_attempts), abort=True, err=True)
-    controller.set_pin_retries(*(pw_attempts + (admin_pin.encode('utf8'),)))
-    click.echo('PIN retries successfully set.')
-    if resets_pins:
-        click.echo('Default PINs are set.')
-        echo_default_pins()
+    if force or click.confirm(
+            'Set PIN retry counters to: {} {} {}?'.format(
+                pin_retries, reset_code_retries,
+                admin_pin_retries), abort=True, err=True):
+
+        controller.set_pin_retries(
+            pin_retries, reset_code_retries, admin_pin_retries, admin_pin)
+
+        if resets_pins:
+            click.echo('Default PINs are set.')
+            echo_default_pins()
+
+
+@openpgp.command()
+@click.pass_context
+@click.option('-P', '--pin', help='PIN code.')
+@click_format_option
+@click.argument(
+    'key', metavar='KEY', type=UpperCaseChoice(['AUT', 'ENC', 'SIG']),
+    callback=lambda c, p, v: KEY_SLOT(v))
+@click.argument('certificate', type=click.File('wb'), metavar='CERTIFICATE')
+def attest(ctx, key, certificate, pin, format):
+    """
+    Generate a attestation certificate for a key.
+
+    Attestation is used to show that an asymmetric key was generated on the
+    YubiKey and therefore doesn't exist outside the device.
+
+    \b
+    KEY         Key slot to attest (sig, enc, aut).
+    CERTIFICATE File to write attestation certificate to. Use '-' to use 
stdout.
+    """
+
+    controller = ctx.obj['controller']
+
+    if not pin:
+        pin = click.prompt(
+            'Enter PIN', default='', hide_input=True,
+            show_default=False, err=True)
+
+    try:
+        cert = controller.read_certificate(key)
+    except ValueError:
+        cert = None
+
+    if not cert or click.confirm(
+            'There is already data stored in the certificate slot for {}, '
+            'do you want to overwrite it?'.format(key.name)):
+        touch_policy = controller.get_touch(KEY_SLOT.ATTESTATION)
+        if touch_policy in [TOUCH_MODE.ON, TOUCH_MODE.FIXED]:
+            click.echo('Touch your YubiKey...')
+        try:
+            cert = controller.attest(key, pin)
+            certificate.write(cert.public_bytes(encoding=format))
+        except Exception as e:
+            logger.debug('Failed to attest', exc_info=e)
+            ctx.fail('Attestation failed')
+
+
+@openpgp.command('export-certificate')
+@click.pass_context
+@click.argument(
+    'key', metavar='KEY', type=UpperCaseChoice(['AUT', 'ENC', 'SIG', 'ATT']),
+    callback=lambda c, p, v: KEY_SLOT(v))
+@click_format_option
+@click.argument('certificate', type=click.File('wb'), metavar='CERTIFICATE')
+def export_certificate(ctx, key, format, certificate):
+    """
+    Export an OpenPGP Cardholder certificate.
+
+    \b
+    KEY         Key slot to read from (sig, enc, aut, or att).
+    CERTIFICATE File to write certificate to. Use '-' to use stdout.
+    """
+    controller = ctx.obj['controller']
+    try:
+        cert = controller.read_certificate(key)
+    except ValueError:
+        ctx.fail('Failed to read certificate from {}'.format(key.name))
+    certificate.write(cert.public_bytes(encoding=format))
+
+
+@openpgp.command('delete-certificate')
+@click.option('-a', '--admin-pin', help='Admin PIN for OpenPGP.')
+@click.pass_context
+@click.argument(
+    'key', metavar='KEY', type=UpperCaseChoice(['AUT', 'ENC', 'SIG', 'ATT']),
+    callback=lambda c, p, v: KEY_SLOT(v))
+def delete_certificate(ctx, key, admin_pin):
+    """
+    Delete an OpenPGP Cardholder certificate.
+
+    \b
+    KEY         Key slot to delete certificate from (sig, enc, aut, or att).
+    """
+    controller = ctx.obj['controller']
+    if admin_pin is None:
+        admin_pin = click.prompt('Enter admin PIN', hide_input=True, err=True)
+    try:
+        controller.delete_certificate(key, admin_pin)
+    except Exception as e:
+        logger.debug('Failed to delete ', exc_info=e)
+        ctx.fail('Failed to delete certificate.')
+
+
+@openpgp.command('import-certificate')
+@click.option('-a', '--admin-pin', help='Admin PIN for OpenPGP.')
+@click.pass_context
+@click.argument(
+    'key', metavar='KEY', type=UpperCaseChoice(['AUT', 'ENC', 'SIG', 'ATT']),
+    callback=lambda c, p, v: KEY_SLOT(v))
+@click.argument('cert', type=click.File('rb'), metavar='CERTIFICATE')
+def import_certificate(ctx, key, cert, admin_pin):
+    """
+    Import an OpenPGP Cardholder certificate.
+
+    \b
+    KEY         Key slot to import certificate to (sig, enc, aut, or att).
+    CERTIFICATE File containing the certificate. Use '-' to use stdin.
+    """
+    controller = ctx.obj['controller']
+
+    if admin_pin is None:
+        admin_pin = click.prompt('Enter admin PIN', hide_input=True, err=True)
+
+    try:
+        certs = parse_certificates(cert.read(), password=None)
+    except Exception as e:
+        logger.debug('Failed to parse', exc_info=e)
+        ctx.fail('Failed to parse certificate.')
+    if len(certs) != 1:
+        ctx.fail('Can only import one certificate.')
+    try:
+        controller.import_certificate(key, certs[0], admin_pin)
+    except Exception as e:
+        logger.debug('Failed to import', exc_info=e)
+        ctx.fail('Failed to import certificate')
+
+
+@openpgp.command('import-attestation-key')
+@click.option('-a', '--admin-pin', help='Admin PIN for OpenPGP.')
+@click.pass_context
+@click.argument('private-key', type=click.File('rb'), metavar='PRIVATE-KEY')
+def import_attestation_key(ctx, private_key, admin_pin):
+    """
+    Import a private attestation key.
+
+    Import a private key for OpenPGP attestation.
+
+    \b
+    PRIVATE-KEY File containing the private key. Use '-' to use stdin.
+    """
+    controller = ctx.obj['controller']
+
+    if admin_pin is None:
+        admin_pin = click.prompt('Enter admin PIN', hide_input=True, err=True)
+    try:
+        private_key = parse_private_key(private_key.read(), password=None)
+    except Exception as e:
+        logger.debug('Failed to parse', exc_info=e)
+        ctx.fail('Failed to parse private key.')
+    try:
+        controller.import_attestation_key(private_key, admin_pin)
+    except Exception as e:
+        logger.debug('Failed to import', exc_info=e)
+        ctx.fail('Failed to import attestation key.')
 
 
 openpgp.transports = TRANSPORT.CCID
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/yubikey-manager-2.1.1/ykman/cli/otp.py 
new/yubikey-manager-3.0.0/ykman/cli/otp.py
--- old/yubikey-manager-2.1.1/ykman/cli/otp.py  2019-05-27 12:04:25.000000000 
+0200
+++ new/yubikey-manager-3.0.0/ykman/cli/otp.py  2019-06-24 09:07:27.000000000 
+0200
@@ -36,7 +36,7 @@
 from binascii import a2b_hex, b2a_hex
 from .. import __version__
 from ..driver_otp import YkpersError
-from ..otp import OtpController, PrepareUploadFailed
+from ..otp import OtpController, PrepareUploadFailed, SlotConfig
 from ..scancodes import KEYBOARD_LAYOUT
 import logging
 import os
@@ -339,7 +339,9 @@
                            abort=True, err=True)
 
     try:
-        controller.program_otp(slot, key, public_id, private_id, not no_enter)
+        controller.program_otp(slot, key, public_id, private_id, SlotConfig(
+            append_cr=not no_enter
+        ))
     except YkpersError as e:
         _failed_to_write_msg(ctx, e)
 
@@ -394,8 +396,9 @@
     if not force:
         _confirm_slot_overwrite(controller, slot)
     try:
-        controller.program_static(
-            slot, password, not no_enter, keyboard_layout=keyboard_layout)
+        controller.program_static(slot, password, keyboard_layout, SlotConfig(
+            append_cr=not no_enter
+        ))
     except YkpersError as e:
         _failed_to_write_msg(ctx, e)
 
@@ -546,7 +549,9 @@
         err=True)
     try:
         controller.program_hotp(
-            slot, key, counter, int(digits) == 8, not no_enter)
+            slot, key, counter, int(digits) == 8, SlotConfig(
+                append_cr=not no_enter
+            ))
     except YkpersError as e:
         _failed_to_write_msg(ctx, e)
 
@@ -569,8 +574,11 @@
     '-p', '--pacing', type=click.Choice(['0', '20', '40', '60']),
     default='0', show_default=True, help='Throttle output speed by '
     'adding a delay (in ms) between characters emitted.')
+@click.option('--use-numeric-keypad', is_flag=True, show_default=True,
+              help='Use scancodes for numeric keypad when sending digits.'
+              ' Helps with some keyboard layouts. ')
 def settings(ctx, slot, new_access_code, delete_access_code, enter, pacing,
-             force):
+             use_numeric_keypad, force):
     """
     Update the settings for a slot.
 
@@ -605,7 +613,11 @@
         pacing = int(pacing)
 
     try:
-        controller.update_settings(slot, enter=enter, pacing=pacing)
+        controller.update_settings(slot, SlotConfig(
+            append_cr=enter,
+            pacing=pacing,
+            numeric_keypad=use_numeric_keypad
+        ))
     except YkpersError as e:
         _failed_to_write_msg(ctx, e)
 
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/yubikey-manager-2.1.1/ykman/cli/piv.py 
new/yubikey-manager-3.0.0/ykman/cli/piv.py
--- old/yubikey-manager-2.1.1/ykman/cli/piv.py  2019-04-30 12:51:29.000000000 
+0200
+++ new/yubikey-manager-3.0.0/ykman/cli/piv.py  2019-06-24 09:07:27.000000000 
+0200
@@ -165,9 +165,11 @@
         except AttributeError:
             print_dn = False
             logger.debug('Failed to read DN, falling back to only CNs')
-            subject_cn = 
cert.subject.get_attributes_for_oid(x509.NameOID.COMMON_NAME)
+            subject_cn = cert.subject.get_attributes_for_oid(
+                x509.NameOID.COMMON_NAME)
             subject_cn = subject_cn[0].value if subject_cn else 'None'
-            issuer_cn = 
cert.issuer.get_attributes_for_oid(x509.NameOID.COMMON_NAME)
+            issuer_cn = cert.issuer.get_attributes_for_oid(
+                x509.NameOID.COMMON_NAME)
             issuer_cn = issuer_cn[0].value if issuer_cn else 'None'
         except ValueError as e:
             # Malformed certificates may throw ValueError
@@ -823,6 +825,7 @@
             show_default=False, hide_input=True, err=True)
     controller.unblock_pin(puk, new_pin)
 
+
 @piv.command('read-object')
 @click_pin_option
 @click.pass_context
@@ -875,8 +878,8 @@
     the range 5f0000 - 5fffff.
 
     \b
-    OBJECT-ID       Id of PIV object in HEX.
-    DATA            File containing the data to be written. Use '-' to use 
stdin.
+    OBJECT-ID      Id of PIV object in HEX.
+    DATA           File containing the data to be written. Use '-' to use 
stdin.
     """
 
     controller = ctx.obj['controller']
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/yubikey-manager-2.1.1/ykman/device.py 
new/yubikey-manager-3.0.0/ykman/device.py
--- old/yubikey-manager-2.1.1/ykman/device.py   2019-05-27 12:04:25.000000000 
+0200
+++ new/yubikey-manager-3.0.0/ykman/device.py   2019-06-24 09:07:27.000000000 
+0200
@@ -270,6 +270,7 @@
                 FORM_FACTOR.USB_A_NANO,
                 FORM_FACTOR.USB_C_KEYCHAIN,
                 FORM_FACTOR.USB_C_NANO,
+                FORM_FACTOR.USB_C_LIGHTNING
                 ):
             config._set(TAG.NFC_SUPPORTED, 0)
             config._set(TAG.NFC_ENABLED, 0)
@@ -284,7 +285,8 @@
             if config.nfc_supported:
                 self.device_name = 'Security Key NFC'
         elif self._key_type == YUBIKEY.YK4:
-            if (5, 1, 0) > self.version >= (5, 0, 0):
+            if (5, 0, 0) <= self.version < (5, 1, 0) or \
+                    self.version in [(5, 2, 0), (5, 2, 1), (5, 2, 2)]:
                 self.device_name = 'YubiKey Preview'
             elif self.version >= (5, 1, 0):
                 logger.debug('Identified YubiKey 5')
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/yubikey-manager-2.1.1/ykman/fido.py 
new/yubikey-manager-3.0.0/ykman/fido.py
--- old/yubikey-manager-2.1.1/ykman/fido.py     2019-04-29 14:32:48.000000000 
+0200
+++ new/yubikey-manager-3.0.0/ykman/fido.py     2019-06-24 09:07:27.000000000 
+0200
@@ -29,13 +29,35 @@
 
 import six
 import time
+import logging
 from fido2.ctap1 import CTAP1, ApduError
-from fido2.ctap2 import CTAP2, PinProtocolV1
+from fido2.ctap2 import CTAP2, PinProtocolV1, CredentialManagement
 from threading import Timer
 from .driver_ccid import SW
 from .driver_fido import FIPS_U2F_CMD
 
 
+logger = logging.getLogger(__name__)
+
+
+class ResidentCredential(object):
+    def __init__(self, raw_credential, raw_rp):
+        self._raw_credential = raw_credential
+        self._raw_rp = raw_rp
+
+    @property
+    def credential_id(self):
+        return self._raw_credential[CredentialManagement.RESULT.CREDENTIAL_ID]
+
+    @property
+    def rp_id(self):
+        return self._raw_rp[CredentialManagement.RESULT.RP]['id']
+
+    @property
+    def user_name(self):
+        return self._raw_credential[CredentialManagement.RESULT.USER]['name']
+
+
 class Fido2Controller(object):
 
     def __init__(self, driver):
@@ -48,6 +70,27 @@
     def has_pin(self):
         return self._pin
 
+    def get_resident_credentials(self, pin):
+        _credman = CredentialManagement(
+            self.ctap,
+            self.pin.VERSION,
+            self.pin.get_pin_token(pin))
+
+        for rp in _credman.enumerate_rps():
+            for cred in _credman.enumerate_creds(
+                    rp[CredentialManagement.RESULT.RP_ID_HASH]):
+                yield ResidentCredential(cred, rp)
+
+    def delete_resident_credential(self, credential_id, pin):
+        _credman = CredentialManagement(
+            self.ctap,
+            self.pin.VERSION,
+            self.pin.get_pin_token(pin))
+
+        for cred in self.get_resident_credentials(pin):
+            if credential_id == cred.credential_id:
+                _credman.delete_cred(credential_id)
+
     def get_pin_retries(self):
         return self.pin.get_pin_retries()
 
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/yubikey-manager-2.1.1/ykman/opgp.py 
new/yubikey-manager-3.0.0/ykman/opgp.py
--- old/yubikey-manager-2.1.1/ykman/opgp.py     2019-04-09 09:39:55.000000000 
+0200
+++ new/yubikey-manager-3.0.0/ykman/opgp.py     2019-06-24 09:07:27.000000000 
+0200
@@ -28,18 +28,62 @@
 from __future__ import absolute_import
 
 import six
+import struct
+import logging
 from .util import AID
 from .driver_ccid import (APDUError, SW, GP_INS_SELECT)
-from enum import IntEnum, unique
-from binascii import b2a_hex
+from enum import Enum, IntEnum, unique
+from binascii import b2a_hex, a2b_hex
 from collections import namedtuple
+from cryptography import x509
+from cryptography.utils import int_to_bytes
+from cryptography.hazmat.backends import default_backend
+from cryptography.hazmat.primitives.serialization import Encoding
+from cryptography.hazmat.primitives.asymmetric import rsa, ec
+
+
+logger = logging.getLogger(__name__)
 
 
 @unique
-class KEY_SLOT(IntEnum):  # noqa: N801
-    SIGNATURE = 0xd6
-    ENCRYPTION = 0xd7
-    AUTHENTICATION = 0xd8
+class KEY_SLOT(Enum):  # noqa: N801
+    SIGNATURE = 'SIG'
+    ENCRYPTION = 'ENC'
+    AUTHENTICATION = 'AUT'
+    ATTESTATION = 'ATT'
+
+    @property
+    def key_position(self):
+        if self == KEY_SLOT.SIGNATURE:
+            return 0x01
+        if self == KEY_SLOT.ENCRYPTION:
+            return 0x02
+        if self == KEY_SLOT.AUTHENTICATION:
+            return 0x03
+        if self == KEY_SLOT.ATTESTATION:
+            return 0x04
+
+    @property
+    def touch_position(self):
+        if self == KEY_SLOT.SIGNATURE:
+            return 0xd6
+        if self == KEY_SLOT.ENCRYPTION:
+            return 0xd7
+        if self == KEY_SLOT.AUTHENTICATION:
+            return 0xd8
+        if self == KEY_SLOT.ATTESTATION:
+            return 0xd9
+
+    @property
+    def cert_position(self):
+        if self == KEY_SLOT.SIGNATURE:
+            return 0x02
+        if self == KEY_SLOT.ENCRYPTION:
+            return 0x01
+        if self == KEY_SLOT.AUTHENTICATION:
+            return 0x00
+        if self == KEY_SLOT.ATTESTATION:
+            return 0x03
 
 
 @unique
@@ -47,6 +91,25 @@
     OFF = 0x00
     ON = 0x01
     FIXED = 0x02
+    CACHED = 0x03
+    CACHED_FIXED = 0x04
+
+    def __str__(self):
+        if self == TOUCH_MODE.OFF:
+            return 'Off'
+        elif self == TOUCH_MODE.ON:
+            return 'On'
+        elif self == TOUCH_MODE.FIXED:
+            return 'On (fixed)'
+        elif self == TOUCH_MODE.CACHED:
+            return 'Cached'
+        elif self == TOUCH_MODE.CACHED_FIXED:
+            return 'Cached (fixed)'
+
+
+@unique
+class TAG(IntEnum):  # noqa: N801
+    CARDHOLDER_CERTIFICATE = 0x7f
 
 
 @unique
@@ -58,6 +121,9 @@
     TERMINATE = 0xe6
     ACTIVATE = 0x44
     PUT_DATA = 0xda
+    GET_ATTESTATION = 0xfb
+    SEND_REMAINING = 0xc0
+    SELECT_DATA = 0xa5
 
 
 PinRetries = namedtuple('PinRetries', ['pin', 'reset', 'admin'])
@@ -92,6 +158,23 @@
                 return self._driver.send_apdu(cl, ins, p1, p2, data, check)
             raise
 
+    def send_cmd(self, cl, ins, p1=0, p2=0, data=b'', check=SW.OK):
+        while len(data) > 0xff:
+            self._driver.send_apdu(0x10, ins, p1, p2, data[:0xff])
+            data = data[0xff:]
+        resp, sw = self._driver.send_apdu(0, ins, p1, p2, data, check=None)
+
+        while (sw >> 8) == SW.MORE_DATA:
+            more, sw = self._driver.send_apdu(
+                0, INS.SEND_REMAINING, 0, 0, b'', check=None)
+            resp += more
+
+        if check is None:
+            return resp, sw
+        elif sw != check:
+            raise APDUError(resp, sw)
+        return resp
+
     def _read_version(self):
         bcd_hex = b2a_hex(self.send_apdu(0, INS.GET_VERSION, 0, 0))
         return tuple(int(bcd_hex[i:i+2]) for i in range(0, 6, 2))
@@ -118,32 +201,163 @@
 
     def _verify(self, pw, pin):
         try:
+            pin = pin.encode('utf-8')
             self.send_apdu(0, INS.VERIFY, 0, pw, pin)
         except APDUError:
             pw_remaining = self.get_remaining_pin_tries()[pw-PW1]
             raise ValueError('Invalid PIN, {} tries remaining.'.format(
                 pw_remaining))
 
-    def get_touch(self, key_slot):
+    @property
+    def supported_touch_policies(self):
         if self.version < (4, 2, 0):
+            return []
+        if self.version < (5, 2, 1):
+            return [TOUCH_MODE.ON, TOUCH_MODE.OFF, TOUCH_MODE.FIXED]
+        if self.version >= (5, 2, 1):
+            return [
+                TOUCH_MODE.ON, TOUCH_MODE.OFF, TOUCH_MODE.FIXED,
+                TOUCH_MODE.CACHED, TOUCH_MODE.CACHED_FIXED]
+
+    @property
+    def supports_attestation(self):
+        return self.version >= (5, 2, 1)
+
+    def get_touch(self, key_slot):
+        if not self.supported_touch_policies:
             raise ValueError('Touch policy is available on YubiKey 4 or 
later.')
-        data = self.send_apdu(0, INS.GET_DATA, 0, key_slot)
+        if key_slot == KEY_SLOT.ATTESTATION and not self.supports_attestation:
+            raise ValueError('Attestation key not available on this device.')
+        data = self.send_apdu(0, INS.GET_DATA, 0, key_slot.touch_position)
         return TOUCH_MODE(six.indexbytes(data, 0))
 
-    def set_touch(self, key_slot, mode, pin):
-        if self.version < (4, 2, 0):
+    def set_touch(self, key_slot, mode, admin_pin):
+        if not self.supported_touch_policies:
             raise ValueError('Touch policy is available on YubiKey 4 or 
later.')
-        self._verify(PW3, pin)
-        self.send_apdu(0, INS.PUT_DATA, 0, key_slot,
+        if mode not in self.supported_touch_policies:
+            raise ValueError('Touch policy not available on this device.')
+        self._verify(PW3, admin_pin)
+        self.send_apdu(0, INS.PUT_DATA, 0, key_slot.touch_position,
                        bytes(bytearray([mode, TOUCH_METHOD_BUTTON])))
 
-    def set_pin_retries(self, pw1_tries, pw2_tries, pw3_tries, pin):
+    def set_pin_retries(self, pw1_tries, pw2_tries, pw3_tries, admin_pin):
         if self.version < (1, 0, 7):  # For YubiKey NEO
             raise ValueError('Setting PIN retry counters requires version '
                              '1.0.7 or later.')
         if (4, 0, 0) <= self.version < (4, 3, 1):  # For YubiKey 4
             raise ValueError('Setting PIN retry counters requires version '
                              '4.3.1 or later.')
-        self._verify(PW3, pin)
+        self._verify(PW3, admin_pin)
         self.send_apdu(0, INS.SET_PIN_RETRIES, 0, 0,
                        bytes(bytearray([pw1_tries, pw2_tries, pw3_tries])))
+
+    def read_certificate(self, key_slot):
+        self.send_cmd(
+            0, INS.SELECT_DATA, key_slot.cert_position,
+            0x04, data=a2b_hex('0660045C027F21'))
+        data = self.send_cmd(
+            0, INS.GET_DATA, TAG.CARDHOLDER_CERTIFICATE, 0x21)
+        if not data:
+            raise ValueError('No certificate found!')
+        return x509.load_der_x509_certificate(data, default_backend())
+
+    def import_certificate(self, key_slot, certificate, admin_pin):
+        self._verify(PW3, admin_pin)
+        cert_data = certificate.public_bytes(Encoding.DER)
+        self.send_cmd(
+            0, INS.SELECT_DATA, key_slot.cert_position,
+            0x04, data=a2b_hex('0660045C027F21'))
+        self.send_cmd(
+            0, INS.PUT_DATA, TAG.CARDHOLDER_CERTIFICATE, 0x21, data=cert_data)
+
+    def _get_key_attributes(self, key):
+        if isinstance(key, rsa.RSAPrivateKey):
+            return struct.pack('>BHHB', 0x01, key.key_size, 32, 0)
+        if isinstance(key, ec.EllipticCurvePrivateKey):
+            return int_to_bytes(
+                self._get_opgp_algo_id_from_ec(
+                    key)) + a2b_hex(self._get_oid_from_ec(key))
+        raise ValueError('Not a valid private key!')
+
+    def _get_oid_from_ec(self, key):
+        curve = key.curve.name
+        if curve == 'secp384r1':
+            return '2B81040022'
+        if curve == 'secp256r1':
+            return '2A8648CE3D030107'
+        if curve == 'secp521r1':
+            return '2B81040023'
+        if curve == 'x25519':
+            return '2B060104019755010501'
+        raise ValueError('No OID for curve: ' + curve)
+
+    def _get_opgp_algo_id_from_ec(self, key):
+        curve = key.curve.name
+        if curve in ['secp384r1', 'secp256r1', 'secp521r1']:
+            return 0x13
+        if curve == 'x25519':
+            return 0x16
+        raise ValueError('No Algo ID for curve: ' + curve)
+
+    def _get_key_data(self, key):
+
+        def _der_len(data):
+            ln = len(data)
+            if ln <= 128:
+                res = [ln]
+            elif ln <= 255:
+                res = [0x81, ln]
+            else:
+                res = [0x82, (ln >> 8) & 0xff, ln & 0xff]
+            return bytearray(res)
+
+        private_numbers = key.private_numbers()
+        data = a2b_hex('B603840181')
+
+        if isinstance(key, rsa.RSAPrivateKey):
+            ln = key.key_size // 8 // 2
+            data += 
b'\x7f\x48\x08\x91\x03\x92\x81\x80\x93\x81\x80\x5f\x48\x82\x01\x03\x01\x00\x01' 
 # noqa: E501
+            data += int_to_bytes(private_numbers.p, ln)
+            data += int_to_bytes(private_numbers.q, ln)
+            return b'\x4d' + _der_len(data) + data
+        elif isinstance(key, ec.EllipticCurvePrivateKey):
+            ln = key.key_size // 8
+            privkey = int_to_bytes(private_numbers.private_value, ln)
+            data += b'\x7f\x48\x02\x92' + _der_len(privkey)
+            data += b'\x5f\x48' + _der_len(privkey) + privkey
+            return b'\x4d' + _der_len(data) + data
+
+    def import_attestation_key(self, key, admin_pin):
+        self._verify(PW3, admin_pin)
+        data = self._get_key_attributes(key)
+        self.send_cmd(0, INS.PUT_DATA, 0, 0xda, data=data)
+        data = self._get_key_data(key)
+        self.send_cmd(0, 0xdb, 0x3f, 0xff, data=data)
+
+    def delete_attestation_key(self, admin_pin):
+        self._verify(PW3, admin_pin)
+        # Delete attestation key by changing the key attributes twice.
+        self.send_cmd(
+            0, INS.PUT_DATA, 0, 0xda,
+            data=struct.pack('>BHHB', 0x01, 2048, 32, 0))
+        self.send_cmd(
+            0, INS.PUT_DATA, 0, 0xda,
+            data=struct.pack('>BHHB', 0x01, 4096, 32, 0))
+
+    def delete_certificate(self, key_slot, admin_pin):
+        self._verify(PW3, admin_pin)
+        self.send_cmd(
+            0, INS.SELECT_DATA, key_slot.cert_position(),
+            0x04, data=a2b_hex('0660045C027F21'))
+        self.send_apdu(
+            0, INS.PUT_DATA, TAG.CARDHOLDER_CERTIFICATE, 0x21, data=b'')
+
+    def attest(self, key_slot, pin):
+        self._verify(PW1, pin)
+        self.send_apdu(0x80, INS.GET_ATTESTATION, key_slot.key_position, 0)
+        self.send_cmd(
+            0, INS.SELECT_DATA, key_slot.cert_position(),
+            0x04, data=a2b_hex('0660045C027F21'))
+        data = self.send_cmd(
+            0, INS.GET_DATA, TAG.CARDHOLDER_CERTIFICATE, 0x21)
+        return x509.load_der_x509_certificate(data, default_backend())
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/yubikey-manager-2.1.1/ykman/otp.py 
new/yubikey-manager-3.0.0/ykman/otp.py
--- old/yubikey-manager-2.1.1/ykman/otp.py      2019-05-16 09:16:05.000000000 
+0200
+++ new/yubikey-manager-3.0.0/ykman/otp.py      2019-06-24 09:07:27.000000000 
+0200
@@ -111,6 +111,68 @@
         return [e.message() for e in self.errors]
 
 
+class SlotConfig(object):
+    def __init__(
+            self,
+            serial_api_visible=True,
+            allow_update=True,
+            append_cr=True,
+            pacing=None,
+            numeric_keypad=False,
+    ):
+        self.serial_api_visible = serial_api_visible
+        self.allow_update = allow_update
+        self.append_cr = append_cr
+        self.pacing = pacing
+        self.numeric_keypad = numeric_keypad
+
+
+class _SlotConfigContext(object):
+
+    def __init__(self, dev, cmd, conf):
+        st = ykpers.ykds_alloc()
+        self.cfg = ykpers.ykp_alloc()
+        try:
+            check(ykpers.yk_get_status(dev, st))
+            ykpers.ykp_configure_version(self.cfg, st)
+            ykpers.ykp_configure_command(self.cfg, cmd)
+        except YkpersError:
+            ykpers.ykp_free_config(self.cfg)
+            raise
+        finally:
+            ykpers.ykds_free(st)
+
+        if conf is None:
+            conf = SlotConfig()
+        self._apply(conf)
+
+    def _apply(self, config):
+        if config.serial_api_visible:
+            check(ykpers.ykp_set_extflag(self.cfg, 'SERIAL_API_VISIBLE'))
+        if config.allow_update:
+            check(ykpers.ykp_set_extflag(self.cfg, 'ALLOW_UPDATE'))
+        if config.append_cr:
+            check(ykpers.ykp_set_tktflag(self.cfg, 'APPEND_CR'))
+
+        # Output speed throttling
+        if config.pacing == 20:
+            check(ykpers.ykp_set_cfgflag(self.cfg, 'PACING_10MS'))
+        elif config.pacing == 40:
+            check(ykpers.ykp_set_cfgflag(self.cfg, 'PACING_20MS'))
+        elif config.pacing == 60:
+            check(ykpers.ykp_set_cfgflag(self.cfg, 'PACING_10MS'))
+            check(ykpers.ykp_set_cfgflag(self.cfg, 'PACING_20MS'))
+
+        if config.numeric_keypad:
+            check(ykpers.ykp_set_extflag(self.cfg, 'USE_NUMERIC_KEYPAD'))
+
+    def __enter__(self):
+        return self.cfg
+
+    def __exit__(self, type, value, traceback):
+        ykpers.ykp_free_config(self.cfg)
+
+
 class OtpController(object):
 
     def __init__(self, driver):
@@ -126,30 +188,19 @@
     def access_code(self, value):
         self._access_code = value
 
-    def _create_cfg(self, cmd):
-        st = ykpers.ykds_alloc()
-        cfg = ykpers.ykp_alloc()
-        try:
-            check(ykpers.yk_get_status(self._dev, st))
-            ykpers.ykp_configure_version(cfg, st)
-            ykpers.ykp_configure_command(cfg, cmd)
-            check(ykpers.ykp_set_extflag(cfg, 'SERIAL_API_VISIBLE'))
-            check(ykpers.ykp_set_extflag(cfg, 'ALLOW_UPDATE'))
-            if self.access_code is not None:
-                check(ykpers.ykp_set_access_code(
-                    cfg, self.access_code, _ACCESS_CODE_LENGTH))
-            return cfg
-        except YkpersError:
-            ykpers.ykp_free_config(cfg)
-            raise
-        finally:
-            ykpers.ykds_free(st)
+    def _create_cfg(self, cmd, conf=None):
+        context = _SlotConfigContext(self._dev, cmd, conf)
+        if self.access_code is not None:
+            check(ykpers.ykp_set_access_code(
+                context.cfg, self.access_code, _ACCESS_CODE_LENGTH))
+
+        return context
 
     @property
     def slot_status(self):
         return self._driver.slot_status
 
-    def program_otp(self, slot, key, fixed, uid, append_cr=True):
+    def program_otp(self, slot, key, fixed, uid, config=None):
         if len(key) != 16:
             raise ValueError('key must be 16 bytes')
         if len(uid) != 6:
@@ -158,19 +209,16 @@
             raise ValueError('public ID must be <= 16 bytes')
 
         cmd = slot_to_cmd(slot)
-        cfg = self._create_cfg(cmd)
 
-        try:
+        with self._create_cfg(cmd, config) as cfg:
             check(ykpers.ykp_set_fixed(cfg, fixed, len(fixed)))
             check(ykpers.ykp_set_uid(cfg, uid, 6))
             ykpers.ykp_AES_key_from_raw(cfg, key)
-            if append_cr:
-                check(ykpers.ykp_set_tktflag(cfg, 'APPEND_CR'))
-            check(ykpers.yk_write_command(self._dev,
-                                          ykpers.ykp_core_config(cfg),
-                                          cmd, self.access_code))
-        finally:
-            ykpers.ykp_free_config(cfg)
+            check(ykpers.yk_write_command(
+                self._dev,
+                ykpers.ykp_core_config(cfg),
+                cmd, self.access_code
+            ))
 
     def prepare_upload_key(self, key, public_id, private_id, serial=None,
                            user_agent='python-yubikey-manager/' + __version__):
@@ -218,8 +266,13 @@
                     errors = []
                 raise PrepareUploadFailed(resp.status, resp_body, errors)
 
-    def program_static(self, slot, password, append_cr=True,
-                       keyboard_layout=KEYBOARD_LAYOUT.MODHEX):
+    def program_static(
+            self,
+            slot,
+            password,
+            keyboard_layout=KEYBOARD_LAYOUT.MODHEX,
+            config=None
+    ):
         pw_len = len(password)
         if self._driver.version < (2, 0, 0):
             raise ValueError('static password requires YubiKey 2.0.0 or later')
@@ -231,14 +284,9 @@
                              'maximum of %d characters' % 38)
 
         cmd = slot_to_cmd(slot)
-        cfg = self._create_cfg(cmd)
 
-        try:
+        with self._create_cfg(cmd, config) as cfg:
             check(ykpers.ykp_set_cfgflag(cfg, 'SHORT_TICKET'))
-
-            if append_cr:
-                check(ykpers.ykp_set_tktflag(cfg, 'APPEND_CR'))
-
             pw_bytes = encode(password, keyboard_layout=keyboard_layout)
             if pw_len <= 16:  # All in fixed
                 check(ykpers.ykp_set_fixed(cfg, pw_bytes, pw_len))
@@ -252,10 +300,8 @@
 
             check(ykpers.yk_write_command(
                 self._dev, ykpers.ykp_core_config(cfg), cmd, self.access_code))
-        finally:
-            ykpers.ykp_free_config(cfg)
 
-    def program_chalresp(self, slot, key, touch=False):
+    def program_chalresp(self, slot, key, touch=False, config=None):
         if self._driver.version < (2, 2, 0):
             raise ValueError('challenge-response requires YubiKey 2.2.0 or '
                              'later')
@@ -263,9 +309,9 @@
         if len(key) > 20:
             raise ValueError('key lengths >20 bytes not supported')
         cmd = slot_to_cmd(slot)
-        cfg = self._create_cfg(cmd)
         key = key.ljust(20, b'\0')  # Pad key to 20 bytes
-        try:
+
+        with self._create_cfg(cmd, config) as cfg:
             check(ykpers.ykp_set_tktflag(cfg, 'CHAL_RESP'))
             check(ykpers.ykp_set_cfgflag(cfg, 'CHAL_HMAC'))
             check(ykpers.ykp_set_cfgflag(cfg, 'HMAC_LT64'))
@@ -274,8 +320,6 @@
             ykpers.ykp_HMAC_key_from_raw(cfg, key)
             check(ykpers.yk_write_command(
                 self._dev, ykpers.ykp_core_config(cfg), cmd, self.access_code))
-        finally:
-            ykpers.ykp_free_config(cfg)
 
     def calculate(
             self, slot, challenge=None, totp=False,
@@ -323,7 +367,7 @@
         else:
             return b2a_hex(resp.raw[:20])
 
-    def program_hotp(self, slot, key, imf=0, hotp8=False, append_cr=True):
+    def program_hotp(self, slot, key, imf=0, hotp8=False, config=None):
         if self._driver.version < (2, 1, 0):
             raise ValueError('HOTP requires YubiKey 2.1.0 or later')
         key = hmac_shorten_key(key, 'SHA1')
@@ -333,20 +377,15 @@
         if imf % 16 != 0:
             raise ValueError('imf must be a multiple of 16')
         cmd = slot_to_cmd(slot)
-        cfg = self._create_cfg(cmd)
 
-        try:
+        with self._create_cfg(cmd, config) as cfg:
             check(ykpers.ykp_set_tktflag(cfg, 'OATH_HOTP'))
             check(ykpers.ykp_set_oath_imf(cfg, imf))
             if hotp8:
                 check(ykpers.ykp_set_cfgflag(cfg, 'OATH_HOTP8'))
-            if append_cr:
-                check(ykpers.ykp_set_tktflag(cfg, 'APPEND_CR'))
             ykpers.ykp_HMAC_key_from_raw(cfg, key)
             check(ykpers.yk_write_command(
                 self._dev, ykpers.ykp_core_config(cfg), cmd, self.access_code))
-        finally:
-            ykpers.ykp_free_config(cfg)
 
     def zap_slot(self, slot):
         check(ykpers.yk_write_command(self._dev, None, slot_to_cmd(slot),
@@ -355,12 +394,10 @@
     def swap_slots(self):
         if self._driver.version < (2, 3, 0):
             raise ValueError('swapping slots requires YubiKey 2.3.0 or later')
-        cfg = self._create_cfg(SLOT.SWAP)
-        try:
+
+        with self._create_cfg(SLOT.SWAP) as cfg:
             ycfg = ykpers.ykp_core_config(cfg)
             check(ykpers.yk_write_command(self._dev, ycfg, SLOT.SWAP, None))
-        finally:
-            ykpers.ykp_free_config(cfg)
 
     def configure_ndef_slot(self, slot, prefix='https://my.yubico.com/yk/#'):
         ndef = ykpers.ykp_alloc_ndef()
@@ -389,8 +426,7 @@
                 'code when initially programming the slot instead.')
 
         cmd = slot_to_cmd(slot, update)
-        cfg = self._create_cfg(cmd)
-        try:
+        with self._create_cfg(cmd) as cfg:
             check(ykpers.ykp_set_access_code(
                 cfg, new_code or _RESET_ACCESS_CODE, _ACCESS_CODE_LENGTH))
             ycfg = ykpers.ykp_core_config(cfg)
@@ -399,9 +435,6 @@
 
             self.access_code = new_code
 
-        finally:
-            ykpers.ykp_free_config(cfg)
-
     def delete_access_code(self, slot):
         if self._has_update_access_code_bug:
             raise ValueError(
@@ -411,26 +444,11 @@
 
         self.set_access_code(slot, None)
 
-    def update_settings(self, slot, enter=True, pacing=None):
+    def update_settings(self, slot, config=None):
         cmd = slot_to_cmd(slot, update=True)
-        cfg = self._create_cfg(cmd)
-        if enter:
-            check(ykpers.ykp_set_tktflag(cfg, 'APPEND_CR'))
-
-        # Output speed throttling
-        if pacing == 20:
-            check(ykpers.ykp_set_cfgflag(cfg, 'PACING_10MS'))
-        elif pacing == 40:
-            check(ykpers.ykp_set_cfgflag(cfg, 'PACING_20MS'))
-        elif pacing == 60:
-            check(ykpers.ykp_set_cfgflag(cfg, 'PACING_10MS'))
-            check(ykpers.ykp_set_cfgflag(cfg, 'PACING_20MS'))
-
-        try:
+        with self._create_cfg(cmd, config) as cfg:
             check(ykpers.yk_write_command(
                 self._dev, ykpers.ykp_core_config(cfg), cmd, self.access_code))
-        finally:
-            ykpers.ykp_free_config(cfg)
 
     @property
     def is_in_fips_mode(self):
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/yubikey-manager-2.1.1/ykman/piv.py 
new/yubikey-manager-3.0.0/ykman/piv.py
--- old/yubikey-manager-2.1.1/ykman/piv.py      2019-04-09 09:39:55.000000000 
+0200
+++ new/yubikey-manager-3.0.0/ykman/piv.py      2019-06-24 09:07:27.000000000 
+0200
@@ -846,10 +846,18 @@
             ).public_key(default_backend())
         elif algorithm in [ALGO.ECCP256, ALGO.ECCP384]:
             curve = ec.SECP256R1 if algorithm == ALGO.ECCP256 else ec.SECP384R1
-            return ec.EllipticCurvePublicNumbers.from_encoded_point(
-                curve(),
-                resp[5:]
-            ).public_key(default_backend())
+
+            try:
+                # Added in cryptography 2.5
+                return ec.EllipticCurvePublicKey.from_encoded_point(
+                    curve(),
+                    resp[5:]
+                )
+            except AttributeError:
+                return ec.EllipticCurvePublicNumbers.from_encoded_point(
+                    curve(),
+                    resp[5:]
+                ).public_key(default_backend())
 
         raise UnsupportedAlgorithm(
             'Invalid algorithm: {}'.format(algorithm),
@@ -949,7 +957,7 @@
                     raise KeypairMismatch(slot, certificate)
                 raise
 
-            except InvalidSignature as e:
+            except InvalidSignature:
                 raise KeypairMismatch(slot, certificate)
 
         self.put_data(OBJ.from_slot(slot), Tlv(TAG.CERTIFICATE, cert_data) +
@@ -1010,10 +1018,10 @@
     def update_chuid(self):
         # Non-Federal Issuer FASC-N
         # [9999-9999-999999-0-1-0000000000300001]
-        FASC_N=b'\xd4\xe7\x39\xda\x73\x9c\xed\x39\xce\x73\x9d\x83\x68' + \
-               b'\x58\x21\x08\x42\x10\x84\x21\xc8\x42\x10\xc3\xeb'
+        FASC_N = b'\xd4\xe7\x39\xda\x73\x9c\xed\x39\xce\x73\x9d\x83\x68' + \
+                 b'\x58\x21\x08\x42\x10\x84\x21\xc8\x42\x10\xc3\xeb'
         # Expires on: 2030-01-01
-        EXPIRY=b'\x32\x30\x33\x30\x30\x31\x30\x31'
+        EXPIRY = b'\x32\x30\x33\x30\x30\x31\x30\x31'
 
         self.put_data(
             OBJ.CHUID,
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/yubikey-manager-2.1.1/ykman/util.py 
new/yubikey-manager-3.0.0/ykman/util.py
--- old/yubikey-manager-2.1.1/ykman/util.py     2019-05-27 10:21:11.000000000 
+0200
+++ new/yubikey-manager-3.0.0/ykman/util.py     2019-06-24 09:07:27.000000000 
+0200
@@ -111,6 +111,7 @@
     USB_A_NANO = 0x02
     USB_C_KEYCHAIN = 0x03
     USB_C_NANO = 0x04
+    USB_C_LIGHTNING = 0x05
 
     def __str__(self):
         if self == FORM_FACTOR.USB_A_KEYCHAIN:
@@ -121,6 +122,8 @@
             return 'Keychain (USB-C)'
         elif self == FORM_FACTOR.USB_C_NANO:
             return 'Nano (USB-C)'
+        elif self == FORM_FACTOR.USB_C_LIGHTNING:
+            return 'Keychain (USB-C, Lightning)'
         elif self == FORM_FACTOR.UNKNOWN:
             return 'Unknown.'
 
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' 
old/yubikey-manager-2.1.1/yubikey_manager.egg-info/PKG-INFO 
new/yubikey-manager-3.0.0/yubikey_manager.egg-info/PKG-INFO
--- old/yubikey-manager-2.1.1/yubikey_manager.egg-info/PKG-INFO 2019-05-28 
10:05:25.000000000 +0200
+++ new/yubikey-manager-3.0.0/yubikey_manager.egg-info/PKG-INFO 2019-06-24 
09:12:28.000000000 +0200
@@ -1,13 +1,12 @@
-Metadata-Version: 1.2
+Metadata-Version: 1.1
 Name: yubikey-manager
-Version: 2.1.1
+Version: 3.0.0
 Summary: Tool for managing your YubiKey configuration.
 Home-page: https://github.com/Yubico/yubikey-manager
-Author: Dain Nilsson
-Author-email: d...@yubico.com
-Maintainer: Yubico Open Source Maintainers
-Maintainer-email: ossma...@yubico.com
+Author: Yubico Open Source Maintainers
+Author-email: ossma...@yubico.com
 License: BSD 2 clause
+Description-Content-Type: UNKNOWN
 Description: UNKNOWN
 Platform: UNKNOWN
 Classifier: License :: OSI Approved :: BSD License
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' 
old/yubikey-manager-2.1.1/yubikey_manager.egg-info/SOURCES.txt 
new/yubikey-manager-3.0.0/yubikey_manager.egg-info/SOURCES.txt
--- old/yubikey-manager-2.1.1/yubikey_manager.egg-info/SOURCES.txt      
2019-05-28 10:05:25.000000000 +0200
+++ new/yubikey-manager-3.0.0/yubikey_manager.egg-info/SOURCES.txt      
2019-06-24 09:12:28.000000000 +0200
@@ -14,6 +14,14 @@
 test/test_scancodes.py
 test/test_util.py
 test/util.py
+test/__pycache__/__init__.cpython-36.pyc
+test/__pycache__/test_device.cpython-36.pyc
+test/__pycache__/test_external_libs.cpython-36.pyc
+test/__pycache__/test_oath.cpython-36.pyc
+test/__pycache__/test_piv.cpython-36.pyc
+test/__pycache__/test_scancodes.cpython-36.pyc
+test/__pycache__/test_util.cpython-36.pyc
+test/__pycache__/util.cpython-36.pyc
 test/files/rsa_1024_key.pem
 test/files/rsa_2048_cert.der
 test/files/rsa_2048_cert.pem
@@ -32,6 +40,16 @@
 test/on_yubikey/test_interfaces.py
 test/on_yubikey/test_piv.py
 test/on_yubikey/util.py
+test/on_yubikey/__pycache__/__init__.cpython-36.pyc
+test/on_yubikey/__pycache__/test_cli_config.cpython-36.pyc
+test/on_yubikey/__pycache__/test_cli_misc.cpython-36.pyc
+test/on_yubikey/__pycache__/test_cli_oath.cpython-36.pyc
+test/on_yubikey/__pycache__/test_cli_openpgp.cpython-36.pyc
+test/on_yubikey/__pycache__/test_cli_otp.cpython-36.pyc
+test/on_yubikey/__pycache__/test_fips_u2f_commands.cpython-36.pyc
+test/on_yubikey/__pycache__/test_interfaces.cpython-36.pyc
+test/on_yubikey/__pycache__/test_piv.cpython-36.pyc
+test/on_yubikey/__pycache__/util.cpython-36.pyc
 test/on_yubikey/cli_piv/__init__.py
 test/on_yubikey/cli_piv/test_fips.py
 test/on_yubikey/cli_piv/test_generate_cert_and_csr.py
@@ -40,6 +58,14 @@
 test/on_yubikey/cli_piv/test_misc.py
 test/on_yubikey/cli_piv/test_pin_puk.py
 test/on_yubikey/cli_piv/util.py
+test/on_yubikey/cli_piv/__pycache__/__init__.cpython-36.pyc
+test/on_yubikey/cli_piv/__pycache__/test_fips.cpython-36.pyc
+test/on_yubikey/cli_piv/__pycache__/test_generate_cert_and_csr.cpython-36.pyc
+test/on_yubikey/cli_piv/__pycache__/test_key_management.cpython-36.pyc
+test/on_yubikey/cli_piv/__pycache__/test_management_key.cpython-36.pyc
+test/on_yubikey/cli_piv/__pycache__/test_misc.cpython-36.pyc
+test/on_yubikey/cli_piv/__pycache__/test_pin_puk.cpython-36.pyc
+test/on_yubikey/cli_piv/__pycache__/util.cpython-36.pyc
 ykman/VERSION
 ykman/__init__.py
 ykman/descriptor.py


Reply via email to