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 test_get_pin_token(self): prot = ClientPin(mock.MagicMock(), PinProtocolV1()) prot._get_shared_secret = mock.Mock(return_value=({}, SHARED)) prot.ctap.client_pin.return_value = {2: TOKEN_ENC} self.assertEqual(prot.get_pin_token("1234"), TOKEN) prot.ctap.client_pin.assert_called_once() self.assertEqual( prot.ctap.client_pin.call_args[1]["pin_hash_enc"], PIN_HASH_ENC )
def _init_credman(ctx, pin): pin = _require_pin(ctx, pin, "Credential Management") ctap2 = ctx.obj.get("ctap2") client_pin = ClientPin(ctap2) try: token = client_pin.get_pin_token(pin, ClientPin.PERMISSION.CREDENTIAL_MGMT) except CtapError as e: logger.error("Ctap error", exc_info=e) _fail_pin_error(ctx, e, "PIN error: %s") return CredentialManagement(ctap2, client_pin.protocol, token)
def _init_bio(ctx, pin): ctap2 = ctx.obj.get("ctap2") if not ctap2 or "bioEnroll" not in ctap2.info.options: cli_fail("Biometrics is not supported on this YubiKey.") pin = _require_pin(ctx, pin, "Biometrics") client_pin = ClientPin(ctap2) try: token = client_pin.get_pin_token(pin, ClientPin.PERMISSION.BIO_ENROLL) except CtapError as e: logger.error("Ctap error", exc_info=e) _fail_pin_error(ctx, e, "PIN error: %s") return FPBioEnrollment(ctap2, client_pin.protocol, token)
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 _init_bio(ctx, pin): ctap2 = ctx.obj.get("ctap2") if not ctap2 or "bioEnroll" not in ctap2.info.options: cli_fail("Biometrics is not supported on this YubiKey.") elif not ctap2.info.options.get("clientPin"): cli_fail("Biometrics requires having a PIN. Set a PIN first.") if pin is None: pin = _prompt_current_pin(prompt="Enter your PIN") client_pin = ClientPin(ctap2) try: token = client_pin.get_pin_token(pin, ClientPin.PERMISSION.BIO_ENROLL) except CtapError as e: logger.error("Ctap error", exc_info=e) _fail_pin_error(ctx, e, "PIN error: %s") return FPBioEnrollment(ctap2, client_pin.protocol, token)
def _init_credman(ctx, pin): ctap2 = ctx.obj.get("ctap2") if not ctap2: cli_fail("Credential management not supported on this YubiKey.") elif not ctap2.info.options.get("clientPin"): cli_fail("Credential management requires having a PIN. Set a PIN first.") if pin is None: pin = _prompt_current_pin(prompt="Enter your PIN") client_pin = ClientPin(ctap2) try: token = client_pin.get_pin_token(pin, ClientPin.PERMISSION.CREDENTIAL_MGMT) except CtapError as e: logger.error("Ctap error", exc_info=e) _fail_pin_error(ctx, e, "PIN error: %s") return CredentialManagement(ctap2, client_pin.protocol, token)
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) print("Fingerprint registered successfully with ID:", template_id)
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 options = request_options["publicKey"] options.extensions = {"largeBlobKey": True}