You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@commons.apache.org by sd...@apache.org on 2016/04/29 11:11:11 UTC
[4/5] commons-crypto git commit: CRYPTO-33: avoid shadowing JVM class
renaming Cipher/Stream/Random classes
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);
- }
-}