Exemple #1
0
def get_name(info: DeviceInfo, key_type: Optional[YUBIKEY]) -> str:
    """Determine the product name of a YubiKey"""
    usb_supported = info.supported_capabilities[TRANSPORT.USB]
    if not key_type:
        if info.serial is None and _fido_only(usb_supported):
            key_type = YUBIKEY.SKY
        elif info.version[0] == 3:
            key_type = YUBIKEY.NEO
        else:
            key_type = YUBIKEY.YK4

    device_name = key_type.value

    if key_type == YUBIKEY.SKY:
        if CAPABILITY.FIDO2 not in usb_supported:
            device_name = "FIDO U2F Security Key"  # SKY 1
        if info.has_transport(TRANSPORT.NFC):
            device_name = "Security Key NFC"
    elif key_type == YUBIKEY.YK4:
        if info.version[0] == 0:
            return "Yubikey (%d.%d.%d)" % info.version
        if _is_preview(info.version):
            device_name = "YubiKey Preview"
        elif is_fips_version(info.version):
            device_name = "YubiKey FIPS"
        elif usb_supported == CAPABILITY.OTP | CAPABILITY.U2F:
            device_name = "YubiKey Edge"
        elif info.version >= (5, 1, 0):
            if info.form_factor in (FORM_FACTOR.USB_A_BIO,
                                    FORM_FACTOR.USB_C_BIO):
                device_name = "YubiKey "
                if info.form_factor == FORM_FACTOR.USB_C_BIO:
                    device_name += "C "
                device_name += "BIO"
                if _fido_only(usb_supported):
                    device_name += " (FIDO Edition)"
            else:
                device_name = "YubiKey 5"
                if info.form_factor == FORM_FACTOR.USB_A_KEYCHAIN:
                    if info.has_transport(TRANSPORT.NFC):
                        device_name += " NFC"
                    else:
                        device_name += "A"
                elif info.form_factor == FORM_FACTOR.USB_A_NANO:
                    device_name += " Nano"
                elif info.form_factor == FORM_FACTOR.USB_C_KEYCHAIN:
                    device_name += "C"
                    if info.has_transport(TRANSPORT.NFC):
                        device_name += " NFC"
                elif info.form_factor == FORM_FACTOR.USB_C_NANO:
                    device_name += "C Nano"
                elif info.form_factor == FORM_FACTOR.USB_C_LIGHTNING:
                    device_name += "Ci"

    return device_name
Exemple #2
0
def get_name(info: DeviceInfo, key_type: Optional[YUBIKEY]) -> str:
    """Determine the product name of a YubiKey"""
    usb_supported = info.supported_applications[TRANSPORT.USB]
    if not key_type:
        if info.serial is None and _fido_only(usb_supported):
            key_type = YUBIKEY.SKY
        elif info.version[0] == 3:
            key_type = YUBIKEY.NEO
        else:
            key_type = YUBIKEY.YK4

    device_name = key_type.value

    if key_type == YUBIKEY.SKY:
        if APPLICATION.FIDO2 not in usb_supported:
            device_name = "FIDO U2F Security Key"  # SKY 1
        if info.has_transport(TRANSPORT.NFC):
            device_name = "Security Key NFC"
    elif key_type == YUBIKEY.YK4:
        if usb_supported == APPLICATION.OTP | APPLICATION.U2F:
            device_name = "YubiKey Edge"
        elif (5, 0, 0) <= info.version < (5, 1, 0) or info.version in [
            (5, 2, 0),
            (5, 2, 1),
            (5, 2, 2),
        ]:
            device_name = "YubiKey Preview"
        elif info.version >= (5, 1, 0):
            device_name = "YubiKey 5"
            if info.form_factor == FORM_FACTOR.USB_A_KEYCHAIN:
                if info.has_transport(TRANSPORT.NFC):
                    device_name += " NFC"
                else:
                    device_name += "A"
            elif info.form_factor == FORM_FACTOR.USB_A_NANO:
                device_name += " Nano"
            elif info.form_factor == FORM_FACTOR.USB_C_KEYCHAIN:
                device_name += "C"
                if info.has_transport(TRANSPORT.NFC):
                    device_name += " NFC"
            elif info.form_factor == FORM_FACTOR.USB_C_NANO:
                device_name += "C Nano"
            elif info.form_factor == FORM_FACTOR.USB_C_LIGHTNING:
                device_name += "Ci"

        elif is_fips_version(info.version):
            device_name = "YubiKey FIPS"

    return device_name
Exemple #3
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,
        )
Exemple #4
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,
        )
Exemple #5
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,
    )
def info(form_factor):
    return DeviceInfo(
        config=None,
        serial=None,
        version=(5, 3, 0),
        form_factor=form_factor,
        supported_capabilities={TRANSPORT.USB: 0xFF},
        is_locked=False,
        is_fips=False,
    )
Exemple #7
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}"]
Exemple #8
0
def info(form_factor):
    return DeviceInfo(
        config=cast(DeviceConfig, None),
        serial=None,
        version=Version(5, 3, 0),
        form_factor=form_factor,
        supported_capabilities={TRANSPORT.USB: 0xFF},  # type: ignore
        is_locked=False,
        is_fips=False,
    )
Exemple #9
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,
    )
Exemple #10
0
def get_name(info: DeviceInfo, key_type: Optional[YUBIKEY]) -> str:
    """Determine the product name of a YubiKey"""
    usb_supported = info.supported_capabilities[TRANSPORT.USB]
    if not key_type:
        if info.serial is None and _fido_only(usb_supported):
            key_type = YUBIKEY.SKY
        elif info.version[0] == 3:
            key_type = YUBIKEY.NEO
        else:
            key_type = YUBIKEY.YK4

    device_name = key_type.value

    if key_type == YUBIKEY.SKY:
        if CAPABILITY.FIDO2 not in usb_supported:
            device_name = "FIDO U2F Security Key"  # SKY 1
        if info.has_transport(TRANSPORT.NFC):
            device_name = "Security Key NFC"
    elif key_type == YUBIKEY.YK4:
        if info.version[0] == 0:
            return "Yubikey (%d.%d.%d)" % info.version
        if _is_preview(info.version):
            device_name = "YubiKey Preview"
        elif is_fips_version(info.version):  # YK4 FIPS
            device_name = "YubiKey FIPS"
        elif usb_supported == CAPABILITY.OTP | CAPABILITY.U2F:
            device_name = "YubiKey Edge"
        elif info.version >= (5, 1, 0):
            is_nano = info.form_factor in (
                FORM_FACTOR.USB_A_NANO,
                FORM_FACTOR.USB_C_NANO,
            )
            is_bio = info.form_factor in (FORM_FACTOR.USB_A_BIO,
                                          FORM_FACTOR.USB_C_BIO)
            is_c = info.form_factor in (  # Does NOT include Ci
                FORM_FACTOR.USB_C_KEYCHAIN,
                FORM_FACTOR.USB_C_NANO,
                FORM_FACTOR.USB_C_BIO,
            )

            name_parts = ["YubiKey"]
            if not is_bio:
                name_parts.append("5")
            if is_c:
                name_parts.append("C")
            elif info.form_factor == FORM_FACTOR.USB_C_LIGHTNING:
                name_parts.append("Ci")
            if is_nano:
                name_parts.append("Nano")
            if info.has_transport(TRANSPORT.NFC):
                name_parts.append("NFC")
            elif info.form_factor == FORM_FACTOR.USB_A_KEYCHAIN:
                name_parts.append("A")  # Only for non-NFC A Keychain.
            if is_bio:
                name_parts.append("Bio")
                if _fido_only(usb_supported):
                    name_parts.append("- FIDO Edition")
            if info.is_fips:
                name_parts.append("FIPS")
            device_name = " ".join(name_parts).replace("5 C", "5C").replace(
                "5 A", "5A")

    return device_name
Exemple #11
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,
    )