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), \