Beispiel #1
0
def _read_info_ctap(conn, key_type, interfaces):
    try:
        mgmt = ManagementSession(conn)
        return mgmt.read_device_info()
    except Exception:  # SKY 1 or NEO
        version = (3, 0, 0)  # Guess, no way to know
        enabled_apps = {TRANSPORT.USB: APPLICATION.U2F}
        if USB_INTERFACE.CCID in interfaces:
            enabled_apps[TRANSPORT.USB] |= (
                APPLICATION.OPGP | APPLICATION.PIV | APPLICATION.OATH
            )
        if USB_INTERFACE.OTP in interfaces:
            enabled_apps[TRANSPORT.USB] |= APPLICATION.OTP

        supported_apps = {TRANSPORT.USB: APPLICATION.U2F}
        if key_type == YUBIKEY.NEO:
            supported_apps[TRANSPORT.USB] |= BASE_NEO_APPS
            supported_apps[TRANSPORT.NFC] = supported_apps[TRANSPORT.USB]
            enabled_apps[TRANSPORT.NFC] = supported_apps[TRANSPORT.NFC]

        return DeviceInfo(
            config=DeviceConfig(
                enabled_applications=enabled_apps,
                auto_eject_timeout=0,
                challenge_response_timeout=0,
                device_flags=0,
            ),
            serial=None,
            version=version,
            form_factor=FORM_FACTOR.USB_A_KEYCHAIN,
            supported_applications=supported_apps,
            is_locked=False,
        )
Beispiel #2
0
def _read_info_ctap(conn, key_type, interfaces):
    try:
        mgmt = ManagementSession(conn)
        return mgmt.read_device_info()
    except Exception:  # SKY 1 or NEO
        version = (3, 0, 0)  # Guess, no way to know

        supported_apps = {TRANSPORT.USB: CAPABILITY.U2F}
        if key_type == YUBIKEY.NEO:
            supported_apps[TRANSPORT.USB] |= BASE_NEO_APPS
            supported_apps[TRANSPORT.NFC] = supported_apps[TRANSPORT.USB]

        return DeviceInfo(
            config=DeviceConfig(
                enabled_capabilities={},  # Populated later
                auto_eject_timeout=0,
                challenge_response_timeout=0,
                device_flags=0,
            ),
            serial=None,
            version=version,
            form_factor=FORM_FACTOR.USB_A_KEYCHAIN,
            supported_capabilities=supported_apps,
            is_locked=False,
        )
Beispiel #3
0
def _read_info_otp(conn, key_type, interfaces):
    otp = None
    serial = None

    try:
        mgmt = ManagementSession(conn)
    except ApplicationNotAvailableError:
        otp = YubiOtpSession(conn)

    # Retry during potential reclaim timeout period (~3s).
    for _ in range(8):
        try:
            if otp is None:
                try:
                    return mgmt.read_device_info()  # Rejected while reclaim
                except NotSupportedError:
                    otp = YubiOtpSession(conn)
            serial = otp.get_serial(
            )  # Rejected if reclaim (or not API_SERIAL_VISIBLE)
            break
        except CommandRejectedError:
            sleep(0.5)  # Potential reclaim
    else:
        otp = YubiOtpSession(conn)

    # Synthesize info
    logger.debug("Unable to get info via Management application, use fallback")

    version = otp.version
    if key_type == YUBIKEY.NEO:
        usb_supported = BASE_NEO_APPS
        if USB_INTERFACE.FIDO in interfaces or version >= (3, 3, 0):
            usb_supported |= CAPABILITY.U2F
        capabilities = {
            TRANSPORT.USB: usb_supported,
            TRANSPORT.NFC: usb_supported,
        }
    elif key_type == YUBIKEY.YKP:
        capabilities = {
            TRANSPORT.USB: CAPABILITY.OTP | TRANSPORT.U2F,
        }
    else:
        capabilities = {
            TRANSPORT.USB: CAPABILITY.OTP,
        }

    return DeviceInfo(
        config=DeviceConfig(
            enabled_capabilities={},  # Populated later
            auto_eject_timeout=0,
            challenge_response_timeout=0,
            device_flags=0,
        ),
        serial=serial,
        version=version,
        form_factor=FORM_FACTOR.UNKNOWN,
        supported_capabilities=capabilities.copy(),
        is_locked=False,
    )
Beispiel #4
0
def config(ctx):
    """
    Enable/Disable applications.

    The applications may be enabled and disabled independently
    over different transports (USB and NFC). The configuration may
    also be protected by a lock code.

    Examples:

    \b
      Disable PIV over NFC:
      $ ykman config nfc --disable PIV

    \b
      Enable all applications over USB:
      $ ykman config usb --enable-all

    \b
      Generate and set a random application lock code:
      $ ykman config set-lock-code --generate
    """
    info = ctx.obj["info"]
    if info.version < (5, 0, 0):
        ctx.fail("Configuring applications is not supported on this YubiKey. "
                 "Use the `mode` command to configure USB interfaces.")
    ctx.obj["controller"] = ManagementSession(ctx.obj["conn"])
    def set_mode(self, interfaces):
        interfaces_enabled = 0x00
        for usb_interface in interfaces:
            interfaces_enabled |= USB_INTERFACE[usb_interface]

        with self._open_device() as conn:
            try:
                session = ManagementSession(conn)
                session.set_mode(Mode(interfaces_enabled))

            except ValueError as e:
                if str(e) == 'Configuration locked!':
                    return failure('interface_config_locked')
                raise

            return success()
Beispiel #6
0
def mgmt_info(conn):
    try:
        raw_info = ManagementSession(conn).backend.read_config()
        info = DeviceInfo.parse(raw_info, Version(0, 0, 0))
        return [
            f"\t{info}",
            f"\tRawInfo: {raw_info.hex()}",
        ]
    except Exception as e:
        return [f"\tFailed to read device info: {e}"]
Beispiel #7
0
def _read_info_otp(conn, key_type, interfaces):
    try:
        mgmt = ManagementSession(conn)
        return mgmt.read_device_info()
    except (ApplicationNotAvailableError, NotSupportedError):
        logger.debug("Unable to get info via Management application, use fallback")

    # Synthesize info
    version, serial = _otp_read_data(conn)

    if key_type == YUBIKEY.NEO:
        usb_supported = BASE_NEO_APPS
        if USB_INTERFACE.FIDO in interfaces or version >= (3, 3, 0):
            usb_supported |= APPLICATION.U2F
        applications = {
            TRANSPORT.USB: usb_supported,
            TRANSPORT.NFC: usb_supported,
        }
    elif key_type == YUBIKEY.YKP:
        applications = {
            TRANSPORT.USB: APPLICATION.OTP | TRANSPORT.U2F,
        }
    else:
        applications = {
            TRANSPORT.USB: APPLICATION.OTP,
        }

    return DeviceInfo(
        config=DeviceConfig(
            enabled_applications=applications.copy(),
            auto_eject_timeout=0,
            challenge_response_timeout=0,
            device_flags=0,
        ),
        serial=serial,
        version=version,
        form_factor=FORM_FACTOR.UNKNOWN,
        supported_applications=applications.copy(),
        is_locked=False,
    )
Beispiel #8
0
    def write_config(self, usb_applications, nfc_applications, lock_code):
        usb_enabled = 0x00
        nfc_enabled = 0x00
        for app in usb_applications:
            usb_enabled |= CAPABILITY [app]
        for app in nfc_applications:
            nfc_enabled |= CAPABILITY [app]

        with self._open_device() as conn:

            if lock_code:
                lock_code = a2b_hex(lock_code)
                if len(lock_code) != 16:
                    return failure('lock_code_not_16_bytes')

            try:
                session = ManagementSession(conn)
                session.write_device_config(
                    DeviceConfig(
                        {TRANSPORT.USB: usb_enabled,
                        TRANSPORT.NFC: nfc_enabled},
                        None,
                        None,
                        None,
                    ),
                    True,
                    lock_code)

                self._state = None
            except ApduError as e:
                if (e.sw == SW.VERIFY_FAIL_NO_RETRY):
                    return failure('wrong_lock_code')
                raise
            except ValueError as e:
                if str(e) == 'Configuration locked!':
                    return failure('interface_config_locked')
                raise

            return success()
Beispiel #9
0
 def call():
     otp = session
     if need_reboot:
         protocol = session.backend.protocol
         if transport == TRANSPORT.NFC:
             protocol.connection.connection.disconnect()
             conn = protocol.connection
             conn.connection.connect()
         else:
             ManagementSession(protocol.connection).write_device_config(reboot=True)
             await_reboot()
             conn = connect_to_device(info.serial, [SmartCardConnection])[0]
         otp = YubiOtpSession(conn)
         session.backend = otp.backend
     return otp.get_config_state()
Beispiel #10
0
def mgmt_info(pid, conn):
    lines = []
    try:
        raw_info = ManagementSession(conn).backend.read_config()
        lines.append(f"\tRawInfo: {raw_info.hex()}")
    except Exception as e:
        lines.append(f"\tFailed to read device info via Management: {e!r}")
    try:
        info = read_info(pid, conn)
        lines.append(f"\t{info}")
        name = get_name(info, pid.get_type())
        lines.append(f"\tDevice name: {name}")
    except Exception as e:
        lines.append(f"\tFailed to read device info: {e!r}")
    return lines
Beispiel #11
0
def config(ctx):
    """
    Enable or disable applications.

    The applications may be enabled and disabled independently
    over different transports (USB and NFC). The configuration may
    also be protected by a lock code.

    Examples:

    \b
      Disable PIV over NFC:
      $ ykman config nfc --disable PIV

    \b
      Enable all applications over USB:
      $ ykman config usb --enable-all

    \b
      Generate and set a random application lock code:
      $ ykman config set-lock-code --generate
    """
    ctx.obj["controller"] = ManagementSession(ctx.obj["conn"])
Beispiel #12
0
def _read_info_ccid(conn, key_type, interfaces):
    try:
        mgmt = ManagementSession(conn)
        version = mgmt.version
        try:
            return mgmt.read_device_info()
        except NotSupportedError:
            # Workaround to "de-select" the Management Applet needed for NEO
            conn.send_and_receive(b"\xa4\x04\x00\x08")
    except ApplicationNotAvailableError:
        logger.debug("Unable to select Management application, use fallback.")
        version = None

    # Synthesize data
    capabilities = CAPABILITY(0)

    # Try to read serial (and version if needed) from OTP application
    try:
        otp_version, serial = _otp_read_data(conn)
        capabilities |= CAPABILITY.OTP
        if version is None:
            version = otp_version
    except ApplicationNotAvailableError:
        logger.debug("Unable to select OTP application")
        serial = None

    if version is None:
        version = (3, 0, 0)  # Guess, no way to know

    # Scan for remaining capabilities
    protocol = SmartCardProtocol(conn)
    for aid, code in SCAN_APPLETS.items():
        try:
            logger.debug("Check for %s", code)
            protocol.select(aid)
            capabilities |= code
            logger.debug("Found applet: aid: %s, capability: %s", aid, code)
        except ApplicationNotAvailableError:
            logger.debug("Missing applet: aid: %s, capability: %s", aid, code)
        except Exception as e:
            logger.error(
                "Error selecting aid: %s, capability: %s",
                aid,
                code,
                exc_info=e,
            )

    # Assume U2F on devices >= 3.3.0
    if USB_INTERFACE.FIDO in interfaces or version >= (3, 3, 0):
        capabilities |= CAPABILITY.U2F

    return DeviceInfo(
        config=DeviceConfig(
            enabled_capabilities={},  # Populated later
            auto_eject_timeout=0,
            challenge_response_timeout=0,
            device_flags=0,
        ),
        serial=serial,
        version=version,
        form_factor=FORM_FACTOR.UNKNOWN,
        supported_capabilities={
            TRANSPORT.USB: capabilities,
            TRANSPORT.NFC: capabilities,
        },
        is_locked=False,
    )
Beispiel #13
0
def mode(ctx, mode, touch_eject, autoeject_timeout, chalresp_timeout, force):
    """
    Manage connection modes (USB Interfaces).

    Get the current connection mode of the YubiKey, or set it to MODE.

    MODE can be a string, such as "OTP+FIDO+CCID", or a shortened form: "o+f+c".
    It can also be a mode number.

    Examples:

    \b
      Set the OTP and FIDO mode:
      $ ykman mode OTP+FIDO

    \b
      Set the CCID only mode and use touch to eject the smart card:
      $ ykman mode CCID --touch-eject
    """
    info = ctx.obj["info"]
    mgmt = ManagementSession(ctx.obj["conn"])
    usb_enabled = info.config.enabled_applications[TRANSPORT.USB]
    my_mode = _mode_from_usb_enabled(usb_enabled)
    usb_supported = info.supported_applications[TRANSPORT.USB]
    interfaces_supported = _mode_from_usb_enabled(usb_supported).interfaces
    pid = ctx.obj["pid"]
    if pid:
        key_type = pid.get_type()
    else:
        key_type = None

    if autoeject_timeout:
        touch_eject = True
    autoeject = autoeject_timeout if touch_eject else 0

    if mode is not None:
        if mode.interfaces != USB_INTERFACE.CCID:
            if touch_eject:
                ctx.fail("--touch-eject can only be used when setting"
                         " CCID-only mode")

        if not force:
            if mode == my_mode:
                click.echo("Mode is already {}, nothing to do...".format(mode))
                ctx.exit()
            elif key_type in (YUBIKEY.YKS, YUBIKEY.YKP):
                click.echo("Mode switching is not supported on this YubiKey!")
                ctx.fail("Use --force to attempt to set it anyway.")
            elif mode.interfaces not in interfaces_supported:
                click.echo(
                    "Mode {} is not supported on this YubiKey!".format(mode))
                ctx.fail("Use --force to attempt to set it anyway.")
            force or click.confirm("Set mode of YubiKey to {}?".format(mode),
                                   abort=True,
                                   err=True)

        try:
            mgmt.set_mode(mode, chalresp_timeout, autoeject)
            click.echo("Mode set! You must remove and re-insert your YubiKey "
                       "for this change to take effect.")
        except Exception as e:
            logger.debug("Failed to switch mode", exc_info=e)
            click.echo("Failed to switch mode on the YubiKey. Make sure your "
                       "YubiKey does not have an access code set.")

    else:
        click.echo("Current connection mode is: {}".format(my_mode))
        mode = _mode_from_usb_enabled(
            info.supported_applications[TRANSPORT.USB])
        supported = ", ".join(t.name for t in USB_INTERFACE
                              if t in mode.interfaces)
        click.echo("Supported USB interfaces are: {}".format(supported))