private static void tryDecryptModifiedCiphertextWithSeekableByteChannel(
      StreamingAead ags, byte[] modifiedCiphertext, byte[] associatedData, byte[] plaintext)
      throws Exception {

    SeekableByteChannel bbc = new SeekableByteBufferChannel(modifiedCiphertext);
    SeekableByteChannel ptChannel;
    // Failing in the constructor is valid in principle, but does not happen
    // with the current implementation. Hence we don't catch these exceptions at the moment.
    try {
      ptChannel = ags.newSeekableDecryptingChannel(bbc, associatedData);
    } catch (IOException | GeneralSecurityException ex) {
      return;
    }
    for (int start = 0; start <= plaintext.length; start += 1 + start / 2) {
      for (int length = 1; length <= plaintext.length; length += 1 + length / 2) {
        ByteBuffer pt = ByteBuffer.allocate(length);
        ptChannel.position(start);
        int read;
        try {
          read = ptChannel.read(pt);
        } catch (IOException ex) {
          // Modified ciphertext was found.
          // TODO(bleichen): Currently it is undefined whether we should be able to read
          //   more plaintext from the stream (i.e. unmodified segments).
          //   However, if later calls return plaintext this has to be valid plaintext.
          continue;
        }
        if (read == -1) {
          // ptChannel claims that we reached the end of the plaintext.
          assertTrue("Incorrect truncation: ", start == plaintext.length);
        } else {
          // Expect the decrypted plaintext not to be longer than the expected plaintext.
          assertTrue(
              "start:" + start + " read:" + read + " length:" + length,
              start + read <= plaintext.length);
          // Check that the decrypted plaintext matches the original plaintext.
          String expected = Hex.encode(Arrays.copyOfRange(plaintext, start, start + pt.position()));
          String actual = Hex.encode(Arrays.copyOf(pt.array(), pt.position()));
          assertEquals("start: " + start, expected, actual);
        }
      }
    }
  }
Tries to decrypt a modified ciphertext using an SeekableByteChannel. Each call to read must
either return the original plaintext (e.g. when the modification in the ciphertext does not
affect the plaintext) or it must throw an IOException.
java_src/src/main/java/com/google/crypto/tink/testing/StreamingTestUtil.java