@Override
  public Boolean doAuth(Buffer buffer, boolean init) throws Exception {
    ValidateUtils.checkTrue(init, "Instance not initialized");
    boolean hasSig = buffer.getBoolean();
    String alg = buffer.getString();

    int oldLim = buffer.wpos();
    int oldPos = buffer.rpos();
    int len = buffer.getInt();
    buffer.wpos(buffer.rpos() + len);
    PublicKey key = buffer.getRawPublicKey();
    ServerFactoryManager manager = session.getFactoryManager();
    Signature verif =
        ValidateUtils.checkNotNull(
            NamedFactory.Utils.create(manager.getSignatureFactories(), alg),
            "No verifier located for algorithm=%s",
            alg);
    verif.initVerifier(key);
    buffer.wpos(oldLim);

    byte[] sig = hasSig ? buffer.getBytes() : null;

    PublickeyAuthenticator authenticator =
        ValidateUtils.checkNotNull(
            manager.getPublickeyAuthenticator(), "No PublickeyAuthenticator configured");
    if (!authenticator.authenticate(username, key, session)) {
      return Boolean.FALSE;
    }

    if (!hasSig) {
      Buffer buf = session.createBuffer(SshConstants.SSH_MSG_USERAUTH_PK_OK);
      buf.putString(alg);
      buf.putRawBytes(buffer.array(), oldPos, 4 + len);
      session.writePacket(buf);
      return null;
    } else {
      Buffer buf = new ByteArrayBuffer();
      buf.putBytes(session.getKex().getH());
      buf.putByte(SshConstants.SSH_MSG_USERAUTH_REQUEST);
      buf.putString(username);
      buf.putString(service);
      buf.putString(UserAuthPublicKeyFactory.NAME);
      buf.putBoolean(true);
      buf.putString(alg);
      buffer.rpos(oldPos);
      buffer.wpos(oldPos + 4 + len);
      buf.putBuffer(buffer);
      verif.update(buf.array(), buf.rpos(), buf.available());
      if (!verif.verify(sig)) {
        throw new Exception("Key verification failed");
      }
      return Boolean.TRUE;
    }
  }
  protected Pair<String, Collection<byte[]>> doGetHash(
      Object target, Collection<String> algorithms, long offset, long length, int blockSize)
      throws IOException {
    Buffer buffer = getCommandBuffer(target, Byte.MAX_VALUE);
    putTarget(buffer, target);
    buffer.putString(GenericUtils.join(algorithms, ','));
    buffer.putLong(offset);
    buffer.putLong(length);
    buffer.putInt(blockSize);

    if (log.isDebugEnabled()) {
      log.debug(
          "doGetHash({})[{}] - offset={}, length={}, block-size={}",
          getName(),
          (target instanceof CharSequence)
              ? target
              : BufferUtils.toHex(BufferUtils.EMPTY_HEX_SEPARATOR, (byte[]) target),
          offset,
          length,
          blockSize);
    }

    buffer = checkExtendedReplyBuffer(receive(sendExtendedCommand(buffer)));
    if (buffer == null) {
      throw new StreamCorruptedException("Missing extended reply data");
    }

    String targetType = buffer.getString();
    if (String.CASE_INSENSITIVE_ORDER.compare(targetType, SftpConstants.EXT_CHECK_FILE) != 0) {
      throw new StreamCorruptedException(
          "Mismatched reply type: expected="
              + SftpConstants.EXT_CHECK_FILE
              + ", actual="
              + targetType);
    }

    String algo = buffer.getString();
    Collection<byte[]> hashes = new LinkedList<>();
    while (buffer.available() > 0) {
      byte[] hashValue = buffer.getBytes();
      hashes.add(hashValue);
    }

    return new Pair<String, Collection<byte[]>>(algo, hashes);
  }