Blob Blame History Raw
From 1d38bede51448043ccda379adc6a254ddd313893 Mon Sep 17 00:00:00 2001
From: Petr Viktorin <pviktori@redhat.com>
Date: Wed, 1 Jul 2015 15:04:18 +0200
Subject: [PATCH 1/6] Breaking circular imports: Move __version__ to a
 dedicated module

---
 setup.py                      | 2 +-
 yubico/__init__.py            | 2 +-
 yubico/yubico_exception.py    | 2 +-
 yubico/yubico_util.py         | 2 +-
 yubico/yubico_version.py      | 1 +
 yubico/yubikey.py             | 2 +-
 yubico/yubikey_config.py      | 2 +-
 yubico/yubikey_defs.py        | 2 +-
 yubico/yubikey_frame.py       | 2 +-
 yubico/yubikey_neo_usb_hid.py | 2 +-
 yubico/yubikey_usb_hid.py     | 2 +-
 11 files changed, 11 insertions(+), 10 deletions(-)
 create mode 100644 yubico/yubico_version.py

diff --git a/setup.py b/setup.py
index cc3e92e..0b08fec 100644
--- a/setup.py
+++ b/setup.py
@@ -10,7 +10,7 @@
 def get_version():
     """Return the current version as defined by yubico/__init__.py."""
 
-    with open('yubico/__init__.py', 'r') as f:
+    with open('yubico/yubico_version.py', 'r') as f:
         match = VERSION_PATTERN.search(f.read())
         return match.group(1)
 
diff --git a/yubico/__init__.py b/yubico/__init__.py
index 0f868b2..738897e 100644
--- a/yubico/__init__.py
+++ b/yubico/__init__.py
@@ -20,7 +20,7 @@
 # Copyright (c) 2010, 2011, 2012 Yubico AB
 # See the file COPYING for licence statement.
 
-__version__ = "1.2.3"
+from yubico_version import __version__
 
 __all__ = [
     # classes
diff --git a/yubico/yubico_exception.py b/yubico/yubico_exception.py
index bb36f0d..3adba6c 100644
--- a/yubico/yubico_exception.py
+++ b/yubico/yubico_exception.py
@@ -22,7 +22,7 @@ class for exceptions used in the other Yubico modules
     'YubiKeyTimeout',
 ]
 
-from yubico import __version__
+from yubico_version import __version__
 
 class YubicoError(Exception):
     """
diff --git a/yubico/yubico_util.py b/yubico/yubico_util.py
index 2f3f1bc..8e13f32 100644
--- a/yubico/yubico_util.py
+++ b/yubico/yubico_util.py
@@ -15,7 +15,7 @@
     # classes
 ]
 
-from yubico import __version__
+from yubico_version import __version__
 import yubikey_defs
 import yubico_exception
 import string
diff --git a/yubico/yubico_version.py b/yubico/yubico_version.py
new file mode 100644
index 0000000..10aa336
--- /dev/null
+++ b/yubico/yubico_version.py
@@ -0,0 +1 @@
+__version__ = "1.2.3"
diff --git a/yubico/yubikey.py b/yubico/yubikey.py
index a44dabc..c576e80 100644
--- a/yubico/yubikey.py
+++ b/yubico/yubikey.py
@@ -31,7 +31,7 @@
     'YubiKeyTimeout',
 ]
 
-from yubico  import __version__
+from yubico_version  import __version__
 import yubico_exception
 
 class YubiKeyError(yubico_exception.YubicoError):
diff --git a/yubico/yubikey_config.py b/yubico/yubikey_config.py
index 0f08e3f..4f9ae29 100644
--- a/yubico/yubikey_config.py
+++ b/yubico/yubikey_config.py
@@ -15,7 +15,7 @@
     'YubiKeyConfig',
 ]
 
-from yubico import __version__
+from yubico_version import __version__
 
 import struct
 import binascii
diff --git a/yubico/yubikey_defs.py b/yubico/yubikey_defs.py
index 74f5bb8..843b8d9 100644
--- a/yubico/yubikey_defs.py
+++ b/yubico/yubikey_defs.py
@@ -18,7 +18,7 @@
     # classes
 ]
 
-from yubico import __version__
+from yubico_version import __version__
 
 # Yubikey Low level interface #2.3
 RESP_TIMEOUT_WAIT_MASK	= 0x1f # Mask to get timeout value
diff --git a/yubico/yubikey_frame.py b/yubico/yubikey_frame.py
index 018f9c7..c34b4ec 100644
--- a/yubico/yubikey_frame.py
+++ b/yubico/yubikey_frame.py
@@ -17,7 +17,7 @@
 import yubikey_defs
 import yubico_exception
 import yubikey_config
-from yubico import __version__
+from yubico_version import __version__
 
 class YubiKeyFrame:
     """
diff --git a/yubico/yubikey_neo_usb_hid.py b/yubico/yubikey_neo_usb_hid.py
index f7913c7..7711779 100644
--- a/yubico/yubikey_neo_usb_hid.py
+++ b/yubico/yubikey_neo_usb_hid.py
@@ -16,7 +16,7 @@
 
 import struct
 
-from yubico import __version__
+from yubico_version import __version__
 import yubikey_usb_hid
 import yubikey_frame
 import yubico_exception
diff --git a/yubico/yubikey_usb_hid.py b/yubico/yubikey_usb_hid.py
index e808804..716ddaf 100644
--- a/yubico/yubikey_usb_hid.py
+++ b/yubico/yubikey_usb_hid.py
@@ -14,7 +14,7 @@
   'YubiKeyUSBHIDStatus',
 ]
 
-from yubico import __version__
+from yubico_version import __version__
 
 import yubico_util
 import yubico_exception

From 8061cb91bb582f7000e7432bb2319b9f70add306 Mon Sep 17 00:00:00 2001
From: Petr Viktorin <pviktori@redhat.com>
Date: Wed, 1 Jul 2015 15:12:45 +0200
Subject: [PATCH 2/6] Breaking circular imports: Move command definitions to
 yubikey_defs

This breaks the yubikey_config <-> yubikey_frame circular import.
---
 yubico/yubikey_config.py  | 22 ++++------------------
 yubico/yubikey_defs.py    | 18 ++++++++++++++++++
 yubico/yubikey_frame.py   | 13 ++++++-------
 yubico/yubikey_usb_hid.py |  2 +-
 4 files changed, 29 insertions(+), 26 deletions(-)

diff --git a/yubico/yubikey_config.py b/yubico/yubikey_config.py
index 4f9ae29..24f0ba0 100644
--- a/yubico/yubikey_config.py
+++ b/yubico/yubikey_config.py
@@ -27,6 +27,10 @@
 from yubikey_config_util import YubiKeyConfigBits, YubiKeyConfigFlag, YubiKeyExtendedFlag, YubiKeyTicketFlag
 import yubikey
 
+# these used to be defined here; import them for backwards compatibility
+from yubikey_defs import SLOT_CONFIG, SLOT_CONFIG2, SLOT_UPDATE1, SLOT_UPDATE2, SLOT_SWAP, command2str
+
+
 TicketFlags = [
     YubiKeyTicketFlag('TAB_FIRST',		0x01, min_ykver=(1, 0), doc='Send TAB before first part'),
     YubiKeyTicketFlag('APPEND_TAB1',		0x02, min_ykver=(1, 0), doc='Send TAB after first part'),
@@ -79,24 +83,6 @@
     YubiKeyExtendedFlag('DORMANT',		0x40, min_ykver=(2, 3), doc='Dormant configuration (can be woken up and flag removed = requires update flag)'),
     ]
 
-SLOT_CONFIG			= 0x01	# First (default / V1) configuration
-SLOT_CONFIG2			= 0x03	# Second (V2) configuration
-SLOT_UPDATE1			= 0x04	# Update slot 1
-SLOT_UPDATE2			= 0x05	# Update slot 2
-SLOT_SWAP			= 0x06	# Swap slot 1 and 2
-
-def command2str(num):
-    """ Turn command number into name """
-    known = {0x01: "SLOT_CONFIG",
-             0x03: "SLOT_CONFIG2",
-             0x04: "SLOT_UPDATE1",
-             0x05: "SLOT_UPDATE2",
-             0x06: "SLOT_SWAP",
-             }
-    if num in known:
-        return known[num]
-    return "0x%02x" % (num)
-
 class YubiKeyConfigError(yubico_exception.YubicoError):
     """
     Exception raised for YubiKey configuration errors.
diff --git a/yubico/yubikey_defs.py b/yubico/yubikey_defs.py
index 843b8d9..bd4f7c4 100644
--- a/yubico/yubikey_defs.py
+++ b/yubico/yubikey_defs.py
@@ -31,3 +31,21 @@
 OTP_CHALRESP_SIZE	= 16   # Number of bytes returned for an Yubico-OTP challenge (not from ykdef.h)
 
 UID_SIZE		= 6    # Size of secret ID field
+
+SLOT_CONFIG			= 0x01	# First (default / V1) configuration
+SLOT_CONFIG2			= 0x03	# Second (V2) configuration
+SLOT_UPDATE1			= 0x04	# Update slot 1
+SLOT_UPDATE2			= 0x05	# Update slot 2
+SLOT_SWAP			= 0x06	# Swap slot 1 and 2
+
+def command2str(num):
+    """ Turn command number into name """
+    known = {0x01: "SLOT_CONFIG",
+             0x03: "SLOT_CONFIG2",
+             0x04: "SLOT_UPDATE1",
+             0x05: "SLOT_UPDATE2",
+             0x06: "SLOT_SWAP",
+             }
+    if num in known:
+        return known[num]
+    return "0x%02x" % (num)
diff --git a/yubico/yubikey_frame.py b/yubico/yubikey_frame.py
index c34b4ec..63df89d 100644
--- a/yubico/yubikey_frame.py
+++ b/yubico/yubikey_frame.py
@@ -16,7 +16,6 @@
 import yubico_util
 import yubikey_defs
 import yubico_exception
-import yubikey_config
 from yubico_version import __version__
 
 class YubiKeyFrame:
@@ -93,13 +92,13 @@ def _debug_string(self, debug, data):
         """
         if not debug:
             return data
-        if self.command in [yubikey_config.SLOT_CONFIG,
-                            yubikey_config.SLOT_CONFIG2,
-                            yubikey_config.SLOT_UPDATE1,
-                            yubikey_config.SLOT_UPDATE2,
-                            yubikey_config.SLOT_SWAP,
+        if self.command in [yubikey_defs.SLOT_CONFIG,
+                            yubikey_defs.SLOT_CONFIG2,
+                            yubikey_defs.SLOT_UPDATE1,
+                            yubikey_defs.SLOT_UPDATE2,
+                            yubikey_defs.SLOT_SWAP,
                             ]:
-            # annotate according to config_st (see yubikey_config.to_string())
+            # annotate according to config_st (see yubikey_defs.to_string())
             if ord(data[-1]) == 0x80:
                 return (data, "FFFFFFF")
             if ord(data[-1]) == 0x81:
diff --git a/yubico/yubikey_usb_hid.py b/yubico/yubikey_usb_hid.py
index 716ddaf..1fd4baa 100644
--- a/yubico/yubikey_usb_hid.py
+++ b/yubico/yubikey_usb_hid.py
@@ -280,7 +280,7 @@ def _write_config(self, cfg, slot):
         old_pgm_seq = self._status.pgm_seq
         frame = cfg.to_frame(slot=slot)
         self._debug("Writing %s frame :\n%s\n" % \
-                        (yubikey_config.command2str(frame.command), cfg))
+                        (yubikey_defs.command2str(frame.command), cfg))
         self._write(frame)
         self._waitfor_clear(yubikey_defs.SLOT_WRITE_FLAG)
         # make sure we have a fresh pgm_seq value

From 3fe8f804faae595e32e880abe349cc6e6d0c26bf Mon Sep 17 00:00:00 2001
From: Petr Viktorin <pviktori@redhat.com>
Date: Wed, 1 Jul 2015 15:19:06 +0200
Subject: [PATCH 3/6] Breaking circular imports: Move base classes to
 yubico_base.py

This breaks the yubikey <-> yubikey_usb_hid circular import
---
 yubico/yubikey.py         | 180 +--------------------------------------------
 yubico/yubikey_base.py    | 182 ++++++++++++++++++++++++++++++++++++++++++++++
 yubico/yubikey_config.py  |  32 ++++----
 yubico/yubikey_usb_hid.py |  20 ++---
 4 files changed, 209 insertions(+), 205 deletions(-)
 create mode 100644 yubico/yubikey_base.py

diff --git a/yubico/yubikey.py b/yubico/yubikey.py
index c576e80..d23af88 100644
--- a/yubico/yubikey.py
+++ b/yubico/yubikey.py
@@ -32,185 +32,7 @@
 ]
 
 from yubico_version  import __version__
-import yubico_exception
-
-class YubiKeyError(yubico_exception.YubicoError):
-    """
-    Exception raised concerning YubiKey operations.
-
-    Attributes:
-        reason -- explanation of the error
-    """
-    def __init__(self, reason='no details'):
-        yubico_exception.YubicoError.__init__(self, reason)
-
-class YubiKeyTimeout(YubiKeyError):
-    """
-    Exception raised when a YubiKey operation timed out.
-
-    Attributes:
-        reason -- explanation of the error
-    """
-    def __init__(self, reason='no details'):
-        YubiKeyError.__init__(self, reason)
-
-class YubiKeyVersionError(YubiKeyError):
-    """
-    Exception raised when the YubiKey is not capable of something requested.
-
-    Attributes:
-        reason -- explanation of the error
-    """
-    def __init__(self, reason='no details'):
-        YubiKeyError.__init__(self, reason)
-
-
-class YubiKeyCapabilities():
-    """
-    Class expressing the functionality of a YubiKey.
-
-    This base class should be the superset of all sub-classes.
-
-    In this base class, we lie and say 'yes' to all capabilities.
-
-    If the base class is used (such as when creating a YubiKeyConfig()
-    before getting a YubiKey()), errors must be handled at runtime
-    (or later, when the user is unable to use the YubiKey).
-    """
-
-    model = 'Unknown'
-    version = (0, 0, 0,)
-    version_num = 0x0
-    default_answer = True
-
-    def __init__(self, model = None, version = None, default_answer = None):
-        self.model = model
-        if default_answer is not None:
-            self.default_answer = default_answer
-        if version is not None:
-            self.version = version
-            (major, minor, build,) = version
-            # convert 2.1.3 to 0x00020103
-            self.version_num = (major << 24) | (minor << 16) | build
-        return None
-
-    def __repr__(self):
-        return '<%s instance at %s: Device %s %s (default: %s)>' % (
-            self.__class__.__name__,
-            hex(id(self)),
-            self.model,
-            self.version,
-            self.default_answer,
-            )
-
-    def have_yubico_OTP(self):
-        return self.default_answer
-
-    def have_OATH(self, mode):
-        return self.default_answer
-
-    def have_challenge_response(self, mode):
-        return self.default_answer
-
-    def have_serial_number(self):
-        return self.default_answer
-
-    def have_ticket_flag(self, flag):
-        return self.default_answer
-
-    def have_config_flag(self, flag):
-        return self.default_answer
-
-    def have_extended_flag(self, flag):
-        return self.default_answer
-
-    def have_extended_scan_code_mode(self):
-        return self.default_answer
-
-    def have_shifted_1_mode(self):
-        return self.default_answer
-
-    def have_nfc_ndef(self):
-        return self.default_answer
-
-    def have_configuration_slot(self):
-        return self.default_answer
-
-class YubiKey():
-    """
-    Base class for accessing YubiKeys
-    """
-
-    debug = None
-    capabilities = None
-
-    def __init__(self, debug, capabilities = None):
-        self.debug = debug
-        if capabilities is None:
-            self.capabilities = YubiKeyCapabilities(default_answer = False)
-        else:
-            self.capabilities = capabilities
-        return None
-
-    def version(self):
-        """ Get the connected YubiKey's version as a string. """
-        pass
-
-    def serial(self, may_block=True):
-        """
-        Get the connected YubiKey's serial number.
-
-        Note that since version 2.?.? this requires the YubiKey to be
-        configured with the extended flag SERIAL_API_VISIBLE.
-
-        If the YubiKey is configured with SERIAL_BTN_VISIBLE set to True,
-        it will start blinking and require a button press before revealing
-        the serial number, with a 15 seconds timeout. Set `may_block'
-        to False to abort if this is the case.
-        """
-        pass
-
-    def challenge(self, challenge, mode='HMAC', slot=1, variable=True, may_block=True):
-        """
-        Get the response to a challenge from a connected YubiKey.
-
-        `mode' is either 'HMAC' or 'OTP'.
-        `slot' is 1 or 2.
-        `variable' is only relevant for mode == HMAC.
-
-        If variable is True, challenge will be padded such that the
-        YubiKey will compute the HMAC as if there were no padding.
-        If variable is False, challenge will always be NULL-padded
-        to 64 bytes.
-
-        The special case of no input will be HMACed by the YubiKey
-        (in variable HMAC mode) as data = 0x00, length = 1.
-
-        In mode 'OTP', the challenge should be exactly 6 bytes. The
-        response will be a YubiKey "ticket" with the 6-byte challenge
-        in the ticket.uid field. The rest of the "ticket" will contain
-        timestamp and counter information, so two identical challenges
-        will NOT result in the same responses. The response is
-        decryptable using AES ECB if you have access to the AES key
-        programmed into the YubiKey.
-        """
-        pass
-
-    def init_config(self):
-        """
-        Return a YubiKey configuration object for this type of YubiKey.
-        """
-        pass
-
-    def write_config(self, cfg, slot):
-        """
-        Configure a YubiKey using a configuration object.
-        """
-        pass
-
-# Since YubiKeyUSBHID is a subclass of YubiKey (defined here above),
-# the import must be after the declaration of YubiKey. We also carefully
-# import only what we need to not get a circular import of modules.
+from yubikey_base import YubiKeyError, YubiKeyTimeout, YubiKeyVersionError, YubiKeyCapabilities, YubiKey
 from yubikey_usb_hid import YubiKeyUSBHID, YubiKeyUSBHIDError
 from yubikey_neo_usb_hid import YubiKeyNEO_USBHID, YubiKeyNEO_USBHIDError
 
diff --git a/yubico/yubikey_base.py b/yubico/yubikey_base.py
new file mode 100644
index 0000000..ef53e2e
--- /dev/null
+++ b/yubico/yubikey_base.py
@@ -0,0 +1,182 @@
+"""
+module for Yubikey base classes
+"""
+# Copyright (c) 2010, 2011, 2012 Yubico AB
+# See the file COPYING for licence statement.
+
+from yubico_version  import __version__
+import yubico_exception
+
+class YubiKeyError(yubico_exception.YubicoError):
+    """
+    Exception raised concerning YubiKey operations.
+
+    Attributes:
+        reason -- explanation of the error
+    """
+    def __init__(self, reason='no details'):
+        yubico_exception.YubicoError.__init__(self, reason)
+
+class YubiKeyTimeout(YubiKeyError):
+    """
+    Exception raised when a YubiKey operation timed out.
+
+    Attributes:
+        reason -- explanation of the error
+    """
+    def __init__(self, reason='no details'):
+        YubiKeyError.__init__(self, reason)
+
+class YubiKeyVersionError(YubiKeyError):
+    """
+    Exception raised when the YubiKey is not capable of something requested.
+
+    Attributes:
+        reason -- explanation of the error
+    """
+    def __init__(self, reason='no details'):
+        YubiKeyError.__init__(self, reason)
+
+
+class YubiKeyCapabilities():
+    """
+    Class expressing the functionality of a YubiKey.
+
+    This base class should be the superset of all sub-classes.
+
+    In this base class, we lie and say 'yes' to all capabilities.
+
+    If the base class is used (such as when creating a YubiKeyConfig()
+    before getting a YubiKey()), errors must be handled at runtime
+    (or later, when the user is unable to use the YubiKey).
+    """
+
+    model = 'Unknown'
+    version = (0, 0, 0,)
+    version_num = 0x0
+    default_answer = True
+
+    def __init__(self, model = None, version = None, default_answer = None):
+        self.model = model
+        if default_answer is not None:
+            self.default_answer = default_answer
+        if version is not None:
+            self.version = version
+            (major, minor, build,) = version
+            # convert 2.1.3 to 0x00020103
+            self.version_num = (major << 24) | (minor << 16) | build
+        return None
+
+    def __repr__(self):
+        return '<%s instance at %s: Device %s %s (default: %s)>' % (
+            self.__class__.__name__,
+            hex(id(self)),
+            self.model,
+            self.version,
+            self.default_answer,
+            )
+
+    def have_yubico_OTP(self):
+        return self.default_answer
+
+    def have_OATH(self, mode):
+        return self.default_answer
+
+    def have_challenge_response(self, mode):
+        return self.default_answer
+
+    def have_serial_number(self):
+        return self.default_answer
+
+    def have_ticket_flag(self, flag):
+        return self.default_answer
+
+    def have_config_flag(self, flag):
+        return self.default_answer
+
+    def have_extended_flag(self, flag):
+        return self.default_answer
+
+    def have_extended_scan_code_mode(self):
+        return self.default_answer
+
+    def have_shifted_1_mode(self):
+        return self.default_answer
+
+    def have_nfc_ndef(self):
+        return self.default_answer
+
+    def have_configuration_slot(self):
+        return self.default_answer
+
+class YubiKey():
+    """
+    Base class for accessing YubiKeys
+    """
+
+    debug = None
+    capabilities = None
+
+    def __init__(self, debug, capabilities = None):
+        self.debug = debug
+        if capabilities is None:
+            self.capabilities = YubiKeyCapabilities(default_answer = False)
+        else:
+            self.capabilities = capabilities
+        return None
+
+    def version(self):
+        """ Get the connected YubiKey's version as a string. """
+        pass
+
+    def serial(self, may_block=True):
+        """
+        Get the connected YubiKey's serial number.
+
+        Note that since version 2.?.? this requires the YubiKey to be
+        configured with the extended flag SERIAL_API_VISIBLE.
+
+        If the YubiKey is configured with SERIAL_BTN_VISIBLE set to True,
+        it will start blinking and require a button press before revealing
+        the serial number, with a 15 seconds timeout. Set `may_block'
+        to False to abort if this is the case.
+        """
+        pass
+
+    def challenge(self, challenge, mode='HMAC', slot=1, variable=True, may_block=True):
+        """
+        Get the response to a challenge from a connected YubiKey.
+
+        `mode' is either 'HMAC' or 'OTP'.
+        `slot' is 1 or 2.
+        `variable' is only relevant for mode == HMAC.
+
+        If variable is True, challenge will be padded such that the
+        YubiKey will compute the HMAC as if there were no padding.
+        If variable is False, challenge will always be NULL-padded
+        to 64 bytes.
+
+        The special case of no input will be HMACed by the YubiKey
+        (in variable HMAC mode) as data = 0x00, length = 1.
+
+        In mode 'OTP', the challenge should be exactly 6 bytes. The
+        response will be a YubiKey "ticket" with the 6-byte challenge
+        in the ticket.uid field. The rest of the "ticket" will contain
+        timestamp and counter information, so two identical challenges
+        will NOT result in the same responses. The response is
+        decryptable using AES ECB if you have access to the AES key
+        programmed into the YubiKey.
+        """
+        pass
+
+    def init_config(self):
+        """
+        Return a YubiKey configuration object for this type of YubiKey.
+        """
+        pass
+
+    def write_config(self, cfg, slot):
+        """
+        Configure a YubiKey using a configuration object.
+        """
+        pass
diff --git a/yubico/yubikey_config.py b/yubico/yubikey_config.py
index 24f0ba0..a3e9146 100644
--- a/yubico/yubikey_config.py
+++ b/yubico/yubikey_config.py
@@ -25,7 +25,7 @@
 import yubico_exception
 import yubikey_config_util
 from yubikey_config_util import YubiKeyConfigBits, YubiKeyConfigFlag, YubiKeyExtendedFlag, YubiKeyTicketFlag
-import yubikey
+import yubikey_base
 
 # these used to be defined here; import them for backwards compatibility
 from yubikey_defs import SLOT_CONFIG, SLOT_CONFIG2, SLOT_UPDATE1, SLOT_UPDATE2, SLOT_SWAP, command2str
@@ -110,7 +110,7 @@ def __init__(self, ykver=None, capabilities=None, update=False, swap=False):
         slot 1 be slot 2 and vice versa. Set swap=True for this.
         """
         if capabilities is None:
-            self.capabilities = yubikey.YubiKeyCapabilities(default_answer = True)
+            self.capabilities = yubikey_base.YubiKeyCapabilities(default_answer = True)
         else:
             self.capabilities = capabilities
 
@@ -270,8 +270,8 @@ def mode_yubikey_otp(self, private_uid, aes_key):
         Set the YubiKey up for standard OTP validation.
         """
         if not self.capabilities.have_yubico_OTP():
-            raise yubikey.YubiKeyVersionError('Yubico OTP not available in %s version %d.%d' \
-                                                  % (self.capabilities.model, self.ykver[0], self.ykver[1]))
+            raise yubikey_base.YubiKeyVersionError('Yubico OTP not available in %s version %d.%d' \
+                                                   % (self.capabilities.model, self.ykver[0], self.ykver[1]))
         if private_uid.startswith('h:'):
             private_uid = binascii.unhexlify(private_uid[2:])
         if len(private_uid) != yubikey_defs.UID_SIZE:
@@ -288,8 +288,8 @@ def mode_oath_hotp(self, secret, digits=6, factor_seed=None, omp=0x0, tt=0x0, mu
         Requires YubiKey 2.1.
         """
         if not self.capabilities.have_OATH('HOTP'):
-            raise yubikey.YubiKeyVersionError('OATH HOTP not available in %s version %d.%d' \
-                                                  % (self.capabilities.model, self.ykver[0], self.ykver[1]))
+            raise yubikey_base.YubiKeyVersionError('OATH HOTP not available in %s version %d.%d' \
+                                                   % (self.capabilities.model, self.ykver[0], self.ykver[1]))
         if digits != 6 and digits != 8:
             raise yubico_exception.InputError('OATH-HOTP digits must be 6 or 8')
 
@@ -320,9 +320,9 @@ def mode_challenge_response(self, secret, type='HMAC', variable=True, require_bu
         if not type.upper() in ['HMAC', 'OTP']:
             raise yubico_exception.InputError('Invalid \'type\' (%s)' % type)
         if not self.capabilities.have_challenge_response(type.upper()):
-            raise yubikey.YubiKeyVersionError('%s Challenge-Response not available in %s version %d.%d' \
-                                                  % (type.upper(), self.capabilities.model, \
-                                                         self.ykver[0], self.ykver[1]))
+            raise yubikey_base.YubiKeyVersionError('%s Challenge-Response not available in %s version %d.%d' \
+                                                   % (type.upper(), self.capabilities.model, \
+                                                          self.ykver[0], self.ykver[1]))
         self._change_mode('CHAL_RESP', major=2, minor=2)
         if type.upper() == 'HMAC':
             self.config_flag('CHAL_HMAC', True)
@@ -344,8 +344,8 @@ def ticket_flag(self, which, new=None):
         flag = _get_flag(which, TicketFlags)
         if flag:
             if not self.capabilities.have_ticket_flag(flag):
-                raise yubikey.YubiKeyVersionError('Ticket flag %s requires %s, and this is %s %d.%d'
-                                                  % (which, flag.req_string(self.capabilities.model), \
+                raise yubikey_base.YubiKeyVersionError('Ticket flag %s requires %s, and this is %s %d.%d'
+                                                       % (which, flag.req_string(self.capabilities.model), \
                                                          self.capabilities.model, self.ykver[0], self.ykver[1]))
             req_major, req_minor = flag.req_version()
             self._require_version(major=req_major, minor=req_minor)
@@ -367,8 +367,8 @@ def config_flag(self, which, new=None):
         flag = _get_flag(which, ConfigFlags)
         if flag:
             if not self.capabilities.have_config_flag(flag):
-                raise yubikey.YubiKeyVersionError('Config flag %s requires %s, and this is %s %d.%d'
-                                                  % (which, flag.req_string(self.capabilities.model), \
+                raise yubikey_base.YubiKeyVersionError('Config flag %s requires %s, and this is %s %d.%d'
+                                                       % (which, flag.req_string(self.capabilities.model), \
                                                          self.capabilities.model, self.ykver[0], self.ykver[1]))
             req_major, req_minor = flag.req_version()
             self._require_version(major=req_major, minor=req_minor)
@@ -390,8 +390,8 @@ def extended_flag(self, which, new=None):
         flag = _get_flag(which, ExtendedFlags)
         if flag:
             if not self.capabilities.have_extended_flag(flag):
-                raise yubikey.YubiKeyVersionError('Extended flag %s requires %s, and this is %s %d.%d'
-                                                  % (which, flag.req_string(self.capabilities.model), \
+                raise yubikey_base.YubiKeyVersionError('Extended flag %s requires %s, and this is %s %d.%d'
+                                                       % (which, flag.req_string(self.capabilities.model), \
                                                          self.capabilities.model, self.ykver[0], self.ykver[1]))
             req_major, req_minor = flag.req_version()
             self._require_version(major=req_major, minor=req_minor)
@@ -472,7 +472,7 @@ def _require_version(self, major, minor=0):
         """ Update the minimum version of YubiKey this configuration can be applied to. """
         new_ver = (major, minor)
         if self.ykver and new_ver > self.ykver:
-            raise yubikey.YubiKeyVersionError('Configuration requires YubiKey %d.%d, and this is %d.%d'
+            raise yubikey_base.YubiKeyVersionError('Configuration requires YubiKey %d.%d, and this is %d.%d'
                                               % (major, minor, self.ykver[0], self.ykver[1]))
         if new_ver > self.yk_req_version:
             self.yk_req_version = new_ver
diff --git a/yubico/yubikey_usb_hid.py b/yubico/yubikey_usb_hid.py
index 1fd4baa..6af248b 100644
--- a/yubico/yubikey_usb_hid.py
+++ b/yubico/yubikey_usb_hid.py
@@ -21,8 +21,8 @@
 import yubikey_frame
 import yubikey_config
 import yubikey_defs
-import yubikey
-from yubikey import YubiKey
+import yubikey_base
+from yubikey_base import YubiKey
 import struct
 import time
 import sys
@@ -85,7 +85,7 @@
 class YubiKeyUSBHIDError(yubico_exception.YubicoError):
     """ Exception raised for errors with the USB HID communication. """
 
-class YubiKeyUSBHIDCapabilities(yubikey.YubiKeyCapabilities):
+class YubiKeyUSBHIDCapabilities(yubikey_base.YubiKeyCapabilities):
     """
     Capture the capabilities of the various versions of YubiKeys.
 
@@ -93,9 +93,9 @@ class YubiKeyUSBHIDCapabilities(yubikey.YubiKeyCapabilities):
     in one or more versions, leaving the other ones at False through default_answer.
     """
     def __init__(self, model, version, default_answer):
-        yubikey.YubiKeyCapabilities.__init__(self, model = model, \
-                                                 version = version, \
-                                                 default_answer = default_answer)
+        yubikey_base.YubiKeyCapabilities.__init__(self, model = model, \
+                                                    version = version, \
+                                                    default_answer = default_answer)
 
     def have_yubico_OTP(self):
         """ Yubico OTP support has always been available in the standard YubiKey. """
@@ -203,13 +203,13 @@ def version(self):
     def serial(self, may_block=True):
         """ Get the YubiKey serial number (requires YubiKey 2.2). """
         if not self.capabilities.have_serial_number():
-            raise yubikey.YubiKeyVersionError("Serial number unsupported in YubiKey %s" % self.version() )
+            raise yubikey_base.YubiKeyVersionError("Serial number unsupported in YubiKey %s" % self.version() )
         return self._read_serial(may_block)
 
     def challenge_response(self, challenge, mode='HMAC', slot=1, variable=True, may_block=True):
         """ Issue a challenge to the YubiKey and return the response (requires YubiKey 2.2). """
         if not self.capabilities.have_challenge_response(mode):
-            raise yubikey.YubiKeyVersionError("%s challenge-response unsupported in YubiKey %s" % (mode, self.version()) )
+            raise yubikey_base.YubiKeyVersionError("%s challenge-response unsupported in YubiKey %s" % (mode, self.version()) )
         return self._challenge_response(challenge, mode, slot, variable, may_block)
 
     def init_config(self, **kw):
@@ -222,7 +222,7 @@ def write_config(self, cfg, slot=1):
         """ Write a configuration to the YubiKey. """
         cfg_req_ver = cfg.version_required()
         if cfg_req_ver > self.version_num():
-            raise yubikey.YubiKeyVersionError('Configuration requires YubiKey version %i.%i (this is %s)' % \
+            raise yubikey_base.YubiKeyVersionError('Configuration requires YubiKey version %i.%i (this is %s)' % \
                                                   (cfg_req_ver[0], cfg_req_ver[1], self.version()))
         if not self.capabilities.have_configuration_slot(slot):
             raise YubiKeyUSBHIDError("Can't write configuration to slot %i" % (slot))
@@ -436,7 +436,7 @@ def _waitfor(self, mode, mask, may_block, timeout=2):
                         reason = 'Timed out waiting for YubiKey to clear status 0x%x' % mask
                     else:
                         reason = 'Timed out waiting for YubiKey to set status 0x%x' % mask
-                    raise yubikey.YubiKeyTimeout(reason)
+                    raise yubikey_base.YubiKeyTimeout(reason)
                 time.sleep(sleep)
                 sleep = min(sleep + sleep, 0.5)
             else:

From 1f2e611c491a168ff5632b3365c399b0e059d367 Mon Sep 17 00:00:00 2001
From: Petr Viktorin <pviktori@redhat.com>
Date: Wed, 1 Jul 2015 14:18:34 +0200
Subject: [PATCH 4/6] Use Python 3-compatible syntax

- Use parentheses with print
  (python-yubico only ever gives one argument to print)
- Use 'as' syntax when catching expressions (PEP; Python 2.6+)
- Use explicit relative imports (PEP 328; Python 2.4+)
- Use range instead of xrange
  (this is in a debugging tool, the memory overhead is negligible,
  and the entire result is iterated over)
---
 test/test_yubikey_config.py   | 32 ++++++++++++++++----------------
 test/test_yubikey_frame.py    |  2 +-
 test/test_yubikey_usb_hid.py  |  8 ++++----
 yubico/__init__.py            |  6 +++---
 yubico/yubico_exception.py    |  2 +-
 yubico/yubico_util.py         |  8 ++++----
 yubico/yubikey.py             |  8 ++++----
 yubico/yubikey_base.py        |  4 ++--
 yubico/yubikey_config.py      | 18 +++++++++---------
 yubico/yubikey_defs.py        |  2 +-
 yubico/yubikey_frame.py       |  8 ++++----
 yubico/yubikey_neo_usb_hid.py |  8 ++++----
 yubico/yubikey_usb_hid.py     | 20 ++++++++++----------
 13 files changed, 63 insertions(+), 63 deletions(-)

diff --git a/test/test_yubikey_config.py b/test/test_yubikey_config.py
index 29b8649..5fc694c 100644
--- a/test/test_yubikey_config.py
+++ b/test/test_yubikey_config.py
@@ -41,8 +41,8 @@ def test_static_ticket(self):
 
         data = Config.to_frame(slot=1).to_feature_reports()
 
-        print "EXPECT:\n%s\nGOT:\n%s\n" %( yubico.yubico_util.hexdump(''.join(expected)),
-                                           yubico.yubico_util.hexdump(''.join(data)))
+        print("EXPECT:\n%s\nGOT:\n%s\n" %( yubico.yubico_util.hexdump(''.join(expected)),
+                                           yubico.yubico_util.hexdump(''.join(data))))
 
         self.assertEqual(data, expected)
 
@@ -75,8 +75,8 @@ def test_static_ticket_with_access_code(self):
 
         data = Config.to_frame(slot=1).to_feature_reports()
 
-        print "EXPECT:\n%s\nGOT:\n%s\n" %( yubico.yubico_util.hexdump(''.join(expected)),
-                                           yubico.yubico_util.hexdump(''.join(data)))
+        print("EXPECT:\n%s\nGOT:\n%s\n" %( yubico.yubico_util.hexdump(''.join(expected)),
+                                           yubico.yubico_util.hexdump(''.join(data))))
 
         self.assertEqual(data, expected)
 
@@ -113,8 +113,8 @@ def test_fixed_and_oath_hotp(self):
 
         data = Config.to_frame(slot=2).to_feature_reports()
 
-        print "EXPECT:\n%s\nGOT:\n%s\n" %( yubico.yubico_util.hexdump(''.join(expected)),
-                                           yubico.yubico_util.hexdump(''.join(data)))
+        print("EXPECT:\n%s\nGOT:\n%s\n" %( yubico.yubico_util.hexdump(''.join(expected)),
+                                           yubico.yubico_util.hexdump(''.join(data))))
 
         self.assertEqual(data, expected)
 
@@ -138,8 +138,8 @@ def test_challenge_response_hmac_nist(self):
 
         data = Config.to_frame(slot=2).to_feature_reports()
 
-        print "EXPECT:\n%s\nGOT:\n%s\n" %( yubico.yubico_util.hexdump(''.join(expected)),
-                                           yubico.yubico_util.hexdump(''.join(data)))
+        print("EXPECT:\n%s\nGOT:\n%s\n" %( yubico.yubico_util.hexdump(''.join(expected)),
+                                           yubico.yubico_util.hexdump(''.join(data))))
 
         self.assertEqual(data, expected)
 
@@ -186,8 +186,8 @@ def test_oath_hotp_like_windows(self):
 
         data = Config.to_frame(slot=2).to_feature_reports()
 
-        print "EXPECT:\n%s\nGOT:\n%s\n" %( yubico.yubico_util.hexdump(''.join(expected)),
-                                           yubico.yubico_util.hexdump(''.join(data)))
+        print("EXPECT:\n%s\nGOT:\n%s\n" %( yubico.yubico_util.hexdump(''.join(expected)),
+                                           yubico.yubico_util.hexdump(''.join(data))))
 
         self.assertEqual(data, expected)
 
@@ -211,8 +211,8 @@ def test_oath_hotp_like_windows2(self):
 
         data = Config.to_frame(slot=2).to_feature_reports()
 
-        print "EXPECT:\n%s\nGOT:\n%s\n" %( yubico.yubico_util.hexdump(''.join(expected)),
-                                           yubico.yubico_util.hexdump(''.join(data)))
+        print("EXPECT:\n%s\nGOT:\n%s\n" %( yubico.yubico_util.hexdump(''.join(expected)),
+                                           yubico.yubico_util.hexdump(''.join(data))))
 
         self.assertEqual(data, expected)
 
@@ -236,8 +236,8 @@ def test_oath_hotp_like_windows_factory_seed(self):
 
         data = Config.to_frame(slot=2).to_feature_reports()
 
-        print "EXPECT:\n%s\nGOT:\n%s\n" %( yubico.yubico_util.hexdump(''.join(expected)),
-                                           yubico.yubico_util.hexdump(''.join(data)))
+        print("EXPECT:\n%s\nGOT:\n%s\n" %( yubico.yubico_util.hexdump(''.join(expected)),
+                                           yubico.yubico_util.hexdump(''.join(data))))
 
         self.assertEqual(data, expected)
 
@@ -260,8 +260,8 @@ def test_fixed_length_hmac_like_windows(self):
 
         data = Config.to_frame(slot=2).to_feature_reports()
 
-        print "EXPECT:\n%s\nGOT:\n%s\n" %( yubico.yubico_util.hexdump(''.join(expected)),
-                                           yubico.yubico_util.hexdump(''.join(data)))
+        print("EXPECT:\n%s\nGOT:\n%s\n" %( yubico.yubico_util.hexdump(''.join(expected)),
+                                           yubico.yubico_util.hexdump(''.join(data))))
 
         self.assertEqual(data, expected)
 
diff --git a/test/test_yubikey_frame.py b/test/test_yubikey_frame.py
index f6cbb6e..5437922 100644
--- a/test/test_yubikey_frame.py
+++ b/test/test_yubikey_frame.py
@@ -50,7 +50,7 @@ def test_repr(self):
     """ Test string representation of object """
     # to achieve 100% test coverage ;)
     frame = YubiKeyFrame(command=0x4d)
-    print "Frame is represented as %s" % frame
+    print("Frame is represented as %s" % frame)
     re_match = re.search("YubiKeyFrame instance at .*: 77.$", str(frame))
     self.assertNotEqual(re_match, None)
 
diff --git a/test/test_yubikey_usb_hid.py b/test/test_yubikey_usb_hid.py
index 356b461..f801a33 100644
--- a/test/test_yubikey_usb_hid.py
+++ b/test/test_yubikey_usb_hid.py
@@ -18,10 +18,10 @@ def setUp(self):
         """ Test connecting to the YubiKey """
         if self.YK is None:
             try:
-                print "open key"
+                print("open key")
                 self.YK = YubiKeyUSBHID()
                 return
-            except YubiKeyUSBHIDError, err:
+            except YubiKeyUSBHIDError as err:
                 self.fail("No YubiKey connected (?) : %s" % str(err))
 
     def tearDown(self):
@@ -33,7 +33,7 @@ def test_status(self):
         """ Test the simplest form of communication : a status read request """
         status = self.YK.status()
         version = self.YK.version()
-        print "Version returned: %s" % version
+        print("Version returned: %s" % version)
         re_match = re.match("\d+\.\d+\.\d+$", version)
         self.assertNotEqual(re_match, None)
 
@@ -49,7 +49,7 @@ def test_challenge_response(self):
     def test_serial(self):
         """ Test serial number retrieval (requires YubiKey 2) """
         serial = self.YK.serial()
-        print "Serial returned : %s" % serial
+        print("Serial returned : %s" % serial)
         self.assertEqual(type(serial), type(1))
 
 if __name__ == '__main__':
diff --git a/yubico/__init__.py b/yubico/__init__.py
index 738897e..d81c1b1 100644
--- a/yubico/__init__.py
+++ b/yubico/__init__.py
@@ -20,7 +20,7 @@
 # Copyright (c) 2010, 2011, 2012 Yubico AB
 # See the file COPYING for licence statement.
 
-from yubico_version import __version__
+from .yubico_version import __version__
 
 __all__ = [
     # classes
@@ -40,5 +40,5 @@
     ]
 
 # to not have to import yubico.yubikey
-from yubikey import YubiKey
-from yubikey import find_key as find_yubikey
+from .yubikey import YubiKey
+from .yubikey import find_key as find_yubikey
diff --git a/yubico/yubico_exception.py b/yubico/yubico_exception.py
index 3adba6c..e2fd393 100644
--- a/yubico/yubico_exception.py
+++ b/yubico/yubico_exception.py
@@ -22,7 +22,7 @@ class for exceptions used in the other Yubico modules
     'YubiKeyTimeout',
 ]
 
-from yubico_version import __version__
+from .yubico_version import __version__
 
 class YubicoError(Exception):
     """
diff --git a/yubico/yubico_util.py b/yubico/yubico_util.py
index 8e13f32..5af3956 100644
--- a/yubico/yubico_util.py
+++ b/yubico/yubico_util.py
@@ -15,9 +15,9 @@
     # classes
 ]
 
-from yubico_version import __version__
-import yubikey_defs
-import yubico_exception
+from .yubico_version import __version__
+from . import yubikey_defs
+from . import yubico_exception
 import string
 
 _CRC_OK_RESIDUAL = 0xf0b8
@@ -101,7 +101,7 @@ def hexdump(src, length=8, colorize=False):
 
 def group(data, num):
     """ Split data into chunks of num chars each """
-    return [data[i:i+num] for i in xrange(0, len(data), num)]
+    return [data[i:i+num] for i in range(0, len(data), num)]
 
 def modhex_decode(data):
     """ Convert a modhex string to ordinary hex. """
diff --git a/yubico/yubikey.py b/yubico/yubikey.py
index d23af88..f71d56f 100644
--- a/yubico/yubikey.py
+++ b/yubico/yubikey.py
@@ -31,10 +31,10 @@
     'YubiKeyTimeout',
 ]
 
-from yubico_version  import __version__
-from yubikey_base import YubiKeyError, YubiKeyTimeout, YubiKeyVersionError, YubiKeyCapabilities, YubiKey
-from yubikey_usb_hid import YubiKeyUSBHID, YubiKeyUSBHIDError
-from yubikey_neo_usb_hid import YubiKeyNEO_USBHID, YubiKeyNEO_USBHIDError
+from .yubico_version  import __version__
+from .yubikey_base import YubiKeyError, YubiKeyTimeout, YubiKeyVersionError, YubiKeyCapabilities, YubiKey
+from .yubikey_usb_hid import YubiKeyUSBHID, YubiKeyUSBHIDError
+from .yubikey_neo_usb_hid import YubiKeyNEO_USBHID, YubiKeyNEO_USBHIDError
 
 def find_key(debug=False, skip=0):
     """
diff --git a/yubico/yubikey_base.py b/yubico/yubikey_base.py
index ef53e2e..9378082 100644
--- a/yubico/yubikey_base.py
+++ b/yubico/yubikey_base.py
@@ -4,8 +4,8 @@
 # Copyright (c) 2010, 2011, 2012 Yubico AB
 # See the file COPYING for licence statement.
 
-from yubico_version  import __version__
-import yubico_exception
+from .yubico_version  import __version__
+from . import yubico_exception
 
 class YubiKeyError(yubico_exception.YubicoError):
     """
diff --git a/yubico/yubikey_config.py b/yubico/yubikey_config.py
index a3e9146..3a02114 100644
--- a/yubico/yubikey_config.py
+++ b/yubico/yubikey_config.py
@@ -15,20 +15,20 @@
     'YubiKeyConfig',
 ]
 
-from yubico_version import __version__
+from .yubico_version import __version__
 
 import struct
 import binascii
-import yubico_util
-import yubikey_defs
-import yubikey_frame
-import yubico_exception
-import yubikey_config_util
-from yubikey_config_util import YubiKeyConfigBits, YubiKeyConfigFlag, YubiKeyExtendedFlag, YubiKeyTicketFlag
-import yubikey_base
+from . import yubico_util
+from . import yubikey_defs
+from . import yubikey_frame
+from . import yubico_exception
+from . import yubikey_config_util
+from .yubikey_config_util import YubiKeyConfigBits, YubiKeyConfigFlag, YubiKeyExtendedFlag, YubiKeyTicketFlag
+from . import yubikey_base
 
 # these used to be defined here; import them for backwards compatibility
-from yubikey_defs import SLOT_CONFIG, SLOT_CONFIG2, SLOT_UPDATE1, SLOT_UPDATE2, SLOT_SWAP, command2str
+from .yubikey_defs import SLOT_CONFIG, SLOT_CONFIG2, SLOT_UPDATE1, SLOT_UPDATE2, SLOT_SWAP, command2str
 
 
 TicketFlags = [
diff --git a/yubico/yubikey_defs.py b/yubico/yubikey_defs.py
index bd4f7c4..777df30 100644
--- a/yubico/yubikey_defs.py
+++ b/yubico/yubikey_defs.py
@@ -18,7 +18,7 @@
     # classes
 ]
 
-from yubico_version import __version__
+from .yubico_version import __version__
 
 # Yubikey Low level interface #2.3
 RESP_TIMEOUT_WAIT_MASK	= 0x1f # Mask to get timeout value
diff --git a/yubico/yubikey_frame.py b/yubico/yubikey_frame.py
index 63df89d..80919c9 100644
--- a/yubico/yubikey_frame.py
+++ b/yubico/yubikey_frame.py
@@ -13,10 +13,10 @@
 
 import struct
 
-import yubico_util
-import yubikey_defs
-import yubico_exception
-from yubico_version import __version__
+from . import yubico_util
+from . import yubikey_defs
+from . import yubico_exception
+from .yubico_version import __version__
 
 class YubiKeyFrame:
     """
diff --git a/yubico/yubikey_neo_usb_hid.py b/yubico/yubikey_neo_usb_hid.py
index 7711779..0543b8e 100644
--- a/yubico/yubikey_neo_usb_hid.py
+++ b/yubico/yubikey_neo_usb_hid.py
@@ -16,10 +16,10 @@
 
 import struct
 
-from yubico_version import __version__
-import yubikey_usb_hid
-import yubikey_frame
-import yubico_exception
+from .yubico_version import __version__
+from . import yubikey_usb_hid
+from . import yubikey_frame
+from . import yubico_exception
 
 # commands from ykdef.h
 _SLOT_NDEF		= 0x08 # Write YubiKey NEO NDEF
diff --git a/yubico/yubikey_usb_hid.py b/yubico/yubikey_usb_hid.py
index 6af248b..3cf47a4 100644
--- a/yubico/yubikey_usb_hid.py
+++ b/yubico/yubikey_usb_hid.py
@@ -14,15 +14,15 @@
   'YubiKeyUSBHIDStatus',
 ]
 
-from yubico_version import __version__
-
-import yubico_util
-import yubico_exception
-import yubikey_frame
-import yubikey_config
-import yubikey_defs
-import yubikey_base
-from yubikey_base import YubiKey
+from .yubico_version import __version__
+
+from . import yubico_util
+from . import yubico_exception
+from . import yubikey_frame
+from . import yubikey_config
+from . import yubikey_defs
+from . import yubikey_base
+from .yubikey_base import YubiKey
 import struct
 import time
 import sys
@@ -455,7 +455,7 @@ def _open(self, skip=0):
         try:
             self._usb_handle = usb_device.open()
             self._usb_handle.detachKernelDriver(0)
-        except Exception, error:
+        except Exception as error:
             if 'could not detach kernel driver from interface' in str(error):
                 self._debug('The in-kernel-HID driver has already been detached\n')
             else:

From ebb015bf0b32a6d3c3a9e86580c90b13545506b6 Mon Sep 17 00:00:00 2001
From: Petr Viktorin <pviktori@redhat.com>
Date: Wed, 1 Jul 2015 17:38:05 +0200
Subject: [PATCH 5/6] Distinguish text strings and bytestrings for Python 3
 compatibility

All (potentially binary) data is bytestrings; text (including
e.g. hexdumps and exception messages) is text strings.

Note that in Python 2, there's no difference between text (str, '...')
and bytestrings (bytes, b'...').
---
 test/test_yubico.py           |   8 +-
 test/test_yubikey_config.py   | 190 +++++++++++++++++++++---------------------
 test/test_yubikey_frame.py    |  20 ++---
 test/test_yubikey_usb_hid.py  |   4 +-
 yubico/yubico_util.py         |  70 +++++++++++-----
 yubico/yubikey_config.py      |  37 ++++----
 yubico/yubikey_frame.py       |  16 ++--
 yubico/yubikey_neo_usb_hid.py |  13 +--
 yubico/yubikey_usb_hid.py     |  16 ++--
 9 files changed, 205 insertions(+), 169 deletions(-)

diff --git a/test/test_yubico.py b/test/test_yubico.py
index f045d31..cd87f42 100644
--- a/test/test_yubico.py
+++ b/test/test_yubico.py
@@ -14,7 +14,7 @@ class TestCRC(unittest.TestCase):
 
     def test_first(self):
         """ Test CRC16 trivial case """
-        buffer = '\x01\x02\x03\x04'
+        buffer = b'\x01\x02\x03\x04'
         crc = crc16(buffer)
         self.assertEqual(crc, 0xc66e)
         return buffer,crc
@@ -31,19 +31,19 @@ def test_second(self):
 
     def test_hexdump(self):
         """ Test hexdump function, normal use """
-        bytes = '\x01\x02\x03\x04\x05\x06\x07\x08'
+        bytes = b'\x01\x02\x03\x04\x05\x06\x07\x08'
         self.assertEqual(yubico_util.hexdump(bytes, length=4), \
                              '0000   01 02 03 04\n0004   05 06 07 08\n')
 
     def test_hexdump(self):
         """ Test hexdump function, with colors """
-        bytes = '\x01\x02\x03\x04\x05\x06\x07\x08'
+        bytes = b'\x01\x02\x03\x04\x05\x06\x07\x08'
         self.assertEqual(yubico_util.hexdump(bytes, length=4, colorize=True), \
                              '0000   \x1b[0m01 02 03\x1b[0m 04\n0004   \x1b[0m05 06 07\x1b[0m 08\n')
 
     def test_modhex_decode(self):
         """ Test modhex decoding """
-        self.assertEqual("0123456789abcdef", yubico_util.modhex_decode("cbdefghijklnrtuv"))
+        self.assertEqual(b"0123456789abcdef", yubico_util.modhex_decode(b"cbdefghijklnrtuv"))
 
 if __name__ == '__main__':
     unittest.main()
diff --git a/test/test_yubikey_config.py b/test/test_yubikey_config.py
index 5fc694c..50067d7 100644
--- a/test/test_yubikey_config.py
+++ b/test/test_yubikey_config.py
@@ -25,24 +25,24 @@ def test_static_ticket(self):
         #ticket_flags: APPEND_CR
         #config_flags: STATIC_TICKET
 
-        expected = ['\x00\x00\x00\x00\x00\x00\x00\x80',
-                    '\x00\xe2\xbe\xe9\xa3\x65\x68\x83',
-                    '\xa0\x0d\x02\x6a\x02\xf8\x5e\x84',
-                    '\x61\xe6\xfb\x00\x00\x00\x00\x85',
-                    '\x00\x00\x00\x00\x20\x20\x00\x86',
-                    '\x00\x5a\x93\x00\x00\x00\x00\x87',
-                    '\x00\x01\x95\x56\x00\x00\x00\x89'
+        expected = [b'\x00\x00\x00\x00\x00\x00\x00\x80',
+                    b'\x00\xe2\xbe\xe9\xa3\x65\x68\x83',
+                    b'\xa0\x0d\x02\x6a\x02\xf8\x5e\x84',
+                    b'\x61\xe6\xfb\x00\x00\x00\x00\x85',
+                    b'\x00\x00\x00\x00\x20\x20\x00\x86',
+                    b'\x00\x5a\x93\x00\x00\x00\x00\x87',
+                    b'\x00\x01\x95\x56\x00\x00\x00\x89'
                     ]
 
         Config = self.Config
-        Config.aes_key('h:e2bee9a36568a00d026a02f85e61e6fb')
+        Config.aes_key(b'h:e2bee9a36568a00d026a02f85e61e6fb')
         Config.ticket_flag('APPEND_CR', True)
         Config.config_flag('STATIC_TICKET', True)
 
         data = Config.to_frame(slot=1).to_feature_reports()
 
-        print("EXPECT:\n%s\nGOT:\n%s\n" %( yubico.yubico_util.hexdump(''.join(expected)),
-                                           yubico.yubico_util.hexdump(''.join(data))))
+        print("EXPECT:\n%s\nGOT:\n%s\n" %( yubico.yubico_util.hexdump(b''.join(expected)),
+                                           yubico.yubico_util.hexdump(b''.join(data))))
 
         self.assertEqual(data, expected)
 
@@ -57,26 +57,26 @@ def test_static_ticket_with_access_code(self):
         #ticket_flags: APPEND_CR
         #config_flags: STATIC_TICKET
 
-        expected = ['\x00\x00\x00\x00\x00\x00\x00\x80',
-                    '\x00\xe2\xbe\xe9\xa3\x65\x68\x83',
-                    '\xa0\x0d\x02\x6a\x02\xf8\x5e\x84',
-                    '\x61\xe6\xfb\x01\x02\x03\x04\x85',
-                    '\x05\x06\x00\x00\x20\x20\x00\x86',
-                    '\x00\x0d\x39\x01\x02\x03\x04\x87',
-                    '\x05\x06\x00\x00\x00\x00\x00\x88',
-                    '\x00\x01\xc2\xfc\x00\x00\x00\x89',
+        expected = [b'\x00\x00\x00\x00\x00\x00\x00\x80',
+                    b'\x00\xe2\xbe\xe9\xa3\x65\x68\x83',
+                    b'\xa0\x0d\x02\x6a\x02\xf8\x5e\x84',
+                    b'\x61\xe6\xfb\x01\x02\x03\x04\x85',
+                    b'\x05\x06\x00\x00\x20\x20\x00\x86',
+                    b'\x00\x0d\x39\x01\x02\x03\x04\x87',
+                    b'\x05\x06\x00\x00\x00\x00\x00\x88',
+                    b'\x00\x01\xc2\xfc\x00\x00\x00\x89',
                     ]
 
         Config = self.Config
-        Config.aes_key('h:e2bee9a36568a00d026a02f85e61e6fb')
+        Config.aes_key(b'h:e2bee9a36568a00d026a02f85e61e6fb')
         Config.ticket_flag('APPEND_CR', True)
         Config.config_flag('STATIC_TICKET', True)
-        Config.unlock_key('h:010203040506')
+        Config.unlock_key(b'h:010203040506')
 
         data = Config.to_frame(slot=1).to_feature_reports()
 
-        print("EXPECT:\n%s\nGOT:\n%s\n" %( yubico.yubico_util.hexdump(''.join(expected)),
-                                           yubico.yubico_util.hexdump(''.join(data))))
+        print("EXPECT:\n%s\nGOT:\n%s\n" %( yubico.yubico_util.hexdump(b''.join(expected)),
+                                           yubico.yubico_util.hexdump(b''.join(data))))
 
         self.assertEqual(data, expected)
 
@@ -90,56 +90,56 @@ def test_fixed_and_oath_hotp(self):
         #ticket_flags: APPEND_CR|OATH_HOTP
         #config_flags: OATH_FIXED_MODHEX1|OATH_FIXED_MODHEX2|STATIC_TICKET
 
-        expected = ['\x4d\x4d\x4d\x4d\x00\x00\x00\x80',
-                    '\x00\x52\x3d\x7c\xe7\xe7\xb6\x83',
-                    '\xee\x85\x35\x17\xa3\xe3\xcc\x84',
-                    '\x19\x85\xc7\x00\x00\x00\x00\x85',
-                    '\x00\x00\x04\x00\x60\x70\x00\x86',
-                    '\x00\x72\xad\xaa\xbb\xcc\xdd\x87',
-                    '\xee\xff\x00\x00\x00\x00\x00\x88',
-                    '\x00\x03\xfe\xc4\x00\x00\x00\x89',
+        expected = [b'\x4d\x4d\x4d\x4d\x00\x00\x00\x80',
+                    b'\x00\x52\x3d\x7c\xe7\xe7\xb6\x83',
+                    b'\xee\x85\x35\x17\xa3\xe3\xcc\x84',
+                    b'\x19\x85\xc7\x00\x00\x00\x00\x85',
+                    b'\x00\x00\x04\x00\x60\x70\x00\x86',
+                    b'\x00\x72\xad\xaa\xbb\xcc\xdd\x87',
+                    b'\xee\xff\x00\x00\x00\x00\x00\x88',
+                    b'\x00\x03\xfe\xc4\x00\x00\x00\x89',
                     ]
 
         Config = self.Config
-        Config.aes_key('h:523d7ce7e7b6ee853517a3e3cc1985c7')
-        Config.fixed_string('m:ftftftft')
+        Config.aes_key(b'h:523d7ce7e7b6ee853517a3e3cc1985c7')
+        Config.fixed_string(b'm:ftftftft')
         Config.ticket_flag('APPEND_CR', True)
         Config.ticket_flag('OATH_HOTP', True)
         Config.config_flag('OATH_FIXED_MODHEX1', True)
         Config.config_flag('OATH_FIXED_MODHEX2', True)
         Config.config_flag('STATIC_TICKET', True)
-        Config.unlock_key('h:aabbccddeeff')
-        Config.access_key('h:000000000000')
+        Config.unlock_key(b'h:aabbccddeeff')
+        Config.access_key(b'h:000000000000')
 
         data = Config.to_frame(slot=2).to_feature_reports()
 
-        print("EXPECT:\n%s\nGOT:\n%s\n" %( yubico.yubico_util.hexdump(''.join(expected)),
-                                           yubico.yubico_util.hexdump(''.join(data))))
+        print("EXPECT:\n%s\nGOT:\n%s\n" %( yubico.yubico_util.hexdump(b''.join(expected)),
+                                           yubico.yubico_util.hexdump(b''.join(data))))
 
         self.assertEqual(data, expected)
 
     def test_challenge_response_hmac_nist(self):
         """ Test HMAC challenge response with NIST test vector """
 
-        expected = ['\x00\x00\x00\x00\x00\x00\x00\x80',
-                    '\x00\x00\x40\x41\x42\x43\x00\x82',
-                    '\x00\x30\x31\x32\x33\x34\x35\x83',
-                    '\x36\x37\x38\x39\x3a\x3b\x3c\x84',
-                    '\x3d\x3e\x3f\x00\x00\x00\x00\x85',
-                    '\x00\x00\x00\x04\x40\x26\x00\x86',
-                    '\x00\x98\x41\x00\x00\x00\x00\x87',
-                    '\x00\x03\x95\x56\x00\x00\x00\x89',
+        expected = [b'\x00\x00\x00\x00\x00\x00\x00\x80',
+                    b'\x00\x00\x40\x41\x42\x43\x00\x82',
+                    b'\x00\x30\x31\x32\x33\x34\x35\x83',
+                    b'\x36\x37\x38\x39\x3a\x3b\x3c\x84',
+                    b'\x3d\x3e\x3f\x00\x00\x00\x00\x85',
+                    b'\x00\x00\x00\x04\x40\x26\x00\x86',
+                    b'\x00\x98\x41\x00\x00\x00\x00\x87',
+                    b'\x00\x03\x95\x56\x00\x00\x00\x89',
                     ]
 
         Config = self.Config
-        secret = 'h:303132333435363738393a3b3c3d3e3f40414243'
+        secret = b'h:303132333435363738393a3b3c3d3e3f40414243'
         Config.mode_challenge_response(secret, type='HMAC', variable=True)
         Config.extended_flag('SERIAL_API_VISIBLE', True)
 
         data = Config.to_frame(slot=2).to_feature_reports()
 
-        print("EXPECT:\n%s\nGOT:\n%s\n" %( yubico.yubico_util.hexdump(''.join(expected)),
-                                           yubico.yubico_util.hexdump(''.join(data))))
+        print("EXPECT:\n%s\nGOT:\n%s\n" %( yubico.yubico_util.hexdump(b''.join(expected)),
+                                           yubico.yubico_util.hexdump(b''.join(data))))
 
         self.assertEqual(data, expected)
 
@@ -170,98 +170,98 @@ def test_default_flags(self):
     def test_oath_hotp_like_windows(self):
         """ Test plain OATH-HOTP with NIST test vector """
 
-        expected = ['\x00\x00\x00\x00\x00\x00\x00\x80',
-                    '\x00\x00\x40\x41\x42\x43\x00\x82',
-                    '\x00\x30\x31\x32\x33\x34\x35\x83',
-                    '\x36\x37\x38\x39\x3a\x3b\x3c\x84',
-                    '\x3d\x3e\x3f\x00\x00\x00\x00\x85',
-                    '\x00\x00\x00\x00\x40\x00\x00\x86',
-                    '\x00\x6a\xb9\x00\x00\x00\x00\x87',
-                    '\x00\x03\x95\x56\x00\x00\x00\x89',
+        expected = [b'\x00\x00\x00\x00\x00\x00\x00\x80',
+                    b'\x00\x00\x40\x41\x42\x43\x00\x82',
+                    b'\x00\x30\x31\x32\x33\x34\x35\x83',
+                    b'\x36\x37\x38\x39\x3a\x3b\x3c\x84',
+                    b'\x3d\x3e\x3f\x00\x00\x00\x00\x85',
+                    b'\x00\x00\x00\x00\x40\x00\x00\x86',
+                    b'\x00\x6a\xb9\x00\x00\x00\x00\x87',
+                    b'\x00\x03\x95\x56\x00\x00\x00\x89',
                     ]
 
         Config = self.Config
-        secret = 'h:303132333435363738393a3b3c3d3e3f40414243'
+        secret = b'h:303132333435363738393a3b3c3d3e3f40414243'
         Config.mode_oath_hotp(secret)
 
         data = Config.to_frame(slot=2).to_feature_reports()
 
-        print("EXPECT:\n%s\nGOT:\n%s\n" %( yubico.yubico_util.hexdump(''.join(expected)),
-                                           yubico.yubico_util.hexdump(''.join(data))))
+        print("EXPECT:\n%s\nGOT:\n%s\n" %( yubico.yubico_util.hexdump(b''.join(expected)),
+                                           yubico.yubico_util.hexdump(b''.join(data))))
 
         self.assertEqual(data, expected)
 
     def test_oath_hotp_like_windows2(self):
         """ Test OATH-HOTP with NIST test vector and token identifier """
 
-        expected = ['\x01\x02\x03\x04\x05\x06\x00\x80',
-                    '\x00\x00\x40\x41\x42\x43\x00\x82',
-                    '\x00\x30\x31\x32\x33\x34\x35\x83',
-                    '\x36\x37\x38\x39\x3a\x3b\x3c\x84',
-                    '\x3d\x3e\x3f\x00\x00\x00\x00\x85',
-                    '\x00\x00\x06\x00\x40\x42\x00\x86',
-                    '\x00\x0e\xec\x00\x00\x00\x00\x87',
-                    '\x00\x03\x95\x56\x00\x00\x00\x89',
+        expected = [b'\x01\x02\x03\x04\x05\x06\x00\x80',
+                    b'\x00\x00\x40\x41\x42\x43\x00\x82',
+                    b'\x00\x30\x31\x32\x33\x34\x35\x83',
+                    b'\x36\x37\x38\x39\x3a\x3b\x3c\x84',
+                    b'\x3d\x3e\x3f\x00\x00\x00\x00\x85',
+                    b'\x00\x00\x06\x00\x40\x42\x00\x86',
+                    b'\x00\x0e\xec\x00\x00\x00\x00\x87',
+                    b'\x00\x03\x95\x56\x00\x00\x00\x89',
                     ]
 
         Config = self.Config
-        secret = 'h:303132333435363738393a3b3c3d3e3f40414243'
-        Config.mode_oath_hotp(secret, digits=8, factor_seed='', omp=0x01, tt=0x02, mui='\x03\x04\x05\x06')
+        secret = b'h:303132333435363738393a3b3c3d3e3f40414243'
+        Config.mode_oath_hotp(secret, digits=8, factor_seed='', omp=0x01, tt=0x02, mui=b'\x03\x04\x05\x06')
         Config.config_flag('OATH_FIXED_MODHEX2', True)
 
         data = Config.to_frame(slot=2).to_feature_reports()
 
-        print("EXPECT:\n%s\nGOT:\n%s\n" %( yubico.yubico_util.hexdump(''.join(expected)),
-                                           yubico.yubico_util.hexdump(''.join(data))))
+        print("EXPECT:\n%s\nGOT:\n%s\n" %( yubico.yubico_util.hexdump(b''.join(expected)),
+                                           yubico.yubico_util.hexdump(b''.join(data))))
 
         self.assertEqual(data, expected)
 
     def test_oath_hotp_like_windows_factory_seed(self):
         """ Test OATH-HOTP factor_seed """
 
-        expected = ['\x01\x02\x03\x04\x05\x06\x00\x80',
-                    '\x00\x00\x40\x41\x42\x43\x01\x82',
-                    '\x21\x30\x31\x32\x33\x34\x35\x83',
-                    '\x36\x37\x38\x39\x3a\x3b\x3c\x84',
-                    '\x3d\x3e\x3f\x00\x00\x00\x00\x85',
-                    '\x00\x00\x06\x00\x40\x42\x00\x86',
-                    '\x00\x03\xea\x00\x00\x00\x00\x87',
-                    '\x00\x03\x95\x56\x00\x00\x00\x89',
+        expected = [b'\x01\x02\x03\x04\x05\x06\x00\x80',
+                    b'\x00\x00\x40\x41\x42\x43\x01\x82',
+                    b'\x21\x30\x31\x32\x33\x34\x35\x83',
+                    b'\x36\x37\x38\x39\x3a\x3b\x3c\x84',
+                    b'\x3d\x3e\x3f\x00\x00\x00\x00\x85',
+                    b'\x00\x00\x06\x00\x40\x42\x00\x86',
+                    b'\x00\x03\xea\x00\x00\x00\x00\x87',
+                    b'\x00\x03\x95\x56\x00\x00\x00\x89',
                     ]
 
         Config = self.Config
-        secret = 'h:303132333435363738393a3b3c3d3e3f40414243'
-        Config.mode_oath_hotp(secret, digits=8, factor_seed=0x2101, omp=0x01, tt=0x02, mui='\x03\x04\x05\x06')
+        secret = b'h:303132333435363738393a3b3c3d3e3f40414243'
+        Config.mode_oath_hotp(secret, digits=8, factor_seed=0x2101, omp=0x01, tt=0x02, mui=b'\x03\x04\x05\x06')
         Config.config_flag('OATH_FIXED_MODHEX2', True)
 
         data = Config.to_frame(slot=2).to_feature_reports()
 
-        print("EXPECT:\n%s\nGOT:\n%s\n" %( yubico.yubico_util.hexdump(''.join(expected)),
-                                           yubico.yubico_util.hexdump(''.join(data))))
+        print("EXPECT:\n%s\nGOT:\n%s\n" %( yubico.yubico_util.hexdump(b''.join(expected)),
+                                           yubico.yubico_util.hexdump(b''.join(data))))
 
         self.assertEqual(data, expected)
 
     def test_fixed_length_hmac_like_windows(self):
         """ Test fixed length HMAC SHA1 """
 
-        expected = ['\x00\x00\x00\x00\x00\x00\x00\x80',
-                    '\x00\x00\x40\x41\x42\x43\x00\x82',
-                    '\x00\x30\x31\x32\x33\x34\x35\x83',
-                    '\x36\x37\x38\x39\x3a\x3b\x3c\x84',
-                    '\x3d\x3e\x3f\x00\x00\x00\x00\x85',
-                    '\x00\x00\x00\x00\x40\x22\x00\x86',
-                    '\x00\xe9\x0f\x00\x00\x00\x00\x87',
-                    '\x00\x03\x95\x56\x00\x00\x00\x89',
+        expected = [b'\x00\x00\x00\x00\x00\x00\x00\x80',
+                    b'\x00\x00\x40\x41\x42\x43\x00\x82',
+                    b'\x00\x30\x31\x32\x33\x34\x35\x83',
+                    b'\x36\x37\x38\x39\x3a\x3b\x3c\x84',
+                    b'\x3d\x3e\x3f\x00\x00\x00\x00\x85',
+                    b'\x00\x00\x00\x00\x40\x22\x00\x86',
+                    b'\x00\xe9\x0f\x00\x00\x00\x00\x87',
+                    b'\x00\x03\x95\x56\x00\x00\x00\x89',
                     ]
 
         Config = self.Config
-        secret = 'h:303132333435363738393a3b3c3d3e3f40414243'
+        secret = b'h:303132333435363738393a3b3c3d3e3f40414243'
         Config.mode_challenge_response(secret, type='HMAC', variable=False)
 
         data = Config.to_frame(slot=2).to_feature_reports()
 
-        print("EXPECT:\n%s\nGOT:\n%s\n" %( yubico.yubico_util.hexdump(''.join(expected)),
-                                           yubico.yubico_util.hexdump(''.join(data))))
+        print("EXPECT:\n%s\nGOT:\n%s\n" %( yubico.yubico_util.hexdump(b''.join(expected)),
+                                           yubico.yubico_util.hexdump(b''.join(data))))
 
         self.assertEqual(data, expected)
 
@@ -292,14 +292,14 @@ def test_version_required_4(self):
         capa = yubico.yubikey_usb_hid.YubiKeyUSBHIDCapabilities( \
             model = 'YubiKey', version = version, default_answer = False)
         Config = YubiKeyConfigUSBHID(ykver = version, capabilities = capa)
-        secret = 'h:303132333435363738393a3b3c3d3e3f40414243'
+        secret = b'h:303132333435363738393a3b3c3d3e3f40414243'
         self.assertRaises(yubico.yubikey.YubiKeyVersionError, Config.mode_challenge_response, secret)
 
     def test_version_required_5(self):
         """ Test YubiKey 2.2 with v2.2 mode """
 
         Config = self.Config
-        secret = 'h:303132333435363738393a3b3c3d3e3f'
+        secret = b'h:303132333435363738393a3b3c3d3e3f'
         Config.mode_challenge_response(secret, type='OTP')
         self.assertEqual('CHAL_RESP', Config._mode)
 
diff --git a/test/test_yubikey_frame.py b/test/test_yubikey_frame.py
index 5437922..e1308c4 100644
--- a/test/test_yubikey_frame.py
+++ b/test/test_yubikey_frame.py
@@ -17,7 +17,7 @@ def test_get_ykframe(self):
     self.assertEqual(len(buffer), 70, "yubikey command buffer should always be 70 bytes")
 
     # check that empty payload works (64 * '\x00')
-    all_zeros = '\x00' * 64
+    all_zeros = b'\x00' * 64
 
     self.assertTrue(buffer.startswith(all_zeros))
 
@@ -26,25 +26,25 @@ def test_get_ykframe_feature_reports(self):
     """ Test normal use """
     res = YubiKeyFrame(command=0x32).to_feature_reports()
 
-    self.assertEqual(res, ['\x00\x00\x00\x00\x00\x00\x00\x80',
-                           '\x00\x32\x6b\x5b\x00\x00\x00\x89'
+    self.assertEqual(res, [b'\x00\x00\x00\x00\x00\x00\x00\x80',
+                           b'\x00\x32\x6b\x5b\x00\x00\x00\x89'
                            ])
 
 
   def test_get_ykframe_feature_reports2(self):
     """ Test one serie of non-zero bytes in the middle of the payload """
-    payload = '\x00' * 38
-    payload += '\x01\x02\x03'
-    payload += '\x00' * 23
+    payload = b'\x00' * 38
+    payload += b'\x01\x02\x03'
+    payload += b'\x00' * 23
     res = YubiKeyFrame(command=0x32, payload=payload).to_feature_reports()
 
-    self.assertEqual(res, ['\x00\x00\x00\x00\x00\x00\x00\x80',
-                           '\x00\x00\x00\x01\x02\x03\x00\x85',
-                           '\x002\x01s\x00\x00\x00\x89'])
+    self.assertEqual(res, [b'\x00\x00\x00\x00\x00\x00\x00\x80',
+                           b'\x00\x00\x00\x01\x02\x03\x00\x85',
+                           b'\x002\x01s\x00\x00\x00\x89'])
 
   def test_bad_payload(self):
     """ Test that we get an exception for four bytes payload """
-    self.assertRaises(yubico_exception.InputError, YubiKeyFrame, command=0x32, payload='test')
+    self.assertRaises(yubico_exception.InputError, YubiKeyFrame, command=0x32, payload=b'test')
 
   def test_repr(self):
     """ Test string representation of object """
diff --git a/test/test_yubikey_usb_hid.py b/test/test_yubikey_usb_hid.py
index f801a33..c7e105c 100644
--- a/test/test_yubikey_usb_hid.py
+++ b/test/test_yubikey_usb_hid.py
@@ -41,9 +41,9 @@ def test_status(self):
     def test_challenge_response(self):
         """ Test challenge-response, assumes a NIST PUB 198 A.2 20 bytes test vector in Slot 2 (variable input) """
 
-        secret = struct.pack('64s', 'Sample #2')
+        secret = struct.pack('64s', b'Sample #2')
         response = self.YK.challenge_response(secret, mode='HMAC', slot=2)
-        self.assertEqual(response, '\x09\x22\xd3\x40\x5f\xaa\x3d\x19\x4f\x82\xa4\x58\x30\x73\x7d\x5c\xc6\xc7\x5d\x24')
+        self.assertEqual(response, b'\x09\x22\xd3\x40\x5f\xaa\x3d\x19\x4f\x82\xa4\x58\x30\x73\x7d\x5c\xc6\xc7\x5d\x24')
 
     #@unittest.skipIf(self.YK is None, "No USB HID YubiKey found")
     def test_serial(self):
diff --git a/yubico/yubico_util.py b/yubico/yubico_util.py
index 5af3956..38056e6 100644
--- a/yubico/yubico_util.py
+++ b/yubico/yubico_util.py
@@ -15,20 +15,37 @@
     # classes
 ]
 
+import sys
+import string
+
 from .yubico_version import __version__
 from . import yubikey_defs
 from . import yubico_exception
-import string
 
 _CRC_OK_RESIDUAL = 0xf0b8
 
+def ord_byte(byte):
+    """Convert a byte to its integer value"""
+    if sys.version_info < (3, 0):
+        return ord(byte)
+    else:
+        # In Python 3, single bytes are represented as integers
+        return int(byte)
+
+def chr_byte(number):
+    """Convert an integer value to a length-1 bytestring"""
+    if sys.version_info < (3, 0):
+        return chr(number)
+    else:
+        return bytes([number])
+
 def crc16(data):
     """
-    Calculate an ISO13239 CRC checksum of the input buffer.
+    Calculate an ISO13239 CRC checksum of the input buffer (bytestring).
     """
     m_crc = 0xffff
     for this in data:
-        m_crc ^= ord(this)
+        m_crc ^= ord_byte(this)
         for _ in range(8):
             j = m_crc & 1
             m_crc >>= 1
@@ -39,6 +56,8 @@ def crc16(data):
 def validate_crc16(data):
     """
     Validate that the CRC of the contents of buffer is the residual OK value.
+
+    The input is a bytestring.
     """
     return crc16(data) == _CRC_OK_RESIDUAL
 
@@ -74,27 +93,30 @@ def disable(self):
         self.enabled = False
 
 def hexdump(src, length=8, colorize=False):
-    """ Produce a string hexdump of src, for debug output."""
+    """ Produce a string hexdump of src, for debug output.
+
+    Input: bytestring; output: text string
+    """
     if not src:
         return str(src)
-    if type(src) is not str:
-        raise yubico_exception.InputError('Hexdump \'src\' must be string (got %s)' % type(src))
+    if type(src) is not bytes:
+        raise yubico_exception.InputError('Hexdump \'src\' must be bytestring (got %s)' % type(src))
     offset = 0
     result = ''
     for this in group(src, length):
         if colorize:
-            last, this = this[-1:], this[:-1]
+            last, this = this[-1], this[:-1]
             colors = DumpColors()
             color = colors.get('RESET')
-            if ord(last) & yubikey_defs.RESP_PENDING_FLAG:
+            if ord_byte(last) & yubikey_defs.RESP_PENDING_FLAG:
                 # write to key
                 color = colors.get('BLUE')
-            elif ord(last) & yubikey_defs.SLOT_WRITE_FLAG:
+            elif ord_byte(last) & yubikey_defs.SLOT_WRITE_FLAG:
                 color = colors.get('GREEN')
-            hex_s = color + ' '.join(["%02x" % ord(x) for x in this]) + colors.get('RESET')
-            hex_s += " %02x" % ord(last)
+            hex_s = color + ' '.join(["%02x" % ord_byte(x) for x in this]) + colors.get('RESET')
+            hex_s += " %02x" % ord_byte(last)
         else:
-            hex_s = ' '.join(["%02x" % ord(x) for x in this])
+            hex_s = ' '.join(["%02x" % ord_byte(x) for x in this])
         result += "%04X   %s\n" % (offset, hex_s)
         offset += length
     return result
@@ -104,17 +126,25 @@ def group(data, num):
     return [data[i:i+num] for i in range(0, len(data), num)]
 
 def modhex_decode(data):
-    """ Convert a modhex string to ordinary hex. """
-    t_map = string.maketrans("cbdefghijklnrtuv", "0123456789abcdef")
+    """ Convert a modhex bytestring to ordinary hex. """
+    try:
+        maketrans = string.maketrans
+    except AttributeError:
+        # Python 3
+        maketrans = bytes.maketrans
+    t_map = maketrans(b"cbdefghijklnrtuv", b"0123456789abcdef")
     return data.translate(t_map)
 
 def hotp_truncate(hmac_result, length=6):
-    """ Perform the HOTP Algorithm truncating. """
+    """ Perform the HOTP Algorithm truncating.
+
+    Input is a bytestring.
+    """
     if len(hmac_result) != 20:
         raise yubico_exception.YubicoError("HMAC-SHA-1 not 20 bytes long")
-    offset   =  ord(hmac_result[19]) & 0xf
-    bin_code = (ord(hmac_result[offset]) & 0x7f) << 24 \
-        | (ord(hmac_result[offset+1]) & 0xff) << 16 \
-        | (ord(hmac_result[offset+2]) & 0xff) <<  8 \
-        | (ord(hmac_result[offset+3]) & 0xff)
+    offset   =  ord_byte(hmac_result[19]) & 0xf
+    bin_code = (ord_byte(hmac_result[offset]) & 0x7f) << 24 \
+        | (ord_byte(hmac_result[offset+1]) & 0xff) << 16 \
+        | (ord_byte(hmac_result[offset+2]) & 0xff) <<  8 \
+        | (ord_byte(hmac_result[offset+3]) & 0xff)
     return bin_code % (10 ** length)
diff --git a/yubico/yubikey_config.py b/yubico/yubikey_config.py
index 3a02114..430ec7a 100644
--- a/yubico/yubikey_config.py
+++ b/yubico/yubikey_config.py
@@ -17,6 +17,7 @@
 
 from .yubico_version import __version__
 
+import sys
 import struct
 import binascii
 from . import yubico_util
@@ -118,16 +119,16 @@ def __init__(self, ykver=None, capabilities=None, update=False, swap=False):
         self.yk_req_version = (0, 0)
         self.ykver = ykver
 
-        self.fixed = ''
-        self.uid = ''
-        self.key = ''
-        self.access_code = ''
+        self.fixed = b''
+        self.uid = b''
+        self.key = b''
+        self.access_code = b''
 
         self.ticket_flags = YubiKeyConfigBits(0x0)
         self.config_flags = YubiKeyConfigBits(0x0)
         self.extended_flags = YubiKeyConfigBits(0x0)
 
-        self.unlock_code = ''
+        self.unlock_code = b''
         self._mode = ''
         if update or swap:
             self._require_version(major=2, minor=3)
@@ -233,10 +234,10 @@ def unlock_key(self, data):
         """
         Access code to allow re-programming of your YubiKey.
 
-        Supply data as either a raw string, or a hexlified string prefixed by 'h:'.
+        Supply data as either a raw bytestring, or a hexlified bytestring prefixed by 'h:'.
         The result, after any hex decoding, must be 6 bytes.
         """
-        if data.startswith('h:'):
+        if data.startswith(b'h:'):
             new = binascii.unhexlify(data[2:])
         else:
             new = data
@@ -256,7 +257,7 @@ def access_key(self, data):
         Supply data as either a raw string, or a hexlified string prefixed by 'h:'.
         The result, after any hex decoding, must be 6 bytes.
         """
-        if data.startswith('h:'):
+        if data.startswith(b'h:'):
             new = binascii.unhexlify(data[2:])
         else:
             new = data
@@ -272,7 +273,7 @@ def mode_yubikey_otp(self, private_uid, aes_key):
         if not self.capabilities.have_yubico_OTP():
             raise yubikey_base.YubiKeyVersionError('Yubico OTP not available in %s version %d.%d' \
                                                    % (self.capabilities.model, self.ykver[0], self.ykver[1]))
-        if private_uid.startswith('h:'):
+        if private_uid.startswith(b'h:'):
             private_uid = binascii.unhexlify(private_uid[2:])
         if len(private_uid) != yubikey_defs.UID_SIZE:
             raise yubico_exception.InputError('Private UID must be %i bytes' % (yubikey_defs.UID_SIZE))
@@ -299,7 +300,7 @@ def mode_oath_hotp(self, secret, digits=6, factor_seed=None, omp=0x0, tt=0x0, mu
             self.config_flag('OATH_HOTP8', True)
         if omp or tt or mui:
             decoded_mui = self._decode_input_string(mui)
-            fixed = chr(omp) + chr(tt) + decoded_mui
+            fixed = yubico_util.chr_byte(omp) + yubico_util.chr_byte(tt) + decoded_mui
             self.fixed_string(fixed)
         if factor_seed:
             self.uid = self.uid + struct.pack('<H', factor_seed)
@@ -405,7 +406,7 @@ def extended_flag(self, which, new=None):
 
     def to_string(self):
         """
-        Return the current configuration as a string (always 64 bytes).
+        Return the current configuration as a bytestring (always 64 bytes).
         """
         #define UID_SIZE		6	/* Size of secret ID field */
         #define FIXED_SIZE              16      /* Max size of fixed field */
@@ -449,7 +450,7 @@ def to_frame(self, slot=1):
         Return the current configuration as a YubiKeyFrame object.
         """
         data = self.to_string()
-        payload = data.ljust(64, chr(0x0))
+        payload = data.ljust(64, yubico_util.chr_byte(0x0))
         if slot is 1:
             if self._update_config:
                 command = SLOT_UPDATE1
@@ -478,9 +479,11 @@ def _require_version(self, major, minor=0):
             self.yk_req_version = new_ver
 
     def _decode_input_string(self, data):
-        if data.startswith('m:'):
-            data = 'h:' + yubico_util.modhex_decode(data[2:])
-        if data.startswith('h:'):
+        if sys.version_info >= (3, 0) and isinstance(data, str):
+            data = data.encode('ascii')
+        if data.startswith(b'm:'):
+            data = b'h:' + yubico_util.modhex_decode(data[2:])
+        if data.startswith(b'h:'):
             return(binascii.unhexlify(data[2:]))
         else:
             return(data)
@@ -503,10 +506,10 @@ def _set_20_bytes_key(self, data):
         """
         Set a 20 bytes key. This is used in CHAL_HMAC and OATH_HOTP mode.
 
-        Supply data as either a raw string, or a hexlified string prefixed by 'h:'.
+        Supply data as either a raw bytestring, or a hexlified bytestring prefixed by 'h:'.
         The result, after any hex decoding, must be 20 bytes.
         """
-        if data.startswith('h:'):
+        if data.startswith(b'h:'):
             new = binascii.unhexlify(data[2:])
         else:
             new = data
diff --git a/yubico/yubikey_frame.py b/yubico/yubikey_frame.py
index 80919c9..c43c6a1 100644
--- a/yubico/yubikey_frame.py
+++ b/yubico/yubikey_frame.py
@@ -28,11 +28,13 @@ class YubiKeyFrame:
     flags.
     """
 
-    def __init__(self, command, payload=''):
-        if payload is '':
-            payload = '\x00' * 64
+    def __init__(self, command, payload=b''):
+        if not payload:
+            payload = b'\x00' * 64
         if len(payload) != 64:
             raise yubico_exception.InputError('payload must be empty or 64 bytes')
+        if not isinstance(payload, bytes):
+            raise yubico_exception.InputError('payload must be a bytestring')
         self.payload = payload
         self.command = command
         self.crc = yubico_util.crc16(payload)
@@ -59,7 +61,7 @@ def to_string(self):
         #     unsigned short crc;
         #     unsigned char filler[3];
         # } YKFRAME;
-        filler = ''
+        filler = b''
         return struct.pack('<64sBH3s',
                            self.payload, self.command, self.crc, filler)
 
@@ -77,11 +79,11 @@ def to_feature_reports(self, debug=False):
             this, rest = rest[:7], rest[7:]
             if seq > 0 and rest:
                 # never skip first or last serie
-                if this != '\x00\x00\x00\x00\x00\x00\x00':
-                    this += chr(yubikey_defs.SLOT_WRITE_FLAG + seq)
+                if this != b'\x00\x00\x00\x00\x00\x00\x00':
+                    this += yubico_util.chr_byte(yubikey_defs.SLOT_WRITE_FLAG + seq)
                     out.append(self._debug_string(debug, this))
             else:
-                this += chr(yubikey_defs.SLOT_WRITE_FLAG + seq)
+                this += yubico_util.chr_byte(yubikey_defs.SLOT_WRITE_FLAG + seq)
                 out.append(self._debug_string(debug, this))
             seq += 1
         return out
diff --git a/yubico/yubikey_neo_usb_hid.py b/yubico/yubikey_neo_usb_hid.py
index 0543b8e..88fceba 100644
--- a/yubico/yubikey_neo_usb_hid.py
+++ b/yubico/yubikey_neo_usb_hid.py
@@ -20,6 +20,7 @@
 from . import yubikey_usb_hid
 from . import yubikey_frame
 from . import yubico_exception
+from . import yubico_util
 
 # commands from ykdef.h
 _SLOT_NDEF		= 0x08 # Write YubiKey NEO NDEF
@@ -127,11 +128,11 @@ class YubiKeyNEO_NDEF():
 
     ndef_type = _NDEF_URI_TYPE
     ndef_str = None
-    access_code = chr(0x0) * _ACC_CODE_SIZE
+    access_code = yubico_util.chr_byte(0x0) * _ACC_CODE_SIZE
     # For _NDEF_URI_TYPE
     ndef_uri_rt = 0x0  # No prepending
     # For _NDEF_TEXT_TYPE
-    ndef_text_lang = 'en'
+    ndef_text_lang = b'en'
     ndef_text_enc = 'UTF-8'
 
     def __init__(self, data, access_code = None):
@@ -199,7 +200,7 @@ def to_frame(self, slot=_SLOT_NDEF):
         Return the current configuration as a YubiKeyFrame object.
         """
         data = self.to_string()
-        payload = data.ljust(64, chr(0x0))
+        payload = data.ljust(64, b'\0')
         return yubikey_frame.YubiKeyFrame(command = slot, payload = payload)
 
     def _encode_ndef_uri_type(self, data):
@@ -211,11 +212,11 @@ def _encode_ndef_uri_type(self, data):
         """
         t = 0x0
         for (code, prefix) in uri_identifiers:
-            if data[:len(prefix)].lower() == prefix:
+            if data[:len(prefix)].decode('latin-1').lower() == prefix:
                 t = code
                 data = data[len(prefix):]
                 break
-        data = chr(t) + data
+        data = yubico_util.chr_byte(t) + data
         return data
 
     def _encode_ndef_text_params(self, data):
@@ -226,4 +227,4 @@ def _encode_ndef_text_params(self, data):
         status = len(self.ndef_text_lang)
         if self.ndef_text_enc == 'UTF16':
             status = status & 0b10000000
-        return chr(status) + self.ndef_text_lang + data
+        return yubico_util.chr_byte(status) + self.ndef_text_lang + data
diff --git a/yubico/yubikey_usb_hid.py b/yubico/yubikey_usb_hid.py
index 3cf47a4..3a90701 100644
--- a/yubico/yubikey_usb_hid.py
+++ b/yubico/yubikey_usb_hid.py
@@ -248,16 +248,16 @@ def _challenge_response(self, challenge, mode, slot, variable, may_block):
                 raise yubico_exception.InputError('Mode HMAC challenge too big (%i/%i)' \
                                                       % (yubikey_defs.SHA1_MAX_BLOCK_SIZE, len(challenge)))
             if len(challenge) < yubikey_defs.SHA1_MAX_BLOCK_SIZE:
-                pad_with = chr(0x0)
-                if variable and challenge[-1] == pad_with:
-                    pad_with = chr(0xff)
+                pad_with = b'\0'
+                if variable and challenge[-1:] == pad_with:
+                    pad_with = b'\xff'
                 challenge = challenge.ljust(yubikey_defs.SHA1_MAX_BLOCK_SIZE, pad_with)
             response_len = yubikey_defs.SHA1_DIGEST_SIZE
         elif mode == 'OTP':
             if len(challenge) != yubikey_defs.UID_SIZE:
                 raise yubico_exception.InputError('Mode OTP challenge must be %i bytes (got %i)' \
                                                       % (yubikey_defs.UID_SIZE, len(challenge)))
-            challenge = challenge.ljust(yubikey_defs.SHA1_MAX_BLOCK_SIZE, chr(0x0))
+            challenge = challenge.ljust(yubikey_defs.SHA1_MAX_BLOCK_SIZE, b'\0')
             response_len = 16
         else:
             raise yubico_exception.InputError('Invalid mode supplied (%s, valid values are HMAC and OTP)' \
@@ -297,7 +297,7 @@ def _read_response(self, may_block=False):
         # continue reading while response pending is set
         while True:
             this = self._read()
-            flags = ord(this[7])
+            flags = yubico_util.ord_byte(this[7])
             if flags & yubikey_defs.RESP_PENDING_FLAG:
                 seq = flags & 0b00011111
                 if res and (seq == 0):
@@ -321,7 +321,7 @@ def _read(self):
             self._debug("Failed reading %i bytes (got %i) from USB HID YubiKey.\n"
                         % (_FEATURE_RPT_SIZE, recv))
             raise YubiKeyUSBHIDError('Failed reading from USB HID YubiKey')
-        data = ''.join(chr(c) for c in recv)
+        data = b''.join(yubico_util.chr_byte(c) for c in recv)
         self._debug("READ  : %s" % (yubico_util.hexdump(data, colorize=True)))
         return data
 
@@ -344,7 +344,7 @@ def _write_reset(self):
         """
         Reset read mode by issuing a dummy write.
         """
-        data = '\x00\x00\x00\x00\x00\x00\x00\x8f'
+        data = b'\x00\x00\x00\x00\x00\x00\x00\x8f'
         self._raw_write(data)
         self._waitfor_clear(yubikey_defs.SLOT_WRITE_FLAG)
         return True
@@ -401,7 +401,7 @@ def _waitfor(self, mode, mask, may_block, timeout=2):
         resp_timeout = False    # YubiKey hasn't indicated RESP_TIMEOUT (yet)
         while not finished:
             this = self._read()
-            flags = ord(this[7])
+            flags = yubico_util.ord_byte(this[7])
 
             if flags & yubikey_defs.RESP_TIMEOUT_WAIT_FLAG:
                 if not resp_timeout:

From adde4903eb9efa34b9b5fbd2bf988f934333fd11 Mon Sep 17 00:00:00 2001
From: Petr Viktorin <pviktori@redhat.com>
Date: Wed, 1 Jul 2015 20:02:38 +0200
Subject: [PATCH 6/6] Unshadow a hexdump test

The test_hexdump method was defined twice, the second overwriting
the first. Use a unique name to have them both run.
---
 test/test_yubico.py | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/test/test_yubico.py b/test/test_yubico.py
index cd87f42..de2e2a5 100644
--- a/test/test_yubico.py
+++ b/test/test_yubico.py
@@ -35,7 +35,7 @@ def test_hexdump(self):
         self.assertEqual(yubico_util.hexdump(bytes, length=4), \
                              '0000   01 02 03 04\n0004   05 06 07 08\n')
 
-    def test_hexdump(self):
+    def test_hexdump2(self):
         """ Test hexdump function, with colors """
         bytes = b'\x01\x02\x03\x04\x05\x06\x07\x08'
         self.assertEqual(yubico_util.hexdump(bytes, length=4, colorize=True), \