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 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 test_make_credential_ctap1(self): dev = mock.Mock() dev.capabilities = 0 # No CTAP2 client = Fido2Client(dev, APP_ID) client.ctap1 = mock.MagicMock() client.ctap1.get_version.return_value = "U2F_V2" client.ctap1.register.return_value = REG_DATA response = client.make_credential( PublicKeyCredentialCreationOptions(rp, user, challenge, [{ "type": "public-key", "alg": -7 }])) self.assertIsInstance(response.attestation_object, AttestationObject) self.assertIsInstance(response.client_data, ClientData) client_data = response.client_data client.ctap1.register.assert_called_with(client_data.hash, sha256(rp["id"].encode())) self.assertEqual(client_data.get("origin"), APP_ID) self.assertEqual(client_data.get("type"), "webauthn.create") self.assertEqual(client_data.challenge, challenge) self.assertEqual(response.attestation_object.fmt, "fido-u2f")
def test_make_credential_existing_key(self, PatchedCTAP2): dev = mock.Mock() dev.capabilities = CAPABILITY.CBOR ctap2 = mock.MagicMock() ctap2.get_info.return_value = Info(_INFO_NO_PIN) ctap2.make_credential.side_effect = CtapError( CtapError.ERR.CREDENTIAL_EXCLUDED) PatchedCTAP2.return_value = ctap2 client = Fido2Client(dev, APP_ID) try: client.make_credential( PublicKeyCredentialCreationOptions( rp, user, challenge, [{ "type": "public-key", "alg": -7 }], authenticator_selection={ "userVerification": "discouraged" }, )) self.fail("make_credential did not raise error") except ClientError as e: self.assertEqual(e.code, ClientError.ERR.DEVICE_INELIGIBLE) ctap2.get_info.assert_called_with() ctap2.make_credential.assert_called_once()
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_make_credential_ctap1(self): dev = mock.Mock() dev.capabilities = 0 # No CTAP2 client = Fido2Client(dev, APP_ID) client.ctap1 = mock.MagicMock() client.ctap1.get_version.return_value = "U2F_V2" client.ctap1.register.return_value = REG_DATA attestation, client_data = client.make_credential(rp, user, challenge, timeout=1) self.assertIsInstance(attestation, AttestationObject) self.assertIsInstance(client_data, ClientData) client.ctap1.register.assert_called_with(client_data.hash, sha256(rp["id"].encode())) self.assertEqual(client_data.get("origin"), APP_ID) self.assertEqual(client_data.get("type"), "webauthn.create") self.assertEqual(client_data.get("challenge"), challenge) self.assertEqual(attestation.fmt, "fido-u2f")
def find_device(self,): 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 get_client(): global use_prompt if WindowsClient.is_available( ) and not ctypes.windll.shell32.IsUserAnAdmin(): # Use the Windows WebAuthn API if available, and we're not running as admin client = WindowsClient("https://example.com") else: # Locate a device for dev in enumerate_devices(): client = Fido2Client(dev, "https://example.com") if client.info.options.get("rk"): use_prompt = not (CtapPcscDevice and isinstance(dev, CtapPcscDevice)) break else: print("No Authenticator with support for resident key found!") sys.exit(1) # Prefer UV if supported if client.info.options.get("uv"): uv = "preferred" print("Authenticator supports User Verification") elif client.info.options.get("clientPin"): # Prompt for PIN if needed pin = getpass("Please enter PIN: ") else: print("PIN not set, won't use") return client
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 test_make_credential_ctap2(self): dev = mock.Mock() dev.capabilities = CAPABILITY.CBOR client = Fido2Client(dev, APP_ID) client.ctap = mock.MagicMock() client.ctap.get_info.return_value = Info(_INFO_NO_PIN) client.ctap.make_credential.return_value = AttestationObject(_MC_RESP) attestation, client_data = client.make_credential(rp, user, challenge, timeout=1) self.assertIsInstance(attestation, AttestationObject) self.assertIsInstance(client_data, ClientData) client.ctap.get_info.assert_called_with() client.ctap.make_credential.assert_called_with( client_data.hash, rp, user, [{ 'type': 'public-key', 'alg': -7 }], None, None, None, None, None, 1, None) self.assertEqual(client_data.get('origin'), APP_ID) self.assertEqual(client_data.get('type'), 'webauthn.create') self.assertEqual(client_data.get('challenge'), challenge)
def test_make_credential_ctap1(self): dev = mock.Mock() dev.capabilities = 0 # No CTAP2 client = Fido2Client(dev, APP_ID) client.ctap = mock.MagicMock() client.ctap.get_version.return_value = 'U2F_V2' client.ctap.register.return_value = REG_DATA attestation, client_data = client.make_credential(rp, user, challenge, timeout=1) self.assertIsInstance(attestation, AttestationObject) self.assertIsInstance(client_data, ClientData) client.ctap.register.assert_called_with( client_data.hash, sha256(rp['id'].encode()), ) self.assertEqual(client_data.get('origin'), APP_ID) self.assertEqual(client_data.get('type'), 'webauthn.create') self.assertEqual(client_data.get('challenge'), challenge) self.assertEqual(attestation.fmt, 'fido-u2f')
def per_device(dev, cancel_ev=None): client = Fido2Client(dev, fidocrypt_origin(rp['id']), verify_origin) return client.make_credential( create_options['publicKey'], on_keepalive=on_keepalive if prompt is not None else None, **({} if cancel_ev is None else { 'event': cancel_ev }))
def per_device(dev, cancel_ev=None): client = Fido2Client(dev, fidokdf_origin(rp['id']), verify_origin) return client.get_assertion( request_options['publicKey'], on_keepalive=on_keepalive if prompt is not None else None, **({} if cancel_ev is None else { 'event': cancel_ev }))
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 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 _do_get_assertion(device, challenge, rp_id, allow_credentials, user_verification, extensions, result_queue, stop_event): """ Call get_assertion on an individual authenticator. :param device: The device to use :param challenge: Websafe encoded challenge by the server :param rp_id: Relying Party identifier :param allow_credentials: List of acceptable PKCSs :param user_verification: Requirements for user verification :param extensions: Possible extensions :param result_queue: Queue to write result to :param stop_event: Stop event for the authenticator :return: """ # initialize values client = Fido2Client(device, "https://" + rp_id) user_interaction = False assertions = None client_data = None filtered_allow_credentials = None response = AssertionResponseInformation(assertions, client_data, device.descriptor, user_interaction) # set user verification/ presence user_verification_required = Fido2ClientWrapper._uv_required( user_verification, device) user_presence_required = not user_verification_required # check which of the allow credentials are bound to the authenticator if allow_credentials: filtered_allow_credentials = \ Fido2ClientWrapper._filter_allow_credentials( client, challenge, rp_id, allow_credentials) if len(allow_credentials) > 0 and \ len(filtered_allow_credentials) == 0: result_queue.put((CommunicationObject.ASSERTION, response)) return # get the assertion of the authenticator try: user_interaction = True assertions, client_data = client.get_assertion( rp_id, challenge, filtered_allow_credentials, extensions, user_presence_required, user_verification_required, None, stop_event, None) except Exception: if stop_event.is_set(): user_interaction = False # insert result into the queue response = AssertionResponseInformation(assertions, client_data, device.descriptor, user_interaction) result_queue.put((CommunicationObject.ASSERTION, response))
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 = 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 _webauthn_get_assertion( device, webauthn_credential_request_options, duo_host, sid, webauthn_response, session, ssl_verification_enabled, cancel, rq, ): click.echo( "Activate your FIDO U2F / FIDO2 authenticator now: '{}'".format(device), err=True, ) client = Fido2Client(device, webauthn_credential_request_options["extensions"]["appid"]) try: assertion = client.get_assertion( webauthn_credential_request_options, event=cancel, ) authenticator_assertion_response = assertion.get_response(0) assertion_response = assertion.get_assertions()[0] webauthn_response["id"] = ( base64.urlsafe_b64encode(assertion_response.credential["id"]).decode("ascii").rstrip("=") ) # Strip trailing padding characters webauthn_response["rawId"] = webauthn_response["id"] webauthn_response["type"] = assertion_response.credential["type"] webauthn_response["authenticatorData"] = base64.urlsafe_b64encode(assertion_response.auth_data).decode("ascii") webauthn_response["clientDataJSON"] = base64.urlsafe_b64encode(authenticator_assertion_response["clientData"]).decode( "ascii" ) webauthn_response["signature"] = binascii.hexlify(assertion_response.signature).decode("ascii") webauthn_response["extensionResults"] = authenticator_assertion_response["extensionResults"] logging.debug("webauthn_response: {}".format(webauthn_response)) click.echo( "Got response from FIDO U2F / FIDO2 authenticator: '{}'".format(device), err=True, ) rq.put(_submit_webauthn_response(duo_host, sid, webauthn_response, session, ssl_verification_enabled)) except Exception as e: logging.debug("Got an exception while waiting for {}: {}".format(device, e)) if not cancel.is_set(): raise finally: # Cancel the other FIDO U2F / FIDO2 prompts cancel.set() device.close()
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 test_make_credential_wrong_app_id(self): dev = mock.Mock() dev.capabilities = CAPABILITY.CBOR client = Fido2Client(dev, APP_ID) try: client.make_credential( { 'id': 'bar.example.com', 'name': 'Invalid RP' }, user, challenge, timeout=1) self.fail('make_credential did not raise error') except ClientError as e: self.assertEqual(e.code, ClientError.ERR.BAD_REQUEST)
def test_make_credential_ctap2(self, PatchedCtap2): dev = mock.Mock() dev.capabilities = CAPABILITY.CBOR ctap2 = mock.MagicMock() ctap2.get_info.return_value = Info.from_dict(cbor.decode(_INFO_NO_PIN)) ctap2.info = ctap2.get_info() ctap2.make_credential.return_value = AttestationResponse.from_dict( cbor.decode(_MC_RESP)) PatchedCtap2.return_value = ctap2 client = Fido2Client(dev, APP_ID) response = client.make_credential( PublicKeyCredentialCreationOptions( rp, user, challenge, [{ "type": "public-key", "alg": -7 }], timeout=1000, authenticator_selection={"userVerification": "discouraged"}, )) self.assertIsInstance(response.attestation_object, AttestationObject) self.assertIsInstance(response.client_data, CollectedClientData) ctap2.make_credential.assert_called_with( response.client_data.hash, rp, user, [{ "type": "public-key", "alg": -7 }], None, None, None, None, None, event=mock.ANY, on_keepalive=mock.ANY, ) self.assertEqual(response.client_data.origin, APP_ID) self.assertEqual(response.client_data.type, "webauthn.create") self.assertEqual(response.client_data.challenge, challenge)
def test_make_credential_ctap2(self, PatchedCTAP2): dev = mock.Mock() dev.capabilities = CAPABILITY.CBOR ctap2 = mock.MagicMock() ctap2.get_info.return_value = Info(_INFO_NO_PIN) ctap2.make_credential.return_value = AttestationObject(_MC_RESP) PatchedCTAP2.return_value = ctap2 client = Fido2Client(dev, APP_ID) attestation, client_data = client.make_credential( PublicKeyCredentialCreationOptions( rp, user, challenge, [{ "type": "public-key", "alg": -7 }], timeout=1000, authenticator_selection={"userVerification": "discouraged"}, )) self.assertIsInstance(attestation, AttestationObject) self.assertIsInstance(client_data, ClientData) ctap2.get_info.assert_called_with() ctap2.make_credential.assert_called_with( client_data.hash, rp, user, [{ "type": "public-key", "alg": -7 }], None, None, None, None, None, mock.ANY, None, ) self.assertEqual(client_data.get("origin"), APP_ID) self.assertEqual(client_data.get("type"), "webauthn.create") self.assertEqual(client_data.challenge, challenge)
def test_make_credential_existing_key(self): dev = mock.Mock() dev.capabilities = CAPABILITY.CBOR client = Fido2Client(dev, APP_ID) client.ctap = mock.MagicMock() client.ctap.get_info.return_value = Info(_INFO_NO_PIN) client.ctap.make_credential.side_effect = CtapError( CtapError.ERR.CREDENTIAL_EXCLUDED) try: client.make_credential(rp, user, challenge, timeout=1) self.fail('make_credential did not raise error') except ClientError as e: self.assertEqual(e.code, ClientError.ERR.DEVICE_INELIGIBLE) client.ctap.get_info.assert_called_with() client.ctap.make_credential.assert_called_once()
def test_make_credential_wrong_app_id(self, PatchedCTAP2): dev = mock.Mock() dev.capabilities = CAPABILITY.CBOR ctap2 = mock.MagicMock() ctap2.get_info.return_value = Info(_INFO_NO_PIN) PatchedCTAP2.return_value = ctap2 client = Fido2Client(dev, APP_ID) try: client.make_credential( PublicKeyCredentialCreationOptions( {"id": "bar.example.com", "name": "Invalid RP"}, user, challenge, [{"type": "public-key", "alg": -7}], ) ) self.fail("make_credential did not raise error") except ClientError as e: self.assertEqual(e.code, ClientError.ERR.BAD_REQUEST)
def test_make_credential_wrong_app_id(self, PatchedCTAP2): dev = mock.Mock() dev.capabilities = CAPABILITY.CBOR ctap2 = mock.MagicMock() ctap2.get_info.return_value = Info(_INFO_NO_PIN) PatchedCTAP2.return_value = ctap2 client = Fido2Client(dev, APP_ID) try: client.make_credential( { 'id': 'bar.example.com', 'name': 'Invalid RP' }, user, challenge, timeout=1) self.fail('make_credential did not raise error') except ClientError as e: self.assertEqual(e.code, ClientError.ERR.BAD_REQUEST)
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_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