@Override public void close() throws IOException { super.close(); }
/** * Seeks the stream to a specific position relative to start of the under * layer stream. * * @param position the given position in the data. * @throws IOException if an I/O error occurs. */ public void seek(long position) throws IOException { Utils.checkArgument(position >= 0, "Cannot seek to negative offset."); checkStream(); /* * If data of target pos in the underlying stream has already been read * and decrypted in outBuffer, we just need to re-position outBuffer. */ if (position >= getStreamPosition() && position <= getStreamOffset()) { int forward = (int) (position - getStreamPosition()); if (forward > 0) { outBuffer.position(outBuffer.position() + forward); } } else { input.seek(position); resetStreamOffset(position); } }
/** * Calculates the counter and iv, resets the cipher. * * @param position the given position in the data. * @throws IOException if an I/O error occurs. */ protected void resetCipher(long position) throws IOException { final long counter = getCounter(position); CtrCryptoInputStream.calculateIV(initIV, counter, iv); try { cipher.init(Cipher.DECRYPT_MODE, key, new IvParameterSpec(iv)); } catch (InvalidKeyException e) { throw new IOException(e); } catch (InvalidAlgorithmParameterException e) { throw new IOException(e); } cipherReset = false; }
/** * This method is executed immediately after decryption. Checks whether * cipher should be updated and recalculate padding if needed. * * @param position the given position in the data.. * @return the byte. * @throws IOException if an I/O error occurs. */ protected byte postDecryption(long position) throws IOException { byte padding = 0; if (cipherReset) { /* * This code is generally not executed since the cipher usually * maintains cipher context (e.g. the counter) internally. However, * some implementations can't maintain context so a re-init is * necessary after each decryption call. */ resetCipher(position); padding = getPadding(position); inBuffer.position(padding); } return padding; }
checkStream(); int unread = outBuffer.remaining(); if (unread <= 0) { // Fill the unread decrypted data buffer firstly && padding == 0) { decryptInPlace(buf); padding = postDecryption(streamOffset); return n; decrypt(); padding = postDecryption(streamOffset);
/** * Decrypts more data by reading the under layer stream. The decrypted data * will be put in the output buffer. * * @return The number of decrypted data. -1 if end of the decrypted stream. * @throws IOException if an I/O error occurs. */ @Override protected int decryptMore() throws IOException { int n = input.read(inBuffer); if (n <= 0) { return n; } streamOffset += n; // Read n bytes decrypt(); padding = postDecryption(streamOffset); return outBuffer.remaining(); }
@Override public long getPos() throws IOException { return super.getStreamPosition(); }
public long skip(long n) throws IOException { Utils.checkArgument(n >= 0, "Negative skip length."); checkStream(); resetStreamOffset(pos); return skipped;
/** * Resets the {@link #cipher}: calculate counter and {@link #padding}. * * @throws IOException if an I/O error occurs. */ private void resetCipher() throws IOException { final long counter = streamOffset / cipher.getBlockSize(); padding = (byte) (streamOffset % cipher.getBlockSize()); inBuffer.position(padding); // Set proper position for input data. CtrCryptoInputStream.calculateIV(initIV, counter, iv); try { cipher.init(Cipher.ENCRYPT_MODE, key, new IvParameterSpec(iv)); } catch (InvalidKeyException e) { throw new IOException(e); } catch (InvalidAlgorithmParameterException e) { throw new IOException(e); } cipherReset = false; }
/** * Does the decryption using inBuffer as input and outBuffer as output. Upon * return, inBuffer is cleared; the decrypted data starts at * outBuffer.position() and ends at outBuffer.limit(). * * @throws IOException if an I/O error occurs. */ @Override protected void decrypt() throws IOException { Utils.checkState(inBuffer.position() >= padding); if (inBuffer.position() == padding) { // There is no real data in inBuffer. return; } inBuffer.flip(); outBuffer.clear(); decryptBuffer(outBuffer); inBuffer.clear(); outBuffer.flip(); if (padding > 0) { /* * The plain text and cipher text have a 1:1 mapping, they start at * the same position. */ outBuffer.position(padding); } }
/** * Resets the underlying stream offset; clear {@link #inBuffer} and * {@link #outBuffer}. This Typically happens during {@link #skip(long)}. * * @param offset the offset of the stream. * @throws IOException if an I/O error occurs. */ protected void resetStreamOffset(long offset) throws IOException { streamOffset = offset; inBuffer.clear(); outBuffer.clear(); outBuffer.limit(0); resetCipher(offset); padding = getPadding(offset); inBuffer.position(padding); // Set proper position for input data. }
decrypt(); buf.position(offset + n); buf.limit(limit); buf.put(outBuffer); } finally { padding = postDecryption(streamOffset - (len - n));
/** * Calculates the counter and iv, reset the cipher. * * @param state the CipherState instance. * @param position the offset from the start of the stream. * @param iv the iv. * @throws IOException if an I/O error occurs. */ private void resetCipher(CipherState state, long position, byte[] iv) throws IOException { final long counter = getCounter(position); CtrCryptoInputStream.calculateIV(getInitIV(), counter, iv); try { state.getCryptoCipher().init(Cipher.DECRYPT_MODE, key, new IvParameterSpec(iv)); } catch (InvalidKeyException e) { throw new IOException(e); } catch (InvalidAlgorithmParameterException e) { throw new IOException(e); } state.reset(false); }
/** * Does the decryption using inBuffer as input and buf as output. Upon * return, inBuffer is cleared; the buf's position will be equal to * <i>p</i> <tt>+</tt> <i>n</i> where <i>p</i> is the position * before decryption, <i>n</i> is the number of bytes decrypted. The buf's * limit will not have changed. * * @param buf The buffer into which bytes are to be transferred. * @throws IOException if an I/O error occurs. */ protected void decryptInPlace(ByteBuffer buf) throws IOException { Utils.checkState(inBuffer.position() >= padding); Utils.checkState(buf.isDirect()); Utils.checkState(buf.remaining() >= inBuffer.position()); Utils.checkState(padding == 0); if (inBuffer.position() == padding) { // There is no real data in inBuffer. return; } inBuffer.flip(); decryptBuffer(buf); inBuffer.clear(); }
/** * Overrides the {@link CryptoInputStream#close()}. Closes this input stream * and releases any system resources associated with the stream. * * @throws IOException if an I/O error occurs. */ @Override public void close() throws IOException { if (!isOpen()) { return; } cleanBufferPool(); super.close(); }