Esempio n. 1
0
def challenge_response(serial, host, user, prompt, credential_id, challenge,
                       udp, pin):
    """Uses `hmac-secret` to implement a challenge-response mechanism.

    We abuse hmac-secret, which gives us `HMAC(K, hash(challenge))`, where `K`
    is a secret tied to the `credential_id`. We hash the challenge first, since
    a 32 byte value is expected (in original usage, it's a salt).

    This means that we first need to setup a credential_id; this depends on the
    specific authenticator used. To do this, use `nitropy fido2 make-credential`.

    If so desired, user and relying party can be changed from the defaults.

    The prompt can be suppressed using `--prompt ""`.
    """

    if not pin:
        pin = AskUser.hidden("Please provide pin: ")

    nkfido2.find().simple_secret(
        credential_id,
        challenge,
        host=host,
        user_id=user,
        serial=serial,
        prompt=prompt,
        output=True,
        udp=udp,
        pin=pin,
    )
Esempio n. 2
0
def _enter_bootloader(serial):
    from pynitrokey.fido2 import find
    p = find(serial)

    local_print("please use the button on the device to confirm")
    p.enter_bootloader_or_die()

    local_print("Nitrokey rebooted.  Reconnecting...")
    time.sleep(0.5)
    if find(serial) is None:
        local_critical(RuntimeError("Failed to reconnect!"))
Esempio n. 3
0
def reboot(serial):
    """Reboot.

    \b
    This implementation actually only works for bootloader reboot
    """

    # this implementation actually only works for bootloader
    # firmware doesn't have a reboot command
    from pynitrokey.fido2 import find
    find(serial).reboot()
Esempio n. 4
0
def reset(serial, yes):
    """Reset key - wipes all credentials!!!"""
    local_print("Reset is only possible 10secs after plugging in the device.",
                "Please (re-)plug in your Nitrokey FIDO2 now!")
    if yes or AskUser.yes_no(
            "Warning: Your credentials will be lost!!! continue?"):
        local_print(
            "Press key to confirm -- again, your credentials will be lost!!!")
        try:
            nkfido2.find(serial).reset()
        except CtapError as e:
            local_critical(
                f"Reset failed ({str(e)})",
                "Did you confirm with a key-press 10secs after plugging in?",
                "Please re-try...")
        local_print("....aaaand they're gone")
Esempio n. 5
0
def hexbytes(count, serial):
    """Output COUNT number of random bytes, hex-encoded."""

    if not 0 <= count <= 255:
        local_critical(
            f"Number of bytes must be between 0 and 255, you passed {count}")
    local_print(nkfido2.find(serial).get_rng(count).hex())
Esempio n. 6
0
def probe(serial, udp, hash_type, filename):
    """Calculate HASH"""

    # @todo: move to constsconf.py
    # all_hash_types = ("SHA256", "SHA512", "RSA2048", "Ed25519")
    all_hash_types = ("SHA256", "SHA512", "RSA2048")
    # @fixme: Ed25519 needs `nacl` dependency, which is not available currently?!

    if hash_type.upper() not in all_hash_types:
        local_critical(
            f"invalid [HASH_TYPE] provided: {hash_type}",
            f"use one of: {', '.join(all_hash_types)}",
        )

    data = open(filename, "rb").read()

    # < CTAPHID_BUFFER_SIZE
    # https://fidoalliance.org/specs/fido-v2.0-id-20180227/
    #             fido-client-to-authenticator-protocol-v2.0-id-20180227.html
    #             #usb-message-and-packet-structure
    # also account for padding (see data below....)
    # so 6kb is conservative

    # @todo: proper error/exception + cut in chunks?
    assert len(data) <= 6 * 1024

    p = nkfido2.find(serial, udp=udp)

    serialized_command = cbor.dumps({"subcommand": hash_type, "data": data})
    result = p.send_data_hid(SoloBootloader.HIDCommandProbe,
                             serialized_command)
    result_hex = result.hex()
    local_print(result_hex)

    # @todo: unreachable
    if hash_type == "Ed25519":
        # @fixme: mmmh, where to get `nacl` (python-libnacl? python-pynacl?)
        import nacl.signing

        # print(f"content from hex: {bytes.fromhex(result_hex[128:]).decode()}")
        local_print(
            f"content: {result[64:]}",
            f"content from hex: {bytes.fromhex(result_hex[128:])}",
            f"signature: {result[:128]}",
        )

        # verify_key = nacl.signing.VerifyKey(bytes.fromhex("c69995185efa20bf7a88139f5920335aa3d3e7f20464345a2c095c766dfa157a"))
        # @fixme: where does this 'magic-number' come from!?
        verify_key = nacl.signing.VerifyKey(
            bytes.fromhex(
                "c69995185efa20bf7a88139f5920335aa3d3e7f20464345a2c095c766dfa157a"
            ))
        try:
            verify_key.verify(result)
            local_print("verified!")
        except nacl.exceptions.BadSignatureError:
            local_print("failed verification!")
Esempio n. 7
0
def make_credential(serial, host, user, udp, prompt, pin):
    """Generate a credential.

    Pass `--prompt ""` to output only the `credential_id` as hex.
    """

    if not pin:
        pin = AskUser.hidden("Please provide pin: ")

    nkfido2.find().make_credential(
        host=host,
        user_id=user,
        serial=serial,
        output=True,
        prompt=prompt,
        udp=udp,
        pin=pin,
    )
Esempio n. 8
0
def feedkernel(count, serial):
    """Feed random bytes to /dev/random."""

    if os.name != "posix":
        local_critical("This is a Linux-specific command!")

    if not 0 <= count <= 255:
        local_critical(
            f"Number of bytes must be between 0 and 255, you passed {count}")

    p = nkfido2.find(serial)

    RNDADDENTROPY = 0x40085203

    entropy_info_file = "/proc/sys/kernel/random/entropy_avail"
    print(f"entropy before: 0x{open(entropy_info_file).read().strip()}")

    r = p.get_rng(count)

    # man 4 random

    # RNDADDENTROPY
    #       Add some additional entropy to the input pool, incrementing the
    #       entropy count. This differs from writing to /dev/random or
    #       /dev/urandom, which only adds some data but does not increment the
    #       entropy count. The following structure is used:

    #           struct rand_pool_info {
    #               int    entropy_count;
    #               int    buf_size;
    #               __u32  buf[0];
    #           };

    #       Here entropy_count is the value added to (or subtracted from) the
    #       entropy count, and buf is the buffer of size buf_size which gets
    #       added to the entropy pool.

    # maximum 8, tend to be pessimistic
    entropy_bits_per_byte = 2
    t = struct.pack(f"ii{count}s", count * entropy_bits_per_byte, count, r)

    try:
        with open("/dev/random", mode="wb") as fh:
            fcntl.ioctl(fh, RNDADDENTROPY, t)

    except PermissionError as e:
        local_critical(
            "insufficient permissions to use `fnctl.ioctl` on '/dev/random'",
            "please run 'nitropy' with proper permissions",
            e,
        )

    local_print(f"entropy after:  0x{open(entropy_info_file).read().strip()}")
Esempio n. 9
0
def status(serial, blink: bool):
    """Print device's status"""
    p = nkfido2.find(serial)
    t0 = time()
    while True:
        if time() - t0 > 5 and blink:
            p.wink()
        r = p.get_status()
        for b in r:
            local_print('{:#02d} '.format(b), end='')
        local_print("")
        sleep(0.3)
Esempio n. 10
0
def bootloader(serial, firmware):
    """Program via Nitrokey bootloader interface.

    \b
    FIRMWARE argument should be either a .hex or .json file.

    If the bootloader is verifying, the .json is needed containing
    a signature for the verifying key in the bootloader.

    If the bootloader is nonverifying, either .hex or .json can be used.

    DANGER: if you try to flash a firmware with signature that doesn't
    match the bootloader's verifying key, you will be stuck in bootloader
    mode until you find a signed firmware that does match.

    Enter bootloader mode using `nitropy fido2 util program aux enter-bootloader` first.
    """

    p = find(serial)
    try:
        p.use_hid()
        p.program_file(firmware)
    except CtapError as e:
        if e.code == CtapError.ERR.INVALID_COMMAND:
            local_print("Not in bootloader mode.  Attempting to switch...")
            local_print("Please confirm with button on key!")
        else:
            local_critical(e)

        p.enter_bootloader_or_die()

        local_print("Nitrokey rebooted.  Reconnecting...")
        time.sleep(2.0)

        find(serial)
        if p is None:
            local_critical("Cannot find Nitrokey device.")

        p.use_hid()
        p.program_file(firmware)
Esempio n. 11
0
def reboot(serial, udp):
    """Send reboot command to key (development command)"""
    local_print("Reboot", "Press key to confirm!")

    CTAP_REBOOT = 0x53
    dev = nkfido2.find(serial, udp=udp).dev
    try:
        dev.call(CTAP_REBOOT ^ 0x80, b'')

    except OSError:
        local_print("...done")
    except CtapError as e:
        local_critical(f"...failed ({str(e)})")
Esempio n. 12
0
def simple_secret(
    credential_id,
    secret_input,
    host="nitrokeys.dev",
    user_id="they",
    serial=None,
    prompt="Touch your authenticator to generate a response...",
    output=True,
    udp=False,
):
    user_id = user_id.encode()

    from pynitrokey.fido2 import find
    client = find(solo_serial=serial, udp=udp).client
    hmac_ext = HmacSecretExtension(client.ctap2)

    # rp = {"id": host, "name": "Example RP"}
    client.host = host
    client.origin = f"https://{client.host}"
    client.user_id = user_id
    # user = {"id": user_id, "name": "A. User"}
    credential_id = binascii.a2b_hex(credential_id)

    allow_list = [{"type": "public-key", "id": credential_id}]

    challenge = secrets.token_hex(32)

    h = hashlib.sha256()
    h.update(secret_input.encode())
    salt = h.digest()

    if prompt:
        print(prompt)

    assertions, client_data = client.get_assertion({
        "rpId":
        host,
        "challenge":
        challenge.encode("utf8"),
        "allowCredentials":
        allow_list,
        "extensions":
        hmac_ext.get_dict(salt),
    })

    assertion = assertions[0]  # Only one cred in allowList, only one response.
    response = hmac_ext.results_for(assertion.auth_data)[0]
    if output:
        print(response.hex())

    return response
Esempio n. 13
0
 def connect_and_flash():
     # reconnect and actually flash it...
     # fail after 5 attempts
     exc = None
     for _ in range(5):
         try:
             client = find(serial)
             client.use_hid()
             client.program_file(fw_fn)
             break
         except Exception as e:
             time.sleep(0.5)
             exc = e
             # todo log exception info from each failed iteration
     else:
         local_critical("problem flashing firmware:", exc)
Esempio n. 14
0
def make_credential(
    host="nitrokeys.dev",
    user_id="they",
    serial=None,
    prompt="Touch your authenticator to generate a credential...",
    output=True,
    udp=False,
):
    user_id = user_id.encode()
    from pynitrokey.fido2 import find
    client = find(solo_serial=serial, udp=udp).client

    rp = {"id": host, "name": "Example RP"}
    client.host = host
    client.origin = f"https://{client.host}"
    client.user_id = user_id
    user = {"id": user_id, "name": "A. User"}
    challenge = secrets.token_hex(32)

    if prompt:
        print(prompt)

    hmac_ext = HmacSecretExtension(client.ctap2)

    attestation_object, client_data = client.make_credential({
        "rp":
        rp,
        "user":
        user,
        "challenge":
        challenge.encode("utf8"),
        "pubKeyCredParams": [{
            "type": "public-key",
            "alg": -7
        }],
        "extensions":
        hmac_ext.create_dict(),
    })

    credential = attestation_object.auth_data.credential_data
    credential_id = credential.credential_id
    if output:
        print(credential_id.hex())

    return credential_id
Esempio n. 15
0
def set_pin(serial):
    """Set pin of current key"""
    new_pin = AskUser.hidden("Please enter new pin: ")
    confirm_pin = AskUser.hidden("Please confirm new pin: ")
    if new_pin != confirm_pin:
        local_critical("new pin does not match confirm-pin",
                       "please try again!",
                       support_hint=False)
    try:
        # @fixme: move this (function) into own fido2-client-class
        client = nkfido2.find(serial).client
        PIN(client.ctap2).set_pin(new_pin)
        local_print("done - please use new pin to verify key")

    except Exception as e:
        local_critical("failed setting new pin, maybe it's already set?",
                       "to change an already set pin, please use:",
                       "$ nitropy fido2 change-pin", e)
Esempio n. 16
0
def change_pin(serial):
    """Change pin of current key"""

    old_pin = AskUser.hidden("Please enter old pin: ")
    new_pin = AskUser.hidden("Please enter new pin: ")
    confirm_pin = AskUser.hidden("Please confirm new pin: ")

    if new_pin != confirm_pin:
        local_critical("new pin does not match confirm-pin",
                       "please try again!",
                       support_hint=False)
    try:
        # @fixme: move this (function) into own fido2-client-class
        client = nkfido2.find(serial).client
        PIN(client.ctap2).change_pin(old_pin, new_pin)
        local_print("done - please use new pin to verify key")

    except Exception as e:
        local_critical("failed changing to new pin!",
                       "did you set one already? or is it wrong?", e)
Esempio n. 17
0
def version(serial, udp):
    """Version of firmware on key."""

    try:
        res = nkfido2.find(serial, udp=udp).solo_version()
        major, minor, patch = res[:3]
        locked = ""
        if len(res) > 3:
            if res[3]:
                locked = "locked"
            else:
                locked = "unlocked"
        local_print(f"{major}.{minor}.{patch} {locked}")

    except pynitrokey.exceptions.NoSoloFoundError:
        local_critical("No Nitrokey found.",
                       "If you are on Linux, are your udev rules up to date?")

    # unused ???
    except (pynitrokey.exceptions.NoSoloFoundError, ApduError):
        local_critical(
            "Firmware is out of date (key does not know the NITROKEY_VERSION command)."
        )
Esempio n. 18
0
def bootloader_version(serial, pubkey):
    """Version of bootloader."""
    from pynitrokey.fido2 import find

    p = find(serial)

    if not p.is_solo_bootloader():
        local_print("Not in Bootloader Mode!")
        return
    else:
        local_print("Detected Bootloader Mode")

    local_print("Version: " + ".".join(map(str, p.bootloader_version())))
    from binascii import b2a_hex
    from hashlib import sha256

    if pubkey:
        bpub = p.boot_pubkey()
        bpub = b2a_hex(bpub)
        local_print(f"Bootloader public key: \t\t{bpub}")
        s = sha256()
        s.update(bpub)
        bpubh = b2a_hex(s.digest())
        local_print(f"Bootloader public key sha256: \t{bpubh}")
Esempio n. 19
0
def update(serial, yes):
    """Update Nitrokey key to latest firmware version."""

    # @fixme: print this and allow user to cancel (if not -y is active)
    #update_url = 'https://update.nitrokey.com/'
    #print('Please use {} to run the firmware update'.format(update_url))
    #return

    IS_LINUX = platform.system() == "Linux"

    logger.debug(f"Start session {datetime.now()}")

    # @fixme: move to generic startup stuff logged into file exclusively!
    local_print("Nitrokey FIDO2 firmware update tool",
                f"Platform: {platform.platform()}",
                f"System: {platform.system()}, is_linux: {IS_LINUX}",
                f"Python: {platform.python_version()}",
                f"Saving run log to: {LOG_FN}", "",
                f"Starting update procedure for Nitrokey FIDO2...")

    from pynitrokey.fido2 import find

    # Determine target key
    client = None
    try:
        client = find(serial)

    except pynitrokey.exceptions.NoSoloFoundError as e:
        local_critical(None,
            "No Nitrokey key found!", e, None,
            "If you are on Linux, are your udev rules up to date?",
            "For more, see: ",
            "  https://www.nitrokey.com/documentation/installation#os:linux",
            None)

    except pynitrokey.exceptions.NonUniqueDeviceError as e:
        local_critical(None,
            "Multiple Nitrokey keys are plugged in!", e, None,
            "Please unplug all but one key", None)

    except Exception as e:
        local_critical(None, "Unhandled error connecting to key", e, None)

    # determine asset url: we want the (signed) json file
    # @fixme: move to confconsts.py ...
    api_base_url = "https://api.github.com/repos"
    api_url = f"{api_base_url}/Nitrokey/nitrokey-fido2-firmware/releases/latest"
    try:
        gh_release_data = json.loads(requests.get(api_url).text)
    except Exception as e:
        local_critical("Failed downloading firmware", e)

    # search asset with `fn` suffix being .json and take its url
    assets = [(x["name"], x["browser_download_url"]) \
              for x in gh_release_data["assets"]]
    download_url = None
    for fn, url in assets:
        if fn.endswith(".json"):
            download_url = url
            break
    if not download_url:
        local_critical("Failed to determine latest release (url)",
                       "assets:", *map(str, assets))

    # download asset url
    # @fixme: move to confconsts.py ...
    local_print(f"Downloading latest firmware: {gh_release_data['tag_name']} "
                f"(published at {gh_release_data['published_at']})")
    tmp_dir = tempfile.gettempdir()
    fw_fn = os.path.join(tmp_dir, "fido2_firmware.json")
    try:
        with open(fw_fn, "wb") as fd:
            firmware = requests.get(download_url)
            fd.write(firmware.content)
    except Exception as e:
        local_critical("Failed downloading firmware", e)

    local_print(f"Firmware saved to {fw_fn}",
                f"Downloaded firmware version: {gh_release_data['tag_name']}")

    # @fixme: whyyyyy is this here, move away... (maybe directly next to `fido2.find()`)
    def get_dev_details():

        # @fixme: why not use `find` here...
        from pynitrokey.fido2 import find_all
        c = find_all()[0]

        _props = c.dev.descriptor
        local_print(f"Device connected:")
        if "serial_number" in _props:
            local_print(f"{_props['serial_number']}: {_props['product_string']}")
        else:
            local_print(f"{_props['path']}: {_props['product_string']}")

        version_raw = c.solo_version()
        major, minor, patch = version_raw[:3]
        locked = "" if len(version_raw) > 3 and version_raw[3] else "unlocked"

        local_print(f"Firmware version: {major}.{minor}.{patch} {locked}", None)

    get_dev_details()

    # ask for permission
    if not yes:
        local_print("This will update your Nitrokey FIDO2")
        if not AskUser.strict_yes_no("Do you want to continue?"):
            local_critical("exiting due to user input...", support_hint=False)

    # Ensure we are in bootloader mode
    if client.is_solo_bootloader():
        local_print("Key already in bootloader mode, continuing...")
    else:
        try:
            local_print("Entering bootloader mode, please confirm with button on key!")
            client.enter_bootloader_or_die()
            time.sleep(0.5)
        except Exception as e:
            local_critical("problem switching to bootloader mode:", e)

    # reconnect and actually flash it...
    try:
        from pynitrokey.fido2 import find
        client = find(serial)
        client.use_hid()
        client.program_file(fw_fn)

    except Exception as e:
        local_critical("problem flashing firmware:", e)

    local_print(None, "After update check")
    tries = 100
    for i in range(tries):
        try:
            get_dev_details()
            break
        except Exception as e:
            if i > tries-1:
                local_critical("Could not connect to device after update", e)
                raise
            time.sleep(0.5)

    local_print("Congratulations, your key was updated to the latest firmware.")
    logger.debug("Finishing session {}".format(datetime.now()))
    local_print("Log saved to: {}".format(LOG_FN))
Esempio n. 20
0
def leave_bootloader(serial):
    """Switch from Nitrokey bootloader to Nitrokey firmware."""
    from pynitrokey.fido2 import find

    find(serial).reboot()
Esempio n. 21
0
def update(serial, yes, force):
    """Update Nitrokey key to latest firmware version."""

    # @fixme: print this and allow user to cancel (if not -y is active)
    # update_url = 'https://update.nitrokey.com/'
    # print('Please use {} to run the firmware update'.format(update_url))
    # return

    IS_LINUX = platform.system() == "Linux"

    logger.debug(f"Start session {datetime.now()}")

    # @fixme: move to generic startup stuff logged into file exclusively!
    local_print(
        "Nitrokey FIDO2 firmware update tool",
        f"Platform: {platform.platform()}",
        f"System: {platform.system()}, is_linux: {IS_LINUX}",
        f"Python: {platform.python_version()}",
        f"Saving run log to: {LOG_FN}",
        "",
        f"Starting update procedure for Nitrokey FIDO2...",
    )

    from pynitrokey.fido2 import find

    # Determine target key
    client = None
    try:
        client = find(serial)

    except pynitrokey.exceptions.NoSoloFoundError as e:
        local_critical(
            None,
            "No Nitrokey key found!",
            e,
            None,
            "If you are on Linux, are your udev rules up to date?",
            "For more, see: ",
            "  https://docs.nitrokey.com/fido2/linux/index.html#troubleshooting",
            None,
        )

    except pynitrokey.exceptions.NonUniqueDeviceError as e:
        local_critical(
            None,
            "Multiple Nitrokey keys are plugged in!",
            e,
            None,
            "Please unplug all but one key",
            None,
        )

    except Exception as e:
        local_critical(None, "Unhandled error connecting to key", e, None)

    # determine asset url: we want the (signed) json file
    # @fixme: move to confconsts.py ...
    api_base_url = "https://api.github.com/repos"
    api_url = f"{api_base_url}/Nitrokey/nitrokey-fido2-firmware/releases/latest"
    gh_release_data = None
    try:
        gh_release_data = json.loads(requests.get(api_url).text)
    except Exception as e:
        local_critical("Failed downloading firmware", e)

    # search asset with `fn` suffix being .json and take its url
    assets = [(x["name"], x["browser_download_url"])
              for x in gh_release_data["assets"]]
    download_url = None
    for fn, url in assets:
        if fn.endswith(".json"):
            download_url = url
            break
    if not download_url:
        local_critical("Failed to determine latest release (url)", "assets:",
                       *map(str, assets))

    import os.path

    local_print(
        f"Found latest firmware: {os.path.basename(download_url)}\n"
        f"\t\t(published at {gh_release_data['published_at']}, under tag {gh_release_data['tag_name']})"
    )

    ver = client.solo_version()
    local_print(f"\tCurrent Firmware version: {ver[0]}.{ver[1]}.{ver[2]}")

    # if the downloaded firmware version is the same as the current one, skip update unless force switch is provided
    # if f'firmware-{ver[0]}.{ver[1]}.{ver[2]}' in gh_release_data['tag_name'] and not force:
    if f"firmware-{ver[0]}.{ver[1]}.{ver[2]}" in download_url:
        if not force:
            local_critical(
                "Your firmware is up-to-date!\n"
                "Use --force flag to run update process anyway.",
                support_hint=False,
            )
        else:
            local_print(
                "Firmware is up-to-date. Continue due to --force switch applied."
            )

    def download_firmware():
        # download asset url
        # @fixme: move to confconsts.py ...
        local_print(
            f"Downloading latest firmware: {gh_release_data['tag_name']} "
            f"(published at {gh_release_data['published_at']})")
        tmp_dir = tempfile.gettempdir()
        fw_fn = os.path.join(tmp_dir, "fido2_firmware.json")
        try:
            with open(fw_fn, "wb") as fd:
                firmware = requests.get(download_url)
                fd.write(firmware.content)
        except Exception as e:
            local_critical("Failed downloading firmware", e)

        local_print(
            f"\tFirmware saved to {fw_fn}",
            f"\tDownloaded firmware version: {gh_release_data['tag_name']}",
        )
        return fw_fn

    fw_fn = download_firmware()

    # ask for permission
    if not yes:
        local_print("")
        local_print("This will update your Nitrokey FIDO2")
        if not AskUser.strict_yes_no("Do you want to continue?"):
            local_critical("exiting due to user input...", support_hint=False)

    # Ensure we are in bootloader mode
    if client.is_solo_bootloader():
        local_print("Key already in bootloader mode, continuing...")
    else:
        try:
            local_print(
                "Entering bootloader mode, please confirm with button on key! (long 10 second press)"
            )
            client.use_hid()
            client.enter_bootloader_or_die()
            time.sleep(0.5)
        except Exception as e:
            local_critical("problem switching to bootloader mode:", e)

    time.sleep(1.0)

    def connect_and_flash():
        # reconnect and actually flash it...
        # fail after 5 attempts
        exc = None
        for _ in range(5):
            try:
                client = find(serial)
                client.use_hid()
                client.program_file(fw_fn)
                break
            except Exception as e:
                time.sleep(0.5)
                exc = e
                # todo log exception info from each failed iteration
        else:
            local_critical("problem flashing firmware:", exc)

    connect_and_flash()

    local_print(None, "After update version check...")

    for _ in range(100):
        try:
            client = find(serial)
            new_ver = client.solo_version()
            local_print(
                f"New Firmware version: {new_ver[0]}.{new_ver[1]}.{new_ver[2]}"
            )
            break

        # expected until the devices comes up again
        except OSError:
            continue
        # unexpected...
        except Exception as e:
            local_print("unexpected error", e)
            break

    local_print(
        "Congratulations, your key was updated to the latest firmware.")
    logger.debug("Finishing session {}".format(datetime.now()))
    local_print("Log saved to: {}".format(LOG_FN))
Esempio n. 22
0
def bootloader_version(serial):
    """Version of bootloader."""
    from pynitrokey.fido2 import find
    p = find(serial)
    local_print(".".join(map(str, p.bootloader_version())))
Esempio n. 23
0
def raw(serial):
    """Output raw entropy endlessly."""
    p = nkfido2.find(serial)
    while True:
        r = p.get_rng(255)
        sys.stdout.buffer.write(r)
Esempio n. 24
0
def verify(serial, udp):
    """Verify key is valid Nitrokey 'Start' or 'FIDO2' key."""

    #if not pin:
    #    pin = AskUser("PIN required: ", repeat=0, hide_input=True).ask()

    # Any longer and this needs to go in a submodule
    local_print("please press the button on your Nitrokey key")

    cert = None
    try:
        cert = nkfido2.find(serial, udp=udp).make_credential()

    except Fido2ClientError as e:
        cause = str(e.cause)
        # error 0x31
        if "PIN_INVALID" in cause:
            local_critical(
                "your key has a different PIN. Please try to remember it :)",
                e)

        # error 0x34 (power cycle helps)
        if "PIN_AUTH_BLOCKED" in cause:
            local_critical(
                "your key's PIN auth is blocked due to too many incorrect attempts.",
                "please plug it out and in again, then again!",
                "please be careful, after too many incorrect attempts, ",
                "   the key will fully block.", e)

        # error 0x32 (only reset helps)
        if "PIN_BLOCKED" in cause:
            local_critical("your key's PIN is blocked. ",
                           "to use it again, you need to fully reset it.",
                           "you can do this using: `nitropy fido2 reset`", e)

        # error 0x01
        if "INVALID_COMMAND" in cause:
            local_critical(
                "error getting credential, is your key in bootloader mode?",
                "try: `nitropy fido2 util program aux leave-bootloader`", e)

        # pin required error
        if "PIN required" in str(e):
            local_critical(
                "your key has a PIN set - pass it using `--pin <PIN>`", e)

        local_critical("unexpected Fido2Client (CTAP) error", e)

    except Exception as e:
        local_critical("unexpected error", e)

    hashdb = {
        b'd7a23679007fe799aeda4388890f33334aba4097bb33fee609c8998a1ba91bd3':
        "Nitrokey FIDO2 1.x",
        b'6d586c0b00b94148df5b54f4a866acd93728d584c6f47c845ac8dade956b12cb':
        "Nitrokey FIDO2 2.x",
        b'e1f40563be291c30bc3cc381a7ef46b89ef972bdb048b716b0a888043cf9072a':
        "Nitrokey FIDO2 Dev 2.x ",
    }

    dev_fingerprint = cert.fingerprint(hashes.SHA256())
    a_hex = binascii.b2a_hex(dev_fingerprint)
    if a_hex in hashdb:
        local_print(f"found device: {hashdb[a_hex]}")
    else:
        local_print(f"unknown fingerprint! {a_hex}")
Esempio n. 25
0
def wink(serial, udp):
    """Send wink command to key (blinks LED a few times)."""

    nkfido2.find(serial, udp=udp).wink()