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
|
# Original work Copyright 2016 Google Inc. All Rights Reserved.
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
#
# Modified work Copyright 2020 Yubico AB. All Rights Reserved.
# This file, with modifications, is licensed under the above Apache License.
from __future__ import annotations
import fcntl
import logging
import os
import os.path
import select
import sys
from ctypes import Structure, c_char, c_int, c_uint8, c_uint16, c_uint32
from .base import FileCtapHidConnection, HidDescriptor
# Don't typecheck this file on Windows
assert sys.platform != "win32" # nosec
logger = logging.getLogger(__name__)
# /usr/include/dev/usb/usb.h
USB_GET_DEVICEINFO = 0x421C5570
USB_MAX_STRING_LEN = 127
USB_MAX_DEVNAMES = 4
USB_MAX_DEVNAMELEN = 16
FIDO_DEVS = "/dev/fido"
MAX_U2F_HIDLEN = 64
class UsbDeviceInfo(Structure):
_fields_ = [
("udi_bus", c_uint8),
("udi_addr", c_uint8),
("udi_product", c_char * USB_MAX_STRING_LEN),
("udi_vendor", c_char * USB_MAX_STRING_LEN),
("udi_release", c_char * 8),
("udi_productNo", c_uint16),
("udi_vendorNo", c_uint16),
("udi_releaseNo", c_uint16),
("udi_class", c_uint8),
("udi_subclass", c_uint8),
("udi_protocol", c_uint8),
("udi_config", c_uint8),
("udi_speed", c_uint8),
("udi_power", c_int),
("udi_nports", c_int),
("udi_devnames", c_char * USB_MAX_DEVNAMELEN * USB_MAX_DEVNAMES),
("udi_ports", c_uint32 * 16),
("udi_serial", c_char * USB_MAX_STRING_LEN),
]
class OpenBsdCtapHidConnection(FileCtapHidConnection):
def __init__(self, descriptor):
super().__init__(descriptor)
try:
self._terrible_ping_kludge()
except Exception:
self.close()
raise
def _terrible_ping_kludge(self):
# This is pulled from
# https://github.com/Yubico/libfido2/blob/da24193aa901086960f8d31b60d930ebef21f7a2/src/hid_openbsd.c#L128
for _ in range(4):
# 1 byte ping
data = b"\xff\xff\xff\xff\x81\0\1".ljust(
self.descriptor.report_size_out, b"\0"
)
poll = select.poll()
poll.register(self.handle, select.POLLIN)
self.write_packet(data)
poll.poll(100)
data = self.read_packet()
def open_connection(descriptor):
return OpenBsdCtapHidConnection(descriptor)
def get_descriptor(path):
f = os.open(path, os.O_RDONLY)
dev_info = UsbDeviceInfo()
try:
fcntl.ioctl(f, USB_GET_DEVICEINFO, dev_info) # type: ignore
finally:
os.close(f)
vid = int(dev_info.udi_vendorNo)
pid = int(dev_info.udi_productNo)
name = dev_info.udi_product.decode("utf-8") or None
serial = dev_info.udi_serial.decode("utf-8") or None
return HidDescriptor(path, vid, pid, MAX_U2F_HIDLEN, MAX_U2F_HIDLEN, name, serial)
# Cache for continuously failing devices
_failed_cache: set[str] = set()
def list_descriptors():
stale = set(_failed_cache)
descriptors = []
for dev in os.listdir(FIDO_DEVS):
path = os.path.join(FIDO_DEVS, dev)
stale.discard(path)
try:
descriptors.append(get_descriptor(path))
except Exception:
if path not in _failed_cache:
logger.debug("Failed opening FIDO device %s", path, exc_info=True)
_failed_cache.add(path)
return descriptors
|