/usr/share/pyshared/yubico/yubikey.py is in python-yubico 1.1.0-2.
This file is owned by root:root, with mode 0o644.
The actual contents of the file can be viewed below.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 | """
module for accessing a YubiKey
In an attempt to support any future versions of the YubiKey which
might not be USB HID devices, you should always use the yubikey.find_key()
(or better yet, yubico.find_yubikey()) function to initialize
communication with YubiKeys.
Example usage (if using this module directly, see base module yubico) :
import yubico.yubikey
try:
YK = yubico.yubikey.find_key()
print "Version : %s " % YK.version()
except yubico.yubico_exception.YubicoError as inst:
print "ERROR: %s" % inst.reason
"""
# Copyright (c) 2010, 2011, 2012 Yubico AB
# See the file COPYING for licence statement.
__all__ = [
# constants
'RESP_TIMEOUT_WAIT_FLAG',
'RESP_PENDING_FLAG',
'SLOT_WRITE_FLAG',
# functions
'find_key',
# classes
'YubiKey',
'YubiKeyTimeout',
]
from yubico 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_usb_hid import YubiKeyUSBHID, YubiKeyUSBHIDError
from yubikey_neo_usb_hid import YubiKeyNEO_USBHID, YubiKeyNEO_USBHIDError
def find_key(debug=False, skip=0):
"""
Locate a connected YubiKey. Throws an exception if none is found.
This function is supposed to be possible to extend if any other YubiKeys
appear in the future.
Attributes :
skip -- number of YubiKeys to skip
debug -- True or False
"""
try:
YK = YubiKeyUSBHID(debug=debug, skip=skip)
if (YK.version_num() >= (2, 1, 4,)) and \
(YK.version_num() <= (2, 1, 9,)):
# YubiKey NEO BETA, re-detect
YK2 = YubiKeyNEO_USBHID(debug=debug, skip=skip)
if YK2.version_num() == YK.version_num():
# XXX not guaranteed to be the same one I guess
return YK2
raise YubiKeyError('Found YubiKey NEO BETA, but failed on rescan.')
return YK
except YubiKeyUSBHIDError as inst:
if 'No USB YubiKey found' in str(inst):
# generalize this error
raise YubiKeyError('No YubiKey found')
else:
raise
|