| /** |
| * 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 com.intel.chimera; |
| |
| import java.io.ByteArrayInputStream; |
| import java.io.ByteArrayOutputStream; |
| 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 com.intel.chimera.cipher.Cipher; |
| import com.intel.chimera.cipher.CipherTransformation; |
| import com.intel.chimera.cipher.JceCipher; |
| import com.intel.chimera.cipher.OpensslCipher; |
| import com.intel.chimera.stream.CryptoInputStream; |
| import com.intel.chimera.stream.CryptoOutputStream; |
| import com.intel.chimera.utils.ReflectionUtils; |
| import org.junit.Assert; |
| import org.junit.Before; |
| import org.junit.Test; |
| |
| public class CryptoStreamTest { |
| private final int dataLen = 20000; |
| private byte[] data = new byte[dataLen]; |
| private byte[] encData; |
| private Properties props; |
| private byte[] key = new byte[16]; |
| private byte[] iv = new byte[16]; |
| |
| private ByteArrayOutputStream baos = new ByteArrayOutputStream(); |
| |
| protected static int defaultBufferSize = 8192; |
| protected static int smallBufferSize = 1024; |
| |
| private final String jceCipherClass = JceCipher.class.getName(); |
| private final String opensslCipherClass = OpensslCipher.class.getName(); |
| private final CipherTransformation transformation = CipherTransformation |
| .AES_CTR_NOPADDING; |
| |
| @Before |
| public void setUp() throws IOException { |
| Random random = new SecureRandom(); |
| random.nextBytes(data); |
| random.nextBytes(key); |
| random.nextBytes(iv); |
| props = new Properties(); |
| } |
| |
| /** Test skip. */ |
| @Test(timeout=120000) |
| public void testSkip() throws Exception { |
| prepareData(); |
| |
| 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 { |
| prepareData(); |
| |
| doByteBufferRead(jceCipherClass, false); |
| doByteBufferRead(opensslCipherClass, false); |
| |
| doByteBufferRead(jceCipherClass, true); |
| doByteBufferRead(opensslCipherClass, true); |
| } |
| |
| /** Test byte buffer write. */ |
| @Test(timeout=120000) |
| public void testByteBufferWrite() throws Exception { |
| doByteBufferWrite(jceCipherClass, false); |
| doByteBufferWrite(opensslCipherClass, false); |
| |
| doByteBufferWrite(jceCipherClass, true); |
| doByteBufferWrite(opensslCipherClass, true); |
| } |
| |
| private void doSkipTest(String cipherClass, boolean withChannel) throws IOException { |
| InputStream in = getCryptoInputStream(cipherClass, defaultBufferSize, 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(cipherClass, defaultBufferSize, withChannel); |
| ByteBuffer buf = ByteBuffer.allocate(dataLen + 100); |
| byteBufferReadCheck(in, buf, 0); |
| in.close(); |
| |
| // Default buffer size, initial buffer position is not 0 |
| in = getCryptoInputStream(cipherClass, defaultBufferSize, withChannel); |
| buf.clear(); |
| byteBufferReadCheck(in, buf, 11); |
| in.close(); |
| |
| // Small buffer size, initial buffer position is 0 |
| in = getCryptoInputStream(cipherClass, smallBufferSize, withChannel); |
| buf.clear(); |
| byteBufferReadCheck(in, buf, 0); |
| in.close(); |
| |
| // Small buffer size, initial buffer position is not 0 |
| in = getCryptoInputStream(cipherClass, smallBufferSize, withChannel); |
| buf.clear(); |
| byteBufferReadCheck(in, buf, 11); |
| in.close(); |
| |
| // Direct buffer, default buffer size, initial buffer position is 0 |
| in = getCryptoInputStream(cipherClass, defaultBufferSize, 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(cipherClass, defaultBufferSize, withChannel); |
| buf.clear(); |
| byteBufferReadCheck(in, buf, 11); |
| in.close(); |
| |
| // Direct buffer, small buffer size, initial buffer position is 0 |
| in = getCryptoInputStream(cipherClass, smallBufferSize, withChannel); |
| buf.clear(); |
| byteBufferReadCheck(in, buf, 0); |
| in.close(); |
| |
| // Direct buffer, small buffer size, initial buffer position is not 0 |
| in = getCryptoInputStream(cipherClass, smallBufferSize, withChannel); |
| buf.clear(); |
| byteBufferReadCheck(in, buf, 11); |
| in.close(); |
| } |
| |
| private void doByteBufferWrite(String cipherClass, boolean withChannel) throws Exception { |
| CryptoOutputStream out = getCryptoOutputStream(cipherClass, defaultBufferSize, 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(); |
| encData = baos.toByteArray(); |
| |
| InputStream in = getCryptoInputStream(cipherClass, defaultBufferSize, 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(); |
| encData = baos.toByteArray(); |
| } |
| |
| private CryptoInputStream getCryptoInputStream(String cipherClass, int bufferSize, boolean withChannel) |
| throws IOException { |
| Cipher cipher = null; |
| try { |
| cipher = (Cipher)ReflectionUtils.newInstance( |
| ReflectionUtils.getClassByName(cipherClass), props, transformation); |
| } catch (ClassNotFoundException cnfe) { |
| throw new IOException("Illegal crypto cipher!"); |
| } |
| |
| if (withChannel) { |
| return new CryptoInputStream(Channels.newChannel(new ByteArrayInputStream(encData)), cipher, bufferSize, key, iv); |
| } else { |
| return new CryptoInputStream(new ByteArrayInputStream(encData), cipher, bufferSize, key, iv); |
| } |
| } |
| |
| private CryptoOutputStream getCryptoOutputStream(String cipherClass, int bufferSize, boolean withChannel) |
| throws IOException { |
| Cipher cipher = null; |
| try { |
| cipher = (Cipher)ReflectionUtils.newInstance( |
| ReflectionUtils.getClassByName(cipherClass), props, transformation); |
| } catch (ClassNotFoundException cnfe) { |
| throw new IOException("Illegal crypto cipher!"); |
| } |
| |
| baos.reset(); |
| 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 off, int len) |
| throws IOException { |
| int n = 0; |
| int total = 0; |
| while (n != -1) { |
| total += n; |
| if (total >= len) { |
| break; |
| } |
| n = in.read(b, off + total, len - total); |
| } |
| |
| return total; |
| } |
| } |