You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@commons.apache.org by di...@apache.org on 2016/04/26 05:38:47 UTC
[1/3] commons-crypto git commit: CRYPTO-12: Rename CryptoInputStream
to CipherInputStream and CryptoOutputStream to CipherOutputStream (Xianda Ke
via Dian Fu)
Repository: commons-crypto
Updated Branches:
refs/heads/master f819dd404 -> ad81d236a
http://git-wip-us.apache.org/repos/asf/commons-crypto/blob/ad81d236/src/test/java/org/apache/commons/crypto/stream/PositionedCipherInputStreamTest.java
----------------------------------------------------------------------
diff --git a/src/test/java/org/apache/commons/crypto/stream/PositionedCipherInputStreamTest.java b/src/test/java/org/apache/commons/crypto/stream/PositionedCipherInputStreamTest.java
new file mode 100644
index 0000000..617f1e3
--- /dev/null
+++ b/src/test/java/org/apache/commons/crypto/stream/PositionedCipherInputStreamTest.java
@@ -0,0 +1,381 @@
+/**
+ * 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 org.apache.commons.crypto.cipher.Cipher;
+import org.apache.commons.crypto.cipher.CipherTransformation;
+import org.apache.commons.crypto.cipher.JceCipher;
+import org.apache.commons.crypto.cipher.OpensslCipher;
+import org.apache.commons.crypto.stream.input.Input;
+import org.apache.commons.crypto.utils.ReflectionUtils;
+import org.junit.Assert;
+import org.junit.Before;
+import org.junit.Test;
+
+import java.io.ByteArrayOutputStream;
+import java.io.IOException;
+import java.io.OutputStream;
+import java.nio.ByteBuffer;
+import java.security.SecureRandom;
+import java.util.Arrays;
+import java.util.Properties;
+import java.util.Random;
+
+public class PositionedCipherInputStreamTest {
+
+ private final int dataLen = 20000;
+ private byte[] testData = new byte[dataLen];
+ private byte[] encData;
+ private Properties props = new Properties();
+ private byte[] key = new byte[16];
+ private byte[] iv = new byte[16];
+ int bufferSize = 2048;
+ int bufferSizeLess = bufferSize - 1;
+ int bufferSizeMore = bufferSize + 1;
+ int length = 1024;
+ int lengthLess = length - 1;
+ int lengthMore = length + 1;
+
+ private final String jceCipherClass = JceCipher.class.getName();
+ private final String opensslCipherClass = OpensslCipher.class.getName();
+ private CipherTransformation transformation =
+ CipherTransformation.AES_CTR_NOPADDING;
+
+ @Before
+ public void before() throws IOException {
+ Random random = new SecureRandom();
+ random.nextBytes(testData);
+ random.nextBytes(key);
+ random.nextBytes(iv);
+ prepareData();
+ }
+
+ private void prepareData() throws IOException {
+ Cipher cipher = null;
+ try {
+ cipher = (Cipher)ReflectionUtils.newInstance(
+ ReflectionUtils.getClassByName(jceCipherClass),
+ props, transformation);
+ } catch (ClassNotFoundException cnfe) {
+ throw new IOException("Illegal crypto cipher!");
+ }
+
+ ByteArrayOutputStream baos = new ByteArrayOutputStream();
+ // encryption data
+ OutputStream out = new CipherOutputStream(baos, cipher, bufferSize, key, iv);
+ out.write(testData);
+ out.flush();
+ out.close();
+ encData = baos.toByteArray();
+ }
+
+ public void setUp() throws IOException {}
+
+ private PositionedCipherInputStream getCipherInputStream(Cipher cipher,
+ int bufferSize) throws IOException {
+ return new PositionedCipherInputStream(new PositionedInputForTest(
+ Arrays.copyOf(encData, encData.length)), cipher, bufferSize, key, iv, 0);
+ }
+
+ @Test
+ public void doTest() throws Exception {
+ testCipher(jceCipherClass);
+ testCipher(opensslCipherClass);
+ }
+
+ private void testCipher(String cipherClass) throws Exception {
+ doPositionedReadTests(cipherClass);
+ doReadFullyTests(cipherClass);
+ doSeekTests(cipherClass);
+ doMultipleReadTest(cipherClass);
+ }
+
+ // when there are multiple positioned read actions and one read action,
+ // they will not interfere each other.
+ private void doMultipleReadTest(String cipherClass) throws Exception {
+ PositionedCipherInputStream in = getCipherInputStream(
+ getCipher(cipherClass), bufferSize);
+ int position = 0;
+ while (in.available() > 0) {
+ ByteBuffer buf = ByteBuffer.allocate(length);
+ byte[] bytes1 = new byte[length];
+ byte[] bytes2 = new byte[lengthLess];
+ // do the read and position read
+ int pn1 = in.read(position, bytes1, 0, length);
+ int n = in.read(buf);
+ int pn2 = in.read(position, bytes2, 0, lengthLess);
+
+ // verify the result
+ if (pn1 > 0) {
+ compareByteArray(testData, position, bytes1, pn1);
+ }
+
+ if (pn2 > 0) {
+ compareByteArray(testData, position, bytes2, pn2);
+ }
+
+ if (n > 0) {
+ compareByteArray(testData, position, buf.array(), n);
+ position += n;
+ } else {
+ break;
+ }
+ }
+ in.close();
+ }
+
+ private void doPositionedReadTests(String cipherClass) throws Exception {
+ // test with different bufferSize when position = 0
+ testPositionedReadLoop(cipherClass, 0, length, bufferSize, dataLen);
+ testPositionedReadLoop(cipherClass, 0, length, bufferSizeLess, dataLen);
+ testPositionedReadLoop(cipherClass, 0, length, bufferSizeMore, dataLen);
+ // test with different position when bufferSize = 2048
+ testPositionedReadLoop(cipherClass, dataLen / 2, length, bufferSize, dataLen);
+ testPositionedReadLoop(cipherClass, dataLen / 2 - 1, length,
+ bufferSizeLess, dataLen);
+ testPositionedReadLoop(cipherClass, dataLen / 2 + 1, length,
+ bufferSizeMore, dataLen);
+ // position = -1 or position = max length, read nothing and return -1
+ testPositionedReadNone(cipherClass, -1, length, bufferSize);
+ testPositionedReadNone(cipherClass, dataLen, length, bufferSize);
+ }
+
+ private void doReadFullyTests(String cipherClass) throws Exception {
+ // test with different bufferSize when position = 0
+ testReadFullyLoop(cipherClass, 0, length, bufferSize, dataLen);
+ testReadFullyLoop(cipherClass, 0, length, bufferSizeLess, dataLen);
+ testReadFullyLoop(cipherClass, 0, length, bufferSizeMore, dataLen);
+ // test with different length when position = 0
+ testReadFullyLoop(cipherClass, 0, length, bufferSize, dataLen);
+ testReadFullyLoop(cipherClass, 0, lengthLess, bufferSize, dataLen);
+ testReadFullyLoop(cipherClass, 0, lengthMore, bufferSize, dataLen);
+ // test read fully failed
+ testReadFullyFailed(cipherClass, -1, length, bufferSize);
+ testReadFullyFailed(cipherClass, dataLen, length, bufferSize);
+ testReadFullyFailed(cipherClass, dataLen - length + 1, length, bufferSize);
+ }
+
+ private void doSeekTests(String cipherClass) throws Exception {
+ // test with different length when position = 0
+ testSeekLoop(cipherClass, 0, length, bufferSize);
+ testSeekLoop(cipherClass, 0, lengthLess, bufferSize);
+ testSeekLoop(cipherClass, 0, lengthMore, bufferSize);
+ // there should be none data read when position = dataLen
+ testSeekLoop(cipherClass, dataLen, length, bufferSize);
+ // test exception when position = -1
+ testSeekFailed(cipherClass, -1, bufferSize);
+ }
+
+ private void testSeekLoop(String cipherClass, int position, int length,
+ int bufferSize) throws Exception {
+ PositionedCipherInputStream in = getCipherInputStream(
+ getCipher(cipherClass), bufferSize);
+ while (in.available() > 0) {
+ in.seek(position);
+ ByteBuffer buf = ByteBuffer.allocate(length);
+ int n = in.read(buf);
+ if (n > 0) {
+ compareByteArray(testData, position, buf.array(), n);
+ position += n;
+ } else {
+ break;
+ }
+ }
+ in.close();
+ }
+
+ // test for the out of index position, eg, -1.
+ private void testSeekFailed(String cipherClass, int position,
+ int bufferSize) throws Exception {
+ PositionedCipherInputStream in = getCipherInputStream(
+ getCipher(cipherClass), bufferSize);
+ try {
+ in.seek(position);
+ Assert.fail("Excepted exception for cannot seek to negative offset.");
+ } catch (IllegalArgumentException iae) {
+ }
+ in.close();
+ }
+
+ private void testPositionedReadLoop(String cipherClass, int position,
+ int length, int bufferSize, int total) throws Exception {
+ PositionedCipherInputStream in = getCipherInputStream(
+ getCipher(cipherClass), bufferSize);
+ // do the position read until the end of data
+ while (position < total) {
+ byte[] bytes = new byte[length];
+ int n = in.read(position, bytes, 0, length);
+ if (n >= 0) {
+ compareByteArray(testData, position, bytes, n);
+ position += n;
+ } else {
+ break;
+ }
+ }
+ in.close();
+ }
+
+ // test for the out of index position, eg, -1.
+ private void testPositionedReadNone(String cipherClass, int position,
+ int length, int bufferSize) throws Exception {
+ PositionedCipherInputStream in = getCipherInputStream(
+ getCipher(cipherClass), bufferSize);
+ byte[] bytes = new byte[length];
+ int n = in.read(position, bytes, 0, length);
+ Assert.assertEquals(n, -1);
+ in.close();
+ }
+
+ private void testReadFullyLoop(String cipherClass,int position,
+ int length, int bufferSize, int total) throws Exception {
+ PositionedCipherInputStream in = getCipherInputStream(
+ getCipher(cipherClass), bufferSize);
+
+ // do the position read full until remain < length
+ while (position + length <= total) {
+ byte[] bytes = new byte[length];
+ in.readFully(position, bytes, 0, length);
+ compareByteArray(testData, position, bytes, length);
+ position += length;
+ }
+
+ in.close();
+ }
+
+ // test for the End of file reached before reading fully
+ private void testReadFullyFailed(String cipherClass, int position,
+ int length, int bufferSize) throws Exception {
+ PositionedCipherInputStream in = getCipherInputStream(
+ getCipher(cipherClass), bufferSize);
+ byte[] bytes = new byte[length];
+ try {
+ in.readFully(position, bytes, 0, length);
+ Assert.fail("Excepted EOFException.");
+ } catch (IOException ioe) {
+ // excepted exception
+ }
+ in.close();
+ }
+
+ // compare the data from pos with length and data2 from 0 with length
+ private void compareByteArray(byte[] data1, int pos, byte[] data2, int length) {
+ byte[] expectedData = new byte[length];
+ byte[] realData = new byte[length];
+ // get the expected data with the position and length
+ System.arraycopy(data1, pos, expectedData, 0, length);
+ // get the real data
+ System.arraycopy(data2, 0, realData, 0, length);
+ Assert.assertArrayEquals(expectedData, realData);
+ }
+
+ private Cipher getCipher(String cipherClass) throws IOException {
+ try {
+ return (Cipher)ReflectionUtils.newInstance(
+ ReflectionUtils.getClassByName(cipherClass), props, transformation);
+ } catch (ClassNotFoundException cnfe) {
+ throw new IOException("Illegal crypto cipher!");
+ }
+ }
+
+ class PositionedInputForTest implements Input {
+
+ byte[] data;
+ long pos;
+ long count;
+
+ public PositionedInputForTest(byte[] data) {
+ this.data = data;
+ this.pos = 0;
+ this.count = data.length;
+ }
+
+ @Override
+ public int read(ByteBuffer dst) throws IOException {
+ int remaining = (int)(count - pos);
+ if(remaining <= 0) {
+ return -1;
+ }
+
+ int length = Math.min(dst.remaining(), remaining);
+ dst.put(data, (int)pos, length);
+ pos += length;
+ return length;
+ }
+
+ @Override
+ public long skip(long n) throws IOException {
+ if (n <= 0) {
+ return 0;
+ }
+
+ long remaining = count - pos;
+ if(remaining < n) {
+ n = remaining;
+ }
+ pos += n;
+
+ return n;
+ }
+
+ @Override
+ public int read(long position, byte[] buffer, int offset, int length)
+ throws IOException {
+ if (buffer == null) {
+ throw new NullPointerException();
+ } else if (offset < 0 || length < 0 || length > buffer.length - offset) {
+ throw new IndexOutOfBoundsException();
+ }
+
+ if (position < 0 || position >= count) {
+ return -1;
+ }
+
+ long avail = count - position;
+ if (length > avail) {
+ length = (int)avail;
+ }
+ if (length <= 0) {
+ return 0;
+ }
+ System.arraycopy(data, (int)position, buffer, offset, length);
+ return length;
+ }
+
+ @Override
+ public void seek(long position) throws IOException {
+ if (pos < 0) {
+ throw new IOException("Negative seek offset");
+ } else if (position >= 0 && position < count) {
+ pos = position;
+ } else {
+ // to the end of file
+ pos = count;
+ }
+ }
+
+ @Override
+ public void close() throws IOException {
+ }
+
+ @Override
+ public int available() throws IOException {
+ return (int)(count - pos);
+ }
+ }
+}
http://git-wip-us.apache.org/repos/asf/commons-crypto/blob/ad81d236/src/test/java/org/apache/commons/crypto/stream/PositionedCryptoInputStreamTest.java
----------------------------------------------------------------------
diff --git a/src/test/java/org/apache/commons/crypto/stream/PositionedCryptoInputStreamTest.java b/src/test/java/org/apache/commons/crypto/stream/PositionedCryptoInputStreamTest.java
deleted file mode 100644
index 5907ba8..0000000
--- a/src/test/java/org/apache/commons/crypto/stream/PositionedCryptoInputStreamTest.java
+++ /dev/null
@@ -1,381 +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 org.apache.commons.crypto.cipher.Cipher;
-import org.apache.commons.crypto.cipher.CipherTransformation;
-import org.apache.commons.crypto.cipher.JceCipher;
-import org.apache.commons.crypto.cipher.OpensslCipher;
-import org.apache.commons.crypto.stream.input.Input;
-import org.apache.commons.crypto.utils.ReflectionUtils;
-import org.junit.Assert;
-import org.junit.Before;
-import org.junit.Test;
-
-import java.io.ByteArrayOutputStream;
-import java.io.IOException;
-import java.io.OutputStream;
-import java.nio.ByteBuffer;
-import java.security.SecureRandom;
-import java.util.Arrays;
-import java.util.Properties;
-import java.util.Random;
-
-public class PositionedCryptoInputStreamTest {
-
- private final int dataLen = 20000;
- private byte[] testData = new byte[dataLen];
- private byte[] encData;
- private Properties props = new Properties();
- private byte[] key = new byte[16];
- private byte[] iv = new byte[16];
- int bufferSize = 2048;
- int bufferSizeLess = bufferSize - 1;
- int bufferSizeMore = bufferSize + 1;
- int length = 1024;
- int lengthLess = length - 1;
- int lengthMore = length + 1;
-
- private final String jceCipherClass = JceCipher.class.getName();
- private final String opensslCipherClass = OpensslCipher.class.getName();
- private CipherTransformation transformation =
- CipherTransformation.AES_CTR_NOPADDING;
-
- @Before
- public void before() throws IOException {
- Random random = new SecureRandom();
- random.nextBytes(testData);
- random.nextBytes(key);
- random.nextBytes(iv);
- prepareData();
- }
-
- private void prepareData() throws IOException {
- Cipher cipher = null;
- try {
- cipher = (Cipher)ReflectionUtils.newInstance(
- ReflectionUtils.getClassByName(jceCipherClass),
- props, transformation);
- } catch (ClassNotFoundException cnfe) {
- throw new IOException("Illegal crypto cipher!");
- }
-
- ByteArrayOutputStream baos = new ByteArrayOutputStream();
- // encryption data
- OutputStream out = new CryptoOutputStream(baos, cipher, bufferSize, key, iv);
- out.write(testData);
- out.flush();
- out.close();
- encData = baos.toByteArray();
- }
-
- public void setUp() throws IOException {}
-
- private PositionedCryptoInputStream getCryptoInputStream(Cipher cipher,
- int bufferSize) throws IOException {
- return new PositionedCryptoInputStream(new PositionedInputForTest(
- Arrays.copyOf(encData, encData.length)), cipher, bufferSize, key, iv, 0);
- }
-
- @Test
- public void doTest() throws Exception {
- testCipher(jceCipherClass);
- testCipher(opensslCipherClass);
- }
-
- private void testCipher(String cipherClass) throws Exception {
- doPositionedReadTests(cipherClass);
- doReadFullyTests(cipherClass);
- doSeekTests(cipherClass);
- doMultipleReadTest(cipherClass);
- }
-
- // when there are multiple positioned read actions and one read action,
- // they will not interfere each other.
- private void doMultipleReadTest(String cipherClass) throws Exception {
- PositionedCryptoInputStream in = getCryptoInputStream(
- getCipher(cipherClass), bufferSize);
- int position = 0;
- while (in.available() > 0) {
- ByteBuffer buf = ByteBuffer.allocate(length);
- byte[] bytes1 = new byte[length];
- byte[] bytes2 = new byte[lengthLess];
- // do the read and position read
- int pn1 = in.read(position, bytes1, 0, length);
- int n = in.read(buf);
- int pn2 = in.read(position, bytes2, 0, lengthLess);
-
- // verify the result
- if (pn1 > 0) {
- compareByteArray(testData, position, bytes1, pn1);
- }
-
- if (pn2 > 0) {
- compareByteArray(testData, position, bytes2, pn2);
- }
-
- if (n > 0) {
- compareByteArray(testData, position, buf.array(), n);
- position += n;
- } else {
- break;
- }
- }
- in.close();
- }
-
- private void doPositionedReadTests(String cipherClass) throws Exception {
- // test with different bufferSize when position = 0
- testPositionedReadLoop(cipherClass, 0, length, bufferSize, dataLen);
- testPositionedReadLoop(cipherClass, 0, length, bufferSizeLess, dataLen);
- testPositionedReadLoop(cipherClass, 0, length, bufferSizeMore, dataLen);
- // test with different position when bufferSize = 2048
- testPositionedReadLoop(cipherClass, dataLen / 2, length, bufferSize, dataLen);
- testPositionedReadLoop(cipherClass, dataLen / 2 - 1, length,
- bufferSizeLess, dataLen);
- testPositionedReadLoop(cipherClass, dataLen / 2 + 1, length,
- bufferSizeMore, dataLen);
- // position = -1 or position = max length, read nothing and return -1
- testPositionedReadNone(cipherClass, -1, length, bufferSize);
- testPositionedReadNone(cipherClass, dataLen, length, bufferSize);
- }
-
- private void doReadFullyTests(String cipherClass) throws Exception {
- // test with different bufferSize when position = 0
- testReadFullyLoop(cipherClass, 0, length, bufferSize, dataLen);
- testReadFullyLoop(cipherClass, 0, length, bufferSizeLess, dataLen);
- testReadFullyLoop(cipherClass, 0, length, bufferSizeMore, dataLen);
- // test with different length when position = 0
- testReadFullyLoop(cipherClass, 0, length, bufferSize, dataLen);
- testReadFullyLoop(cipherClass, 0, lengthLess, bufferSize, dataLen);
- testReadFullyLoop(cipherClass, 0, lengthMore, bufferSize, dataLen);
- // test read fully failed
- testReadFullyFailed(cipherClass, -1, length, bufferSize);
- testReadFullyFailed(cipherClass, dataLen, length, bufferSize);
- testReadFullyFailed(cipherClass, dataLen - length + 1, length, bufferSize);
- }
-
- private void doSeekTests(String cipherClass) throws Exception {
- // test with different length when position = 0
- testSeekLoop(cipherClass, 0, length, bufferSize);
- testSeekLoop(cipherClass, 0, lengthLess, bufferSize);
- testSeekLoop(cipherClass, 0, lengthMore, bufferSize);
- // there should be none data read when position = dataLen
- testSeekLoop(cipherClass, dataLen, length, bufferSize);
- // test exception when position = -1
- testSeekFailed(cipherClass, -1, bufferSize);
- }
-
- private void testSeekLoop(String cipherClass, int position, int length,
- int bufferSize) throws Exception {
- PositionedCryptoInputStream in = getCryptoInputStream(
- getCipher(cipherClass), bufferSize);
- while (in.available() > 0) {
- in.seek(position);
- ByteBuffer buf = ByteBuffer.allocate(length);
- int n = in.read(buf);
- if (n > 0) {
- compareByteArray(testData, position, buf.array(), n);
- position += n;
- } else {
- break;
- }
- }
- in.close();
- }
-
- // test for the out of index position, eg, -1.
- private void testSeekFailed(String cipherClass, int position,
- int bufferSize) throws Exception {
- PositionedCryptoInputStream in = getCryptoInputStream(
- getCipher(cipherClass), bufferSize);
- try {
- in.seek(position);
- Assert.fail("Excepted exception for cannot seek to negative offset.");
- } catch (IllegalArgumentException iae) {
- }
- in.close();
- }
-
- private void testPositionedReadLoop(String cipherClass, int position,
- int length, int bufferSize, int total) throws Exception {
- PositionedCryptoInputStream in = getCryptoInputStream(
- getCipher(cipherClass), bufferSize);
- // do the position read until the end of data
- while (position < total) {
- byte[] bytes = new byte[length];
- int n = in.read(position, bytes, 0, length);
- if (n >= 0) {
- compareByteArray(testData, position, bytes, n);
- position += n;
- } else {
- break;
- }
- }
- in.close();
- }
-
- // test for the out of index position, eg, -1.
- private void testPositionedReadNone(String cipherClass, int position,
- int length, int bufferSize) throws Exception {
- PositionedCryptoInputStream in = getCryptoInputStream(
- getCipher(cipherClass), bufferSize);
- byte[] bytes = new byte[length];
- int n = in.read(position, bytes, 0, length);
- Assert.assertEquals(n, -1);
- in.close();
- }
-
- private void testReadFullyLoop(String cipherClass,int position,
- int length, int bufferSize, int total) throws Exception {
- PositionedCryptoInputStream in = getCryptoInputStream(
- getCipher(cipherClass), bufferSize);
-
- // do the position read full until remain < length
- while (position + length <= total) {
- byte[] bytes = new byte[length];
- in.readFully(position, bytes, 0, length);
- compareByteArray(testData, position, bytes, length);
- position += length;
- }
-
- in.close();
- }
-
- // test for the End of file reached before reading fully
- private void testReadFullyFailed(String cipherClass, int position,
- int length, int bufferSize) throws Exception {
- PositionedCryptoInputStream in = getCryptoInputStream(
- getCipher(cipherClass), bufferSize);
- byte[] bytes = new byte[length];
- try {
- in.readFully(position, bytes, 0, length);
- Assert.fail("Excepted EOFException.");
- } catch (IOException ioe) {
- // excepted exception
- }
- in.close();
- }
-
- // compare the data from pos with length and data2 from 0 with length
- private void compareByteArray(byte[] data1, int pos, byte[] data2, int length) {
- byte[] expectedData = new byte[length];
- byte[] realData = new byte[length];
- // get the expected data with the position and length
- System.arraycopy(data1, pos, expectedData, 0, length);
- // get the real data
- System.arraycopy(data2, 0, realData, 0, length);
- Assert.assertArrayEquals(expectedData, realData);
- }
-
- private Cipher getCipher(String cipherClass) throws IOException {
- try {
- return (Cipher)ReflectionUtils.newInstance(
- ReflectionUtils.getClassByName(cipherClass), props, transformation);
- } catch (ClassNotFoundException cnfe) {
- throw new IOException("Illegal crypto cipher!");
- }
- }
-
- class PositionedInputForTest implements Input {
-
- byte[] data;
- long pos;
- long count;
-
- public PositionedInputForTest(byte[] data) {
- this.data = data;
- this.pos = 0;
- this.count = data.length;
- }
-
- @Override
- public int read(ByteBuffer dst) throws IOException {
- int remaining = (int)(count - pos);
- if(remaining <= 0) {
- return -1;
- }
-
- int length = Math.min(dst.remaining(), remaining);
- dst.put(data, (int)pos, length);
- pos += length;
- return length;
- }
-
- @Override
- public long skip(long n) throws IOException {
- if (n <= 0) {
- return 0;
- }
-
- long remaining = count - pos;
- if(remaining < n) {
- n = remaining;
- }
- pos += n;
-
- return n;
- }
-
- @Override
- public int read(long position, byte[] buffer, int offset, int length)
- throws IOException {
- if (buffer == null) {
- throw new NullPointerException();
- } else if (offset < 0 || length < 0 || length > buffer.length - offset) {
- throw new IndexOutOfBoundsException();
- }
-
- if (position < 0 || position >= count) {
- return -1;
- }
-
- long avail = count - position;
- if (length > avail) {
- length = (int)avail;
- }
- if (length <= 0) {
- return 0;
- }
- System.arraycopy(data, (int)position, buffer, offset, length);
- return length;
- }
-
- @Override
- public void seek(long position) throws IOException {
- if (pos < 0) {
- throw new IOException("Negative seek offset");
- } else if (position >= 0 && position < count) {
- pos = position;
- } else {
- // to the end of file
- pos = count;
- }
- }
-
- @Override
- public void close() throws IOException {
- }
-
- @Override
- public int available() throws IOException {
- return (int)(count - pos);
- }
- }
-}
[2/3] commons-crypto git commit: CRYPTO-12: Rename CryptoInputStream
to CipherInputStream and CryptoOutputStream to CipherOutputStream (Xianda Ke
via Dian Fu)
Posted by di...@apache.org.
http://git-wip-us.apache.org/repos/asf/commons-crypto/blob/ad81d236/src/main/java/org/apache/commons/crypto/stream/CryptoOutputStream.java
----------------------------------------------------------------------
diff --git a/src/main/java/org/apache/commons/crypto/stream/CryptoOutputStream.java b/src/main/java/org/apache/commons/crypto/stream/CryptoOutputStream.java
deleted file mode 100644
index 3d3ac8d..0000000
--- a/src/main/java/org/apache/commons/crypto/stream/CryptoOutputStream.java
+++ /dev/null
@@ -1,283 +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 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;
-
-/**
- * CryptoOutputStream encrypts data and writes to the under layer output. It supports
- * any mode of operations such as AES CBC/CTR/GCM mode in concept. It is not thread-safe.
- */
-
-public class CryptoOutputStream extends OutputStream implements
- WritableByteChannel {
- private final byte[] oneByteBuf = new byte[1];
-
- protected Output output;
- protected final Cipher cipher;
- protected final int bufferSize;
-
- protected final byte[] key;
- protected final byte[] initIV;
- protected byte[] iv;
-
- protected boolean closed;
-
- /**
- * Input data buffer. The data starts at inBuffer.position() and ends at
- * inBuffer.limit().
- */
- protected ByteBuffer inBuffer;
-
- /**
- * Encrypted data buffer. The data starts at outBuffer.position() and ends at
- * outBuffer.limit();
- */
- protected ByteBuffer outBuffer;
-
- public CryptoOutputStream(CipherTransformation transformation,
- Properties props, OutputStream out, byte[] key, byte[] iv)
- throws IOException {
- this(out, Utils.getCipherInstance(transformation, props),
- Utils.getBufferSize(props), key, iv);
- }
-
- public CryptoOutputStream(CipherTransformation transformation,
- Properties props, WritableByteChannel out, byte[] key, byte[] iv)
- throws IOException {
- this(out, Utils.getCipherInstance(transformation, props),
- Utils.getBufferSize(props), key, iv);
- }
-
- public CryptoOutputStream(OutputStream out, Cipher cipher,
- int bufferSize, byte[] key, byte[] iv) throws IOException {
- this(new StreamOutput(out, bufferSize), cipher, bufferSize, key, iv);
- }
-
- public CryptoOutputStream(WritableByteChannel channel, Cipher cipher,
- int bufferSize, byte[] key, byte[] iv) throws IOException {
- this(new ChannelOutput(channel), cipher, bufferSize, key, iv);
- }
-
- protected CryptoOutputStream(Output output, Cipher cipher,
- int bufferSize, byte[] key, byte[] iv)
- throws IOException {
-
- this.output = output;
- this.bufferSize = Utils.checkBufferSize(cipher, bufferSize);
- this.cipher = cipher;
- this.key = key.clone();
- this.initIV = iv.clone();
- this.iv = iv.clone();
- inBuffer = ByteBuffer.allocateDirect(this.bufferSize);
- outBuffer = ByteBuffer.allocateDirect(this.bufferSize +
- cipher.getTransformation().getAlgorithmBlockSize());
-
- initCipher();
- }
-
- @Override
- public void write(int b) throws IOException {
- oneByteBuf[0] = (byte)(b & 0xff);
- write(oneByteBuf, 0, oneByteBuf.length);
- }
-
- /**
- * Encryption is buffer based.
- * If there is enough room in {@link #inBuffer}, then write to this buffer.
- * If {@link #inBuffer} is full, then do encryption and write data to the
- * underlying stream.
- * @param b the data.
- * @param off the start offset in the data.
- * @param len the number of bytes to write.
- * @throws IOException
- */
- public void write(byte[] b, int off, int len) throws IOException {
- checkStream();
- if (b == null) {
- throw new NullPointerException();
- } else if (off < 0 || len < 0 || off > b.length ||
- len > b.length - off) {
- throw new IndexOutOfBoundsException();
- }
-
- while (len > 0) {
- final int remaining = inBuffer.remaining();
- if (len < remaining) {
- inBuffer.put(b, off, len);
- len = 0;
- } else {
- inBuffer.put(b, off, remaining);
- off += remaining;
- len -= remaining;
- encrypt();
- }
- }
- }
-
- /**
- * To flush, we need to encrypt the data in the buffer and write to the
- * underlying stream, then do the flush.
- */
- @Override
- public void flush() throws IOException {
- checkStream();
- encrypt();
- output.flush();
- super.flush();
- }
-
- @Override
- public void close() throws IOException {
- if (closed) {
- return;
- }
-
- try {
- encryptFinal();
- output.close();
- freeBuffers();
- cipher.close();
- super.close();
- } finally {
- closed = true;
- }
- }
-
- @Override
- public boolean isOpen() {
- return !closed;
- }
-
- @Override
- public int write(ByteBuffer src) throws IOException {
- checkStream();
- final int len = src.remaining();
- int remaining = len;
- while (remaining > 0) {
- final int space = inBuffer.remaining();
- if (remaining < space) {
- inBuffer.put(src);
- remaining = 0;
- } else {
- // to void copy twice, we set the limit to copy directly
- final int oldLimit = src.limit();
- final int newLimit = src.position() + space;
- src.limit(newLimit);
-
- inBuffer.put(src);
-
- // restore the old limit
- src.limit(oldLimit);
-
- remaining -= space;
- encrypt();
- }
- }
-
- return len;
- }
-
- /** Initialize the cipher. */
- protected void initCipher()
- throws IOException {
- try {
- cipher.init(Cipher.ENCRYPT_MODE, key, iv);
- } catch (InvalidKeyException e) {
- throw new IOException(e);
- } catch(InvalidAlgorithmParameterException e) {
- throw new IOException(e);
- }
- }
-
- /**
- * Do the encryption, input is {@link #inBuffer} and output is
- * {@link #outBuffer}.
- */
- protected void encrypt() throws IOException {
-
- inBuffer.flip();
- outBuffer.clear();
-
- try {
- cipher.update(inBuffer, outBuffer);
- } catch (ShortBufferException e) {
- throw new IOException(e);
- }
-
- inBuffer.clear();
- outBuffer.flip();
-
- // write to output
- output.write(outBuffer);
- }
-
- /**
- * Do final encryption of the last data
- */
- protected void encryptFinal() throws IOException {
- inBuffer.flip();
- outBuffer.clear();
-
- try {
- cipher.doFinal(inBuffer, outBuffer);
- } catch (ShortBufferException e) {
- throw new IOException(e);
- } catch (IllegalBlockSizeException e) {
- throw new IOException(e);
- } catch( BadPaddingException e) {
- throw new IOException(e);
- }
-
- inBuffer.clear();
- outBuffer.flip();
-
- // write to output
- output.write(outBuffer);
- }
-
- 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);
- }
-}
http://git-wip-us.apache.org/repos/asf/commons-crypto/blob/ad81d236/src/main/java/org/apache/commons/crypto/stream/PositionedCipherInputStream.java
----------------------------------------------------------------------
diff --git a/src/main/java/org/apache/commons/crypto/stream/PositionedCipherInputStream.java b/src/main/java/org/apache/commons/crypto/stream/PositionedCipherInputStream.java
new file mode 100644
index 0000000..c6a4169
--- /dev/null
+++ b/src/main/java/org/apache/commons/crypto/stream/PositionedCipherInputStream.java
@@ -0,0 +1,311 @@
+/**
+ * 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.nio.ByteBuffer;
+import java.security.GeneralSecurityException;
+import java.security.InvalidAlgorithmParameterException;
+import java.security.InvalidKeyException;
+import java.util.Properties;
+import java.util.Queue;
+import java.util.concurrent.ConcurrentLinkedQueue;
+
+import javax.crypto.BadPaddingException;
+import javax.crypto.IllegalBlockSizeException;
+import javax.crypto.ShortBufferException;
+
+import org.apache.commons.crypto.cipher.Cipher;
+import org.apache.commons.crypto.cipher.CipherFactory;
+import org.apache.commons.crypto.stream.input.Input;
+import org.apache.commons.crypto.utils.IOUtils;
+import org.apache.commons.crypto.utils.Utils;
+
+import static org.apache.commons.crypto.cipher.CipherTransformation.AES_CTR_NOPADDING;
+
+/**
+ * PositionedCipherInputStream provides the capability to decrypt the stream starting
+ * at random position as well as provides the foundation for positioned read for
+ * decrypting. This needs a stream cipher mode such as AES CTR mode.
+ */
+public class PositionedCipherInputStream extends CTRCipherInputStream {
+
+ /**
+ * DirectBuffer pool
+ */
+ private final Queue<ByteBuffer> bufferPool = new
+ ConcurrentLinkedQueue<ByteBuffer>();
+
+ /**
+ * Cipher pool
+ */
+ private final Queue<CipherState> cipherPool = new
+ ConcurrentLinkedQueue<CipherState>();
+
+ public PositionedCipherInputStream(Properties props, Input in,
+ byte[] key, byte[] iv, long streamOffset) throws IOException {
+ this(in, Utils.getCipherInstance(AES_CTR_NOPADDING, props),
+ Utils.getBufferSize(props), key, iv, streamOffset);
+ }
+
+ public PositionedCipherInputStream(
+ Input input,
+ Cipher cipher,
+ int bufferSize,
+ byte[] key,
+ byte[] iv,
+ long streamOffset) throws IOException {
+ super(input, cipher, bufferSize, key, iv, streamOffset);
+ }
+
+ /**
+ * Read upto the specified number of bytes from a given position
+ * within a stream and return the number of bytes read. This does not
+ * change the current offset of the stream, and is thread-safe.
+ */
+ public int read(long position, byte[] buffer, int offset, int length)
+ throws IOException {
+ checkStream();
+ final int n = input.read(position, buffer, offset, length);
+ if (n > 0) {
+ // This operation does not change the current offset of the file
+ decrypt(position, buffer, offset, n);
+ }
+ return n;
+ }
+
+ /**
+ * Read the specified number of bytes from a given position within a stream.
+ * This does not change the current offset of the stream and is thread-safe.
+ */
+ public void readFully(long position, byte[] buffer, int offset, int length)
+ throws IOException {
+ checkStream();
+ IOUtils.readFully(input, position, buffer, offset, length);
+ if (length > 0) {
+ // This operation does not change the current offset of the file
+ decrypt(position, buffer, offset, length);
+ }
+ }
+
+ public void readFully(long position, byte[] buffer) throws IOException {
+ readFully(position, buffer, 0, buffer.length);
+ }
+
+ /**
+ * Decrypt length bytes in buffer starting at offset. Output is also put
+ * into buffer starting at offset. It is thread-safe.
+ */
+ protected void decrypt(long position, byte[] buffer, int offset, int length)
+ throws IOException {
+ ByteBuffer inBuffer = getBuffer();
+ ByteBuffer outBuffer = getBuffer();
+ CipherState state = null;
+ try {
+ state = getCipherState();
+ byte[] iv = getInitIV().clone();
+ resetCipher(state, position, iv);
+ byte padding = getPadding(position);
+ inBuffer.position(padding); // Set proper position for input data.
+
+ int n = 0;
+ while (n < length) {
+ int toDecrypt = Math.min(length - n, inBuffer.remaining());
+ inBuffer.put(buffer, offset + n, toDecrypt);
+
+ // Do decryption
+ decrypt(state, inBuffer, outBuffer, padding);
+
+ outBuffer.get(buffer, offset + n, toDecrypt);
+ n += toDecrypt;
+ padding = postDecryption(state, inBuffer, position + n, iv);
+ }
+ } finally {
+ returnBuffer(inBuffer);
+ returnBuffer(outBuffer);
+ returnCipherState(state);
+ }
+ }
+
+ /**
+ * Do 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();
+ */
+ private void decrypt(CipherState state, ByteBuffer inBuffer,
+ ByteBuffer outBuffer, byte padding) throws IOException {
+ Utils.checkState(inBuffer.position() >= padding);
+ if(inBuffer.position() == padding) {
+ // There is no real data in inBuffer.
+ return;
+ }
+ inBuffer.flip();
+ outBuffer.clear();
+ decryptBuffer(state, inBuffer, 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);
+ }
+ }
+
+ private void decryptBuffer(CipherState state, ByteBuffer inBuffer, ByteBuffer outBuffer)
+ throws IOException {
+ int inputSize = inBuffer.remaining();
+ try {
+ int n = state.getCipher().update(inBuffer, outBuffer);
+ 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.
+ */
+ state.getCipher().doFinal(inBuffer, outBuffer);
+ state.reset(true);
+ }
+ } catch (ShortBufferException e) {
+ throw new IOException(e);
+ } catch (IllegalBlockSizeException e) {
+ throw new IOException(e);
+ } catch (BadPaddingException e) {
+ throw new IOException(e);
+ }
+ }
+
+ /**
+ * This method is executed immediately after decryption. Check whether
+ * cipher should be updated and recalculate padding if needed.
+ */
+ private byte postDecryption(CipherState state, ByteBuffer inBuffer,
+ long position, byte[] iv) throws IOException {
+ byte padding = 0;
+ if (state.isReset()) {
+ /*
+ * 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(state, position, iv);
+ padding = getPadding(position);
+ inBuffer.position(padding);
+ }
+ return padding;
+ }
+
+ /** Calculate the counter and iv, reset the cipher. */
+ private void resetCipher(CipherState state, long position, byte[] iv)
+ throws IOException {
+ final long counter = getCounter(position);
+ Utils.calculateIV(getInitIV(), counter, iv);
+ try {
+ state.getCipher().init(Cipher.DECRYPT_MODE, getKey(), iv);
+ } catch (InvalidKeyException e) {
+ throw new IOException(e);
+ } catch (InvalidAlgorithmParameterException e) {
+ throw new IOException(e);
+ }
+ state.reset(false);
+ }
+
+ /** Get Cipher from pool */
+ private CipherState getCipherState() throws IOException {
+ CipherState state = cipherPool.poll();
+ if (state == null) {
+ Cipher cipher;
+ try {
+ cipher = CipherFactory.getInstance(getCipher().getTransformation(),
+ getCipher().getProperties());
+ } catch (GeneralSecurityException e) {
+ throw new IOException(e);
+ }
+ state = new CipherState(cipher);
+ }
+
+ return state;
+ }
+
+ /** Return Cipher to pool */
+ private void returnCipherState(CipherState state) {
+ if (state != null) {
+ cipherPool.add(state);
+ }
+ }
+
+ /** Get direct buffer from pool */
+ private ByteBuffer getBuffer() {
+ ByteBuffer buffer = bufferPool.poll();
+ if (buffer == null) {
+ buffer = ByteBuffer.allocateDirect(getBufferSize());
+ }
+
+ return buffer;
+ }
+
+ /** Return direct buffer to pool */
+ private void returnBuffer(ByteBuffer buf) {
+ if (buf != null) {
+ buf.clear();
+ bufferPool.add(buf);
+ }
+ }
+
+ @Override
+ public void close() throws IOException {
+ if (!isOpen()) {
+ return;
+ }
+
+ cleanBufferPool();
+ super.close();
+ }
+
+ /** Clean direct buffer pool */
+ private void cleanBufferPool() {
+ ByteBuffer buf;
+ while ((buf = bufferPool.poll()) != null) {
+ Utils.freeDirectBuffer(buf);
+ }
+ }
+
+ private class CipherState {
+ private Cipher cipher;
+ private boolean reset;
+
+ public CipherState(Cipher cipher) {
+ this.cipher = cipher;
+ this.reset = false;
+ }
+
+ public Cipher getCipher() {
+ return cipher;
+ }
+
+ public boolean isReset() {
+ return reset;
+ }
+
+ public void reset(boolean reset) {
+ this.reset = reset;
+ }
+ }
+}
http://git-wip-us.apache.org/repos/asf/commons-crypto/blob/ad81d236/src/main/java/org/apache/commons/crypto/stream/PositionedCryptoInputStream.java
----------------------------------------------------------------------
diff --git a/src/main/java/org/apache/commons/crypto/stream/PositionedCryptoInputStream.java b/src/main/java/org/apache/commons/crypto/stream/PositionedCryptoInputStream.java
deleted file mode 100644
index 160f6ee..0000000
--- a/src/main/java/org/apache/commons/crypto/stream/PositionedCryptoInputStream.java
+++ /dev/null
@@ -1,311 +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.nio.ByteBuffer;
-import java.security.GeneralSecurityException;
-import java.security.InvalidAlgorithmParameterException;
-import java.security.InvalidKeyException;
-import java.util.Properties;
-import java.util.Queue;
-import java.util.concurrent.ConcurrentLinkedQueue;
-
-import javax.crypto.BadPaddingException;
-import javax.crypto.IllegalBlockSizeException;
-import javax.crypto.ShortBufferException;
-
-import org.apache.commons.crypto.cipher.Cipher;
-import org.apache.commons.crypto.cipher.CipherFactory;
-import org.apache.commons.crypto.stream.input.Input;
-import org.apache.commons.crypto.utils.IOUtils;
-import org.apache.commons.crypto.utils.Utils;
-
-import static org.apache.commons.crypto.cipher.CipherTransformation.AES_CTR_NOPADDING;
-
-/**
- * PositionedCryptoInputStream provides the capability to decrypt the stream starting
- * at random position as well as provides the foundation for positioned read for
- * decrypting. This needs a stream cipher mode such as AES CTR mode.
- */
-public class PositionedCryptoInputStream extends CTRCryptoInputStream {
-
- /**
- * DirectBuffer pool
- */
- private final Queue<ByteBuffer> bufferPool = new
- ConcurrentLinkedQueue<ByteBuffer>();
-
- /**
- * Cipher pool
- */
- private final Queue<CipherState> cipherPool = new
- ConcurrentLinkedQueue<CipherState>();
-
- public PositionedCryptoInputStream(Properties props, Input in,
- byte[] key, byte[] iv, long streamOffset) throws IOException {
- this(in, Utils.getCipherInstance(AES_CTR_NOPADDING, props),
- Utils.getBufferSize(props), key, iv, streamOffset);
- }
-
- public PositionedCryptoInputStream(
- Input input,
- Cipher cipher,
- int bufferSize,
- byte[] key,
- byte[] iv,
- long streamOffset) throws IOException {
- super(input, cipher, bufferSize, key, iv, streamOffset);
- }
-
- /**
- * Read upto the specified number of bytes from a given position
- * within a stream and return the number of bytes read. This does not
- * change the current offset of the stream, and is thread-safe.
- */
- public int read(long position, byte[] buffer, int offset, int length)
- throws IOException {
- checkStream();
- final int n = input.read(position, buffer, offset, length);
- if (n > 0) {
- // This operation does not change the current offset of the file
- decrypt(position, buffer, offset, n);
- }
- return n;
- }
-
- /**
- * Read the specified number of bytes from a given position within a stream.
- * This does not change the current offset of the stream and is thread-safe.
- */
- public void readFully(long position, byte[] buffer, int offset, int length)
- throws IOException {
- checkStream();
- IOUtils.readFully(input, position, buffer, offset, length);
- if (length > 0) {
- // This operation does not change the current offset of the file
- decrypt(position, buffer, offset, length);
- }
- }
-
- public void readFully(long position, byte[] buffer) throws IOException {
- readFully(position, buffer, 0, buffer.length);
- }
-
- /**
- * Decrypt length bytes in buffer starting at offset. Output is also put
- * into buffer starting at offset. It is thread-safe.
- */
- protected void decrypt(long position, byte[] buffer, int offset, int length)
- throws IOException {
- ByteBuffer inBuffer = getBuffer();
- ByteBuffer outBuffer = getBuffer();
- CipherState state = null;
- try {
- state = getCipherState();
- byte[] iv = getInitIV().clone();
- resetCipher(state, position, iv);
- byte padding = getPadding(position);
- inBuffer.position(padding); // Set proper position for input data.
-
- int n = 0;
- while (n < length) {
- int toDecrypt = Math.min(length - n, inBuffer.remaining());
- inBuffer.put(buffer, offset + n, toDecrypt);
-
- // Do decryption
- decrypt(state, inBuffer, outBuffer, padding);
-
- outBuffer.get(buffer, offset + n, toDecrypt);
- n += toDecrypt;
- padding = postDecryption(state, inBuffer, position + n, iv);
- }
- } finally {
- returnBuffer(inBuffer);
- returnBuffer(outBuffer);
- returnCipherState(state);
- }
- }
-
- /**
- * Do 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();
- */
- private void decrypt(CipherState state, ByteBuffer inBuffer,
- ByteBuffer outBuffer, byte padding) throws IOException {
- Utils.checkState(inBuffer.position() >= padding);
- if(inBuffer.position() == padding) {
- // There is no real data in inBuffer.
- return;
- }
- inBuffer.flip();
- outBuffer.clear();
- decryptBuffer(state, inBuffer, 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);
- }
- }
-
- private void decryptBuffer(CipherState state, ByteBuffer inBuffer, ByteBuffer outBuffer)
- throws IOException {
- int inputSize = inBuffer.remaining();
- try {
- int n = state.getCipher().update(inBuffer, outBuffer);
- 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.
- */
- state.getCipher().doFinal(inBuffer, outBuffer);
- state.reset(true);
- }
- } catch (ShortBufferException e) {
- throw new IOException(e);
- } catch (IllegalBlockSizeException e) {
- throw new IOException(e);
- } catch (BadPaddingException e) {
- throw new IOException(e);
- }
- }
-
- /**
- * This method is executed immediately after decryption. Check whether
- * cipher should be updated and recalculate padding if needed.
- */
- private byte postDecryption(CipherState state, ByteBuffer inBuffer,
- long position, byte[] iv) throws IOException {
- byte padding = 0;
- if (state.isReset()) {
- /*
- * 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(state, position, iv);
- padding = getPadding(position);
- inBuffer.position(padding);
- }
- return padding;
- }
-
- /** Calculate the counter and iv, reset the cipher. */
- private void resetCipher(CipherState state, long position, byte[] iv)
- throws IOException {
- final long counter = getCounter(position);
- Utils.calculateIV(getInitIV(), counter, iv);
- try {
- state.getCipher().init(Cipher.DECRYPT_MODE, getKey(), iv);
- } catch (InvalidKeyException e) {
- throw new IOException(e);
- } catch (InvalidAlgorithmParameterException e) {
- throw new IOException(e);
- }
- state.reset(false);
- }
-
- /** Get Cipher from pool */
- private CipherState getCipherState() throws IOException {
- CipherState state = cipherPool.poll();
- if (state == null) {
- Cipher cipher;
- try {
- cipher = CipherFactory.getInstance(getCipher().getTransformation(),
- getCipher().getProperties());
- } catch (GeneralSecurityException e) {
- throw new IOException(e);
- }
- state = new CipherState(cipher);
- }
-
- return state;
- }
-
- /** Return Cipher to pool */
- private void returnCipherState(CipherState state) {
- if (state != null) {
- cipherPool.add(state);
- }
- }
-
- /** Get direct buffer from pool */
- private ByteBuffer getBuffer() {
- ByteBuffer buffer = bufferPool.poll();
- if (buffer == null) {
- buffer = ByteBuffer.allocateDirect(getBufferSize());
- }
-
- return buffer;
- }
-
- /** Return direct buffer to pool */
- private void returnBuffer(ByteBuffer buf) {
- if (buf != null) {
- buf.clear();
- bufferPool.add(buf);
- }
- }
-
- @Override
- public void close() throws IOException {
- if (!isOpen()) {
- return;
- }
-
- cleanBufferPool();
- super.close();
- }
-
- /** Clean direct buffer pool */
- private void cleanBufferPool() {
- ByteBuffer buf;
- while ((buf = bufferPool.poll()) != null) {
- Utils.freeDirectBuffer(buf);
- }
- }
-
- private class CipherState {
- private Cipher cipher;
- private boolean reset;
-
- public CipherState(Cipher cipher) {
- this.cipher = cipher;
- this.reset = false;
- }
-
- public Cipher getCipher() {
- return cipher;
- }
-
- public boolean isReset() {
- return reset;
- }
-
- public void reset(boolean reset) {
- this.reset = reset;
- }
- }
-}
http://git-wip-us.apache.org/repos/asf/commons-crypto/blob/ad81d236/src/main/java/org/apache/commons/crypto/stream/input/ChannelInput.java
----------------------------------------------------------------------
diff --git a/src/main/java/org/apache/commons/crypto/stream/input/ChannelInput.java b/src/main/java/org/apache/commons/crypto/stream/input/ChannelInput.java
index ee24a0c..9aff624 100644
--- a/src/main/java/org/apache/commons/crypto/stream/input/ChannelInput.java
+++ b/src/main/java/org/apache/commons/crypto/stream/input/ChannelInput.java
@@ -23,7 +23,7 @@ import java.nio.channels.ReadableByteChannel;
/**
* The ChannelInput class takes a <code>ReadableByteChannel</code> object and
- * wraps it as <code>Input</code> object acceptable by <code>CryptoInputStream</code>.
+ * wraps it as <code>Input</code> object acceptable by <code>CipherInputStream</code>.
*/
public class ChannelInput implements Input {
private static final int SKIP_BUFFER_SIZE = 2048;
http://git-wip-us.apache.org/repos/asf/commons-crypto/blob/ad81d236/src/main/java/org/apache/commons/crypto/stream/input/Input.java
----------------------------------------------------------------------
diff --git a/src/main/java/org/apache/commons/crypto/stream/input/Input.java b/src/main/java/org/apache/commons/crypto/stream/input/Input.java
index a63c6ca..2971edb 100644
--- a/src/main/java/org/apache/commons/crypto/stream/input/Input.java
+++ b/src/main/java/org/apache/commons/crypto/stream/input/Input.java
@@ -21,7 +21,7 @@ import java.io.IOException;
import java.nio.ByteBuffer;
/**
- * The Input interface abstract the input source of <code>CryptoInputStream</code> so that
+ * The Input interface abstract the input source of <code>CipherInputStream</code> so that
* different implementation of input can be used. The implementation Input interface will usually
* wraps an input mechanism such as <code>InputStream</code> or <code>ReadableByteChannel</code>.
*/
http://git-wip-us.apache.org/repos/asf/commons-crypto/blob/ad81d236/src/main/java/org/apache/commons/crypto/stream/input/StreamInput.java
----------------------------------------------------------------------
diff --git a/src/main/java/org/apache/commons/crypto/stream/input/StreamInput.java b/src/main/java/org/apache/commons/crypto/stream/input/StreamInput.java
index ac3739b..c7e6771 100644
--- a/src/main/java/org/apache/commons/crypto/stream/input/StreamInput.java
+++ b/src/main/java/org/apache/commons/crypto/stream/input/StreamInput.java
@@ -23,7 +23,7 @@ import java.nio.ByteBuffer;
/**
* The StreamInput class takes a <code>InputStream</code> object and
- * wraps it as <code>Input</code> object acceptable by <code>CryptoInputStream</code>.
+ * wraps it as <code>Input</code> object acceptable by <code>CipherInputStream</code>.
*/
public class StreamInput implements Input {
private byte[] buf;
http://git-wip-us.apache.org/repos/asf/commons-crypto/blob/ad81d236/src/main/java/org/apache/commons/crypto/stream/output/ChannelOutput.java
----------------------------------------------------------------------
diff --git a/src/main/java/org/apache/commons/crypto/stream/output/ChannelOutput.java b/src/main/java/org/apache/commons/crypto/stream/output/ChannelOutput.java
index bae82a8..0a60217 100644
--- a/src/main/java/org/apache/commons/crypto/stream/output/ChannelOutput.java
+++ b/src/main/java/org/apache/commons/crypto/stream/output/ChannelOutput.java
@@ -23,7 +23,7 @@ import java.nio.channels.WritableByteChannel;
/**
* The ChannelOutput class takes a <code>WritableByteChannel</code> object and wraps it as
- * <code>Output</code> object acceptable by <code>CryptoOutputStream</code> as the output target.
+ * <code>Output</code> object acceptable by <code>CipherOutputStream</code> as the output target.
*/
public class ChannelOutput implements Output {
http://git-wip-us.apache.org/repos/asf/commons-crypto/blob/ad81d236/src/main/java/org/apache/commons/crypto/stream/output/Output.java
----------------------------------------------------------------------
diff --git a/src/main/java/org/apache/commons/crypto/stream/output/Output.java b/src/main/java/org/apache/commons/crypto/stream/output/Output.java
index 903fcea..c57a89e 100644
--- a/src/main/java/org/apache/commons/crypto/stream/output/Output.java
+++ b/src/main/java/org/apache/commons/crypto/stream/output/Output.java
@@ -21,7 +21,7 @@ import java.io.IOException;
import java.nio.ByteBuffer;
/**
- * The Output interface abstract the output target of <code>CryptoOutputStream</code> so that
+ * The Output interface abstract the output target of <code>CipherOutputStream</code> so that
* different implementation of output can be used. The implementation Output interface will usually
* wraps an output mechanism such as <code>OutputStream</code> or <code>WritableByteChannel</code>.
*/
http://git-wip-us.apache.org/repos/asf/commons-crypto/blob/ad81d236/src/main/java/org/apache/commons/crypto/stream/output/StreamOutput.java
----------------------------------------------------------------------
diff --git a/src/main/java/org/apache/commons/crypto/stream/output/StreamOutput.java b/src/main/java/org/apache/commons/crypto/stream/output/StreamOutput.java
index beead7f..ad067be 100644
--- a/src/main/java/org/apache/commons/crypto/stream/output/StreamOutput.java
+++ b/src/main/java/org/apache/commons/crypto/stream/output/StreamOutput.java
@@ -23,7 +23,7 @@ import java.nio.ByteBuffer;
/**
* The StreamOutput class takes a <code>OutputStream</code> object and wraps it as
- * <code>Output</code> object acceptable by <code>CryptoOutputStream</code> as the output target.
+ * <code>Output</code> object acceptable by <code>CipherOutputStream</code> as the output target.
*/
public class StreamOutput implements Output {
private byte[] buf;
http://git-wip-us.apache.org/repos/asf/commons-crypto/blob/ad81d236/src/test/java/org/apache/commons/crypto/stream/AbstractCipherStreamTest.java
----------------------------------------------------------------------
diff --git a/src/test/java/org/apache/commons/crypto/stream/AbstractCipherStreamTest.java b/src/test/java/org/apache/commons/crypto/stream/AbstractCipherStreamTest.java
new file mode 100644
index 0000000..6e2d864
--- /dev/null
+++ b/src/test/java/org/apache/commons/crypto/stream/AbstractCipherStreamTest.java
@@ -0,0 +1,456 @@
+/**
+ * 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.BufferedInputStream;
+import java.io.ByteArrayInputStream;
+import java.io.ByteArrayOutputStream;
+import java.io.DataInputStream;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.OutputStream;
+import java.nio.ByteBuffer;
+import java.nio.channels.Channels;
+import java.nio.channels.ReadableByteChannel;
+import java.security.SecureRandom;
+import java.util.Properties;
+import java.util.Random;
+
+import org.apache.commons.crypto.cipher.Cipher;
+import org.apache.commons.crypto.cipher.CipherTransformation;
+import org.apache.commons.crypto.cipher.JceCipher;
+import org.apache.commons.crypto.cipher.Openssl;
+import org.apache.commons.crypto.cipher.OpensslCipher;
+import org.apache.commons.crypto.utils.ReflectionUtils;
+import org.apache.commons.logging.Log;
+import org.apache.commons.logging.LogFactory;
+import org.junit.Assert;
+import org.junit.Before;
+import org.junit.Test;
+
+public abstract class AbstractCipherStreamTest {
+ private static final Log LOG= LogFactory.getLog(AbstractCipherStreamTest.class);
+
+ private final int dataLen = 20000;
+ private byte[] data = new byte[dataLen];
+ private byte[] encData;
+ private Properties props = new Properties();
+ protected byte[] key = new byte[16];
+ private byte[] iv = new byte[16];
+ private int count = 10000;
+ protected static int defaultBufferSize = 8192;
+ protected static int smallBufferSize = 1024;
+
+ private final String jceCipherClass = JceCipher.class.getName();
+ private final String opensslCipherClass = OpensslCipher.class.getName();
+ protected CipherTransformation transformation;
+
+ public abstract void setUp() throws IOException;
+
+ @Before
+ public void before() throws IOException {
+ Random random = new SecureRandom();
+ random.nextBytes(data);
+ random.nextBytes(key);
+ random.nextBytes(iv);
+ setUp();
+ prepareData();
+ }
+
+ /** Test skip. */
+ @Test(timeout=120000)
+ public void testSkip() throws Exception {
+ doSkipTest(jceCipherClass, false);
+ doSkipTest(opensslCipherClass, false);
+
+ doSkipTest(jceCipherClass, true);
+ doSkipTest(opensslCipherClass, true);
+ }
+
+ /** Test byte buffer read with different buffer size. */
+ @Test(timeout=120000)
+ public void testByteBufferRead() throws Exception {
+ doByteBufferRead(jceCipherClass, false);
+ doByteBufferRead(opensslCipherClass, false);
+
+ doByteBufferRead(jceCipherClass, true);
+ doByteBufferRead(opensslCipherClass, true);
+ }
+
+ /** Test byte buffer write. */
+ @Test(timeout=120000)
+ public void testByteBufferWrite() throws Exception {
+ ByteArrayOutputStream baos = new ByteArrayOutputStream();
+ doByteBufferWrite(jceCipherClass, baos, false);
+ doByteBufferWrite(opensslCipherClass, baos, false);
+
+ doByteBufferWrite(jceCipherClass, baos, true);
+ doByteBufferWrite(opensslCipherClass, baos, true);
+ }
+
+ private void doSkipTest(String cipherClass, boolean withChannel) throws IOException {
+ InputStream in = getCipherInputStream(new ByteArrayInputStream(encData),
+ getCipher(cipherClass), defaultBufferSize, iv, withChannel);
+ byte[] result = new byte[dataLen];
+ int n1 = readAll(in, result, 0, dataLen / 3);
+
+ long skipped = in.skip(dataLen / 3);
+ int n2 = readAll(in, result, 0, dataLen);
+
+ Assert.assertEquals(dataLen, n1 + skipped + n2);
+ byte[] readData = new byte[n2];
+ System.arraycopy(result, 0, readData, 0, n2);
+ byte[] expectedData = new byte[n2];
+ System.arraycopy(data, dataLen - n2, expectedData, 0, n2);
+ Assert.assertArrayEquals(readData, expectedData);
+
+ try {
+ skipped = in.skip(-3);
+ Assert.fail("Skip Negative length should fail.");
+ } catch (IllegalArgumentException e) {
+ Assert.assertTrue(e.getMessage().contains("Negative skip length"));
+ }
+
+ // Skip after EOF
+ skipped = in.skip(3);
+ Assert.assertEquals(skipped, 0);
+
+ in.close();
+ }
+
+ private void doByteBufferRead(String cipherClass, boolean withChannel) throws Exception {
+ // Default buffer size, initial buffer position is 0
+ InputStream in = getCipherInputStream(new ByteArrayInputStream(encData),
+ getCipher(cipherClass), defaultBufferSize, iv, withChannel);
+ ByteBuffer buf = ByteBuffer.allocate(dataLen + 100);
+ byteBufferReadCheck(in, buf, 0);
+ in.close();
+
+ // Default buffer size, initial buffer position is not 0
+ in = getCipherInputStream(new ByteArrayInputStream(encData),
+ getCipher(cipherClass), defaultBufferSize, iv, withChannel);
+ buf.clear();
+ byteBufferReadCheck(in, buf, 11);
+ in.close();
+
+ // Small buffer size, initial buffer position is 0
+ in = getCipherInputStream(new ByteArrayInputStream(encData),
+ getCipher(cipherClass), smallBufferSize, iv, withChannel);
+ buf.clear();
+ byteBufferReadCheck(in, buf, 0);
+ in.close();
+
+ // Small buffer size, initial buffer position is not 0
+ in = getCipherInputStream(new ByteArrayInputStream(encData),
+ getCipher(cipherClass), smallBufferSize, iv, withChannel);
+ buf.clear();
+ byteBufferReadCheck(in, buf, 11);
+ in.close();
+
+ // Direct buffer, default buffer size, initial buffer position is 0
+ in = getCipherInputStream(new ByteArrayInputStream(encData),
+ getCipher(cipherClass), defaultBufferSize, iv, withChannel);
+ buf = ByteBuffer.allocateDirect(dataLen + 100);
+ byteBufferReadCheck(in, buf, 0);
+ in.close();
+
+ // Direct buffer, default buffer size, initial buffer position is not 0
+ in = getCipherInputStream(new ByteArrayInputStream(encData),
+ getCipher(cipherClass), defaultBufferSize, iv, withChannel);
+ buf.clear();
+ byteBufferReadCheck(in, buf, 11);
+ in.close();
+
+ // Direct buffer, small buffer size, initial buffer position is 0
+ in = getCipherInputStream(new ByteArrayInputStream(encData),
+ getCipher(cipherClass), smallBufferSize, iv, withChannel);
+ buf.clear();
+ byteBufferReadCheck(in, buf, 0);
+ in.close();
+
+ // Direct buffer, small buffer size, initial buffer position is not 0
+ in = getCipherInputStream(new ByteArrayInputStream(encData),
+ getCipher(cipherClass), smallBufferSize, iv, withChannel);
+ buf.clear();
+ byteBufferReadCheck(in, buf, 11);
+ in.close();
+ }
+
+ private void doByteBufferWrite(String cipherClass, ByteArrayOutputStream baos,
+ boolean withChannel)
+ throws Exception {
+ baos.reset();
+ CipherOutputStream out =
+ getCipherOutputStream(baos, getCipher(cipherClass), defaultBufferSize,
+ iv, withChannel);
+ ByteBuffer buf = ByteBuffer.allocateDirect(dataLen / 2);
+ buf.put(data, 0, dataLen / 2);
+ buf.flip();
+ int n1 = out.write(buf);
+
+ buf.clear();
+ buf.put(data, n1, dataLen / 3);
+ buf.flip();
+ int n2 = out.write(buf);
+
+ buf.clear();
+ buf.put(data, n1 + n2, dataLen - n1 - n2);
+ buf.flip();
+ int n3 = out.write(buf);
+
+ Assert.assertEquals(dataLen, n1 + n2 + n3);
+
+ out.flush();
+
+ InputStream in = getCipherInputStream(new ByteArrayInputStream(encData),
+ getCipher(cipherClass), defaultBufferSize, iv, withChannel);
+ buf = ByteBuffer.allocate(dataLen + 100);
+ byteBufferReadCheck(in, buf, 0);
+ in.close();
+ }
+
+ private void byteBufferReadCheck(InputStream in, ByteBuffer buf,
+ int bufPos) throws Exception {
+ buf.position(bufPos);
+ int n = ((ReadableByteChannel) in).read(buf);
+ Assert.assertEquals(bufPos + n, buf.position());
+ byte[] readData = new byte[n];
+ buf.rewind();
+ buf.position(bufPos);
+ buf.get(readData);
+ byte[] expectedData = new byte[n];
+ System.arraycopy(data, 0, expectedData, 0, n);
+ Assert.assertArrayEquals(readData, expectedData);
+ }
+
+ private void prepareData() throws IOException {
+ Cipher cipher = null;
+ try {
+ cipher = (Cipher)ReflectionUtils.newInstance(
+ ReflectionUtils.getClassByName(jceCipherClass), props, transformation);
+ } catch (ClassNotFoundException cnfe) {
+ throw new IOException("Illegal crypto cipher!");
+ }
+
+ ByteArrayOutputStream baos = new ByteArrayOutputStream();
+ OutputStream out = new CipherOutputStream(baos, cipher, defaultBufferSize, key, iv);
+ out.write(data);
+ out.flush();
+ out.close();
+ encData = baos.toByteArray();
+ }
+
+ protected CipherInputStream getCipherInputStream(ByteArrayInputStream bais,
+ Cipher cipher,
+ int bufferSize, byte[] iv,
+ boolean withChannel) throws
+ IOException {
+ if (withChannel) {
+ return new CipherInputStream(Channels.newChannel(bais), cipher,
+ bufferSize, key, iv);
+ } else {
+ return new CipherInputStream(bais, cipher, bufferSize, key, iv);
+ }
+ }
+
+ protected CipherOutputStream getCipherOutputStream(ByteArrayOutputStream baos,
+ Cipher cipher,
+ int bufferSize, byte[] iv,
+ boolean withChannel) throws
+ IOException {
+ if (withChannel) {
+ return new CipherOutputStream(Channels.newChannel(baos), cipher,
+ bufferSize, key, iv);
+ } else {
+ return new CipherOutputStream(baos, cipher, bufferSize, key, iv);
+ }
+ }
+
+ private int readAll(InputStream in, byte[] b, int offset, int len)
+ throws IOException {
+ int n = 0;
+ int total = 0;
+ while (n != -1) {
+ total += n;
+ if (total >= len) {
+ break;
+ }
+ n = in.read(b, offset + total, len - total);
+ }
+
+ return total;
+ }
+
+ protected Cipher getCipher(String cipherClass) throws IOException {
+ try {
+ return (Cipher)ReflectionUtils.newInstance(
+ ReflectionUtils.getClassByName(cipherClass), props, transformation);
+ } catch (ClassNotFoundException cnfe) {
+ throw new IOException("Illegal crypto cipher!");
+ }
+ }
+
+ @Test
+ public void testReadWrite() throws Exception {
+ Assert.assertEquals(null, Openssl.getLoadingFailureReason());
+ doReadWriteTest(0, jceCipherClass, jceCipherClass, iv);
+ doReadWriteTest(0, opensslCipherClass, opensslCipherClass, iv);
+ doReadWriteTest(count, jceCipherClass, jceCipherClass, iv);
+ doReadWriteTest(count, opensslCipherClass, opensslCipherClass, iv);
+ doReadWriteTest(count, jceCipherClass, opensslCipherClass, iv);
+ doReadWriteTest(count, opensslCipherClass, jceCipherClass, iv);
+ // Overflow test, IV: xx xx xx xx xx xx xx xx ff ff ff ff ff ff ff ff
+ for(int i = 0; i < 8; i++) {
+ iv[8 + i] = (byte) 0xff;
+ }
+ doReadWriteTest(count, jceCipherClass, jceCipherClass, iv);
+ doReadWriteTest(count, opensslCipherClass, opensslCipherClass, iv);
+ doReadWriteTest(count, jceCipherClass, opensslCipherClass, iv);
+ doReadWriteTest(count, opensslCipherClass, jceCipherClass, iv);
+ }
+
+ private void doReadWriteTest(int count, String encCipherClass,
+ String decCipherClass, byte[] iv) throws IOException {
+ doReadWriteTestForInputStream(count, encCipherClass, decCipherClass, iv);
+ doReadWriteTestForReadableByteChannel(count, encCipherClass, decCipherClass,
+ iv);
+ }
+
+ private void doReadWriteTestForInputStream(int count, String encCipherClass,
+ String decCipherClass, byte[] iv) throws IOException {
+ Cipher encCipher = getCipher(encCipherClass);
+ LOG.debug("Created a cipher object of type: " + encCipherClass);
+
+ // Generate data
+ SecureRandom random = new SecureRandom();
+ byte[] originalData = new byte[count];
+ byte[] decryptedData = new byte[count];
+ random.nextBytes(originalData);
+ LOG.debug("Generated " + count + " records");
+
+ // Encrypt data
+ ByteArrayOutputStream encryptedData = new ByteArrayOutputStream();
+ CipherOutputStream out =
+ getCipherOutputStream(encryptedData, encCipher, defaultBufferSize, iv,
+ false);
+ out.write(originalData, 0, originalData.length);
+ out.flush();
+ out.close();
+ LOG.debug("Finished encrypting data");
+
+ Cipher decCipher = getCipher(decCipherClass);
+ LOG.debug("Created a cipher object of type: " + decCipherClass);
+
+ // Decrypt data
+ CipherInputStream in = getCipherInputStream(
+ new ByteArrayInputStream(encryptedData.toByteArray()), decCipher,
+ defaultBufferSize, iv, false);
+
+ // Check
+ int remainingToRead = count;
+ int offset = 0;
+ while (remainingToRead > 0) {
+ int n = in.read(decryptedData, offset, decryptedData.length - offset);
+ if (n >=0) {
+ remainingToRead -= n;
+ offset += n;
+ }
+ }
+
+ Assert.assertArrayEquals("originalData and decryptedData not equal",
+ originalData, decryptedData);
+
+ // Decrypt data byte-at-a-time
+ in = getCipherInputStream(
+ new ByteArrayInputStream(encryptedData.toByteArray()), decCipher,
+ defaultBufferSize, iv, false);
+
+ // Check
+ DataInputStream originalIn = new DataInputStream(new BufferedInputStream(new ByteArrayInputStream(originalData)));
+ int expected;
+ do {
+ expected = originalIn.read();
+ Assert.assertEquals("Decrypted stream read by byte does not match",
+ expected, in.read());
+ } while (expected != -1);
+
+ LOG.debug("SUCCESS! Completed checking " + count + " records");
+ }
+
+ private void doReadWriteTestForReadableByteChannel(int count,
+ String encCipherClass,
+ String decCipherClass,
+ byte[] iv) throws IOException {
+ Cipher encCipher = getCipher(encCipherClass);
+ LOG.debug("Created a cipher object of type: " + encCipherClass);
+
+ // Generate data
+ SecureRandom random = new SecureRandom();
+ byte[] originalData = new byte[count];
+ byte[] decryptedData = new byte[count];
+ random.nextBytes(originalData);
+ LOG.debug("Generated " + count + " records");
+
+ // Encrypt data
+ ByteArrayOutputStream encryptedData = new ByteArrayOutputStream();
+ CipherOutputStream out =
+ getCipherOutputStream(encryptedData, encCipher, defaultBufferSize, iv,
+ true);
+ out.write(originalData, 0, originalData.length);
+ out.flush();
+ out.close();
+ LOG.debug("Finished encrypting data");
+
+ Cipher decCipher = getCipher(decCipherClass);
+ LOG.debug("Created a cipher object of type: " + decCipherClass);
+
+ // Decrypt data
+ CipherInputStream in = getCipherInputStream(
+ new ByteArrayInputStream(encryptedData.toByteArray()), decCipher,
+ defaultBufferSize, iv, true);
+
+ // Check
+ int remainingToRead = count;
+ int offset = 0;
+ while (remainingToRead > 0) {
+ int n = in.read(decryptedData, offset, decryptedData.length - offset);
+ if (n >=0) {
+ remainingToRead -= n;
+ offset += n;
+ }
+ }
+
+ Assert.assertArrayEquals("originalData and decryptedData not equal",
+ originalData, decryptedData);
+
+ // Decrypt data byte-at-a-time
+ in = getCipherInputStream(new ByteArrayInputStream(
+ encryptedData.toByteArray()),decCipher,defaultBufferSize,iv,true);
+
+ // Check
+ DataInputStream originalIn = new DataInputStream(new BufferedInputStream(new ByteArrayInputStream(originalData)));
+ int expected;
+ do {
+ expected = originalIn.read();
+ Assert.assertEquals("Decrypted stream read by byte does not match",
+ expected, in.read());
+ } while (expected != -1);
+
+ LOG.debug("SUCCESS! Completed checking " + count + " records");
+ }
+}
http://git-wip-us.apache.org/repos/asf/commons-crypto/blob/ad81d236/src/test/java/org/apache/commons/crypto/stream/AbstractCryptoStreamTest.java
----------------------------------------------------------------------
diff --git a/src/test/java/org/apache/commons/crypto/stream/AbstractCryptoStreamTest.java b/src/test/java/org/apache/commons/crypto/stream/AbstractCryptoStreamTest.java
deleted file mode 100644
index fe73f00..0000000
--- a/src/test/java/org/apache/commons/crypto/stream/AbstractCryptoStreamTest.java
+++ /dev/null
@@ -1,456 +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.BufferedInputStream;
-import java.io.ByteArrayInputStream;
-import java.io.ByteArrayOutputStream;
-import java.io.DataInputStream;
-import java.io.IOException;
-import java.io.InputStream;
-import java.io.OutputStream;
-import java.nio.ByteBuffer;
-import java.nio.channels.Channels;
-import java.nio.channels.ReadableByteChannel;
-import java.security.SecureRandom;
-import java.util.Properties;
-import java.util.Random;
-
-import org.apache.commons.crypto.cipher.Cipher;
-import org.apache.commons.crypto.cipher.CipherTransformation;
-import org.apache.commons.crypto.cipher.JceCipher;
-import org.apache.commons.crypto.cipher.Openssl;
-import org.apache.commons.crypto.cipher.OpensslCipher;
-import org.apache.commons.crypto.utils.ReflectionUtils;
-import org.apache.commons.logging.Log;
-import org.apache.commons.logging.LogFactory;
-import org.junit.Assert;
-import org.junit.Before;
-import org.junit.Test;
-
-public abstract class AbstractCryptoStreamTest {
- private static final Log LOG= LogFactory.getLog(AbstractCryptoStreamTest.class);
-
- private final int dataLen = 20000;
- private byte[] data = new byte[dataLen];
- private byte[] encData;
- private Properties props = new Properties();
- protected byte[] key = new byte[16];
- private byte[] iv = new byte[16];
- private int count = 10000;
- protected static int defaultBufferSize = 8192;
- protected static int smallBufferSize = 1024;
-
- private final String jceCipherClass = JceCipher.class.getName();
- private final String opensslCipherClass = OpensslCipher.class.getName();
- protected CipherTransformation transformation;
-
- public abstract void setUp() throws IOException;
-
- @Before
- public void before() throws IOException {
- Random random = new SecureRandom();
- random.nextBytes(data);
- random.nextBytes(key);
- random.nextBytes(iv);
- setUp();
- prepareData();
- }
-
- /** Test skip. */
- @Test(timeout=120000)
- public void testSkip() throws Exception {
- doSkipTest(jceCipherClass, false);
- doSkipTest(opensslCipherClass, false);
-
- doSkipTest(jceCipherClass, true);
- doSkipTest(opensslCipherClass, true);
- }
-
- /** Test byte buffer read with different buffer size. */
- @Test(timeout=120000)
- public void testByteBufferRead() throws Exception {
- doByteBufferRead(jceCipherClass, false);
- doByteBufferRead(opensslCipherClass, false);
-
- doByteBufferRead(jceCipherClass, true);
- doByteBufferRead(opensslCipherClass, true);
- }
-
- /** Test byte buffer write. */
- @Test(timeout=120000)
- public void testByteBufferWrite() throws Exception {
- ByteArrayOutputStream baos = new ByteArrayOutputStream();
- doByteBufferWrite(jceCipherClass, baos, false);
- doByteBufferWrite(opensslCipherClass, baos, false);
-
- doByteBufferWrite(jceCipherClass, baos, true);
- doByteBufferWrite(opensslCipherClass, baos, true);
- }
-
- private void doSkipTest(String cipherClass, boolean withChannel) throws IOException {
- InputStream in = getCryptoInputStream(new ByteArrayInputStream(encData),
- getCipher(cipherClass), defaultBufferSize, iv, withChannel);
- byte[] result = new byte[dataLen];
- int n1 = readAll(in, result, 0, dataLen / 3);
-
- long skipped = in.skip(dataLen / 3);
- int n2 = readAll(in, result, 0, dataLen);
-
- Assert.assertEquals(dataLen, n1 + skipped + n2);
- byte[] readData = new byte[n2];
- System.arraycopy(result, 0, readData, 0, n2);
- byte[] expectedData = new byte[n2];
- System.arraycopy(data, dataLen - n2, expectedData, 0, n2);
- Assert.assertArrayEquals(readData, expectedData);
-
- try {
- skipped = in.skip(-3);
- Assert.fail("Skip Negative length should fail.");
- } catch (IllegalArgumentException e) {
- Assert.assertTrue(e.getMessage().contains("Negative skip length"));
- }
-
- // Skip after EOF
- skipped = in.skip(3);
- Assert.assertEquals(skipped, 0);
-
- in.close();
- }
-
- private void doByteBufferRead(String cipherClass, boolean withChannel) throws Exception {
- // Default buffer size, initial buffer position is 0
- InputStream in = getCryptoInputStream(new ByteArrayInputStream(encData),
- getCipher(cipherClass), defaultBufferSize, iv, withChannel);
- ByteBuffer buf = ByteBuffer.allocate(dataLen + 100);
- byteBufferReadCheck(in, buf, 0);
- in.close();
-
- // Default buffer size, initial buffer position is not 0
- in = getCryptoInputStream(new ByteArrayInputStream(encData),
- getCipher(cipherClass), defaultBufferSize, iv, withChannel);
- buf.clear();
- byteBufferReadCheck(in, buf, 11);
- in.close();
-
- // Small buffer size, initial buffer position is 0
- in = getCryptoInputStream(new ByteArrayInputStream(encData),
- getCipher(cipherClass), smallBufferSize, iv, withChannel);
- buf.clear();
- byteBufferReadCheck(in, buf, 0);
- in.close();
-
- // Small buffer size, initial buffer position is not 0
- in = getCryptoInputStream(new ByteArrayInputStream(encData),
- getCipher(cipherClass), smallBufferSize, iv, withChannel);
- buf.clear();
- byteBufferReadCheck(in, buf, 11);
- in.close();
-
- // Direct buffer, default buffer size, initial buffer position is 0
- in = getCryptoInputStream(new ByteArrayInputStream(encData),
- getCipher(cipherClass), defaultBufferSize, iv, withChannel);
- buf = ByteBuffer.allocateDirect(dataLen + 100);
- byteBufferReadCheck(in, buf, 0);
- in.close();
-
- // Direct buffer, default buffer size, initial buffer position is not 0
- in = getCryptoInputStream(new ByteArrayInputStream(encData),
- getCipher(cipherClass), defaultBufferSize, iv, withChannel);
- buf.clear();
- byteBufferReadCheck(in, buf, 11);
- in.close();
-
- // Direct buffer, small buffer size, initial buffer position is 0
- in = getCryptoInputStream(new ByteArrayInputStream(encData),
- getCipher(cipherClass), smallBufferSize, iv, withChannel);
- buf.clear();
- byteBufferReadCheck(in, buf, 0);
- in.close();
-
- // Direct buffer, small buffer size, initial buffer position is not 0
- in = getCryptoInputStream(new ByteArrayInputStream(encData),
- getCipher(cipherClass), smallBufferSize, iv, withChannel);
- buf.clear();
- byteBufferReadCheck(in, buf, 11);
- in.close();
- }
-
- private void doByteBufferWrite(String cipherClass, ByteArrayOutputStream baos,
- boolean withChannel)
- throws Exception {
- baos.reset();
- CryptoOutputStream out =
- getCryptoOutputStream(baos, getCipher(cipherClass), defaultBufferSize,
- iv, withChannel);
- ByteBuffer buf = ByteBuffer.allocateDirect(dataLen / 2);
- buf.put(data, 0, dataLen / 2);
- buf.flip();
- int n1 = out.write(buf);
-
- buf.clear();
- buf.put(data, n1, dataLen / 3);
- buf.flip();
- int n2 = out.write(buf);
-
- buf.clear();
- buf.put(data, n1 + n2, dataLen - n1 - n2);
- buf.flip();
- int n3 = out.write(buf);
-
- Assert.assertEquals(dataLen, n1 + n2 + n3);
-
- out.flush();
-
- InputStream in = getCryptoInputStream(new ByteArrayInputStream(encData),
- getCipher(cipherClass), defaultBufferSize, iv, withChannel);
- buf = ByteBuffer.allocate(dataLen + 100);
- byteBufferReadCheck(in, buf, 0);
- in.close();
- }
-
- private void byteBufferReadCheck(InputStream in, ByteBuffer buf,
- int bufPos) throws Exception {
- buf.position(bufPos);
- int n = ((ReadableByteChannel) in).read(buf);
- Assert.assertEquals(bufPos + n, buf.position());
- byte[] readData = new byte[n];
- buf.rewind();
- buf.position(bufPos);
- buf.get(readData);
- byte[] expectedData = new byte[n];
- System.arraycopy(data, 0, expectedData, 0, n);
- Assert.assertArrayEquals(readData, expectedData);
- }
-
- private void prepareData() throws IOException {
- Cipher cipher = null;
- try {
- cipher = (Cipher)ReflectionUtils.newInstance(
- ReflectionUtils.getClassByName(jceCipherClass), props, transformation);
- } catch (ClassNotFoundException cnfe) {
- throw new IOException("Illegal crypto cipher!");
- }
-
- ByteArrayOutputStream baos = new ByteArrayOutputStream();
- OutputStream out = new CryptoOutputStream(baos, cipher, defaultBufferSize, key, iv);
- out.write(data);
- out.flush();
- out.close();
- encData = baos.toByteArray();
- }
-
- protected CryptoInputStream getCryptoInputStream(ByteArrayInputStream bais,
- Cipher cipher,
- int bufferSize, byte[] iv,
- boolean withChannel) throws
- IOException {
- if (withChannel) {
- return new CryptoInputStream(Channels.newChannel(bais), cipher,
- bufferSize, key, iv);
- } else {
- return new CryptoInputStream(bais, cipher, bufferSize, key, iv);
- }
- }
-
- protected CryptoOutputStream getCryptoOutputStream(ByteArrayOutputStream baos,
- Cipher cipher,
- int bufferSize, byte[] iv,
- boolean withChannel) throws
- IOException {
- if (withChannel) {
- return new CryptoOutputStream(Channels.newChannel(baos), cipher,
- bufferSize, key, iv);
- } else {
- return new CryptoOutputStream(baos, cipher, bufferSize, key, iv);
- }
- }
-
- private int readAll(InputStream in, byte[] b, int offset, int len)
- throws IOException {
- int n = 0;
- int total = 0;
- while (n != -1) {
- total += n;
- if (total >= len) {
- break;
- }
- n = in.read(b, offset + total, len - total);
- }
-
- return total;
- }
-
- protected Cipher getCipher(String cipherClass) throws IOException {
- try {
- return (Cipher)ReflectionUtils.newInstance(
- ReflectionUtils.getClassByName(cipherClass), props, transformation);
- } catch (ClassNotFoundException cnfe) {
- throw new IOException("Illegal crypto cipher!");
- }
- }
-
- @Test
- public void testReadWrite() throws Exception {
- Assert.assertEquals(null, Openssl.getLoadingFailureReason());
- doReadWriteTest(0, jceCipherClass, jceCipherClass, iv);
- doReadWriteTest(0, opensslCipherClass, opensslCipherClass, iv);
- doReadWriteTest(count, jceCipherClass, jceCipherClass, iv);
- doReadWriteTest(count, opensslCipherClass, opensslCipherClass, iv);
- doReadWriteTest(count, jceCipherClass, opensslCipherClass, iv);
- doReadWriteTest(count, opensslCipherClass, jceCipherClass, iv);
- // Overflow test, IV: xx xx xx xx xx xx xx xx ff ff ff ff ff ff ff ff
- for(int i = 0; i < 8; i++) {
- iv[8 + i] = (byte) 0xff;
- }
- doReadWriteTest(count, jceCipherClass, jceCipherClass, iv);
- doReadWriteTest(count, opensslCipherClass, opensslCipherClass, iv);
- doReadWriteTest(count, jceCipherClass, opensslCipherClass, iv);
- doReadWriteTest(count, opensslCipherClass, jceCipherClass, iv);
- }
-
- private void doReadWriteTest(int count, String encCipherClass,
- String decCipherClass, byte[] iv) throws IOException {
- doReadWriteTestForInputStream(count, encCipherClass, decCipherClass, iv);
- doReadWriteTestForReadableByteChannel(count, encCipherClass, decCipherClass,
- iv);
- }
-
- private void doReadWriteTestForInputStream(int count, String encCipherClass,
- String decCipherClass, byte[] iv) throws IOException {
- Cipher encCipher = getCipher(encCipherClass);
- LOG.debug("Created a cipher object of type: " + encCipherClass);
-
- // Generate data
- SecureRandom random = new SecureRandom();
- byte[] originalData = new byte[count];
- byte[] decryptedData = new byte[count];
- random.nextBytes(originalData);
- LOG.debug("Generated " + count + " records");
-
- // Encrypt data
- ByteArrayOutputStream encryptedData = new ByteArrayOutputStream();
- CryptoOutputStream out =
- getCryptoOutputStream(encryptedData, encCipher, defaultBufferSize, iv,
- false);
- out.write(originalData, 0, originalData.length);
- out.flush();
- out.close();
- LOG.debug("Finished encrypting data");
-
- Cipher decCipher = getCipher(decCipherClass);
- LOG.debug("Created a cipher object of type: " + decCipherClass);
-
- // Decrypt data
- CryptoInputStream in = getCryptoInputStream(
- new ByteArrayInputStream(encryptedData.toByteArray()), decCipher,
- defaultBufferSize, iv, false);
-
- // Check
- int remainingToRead = count;
- int offset = 0;
- while (remainingToRead > 0) {
- int n = in.read(decryptedData, offset, decryptedData.length - offset);
- if (n >=0) {
- remainingToRead -= n;
- offset += n;
- }
- }
-
- Assert.assertArrayEquals("originalData and decryptedData not equal",
- originalData, decryptedData);
-
- // Decrypt data byte-at-a-time
- in = getCryptoInputStream(
- new ByteArrayInputStream(encryptedData.toByteArray()), decCipher,
- defaultBufferSize, iv, false);
-
- // Check
- DataInputStream originalIn = new DataInputStream(new BufferedInputStream(new ByteArrayInputStream(originalData)));
- int expected;
- do {
- expected = originalIn.read();
- Assert.assertEquals("Decrypted stream read by byte does not match",
- expected, in.read());
- } while (expected != -1);
-
- LOG.debug("SUCCESS! Completed checking " + count + " records");
- }
-
- private void doReadWriteTestForReadableByteChannel(int count,
- String encCipherClass,
- String decCipherClass,
- byte[] iv) throws IOException {
- Cipher encCipher = getCipher(encCipherClass);
- LOG.debug("Created a cipher object of type: " + encCipherClass);
-
- // Generate data
- SecureRandom random = new SecureRandom();
- byte[] originalData = new byte[count];
- byte[] decryptedData = new byte[count];
- random.nextBytes(originalData);
- LOG.debug("Generated " + count + " records");
-
- // Encrypt data
- ByteArrayOutputStream encryptedData = new ByteArrayOutputStream();
- CryptoOutputStream out =
- getCryptoOutputStream(encryptedData, encCipher, defaultBufferSize, iv,
- true);
- out.write(originalData, 0, originalData.length);
- out.flush();
- out.close();
- LOG.debug("Finished encrypting data");
-
- Cipher decCipher = getCipher(decCipherClass);
- LOG.debug("Created a cipher object of type: " + decCipherClass);
-
- // Decrypt data
- CryptoInputStream in = getCryptoInputStream(
- new ByteArrayInputStream(encryptedData.toByteArray()), decCipher,
- defaultBufferSize, iv, true);
-
- // Check
- int remainingToRead = count;
- int offset = 0;
- while (remainingToRead > 0) {
- int n = in.read(decryptedData, offset, decryptedData.length - offset);
- if (n >=0) {
- remainingToRead -= n;
- offset += n;
- }
- }
-
- Assert.assertArrayEquals("originalData and decryptedData not equal",
- originalData, decryptedData);
-
- // Decrypt data byte-at-a-time
- in = getCryptoInputStream(new ByteArrayInputStream(
- encryptedData.toByteArray()),decCipher,defaultBufferSize,iv,true);
-
- // Check
- DataInputStream originalIn = new DataInputStream(new BufferedInputStream(new ByteArrayInputStream(originalData)));
- int expected;
- do {
- expected = originalIn.read();
- Assert.assertEquals("Decrypted stream read by byte does not match",
- expected, in.read());
- } while (expected != -1);
-
- LOG.debug("SUCCESS! Completed checking " + count + " records");
- }
-}
http://git-wip-us.apache.org/repos/asf/commons-crypto/blob/ad81d236/src/test/java/org/apache/commons/crypto/stream/CBCNoPaddingCipherStreamTest.java
----------------------------------------------------------------------
diff --git a/src/test/java/org/apache/commons/crypto/stream/CBCNoPaddingCipherStreamTest.java b/src/test/java/org/apache/commons/crypto/stream/CBCNoPaddingCipherStreamTest.java
new file mode 100644
index 0000000..c8717ff
--- /dev/null
+++ b/src/test/java/org/apache/commons/crypto/stream/CBCNoPaddingCipherStreamTest.java
@@ -0,0 +1,31 @@
+/**
+ * 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 org.apache.commons.crypto.cipher.CipherTransformation;
+
+public class CBCNoPaddingCipherStreamTest extends AbstractCipherStreamTest {
+
+ public void setUp() throws IOException {
+ transformation = CipherTransformation
+ .AES_CBC_NOPADDING;
+ }
+
+}
http://git-wip-us.apache.org/repos/asf/commons-crypto/blob/ad81d236/src/test/java/org/apache/commons/crypto/stream/CBCNoPaddingCryptoStreamTest.java
----------------------------------------------------------------------
diff --git a/src/test/java/org/apache/commons/crypto/stream/CBCNoPaddingCryptoStreamTest.java b/src/test/java/org/apache/commons/crypto/stream/CBCNoPaddingCryptoStreamTest.java
deleted file mode 100644
index 743a3ac..0000000
--- a/src/test/java/org/apache/commons/crypto/stream/CBCNoPaddingCryptoStreamTest.java
+++ /dev/null
@@ -1,31 +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 org.apache.commons.crypto.cipher.CipherTransformation;
-
-public class CBCNoPaddingCryptoStreamTest extends AbstractCryptoStreamTest {
-
- public void setUp() throws IOException {
- transformation = CipherTransformation
- .AES_CBC_NOPADDING;
- }
-
-}
http://git-wip-us.apache.org/repos/asf/commons-crypto/blob/ad81d236/src/test/java/org/apache/commons/crypto/stream/CBCPKCS5PaddingCipherStreamTest.java
----------------------------------------------------------------------
diff --git a/src/test/java/org/apache/commons/crypto/stream/CBCPKCS5PaddingCipherStreamTest.java b/src/test/java/org/apache/commons/crypto/stream/CBCPKCS5PaddingCipherStreamTest.java
new file mode 100644
index 0000000..ff34bc9
--- /dev/null
+++ b/src/test/java/org/apache/commons/crypto/stream/CBCPKCS5PaddingCipherStreamTest.java
@@ -0,0 +1,30 @@
+/**
+ * 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 org.apache.commons.crypto.cipher.CipherTransformation;
+
+public class CBCPKCS5PaddingCipherStreamTest extends AbstractCipherStreamTest {
+
+ public void setUp() throws IOException {
+ transformation = CipherTransformation
+ .AES_CBC_PKCS5PADDING;
+ }
+}
http://git-wip-us.apache.org/repos/asf/commons-crypto/blob/ad81d236/src/test/java/org/apache/commons/crypto/stream/CBCPKCS5PaddingCryptoStreamTest.java
----------------------------------------------------------------------
diff --git a/src/test/java/org/apache/commons/crypto/stream/CBCPKCS5PaddingCryptoStreamTest.java b/src/test/java/org/apache/commons/crypto/stream/CBCPKCS5PaddingCryptoStreamTest.java
deleted file mode 100644
index cb65dac..0000000
--- a/src/test/java/org/apache/commons/crypto/stream/CBCPKCS5PaddingCryptoStreamTest.java
+++ /dev/null
@@ -1,30 +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 org.apache.commons.crypto.cipher.CipherTransformation;
-
-public class CBCPKCS5PaddingCryptoStreamTest extends AbstractCryptoStreamTest {
-
- public void setUp() throws IOException {
- transformation = CipherTransformation
- .AES_CBC_PKCS5PADDING;
- }
-}
http://git-wip-us.apache.org/repos/asf/commons-crypto/blob/ad81d236/src/test/java/org/apache/commons/crypto/stream/CTRCipherStreamTest.java
----------------------------------------------------------------------
diff --git a/src/test/java/org/apache/commons/crypto/stream/CTRCipherStreamTest.java b/src/test/java/org/apache/commons/crypto/stream/CTRCipherStreamTest.java
new file mode 100644
index 0000000..f5e85ff
--- /dev/null
+++ b/src/test/java/org/apache/commons/crypto/stream/CTRCipherStreamTest.java
@@ -0,0 +1,58 @@
+/**
+ * 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.ByteArrayInputStream;
+import java.io.ByteArrayOutputStream;
+import java.io.IOException;
+import java.nio.channels.Channels;
+
+import org.apache.commons.crypto.cipher.Cipher;
+import org.apache.commons.crypto.cipher.CipherTransformation;
+
+public class CTRCipherStreamTest extends AbstractCipherStreamTest {
+
+ public void setUp() throws IOException {
+ transformation = CipherTransformation
+ .AES_CTR_NOPADDING;
+ }
+
+ @Override
+ protected CTRCipherInputStream getCipherInputStream
+ (ByteArrayInputStream bais, Cipher cipher, int
+ bufferSize,byte[] iv, boolean withChannel)
+ throws IOException {
+ if (withChannel) {
+ return new CTRCipherInputStream(Channels.newChannel(bais), cipher,
+ bufferSize, key, iv);
+ } else {
+ return new CTRCipherInputStream(bais, cipher, bufferSize, key, iv);
+ }
+ }
+
+ @Override
+ protected CTRCipherOutputStream getCipherOutputStream(ByteArrayOutputStream baos, Cipher cipher, int
+ bufferSize, byte[] iv, boolean withChannel)
+ throws IOException {
+ if (withChannel) {
+ return new CTRCipherOutputStream(Channels.newChannel(baos), cipher, bufferSize, key, iv);
+ } else {
+ return new CTRCipherOutputStream(baos, cipher, bufferSize, key, iv);
+ }
+ }
+}
http://git-wip-us.apache.org/repos/asf/commons-crypto/blob/ad81d236/src/test/java/org/apache/commons/crypto/stream/CTRCryptoStreamTest.java
----------------------------------------------------------------------
diff --git a/src/test/java/org/apache/commons/crypto/stream/CTRCryptoStreamTest.java b/src/test/java/org/apache/commons/crypto/stream/CTRCryptoStreamTest.java
deleted file mode 100644
index 1a50382..0000000
--- a/src/test/java/org/apache/commons/crypto/stream/CTRCryptoStreamTest.java
+++ /dev/null
@@ -1,58 +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.ByteArrayInputStream;
-import java.io.ByteArrayOutputStream;
-import java.io.IOException;
-import java.nio.channels.Channels;
-
-import org.apache.commons.crypto.cipher.Cipher;
-import org.apache.commons.crypto.cipher.CipherTransformation;
-
-public class CTRCryptoStreamTest extends AbstractCryptoStreamTest {
-
- public void setUp() throws IOException {
- transformation = CipherTransformation
- .AES_CTR_NOPADDING;
- }
-
- @Override
- protected CTRCryptoInputStream getCryptoInputStream
- (ByteArrayInputStream bais, Cipher cipher, int
- bufferSize,byte[] iv, boolean withChannel)
- throws IOException {
- if (withChannel) {
- return new CTRCryptoInputStream(Channels.newChannel(bais), cipher,
- bufferSize, key, iv);
- } else {
- return new CTRCryptoInputStream(bais, cipher, bufferSize, key, iv);
- }
- }
-
- @Override
- protected CTRCryptoOutputStream getCryptoOutputStream(ByteArrayOutputStream baos,Cipher cipher, int
- bufferSize, byte[] iv, boolean withChannel)
- throws IOException {
- if (withChannel) {
- return new CTRCryptoOutputStream(Channels.newChannel(baos), cipher, bufferSize, key, iv);
- } else {
- return new CTRCryptoOutputStream(baos, cipher, bufferSize, key, iv);
- }
- }
-}
http://git-wip-us.apache.org/repos/asf/commons-crypto/blob/ad81d236/src/test/java/org/apache/commons/crypto/stream/CTRNoPaddingCipherStreamTest.java
----------------------------------------------------------------------
diff --git a/src/test/java/org/apache/commons/crypto/stream/CTRNoPaddingCipherStreamTest.java b/src/test/java/org/apache/commons/crypto/stream/CTRNoPaddingCipherStreamTest.java
new file mode 100644
index 0000000..b59761d
--- /dev/null
+++ b/src/test/java/org/apache/commons/crypto/stream/CTRNoPaddingCipherStreamTest.java
@@ -0,0 +1,31 @@
+/**
+ * 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 org.apache.commons.crypto.cipher.CipherTransformation;
+
+public class CTRNoPaddingCipherStreamTest extends AbstractCipherStreamTest {
+
+ public void setUp() throws IOException {
+ transformation = CipherTransformation
+ .AES_CTR_NOPADDING;
+ }
+
+}
http://git-wip-us.apache.org/repos/asf/commons-crypto/blob/ad81d236/src/test/java/org/apache/commons/crypto/stream/CTRNoPaddingCryptoStreamTest.java
----------------------------------------------------------------------
diff --git a/src/test/java/org/apache/commons/crypto/stream/CTRNoPaddingCryptoStreamTest.java b/src/test/java/org/apache/commons/crypto/stream/CTRNoPaddingCryptoStreamTest.java
deleted file mode 100644
index 1bbf600..0000000
--- a/src/test/java/org/apache/commons/crypto/stream/CTRNoPaddingCryptoStreamTest.java
+++ /dev/null
@@ -1,31 +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 org.apache.commons.crypto.cipher.CipherTransformation;
-
-public class CTRNoPaddingCryptoStreamTest extends AbstractCryptoStreamTest {
-
- public void setUp() throws IOException {
- transformation = CipherTransformation
- .AES_CTR_NOPADDING;
- }
-
-}
[3/3] commons-crypto git commit: CRYPTO-12: Rename CryptoInputStream
to CipherInputStream and CryptoOutputStream to CipherOutputStream (Xianda Ke
via Dian Fu)
Posted by di...@apache.org.
CRYPTO-12: Rename CryptoInputStream to CipherInputStream and CryptoOutputStream to CipherOutputStream
(Xianda Ke via Dian Fu)
Project: http://git-wip-us.apache.org/repos/asf/commons-crypto/repo
Commit: http://git-wip-us.apache.org/repos/asf/commons-crypto/commit/ad81d236
Tree: http://git-wip-us.apache.org/repos/asf/commons-crypto/tree/ad81d236
Diff: http://git-wip-us.apache.org/repos/asf/commons-crypto/diff/ad81d236
Branch: refs/heads/master
Commit: ad81d236a9e132f59486b5a73c347faabe329e9e
Parents: f819dd4
Author: dianfu <di...@apache.org>
Authored: Tue Apr 26 11:27:08 2016 +0800
Committer: dianfu <di...@apache.org>
Committed: Tue Apr 26 11:27:08 2016 +0800
----------------------------------------------------------------------
README.md | 6 +-
.../crypto/stream/CTRCipherInputStream.java | 423 +++++++++++++++++
.../crypto/stream/CTRCipherOutputStream.java | 230 ++++++++++
.../crypto/stream/CTRCryptoInputStream.java | 423 -----------------
.../crypto/stream/CTRCryptoOutputStream.java | 230 ----------
.../crypto/stream/CipherInputStream.java | 394 ++++++++++++++++
.../crypto/stream/CipherOutputStream.java | 283 ++++++++++++
.../crypto/stream/CryptoInputStream.java | 394 ----------------
.../crypto/stream/CryptoOutputStream.java | 283 ------------
.../stream/PositionedCipherInputStream.java | 311 +++++++++++++
.../stream/PositionedCryptoInputStream.java | 311 -------------
.../crypto/stream/input/ChannelInput.java | 2 +-
.../commons/crypto/stream/input/Input.java | 2 +-
.../crypto/stream/input/StreamInput.java | 2 +-
.../crypto/stream/output/ChannelOutput.java | 2 +-
.../commons/crypto/stream/output/Output.java | 2 +-
.../crypto/stream/output/StreamOutput.java | 2 +-
.../crypto/stream/AbstractCipherStreamTest.java | 456 +++++++++++++++++++
.../crypto/stream/AbstractCryptoStreamTest.java | 456 -------------------
.../stream/CBCNoPaddingCipherStreamTest.java | 31 ++
.../stream/CBCNoPaddingCryptoStreamTest.java | 31 --
.../stream/CBCPKCS5PaddingCipherStreamTest.java | 30 ++
.../stream/CBCPKCS5PaddingCryptoStreamTest.java | 30 --
.../crypto/stream/CTRCipherStreamTest.java | 58 +++
.../crypto/stream/CTRCryptoStreamTest.java | 58 ---
.../stream/CTRNoPaddingCipherStreamTest.java | 31 ++
.../stream/CTRNoPaddingCryptoStreamTest.java | 31 --
.../stream/PositionedCipherInputStreamTest.java | 381 ++++++++++++++++
.../stream/PositionedCryptoInputStreamTest.java | 381 ----------------
29 files changed, 2637 insertions(+), 2637 deletions(-)
----------------------------------------------------------------------
http://git-wip-us.apache.org/repos/asf/commons-crypto/blob/ad81d236/README.md
----------------------------------------------------------------------
diff --git a/README.md b/README.md
index 34845c0..adc9d63 100644
--- a/README.md
+++ b/README.md
@@ -2,7 +2,7 @@ Chimera [![Build Status](https://travis-ci.org/intel-hadoop/chimera.svg?branch=m
## Features
* Cipher API for low level cryptographic operations.
- * Java stream API (CryptoInputStream/CryptoOutputStream) for high level stream encyrption/decryption.
+ * Java stream API (CipherInputStream/CipherOutputStream) for high level stream encyrption/decryption.
* Both optimized with high performance AES encryption/decryption. (1400 MB/s - 1700 MB/s throughput in modern Xeon processors).
* JNI-based implementation to achieve comparable performance to the native C++ version based on Openssl.
* Portable across various operating systems (currently only Linux); Chimera loads the library according to your machine environment (It looks system properties, `os.name` and `os.arch`).
@@ -47,13 +47,13 @@ String input = "hello world!";
byte[] decryptedData = new byte[1024];
// Encrypt
ByteArrayOutputStream os = new ByteArrayOutputStream();
-CryptoOutputStream cos = new CryptoOutputStream(os, cipher, bufferSize, key, iv);
+CipherOutputStream cos = new CipherOutputStream(os, cipher, bufferSize, key, iv);
cos.write(input.getBytes("UTF-8"));
cos.flush();
cos.close();
// Decrypt
-CryptoInputStream cis = new CryptoInputStream(new ByteArrayInputStream(os.toByteArray()), cipher, bufferSize, key, iv);
+CipherInputStream cis = new CipherInputStream(new ByteArrayInputStream(os.toByteArray()), cipher, bufferSize, key, iv);
int decryptedLen = cis.read(decryptedData, 0, 1024);
```
http://git-wip-us.apache.org/repos/asf/commons-crypto/blob/ad81d236/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
new file mode 100644
index 0000000..6c3c657
--- /dev/null
+++ b/src/main/java/org/apache/commons/crypto/stream/CTRCipherInputStream.java
@@ -0,0 +1,423 @@
+/**
+ * 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 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
+ */
+ protected long streamOffset = 0;
+
+ /**
+ * 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;
+
+ public CTRCipherInputStream(Properties props, InputStream in,
+ byte[] key, byte[] iv)
+ throws IOException {
+ this(props, in, key, iv, 0);
+ }
+
+ public CTRCipherInputStream(Properties props, ReadableByteChannel in,
+ byte[] key, byte[] iv)
+ throws IOException {
+ this(props, in, key, iv, 0);
+ }
+
+ public CTRCipherInputStream(InputStream in, Cipher cipher, int bufferSize,
+ byte[] key, byte[] iv) throws IOException {
+ this(in, cipher, bufferSize, key, iv, 0);
+ }
+
+ public CTRCipherInputStream(ReadableByteChannel in, Cipher cipher,
+ int bufferSize, byte[] key, byte[] iv) throws IOException {
+ this(in, cipher, bufferSize, key, iv, 0);
+ }
+
+ public CTRCipherInputStream(
+ Input input,
+ Cipher cipher,
+ int bufferSize,
+ byte[] key,
+ byte[] iv) throws IOException {
+ this(input, cipher, bufferSize, key, iv, 0);
+ }
+
+ 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);
+ }
+
+ 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);
+ }
+
+ 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);
+ }
+
+ 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);
+ }
+
+ public CTRCipherInputStream(
+ Input input,
+ Cipher cipher,
+ int bufferSize,
+ byte[] key,
+ byte[] iv,
+ long streamOffset) throws IOException {
+ super(input, cipher, bufferSize, key, iv);
+
+ Utils.checkStreamCipher(cipher);
+
+ resetStreamOffset(streamOffset);
+ }
+
+ /** Skip n bytes */
+ @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;
+ }
+ }
+
+ /** ByteBuffer read. */
+ @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;
+ }
+ }
+
+ /**
+ * Seek the stream to a specific position relative to start of the under layer stream.
+ *
+ * @param position The position to seek to
+ * @throws IOException if seek failed
+ */
+ 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);
+ }
+ }
+
+ protected long getStreamOffset() {
+ return streamOffset;
+ }
+
+ protected long getStreamPosition() {
+ return streamOffset - outBuffer.remaining();
+ }
+
+ /**
+ * Decrypt 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
+ */
+ 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();
+ }
+
+ /**
+ * Do 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();
+ */
+ 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);
+ }
+ }
+
+ /**
+ * Do 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.
+ */
+ 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();
+ }
+
+ /**
+ * Decrypt 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.
+ */
+ 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. Check whether
+ * cipher should be updated and recalculate padding if needed.
+ */
+ 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;
+ }
+
+ protected long getCounter(long position) {
+ return position / cipher.getTransformation().getAlgorithmBlockSize();
+ }
+
+ protected byte getPadding(long position) {
+ return (byte)(position % cipher.getTransformation().getAlgorithmBlockSize());
+ }
+
+ /** Initialize the cipher. */
+ @Override
+ protected void initCipher() {
+ // Do nothing for initCipher
+ // Will reset the cipher when reset the stream offset
+ }
+
+ /** Calculate the counter and iv, reset the cipher. */
+ protected void resetCipher(long position)
+ throws IOException {
+ final long counter = getCounter(position);
+ Utils.calculateIV(initIV, counter, iv);
+ try {
+ cipher.init(Cipher.DECRYPT_MODE, key, iv);
+ } catch (InvalidKeyException e) {
+ throw new IOException(e);
+ } catch (InvalidAlgorithmParameterException e) {
+ throw new IOException(e);
+ }
+ cipherReset = false;
+ }
+
+ /**
+ * Reset the underlying stream offset; clear {@link #inBuffer} and
+ * {@link #outBuffer}. This Typically happens during {@link #skip(long)}.
+ */
+ 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.
+ }
+
+ 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/ad81d236/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
new file mode 100644
index 0000000..13ac256
--- /dev/null
+++ b/src/main/java/org/apache/commons/crypto/stream/CTRCipherOutputStream.java
@@ -0,0 +1,230 @@
+/**
+ * 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 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.
+ */
+ protected long streamOffset = 0;
+
+ /**
+ * 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;
+
+ public CTRCipherOutputStream(Properties props, OutputStream out,
+ byte[] key, byte[] iv)
+ throws IOException {
+ this(props, out, key, iv, 0);
+ }
+
+ public CTRCipherOutputStream(Properties props, WritableByteChannel out,
+ byte[] key, byte[] iv)
+ throws IOException {
+ this(props, out, key, iv, 0);
+ }
+
+ public CTRCipherOutputStream(OutputStream out, Cipher cipher,
+ int bufferSize, byte[] key, byte[] iv) throws IOException {
+ this(out, cipher, bufferSize, key, iv, 0);
+ }
+
+ public CTRCipherOutputStream(WritableByteChannel channel, Cipher cipher,
+ int bufferSize, byte[] key, byte[] iv) throws IOException {
+ this(channel, cipher, bufferSize, key, iv, 0);
+ }
+
+ public CTRCipherOutputStream(Output output, Cipher cipher,
+ int bufferSize, byte[] key, byte[] iv)
+ throws IOException {
+ this(output, cipher, bufferSize, key, iv, 0);
+ }
+
+ 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);
+ }
+
+ 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);
+ }
+
+ 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);
+ }
+
+ 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);
+ }
+
+ public CTRCipherOutputStream(Output output, Cipher cipher,
+ int bufferSize, byte[] key, byte[] iv, long streamOffset)
+ throws IOException {
+ super(output, cipher, bufferSize, key, iv);
+
+ Utils.checkStreamCipher(cipher);
+ this.streamOffset = streamOffset;
+
+ resetCipher();
+ }
+
+ /**
+ * Do the encryption, input is {@link #inBuffer} and output is
+ * {@link #outBuffer}.
+ */
+ @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();
+ }
+ }
+
+ /**
+ * Do final encryption of the last data
+ */
+ @Override
+ protected void encryptFinal() throws IOException {
+ // The same as the normal encryption for Counter mode
+ encrypt();
+ }
+
+ /** Initialize the cipher. */
+ @Override
+ protected void initCipher() {
+ // Do nothing for initCipher
+ // Will reset the cipher considering the stream offset
+ }
+
+ /** Reset the {@link #cipher}: calculate counter and {@link #padding}. */
+ 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, iv);
+ } catch (InvalidKeyException e) {
+ throw new IOException(e);
+ }catch (InvalidAlgorithmParameterException e) {
+ throw new IOException(e);
+ }
+ cipherReset = false;
+ }
+
+ 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);
+ }
+ }
+}
http://git-wip-us.apache.org/repos/asf/commons-crypto/blob/ad81d236/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
deleted file mode 100644
index fc76f16..0000000
--- a/src/main/java/org/apache/commons/crypto/stream/CTRCryptoInputStream.java
+++ /dev/null
@@ -1,423 +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 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;
-
-/**
- * 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
- */
- protected long streamOffset = 0;
-
- /**
- * 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;
-
- public CTRCryptoInputStream(Properties props, InputStream in,
- byte[] key, byte[] iv)
- throws IOException {
- this(props, in, key, iv, 0);
- }
-
- public CTRCryptoInputStream(Properties props, ReadableByteChannel in,
- byte[] key, byte[] iv)
- throws IOException {
- this(props, in, key, iv, 0);
- }
-
- public CTRCryptoInputStream(InputStream in, Cipher cipher, int bufferSize,
- byte[] key, byte[] iv) throws IOException {
- this(in, cipher, bufferSize, key, iv, 0);
- }
-
- public CTRCryptoInputStream(ReadableByteChannel in, Cipher cipher,
- int bufferSize, byte[] key, byte[] iv) throws IOException {
- this(in, cipher, bufferSize, key, iv, 0);
- }
-
- public CTRCryptoInputStream(
- Input input,
- Cipher cipher,
- int bufferSize,
- byte[] key,
- byte[] iv) throws IOException {
- this(input, cipher, bufferSize, key, iv, 0);
- }
-
- 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);
- }
-
- 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);
- }
-
- public CTRCryptoInputStream(InputStream in, Cipher cipher, int bufferSize,
- byte[] key, byte[] iv, long streamOffset) throws IOException {
- this(new StreamInput(in, bufferSize), cipher, bufferSize, key, iv, streamOffset);
- }
-
- public CTRCryptoInputStream(ReadableByteChannel in, Cipher cipher,
- int bufferSize, byte[] key, byte[] iv, long streamOffset) throws IOException {
- this(new ChannelInput(in), cipher, bufferSize, key, iv, streamOffset);
- }
-
- public CTRCryptoInputStream(
- Input input,
- Cipher cipher,
- int bufferSize,
- byte[] key,
- byte[] iv,
- long streamOffset) throws IOException {
- super(input, cipher, bufferSize, key, iv);
-
- Utils.checkStreamCipher(cipher);
-
- resetStreamOffset(streamOffset);
- }
-
- /** Skip n bytes */
- @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;
- }
- }
-
- /** ByteBuffer read. */
- @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;
- }
- }
-
- /**
- * Seek the stream to a specific position relative to start of the under layer stream.
- *
- * @param position The position to seek to
- * @throws IOException if seek failed
- */
- 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);
- }
- }
-
- protected long getStreamOffset() {
- return streamOffset;
- }
-
- protected long getStreamPosition() {
- return streamOffset - outBuffer.remaining();
- }
-
- /**
- * Decrypt 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
- */
- 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();
- }
-
- /**
- * Do 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();
- */
- 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);
- }
- }
-
- /**
- * Do 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.
- */
- 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();
- }
-
- /**
- * Decrypt 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.
- */
- 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. Check whether
- * cipher should be updated and recalculate padding if needed.
- */
- 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;
- }
-
- protected long getCounter(long position) {
- return position / cipher.getTransformation().getAlgorithmBlockSize();
- }
-
- protected byte getPadding(long position) {
- return (byte)(position % cipher.getTransformation().getAlgorithmBlockSize());
- }
-
- /** Initialize the cipher. */
- @Override
- protected void initCipher() {
- // Do nothing for initCipher
- // Will reset the cipher when reset the stream offset
- }
-
- /** Calculate the counter and iv, reset the cipher. */
- protected void resetCipher(long position)
- throws IOException {
- final long counter = getCounter(position);
- Utils.calculateIV(initIV, counter, iv);
- try {
- cipher.init(Cipher.DECRYPT_MODE, key, iv);
- } catch (InvalidKeyException e) {
- throw new IOException(e);
- } catch (InvalidAlgorithmParameterException e) {
- throw new IOException(e);
- }
- cipherReset = false;
- }
-
- /**
- * Reset the underlying stream offset; clear {@link #inBuffer} and
- * {@link #outBuffer}. This Typically happens during {@link #skip(long)}.
- */
- 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.
- }
-
- 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/ad81d236/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
deleted file mode 100644
index cb282e3..0000000
--- a/src/main/java/org/apache/commons/crypto/stream/CTRCryptoOutputStream.java
+++ /dev/null
@@ -1,230 +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 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;
-
-/**
- * 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.
- */
- protected long streamOffset = 0;
-
- /**
- * 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;
-
- public CTRCryptoOutputStream(Properties props, OutputStream out,
- byte[] key, byte[] iv)
- throws IOException {
- this(props, out, key, iv, 0);
- }
-
- public CTRCryptoOutputStream(Properties props, WritableByteChannel out,
- byte[] key, byte[] iv)
- throws IOException {
- this(props, out, key, iv, 0);
- }
-
- public CTRCryptoOutputStream(OutputStream out, Cipher cipher,
- int bufferSize, byte[] key, byte[] iv) throws IOException {
- this(out, cipher, bufferSize, key, iv, 0);
- }
-
- public CTRCryptoOutputStream(WritableByteChannel channel, Cipher cipher,
- int bufferSize, byte[] key, byte[] iv) throws IOException {
- this(channel, cipher, bufferSize, key, iv, 0);
- }
-
- public CTRCryptoOutputStream(Output output, Cipher cipher,
- int bufferSize, byte[] key, byte[] iv)
- throws IOException {
- this(output, cipher, bufferSize, key, iv, 0);
- }
-
- 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);
- }
-
- 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);
- }
-
- public CTRCryptoOutputStream(OutputStream out, Cipher cipher,
- int bufferSize, byte[] key, byte[] iv, long streamOffset) throws IOException {
- this(new StreamOutput(out, bufferSize), cipher,
- bufferSize, key, iv, streamOffset);
- }
-
- public CTRCryptoOutputStream(WritableByteChannel channel, Cipher cipher,
- int bufferSize, byte[] key, byte[] iv, long streamOffset) throws IOException {
- this(new ChannelOutput(channel), cipher,
- bufferSize, key, iv, streamOffset);
- }
-
- public CTRCryptoOutputStream(Output output, Cipher cipher,
- int bufferSize, byte[] key, byte[] iv, long streamOffset)
- throws IOException {
- super(output, cipher, bufferSize, key, iv);
-
- Utils.checkStreamCipher(cipher);
- this.streamOffset = streamOffset;
-
- resetCipher();
- }
-
- /**
- * Do the encryption, input is {@link #inBuffer} and output is
- * {@link #outBuffer}.
- */
- @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();
- }
- }
-
- /**
- * Do final encryption of the last data
- */
- @Override
- protected void encryptFinal() throws IOException {
- // The same as the normal encryption for Counter mode
- encrypt();
- }
-
- /** Initialize the cipher. */
- @Override
- protected void initCipher() {
- // Do nothing for initCipher
- // Will reset the cipher considering the stream offset
- }
-
- /** Reset the {@link #cipher}: calculate counter and {@link #padding}. */
- 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, iv);
- } catch (InvalidKeyException e) {
- throw new IOException(e);
- }catch (InvalidAlgorithmParameterException e) {
- throw new IOException(e);
- }
- cipherReset = false;
- }
-
- 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);
- }
- }
-}
http://git-wip-us.apache.org/repos/asf/commons-crypto/blob/ad81d236/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
new file mode 100644
index 0000000..47a1c4b
--- /dev/null
+++ b/src/main/java/org/apache/commons/crypto/stream/CipherInputStream.java
@@ -0,0 +1,394 @@
+/**
+ * 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 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;
+
+/**
+ * CipherInputStream 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];
+
+ protected final Cipher cipher;
+ protected final int bufferSize;
+
+ protected final byte[] key;
+ protected final byte[] initIV;
+ protected byte[] iv;
+
+ protected boolean closed;
+ protected boolean finalDone = false;
+
+ protected 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;
+
+ public CipherInputStream(CipherTransformation transformation,
+ Properties props, InputStream in, byte[] key, byte[] iv)
+ throws IOException {
+ this(in, Utils.getCipherInstance(transformation, props),
+ Utils.getBufferSize(props), key, iv);
+ }
+
+ public CipherInputStream(CipherTransformation transformation,
+ Properties props, ReadableByteChannel in, byte[] key, byte[] iv)
+ throws IOException {
+ this(in, Utils.getCipherInstance(transformation, props),
+ Utils.getBufferSize(props), key, iv);
+ }
+
+ public CipherInputStream(InputStream in, Cipher cipher, int bufferSize,
+ byte[] key, byte[] iv) throws IOException {
+ this(new StreamInput(in, bufferSize), cipher, bufferSize, key, iv);
+ }
+
+ public CipherInputStream(ReadableByteChannel in, Cipher cipher,
+ int bufferSize, byte[] key, byte[] iv) throws IOException {
+ this(new ChannelInput(in), cipher, bufferSize, key, iv);
+ }
+
+ public CipherInputStream(
+ Input input,
+ Cipher cipher,
+ int bufferSize,
+ byte[] key,
+ byte[] iv) throws IOException {
+ this.input = input;
+ this.cipher = cipher;
+ this.bufferSize = Utils.checkBufferSize(cipher, bufferSize);
+ this.key = key.clone();
+ this.initIV = iv.clone();
+ this.iv = iv.clone();
+
+ inBuffer = ByteBuffer.allocateDirect(this.bufferSize);
+ outBuffer = ByteBuffer.allocateDirect(this.bufferSize +
+ cipher.getTransformation().getAlgorithmBlockSize());
+ outBuffer.limit(0);
+
+ initCipher();
+ }
+
+ @Override
+ public int read() throws IOException {
+ int n;
+ while ((n = read(oneByteBuf, 0, 1)) == 0) ;
+ return (n == -1) ? -1 : oneByteBuf[0] & 0xff;
+ }
+
+ /**
+ * 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
+ */
+ @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;
+ }
+ }
+
+ @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;
+ }
+
+ @Override
+ public int available() throws IOException {
+ checkStream();
+
+ return input.available() + outBuffer.remaining();
+ }
+
+ @Override
+ public void close() throws IOException {
+ if (closed) {
+ return;
+ }
+
+ input.close();
+ freeBuffers();
+ cipher.close();
+ super.close();
+ closed = true;
+ }
+
+ @Override
+ public void mark(int readlimit) {
+ }
+
+ @Override
+ public void reset() throws IOException {
+ throw new IOException("Mark/reset not supported");
+ }
+
+ @Override
+ public boolean markSupported() {
+ return false;
+ }
+
+ @Override
+ public boolean isOpen() {
+ return !closed;
+ }
+
+ @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;
+ }
+ }
+
+ /**
+ * Get the buffer size
+ */
+ protected int getBufferSize() {
+ return bufferSize;
+ }
+
+ /**
+ * Get the key
+ */
+ protected byte[] getKey() {
+ return key;
+ }
+
+ /**
+ * Get the initialization vector
+ */
+ protected byte[] getInitIV() {
+ return initIV;
+ }
+
+ /**
+ * Get the internal Cipher
+ */
+ protected Cipher getCipher() {
+ return cipher;
+ }
+
+ /** Initialize the cipher. */
+ protected void initCipher()
+ throws IOException {
+ try {
+ cipher.init(Cipher.DECRYPT_MODE, key, iv);
+ } catch (InvalidKeyException e) {
+ throw new IOException(e);
+ } catch(InvalidAlgorithmParameterException e) {
+ throw new IOException(e);
+ }
+ }
+
+ /**
+ * Decrypt 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();
+ }
+ }
+
+ /**
+ * Do 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();
+ */
+ 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();
+ }
+
+ /**
+ * Do final of the cipher to end the decrypting stream
+ */
+ 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();
+ }
+
+ 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);
+ }
+}
\ No newline at end of file
http://git-wip-us.apache.org/repos/asf/commons-crypto/blob/ad81d236/src/main/java/org/apache/commons/crypto/stream/CipherOutputStream.java
----------------------------------------------------------------------
diff --git a/src/main/java/org/apache/commons/crypto/stream/CipherOutputStream.java b/src/main/java/org/apache/commons/crypto/stream/CipherOutputStream.java
new file mode 100644
index 0000000..b0a0f96
--- /dev/null
+++ b/src/main/java/org/apache/commons/crypto/stream/CipherOutputStream.java
@@ -0,0 +1,283 @@
+/**
+ * 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 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;
+
+/**
+ * CipherOutputStream encrypts data and writes to the under layer output. It supports
+ * any mode of operations such as AES CBC/CTR/GCM mode in concept. It is not thread-safe.
+ */
+
+public class CipherOutputStream extends OutputStream implements
+ WritableByteChannel {
+ private final byte[] oneByteBuf = new byte[1];
+
+ protected Output output;
+ protected final Cipher cipher;
+ protected final int bufferSize;
+
+ protected final byte[] key;
+ protected final byte[] initIV;
+ protected byte[] iv;
+
+ protected boolean closed;
+
+ /**
+ * Input data buffer. The data starts at inBuffer.position() and ends at
+ * inBuffer.limit().
+ */
+ protected ByteBuffer inBuffer;
+
+ /**
+ * Encrypted data buffer. The data starts at outBuffer.position() and ends at
+ * outBuffer.limit();
+ */
+ protected ByteBuffer outBuffer;
+
+ public CipherOutputStream(CipherTransformation transformation,
+ Properties props, OutputStream out, byte[] key, byte[] iv)
+ throws IOException {
+ this(out, Utils.getCipherInstance(transformation, props),
+ Utils.getBufferSize(props), key, iv);
+ }
+
+ public CipherOutputStream(CipherTransformation transformation,
+ Properties props, WritableByteChannel out, byte[] key, byte[] iv)
+ throws IOException {
+ this(out, Utils.getCipherInstance(transformation, props),
+ Utils.getBufferSize(props), key, iv);
+ }
+
+ public CipherOutputStream(OutputStream out, Cipher cipher,
+ int bufferSize, byte[] key, byte[] iv) throws IOException {
+ this(new StreamOutput(out, bufferSize), cipher, bufferSize, key, iv);
+ }
+
+ public CipherOutputStream(WritableByteChannel channel, Cipher cipher,
+ int bufferSize, byte[] key, byte[] iv) throws IOException {
+ this(new ChannelOutput(channel), cipher, bufferSize, key, iv);
+ }
+
+ protected CipherOutputStream(Output output, Cipher cipher,
+ int bufferSize, byte[] key, byte[] iv)
+ throws IOException {
+
+ this.output = output;
+ this.bufferSize = Utils.checkBufferSize(cipher, bufferSize);
+ this.cipher = cipher;
+ this.key = key.clone();
+ this.initIV = iv.clone();
+ this.iv = iv.clone();
+ inBuffer = ByteBuffer.allocateDirect(this.bufferSize);
+ outBuffer = ByteBuffer.allocateDirect(this.bufferSize +
+ cipher.getTransformation().getAlgorithmBlockSize());
+
+ initCipher();
+ }
+
+ @Override
+ public void write(int b) throws IOException {
+ oneByteBuf[0] = (byte)(b & 0xff);
+ write(oneByteBuf, 0, oneByteBuf.length);
+ }
+
+ /**
+ * Encryption is buffer based.
+ * If there is enough room in {@link #inBuffer}, then write to this buffer.
+ * If {@link #inBuffer} is full, then do encryption and write data to the
+ * underlying stream.
+ * @param b the data.
+ * @param off the start offset in the data.
+ * @param len the number of bytes to write.
+ * @throws IOException
+ */
+ public void write(byte[] b, int off, int len) throws IOException {
+ checkStream();
+ if (b == null) {
+ throw new NullPointerException();
+ } else if (off < 0 || len < 0 || off > b.length ||
+ len > b.length - off) {
+ throw new IndexOutOfBoundsException();
+ }
+
+ while (len > 0) {
+ final int remaining = inBuffer.remaining();
+ if (len < remaining) {
+ inBuffer.put(b, off, len);
+ len = 0;
+ } else {
+ inBuffer.put(b, off, remaining);
+ off += remaining;
+ len -= remaining;
+ encrypt();
+ }
+ }
+ }
+
+ /**
+ * To flush, we need to encrypt the data in the buffer and write to the
+ * underlying stream, then do the flush.
+ */
+ @Override
+ public void flush() throws IOException {
+ checkStream();
+ encrypt();
+ output.flush();
+ super.flush();
+ }
+
+ @Override
+ public void close() throws IOException {
+ if (closed) {
+ return;
+ }
+
+ try {
+ encryptFinal();
+ output.close();
+ freeBuffers();
+ cipher.close();
+ super.close();
+ } finally {
+ closed = true;
+ }
+ }
+
+ @Override
+ public boolean isOpen() {
+ return !closed;
+ }
+
+ @Override
+ public int write(ByteBuffer src) throws IOException {
+ checkStream();
+ final int len = src.remaining();
+ int remaining = len;
+ while (remaining > 0) {
+ final int space = inBuffer.remaining();
+ if (remaining < space) {
+ inBuffer.put(src);
+ remaining = 0;
+ } else {
+ // to void copy twice, we set the limit to copy directly
+ final int oldLimit = src.limit();
+ final int newLimit = src.position() + space;
+ src.limit(newLimit);
+
+ inBuffer.put(src);
+
+ // restore the old limit
+ src.limit(oldLimit);
+
+ remaining -= space;
+ encrypt();
+ }
+ }
+
+ return len;
+ }
+
+ /** Initialize the cipher. */
+ protected void initCipher()
+ throws IOException {
+ try {
+ cipher.init(Cipher.ENCRYPT_MODE, key, iv);
+ } catch (InvalidKeyException e) {
+ throw new IOException(e);
+ } catch(InvalidAlgorithmParameterException e) {
+ throw new IOException(e);
+ }
+ }
+
+ /**
+ * Do the encryption, input is {@link #inBuffer} and output is
+ * {@link #outBuffer}.
+ */
+ protected void encrypt() throws IOException {
+
+ inBuffer.flip();
+ outBuffer.clear();
+
+ try {
+ cipher.update(inBuffer, outBuffer);
+ } catch (ShortBufferException e) {
+ throw new IOException(e);
+ }
+
+ inBuffer.clear();
+ outBuffer.flip();
+
+ // write to output
+ output.write(outBuffer);
+ }
+
+ /**
+ * Do final encryption of the last data
+ */
+ protected void encryptFinal() throws IOException {
+ inBuffer.flip();
+ outBuffer.clear();
+
+ try {
+ cipher.doFinal(inBuffer, outBuffer);
+ } catch (ShortBufferException e) {
+ throw new IOException(e);
+ } catch (IllegalBlockSizeException e) {
+ throw new IOException(e);
+ } catch( BadPaddingException e) {
+ throw new IOException(e);
+ }
+
+ inBuffer.clear();
+ outBuffer.flip();
+
+ // write to output
+ output.write(outBuffer);
+ }
+
+ 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);
+ }
+}
http://git-wip-us.apache.org/repos/asf/commons-crypto/blob/ad81d236/src/main/java/org/apache/commons/crypto/stream/CryptoInputStream.java
----------------------------------------------------------------------
diff --git a/src/main/java/org/apache/commons/crypto/stream/CryptoInputStream.java b/src/main/java/org/apache/commons/crypto/stream/CryptoInputStream.java
deleted file mode 100644
index 6498273..0000000
--- a/src/main/java/org/apache/commons/crypto/stream/CryptoInputStream.java
+++ /dev/null
@@ -1,394 +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 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 CryptoInputStream extends InputStream implements
- ReadableByteChannel {
- private final byte[] oneByteBuf = new byte[1];
-
- protected final Cipher cipher;
- protected final int bufferSize;
-
- protected final byte[] key;
- protected final byte[] initIV;
- protected byte[] iv;
-
- protected boolean closed;
- protected boolean finalDone = false;
-
- protected 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;
-
- public CryptoInputStream(CipherTransformation transformation,
- Properties props, InputStream in, byte[] key, byte[] iv)
- throws IOException {
- this(in, Utils.getCipherInstance(transformation, props),
- Utils.getBufferSize(props), key, iv);
- }
-
- public CryptoInputStream(CipherTransformation transformation,
- Properties props, ReadableByteChannel in, byte[] key, byte[] iv)
- throws IOException {
- this(in, Utils.getCipherInstance(transformation, props),
- Utils.getBufferSize(props), key, iv);
- }
-
- public CryptoInputStream(InputStream in, Cipher cipher, int bufferSize,
- byte[] key, byte[] iv) throws IOException {
- this(new StreamInput(in, bufferSize), cipher, bufferSize, key, iv);
- }
-
- public CryptoInputStream(ReadableByteChannel in, Cipher cipher,
- int bufferSize, byte[] key, byte[] iv) throws IOException {
- this(new ChannelInput(in), cipher, bufferSize, key, iv);
- }
-
- public CryptoInputStream(
- Input input,
- Cipher cipher,
- int bufferSize,
- byte[] key,
- byte[] iv) throws IOException {
- this.input = input;
- this.cipher = cipher;
- this.bufferSize = Utils.checkBufferSize(cipher, bufferSize);
- this.key = key.clone();
- this.initIV = iv.clone();
- this.iv = iv.clone();
-
- inBuffer = ByteBuffer.allocateDirect(this.bufferSize);
- outBuffer = ByteBuffer.allocateDirect(this.bufferSize +
- cipher.getTransformation().getAlgorithmBlockSize());
- outBuffer.limit(0);
-
- initCipher();
- }
-
- @Override
- public int read() throws IOException {
- int n;
- while ((n = read(oneByteBuf, 0, 1)) == 0) ;
- return (n == -1) ? -1 : oneByteBuf[0] & 0xff;
- }
-
- /**
- * 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
- */
- @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;
- }
- }
-
- @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;
- }
-
- @Override
- public int available() throws IOException {
- checkStream();
-
- return input.available() + outBuffer.remaining();
- }
-
- @Override
- public void close() throws IOException {
- if (closed) {
- return;
- }
-
- input.close();
- freeBuffers();
- cipher.close();
- super.close();
- closed = true;
- }
-
- @Override
- public void mark(int readlimit) {
- }
-
- @Override
- public void reset() throws IOException {
- throw new IOException("Mark/reset not supported");
- }
-
- @Override
- public boolean markSupported() {
- return false;
- }
-
- @Override
- public boolean isOpen() {
- return !closed;
- }
-
- @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;
- }
- }
-
- /**
- * Get the buffer size
- */
- protected int getBufferSize() {
- return bufferSize;
- }
-
- /**
- * Get the key
- */
- protected byte[] getKey() {
- return key;
- }
-
- /**
- * Get the initialization vector
- */
- protected byte[] getInitIV() {
- return initIV;
- }
-
- /**
- * Get the internal Cipher
- */
- protected Cipher getCipher() {
- return cipher;
- }
-
- /** Initialize the cipher. */
- protected void initCipher()
- throws IOException {
- try {
- cipher.init(Cipher.DECRYPT_MODE, key, iv);
- } catch (InvalidKeyException e) {
- throw new IOException(e);
- } catch(InvalidAlgorithmParameterException e) {
- throw new IOException(e);
- }
- }
-
- /**
- * Decrypt 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();
- }
- }
-
- /**
- * Do 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();
- */
- 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();
- }
-
- /**
- * Do final of the cipher to end the decrypting stream
- */
- 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();
- }
-
- 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);
- }
-}
\ No newline at end of file