def test_call_keepalive(self): dev = mock.Mock() hid_dev = CtapHidDevice(None, dev) on_keepalive = mock.MagicMock() dev.InternalRecv = mock.Mock(side_effect=[ (0xbb, bytearray([0])), (0xbb, bytearray([0])), (0xbb, bytearray([0])), (0xbb, bytearray([0])), (0x81, bytearray(b'done')) ]) self.assertEqual(hid_dev.call(0x01, on_keepalive=on_keepalive), b'done') on_keepalive.assert_called_once_with(0) dev.InternalRecv.side_effect = [ (0xbb, bytearray([1])), (0xbb, bytearray([0])), (0xbb, bytearray([0])), (0xbb, bytearray([1])), (0xbb, bytearray([1])), (0xbb, bytearray([1])), (0xbb, bytearray([1])), (0xbb, bytearray([0])), (0x81, bytearray(b'done')) ] on_keepalive.reset_mock() self.assertEqual(hid_dev.call(0x01, on_keepalive=on_keepalive), b'done') self.assertEqual( on_keepalive.call_args_list, [mock.call(1), mock.call(0), mock.call(1), mock.call(0)] )
def find_device(self, nfcInterfaceOnly=False): dev = None self.nfc_interface_only = nfcInterfaceOnly if not nfcInterfaceOnly: print("--- HID ---") print(list(CtapHidDevice.list_devices())) dev = next(CtapHidDevice.list_devices(), None) else: from fido2.pcsc import CtapPcscDevice print("--- NFC ---") dev = next(MoreRobustPcscDevice.list_devices(), None) if dev: self.is_nfc = True # For ACR1252 readers, with drivers installed # https://www.acs.com.hk/en/products/342/acr1252u-usb-nfc-reader-iii-nfc-forum-certified-reader # disable auto pps, always use 106kbps # dev.control_exchange(SCARD_CTL_CODE(0x3500), b"\xE0\x00\x00\x24\x02\x00\x00") if not dev: raise RuntimeError("No FIDO device found") self.dev = dev self.client = Fido2Client(dev, self.origin) self.ctap2 = self.client.ctap2 self.ctap1 = CTAP1(dev)
def find_device(self, ): print(list(CtapHidDevice.list_devices())) dev = next(CtapHidDevice.list_devices(), None) if not dev: raise RuntimeError('No FIDO device found') self.dev = dev self.client = Fido2Client(dev, self.origin) self.ctap = self.client.ctap2
def test_call_error(self): dev = mock.Mock() hid_dev = CtapHidDevice(None, dev) dev.InternalRecv = mock.Mock(return_value=(0xbf, bytearray([7]))) try: hid_dev.call(0x01) self.fail('call did not raise exception') except CtapError as e: self.assertEqual(e.code, 7)
def find_device(self, dev=None, solo_serial=None): if dev is None: devices = list(CtapHidDevice.list_devices()) if solo_serial is not None: devices = [ d for d in devices if d.descriptor["serial_number"] == solo_serial ] if len(devices) > 1: raise solo.exceptions.NonUniqueDeviceError if len(devices) == 0: raise RuntimeError("No FIDO device found") dev = devices[0] self.dev = dev self.ctap1 = CTAP1(dev) self.ctap2 = CTAP2(dev) try: self.client = Fido2Client(dev, self.origin) except CtapError: print("Not using FIDO2 interface.") self.client = None if self.exchange == self.exchange_hid: self.send_data_hid(CTAPHID.INIT, "\x11\x11\x11\x11\x11\x11\x11\x11") return self.dev
def get_device(self): try: devs = list(CtapHidDevice.list_devices()) assert len(devs) == 1 return devs[0] except Exception: self.skipTest('Tests require a single FIDO HID device')
def locate_device(self): # Locate a device devs = list(CtapHidDevice.list_devices()) if not devs: devs = CtapKeyringDevice.list_devices() self._clients = [Fido2Client(d, self._okta_org_url) for d in devs]
def _discover_devices(communication_queue, abort_event): """ Discover FIDO2 authenticators :param communication_queue: Queue to put newly discovered authenticator in :param abort_event: Event to abort discovery with :return: """ discovered = [] # look for new devices until abort while not abort_event.is_set(): new_devices = [] for d in hidtransport.hid.Enumerate(): if hidtransport.HidUsageSelector(d) and \ d not in [e.descriptor for e in discovered]: # new device found dev = hidtransport.hid.Open(d['path']) dev = CtapHidDevice(d, hidtransport.UsbHidTransport(dev)) new_devices.append(dev) discovered.append(dev) # notify main thread if new_devices: communication_queue.put( (CommunicationObject.DEVICE, new_devices)) # slow down busy wait time.sleep(0.1) # on abort put an empty list in the queue to stop the main thread # from waiting for results communication_queue.put((CommunicationObject.DEVICE, []))
def find_device(self, nfcInterfaceOnly=False): if self.dev is not None: return dev = None self.nfc_interface_only = nfcInterfaceOnly if not nfcInterfaceOnly: # print("--- HID ---") # print(list(CtapHidDevice.list_devices())) dev = next(CtapHidDevice.list_devices(), None) if not dev: from fido2.pcsc import CtapPcscDevice # print("--- NFC ---") # print(list(CtapPcscDevice.list_devices())) dev = next(CtapPcscDevice.list_devices(), None) if dev: self.is_nfc = True if not dev: raise RuntimeError("No FIDO device found") self.dev = dev self.client = Fido2Client(dev, self.origin) self.ctap2 = self.client.ctap2 self.ctap1 = CTAP1(dev)
def iterdevs(per_device): lock = threading.Lock() cancel_ev = threading.Event() result = [None] ok = [False] def go(dev): try: r = per_device(dev, cancel_ev) except ClientError as e: if e.code != ClientError.ERR.TIMEOUT: raise with lock: if not ok[0]: result[0] = r ok[0] = True cancel_ev.set() threads = [] for dev in CtapHidDevice.list_devices(): t = threading.Thread(target=go, args=(dev, )) t.start() threads.append(t) for t in threads: t.join() if not ok[0]: raise FileNotFoundError return result[0]
def u2f_authenticate(authenticateRequests): # type: (List[dict]) -> Optional[dict] global should_cancel_u2f global u2f_response if not authenticateRequests: return None devices = list(CtapHidDevice.list_devices()) if not devices: return None to_auth = [] for i in range(len(devices)): u2f_client = CTAP1(devices[i]) u2f_version = u2f_client.get_version() for request in authenticateRequests: try: version = request['version'] if version == u2f_version: app_id = request['appId'] challenge = request['challenge'] key_handle = base64.urlsafe_b64decode( request['keyHandle'] + '==') app_id_hash = sha256(app_id.encode('ascii')).digest() cl_data = { 'typ': U2F_TYPE.SIGN, 'challenge': challenge, 'origin': app_id } client_data = json.dumps(cl_data) try: client_param = sha256( client_data.encode('utf8')).digest() u2f_client.authenticate(client_param, app_id_hash, key_handle, check_only=True) except ApduError as e: if e.code == APDU.USE_NOT_SATISFIED: to_auth.append((u2f_client, client_data, app_id_hash, key_handle)) except: pass if to_auth: u2f_thread = threading.Thread(target=thread_function, args=((to_auth, ))) u2f_thread.start() try: get_input_interrupted( '\nTouch the flashing U2F device to authenticate or press Enter to resume with the primary two factor authentication...\n' ) should_cancel_u2f = True u2f_thread.join() except KeyboardInterrupt: pass return u2f_response
def locate_device(self): # Locate a device devs = list(CtapHidDevice.list_devices()) if not devs: self.ui.info("No FIDO device found") raise NoFIDODeviceFoundError self._clients = [CTAP1(d) for d in devs]
def locate_device(self): # Locate a device devs = list(CtapHidDevice.list_devices()) if not devs: self.ui.info('No FIDO device found') raise NoFIDODeviceFoundError self._clients = [Fido2Client(d, self._okta_org_url) for d in devs]
def okta_mfa_webauthn(conf, s, factor, state_token): # type: (Conf, requests.Session, Dict[str, str], str) -> Optional[Dict[str, Any]] if not have_fido: err('Need fido2 package(s) for webauthn. Consider doing `pip install fido2` (or similar)' ) devices = list(CtapHidDevice.list_devices()) if not devices: err('webauthn configured, but no U2F devices found') provider = factor.get('provider', '') log('mfa {0} challenge request [okta_url]'.format(provider)) data = {'stateToken': state_token} _, _h, j = send_json_req(conf, s, 'webauthn mfa challenge', factor.get('url', ''), data, expected_url=conf.okta_url, verify=conf.get_cert('okta_url', True)) rfactor = j['_embedded']['factor'] profile = rfactor['profile'] purl = parse_url(conf.okta_url) origin = '{0}://{1}'.format(purl[0], purl[1]) challenge = rfactor['_embedded']['challenge']['challenge'] credentialId = websafe_decode(profile['credentialId']) allow_list = [{'type': 'public-key', 'id': credentialId}] for dev in devices: client = Fido2Client(dev, origin) print('!!! Touch the flashing U2F device to authenticate... !!!') try: result = client.get_assertion(purl[1], challenge, allow_list) dbg(conf.debug, 'assertion.result', result) break except Exception: traceback.print_exc(file=sys.stderr) result = None if not result: return None assertion, client_data = result[0][0], result[ 1] # only one cred in allowList, so only one response. data = { 'stateToken': state_token, 'clientData': to_n((base64.b64encode(client_data)).decode('ascii')), 'signatureData': to_n((base64.b64encode(assertion.signature)).decode('ascii')), 'authenticatorData': to_n((base64.b64encode(assertion.auth_data)).decode('ascii')) } log('mfa {0} signature request [okta_url]'.format(provider)) _, _h, j = send_json_req(conf, s, 'uf2 mfa signature', j['_links']['next']['href'], data, expected_url=conf.okta_url, verify=conf.get_cert('okta_url', True)) return j
def log_devices() -> None: from fido2.hid import CtapHidDevice ctap_devices = [device for device in CtapHidDevice.list_devices()] logger.info(f"Found {len(ctap_devices)} CTAPHID devices:") for device in ctap_devices: descriptor = device.descriptor path = device_path_to_str(descriptor.path) logger.info(f"- {path} ({descriptor.vid:x}:{descriptor.pid:x})")
def get_dev(): dev = next(CtapHidDevice.list_devices(), None) if dev is not None: print("Use USB HID channel.") else: print("No FIDO device found") sys.exit(1) return dev
def list() -> List["Nitrokey3Device"]: devices = [] for device in CtapHidDevice.list_devices(): try: devices.append(Nitrokey3Device(device)) except ValueError: # not a Nitrokey 3 device, skip pass return devices
def find_device(self, ): dev = next(CtapHidDevice.list_devices(), None) if not dev: raise RuntimeError('No FIDO device found') self.dev = dev self.ctap1 = CTAP1(dev) if self.exchange == self.exchange_hid: self.send_data_hid(CTAPHID.INIT, '\x11\x11\x11\x11\x11\x11\x11\x11')
def find_all(): hid_devices = list(CtapHidDevice.list_devices()) solo_devices = [ d for d in hid_devices if all(( d.descriptor["vendor_id"] == 1155, d.descriptor["product_id"] == 41674, # "Solo" in d.descriptor["product_string"], )) ] return [find(raw_device=device) for device in solo_devices]
def connect(domain="https://localhost", aaguid=None): for dev in CtapHidDevice.list_devices(): client = Fido2Client(dev, domain) if not aaguid or aaguid == client.info.aaguid.hex(): if "hmac-secret" in client.info.extensions: return client client.close() else: print("No Authenticator with the HmacSecret extension found!") sys.exit(-1)
def find_device(self, dev=None, solo_serial: str = None): devices = [] if dev is None: if solo_serial is not None: if solo_serial.startswith("device="): solo_serial = solo_serial.split("=")[1] dev = open_device(solo_serial) else: devices = list(CtapHidDevice.list_devices()) devices = [ d for d in devices if d.descriptor.serial_number == solo_serial ] else: devices = list(CtapHidDevice.list_devices()) if len(devices) > 1: raise pynitrokey.exceptions.NonUniqueDeviceError if len(devices) > 0: dev = devices[0] if dev is None: raise RuntimeError("No FIDO device found") self.dev = dev self.ctap1 = CTAP1(dev) try: self.ctap2: Optional[CTAP2] = CTAP2(dev) except CtapError as e: self.ctap2 = None try: self.client: Optional[Fido2Client] = Fido2Client(dev, self.origin) except CtapError: print("Not using FIDO2 interface.") self.client = None if self.exchange == self.exchange_hid: self.send_data_hid(CTAPHID.INIT, "\x11\x11\x11\x11\x11\x11\x11\x11") return self.dev
def find_all(): hid_devices = list(CtapHidDevice.list_devices()) solo_devices = [ d for d in hid_devices if (d.descriptor["vendor_id"], d.descriptor["product_id"]) in [ (1155, 41674), (0x20A0, 0x42B3), (0x20A0, 0x42B1), ] ] return [find(raw_device=device) for device in solo_devices]
def find_device(self, ): dev = next(CtapHidDevice.list_devices(), None) if not dev: raise RuntimeError("No FIDO device found") self.dev = dev self.ctap1 = CTAP1(dev) self.ctap2 = CTAP2(dev) self.client = Fido2Client(dev, self.origin) if self.exchange == self.exchange_hid: self.send_data_hid(CTAPHID.INIT, "\x11\x11\x11\x11\x11\x11\x11\x11")
def find_all(): from fido2.hid import CtapHidDevice hid_devices = list(CtapHidDevice.list_devices()) solo_devices = [d for d in hid_devices if (d.descriptor["vendor_id"], d.descriptor["product_id"]) in [ ## @FIXME: move magic numbers (1155, 41674), (0x20A0, 0x42B3), (0x20A0, 0x42B1), ] ] return [find(raw_device=device) for device in solo_devices]
def find_device(self, nfcInterfaceOnly=False): dev = None if not nfcInterfaceOnly: print("--- HID ---") print(list(CtapHidDevice.list_devices())) dev = next(CtapHidDevice.list_devices(), None) if not dev: try: from fido2.pcsc import CtapPcscDevice print("--- NFC ---") print(list(CtapPcscDevice.list_devices())) dev = next(CtapPcscDevice.list_devices(), None) except (ModuleNotFoundError, ImportError): print("One of NFC library is not installed properly.") if not dev: raise RuntimeError("No FIDO device found") self.dev = dev self.client = Fido2Client(dev, self.origin) self.ctap = self.client.ctap2 self.ctap1 = CTAP1(dev)
def do_register_user(user, rp_id, resident_key=False): """ FIDO2 registration process :param user: The user to register :param rp_id: Relying Party identifier :param resident_key: Boolean indicating whether or not to store a resident key :return: Newly created credentials """ # begin registration relying_part = RelyingParty(rp_id) server = Fido2Server(relying_part) registration_data, state = server.register_begin(user) # make credential dev = next(CtapHidDevice.list_devices(), None) if not dev: print('No FIDO device found') sys.exit(1) client = Fido2Client(dev, 'https://' + rp_id) rp = {'id': rp_id, 'name': rp_id} challenge = websafe_encode(registration_data['publicKey']['challenge']) if resident_key: user['name'] = "." user_string = "(id: {0})".format(user['id'].hex()) else: user_string = "(name: {0}, display name: {1})".format( user['name'], user['displayName']) print("\nRegistration request for user: "******"From service: (Address: {0}, Name: {1})".format(rp['id'], rp['name'])) print('Touch your authenticator device now to consent to registration...\n') try: attestation_object, client_data = client.make_credential( rp, user, challenge, rk=resident_key) except Exception as e: print("Registration failed") raise e # complete registration registration_data = server.register_complete(state, client_data, attestation_object) credential = registration_data.credential_data print("Registration complete") return credential
def find_device(self, dev=None, solo_serial=None): if dev is None: devices = list(CtapHidDevice.list_devices()) if solo_serial is not None: for d in devices: if not hasattr(d, "serial_number"): print( "Currently serial numbers are not supported with current fido2 library. Please upgrade: pip3 install fido2 --upgrade" ) sys.exit(1) devices = [ d for d in devices if d.descriptor.serial_number == solo_serial ] if len(devices) > 1: raise solo.exceptions.NonUniqueDeviceError if len(devices) == 0: raise RuntimeError("No FIDO device found") dev = devices[0] self.dev = dev self.ctap1 = CTAP1(dev) try: self.ctap2 = CTAP2(dev) except CtapError: self.ctap2 = None try: self.client = Fido2Client(dev, self.origin) except CtapError: print("Not using FIDO2 interface.") self.client = None if self.exchange == self.exchange_hid: self.send_data_hid(CTAPHID.INIT, "\x11\x11\x11\x11\x11\x11\x11\x11") return self.dev
httpport = 8080 udpport = 8111 HEX_FILE = '../efm32/GNU ARM v7.2.1 - Debug/EFM32.hex' def ForceU2F(client,device): client.ctap = CTAP1(device) client.pin_protocol = None client._do_make_credential = client._ctap1_make_credential client._do_get_assertion = client._ctap1_get_assertion if __name__ == '__main__': try: dev = next(CtapHidDevice.list_devices(), None) print(dev) if not dev: raise RuntimeError('No FIDO device found') client = Fido2Client(dev, 'https://example.com') ForceU2F(client, dev) ctap = client.ctap except Exception as e: print(e) def write(data): msg = from_websafe(data) msg = base64.b64decode(msg)
def open_devices(): for dev in CtapHidDevice.list_devices(descriptor_filter): try: yield FidoDriver(dev) except Exception as e: logger.debug('Failed opening FIDO device', exc_info=e)
def open_connection(self, connection_type): if self.supports_connection(connection_type): return CtapHidDevice(self.descriptor, open_connection(self.descriptor)) return super(OtpYubiKeyDevice, self).open_connection(connection_type)
def enumerate_devices(): for dev in CtapHidDevice.list_devices(): yield dev if CtapPcscDevice: for dev in CtapPcscDevice.list_devices(): yield dev