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 test_authenticate(self): ctap = CTAP1(mock.MagicMock()) ctap.device.call.return_value = a2b_hex('0100000001304402204b5f0cd17534cedd8c34ee09570ef542a353df4436030ce43d406de870b847780220267bb998fac9b7266eb60e7cb0b5eabdfd5ba9614f53c7b22272ec10047a923f') + b'\x90\x00' # noqa client_param = a2b_hex(b'ccd6ee2e47baef244d49a222db496bad0ef5b6f93aa7cc4d30c4821b3b9dbc57') # noqa app_param = a2b_hex(b'4b0be934baebb5d12d26011b69227fa5e86df94e7d94aa2949a89f2d493992ca') # noqa key_handle = b'\3'*64 resp = ctap.authenticate(client_param, app_param, key_handle) ctap.device.call.assert_called_with(0x03, b'\0\2\3\0\0\0\x81' + client_param + app_param + b'\x40' + key_handle + b'\0\0') self.assertEqual(resp.user_presence, 1) self.assertEqual(resp.counter, 1) self.assertEqual(resp.signature, a2b_hex('304402204b5f0cd17534cedd8c34ee09570ef542a353df4436030ce43d406de870b847780220267bb998fac9b7266eb60e7cb0b5eabdfd5ba9614f53c7b22272ec10047a923f')) # noqa public_key = a2b_hex(b'04d368f1b665bade3c33a20f1e429c7750d5033660c019119d29aa4ba7abc04aa7c80a46bbe11ca8cb5674d74f31f8a903f6bad105fb6ab74aefef4db8b0025e1d') # noqa client_data = ClientData(b'{"typ":"navigator.id.getAssertion","challenge":"opsXqUifDriAAmWclinfbS0e-USY0CgyJHe_Otd7z8o","cid_pubkey":{"kty":"EC","crv":"P-256","x":"HzQwlfXX7Q4S5MtCCnZUNBw3RMzPO9tOyWjBqRl4tJ8","y":"XVguGFLIZx1fXg3wNqfdbn75hi4-_7-BxhMljw42Ht4"},"origin":"http://example.com"}') # noqa resp.verify(app_param, client_param, public_key) key_handle = b'\4'*8 ctap.authenticate(client_param, app_param, key_handle) ctap.device.call.assert_called_with(0x03, b'\0\2\3\0\0\0\x49' + client_param + app_param + b'\x08' + key_handle + b'\0\0') ctap.authenticate(client_param, app_param, key_handle, True) ctap.device.call.assert_called_with(0x03, b'\0\2\7\0\0\0\x49' + client_param + app_param + b'\x08' + key_handle + b'\0\0')
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 is_in_fips_mode(fido_connection: FidoConnection) -> bool: try: ctap = CTAP1(fido_connection) ctap.send_apdu(ins=FIPS_U2F_CMD.VERIFY_FIPS_MODE) return True except ApduError: return False
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 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 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 self.ctap1 = CTAP1(dev)
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 test_send_apdu_err(self): ctap = CTAP1(mock.MagicMock()) ctap.device.call.return_value = b"err\x6a\x80" try: ctap.send_apdu(1, 2, 3, 4, b"foobar") self.fail("send_apdu did not raise error") except ApduError as e: self.assertEqual(e.code, 0x6A80) self.assertEqual(e.data, b"err") ctap.device.call.assert_called_with(0x03, b"\1\2\3\4\0\0\6foobar\0\0")
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 _ctap1_enroll(dev, alg, application): """Enroll a new security key using CTAP version 1""" ctap1 = CTAP1(dev) if alg != SSH_SK_ECDSA: raise ValueError('Unsupported algorithm') app_hash = sha256(application).digest() registration = _ctap1_poll(_CTAP1_POLL_INTERVAL, ctap1.register, _dummy_hash, app_hash) return registration.public_key, registration.key_handle
def test_authenticate(self): ctap = CTAP1(mock.MagicMock()) ctap.device.call.return_value = ( a2b_hex( "0100000001304402204b5f0cd17534cedd8c34ee09570ef542a353df4436030ce43d406de870b847780220267bb998fac9b7266eb60e7cb0b5eabdfd5ba9614f53c7b22272ec10047a923f" # noqa E501 ) + b"\x90\x00") client_param = a2b_hex( b"ccd6ee2e47baef244d49a222db496bad0ef5b6f93aa7cc4d30c4821b3b9dbc57" ) app_param = a2b_hex( b"4b0be934baebb5d12d26011b69227fa5e86df94e7d94aa2949a89f2d493992ca" ) key_handle = b"\3" * 64 resp = ctap.authenticate(client_param, app_param, key_handle) ctap.device.call.assert_called_with( 0x03, b"\0\2\3\0\0\0\x81" + client_param + app_param + b"\x40" + key_handle + b"\0\0", ) self.assertEqual(resp.user_presence, 1) self.assertEqual(resp.counter, 1) self.assertEqual( resp.signature, a2b_hex( "304402204b5f0cd17534cedd8c34ee09570ef542a353df4436030ce43d406de870b847780220267bb998fac9b7266eb60e7cb0b5eabdfd5ba9614f53c7b22272ec10047a923f" # noqa E501 ), ) public_key = a2b_hex( b"04d368f1b665bade3c33a20f1e429c7750d5033660c019119d29aa4ba7abc04aa7c80a46bbe11ca8cb5674d74f31f8a903f6bad105fb6ab74aefef4db8b0025e1d" # noqa E501 ) resp.verify(app_param, client_param, public_key) key_handle = b"\4" * 8 ctap.authenticate(client_param, app_param, key_handle) ctap.device.call.assert_called_with( 0x03, b"\0\2\3\0\0\0\x49" + client_param + app_param + b"\x08" + key_handle + b"\0\0", ) ctap.authenticate(client_param, app_param, key_handle, True) ctap.device.call.assert_called_with( 0x03, b"\0\2\7\0\0\0\x49" + client_param + app_param + b"\x08" + key_handle + b"\0\0", )
def _ctap1_sign(dev, message_hash, application, key_handle): """Sign a message with a security key using CTAP version 1""" ctap1 = CTAP1(dev) app_hash = sha256(application).digest() auth_response = _ctap1_poll(_CTAP1_POLL_INTERVAL, ctap1.authenticate, message_hash, app_hash, key_handle) flags = auth_response[0] counter = int.from_bytes(auth_response[1:5], 'big') sig = auth_response[5:] return flags, counter, sig
def __init__(self, device, origin, verify=verify_rp_id): super(Fido2Client, self).__init__(origin, verify) self.ctap1_poll_delay = 0.25 try: self.ctap2 = CTAP2(device) self.info = self.ctap2.get_info() if PinProtocolV1.VERSION in self.info.pin_protocols: self.pin_protocol = PinProtocolV1(self.ctap2) else: self.pin_protocol = None self._do_make_credential = self._ctap2_make_credential self._do_get_assertion = self._ctap2_get_assertion except (ValueError, CtapError): self.ctap1 = CTAP1(device) self.info = _CTAP1_INFO self._do_make_credential = self._ctap1_make_credential self._do_get_assertion = self._ctap1_get_assertion
def test_register(self): ctap = CTAP1(mock.MagicMock()) ctap.device.call.return_value = ( a2b_hex( "0504b174bc49c7ca254b70d2e5c207cee9cf174820ebd77ea3c65508c26da51b657c1cc6b952f8621697936482da0a6d3d3826a59095daf6cd7c03e2e60385d2f6d9402a552dfdb7477ed65fd84133f86196010b2215b57da75d315b7b9e8fe2e3925a6019551bab61d16591659cbaf00b4950f7abfe6660e2e006f76868b772d70c253082013c3081e4a003020102020a47901280001155957352300a06082a8648ce3d0403023017311530130603550403130c476e756262792050696c6f74301e170d3132303831343138323933325a170d3133303831343138323933325a3031312f302d0603550403132650696c6f74476e756262792d302e342e312d34373930313238303030313135353935373335323059301306072a8648ce3d020106082a8648ce3d030107034200048d617e65c9508e64bcc5673ac82a6799da3c1446682c258c463fffdf58dfd2fa3e6c378b53d795c4a4dffb4199edd7862f23abaf0203b4b8911ba0569994e101300a06082a8648ce3d0403020347003044022060cdb6061e9c22262d1aac1d96d8c70829b2366531dda268832cb836bcd30dfa0220631b1459f09e6330055722c8d89b7f48883b9089b88d60d1d9795902b30410df304502201471899bcc3987e62e8202c9b39c33c19033f7340352dba80fcab017db9230e402210082677d673d891933ade6f617e5dbde2e247e70423fd5ad7804a6d3d3961ef871" # noqa E501 ) + b"\x90\x00") client_param = a2b_hex( b"4142d21c00d94ffb9d504ada8f99b721f4b191ae4e37ca0140f696b6983cfacb" ) app_param = a2b_hex( b"f0e6a6a97042a4f1f1c87f5f7d44315b2d852c2df5c7991cc66241bf7072d1c4" ) resp = ctap.register(client_param, app_param) ctap.device.call.assert_called_with( 0x03, b"\0\1\0\0\0\0\x40" + client_param + app_param + b"\0\0") self.assertEqual( resp.public_key, a2b_hex( "04b174bc49c7ca254b70d2e5c207cee9cf174820ebd77ea3c65508c26da51b657c1cc6b952f8621697936482da0a6d3d3826a59095daf6cd7c03e2e60385d2f6d9" # noqa E501 ), ) self.assertEqual( resp.key_handle, a2b_hex( "2a552dfdb7477ed65fd84133f86196010b2215b57da75d315b7b9e8fe2e3925a6019551bab61d16591659cbaf00b4950f7abfe6660e2e006f76868b772d70c25" # noqa E501 ), ) self.assertEqual( resp.certificate, a2b_hex( "3082013c3081e4a003020102020a47901280001155957352300a06082a8648ce3d0403023017311530130603550403130c476e756262792050696c6f74301e170d3132303831343138323933325a170d3133303831343138323933325a3031312f302d0603550403132650696c6f74476e756262792d302e342e312d34373930313238303030313135353935373335323059301306072a8648ce3d020106082a8648ce3d030107034200048d617e65c9508e64bcc5673ac82a6799da3c1446682c258c463fffdf58dfd2fa3e6c378b53d795c4a4dffb4199edd7862f23abaf0203b4b8911ba0569994e101300a06082a8648ce3d0403020347003044022060cdb6061e9c22262d1aac1d96d8c70829b2366531dda268832cb836bcd30dfa0220631b1459f09e6330055722c8d89b7f48883b9089b88d60d1d9795902b30410df" # noqa E501 ), ) self.assertEqual( resp.signature, a2b_hex( "304502201471899bcc3987e62e8202c9b39c33c19033f7340352dba80fcab017db9230e402210082677d673d891933ade6f617e5dbde2e247e70423fd5ad7804a6d3d3961ef871" # noqa E501 ), ) resp.verify(app_param, client_param)
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 test_register(self): ctap = CTAP1(mock.MagicMock()) ctap.device.call.return_value = a2b_hex('0504b174bc49c7ca254b70d2e5c207cee9cf174820ebd77ea3c65508c26da51b657c1cc6b952f8621697936482da0a6d3d3826a59095daf6cd7c03e2e60385d2f6d9402a552dfdb7477ed65fd84133f86196010b2215b57da75d315b7b9e8fe2e3925a6019551bab61d16591659cbaf00b4950f7abfe6660e2e006f76868b772d70c253082013c3081e4a003020102020a47901280001155957352300a06082a8648ce3d0403023017311530130603550403130c476e756262792050696c6f74301e170d3132303831343138323933325a170d3133303831343138323933325a3031312f302d0603550403132650696c6f74476e756262792d302e342e312d34373930313238303030313135353935373335323059301306072a8648ce3d020106082a8648ce3d030107034200048d617e65c9508e64bcc5673ac82a6799da3c1446682c258c463fffdf58dfd2fa3e6c378b53d795c4a4dffb4199edd7862f23abaf0203b4b8911ba0569994e101300a06082a8648ce3d0403020347003044022060cdb6061e9c22262d1aac1d96d8c70829b2366531dda268832cb836bcd30dfa0220631b1459f09e6330055722c8d89b7f48883b9089b88d60d1d9795902b30410df304502201471899bcc3987e62e8202c9b39c33c19033f7340352dba80fcab017db9230e402210082677d673d891933ade6f617e5dbde2e247e70423fd5ad7804a6d3d3961ef871') + b'\x90\x00' # noqa client_param = a2b_hex(b'4142d21c00d94ffb9d504ada8f99b721f4b191ae4e37ca0140f696b6983cfacb') # noqa app_param = a2b_hex(b'f0e6a6a97042a4f1f1c87f5f7d44315b2d852c2df5c7991cc66241bf7072d1c4') # noqa resp = ctap.register(client_param, app_param) ctap.device.call.assert_called_with( 0x03, b'\0\1\0\0\0\0\x40' + client_param + app_param + b'\0\0' ) self.assertEqual(resp.public_key, a2b_hex('04b174bc49c7ca254b70d2e5c207cee9cf174820ebd77ea3c65508c26da51b657c1cc6b952f8621697936482da0a6d3d3826a59095daf6cd7c03e2e60385d2f6d9')) # noqa self.assertEqual(resp.key_handle, a2b_hex('2a552dfdb7477ed65fd84133f86196010b2215b57da75d315b7b9e8fe2e3925a6019551bab61d16591659cbaf00b4950f7abfe6660e2e006f76868b772d70c25')) # noqa self.assertEqual(resp.certificate, a2b_hex('3082013c3081e4a003020102020a47901280001155957352300a06082a8648ce3d0403023017311530130603550403130c476e756262792050696c6f74301e170d3132303831343138323933325a170d3133303831343138323933325a3031312f302d0603550403132650696c6f74476e756262792d302e342e312d34373930313238303030313135353935373335323059301306072a8648ce3d020106082a8648ce3d030107034200048d617e65c9508e64bcc5673ac82a6799da3c1446682c258c463fffdf58dfd2fa3e6c378b53d795c4a4dffb4199edd7862f23abaf0203b4b8911ba0569994e101300a06082a8648ce3d0403020347003044022060cdb6061e9c22262d1aac1d96d8c70829b2366531dda268832cb836bcd30dfa0220631b1459f09e6330055722c8d89b7f48883b9089b88d60d1d9795902b30410df')) # noqa self.assertEqual(resp.signature, a2b_hex('304502201471899bcc3987e62e8202c9b39c33c19033f7340352dba80fcab017db9230e402210082677d673d891933ade6f617e5dbde2e247e70423fd5ad7804a6d3d3961ef871')) # noqa client_data = ClientData(b'{"typ":"navigator.id.finishEnrollment","challenge":"vqrS6WXDe1JUs5_c3i4-LkKIHRr-3XVb3azuA5TifHo","cid_pubkey":{"kty":"EC","crv":"P-256","x":"HzQwlfXX7Q4S5MtCCnZUNBw3RMzPO9tOyWjBqRl4tJ8","y":"XVguGFLIZx1fXg3wNqfdbn75hi4-_7-BxhMljw42Ht4"},"origin":"http://example.com"}') # noqa resp.verify(app_param, client_param)
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 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
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
def test_get_version(self): ctap = CTAP1(mock.MagicMock()) ctap.device.call.return_value = b"U2F_V2\x90\x00" self.assertEqual("U2F_V2", ctap.get_version()) ctap.device.call.assert_called_with(0x03, b"\0\3\0\0\0\0\0\0\0")
def test_send_apdu_ok(self): ctap = CTAP1(mock.MagicMock()) ctap.device.call.return_value = b'response\x90\x00' self.assertEqual(b'response', ctap.send_apdu(1, 2, 3, 4, b'foobar')) ctap.device.call.assert_called_with(0x03, b'\1\2\3\4\0\0\6foobar\0\0')
def test_send_apdu_ok(self): ctap = CTAP1(mock.MagicMock()) ctap.device.call.return_value = b"response\x90\x00" self.assertEqual(b"response", ctap.send_apdu(1, 2, 3, 4, b"foobar")) ctap.device.call.assert_called_with(0x03, b"\1\2\3\4\0\0\6foobar\0\0")
dev = next(Acr122uSamPcscDevice.list_devices()) print('CONNECT: %s' % dev) print('version: %s' % dev.reader_version()) print('atr: %s' % bytes(dev.get_atr()).hex()) print('ats: %s' % dev.ats.hex()) # uncomment if you want to see parameters from card's selection # dev.get_ats(True) # dev._select() dev.led_control(False, True, 0) chal = sha256(b'AAA') appid = sha256(b'BBB') ctap1 = CTAP1(dev) print('ctap1 version:', ctap1.get_version()) reg = ctap1.register(chal, appid) print('u2f register:', reg) reg.verify(appid, chal) print('Register message verify OK') auth = ctap1.authenticate(chal, appid, reg.key_handle) print('u2f authenticate: ', auth) res = auth.verify(appid, chal, reg.public_key) print('Authenticate message verify OK') dev.led_control()
def __init__(self, driver): self.driver = driver self.ctap = CTAP1(driver._dev)
def __init__(self, ctap_device): self.ctap = CTAP1(ctap_device)