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)
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.")
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
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
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"]
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)
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)
# 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
def test_long_pin(self): prot = ClientPin(mock.MagicMock(), PinProtocolV1()) with self.assertRaises(ValueError): prot.set_pin("1" * 256)