http://git-wip-us.apache.org/repos/asf/commons-crypto/blob/ea89d802/src/main/java/org/apache/commons/crypto/stream/CTRCipherInputStream.java ---------------------------------------------------------------------- diff --git a/src/main/java/org/apache/commons/crypto/stream/CTRCipherInputStream.java b/src/main/java/org/apache/commons/crypto/stream/CTRCipherInputStream.java deleted file mode 100644 index b0cea74..0000000 --- a/src/main/java/org/apache/commons/crypto/stream/CTRCipherInputStream.java +++ /dev/null @@ -1,625 +0,0 @@ -/** - * Licensed to the Apache Software Foundation (ASF) under one - * or more contributor license agreements. See the NOTICE file - * distributed with this work for additional information - * regarding copyright ownership. The ASF licenses this file - * to you under the Apache License, Version 2.0 (the - * "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package org.apache.commons.crypto.stream; - -import java.io.IOException; -import java.io.InputStream; -import java.nio.ByteBuffer; -import java.nio.channels.ReadableByteChannel; -import java.security.InvalidAlgorithmParameterException; -import java.security.InvalidKeyException; -import java.util.Properties; -import javax.crypto.BadPaddingException; -import javax.crypto.IllegalBlockSizeException; -import javax.crypto.ShortBufferException; -import javax.crypto.spec.IvParameterSpec; -import javax.crypto.spec.SecretKeySpec; - -import org.apache.commons.crypto.cipher.Cipher; -import org.apache.commons.crypto.cipher.CipherTransformation; -import org.apache.commons.crypto.stream.input.ChannelInput; -import org.apache.commons.crypto.stream.input.Input; -import org.apache.commons.crypto.stream.input.StreamInput; -import org.apache.commons.crypto.utils.Utils; - -/** - * CTRCipherInputStream decrypts data. AES CTR mode is required in order to - * ensure that the plain text and cipher text have a 1:1 mapping. CTR crypto - * stream has stream characteristic which is useful for implement features - * like random seek. The decryption is buffer based. The key points of the - * decryption are (1) calculating the counter and (2) padding through stream - * position: - * <p/> - * counter = base + pos/(algorithm blocksize); - * padding = pos%(algorithm blocksize); - * <p/> - * The underlying stream offset is maintained as state. It is not thread-safe. - */ -public class CTRCipherInputStream extends CipherInputStream { - /** - * Underlying stream offset - */ - long streamOffset = 0; - - /** - * The initial IV. - */ - protected final byte[] initIV; - - /** - * Initialization vector for the cipher. - */ - protected byte[] iv; - - /** - * Padding = pos%(algorithm blocksize); Padding is put into {@link #inBuffer} - * before any other data goes in. The purpose of padding is to put the input - * data at proper position. - */ - private byte padding; - - /** - * Flag to mark whether the cipher has been reset - */ - private boolean cipherReset = false; - - /** - * Constructs a {@link org.apache.commons.crypto.stream.CTRCipherInputStream}. - * - * @param props The <code>Properties</code> class represents a set of - * properties. - * @param in the input stream. - * @param key crypto key for the cipher. - * @param iv Initialization vector for the cipher. - * @throws IOException if an I/O error occurs. - */ - public CTRCipherInputStream(Properties props, InputStream in, - byte[] key, byte[] iv) - throws IOException { - this(props, in, key, iv, 0); - } - - /** - * Constructs a {@link org.apache.commons.crypto.stream.CTRCipherInputStream}. - * - * @param props The <code>Properties</code> class represents a set of - * properties. - * @param in the ReadableByteChannel instance. - * @param key crypto key for the cipher. - * @param iv Initialization vector for the cipher. - * @throws IOException if an I/O error occurs. - */ - public CTRCipherInputStream(Properties props, ReadableByteChannel in, - byte[] key, byte[] iv) - throws IOException { - this(props, in, key, iv, 0); - } - - /** - * Constructs a {@link org.apache.commons.crypto.stream.CTRCipherInputStream}. - * - * @param in the input stream. - * @param cipher the Cipher instance. - * @param bufferSize the bufferSize. - * @param key crypto key for the cipher. - * @param iv Initialization vector for the cipher. - * @throws IOException if an I/O error occurs. - */ - public CTRCipherInputStream(InputStream in, Cipher cipher, int bufferSize, - byte[] key, byte[] iv) throws IOException { - this(in, cipher, bufferSize, key, iv, 0); - } - - /** - * Constructs a {@link org.apache.commons.crypto.stream.CTRCipherInputStream}. - * - * @param in the ReadableByteChannel instance. - * @param cipher the cipher instance. - * @param bufferSize the bufferSize. - * @param key crypto key for the cipher. - * @param iv Initialization vector for the cipher. - * @throws IOException if an I/O error occurs. - */ - public CTRCipherInputStream(ReadableByteChannel in, Cipher cipher, - int bufferSize, byte[] key, byte[] iv) throws IOException { - this(in, cipher, bufferSize, key, iv, 0); - } - - /** - * Constructs a {@link org.apache.commons.crypto.stream.CTRCipherInputStream}. - * - * @param input the input data. - * @param cipher the Cipher instance. - * @param bufferSize the bufferSize. - * @param key crypto key for the cipher. - * @param iv Initialization vector for the cipher. - * @throws IOException if an I/O error occurs. - */ - public CTRCipherInputStream( - Input input, - Cipher cipher, - int bufferSize, - byte[] key, - byte[] iv) throws IOException { - this(input, cipher, bufferSize, key, iv, 0); - } - - /** - * Constructs a {@link org.apache.commons.crypto.stream.CTRCipherInputStream}. - * - * @param props The <code>Properties</code> class represents a set of - * properties. - * @param in the InputStream instance. - * @param key crypto key for the cipher. - * @param iv Initialization vector for the cipher. - * @param streamOffset the start offset in the stream. - * @throws IOException if an I/O error occurs. - */ - public CTRCipherInputStream(Properties props, InputStream in, - byte[] key, byte[] iv, long streamOffset) - throws IOException { - this(in, Utils.getCipherInstance(CipherTransformation.AES_CTR_NOPADDING, props), - Utils.getBufferSize(props), key, iv, streamOffset); - } - - /** - *Constructs a {@link org.apache.commons.crypto.stream.CTRCipherInputStream}. - * - * @param props The <code>Properties</code> class represents a set of - * properties. - * @param in the ReadableByteChannel instance. - * @param key crypto key for the cipher. - * @param iv Initialization vector for the cipher. - * @param streamOffset the start offset in the stream. - * @throws IOException if an I/O error occurs. - */ - public CTRCipherInputStream(Properties props, ReadableByteChannel in, - byte[] key, byte[] iv, long streamOffset) - throws IOException { - this(in, Utils.getCipherInstance(CipherTransformation.AES_CTR_NOPADDING, props), - Utils.getBufferSize(props), key, iv, streamOffset); - } - - /** - * Constructs a {@link org.apache.commons.crypto.stream.CTRCipherInputStream}. - * - * @param in the InputStream instance. - * @param cipher the Cipher instance. - * @param bufferSize the bufferSize. - * @param key crypto key for the cipher. - * @param iv Initialization vector for the cipher. - * @param streamOffset the start offset in the stream. - * @throws IOException if an I/O error occurs. - */ - public CTRCipherInputStream(InputStream in, Cipher cipher, int bufferSize, - byte[] key, byte[] iv, long streamOffset) throws IOException { - this(new StreamInput(in, bufferSize), cipher, bufferSize, key, iv, streamOffset); - } - - /** - * Constructs a {@link org.apache.commons.crypto.stream.CTRCipherInputStream}. - * - * @param in the ReadableByteChannel instance. - * @param cipher the Cipher instance. - * @param bufferSize the bufferSize. - * @param key crypto key for the cipher. - * @param iv Initialization vector for the cipher. - * @param streamOffset the start offset in the stream. - * @throws IOException if an I/O error occurs. - */ - public CTRCipherInputStream(ReadableByteChannel in, Cipher cipher, - int bufferSize, byte[] key, byte[] iv, long streamOffset) throws IOException { - this(new ChannelInput(in), cipher, bufferSize, key, iv, streamOffset); - } - - /** - * Constructs a {@link org.apache.commons.crypto.stream.CTRCipherInputStream}. - * - * @param input the input data. - * @param cipher the Cipher instance. - * @param bufferSize the bufferSize. - * @param key crypto key for the cipher. - * @param iv Initialization vector for the cipher. - * @param streamOffset the start offset in the stream. - * @throws IOException if an I/O error occurs. - */ - public CTRCipherInputStream( - Input input, - Cipher cipher, - int bufferSize, - byte[] key, - byte[] iv, - long streamOffset) throws IOException { - super(input, cipher, bufferSize, new SecretKeySpec(key, "AES"), new IvParameterSpec(iv)); - - this.initIV = iv.clone(); - this.iv = iv.clone(); - - Utils.checkStreamCipher(cipher); - - resetStreamOffset(streamOffset); - } - - /** - * Overrides the {@link org.apache.commons.crypto.stream.CipherInputStream#skip(long)}. - * Skips over and discards <code>n</code> bytes of data from this input - * stream. - * - * @param n the number of bytes to be skipped. - * @return the actual number of bytes skipped. - * @throws IOException if an I/O error occurs. - */ - @Override - public long skip(long n) throws IOException { - Utils.checkArgument(n >= 0, "Negative skip length."); - checkStream(); - - if (n == 0) { - return 0; - } else if (n <= outBuffer.remaining()) { - int pos = outBuffer.position() + (int) n; - outBuffer.position(pos); - return n; - } else { - /* - * Subtract outBuffer.remaining() to see how many bytes we need to - * skip in the underlying stream. Add outBuffer.remaining() to the - * actual number of skipped bytes in the underlying stream to get the - * number of skipped bytes from the user's point of view. - */ - n -= outBuffer.remaining(); - long skipped = input.skip(n); - if (skipped < 0) { - skipped = 0; - } - long pos = streamOffset + skipped; - skipped += outBuffer.remaining(); - resetStreamOffset(pos); - return skipped; - } - } - - /** - * Overrides the {@link org.apache.commons.crypto.stream.CTRCipherInputStream#read(ByteBuffer)}. - * Reads a sequence of bytes from this channel into the given buffer. - * - * @param buf The buffer into which bytes are to be transferred. - * @return The number of bytes read, possibly zero, or <tt>-1</tt> if the - * channel has reached end-of-stream. - * @throws IOException if an I/O error occurs. - */ - @Override - public int read(ByteBuffer buf) throws IOException { - checkStream(); - int unread = outBuffer.remaining(); - if (unread <= 0) { // Fill the unread decrypted data buffer firstly - final int n = input.read(inBuffer); - if (n <= 0) { - return n; - } - - streamOffset += n; // Read n bytes - if (buf.isDirect() && buf.remaining() >= inBuffer.position() && padding == 0) { - // Use buf as the output buffer directly - decryptInPlace(buf); - padding = postDecryption(streamOffset); - return n; - } else { - // Use outBuffer as the output buffer - decrypt(); - padding = postDecryption(streamOffset); - } - } - - // Copy decrypted data from outBuffer to buf - unread = outBuffer.remaining(); - final int toRead = buf.remaining(); - if (toRead <= unread) { - final int limit = outBuffer.limit(); - outBuffer.limit(outBuffer.position() + toRead); - buf.put(outBuffer); - outBuffer.limit(limit); - return toRead; - } else { - buf.put(outBuffer); - return unread; - } - } - - /** - * 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); - } - } - - /** - * Gets the offset of the stream. - * - * @return the stream offset. - */ - protected long getStreamOffset() { - return streamOffset; - } - - protected void setStreamOffset(long streamOffset) { - this.streamOffset = streamOffset; - } - - /** - * Gets the position of the stream. - * - * @return the position of the stream. - */ - protected long getStreamPosition() { - return streamOffset - outBuffer.remaining(); - } - - /** - * 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(); - } - - /** - * 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); - } - } - - /** - * 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(); - } - - /** - * Decrypts all data in buf: total n bytes from given start position. - * Output is also buf and same start position. - * buf.position() and buf.limit() should be unchanged after decryption. - * - * @param buf The buffer into which bytes are to be transferred. - * @param offset the start offset in the data. - * @param len the the maximum number of decrypted data bytes to read. - * @throws IOException if an I/O error occurs. - */ - protected void decrypt(ByteBuffer buf, int offset, int len) - throws IOException { - final int pos = buf.position(); - final int limit = buf.limit(); - int n = 0; - while (n < len) { - buf.position(offset + n); - buf.limit(offset + n + Math.min(len - n, inBuffer.remaining())); - inBuffer.put(buf); - // Do decryption - try { - decrypt(); - buf.position(offset + n); - buf.limit(limit); - n += outBuffer.remaining(); - buf.put(outBuffer); - } finally { - padding = postDecryption(streamOffset - (len - n)); - } - } - buf.position(pos); - } - - /** - * 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. - */ - 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; - } - - /** - * Gets the initialization vector. - * - * @return the initIV. - */ - protected byte[] getInitIV() { - return initIV; - } - - /** - * Gets the counter for input stream position. - * - * @param position the given position in the data. - * @return the counter for input stream position. - */ - protected long getCounter(long position) { - return position / cipher.getTransformation().getAlgorithmBlockSize(); - } - - /** - * Gets the padding for input stream position. - * - * @param position the given position in the data. - * @return the padding for input stream position. - */ - protected byte getPadding(long position) { - return (byte)(position % cipher.getTransformation().getAlgorithmBlockSize()); - } - - /** - * Overrides the {@link CTRCipherInputStream#initCipher()}. - * Initializes the cipher. - */ - @Override - protected void initCipher() { - // Do nothing for initCipher - // Will reset the cipher when reset the stream offset - } - - /** - * 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); - Utils.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; - } - - /** - * 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. - } - - /** - * Does the decryption using out as output. - * - * @param out the output ByteBuffer. - * @throws IOException if an I/O error occurs. - */ - protected void decryptBuffer(ByteBuffer out) - throws IOException { - int inputSize = inBuffer.remaining(); - try { - int n = cipher.update(inBuffer, out); - if (n < inputSize) { - /** - * Typically code will not get here. Cipher#update will consume all - * input data and put result in outBuffer. - * Cipher#doFinal will reset the cipher context. - */ - cipher.doFinal(inBuffer, out); - cipherReset = true; - } - } catch (ShortBufferException e) { - throw new IOException(e); - } catch (IllegalBlockSizeException e) { - throw new IOException(e); - } catch (BadPaddingException e) { - throw new IOException(e); - } - } -}
http://git-wip-us.apache.org/repos/asf/commons-crypto/blob/ea89d802/src/main/java/org/apache/commons/crypto/stream/CTRCipherOutputStream.java ---------------------------------------------------------------------- diff --git a/src/main/java/org/apache/commons/crypto/stream/CTRCipherOutputStream.java b/src/main/java/org/apache/commons/crypto/stream/CTRCipherOutputStream.java deleted file mode 100644 index 7889123..0000000 --- a/src/main/java/org/apache/commons/crypto/stream/CTRCipherOutputStream.java +++ /dev/null @@ -1,385 +0,0 @@ -/** - * Licensed to the Apache Software Foundation (ASF) under one - * or more contributor license agreements. See the NOTICE file - * distributed with this work for additional information - * regarding copyright ownership. The ASF licenses this file - * to you under the Apache License, Version 2.0 (the - * "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package org.apache.commons.crypto.stream; - -import java.io.IOException; -import java.io.OutputStream; -import java.nio.ByteBuffer; -import java.nio.channels.WritableByteChannel; -import java.security.InvalidAlgorithmParameterException; -import java.security.InvalidKeyException; -import java.util.Properties; - -import javax.crypto.BadPaddingException; -import javax.crypto.IllegalBlockSizeException; -import javax.crypto.ShortBufferException; -import javax.crypto.spec.IvParameterSpec; -import javax.crypto.spec.SecretKeySpec; - -import org.apache.commons.crypto.cipher.Cipher; -import org.apache.commons.crypto.cipher.CipherTransformation; -import org.apache.commons.crypto.stream.output.ChannelOutput; -import org.apache.commons.crypto.stream.output.Output; -import org.apache.commons.crypto.stream.output.StreamOutput; -import org.apache.commons.crypto.utils.Utils; - -/** - * CTRCipherOutputStream encrypts data. It is not thread-safe. AES CTR mode is - * required in order to ensure that the plain text and cipher text have a 1:1 - * mapping. The encryption is buffer based. The key points of the encryption are - * (1) calculating counter and (2) padding through stream position. - * <p/> - * counter = base + pos/(algorithm blocksize); - * padding = pos%(algorithm blocksize); - * <p/> - * The underlying stream offset is maintained as state. - */ -public class CTRCipherOutputStream extends CipherOutputStream { - /** - * Underlying stream offset. - */ - long streamOffset = 0; - - /** - * The initial IV. - */ - protected final byte[] initIV; - - /** - * Initialization vector for the cipher. - */ - protected byte[] iv; - - /** - * Padding = pos%(algorithm blocksize); Padding is put into {@link #inBuffer} - * before any other data goes in. The purpose of padding is to put input data - * at proper position. - */ - private byte padding; - - /** - * Flag to mark whether the cipher has been reset - */ - private boolean cipherReset = false; - - /** - * Constructs a {@link org.apache.commons.crypto.stream.CTRCipherOutputStream}. - * - * @param props The <code>Properties</code> class represents a set of - * properties. - * @param out the output stream. - * @param key crypto key for the cipher. - * @param iv Initialization vector for the cipher. - * @throws IOException if an I/O error occurs. - */ - public CTRCipherOutputStream(Properties props, OutputStream out, byte[] key, - byte[] iv) - throws IOException { - this(props, out, key, iv, 0); - } - - /** - * Constructs a {@link org.apache.commons.crypto.stream.CTRCipherOutputStream}. - * - * @param props The <code>Properties</code> class represents a set of - * properties. - * @param out the WritableByteChannel instance. - * @param key crypto key for the cipher. - * @param iv Initialization vector for the cipher. - * @throws IOException if an I/O error occurs. - */ - public CTRCipherOutputStream(Properties props, WritableByteChannel out, - byte[] key, byte[] iv) - throws IOException { - this(props, out, key, iv, 0); - } - - /** - * Constructs a {@link org.apache.commons.crypto.stream.CTRCipherOutputStream}. - * - * @param out the output stream. - * @param cipher the Cipher instance. - * @param bufferSize the bufferSize. - * @param key crypto key for the cipher. - * @param iv Initialization vector for the cipher. - * @throws IOException if an I/O error occurs. - */ - public CTRCipherOutputStream(OutputStream out, Cipher cipher, int bufferSize, - byte[] key, byte[] iv) throws IOException { - this(out, cipher, bufferSize, key, iv, 0); - } - - /** - * Constructs a {@link org.apache.commons.crypto.stream.CTRCipherOutputStream}. - * - * @param channel the WritableByteChannel instance. - * @param cipher the Cipher instance. - * @param bufferSize the bufferSize. - * @param key crypto key for the cipher. - * @param iv Initialization vector for the cipher. - * @throws IOException if an I/O error occurs. - */ - public CTRCipherOutputStream(WritableByteChannel channel, Cipher cipher, - int bufferSize, byte[] key, byte[] iv) throws IOException { - this(channel, cipher, bufferSize, key, iv, 0); - } - - /** - * Constructs a {@link org.apache.commons.crypto.stream.CTRCipherOutputStream}. - * - * @param output the Output instance. - * @param cipher the Cipher instance. - * @param bufferSize the bufferSize. - * @param key crypto key for the cipher. - * @param iv Initialization vector for the cipher. - * @throws IOException if an I/O error occurs. - */ - public CTRCipherOutputStream(Output output, Cipher cipher, int bufferSize, - byte[] key, byte[] iv) - throws IOException { - this(output, cipher, bufferSize, key, iv, 0); - } - - /** - * Constructs a {@link org.apache.commons.crypto.stream.CTRCipherOutputStream}. - * - * @param props The <code>Properties</code> class represents a set of - * properties. - * @param out the output stream. - * @param key crypto key for the cipher. - * @param iv Initialization vector for the cipher. - * @param streamOffset the start offset in the data. - * @throws IOException if an I/O error occurs. - */ - public CTRCipherOutputStream(Properties props, OutputStream out, byte[] key, - byte[] iv, long streamOffset) - throws IOException { - this(out, Utils.getCipherInstance(CipherTransformation.AES_CTR_NOPADDING, props), - Utils.getBufferSize(props), key, iv, streamOffset); - } - - /** - * Constructs a {@link org.apache.commons.crypto.stream.CTRCipherOutputStream}. - * - * @param props The <code>Properties</code> class represents a set of - * properties. - * @param out the WritableByteChannel instance. - * @param key crypto key for the cipher. - * @param iv Initialization vector for the cipher. - * @param streamOffset the start offset in the data. - * @throws IOException if an I/O error occurs. - */ - public CTRCipherOutputStream(Properties props, WritableByteChannel out, - byte[] key, byte[] iv, long streamOffset) - throws IOException { - this(out, Utils.getCipherInstance(CipherTransformation.AES_CTR_NOPADDING, props), - Utils.getBufferSize(props), key, iv, streamOffset); - } - - /** - * Constructs a {@link org.apache.commons.crypto.stream.CTRCipherOutputStream}. - * - * @param out the output stream. - * @param cipher the Cipher instance. - * @param bufferSize the bufferSize. - * @param key crypto key for the cipher. - * @param iv Initialization vector for the cipher. - * @param streamOffset the start offset in the data. - * @throws IOException if an I/O error occurs. - */ - public CTRCipherOutputStream(OutputStream out, Cipher cipher, int bufferSize, - byte[] key, byte[] iv, long streamOffset) throws IOException { - this(new StreamOutput(out, bufferSize), cipher, - bufferSize, key, iv, streamOffset); - } - - /** - * Constructs a {@link org.apache.commons.crypto.stream.CTRCipherOutputStream}. - * - * @param channel the WritableByteChannel instance. - * @param cipher the Cipher instance. - * @param bufferSize the bufferSize. - * @param key crypto key for the cipher. - * @param iv Initialization vector for the cipher. - * @param streamOffset the start offset in the data. - * @throws IOException if an I/O error occurs. - */ - public CTRCipherOutputStream(WritableByteChannel channel, Cipher cipher, - int bufferSize, byte[] key, byte[] iv, - long streamOffset) throws IOException { - this(new ChannelOutput(channel), cipher, - bufferSize, key, iv, streamOffset); - } - - /** - * Constructs a {@link org.apache.commons.crypto.stream.CTRCipherOutputStream}. - * - * @param output the output stream. - * @param cipher the Cipher instance. - * @param bufferSize the bufferSize. - * @param key crypto key for the cipher. - * @param iv Initialization vector for the cipher. - * @param streamOffset the start offset in the data. - * @throws IOException if an I/O error occurs. - */ - public CTRCipherOutputStream(Output output, Cipher cipher, int bufferSize, - byte[] key, byte[] iv, long streamOffset) - throws IOException { - super(output, cipher, bufferSize, new SecretKeySpec(key, "AES"), new IvParameterSpec(iv)); - - Utils.checkStreamCipher(cipher); - this.streamOffset = streamOffset; - this.initIV = iv.clone(); - this.iv = iv.clone(); - - resetCipher(); - } - - /** - * Does the encryption, input is {@link #inBuffer} and output is - * {@link #outBuffer}. - * - * @throws IOException if an I/O error occurs. - */ - @Override - protected void encrypt() throws IOException { - Utils.checkState(inBuffer.position() >= padding); - if (inBuffer.position() == padding) { - // There is no real data in the inBuffer. - return; - } - - inBuffer.flip(); - outBuffer.clear(); - encryptBuffer(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); - padding = 0; - } - - final int len = output.write(outBuffer); - streamOffset += len; - if (cipherReset) { - /* - * This code is generally not executed since the encryptor usually - * maintains encryption context (e.g. the counter) internally. However, - * some implementations can't maintain context so a re-init is necessary - * after each encryption call. - */ - resetCipher(); - } - } - - /** - * Does final encryption of the last data. - * - * @throws IOException if an I/O error occurs. - */ - @Override - protected void encryptFinal() throws IOException { - // The same as the normal encryption for Counter mode - encrypt(); - } - - /** - * Overrides the {@link CipherOutputStream#initCipher()}. - * Initializes the cipher. - */ - @Override - protected void initCipher() { - // Do nothing for initCipher - // Will reset the cipher considering the stream offset - } - - /** - * 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.getTransformation().getAlgorithmBlockSize(); - padding = - (byte)(streamOffset % cipher.getTransformation().getAlgorithmBlockSize()); - inBuffer.position(padding); // Set proper position for input data. - - Utils.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 encryption if the ByteBuffer data. - * - * @param out the output ByteBuffer. - * @throws IOException if an I/O error occurs. - */ - private void encryptBuffer(ByteBuffer out) - throws IOException { - int inputSize = inBuffer.remaining(); - try { - int n = cipher.update(inBuffer, out); - if (n < inputSize) { - /** - * Typically code will not get here. Cipher#update will consume all - * input data and put result in outBuffer. - * Cipher#doFinal will reset the cipher context. - */ - cipher.doFinal(inBuffer, out); - cipherReset = true; - } - } catch (ShortBufferException e) { - throw new IOException(e); - } catch (BadPaddingException e) { - throw new IOException(e); - } catch (IllegalBlockSizeException e) { - throw new IOException(e); - } - } - - /** - * Get the underlying stream offset - * - * @return the underlying stream offset - */ - protected long getStreamOffset() { - return streamOffset; - } - - /** - * Set the underlying stream offset - * - * @param streamOffset the underlying stream offset - */ - protected void setStreamOffset(long streamOffset) { - this.streamOffset = streamOffset; - } -} http://git-wip-us.apache.org/repos/asf/commons-crypto/blob/ea89d802/src/main/java/org/apache/commons/crypto/stream/CTRCryptoInputStream.java ---------------------------------------------------------------------- diff --git a/src/main/java/org/apache/commons/crypto/stream/CTRCryptoInputStream.java b/src/main/java/org/apache/commons/crypto/stream/CTRCryptoInputStream.java new file mode 100644 index 0000000..dd9202d --- /dev/null +++ b/src/main/java/org/apache/commons/crypto/stream/CTRCryptoInputStream.java @@ -0,0 +1,625 @@ +/** + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.commons.crypto.stream; + +import java.io.IOException; +import java.io.InputStream; +import java.nio.ByteBuffer; +import java.nio.channels.ReadableByteChannel; +import java.security.InvalidAlgorithmParameterException; +import java.security.InvalidKeyException; +import java.util.Properties; +import javax.crypto.BadPaddingException; +import javax.crypto.IllegalBlockSizeException; +import javax.crypto.ShortBufferException; +import javax.crypto.spec.IvParameterSpec; +import javax.crypto.spec.SecretKeySpec; + +import org.apache.commons.crypto.cipher.CryptoCipher; +import org.apache.commons.crypto.cipher.CipherTransformation; +import org.apache.commons.crypto.stream.input.ChannelInput; +import org.apache.commons.crypto.stream.input.Input; +import org.apache.commons.crypto.stream.input.StreamInput; +import org.apache.commons.crypto.utils.Utils; + +/** + * CTRCryptoInputStream decrypts data. AES CTR mode is required in order to + * ensure that the plain text and cipher text have a 1:1 mapping. CTR crypto + * stream has stream characteristic which is useful for implement features + * like random seek. The decryption is buffer based. The key points of the + * decryption are (1) calculating the counter and (2) padding through stream + * position: + * <p/> + * counter = base + pos/(algorithm blocksize); + * padding = pos%(algorithm blocksize); + * <p/> + * The underlying stream offset is maintained as state. It is not thread-safe. + */ +public class CTRCryptoInputStream extends CryptoInputStream { + /** + * Underlying stream offset + */ + long streamOffset = 0; + + /** + * The initial IV. + */ + protected final byte[] initIV; + + /** + * Initialization vector for the cipher. + */ + protected byte[] iv; + + /** + * Padding = pos%(algorithm blocksize); Padding is put into {@link #inBuffer} + * before any other data goes in. The purpose of padding is to put the input + * data at proper position. + */ + private byte padding; + + /** + * Flag to mark whether the cipher has been reset + */ + private boolean cipherReset = false; + + /** + * Constructs a {@link org.apache.commons.crypto.stream.CTRCryptoInputStream}. + * + * @param props The <code>Properties</code> class represents a set of + * properties. + * @param in the input stream. + * @param key crypto key for the cipher. + * @param iv Initialization vector for the cipher. + * @throws IOException if an I/O error occurs. + */ + public CTRCryptoInputStream(Properties props, InputStream in, + byte[] key, byte[] iv) + throws IOException { + this(props, in, key, iv, 0); + } + + /** + * Constructs a {@link org.apache.commons.crypto.stream.CTRCryptoInputStream}. + * + * @param props The <code>Properties</code> class represents a set of + * properties. + * @param in the ReadableByteChannel instance. + * @param key crypto key for the cipher. + * @param iv Initialization vector for the cipher. + * @throws IOException if an I/O error occurs. + */ + public CTRCryptoInputStream(Properties props, ReadableByteChannel in, + byte[] key, byte[] iv) + throws IOException { + this(props, in, key, iv, 0); + } + + /** + * Constructs a {@link org.apache.commons.crypto.stream.CTRCryptoInputStream}. + * + * @param in the input stream. + * @param cipher the CryptoCipher instance. + * @param bufferSize the bufferSize. + * @param key crypto key for the cipher. + * @param iv Initialization vector for the cipher. + * @throws IOException if an I/O error occurs. + */ + public CTRCryptoInputStream(InputStream in, CryptoCipher cipher, int bufferSize, + byte[] key, byte[] iv) throws IOException { + this(in, cipher, bufferSize, key, iv, 0); + } + + /** + * Constructs a {@link org.apache.commons.crypto.stream.CTRCryptoInputStream}. + * + * @param in the ReadableByteChannel instance. + * @param cipher the cipher instance. + * @param bufferSize the bufferSize. + * @param key crypto key for the cipher. + * @param iv Initialization vector for the cipher. + * @throws IOException if an I/O error occurs. + */ + public CTRCryptoInputStream(ReadableByteChannel in, CryptoCipher cipher, + int bufferSize, byte[] key, byte[] iv) throws IOException { + this(in, cipher, bufferSize, key, iv, 0); + } + + /** + * Constructs a {@link org.apache.commons.crypto.stream.CTRCryptoInputStream}. + * + * @param input the input data. + * @param cipher the CryptoCipher instance. + * @param bufferSize the bufferSize. + * @param key crypto key for the cipher. + * @param iv Initialization vector for the cipher. + * @throws IOException if an I/O error occurs. + */ + public CTRCryptoInputStream( + Input input, + CryptoCipher cipher, + int bufferSize, + byte[] key, + byte[] iv) throws IOException { + this(input, cipher, bufferSize, key, iv, 0); + } + + /** + * Constructs a {@link org.apache.commons.crypto.stream.CTRCryptoInputStream}. + * + * @param props The <code>Properties</code> class represents a set of + * properties. + * @param in the InputStream instance. + * @param key crypto key for the cipher. + * @param iv Initialization vector for the cipher. + * @param streamOffset the start offset in the stream. + * @throws IOException if an I/O error occurs. + */ + public CTRCryptoInputStream(Properties props, InputStream in, + byte[] key, byte[] iv, long streamOffset) + throws IOException { + this(in, Utils.getCipherInstance(CipherTransformation.AES_CTR_NOPADDING, props), + Utils.getBufferSize(props), key, iv, streamOffset); + } + + /** + *Constructs a {@link org.apache.commons.crypto.stream.CTRCryptoInputStream}. + * + * @param props The <code>Properties</code> class represents a set of + * properties. + * @param in the ReadableByteChannel instance. + * @param key crypto key for the cipher. + * @param iv Initialization vector for the cipher. + * @param streamOffset the start offset in the stream. + * @throws IOException if an I/O error occurs. + */ + public CTRCryptoInputStream(Properties props, ReadableByteChannel in, + byte[] key, byte[] iv, long streamOffset) + throws IOException { + this(in, Utils.getCipherInstance(CipherTransformation.AES_CTR_NOPADDING, props), + Utils.getBufferSize(props), key, iv, streamOffset); + } + + /** + * Constructs a {@link org.apache.commons.crypto.stream.CTRCryptoInputStream}. + * + * @param in the InputStream instance. + * @param cipher the CryptoCipher instance. + * @param bufferSize the bufferSize. + * @param key crypto key for the cipher. + * @param iv Initialization vector for the cipher. + * @param streamOffset the start offset in the stream. + * @throws IOException if an I/O error occurs. + */ + public CTRCryptoInputStream(InputStream in, CryptoCipher cipher, int bufferSize, + byte[] key, byte[] iv, long streamOffset) throws IOException { + this(new StreamInput(in, bufferSize), cipher, bufferSize, key, iv, streamOffset); + } + + /** + * Constructs a {@link org.apache.commons.crypto.stream.CTRCryptoInputStream}. + * + * @param in the ReadableByteChannel instance. + * @param cipher the CryptoCipher instance. + * @param bufferSize the bufferSize. + * @param key crypto key for the cipher. + * @param iv Initialization vector for the cipher. + * @param streamOffset the start offset in the stream. + * @throws IOException if an I/O error occurs. + */ + public CTRCryptoInputStream(ReadableByteChannel in, CryptoCipher cipher, + int bufferSize, byte[] key, byte[] iv, long streamOffset) throws IOException { + this(new ChannelInput(in), cipher, bufferSize, key, iv, streamOffset); + } + + /** + * Constructs a {@link org.apache.commons.crypto.stream.CTRCryptoInputStream}. + * + * @param input the input data. + * @param cipher the CryptoCipher instance. + * @param bufferSize the bufferSize. + * @param key crypto key for the cipher. + * @param iv Initialization vector for the cipher. + * @param streamOffset the start offset in the stream. + * @throws IOException if an I/O error occurs. + */ + public CTRCryptoInputStream( + Input input, + CryptoCipher cipher, + int bufferSize, + byte[] key, + byte[] iv, + long streamOffset) throws IOException { + super(input, cipher, bufferSize, new SecretKeySpec(key, "AES"), new IvParameterSpec(iv)); + + this.initIV = iv.clone(); + this.iv = iv.clone(); + + Utils.checkStreamCipher(cipher); + + resetStreamOffset(streamOffset); + } + + /** + * Overrides the {@link org.apache.commons.crypto.stream.CryptoInputStream#skip(long)}. + * Skips over and discards <code>n</code> bytes of data from this input + * stream. + * + * @param n the number of bytes to be skipped. + * @return the actual number of bytes skipped. + * @throws IOException if an I/O error occurs. + */ + @Override + public long skip(long n) throws IOException { + Utils.checkArgument(n >= 0, "Negative skip length."); + checkStream(); + + if (n == 0) { + return 0; + } else if (n <= outBuffer.remaining()) { + int pos = outBuffer.position() + (int) n; + outBuffer.position(pos); + return n; + } else { + /* + * Subtract outBuffer.remaining() to see how many bytes we need to + * skip in the underlying stream. Add outBuffer.remaining() to the + * actual number of skipped bytes in the underlying stream to get the + * number of skipped bytes from the user's point of view. + */ + n -= outBuffer.remaining(); + long skipped = input.skip(n); + if (skipped < 0) { + skipped = 0; + } + long pos = streamOffset + skipped; + skipped += outBuffer.remaining(); + resetStreamOffset(pos); + return skipped; + } + } + + /** + * Overrides the {@link org.apache.commons.crypto.stream.CTRCryptoInputStream#read(ByteBuffer)}. + * Reads a sequence of bytes from this channel into the given buffer. + * + * @param buf The buffer into which bytes are to be transferred. + * @return The number of bytes read, possibly zero, or <tt>-1</tt> if the + * channel has reached end-of-stream. + * @throws IOException if an I/O error occurs. + */ + @Override + public int read(ByteBuffer buf) throws IOException { + checkStream(); + int unread = outBuffer.remaining(); + if (unread <= 0) { // Fill the unread decrypted data buffer firstly + final int n = input.read(inBuffer); + if (n <= 0) { + return n; + } + + streamOffset += n; // Read n bytes + if (buf.isDirect() && buf.remaining() >= inBuffer.position() && padding == 0) { + // Use buf as the output buffer directly + decryptInPlace(buf); + padding = postDecryption(streamOffset); + return n; + } else { + // Use outBuffer as the output buffer + decrypt(); + padding = postDecryption(streamOffset); + } + } + + // Copy decrypted data from outBuffer to buf + unread = outBuffer.remaining(); + final int toRead = buf.remaining(); + if (toRead <= unread) { + final int limit = outBuffer.limit(); + outBuffer.limit(outBuffer.position() + toRead); + buf.put(outBuffer); + outBuffer.limit(limit); + return toRead; + } else { + buf.put(outBuffer); + return unread; + } + } + + /** + * 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); + } + } + + /** + * Gets the offset of the stream. + * + * @return the stream offset. + */ + protected long getStreamOffset() { + return streamOffset; + } + + protected void setStreamOffset(long streamOffset) { + this.streamOffset = streamOffset; + } + + /** + * Gets the position of the stream. + * + * @return the position of the stream. + */ + protected long getStreamPosition() { + return streamOffset - outBuffer.remaining(); + } + + /** + * 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(); + } + + /** + * 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); + } + } + + /** + * 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(); + } + + /** + * Decrypts all data in buf: total n bytes from given start position. + * Output is also buf and same start position. + * buf.position() and buf.limit() should be unchanged after decryption. + * + * @param buf The buffer into which bytes are to be transferred. + * @param offset the start offset in the data. + * @param len the the maximum number of decrypted data bytes to read. + * @throws IOException if an I/O error occurs. + */ + protected void decrypt(ByteBuffer buf, int offset, int len) + throws IOException { + final int pos = buf.position(); + final int limit = buf.limit(); + int n = 0; + while (n < len) { + buf.position(offset + n); + buf.limit(offset + n + Math.min(len - n, inBuffer.remaining())); + inBuffer.put(buf); + // Do decryption + try { + decrypt(); + buf.position(offset + n); + buf.limit(limit); + n += outBuffer.remaining(); + buf.put(outBuffer); + } finally { + padding = postDecryption(streamOffset - (len - n)); + } + } + buf.position(pos); + } + + /** + * 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. + */ + 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; + } + + /** + * Gets the initialization vector. + * + * @return the initIV. + */ + protected byte[] getInitIV() { + return initIV; + } + + /** + * Gets the counter for input stream position. + * + * @param position the given position in the data. + * @return the counter for input stream position. + */ + protected long getCounter(long position) { + return position / cipher.getTransformation().getAlgorithmBlockSize(); + } + + /** + * Gets the padding for input stream position. + * + * @param position the given position in the data. + * @return the padding for input stream position. + */ + protected byte getPadding(long position) { + return (byte)(position % cipher.getTransformation().getAlgorithmBlockSize()); + } + + /** + * Overrides the {@link CTRCryptoInputStream#initCipher()}. + * Initializes the cipher. + */ + @Override + protected void initCipher() { + // Do nothing for initCipher + // Will reset the cipher when reset the stream offset + } + + /** + * 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); + Utils.calculateIV(initIV, counter, iv); + try { + cipher.init(CryptoCipher.DECRYPT_MODE, key, new IvParameterSpec(iv)); + } catch (InvalidKeyException e) { + throw new IOException(e); + } catch (InvalidAlgorithmParameterException e) { + throw new IOException(e); + } + cipherReset = false; + } + + /** + * 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. + } + + /** + * Does the decryption using out as output. + * + * @param out the output ByteBuffer. + * @throws IOException if an I/O error occurs. + */ + protected void decryptBuffer(ByteBuffer out) + throws IOException { + int inputSize = inBuffer.remaining(); + try { + int n = cipher.update(inBuffer, out); + if (n < inputSize) { + /** + * Typically code will not get here. CryptoCipher#update will consume all + * input data and put result in outBuffer. + * CryptoCipher#doFinal will reset the cipher context. + */ + cipher.doFinal(inBuffer, out); + cipherReset = true; + } + } catch (ShortBufferException e) { + throw new IOException(e); + } catch (IllegalBlockSizeException e) { + throw new IOException(e); + } catch (BadPaddingException e) { + throw new IOException(e); + } + } +} http://git-wip-us.apache.org/repos/asf/commons-crypto/blob/ea89d802/src/main/java/org/apache/commons/crypto/stream/CTRCryptoOutputStream.java ---------------------------------------------------------------------- diff --git a/src/main/java/org/apache/commons/crypto/stream/CTRCryptoOutputStream.java b/src/main/java/org/apache/commons/crypto/stream/CTRCryptoOutputStream.java new file mode 100644 index 0000000..483f03c --- /dev/null +++ b/src/main/java/org/apache/commons/crypto/stream/CTRCryptoOutputStream.java @@ -0,0 +1,385 @@ +/** + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.commons.crypto.stream; + +import java.io.IOException; +import java.io.OutputStream; +import java.nio.ByteBuffer; +import java.nio.channels.WritableByteChannel; +import java.security.InvalidAlgorithmParameterException; +import java.security.InvalidKeyException; +import java.util.Properties; + +import javax.crypto.BadPaddingException; +import javax.crypto.IllegalBlockSizeException; +import javax.crypto.ShortBufferException; +import javax.crypto.spec.IvParameterSpec; +import javax.crypto.spec.SecretKeySpec; + +import org.apache.commons.crypto.cipher.CryptoCipher; +import org.apache.commons.crypto.cipher.CipherTransformation; +import org.apache.commons.crypto.stream.output.ChannelOutput; +import org.apache.commons.crypto.stream.output.Output; +import org.apache.commons.crypto.stream.output.StreamOutput; +import org.apache.commons.crypto.utils.Utils; + +/** + * CTRCryptoOutputStream encrypts data. It is not thread-safe. AES CTR mode is + * required in order to ensure that the plain text and cipher text have a 1:1 + * mapping. The encryption is buffer based. The key points of the encryption are + * (1) calculating counter and (2) padding through stream position. + * <p/> + * counter = base + pos/(algorithm blocksize); + * padding = pos%(algorithm blocksize); + * <p/> + * The underlying stream offset is maintained as state. + */ +public class CTRCryptoOutputStream extends CryptoOutputStream { + /** + * Underlying stream offset. + */ + long streamOffset = 0; + + /** + * The initial IV. + */ + protected final byte[] initIV; + + /** + * Initialization vector for the cipher. + */ + protected byte[] iv; + + /** + * Padding = pos%(algorithm blocksize); Padding is put into {@link #inBuffer} + * before any other data goes in. The purpose of padding is to put input data + * at proper position. + */ + private byte padding; + + /** + * Flag to mark whether the cipher has been reset + */ + private boolean cipherReset = false; + + /** + * Constructs a {@link org.apache.commons.crypto.stream.CTRCryptoOutputStream}. + * + * @param props The <code>Properties</code> class represents a set of + * properties. + * @param out the output stream. + * @param key crypto key for the cipher. + * @param iv Initialization vector for the cipher. + * @throws IOException if an I/O error occurs. + */ + public CTRCryptoOutputStream(Properties props, OutputStream out, byte[] key, + byte[] iv) + throws IOException { + this(props, out, key, iv, 0); + } + + /** + * Constructs a {@link org.apache.commons.crypto.stream.CTRCryptoOutputStream}. + * + * @param props The <code>Properties</code> class represents a set of + * properties. + * @param out the WritableByteChannel instance. + * @param key crypto key for the cipher. + * @param iv Initialization vector for the cipher. + * @throws IOException if an I/O error occurs. + */ + public CTRCryptoOutputStream(Properties props, WritableByteChannel out, + byte[] key, byte[] iv) + throws IOException { + this(props, out, key, iv, 0); + } + + /** + * Constructs a {@link org.apache.commons.crypto.stream.CTRCryptoOutputStream}. + * + * @param out the output stream. + * @param cipher the CryptoCipher instance. + * @param bufferSize the bufferSize. + * @param key crypto key for the cipher. + * @param iv Initialization vector for the cipher. + * @throws IOException if an I/O error occurs. + */ + public CTRCryptoOutputStream(OutputStream out, CryptoCipher cipher, int bufferSize, + byte[] key, byte[] iv) throws IOException { + this(out, cipher, bufferSize, key, iv, 0); + } + + /** + * Constructs a {@link org.apache.commons.crypto.stream.CTRCryptoOutputStream}. + * + * @param channel the WritableByteChannel instance. + * @param cipher the CryptoCipher instance. + * @param bufferSize the bufferSize. + * @param key crypto key for the cipher. + * @param iv Initialization vector for the cipher. + * @throws IOException if an I/O error occurs. + */ + public CTRCryptoOutputStream(WritableByteChannel channel, CryptoCipher cipher, + int bufferSize, byte[] key, byte[] iv) throws IOException { + this(channel, cipher, bufferSize, key, iv, 0); + } + + /** + * Constructs a {@link org.apache.commons.crypto.stream.CTRCryptoOutputStream}. + * + * @param output the Output instance. + * @param cipher the CryptoCipher instance. + * @param bufferSize the bufferSize. + * @param key crypto key for the cipher. + * @param iv Initialization vector for the cipher. + * @throws IOException if an I/O error occurs. + */ + public CTRCryptoOutputStream(Output output, CryptoCipher cipher, int bufferSize, + byte[] key, byte[] iv) + throws IOException { + this(output, cipher, bufferSize, key, iv, 0); + } + + /** + * Constructs a {@link org.apache.commons.crypto.stream.CTRCryptoOutputStream}. + * + * @param props The <code>Properties</code> class represents a set of + * properties. + * @param out the output stream. + * @param key crypto key for the cipher. + * @param iv Initialization vector for the cipher. + * @param streamOffset the start offset in the data. + * @throws IOException if an I/O error occurs. + */ + public CTRCryptoOutputStream(Properties props, OutputStream out, byte[] key, + byte[] iv, long streamOffset) + throws IOException { + this(out, Utils.getCipherInstance(CipherTransformation.AES_CTR_NOPADDING, props), + Utils.getBufferSize(props), key, iv, streamOffset); + } + + /** + * Constructs a {@link org.apache.commons.crypto.stream.CTRCryptoOutputStream}. + * + * @param props The <code>Properties</code> class represents a set of + * properties. + * @param out the WritableByteChannel instance. + * @param key crypto key for the cipher. + * @param iv Initialization vector for the cipher. + * @param streamOffset the start offset in the data. + * @throws IOException if an I/O error occurs. + */ + public CTRCryptoOutputStream(Properties props, WritableByteChannel out, + byte[] key, byte[] iv, long streamOffset) + throws IOException { + this(out, Utils.getCipherInstance(CipherTransformation.AES_CTR_NOPADDING, props), + Utils.getBufferSize(props), key, iv, streamOffset); + } + + /** + * Constructs a {@link org.apache.commons.crypto.stream.CTRCryptoOutputStream}. + * + * @param out the output stream. + * @param cipher the CryptoCipher instance. + * @param bufferSize the bufferSize. + * @param key crypto key for the cipher. + * @param iv Initialization vector for the cipher. + * @param streamOffset the start offset in the data. + * @throws IOException if an I/O error occurs. + */ + public CTRCryptoOutputStream(OutputStream out, CryptoCipher cipher, int bufferSize, + byte[] key, byte[] iv, long streamOffset) throws IOException { + this(new StreamOutput(out, bufferSize), cipher, + bufferSize, key, iv, streamOffset); + } + + /** + * Constructs a {@link org.apache.commons.crypto.stream.CTRCryptoOutputStream}. + * + * @param channel the WritableByteChannel instance. + * @param cipher the CryptoCipher instance. + * @param bufferSize the bufferSize. + * @param key crypto key for the cipher. + * @param iv Initialization vector for the cipher. + * @param streamOffset the start offset in the data. + * @throws IOException if an I/O error occurs. + */ + public CTRCryptoOutputStream(WritableByteChannel channel, CryptoCipher cipher, + int bufferSize, byte[] key, byte[] iv, + long streamOffset) throws IOException { + this(new ChannelOutput(channel), cipher, + bufferSize, key, iv, streamOffset); + } + + /** + * Constructs a {@link org.apache.commons.crypto.stream.CTRCryptoOutputStream}. + * + * @param output the output stream. + * @param cipher the CryptoCipher instance. + * @param bufferSize the bufferSize. + * @param key crypto key for the cipher. + * @param iv Initialization vector for the cipher. + * @param streamOffset the start offset in the data. + * @throws IOException if an I/O error occurs. + */ + public CTRCryptoOutputStream(Output output, CryptoCipher cipher, int bufferSize, + byte[] key, byte[] iv, long streamOffset) + throws IOException { + super(output, cipher, bufferSize, new SecretKeySpec(key, "AES"), new IvParameterSpec(iv)); + + Utils.checkStreamCipher(cipher); + this.streamOffset = streamOffset; + this.initIV = iv.clone(); + this.iv = iv.clone(); + + resetCipher(); + } + + /** + * Does the encryption, input is {@link #inBuffer} and output is + * {@link #outBuffer}. + * + * @throws IOException if an I/O error occurs. + */ + @Override + protected void encrypt() throws IOException { + Utils.checkState(inBuffer.position() >= padding); + if (inBuffer.position() == padding) { + // There is no real data in the inBuffer. + return; + } + + inBuffer.flip(); + outBuffer.clear(); + encryptBuffer(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); + padding = 0; + } + + final int len = output.write(outBuffer); + streamOffset += len; + if (cipherReset) { + /* + * This code is generally not executed since the encryptor usually + * maintains encryption context (e.g. the counter) internally. However, + * some implementations can't maintain context so a re-init is necessary + * after each encryption call. + */ + resetCipher(); + } + } + + /** + * Does final encryption of the last data. + * + * @throws IOException if an I/O error occurs. + */ + @Override + protected void encryptFinal() throws IOException { + // The same as the normal encryption for Counter mode + encrypt(); + } + + /** + * Overrides the {@link CryptoOutputStream#initCipher()}. + * Initializes the cipher. + */ + @Override + protected void initCipher() { + // Do nothing for initCipher + // Will reset the cipher considering the stream offset + } + + /** + * 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.getTransformation().getAlgorithmBlockSize(); + padding = + (byte)(streamOffset % cipher.getTransformation().getAlgorithmBlockSize()); + inBuffer.position(padding); // Set proper position for input data. + + Utils.calculateIV(initIV, counter, iv); + try { + cipher.init(CryptoCipher.ENCRYPT_MODE, key, new IvParameterSpec(iv)); + } catch (InvalidKeyException e) { + throw new IOException(e); + }catch (InvalidAlgorithmParameterException e) { + throw new IOException(e); + } + cipherReset = false; + } + + /** + * Does the encryption if the ByteBuffer data. + * + * @param out the output ByteBuffer. + * @throws IOException if an I/O error occurs. + */ + private void encryptBuffer(ByteBuffer out) + throws IOException { + int inputSize = inBuffer.remaining(); + try { + int n = cipher.update(inBuffer, out); + if (n < inputSize) { + /** + * Typically code will not get here. CryptoCipher#update will consume all + * input data and put result in outBuffer. + * CryptoCipher#doFinal will reset the cipher context. + */ + cipher.doFinal(inBuffer, out); + cipherReset = true; + } + } catch (ShortBufferException e) { + throw new IOException(e); + } catch (BadPaddingException e) { + throw new IOException(e); + } catch (IllegalBlockSizeException e) { + throw new IOException(e); + } + } + + /** + * Get the underlying stream offset + * + * @return the underlying stream offset + */ + protected long getStreamOffset() { + return streamOffset; + } + + /** + * Set the underlying stream offset + * + * @param streamOffset the underlying stream offset + */ + protected void setStreamOffset(long streamOffset) { + this.streamOffset = streamOffset; + } +} http://git-wip-us.apache.org/repos/asf/commons-crypto/blob/ea89d802/src/main/java/org/apache/commons/crypto/stream/CipherInputStream.java ---------------------------------------------------------------------- diff --git a/src/main/java/org/apache/commons/crypto/stream/CipherInputStream.java b/src/main/java/org/apache/commons/crypto/stream/CipherInputStream.java deleted file mode 100644 index 83df143..0000000 --- a/src/main/java/org/apache/commons/crypto/stream/CipherInputStream.java +++ /dev/null @@ -1,559 +0,0 @@ -/** - * Licensed to the Apache Software Foundation (ASF) under one - * or more contributor license agreements. See the NOTICE file - * distributed with this work for additional information - * regarding copyright ownership. The ASF licenses this file - * to you under the Apache License, Version 2.0 (the - * "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package org.apache.commons.crypto.stream; - -import java.io.IOException; -import java.io.InputStream; -import java.nio.ByteBuffer; -import java.nio.channels.Channel; -import java.nio.channels.ReadableByteChannel; -import java.security.InvalidAlgorithmParameterException; -import java.security.InvalidKeyException; -import java.security.Key; -import java.security.spec.AlgorithmParameterSpec; -import java.util.Properties; -import javax.crypto.BadPaddingException; -import javax.crypto.IllegalBlockSizeException; -import javax.crypto.ShortBufferException; -import javax.crypto.spec.IvParameterSpec; - -import org.apache.commons.crypto.cipher.Cipher; -import org.apache.commons.crypto.cipher.CipherTransformation; -import org.apache.commons.crypto.stream.input.ChannelInput; -import org.apache.commons.crypto.stream.input.Input; -import org.apache.commons.crypto.stream.input.StreamInput; -import org.apache.commons.crypto.utils.Utils; - -/** - * CryptoInputStream reads input data and decrypts data in stream manner. It supports - * any mode of operations such as AES CBC/CTR/GCM mode in concept.It is not thread-safe. - * - */ - -public class CipherInputStream extends InputStream implements - ReadableByteChannel { - private final byte[] oneByteBuf = new byte[1]; - - /**The Cipher instance.*/ - final Cipher cipher; - - /**The buffer size.*/ - final int bufferSize; - - /**Crypto key for the cipher.*/ - final Key key; - - /** the algorithm parameters */ - final AlgorithmParameterSpec params; - - /** Flag to mark whether the input stream is closed.*/ - private boolean closed; - - /** Flag to mark whether do final of the cipher to end the decrypting stream.*/ - private boolean finalDone = false; - - /**The input data.*/ - Input input; - - /** - * Input data buffer. The data starts at inBuffer.position() and ends at - * to inBuffer.limit(). - */ - protected ByteBuffer inBuffer; - - /** - * The decrypted data buffer. The data starts at outBuffer.position() and - * ends at outBuffer.limit(). - */ - protected ByteBuffer outBuffer; - - /** - * Constructs a {@link CipherInputStream}. - * - * @param transformation the CipherTransformation instance. - * @param props The <code>Properties</code> class represents a set of - * properties. - * @param in the input stream. - * @param key crypto key for the cipher. - * @param params the algorithm parameters. - * @throws IOException if an I/O error occurs. - */ - public CipherInputStream(CipherTransformation transformation, - Properties props, InputStream in, Key key, AlgorithmParameterSpec params) - throws IOException { - this(in, Utils.getCipherInstance(transformation, props), Utils.getBufferSize(props), key, - params); - } - - /** - * Constructs a {@link CipherInputStream}. - * - * @param transformation the CipherTransformation instance. - * @param props The <code>Properties</code> class represents a set of - * properties. - * @param in the ReadableByteChannel object. - * @param key crypto key for the cipher. - * @param params the algorithm parameters. - * @throws IOException if an I/O error occurs. - */ - public CipherInputStream(CipherTransformation transformation, - Properties props, ReadableByteChannel in, Key key, AlgorithmParameterSpec params) - throws IOException { - this(in, Utils.getCipherInstance(transformation, props), - Utils.getBufferSize(props), key, params); - } - - /** - * Constructs a {@link CipherInputStream}. - * - * @param cipher the cipher instance. - * @param in the input stream. - * @param bufferSize the bufferSize. - * @param key crypto key for the cipher. - * @param params the algorithm parameters. - * @throws IOException if an I/O error occurs. - */ - public CipherInputStream(InputStream in, Cipher cipher, int bufferSize, - Key key, AlgorithmParameterSpec params) throws IOException { - this(new StreamInput(in, bufferSize), cipher, bufferSize, key, params); - } - - /** - * Constructs a {@link CipherInputStream}. - * - * @param in the ReadableByteChannel instance. - * @param cipher the cipher instance. - * @param bufferSize the bufferSize. - * @param key crypto key for the cipher. - * @param params the algorithm parameters. - * @throws IOException if an I/O error occurs. - */ - public CipherInputStream(ReadableByteChannel in, Cipher cipher, - int bufferSize, Key key, AlgorithmParameterSpec params) throws IOException { - this(new ChannelInput(in), cipher, bufferSize, key, params); - } - - /** - * Constructs a {@link CipherInputStream}. - * - * @param input the input data. - * @param cipher the cipher instance. - * @param bufferSize the bufferSize. - * @param key crypto key for the cipher. - * @param params the algorithm parameters. - * @throws IOException if an I/O error occurs. - */ - public CipherInputStream(Input input, Cipher cipher, int bufferSize, - Key key, AlgorithmParameterSpec params) throws IOException { - this.input = input; - this.cipher = cipher; - this.bufferSize = Utils.checkBufferSize(cipher, bufferSize); - - this.key = key; - this.params = params; - if (!(params instanceof IvParameterSpec)) { - //other AlgorithmParameterSpec such as GCMParameterSpec is not supported now. - throw new IOException("Illegal parameters"); - } - - inBuffer = ByteBuffer.allocateDirect(this.bufferSize); - outBuffer = ByteBuffer.allocateDirect(this.bufferSize + - cipher.getTransformation().getAlgorithmBlockSize()); - outBuffer.limit(0); - - initCipher(); - } - - /** - * Overrides the {@link java.io.InputStream#read()}. - * Reads the next byte of data from the input stream. - * - * @return the next byte of data, or <code>-1</code> if the end of the - * stream is reached. - * @throws IOException if an I/O error occurs. - */ - @Override - public int read() throws IOException { - int n; - while ((n = read(oneByteBuf, 0, 1)) == 0) ; - return (n == -1) ? -1 : oneByteBuf[0] & 0xff; - } - - /** - * Overrides the {@link java.io.InputStream#read(byte[], int, int)}. - * Decryption is buffer based. - * If there is data in {@link #outBuffer}, then read it out of this buffer. - * If there is no data in {@link #outBuffer}, then read more from the - * underlying stream and do the decryption. - * - * @param b the buffer into which the decrypted data is read. - * @param off the buffer offset. - * @param len the maximum number of decrypted data bytes to read. - * @return int the total number of decrypted data bytes read into the buffer. - * @throws IOException if an I/O error occurs. - */ - @Override - public int read(byte[] b, int off, int len) throws IOException { - checkStream(); - if (b == null) { - throw new NullPointerException(); - } else if (off < 0 || len < 0 || len > b.length - off) { - throw new IndexOutOfBoundsException(); - } else if (len == 0) { - return 0; - } - - int remaining = outBuffer.remaining(); - if (remaining > 0) { - // Satisfy the read with the existing data - int n = Math.min(len, remaining); - outBuffer.get(b, off, n); - return n; - } else { - // No data in the out buffer, try read new data and decrypt it - int nd = decryptMore(); - if(nd <= 0) - return nd; - - int n = Math.min(len, outBuffer.remaining()); - outBuffer.get(b, off, n); - return n; - } - } - - /** - * Overrides the {@link java.io.InputStream#skip(long)}. - * Skips over and discards <code>n</code> bytes of data from this input - * stream. - * - * @param n the number of bytes to be skipped. - * @return the actual number of bytes skipped. - * @throws IOException if an I/O error occurs. - */ - @Override - public long skip(long n) throws IOException { - Utils.checkArgument(n >= 0, "Negative skip length."); - checkStream(); - - if (n == 0) { - return 0; - } - - long remaining = n; - int nd; - - while (remaining > 0) { - if(remaining <= outBuffer.remaining()) { - // Skip in the remaining buffer - int pos = outBuffer.position() + (int) remaining; - outBuffer.position(pos); - - remaining = 0; - break; - } else { - remaining -= outBuffer.remaining(); - outBuffer.clear(); - } - - nd = decryptMore(); - if (nd < 0) { - break; - } - } - - return n - remaining; - } - - /** - * Overrides the {@link InputStream#available()}. - * Returns an estimate of the number of bytes that can be read (or - * skipped over) from this input stream without blocking by the next - * invocation of a method for this input stream. - * - * @return an estimate of the number of bytes that can be read (or skipped - * over) from this input stream without blocking or {@code 0} when - * it reaches the end of the input stream. - * @throws IOException if an I/O error occurs. - */ - @Override - public int available() throws IOException { - checkStream(); - - return input.available() + outBuffer.remaining(); - } - - /** - * Overrides the {@link InputStream#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 (closed) { - return; - } - - input.close(); - freeBuffers(); - cipher.close(); - super.close(); - closed = true; - } - - /** - * Overrides the {@link java.io.InputStream#mark(int)}. - * For {@link CipherInputStream},we don't support the mark method. - * - * @param readlimit the maximum limit of bytes that can be read before - * the mark position becomes invalid. - */ - @Override - public void mark(int readlimit) { - } - - /** - * Overrides the {@link InputStream#reset()}. - * For {@link CipherInputStream},we don't support the reset method. - * - * @throws IOException if an I/O error occurs. - */ - @Override - public void reset() throws IOException { - throw new IOException("Mark/reset not supported"); - } - - /** - * Overrides the {@link InputStream#markSupported()}. - * - * @return false,the {@link CTRCipherInputStream} don't support the mark method. - */ - @Override - public boolean markSupported() { - return false; - } - - /** - * Overrides the {@link Channel#isOpen()}. - * - * @return <tt>true</tt> if, and only if, this channel is open. - */ - @Override - public boolean isOpen() { - return !closed; - } - - /** - * Overrides the {@link java.nio.channels.ReadableByteChannel#read(ByteBuffer)}. - * Reads a sequence of bytes from this channel into the given buffer. - * - * @param dst The buffer into which bytes are to be transferred. - * @return The number of bytes read, possibly zero, or <tt>-1</tt> if the - * channel has reached end-of-stream. - * @throws IOException if an I/O error occurs. - */ - @Override - public int read(ByteBuffer dst) throws IOException { - checkStream(); - int remaining = outBuffer.remaining(); - if (remaining <= 0) { - // Decrypt more data - int nd = decryptMore(); - if(nd < 0) { - return -1; - } - } - - // Copy decrypted data from outBuffer to dst - remaining = outBuffer.remaining(); - final int toRead = dst.remaining(); - if (toRead <= remaining) { - final int limit = outBuffer.limit(); - outBuffer.limit(outBuffer.position() + toRead); - dst.put(outBuffer); - outBuffer.limit(limit); - return toRead; - } else { - dst.put(outBuffer); - return remaining; - } - } - - /** - * Gets the buffer size. - * - * @return the bufferSize. - */ - protected int getBufferSize() { - return bufferSize; - } - - /** - * Gets the key. - * - * @return the key. - */ - protected Key getKey() { - return key; - } - - - /** - * Gets the internal Cipher. - * - * @return the cipher instance. - */ - protected Cipher getCipher() { - return cipher; - } - - /** - * Gets the specification of cryptographic parameters. - * - * @return the params. - */ - protected AlgorithmParameterSpec getParams() { - return params; - } - - /** - * Gets the input. - * - * @return the input. - */ - protected Input getInput() { - return input; - } - - /** - * Initializes the cipher. - * - * @throws IOException if an I/O error occurs. - */ - protected void initCipher() - throws IOException { - try { - cipher.init(Cipher.DECRYPT_MODE, key, params); - } catch (InvalidKeyException e) { - throw new IOException(e); - } catch(InvalidAlgorithmParameterException e) { - throw new IOException(e); - } - } - - /** - * Decrypts more data by reading the under layer stream. The decrypted data will - * be put in the output buffer. If the end of the under stream reached, we will - * do final of the cipher to finish all the decrypting of data. - * - * @return The number of decrypted data. -1 if end of the decrypted stream. - */ - protected int decryptMore() throws IOException { - if(finalDone) { - return -1; - } - - int n = input.read(inBuffer); - if (n < 0) { - // The stream is end, finalize the cipher stream - decryptFinal(); - - // Satisfy the read with the remaining - int remaining = outBuffer.remaining(); - if (remaining > 0) { - return remaining; - } - - // End of the stream - return -1; - } else if(n == 0) { - // No data is read, but the stream is not end yet - return 0; - } else { - decrypt(); - return outBuffer.remaining(); - } - } - - /** - * 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. - */ - protected void decrypt() throws IOException { - // Prepare the input buffer and clear the out buffer - inBuffer.flip(); - outBuffer.clear(); - - try { - cipher.update(inBuffer, outBuffer); - } catch (ShortBufferException e) { - throw new IOException(e); - } - - // Clear the input buffer and prepare out buffer - inBuffer.clear(); - outBuffer.flip(); - } - - /** - * Does final of the cipher to end the decrypting stream. - * - *@throws IOException if an I/O error occurs. - */ - protected void decryptFinal() throws IOException { - // Prepare the input buffer and clear the out buffer - inBuffer.flip(); - outBuffer.clear(); - - try { - cipher.doFinal(inBuffer, outBuffer); - finalDone = true; - } catch (ShortBufferException e) { - throw new IOException(e); - } catch (IllegalBlockSizeException e) { - throw new IOException(e); - } catch( BadPaddingException e) { - throw new IOException(e); - } - - // Clear the input buffer and prepare out buffer - inBuffer.clear(); - outBuffer.flip(); - } - - /** - * Checks whether the stream is closed. - * - * @throws IOException if an I/O error occurs. - */ - protected void checkStream() throws IOException { - if (closed) { - throw new IOException("Stream closed"); - } - } - - /** Forcibly free the direct buffers. */ - protected void freeBuffers() { - Utils.freeDirectBuffer(inBuffer); - Utils.freeDirectBuffer(outBuffer); - } -}