Exemplo n.º 1
0
    def test_establish_shared_secret(self, patched_generate):
        ctap = mock.MagicMock()
        ctap.info.options = {"clientPin": True}
        prot = ClientPin(ctap, PinProtocolV1())

        patched_generate.return_value = ec.derive_private_key(
            EC_PRIV, ec.SECP256R1(), default_backend())

        ctap.client_pin.return_value = {
            1: {
                1: 2,
                3: -25,
                -1: 1,
                -2: DEV_PUB_X,
                -3: DEV_PUB_Y
            }
        }

        key_agreement, shared = prot._get_shared_secret()

        self.assertEqual(shared, SHARED)
        self.assertEqual(key_agreement[-2], EC_PUB_X)
        self.assertEqual(key_agreement[-3], EC_PUB_Y)
Exemplo n.º 2
0
def verify(ctx, pin):
    """
    Verify the FIDO PIN against a YubiKey.

    For YubiKeys supporting FIDO2 this will reset the "retries" counter of the PIN.
    For YubiKey FIPS this will unlock the session, allowing U2F registration.
    """

    ctap2 = ctx.obj.get("ctap2")
    if ctap2:
        pin = _require_pin(ctx, pin)
        client_pin = ClientPin(ctap2)
        try:
            # Get a PIN token to verify the PIN.
            client_pin.get_pin_token(
                pin, ClientPin.PERMISSION.GET_ASSERTION, "ykman.example.com"
            )
        except CtapError as e:
            logger.error("PIN verification failed", exc_info=e)
            cli_fail(f"Error: {e}")
    elif is_fips_version(ctx.obj["info"].version):
        _fail_if_not_valid_pin(ctx, pin, True)
        try:
            fips_verify_pin(ctx.obj["conn"], pin)
        except ApduError as e:
            logger.error("PIN verification failed", exc_info=e)
            if e.code == SW.VERIFY_FAIL_NO_RETRY:
                cli_fail("Wrong PIN.")
            elif e.code == SW.AUTH_METHOD_BLOCKED:
                cli_fail("PIN is blocked.")
            elif e.code == SW.COMMAND_NOT_ALLOWED:
                cli_fail("PIN is not set.")
            else:
                cli_fail(f"PIN verification failed: {e.code.name}")
    else:
        cli_fail("This YubiKey does not support a FIDO PIN.")
    click.echo("PIN verified.")
Exemplo n.º 3
0
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
Exemplo n.º 4
0
def fido_info():
    lines = []
    lines.append("Detected YubiKeys over HID FIDO:")
    try:
        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(dev.pid, 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("Fingerprint retries: "
                                             f"{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!r}")
            except Exception as e:
                lines.append(f"\tFIDO connection failure: {e!r}")
            lines.append("")
    except Exception as e:
        lines.append(f"\tHID FIDO backend failure: {e!r}")
    return lines
Exemplo n.º 5
0
 def __init__(self, ctap_device):
     self.ctap = CTAP2(ctap_device)
     self.pin = ClientPin(self.ctap)
     self._info = self.ctap.get_info()
     self._pin = self._info.options["clientPin"]
Exemplo n.º 6
0
        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: ")
client_pin = ClientPin(ctap)
pin_token = client_pin.get_pin_token(pin, ClientPin.PERMISSION.BIO_ENROLL)
bio = FPBioEnrollment(ctap, client_pin.protocol, pin_token)

print(bio.enumerate_enrollments())

# Start enrollment
enroller = bio.enroll()
template_id = None
while template_id is None:
    print("Press your fingerprint against the sensor now...")
    try:
        template_id = enroller.capture()
        print(enroller.remaining, "more scans needed.")
    except CaptureError as e:
        print(e)
Exemplo n.º 7
0
def change_pin(ctx, pin, new_pin, u2f):
    """
    Set or change the PIN code.

    The FIDO2 PIN must be at least 4 characters long, and supports any type
    of alphanumeric characters.

    On YubiKey FIPS, a PIN can be set for FIDO U2F. That PIN must be at least
    6 characters long.
    """

    is_fips = is_fips_version(ctx.obj["info"].version)

    if is_fips and not u2f:
        cli_fail(
            "This is a YubiKey FIPS. To set the U2F PIN, pass the --u2f option."
        )

    if u2f and not is_fips:
        cli_fail(
            "This is not a YubiKey FIPS, and therefore does not support a U2F PIN. "
            "To set the FIDO2 PIN, remove the --u2f option.")

    if is_fips:
        conn = ctx.obj["conn"]
    else:
        ctap2 = ctx.obj.get("ctap2")
        if not ctap2:
            cli_fail("PIN is not supported on this YubiKey.")
        client_pin = ClientPin(ctap2)

    def prompt_new_pin():
        return click_prompt(
            "Enter your new PIN",
            hide_input=True,
            confirmation_prompt=True,
        )

    def change_pin(pin, new_pin):
        if pin is not None:
            _fail_if_not_valid_pin(ctx, pin, is_fips)
        try:
            if is_fips:
                try:
                    # Failing this with empty current PIN does not cost a retry
                    fips_change_pin(conn, pin or "", new_pin)
                except ApduError as e:
                    if e.code == SW.WRONG_LENGTH:
                        pin = _prompt_current_pin()
                        _fail_if_not_valid_pin(ctx, pin, is_fips)
                        fips_change_pin(conn, pin, new_pin)
                    else:
                        raise

            else:
                client_pin.change_pin(pin, new_pin)

        except CtapError as e:
            logger.error("Failed to change PIN", exc_info=e)
            if e.code == CtapError.ERR.PIN_POLICY_VIOLATION:
                cli_fail("New PIN doesn't meet policy requirements.")
            else:
                _fail_pin_error(ctx, e, "Failed to change PIN: %s")

        except ApduError as e:
            logger.error("Failed to change PIN", exc_info=e)
            if e.code == SW.VERIFY_FAIL_NO_RETRY:
                cli_fail("Wrong PIN.")
            elif e.code == SW.AUTH_METHOD_BLOCKED:
                cli_fail("PIN is blocked.")
            else:
                cli_fail(f"Failed to change PIN: SW={e.code:04x}")

    def set_pin(new_pin):
        _fail_if_not_valid_pin(ctx, new_pin, is_fips)
        try:
            client_pin.set_pin(new_pin)
        except CtapError as e:
            logger.error("Failed to set PIN", exc_info=e)
            if e.code == CtapError.ERR.PIN_POLICY_VIOLATION:
                cli_fail("PIN is too long.")
            else:
                cli_fail(f"Failed to set PIN: {e.code}")

    if not is_fips:
        if ctap2.info.options.get("clientPin"):
            if not pin:
                pin = _prompt_current_pin()
        else:
            if pin:
                cli_fail(
                    "There is no current PIN set. Use --new-pin to set one.")

    if not new_pin:
        new_pin = prompt_new_pin()

    if is_fips:
        _fail_if_not_valid_pin(ctx, new_pin, is_fips)
        change_pin(pin, new_pin)
    else:
        if len(new_pin) < ctap2.info.min_pin_length:
            cli_fail("New PIN is too short.")
        if ctap2.info.options.get("clientPin"):
            change_pin(pin, new_pin)
        else:
            set_pin(new_pin)
Exemplo n.º 8
0
# Create a credential
print("\nTouch your authenticator device now...\n")

result = client.make_credential(options, pin=pin)
key = result.attestation_object.large_blob_key

# Complete registration
auth_data = server.register_complete(state, result.client_data,
                                     result.attestation_object)
credentials = [auth_data.credential_data]

print("New credential created!")
print("Large Blob Key:", key)

client_pin = ClientPin(client.ctap2)
if pin:
    token = client_pin.get_pin_token(pin,
                                     ClientPin.PERMISSION.LARGE_BLOB_WRITE)
else:
    token = client_pin.get_uv_token(ClientPin.PERMISSION.LARGE_BLOB_WRITE)
large_blobs = LargeBlobs(client.ctap2, client_pin.protocol, token)

# Write a large blob
print("Writing a large blob...")
large_blobs.put_blob(key, b"Here is some data to store!")

# Prepare parameters for getAssertion
request_options, state = server.authenticate_begin(user_verification=uv)

# Enable largeBlobKey
Exemplo n.º 9
0
 def test_long_pin(self):
     prot = ClientPin(mock.MagicMock(), PinProtocolV1())
     with self.assertRaises(ValueError):
         prot.set_pin("1" * 256)