docs: def Authenticate(self, app_id, challenge, registered_keys):
      """Authenticates app_id with the security key.
  
      Executes the U2F authentication/signature flow with the security key.
  
      Args:
        app_id: The app_id to register the security key against.
        challenge: Server challenge passed to the security key as a bytes object.
        registered_keys: List of keys already registered for this app_id+user.
  
      Returns:
        SignResponse with client_data, key_handle, and signature_data.  The client
        data is an object, while the signature_data is encoded in FIDO U2F binary
        format.
  
      Raises:
        U2FError: There was some kind of problem with authentication (e.g.
          there was a timeout while waiting for the test of user presence.)
      """
      client_data = model.ClientData(model.ClientData.TYP_AUTHENTICATION,
                                     challenge, self.origin)
      app_param = self.InternalSHA256(app_id)
      challenge_param = self.InternalSHA256(client_data.GetJson())
      num_invalid_keys = 0
      for key in registered_keys:
        try:
          if key.version != u'U2F_V2':
            continue
          for _ in range(30):
            try:
              resp = self.security_key.CmdAuthenticate(challenge_param, app_param,
                                                       key.key_handle)
              return model.SignResponse(key.key_handle, resp, client_data)
            except errors.TUPRequiredError:
              self.security_key.CmdWink()
              time.sleep(0.5)
        except errors.InvalidKeyHandleError:
          num_invalid_keys += 1
          continue
        except errors.HardwareError as e:
          raise errors.U2FError(errors.U2FError.BAD_REQUEST, e)
  
      if num_invalid_keys == len(registered_keys):
        # In this case, all provided keys were invalid.
        raise errors.U2FError(errors.U2FError.DEVICE_INELIGIBLE)
  
      # In this case, the TUP was not pressed.
      raise errors.U2FError(errors.U2FError.TIMEOUT)