114 lines
3.5 KiB
Python
114 lines
3.5 KiB
Python
"""
|
|
module for accessing a USB HID YubiKey 4
|
|
"""
|
|
|
|
# Copyright (c) 2012 Yubico AB
|
|
# See the file COPYING for licence statement.
|
|
|
|
__all__ = [
|
|
# constants
|
|
# functions
|
|
# classes
|
|
'YubiKey4_USBHID',
|
|
'YubiKey4_USBHIDError'
|
|
]
|
|
|
|
from .yubikey_defs import SLOT, MODE, YK4_CAPA
|
|
from . import yubikey_frame
|
|
from . import yubikey_base
|
|
from . import yubico_exception
|
|
from . import yubico_util
|
|
from . import yubikey_neo_usb_hid
|
|
|
|
MODE_CAPABILITIES = { # Required capabilities to support USB mode.
|
|
MODE.OTP : [YK4_CAPA.OTP],
|
|
MODE.CCID : [YK4_CAPA.CCID],
|
|
MODE.OTP_CCID : [YK4_CAPA.OTP, YK4_CAPA.CCID],
|
|
MODE.U2F : [YK4_CAPA.U2F],
|
|
MODE.OTP_U2F : [YK4_CAPA.OTP, YK4_CAPA.U2F],
|
|
MODE.U2F_CCID : [YK4_CAPA.U2F, YK4_CAPA.CCID],
|
|
MODE.OTP_U2F_CCID : [YK4_CAPA.OTP, YK4_CAPA.U2F, YK4_CAPA.CCID]
|
|
}
|
|
|
|
|
|
class YubiKey4_USBHIDError(yubico_exception.YubicoError):
|
|
""" Exception raised for errors with the YK4 USB HID communication. """
|
|
|
|
|
|
class YubiKey4_USBHIDCapabilities(yubikey_neo_usb_hid.YubiKeyNEO_USBHIDCapabilities):
|
|
"""
|
|
Capabilities of current YubiKey 4.
|
|
"""
|
|
_yk4_capa = 0
|
|
|
|
def _set_yk4_capa(self, yk4_capa):
|
|
int_val = 0
|
|
for b in yk4_capa:
|
|
int_val <<= 8
|
|
int_val += yubico_util.ord_byte(b)
|
|
self._yk4_capa = int_val
|
|
|
|
def have_nfc_ndef(self, slot=1):
|
|
return False
|
|
|
|
def have_usb_mode(self, mode):
|
|
mode &= ~MODE.FLAG_EJECT # Mask away eject flag
|
|
if self.version < (4, 1, 0): # YK Plus is locked in OTP+U2F
|
|
return mode == MODE.OTP_U2F
|
|
for cap_req in MODE_CAPABILITIES.get(mode, [0]):
|
|
if not self.have_capability(cap_req):
|
|
return False
|
|
return True
|
|
|
|
def have_capabilities(self):
|
|
return self.version >= (4, 1, 0)
|
|
|
|
def have_capability(self, capability):
|
|
return self._yk4_capa & capability != 0
|
|
|
|
|
|
class YubiKey4_USBHID(yubikey_neo_usb_hid.YubiKeyNEO_USBHID):
|
|
"""
|
|
Class for accessing a YubiKey 4 over USB HID.
|
|
|
|
"""
|
|
|
|
model = 'YubiKey 4'
|
|
description = 'YubiKey 4'
|
|
_capabilities_cls = YubiKey4_USBHIDCapabilities
|
|
|
|
def __init__(self, debug=False, skip=0, hid_device=None):
|
|
"""
|
|
Find and connect to a YubiKey 4 (USB HID).
|
|
|
|
Attributes :
|
|
skip -- number of YubiKeys to skip
|
|
debug -- True or False
|
|
"""
|
|
super(YubiKey4_USBHID, self).__init__(debug, skip, hid_device)
|
|
if self.version_num() < (4, 0, 0):
|
|
raise yubikey_base.YubiKeyVersionError(
|
|
"Incorrect version for YubiKey 4 %s" % self.version())
|
|
elif self.version_num() < (4, 1, 0):
|
|
self.description = 'YubiKey Plus'
|
|
elif self.version_num() < (4, 2, 0):
|
|
self.description = 'YubiKey Edge/Edge-n'
|
|
|
|
if self.capabilities.have_capabilities():
|
|
data = yubico_util.tlv_parse(self._read_capabilities())
|
|
self.capabilities._set_yk4_capa(data.get(YK4_CAPA.TAG.CAPA, b''))
|
|
|
|
def _read_capabilities(self):
|
|
""" Read the capabilities list from a YubiKey >= 4.0.0 """
|
|
|
|
frame = yubikey_frame.YubiKeyFrame(command=SLOT.YK4_CAPABILITIES)
|
|
self._device._write(frame)
|
|
response = self._device._read_response()
|
|
r_len = yubico_util.ord_byte(response[0])
|
|
|
|
# 1 byte length, 2 byte CRC.
|
|
if not yubico_util.validate_crc16(response[:r_len+3]):
|
|
raise YubiKey4_USBHIDError("Read from device failed CRC check")
|
|
|
|
return response[1:r_len+1]
|