2116 lines
84 KiB
Diff
2116 lines
84 KiB
Diff
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), \
|