code: @Override
    public int read(byte[] b, int off, int len) throws IOException {
      Validate.notNull(b, "Byte array cannot be null.");
  
      Validate.isTrue(off >= 0, "Offset cannot be negative.");
      Validate.isTrue(len >= 0, "Length cannot be negative.");
      Validate.isTrue(len + off <= b.length,
          "Length plus offset cannot be longer than byte array.");
  
      if (len == 0) {
        return 0;
      }
  
      if (trailerIn == null) {
        initializeStream();
      }
  
      int result = pushbackInputStream.read(b, off, len);
      return completeRead(result);
    }
docs: Reads a number of bytes into the byte array. If this includes the last byte
  in the stream (determined by peeking ahead to the next byte), the value of
  the HMAC is verified. If the verification fails an exception is thrown.
  
  @param b
  the buffer into which the data is read.
  @param off
  the start offset in array <code>b</code> at which the data is
  written.
  @param len
  the maximum number of bytes to read.
  @return the total number of bytes read into the buffer, or <code>-1</code>
  if there is no more data because the end of the stream has been
  reached.
  @throws IOException
  If the first byte cannot be read for any reason other than end of
  file, or if the input stream has been closed, or if some other
  I/O error occurs.
  @throws NullPointerException
  If <code>b</code> is <code>null</code>.
  @throws IndexOutOfBoundsException
  If <code>off</code> is negative, <code>len</code> is negative, or
  <code>len</code> is greater than <code>b.length - off</code>
  @throws StreamIntegrityException
  if the final byte has been read and the HMAC fails validation