Example #1
0
uv = "discouraged"

if WindowsClient.is_available():
    # Use the Windows WebAuthn API if available
    client = WindowsClient("https://example.com")
else:
    # Locate a device
    dev = next(CtapHidDevice.list_devices(), None)
    if dev is not None:
        print("Use USB HID channel.")
        use_prompt = True
    else:
        try:
            from fido2.pcsc import CtapPcscDevice

            dev = next(CtapPcscDevice.list_devices(), None)
            print("Use NFC channel.")
        except Exception as e:
            print("NFC channel search error:", e)

    if not dev:
        print("No FIDO device found")
        sys.exit(1)

    # Set up a FIDO 2 client using the origin https://example.com
    client = Fido2Client(dev, "https://example.com")

    # Prefer UV if supported
    if client.info.options.get("uv"):
        uv = "preferred"
        print("Authenticator supports User Verification")
Example #2
0
def enumerate_devices():
    for dev in CtapHidDevice.list_devices():
        yield dev
    if CtapPcscDevice:
        for dev in CtapPcscDevice.list_devices():
            yield dev
Example #3
0
def _verify_authentication_status(duo_host, sid, duo_transaction_id, session,
                                  ssl_verification_enabled):
    responses = []
    while len(responses) < 10:
        status_for_url = "https://{}/frame/status".format(duo_host)
        response = session.post(
            status_for_url,
            verify=ssl_verification_enabled,
            headers=_headers,
            data={
                'sid': sid,
                'txid': duo_transaction_id
            }
        )
        logging.debug(u'''Request:
            * url: {}
            * headers: {}
        Response:
            * status: {}
            * headers: {}
            * body: {}
        '''.format(status_for_url, response.request.headers, response.status_code, response.headers,
                response.text))

        if response.status_code != 200:
            raise click.ClickException(
                u'Issues during second factor verification. The error response {}'.format(
                    response
                )
            )

        json_response = response.json()
        if json_response['stat'] != 'OK':
            raise click.ClickException(
                u'There was an issue during second factor verification. The error response: {}'.format(
                    response.text
                )
            )

        if json_response['response']['status_code'] not in ['answered', 'calling', 'pushed', 'u2f_sent']:
            raise click.ClickException(
                u'There was an issue during second factor verification. The error response: {}'.format(
                    response.text
                )
            )

        if json_response['response']['status_code'] in ['pushed', 'answered', 'allow']:
            return duo_transaction_id

        if json_response['response']['status_code'] == 'u2f_sent' and len(json_response['response']['u2f_sign_request']) > 0:
            u2f_sign_requests = json_response['response']['u2f_sign_request']

            # appId, challenge and session is the same for all requests, get them from the first
            u2f_app_id = u2f_sign_requests[0]['appId']
            u2f_challenge = u2f_sign_requests[0]['challenge']
            u2f_session_id = u2f_sign_requests[0]['sessionId']

            devices = list(CtapHidDevice.list_devices())
            if CtapPcscDevice:
                devices.extend(list(CtapPcscDevice.list_devices()))

            if not devices:
                click.echo("No FIDO U2F authenticator is eligible.")
                return "cancelled"

            threads = []
            u2f_response = {
                "sessionId": u2f_session_id
            }
            rq = queue.Queue()
            cancel = Event()
            for device in devices:
                t = Thread(
                    target=_u2f_sign,
                    args=(
                        device,
                        u2f_app_id,
                        u2f_challenge,
                        u2f_sign_requests,
                        duo_host,
                        sid,
                        u2f_response,
                        session,
                        ssl_verification_enabled,
                        cancel,
                        rq
                    )
                )
                t.daemon = True
                threads.append(t)
                t.start()

            # Wait for first answer
            return rq.get()

        responses.append(response.text)

    raise click.ClickException(
        u'There was an issue during second factor verification. The responses: {}'.format(
            responses
        )
    )
Example #4
0
def _verify_authentication_status(duo_host, sid, duo_transaction_id, session,
                                  ssl_verification_enabled):
    responses = []
    while len(responses) < 10:
        status_for_url = "https://{}/frame/status".format(duo_host)
        data = {'sid': sid, 'txid': duo_transaction_id}
        response = session.post(status_for_url,
                                verify=ssl_verification_enabled,
                                headers=_headers,
                                data=data)
        trace_http_request(response)

        if response.status_code != 200:
            raise click.ClickException(
                u'Issues during second factor verification. The error response {}'
                .format(response))

        json_response = response.json()
        if json_response['stat'] != 'OK':
            raise click.ClickException(
                u'There was an issue during second factor verification. The error response: {}'
                .format(response.text))

        if json_response['response']['status_code'] not in [
                'answered', 'calling', 'pushed', 'webauthn_sent'
        ]:
            raise click.ClickException(
                u'There was an issue during second factor verification. The error response: {}'
                .format(response.text))

        if json_response['response']['status_code'] in [
                'pushed', 'answered', 'allow'
        ]:
            return duo_transaction_id

        if json_response['response']['status_code'] == 'webauthn_sent' and len(
                json_response['response']
            ['webauthn_credential_request_options']) > 0:
            webauthn_credential_request_options = json_response['response'][
                'webauthn_credential_request_options']
            webauthn_credential_request_options[
                "challenge"] = base64.b64decode(
                    webauthn_credential_request_options["challenge"])
            for cred in webauthn_credential_request_options[
                    "allowCredentials"]:
                cred["id"] = base64.urlsafe_b64decode(
                    cred["id"] + "=="
                )  # Add arbitrary padding characters, unnecessary ones are ignored
                cred.pop("transports")

            webauthn_session_id = webauthn_credential_request_options.pop(
                'sessionId')

            devices = list(CtapHidDevice.list_devices())
            if CtapPcscDevice:
                devices.extend(list(CtapPcscDevice.list_devices()))

            if not devices:
                click.echo("No FIDO U2F / FIDO2 authenticator is eligible.")
                return "cancelled"

            threads = []
            webauthn_response = {"sessionId": webauthn_session_id}
            rq = queue.Queue()
            cancel = Event()
            for device in devices:
                t = Thread(target=_webauthn_get_assertion,
                           args=(device, webauthn_credential_request_options,
                                 duo_host, sid, webauthn_response, session,
                                 ssl_verification_enabled, cancel, rq))
                t.daemon = True
                threads.append(t)
                t.start()

            # Wait for first answer
            return rq.get()

        responses.append(response.text)

    raise click.ClickException(
        u'There was an issue during second factor verification. The responses: {}'
        .format(responses))
Example #5
0
def _verify_authentication_status(duo_host, sid, txid, session, ssl_verification_enabled):
    status_for_url = duo_host + "/frame/v4/status"

    responses = []
    while len(responses) < 10:
        data = {"sid": sid, "txid": txid}
        response = session.post(status_for_url, verify=ssl_verification_enabled, headers=_headers, data=data)
        trace_http_request(response)

        if response.status_code != 200:
            raise click.ClickException("Issues during second factor verification. The error response {}".format(response))

        json_response = response.json()
        if json_response["stat"] != "OK":
            raise click.ClickException(
                "There was an issue during second factor verification. The error response: {}".format(response.text)
            )

        if json_response["response"]["status_code"] not in [
            "answered",
            "calling",
            "pushed",
            "webauthn_sent",
        ]:
            raise click.ClickException(
                "There was an issue during second factor verification. The error response: {}".format(response.text)
            )

        if json_response["response"]["status_code"] in ["pushed", "answered", "allow"]:
            return txid

        if (
            json_response["response"]["status_code"] == "webauthn_sent"
            and len(json_response["response"]["webauthn_credential_request_options"]) > 0
        ):
            webauthn_credential_request_options = json_response["response"]["webauthn_credential_request_options"]
            webauthn_credential_request_options["challenge"] = base64.b64decode(webauthn_credential_request_options["challenge"])
            for cred in webauthn_credential_request_options["allowCredentials"]:
                cred["id"] = base64.urlsafe_b64decode(
                    cred["id"] + "=="
                )  # Add arbitrary padding characters, unnecessary ones are ignored
                cred.pop("transports")

            webauthn_session_id = webauthn_credential_request_options.pop("sessionId")

            devices = list(CtapHidDevice.list_devices())
            if CtapPcscDevice:
                devices.extend(list(CtapPcscDevice.list_devices()))

            if not devices:
                click.echo("No FIDO U2F / FIDO2 authenticator is eligible.")
                return "cancelled"

            threads = []
            webauthn_response = {"sessionId": webauthn_session_id}
            rq = queue.Queue()
            cancel = Event()
            for device in devices:
                t = Thread(
                    target=_webauthn_get_assertion,
                    args=(
                        device,
                        webauthn_credential_request_options,
                        duo_host,
                        sid,
                        webauthn_response,
                        session,
                        ssl_verification_enabled,
                        cancel,
                        rq,
                    ),
                )
                t.daemon = True
                threads.append(t)
                t.start()

            # Wait for first answer
            return rq.get()

        responses.append(response.text)

    raise click.ClickException("There was an issue during second factor verification. The responses: {}".format(responses))