class Fido2Controller(object): 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"] @property def has_pin(self): return self._pin def get_resident_credentials(self, pin): _credman = CredentialManagement( self.ctap, self.pin.protocol, self.pin.get_pin_token(pin, ClientPin.PERMISSION.CREDENTIAL_MGMT), ) for rp in _credman.enumerate_rps(): for cred in _credman.enumerate_creds( rp[CredentialManagement.RESULT.RP_ID_HASH]): yield ResidentCredential(cred, rp) def delete_resident_credential(self, credential_id, pin): _credman = CredentialManagement( self.ctap, self.pin.protocol, self.pin.get_pin_token(pin, ClientPin.PERMISSION.CREDENTIAL_MGMT), ) for cred in self.get_resident_credentials(pin): if credential_id == cred.credential_id: _credman.delete_cred(credential_id) def get_pin_retries(self): return self.pin.get_pin_retries() def set_pin(self, pin): self.pin.set_pin(pin) self._pin = True def change_pin(self, old_pin, new_pin): self.pin.change_pin(old_pin, new_pin) def reset(self, touch_callback=None): if touch_callback: touch_timer = Timer(0.500, touch_callback) touch_timer.start() try: self.ctap.reset() self._pin = False finally: if touch_callback: touch_timer.cancel() @property def is_fips(self): return False
def info(ctx): """ Display general status of the FIDO2 application. """ conn = ctx.obj["conn"] ctap2 = ctx.obj.get("ctap2") if is_fips_version(ctx.obj["info"].version): click.echo("FIPS Approved Mode: " + ("Yes" if is_in_fips_mode(conn) else "No")) elif ctap2: client_pin = ClientPin(ctap2) # N.B. All YubiKeys with CTAP2 support PIN. if ctap2.info.options["clientPin"]: if ctap2.info.force_pin_change: click.echo( "NOTE: The FIDO PID is disabled and must be changed before it can " "be used!" ) pin_retries, power_cycle = client_pin.get_pin_retries() if pin_retries: click.echo(f"PIN is set, with {pin_retries} attempt(s) remaining.") if power_cycle: click.echo( "PIN is temporarily blocked. " "Remove and re-insert the YubiKey to unblock." ) else: click.echo("PIN is set, but has been blocked.") else: click.echo("PIN is not set.") bio_enroll = ctap2.info.options.get("bioEnroll") if bio_enroll: uv_retries, _ = client_pin.get_uv_retries() if uv_retries: click.echo( f"Fingerprints registered, with {uv_retries} attempt(s) " "remaining." ) else: click.echo( "Fingerprints registered, but blocked until PIN is verified." ) elif bio_enroll is False: click.echo("No fingerprints have been registered.") always_uv = ctap2.info.options.get("alwaysUv") if always_uv is not None: click.echo( "Always Require User Verification is turned " + ("on." if always_uv else "off.") ) else: click.echo("PIN is not supported.")
def fido_pin_retries(self): try: with self._open_device([FidoConnection]) as conn: ctap2 = Ctap2(conn) client_pin = ClientPin(ctap2) return success({'retries': client_pin.get_pin_retries()[0]}) except CtapError as e: if e.code == CtapError.ERR.PIN_AUTH_BLOCKED: return failure('PIN authentication is currently blocked. ' 'Remove and re-insert the YubiKey.') if e.code == CtapError.ERR.PIN_BLOCKED: return failure('PIN is blocked.') raise