예제 #1
0
파일: sk.py 프로젝트: velavokr/asyncssh
def _ctap2_enroll(dev, alg, application, user, pin, resident):
    """Enroll a new security key using CTAP version 2"""

    ctap2 = Ctap2(dev)

    application = application.decode('utf-8')
    rp = {'id': application, 'name': application}
    user = {'id': user.encode('utf-8'), 'name': user}
    key_params = [{'type': 'public-key', 'alg': alg}]
    options = {'rk': resident}

    if pin:
        pin_protocol = PinProtocolV1()
        pin_token = ClientPin(ctap2, pin_protocol).get_pin_token(pin)
        pin_auth = hmac.new(pin_token, _dummy_hash, sha256).digest()[:16]
    else:
        pin_protocol = None
        pin_auth = None

    cred = ctap2.make_credential(_dummy_hash, rp, user, key_params,
                                 options=options, pin_uv_param=pin_auth,
                                 pin_uv_protocol=pin_protocol)
    cdata = cred.auth_data.credential_data

    return _decode_public_key(alg, cdata.public_key), cdata.credential_id
예제 #2
0
def fido_info():
    lines = []
    lines.append("Detected YubiKeys over HID FIDO:")
    for dev in list_ctap_devices():
        lines.append(f"\t{dev!r}")
        try:
            with dev.open_connection(FidoConnection) as conn:
                lines.append("CTAP device version: %d.%d.%d" %
                             conn.device_version)
                lines.append(f"CTAPHID protocol version: {conn.version}")
                lines.append("Capabilities: %d" % conn.capabilities)
                lines.extend(mgmt_info(conn))
                try:
                    ctap2 = Ctap2(conn)
                    lines.append(f"\tCtap2Info: {ctap2.info.data!r}")
                    if ctap2.info.options.get("clientPin"):
                        client_pin = ClientPin(ctap2)
                        lines.append(
                            f"PIN retries: {client_pin.get_pin_retries()}")
                        bio_enroll = ctap2.info.options.get("bioEnroll")
                        if bio_enroll:
                            lines.append(
                                f"Fingerprint retries: {client_pin.get_uv_retries()}"
                            )
                        elif bio_enroll is False:
                            lines.append("Fingerprints: Not configured")
                    else:
                        lines.append("PIN: Not configured")

                except (ValueError, CtapError) as e:
                    lines.append(f"\tCouldn't get info: {e}")
        except Exception as e:
            lines.append(f"\tFIDO connection failure: {e}")
        lines.append("")
    return lines
예제 #3
0
 def try_reset():
     if not force:
         dev = prompt_re_insert_key()
         conn = dev.open_connection(FidoConnection)
     with prompt_timeout():
         if is_fips:
             fips_reset(conn)
         else:
             Ctap2(conn).reset()
예제 #4
0
 def fido_reset(self):
     try:
         with self._open_device([FidoConnection]) as conn:
             ctap2 = Ctap2(conn)
             ctap2.reset()
             return success()
     except CtapError as e:
         if e.code == CtapError.ERR.NOT_ALLOWED:
             return failure('not allowed')
         if e.code == CtapError.ERR.ACTION_TIMEOUT:
             return failure('touch timeout')
         raise
예제 #5
0
 def fido_pin_retries(self):
     try:
         with self._open_device([FidoConnection]) as conn:
             ctap2 = Ctap2(conn)
             client_pin = ClientPin(ctap2)
             return success({'retries': client_pin.get_pin_retries()[0]})
     except CtapError as e:
         if e.code == CtapError.ERR.PIN_AUTH_BLOCKED:
             return failure('PIN authentication is currently blocked. '
                            'Remove and re-insert the YubiKey.')
         if e.code == CtapError.ERR.PIN_BLOCKED:
             return failure('PIN is blocked.')
         raise
예제 #6
0
 def fido_set_pin(self, new_pin):
     try:
         with self._open_device([FidoConnection]) as conn:
             ctap2 = Ctap2(conn)
             if len(new_pin) < ctap2.info.min_pin_length:
                 return failure('too short')
             client_pin = ClientPin(ctap2)
             client_pin.set_pin(new_pin)
             return success()
     except CtapError as e:
         if e.code == CtapError.ERR.INVALID_LENGTH or \
                 e.code == CtapError.ERR.PIN_POLICY_VIOLATION:
             return failure('too long')
         raise
예제 #7
0
파일: sk.py 프로젝트: velavokr/asyncssh
def _ctap2_sign(dev, message_hash, application, key_handle, touch_required):
    """Sign a message with a security key using CTAP version 2"""

    ctap2 = Ctap2(dev)

    application = application.decode('utf-8')
    allow_creds = [{'type': 'public-key', 'id': key_handle}]
    options = {'up': touch_required}

    assertion = ctap2.get_assertions(application, message_hash,
                                     allow_creds, options=options)[0]

    auth_data = assertion.auth_data

    return auth_data.flags, auth_data.counter, assertion.signature
예제 #8
0
def fido_info():
    lines = []
    lines.append("Detected YubiKeys over HID FIDO:")
    for dev in list_ctap_devices():
        lines.append(f"\t{dev!r}")
        with dev.open_connection(FidoConnection) as conn:
            lines.append("CTAP device version: %d.%d.%d" % conn.device_version)
            lines.append(f"CTAPHID protocol version: {conn.version}")
            lines.append("Capabilities: %d" % conn.capabilities)
            lines.extend(mgmt_info(conn))
            try:
                ctap2 = Ctap2(conn)
                lines.append(f"\tCtap2Info: {ctap2.info.data!r}")
            except (ValueError, CtapError) as e:
                lines.append(f"\tCouldn't get info: {e}")
        lines.append("")
    return lines
예제 #9
0
 def fido_change_pin(self, current_pin, new_pin):
     try:
         with self._open_device([FidoConnection]) as conn:
             ctap2 = Ctap2(conn)
             if len(new_pin) < ctap2.info.min_pin_length:
                 return failure('too short')
             client_pin = ClientPin(ctap2)
             client_pin.change_pin(current_pin, new_pin)
             return success()
     except CtapError as e:
         if e.code == CtapError.ERR.INVALID_LENGTH or \
                 e.code == CtapError.ERR.PIN_POLICY_VIOLATION:
             return failure('too long')
         if e.code == CtapError.ERR.PIN_INVALID:
             return failure('wrong pin')
         if e.code == CtapError.ERR.PIN_AUTH_BLOCKED:
             return failure('currently blocked')
         if e.code == CtapError.ERR.PIN_BLOCKED:
             return failure('blocked')
         raise
예제 #10
0
def fido(ctx):
    """
    Manage the FIDO applications.

    Examples:

    \b
      Reset the FIDO (FIDO2 and U2F) applications:
      $ ykman fido reset

    \b
      Change the FIDO2 PIN from 123456 to 654321:
      $ ykman fido access change-pin --pin 123456 --new-pin 654321

    """
    conn = ctx.obj["conn"]
    try:
        ctx.obj["ctap2"] = Ctap2(conn)
    except (ValueError, CtapError) as e:
        logger.info("FIDO device does not support CTAP2: %s", e)
예제 #11
0
파일: sk.py 프로젝트: velavokr/asyncssh
def sk_get_resident(application, user, pin):
    """Get keys resident on a security key"""

    app_hash = sha256(application).digest()
    result = []

    for dev in CtapHidDevice.list_devices():
        try:
            ctap2 = Ctap2(dev)

            pin_protocol = PinProtocolV1()
            pin_token = ClientPin(ctap2, pin_protocol).get_pin_token(pin)
            cred_mgmt = CredentialManagement(ctap2, pin_protocol, pin_token)

            for cred in cred_mgmt.enumerate_creds(app_hash):
                name = cred[CredentialManagement.RESULT.USER]['name']

                if user and name != user:
                    continue

                cred_id = cred[CredentialManagement.RESULT.CREDENTIAL_ID]
                key_handle = cred_id['id']

                public_key = cred[CredentialManagement.RESULT.PUBLIC_KEY]
                alg = public_key[3]
                public_value = _decode_public_key(alg, public_key)

                result.append((alg, name, public_value, key_handle))
        except CtapError as exc:
            if exc.code == CtapError.ERR.NO_CREDENTIALS:
                continue
            elif exc.code == CtapError.ERR.PIN_INVALID:
                raise ValueError('Invalid PIN') from None
            elif exc.code == CtapError.ERR.PIN_NOT_SET:
                raise ValueError('PIN not set') from None
            else:
                raise ValueError(str(exc)) from None
        finally:
            dev.close()

    return result
예제 #12
0
 def mock_ctap(self):
     device = mock.MagicMock()
     device.call.return_value = b"\0" + _INFO
     return Ctap2(device)
예제 #13
0
 def fido_has_pin(self):
     with self._open_device([FidoConnection]) as conn:
         ctap2 = Ctap2(conn)
         return success({'hasPin': ctap2.info.options.get("clientPin")})
예제 #14
0
NOTE: This uses a draft bio enrollment specification which is not yet final.
Consider this highly experimental.
"""
from __future__ import print_function, absolute_import, unicode_literals

from fido2.hid import CtapHidDevice
from fido2.ctap2 import Ctap2, ClientPin, FPBioEnrollment, CaptureError
from getpass import getpass
import sys

pin = None
uv = "discouraged"

for dev in CtapHidDevice.list_devices():
    try:
        ctap = Ctap2(dev)
        if "bioEnroll" in ctap.info.options:
            break
    except Exception:  # nosec
        continue
else:
    print("No Authenticator supporting bioEnroll found")
    sys.exit(1)

if not ctap.info.options.get("clientPin"):
    print("PIN not set for the device!")
    sys.exit(1)

# Authenticate with PIN
print("Preparing to enroll a new fingerprint.")
pin = getpass("Please enter PIN: ")
예제 #15
0
def reset(ctx, force):
    """
    Reset all FIDO applications.

    This action will wipe all FIDO credentials, including FIDO U2F credentials,
    on the YubiKey and remove the PIN code.

    The reset must be triggered immediately after the YubiKey is
    inserted, and requires a touch on the YubiKey.
    """

    conn = ctx.obj["conn"]

    if isinstance(conn, CtapPcscDevice):  # NFC
        readers = list_ccid(conn._name)
        if not readers or readers[0].reader.name != conn._name:
            logger.error(f"Multiple readers matched: {readers}")
            cli_fail("Unable to isolate NFC reader.")
        dev = readers[0]
        logger.debug(f"use: {dev}")
        is_fips = False

        def prompt_re_insert():
            click.echo(
                "Remove and re-place your YubiKey on the NFC reader to perform the "
                "reset...")

            removed = False
            while True:
                sleep(0.5)
                try:
                    with dev.open_connection(FidoConnection):
                        if removed:
                            sleep(1.0)  # Wait for the device to settle
                            break
                except CardConnectionException:
                    pass  # Expected, ignore
                except NoCardException:
                    removed = True
            return dev.open_connection(FidoConnection)

    else:  # USB
        n_keys = len(list_ctap_devices())
        if n_keys > 1:
            cli_fail("Only one YubiKey can be connected to perform a reset.")
        is_fips = is_fips_version(ctx.obj["info"].version)

        ctap2 = ctx.obj.get("ctap2")
        if not is_fips and not ctap2:
            cli_fail("This YubiKey does not support FIDO reset.")

        def prompt_re_insert():
            click.echo(
                "Remove and re-insert your YubiKey to perform the reset...")

            removed = False
            while True:
                sleep(0.5)
                keys = list_ctap_devices()
                if not keys:
                    removed = True
                if removed and len(keys) == 1:
                    return keys[0].open_connection(FidoConnection)

    if not force:
        if not click.confirm(
                "WARNING! This will delete all FIDO credentials, including FIDO U2F "
                "credentials, and restore factory settings. Proceed?",
                err=True,
        ):
            ctx.abort()
        if is_fips:
            destroy_input = click_prompt(
                "WARNING! This is a YubiKey FIPS device. This command will also "
                "overwrite the U2F attestation key; this action cannot be undone and "
                "this YubiKey will no longer be a FIPS compliant device.\n"
                'To proceed, please enter the text "OVERWRITE"',
                default="",
                show_default=False,
            )
            if destroy_input != "OVERWRITE":
                cli_fail("Reset aborted by user.")

        conn = prompt_re_insert()

    try:
        with prompt_timeout():
            if is_fips:
                fips_reset(conn)
            else:
                Ctap2(conn).reset()
    except CtapError as e:
        logger.error("Reset failed", exc_info=e)
        if e.code == CtapError.ERR.ACTION_TIMEOUT:
            cli_fail(
                "Reset failed. You need to touch your YubiKey to confirm the reset."
            )
        elif e.code in (CtapError.ERR.NOT_ALLOWED,
                        CtapError.ERR.PIN_AUTH_BLOCKED):
            cli_fail(
                "Reset failed. Reset must be triggered within 5 seconds after the "
                "YubiKey is inserted.")
        else:
            cli_fail(f"Reset failed: {e.code.name}")
    except ApduError as e:  # From fips_reset
        logger.error("Reset failed", exc_info=e)
        if e.code == SW.COMMAND_NOT_ALLOWED:
            cli_fail(
                "Reset failed. Reset must be triggered within 5 seconds after the "
                "YubiKey is inserted.")
        else:
            cli_fail("Reset failed.")
    except Exception as e:
        logger.error(e)
        cli_fail("Reset failed.")
예제 #16
0

def enumerate_devices():
    for dev in CtapHidDevice.list_devices():
        yield dev
    if CtapPcscDevice:
        for dev in CtapPcscDevice.list_devices():
            yield dev


for dev in enumerate_devices():
    print("CONNECT: %s" % dev)
    print("Product name: %s" % dev.product_name)
    print("Serial number: %s" % dev.serial_number)
    print("CTAPHID protocol version: %d" % dev.version)

    if dev.capabilities & CAPABILITY.CBOR:
        ctap2 = Ctap2(dev)
        info = ctap2.get_info()
        print("DEVICE INFO: %s" % info)
    else:
        print("Device does not support CBOR")

    if dev.capabilities & CAPABILITY.WINK:
        dev.wink()
        print("WINK sent!")
    else:
        print("Device does not support WINK")

    dev.close()