180b486724ca19b3c1c3c36334d06856330362f83Jesse Wilson/* 280b486724ca19b3c1c3c36334d06856330362f83Jesse Wilson * Copyright (C) 2010 The Android Open Source Project 380b486724ca19b3c1c3c36334d06856330362f83Jesse Wilson * 480b486724ca19b3c1c3c36334d06856330362f83Jesse Wilson * Licensed under the Apache License, Version 2.0 (the "License"); 580b486724ca19b3c1c3c36334d06856330362f83Jesse Wilson * you may not use this file except in compliance with the License. 680b486724ca19b3c1c3c36334d06856330362f83Jesse Wilson * You may obtain a copy of the License at 780b486724ca19b3c1c3c36334d06856330362f83Jesse Wilson * 880b486724ca19b3c1c3c36334d06856330362f83Jesse Wilson * http://www.apache.org/licenses/LICENSE-2.0 980b486724ca19b3c1c3c36334d06856330362f83Jesse Wilson * 1080b486724ca19b3c1c3c36334d06856330362f83Jesse Wilson * Unless required by applicable law or agreed to in writing, software 1180b486724ca19b3c1c3c36334d06856330362f83Jesse Wilson * distributed under the License is distributed on an "AS IS" BASIS, 1280b486724ca19b3c1c3c36334d06856330362f83Jesse Wilson * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 1380b486724ca19b3c1c3c36334d06856330362f83Jesse Wilson * See the License for the specific language governing permissions and 1480b486724ca19b3c1c3c36334d06856330362f83Jesse Wilson * limitations under the License. 1580b486724ca19b3c1c3c36334d06856330362f83Jesse Wilson */ 1680b486724ca19b3c1c3c36334d06856330362f83Jesse Wilson 1780b486724ca19b3c1c3c36334d06856330362f83Jesse Wilsonpackage libcore.javax.crypto; 1880b486724ca19b3c1c3c36334d06856330362f83Jesse Wilson 1980b486724ca19b3c1c3c36334d06856330362f83Jesse Wilsonimport java.io.ByteArrayInputStream; 2080b486724ca19b3c1c3c36334d06856330362f83Jesse Wilsonimport java.io.ByteArrayOutputStream; 2129d763959dd79e8aeacf2674c074d708d596477aKenny Rootimport java.io.FilterInputStream; 2280b486724ca19b3c1c3c36334d06856330362f83Jesse Wilsonimport java.io.IOException; 2380b486724ca19b3c1c3c36334d06856330362f83Jesse Wilsonimport java.io.InputStream; 24be43e6b8fa5623790492f317a056d738c5306232Kenny Rootimport java.security.spec.AlgorithmParameterSpec; 2580b486724ca19b3c1c3c36334d06856330362f83Jesse Wilsonimport java.util.Arrays; 2680b486724ca19b3c1c3c36334d06856330362f83Jesse Wilsonimport javax.crypto.Cipher; 2780b486724ca19b3c1c3c36334d06856330362f83Jesse Wilsonimport javax.crypto.CipherInputStream; 2880b486724ca19b3c1c3c36334d06856330362f83Jesse Wilsonimport javax.crypto.SecretKey; 299a4eb515a0b50c9ff5c57127a0ddcbd07dbcc312Kenny Rootimport javax.crypto.spec.IvParameterSpec; 3080b486724ca19b3c1c3c36334d06856330362f83Jesse Wilsonimport javax.crypto.spec.SecretKeySpec; 3180b486724ca19b3c1c3c36334d06856330362f83Jesse Wilsonimport junit.framework.TestCase; 3280b486724ca19b3c1c3c36334d06856330362f83Jesse Wilson 3380b486724ca19b3c1c3c36334d06856330362f83Jesse Wilsonpublic final class CipherInputStreamTest extends TestCase { 3480b486724ca19b3c1c3c36334d06856330362f83Jesse Wilson 35be43e6b8fa5623790492f317a056d738c5306232Kenny Root private final byte[] aesKeyBytes = { 36be43e6b8fa5623790492f317a056d738c5306232Kenny Root (byte) 0x50, (byte) 0x98, (byte) 0xF2, (byte) 0xC3, (byte) 0x85, (byte) 0x23, 37be43e6b8fa5623790492f317a056d738c5306232Kenny Root (byte) 0xA3, (byte) 0x33, (byte) 0x50, (byte) 0x98, (byte) 0xF2, (byte) 0xC3, 38be43e6b8fa5623790492f317a056d738c5306232Kenny Root (byte) 0x85, (byte) 0x23, (byte) 0xA3, (byte) 0x33, 39be43e6b8fa5623790492f317a056d738c5306232Kenny Root }; 40be43e6b8fa5623790492f317a056d738c5306232Kenny Root 41be43e6b8fa5623790492f317a056d738c5306232Kenny Root private final byte[] aesIvBytes = { 42be43e6b8fa5623790492f317a056d738c5306232Kenny Root (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, 43be43e6b8fa5623790492f317a056d738c5306232Kenny Root (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, 44be43e6b8fa5623790492f317a056d738c5306232Kenny Root (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, 45be43e6b8fa5623790492f317a056d738c5306232Kenny Root }; 46be43e6b8fa5623790492f317a056d738c5306232Kenny Root 47be43e6b8fa5623790492f317a056d738c5306232Kenny Root private final byte[] aesCipherText = { 48be43e6b8fa5623790492f317a056d738c5306232Kenny Root (byte) 0x2F, (byte) 0x2C, (byte) 0x74, (byte) 0x31, (byte) 0xFF, (byte) 0xCC, 49be43e6b8fa5623790492f317a056d738c5306232Kenny Root (byte) 0x28, (byte) 0x7D, (byte) 0x59, (byte) 0xBD, (byte) 0xE5, (byte) 0x0A, 50be43e6b8fa5623790492f317a056d738c5306232Kenny Root (byte) 0x30, (byte) 0x7E, (byte) 0x6A, (byte) 0x4A 51be43e6b8fa5623790492f317a056d738c5306232Kenny Root }; 52be43e6b8fa5623790492f317a056d738c5306232Kenny Root 5329d763959dd79e8aeacf2674c074d708d596477aKenny Root private final byte[] rc4CipherText = { 5429d763959dd79e8aeacf2674c074d708d596477aKenny Root (byte) 0x88, (byte) 0x01, (byte) 0xE3, (byte) 0x52, (byte) 0x7B 5529d763959dd79e8aeacf2674c074d708d596477aKenny Root }; 5629d763959dd79e8aeacf2674c074d708d596477aKenny Root 5780b486724ca19b3c1c3c36334d06856330362f83Jesse Wilson private final String plainText = "abcde"; 5880b486724ca19b3c1c3c36334d06856330362f83Jesse Wilson private SecretKey key; 5929d763959dd79e8aeacf2674c074d708d596477aKenny Root private SecretKey rc4Key; 60be43e6b8fa5623790492f317a056d738c5306232Kenny Root private AlgorithmParameterSpec iv; 6180b486724ca19b3c1c3c36334d06856330362f83Jesse Wilson 6280b486724ca19b3c1c3c36334d06856330362f83Jesse Wilson @Override protected void setUp() throws Exception { 63be43e6b8fa5623790492f317a056d738c5306232Kenny Root key = new SecretKeySpec(aesKeyBytes, "AES"); 6429d763959dd79e8aeacf2674c074d708d596477aKenny Root rc4Key = new SecretKeySpec(aesKeyBytes, "RC4"); 65be43e6b8fa5623790492f317a056d738c5306232Kenny Root iv = new IvParameterSpec(aesIvBytes); 6680b486724ca19b3c1c3c36334d06856330362f83Jesse Wilson } 6780b486724ca19b3c1c3c36334d06856330362f83Jesse Wilson 6829d763959dd79e8aeacf2674c074d708d596477aKenny Root private static class MeasuringInputStream extends FilterInputStream { 6929d763959dd79e8aeacf2674c074d708d596477aKenny Root private int totalRead; 7029d763959dd79e8aeacf2674c074d708d596477aKenny Root 7129d763959dd79e8aeacf2674c074d708d596477aKenny Root protected MeasuringInputStream(InputStream in) { 7229d763959dd79e8aeacf2674c074d708d596477aKenny Root super(in); 7329d763959dd79e8aeacf2674c074d708d596477aKenny Root } 7429d763959dd79e8aeacf2674c074d708d596477aKenny Root 7529d763959dd79e8aeacf2674c074d708d596477aKenny Root @Override 7629d763959dd79e8aeacf2674c074d708d596477aKenny Root public int read() throws IOException { 7729d763959dd79e8aeacf2674c074d708d596477aKenny Root int c = super.read(); 7829d763959dd79e8aeacf2674c074d708d596477aKenny Root totalRead++; 7929d763959dd79e8aeacf2674c074d708d596477aKenny Root return c; 8029d763959dd79e8aeacf2674c074d708d596477aKenny Root } 8129d763959dd79e8aeacf2674c074d708d596477aKenny Root 8229d763959dd79e8aeacf2674c074d708d596477aKenny Root @Override 8329d763959dd79e8aeacf2674c074d708d596477aKenny Root public int read(byte[] buffer, int byteOffset, int byteCount) throws IOException { 8429d763959dd79e8aeacf2674c074d708d596477aKenny Root int numRead = super.read(buffer, byteOffset, byteCount); 8529d763959dd79e8aeacf2674c074d708d596477aKenny Root if (numRead != -1) { 8629d763959dd79e8aeacf2674c074d708d596477aKenny Root totalRead += numRead; 8729d763959dd79e8aeacf2674c074d708d596477aKenny Root } 8829d763959dd79e8aeacf2674c074d708d596477aKenny Root return numRead; 8929d763959dd79e8aeacf2674c074d708d596477aKenny Root } 9029d763959dd79e8aeacf2674c074d708d596477aKenny Root 9129d763959dd79e8aeacf2674c074d708d596477aKenny Root public int getTotalRead() { 9229d763959dd79e8aeacf2674c074d708d596477aKenny Root return totalRead; 9329d763959dd79e8aeacf2674c074d708d596477aKenny Root } 9429d763959dd79e8aeacf2674c074d708d596477aKenny Root } 9529d763959dd79e8aeacf2674c074d708d596477aKenny Root 9629d763959dd79e8aeacf2674c074d708d596477aKenny Root public void testAvailable() throws Exception { 9729d763959dd79e8aeacf2674c074d708d596477aKenny Root Cipher cipher = Cipher.getInstance("AES/CBC/PKCS5Padding"); 9829d763959dd79e8aeacf2674c074d708d596477aKenny Root cipher.init(Cipher.DECRYPT_MODE, key, iv); 9929d763959dd79e8aeacf2674c074d708d596477aKenny Root MeasuringInputStream in = new MeasuringInputStream(new ByteArrayInputStream(aesCipherText)); 10029d763959dd79e8aeacf2674c074d708d596477aKenny Root InputStream cin = new CipherInputStream(in, cipher); 10129d763959dd79e8aeacf2674c074d708d596477aKenny Root assertTrue(cin.read() != -1); 10229d763959dd79e8aeacf2674c074d708d596477aKenny Root assertEquals(aesCipherText.length, in.getTotalRead()); 10329d763959dd79e8aeacf2674c074d708d596477aKenny Root } 10429d763959dd79e8aeacf2674c074d708d596477aKenny Root 10529d763959dd79e8aeacf2674c074d708d596477aKenny Root public void testDecrypt_NullInput_Discarded() throws Exception { 10629d763959dd79e8aeacf2674c074d708d596477aKenny Root Cipher cipher = Cipher.getInstance("AES/CBC/PKCS5Padding"); 10729d763959dd79e8aeacf2674c074d708d596477aKenny Root cipher.init(Cipher.DECRYPT_MODE, key, iv); 10829d763959dd79e8aeacf2674c074d708d596477aKenny Root InputStream in = new CipherInputStream(new ByteArrayInputStream(aesCipherText), cipher); 10929d763959dd79e8aeacf2674c074d708d596477aKenny Root int discard = 3; 11029d763959dd79e8aeacf2674c074d708d596477aKenny Root while (discard != 0) { 11129d763959dd79e8aeacf2674c074d708d596477aKenny Root discard -= in.read(null, 0, discard); 11229d763959dd79e8aeacf2674c074d708d596477aKenny Root } 11329d763959dd79e8aeacf2674c074d708d596477aKenny Root byte[] bytes = readAll(in); 11429d763959dd79e8aeacf2674c074d708d596477aKenny Root assertEquals(Arrays.toString(plainText.substring(3).getBytes("UTF-8")), 11529d763959dd79e8aeacf2674c074d708d596477aKenny Root Arrays.toString(bytes)); 11629d763959dd79e8aeacf2674c074d708d596477aKenny Root } 11729d763959dd79e8aeacf2674c074d708d596477aKenny Root 11880b486724ca19b3c1c3c36334d06856330362f83Jesse Wilson public void testEncrypt() throws Exception { 119be43e6b8fa5623790492f317a056d738c5306232Kenny Root Cipher cipher = Cipher.getInstance("AES/CBC/PKCS5Padding"); 120be43e6b8fa5623790492f317a056d738c5306232Kenny Root cipher.init(Cipher.ENCRYPT_MODE, key, iv); 12180b486724ca19b3c1c3c36334d06856330362f83Jesse Wilson InputStream in = new CipherInputStream( 12280b486724ca19b3c1c3c36334d06856330362f83Jesse Wilson new ByteArrayInputStream(plainText.getBytes("UTF-8")), cipher); 12380b486724ca19b3c1c3c36334d06856330362f83Jesse Wilson byte[] bytes = readAll(in); 124be43e6b8fa5623790492f317a056d738c5306232Kenny Root assertEquals(Arrays.toString(aesCipherText), Arrays.toString(bytes)); 12529d763959dd79e8aeacf2674c074d708d596477aKenny Root 12629d763959dd79e8aeacf2674c074d708d596477aKenny Root // Reading again shouldn't throw an exception. 12729d763959dd79e8aeacf2674c074d708d596477aKenny Root assertEquals(-1, in.read()); 12829d763959dd79e8aeacf2674c074d708d596477aKenny Root } 12929d763959dd79e8aeacf2674c074d708d596477aKenny Root 13029d763959dd79e8aeacf2674c074d708d596477aKenny Root public void testEncrypt_RC4() throws Exception { 13129d763959dd79e8aeacf2674c074d708d596477aKenny Root Cipher cipher = Cipher.getInstance("RC4"); 13229d763959dd79e8aeacf2674c074d708d596477aKenny Root cipher.init(Cipher.ENCRYPT_MODE, rc4Key); 13329d763959dd79e8aeacf2674c074d708d596477aKenny Root InputStream in = new CipherInputStream( 13429d763959dd79e8aeacf2674c074d708d596477aKenny Root new ByteArrayInputStream(plainText.getBytes("UTF-8")), cipher); 13529d763959dd79e8aeacf2674c074d708d596477aKenny Root byte[] bytes = readAll(in); 13629d763959dd79e8aeacf2674c074d708d596477aKenny Root assertEquals(Arrays.toString(rc4CipherText), Arrays.toString(bytes)); 13729d763959dd79e8aeacf2674c074d708d596477aKenny Root 13829d763959dd79e8aeacf2674c074d708d596477aKenny Root // Reading again shouldn't throw an exception. 13929d763959dd79e8aeacf2674c074d708d596477aKenny Root assertEquals(-1, in.read()); 14080b486724ca19b3c1c3c36334d06856330362f83Jesse Wilson } 14180b486724ca19b3c1c3c36334d06856330362f83Jesse Wilson 14280b486724ca19b3c1c3c36334d06856330362f83Jesse Wilson public void testDecrypt() throws Exception { 143be43e6b8fa5623790492f317a056d738c5306232Kenny Root Cipher cipher = Cipher.getInstance("AES/CBC/PKCS5Padding"); 144be43e6b8fa5623790492f317a056d738c5306232Kenny Root cipher.init(Cipher.DECRYPT_MODE, key, iv); 145be43e6b8fa5623790492f317a056d738c5306232Kenny Root InputStream in = new CipherInputStream(new ByteArrayInputStream(aesCipherText), cipher); 14680b486724ca19b3c1c3c36334d06856330362f83Jesse Wilson byte[] bytes = readAll(in); 147be43e6b8fa5623790492f317a056d738c5306232Kenny Root assertEquals(Arrays.toString(plainText.getBytes("UTF-8")), Arrays.toString(bytes)); 14880b486724ca19b3c1c3c36334d06856330362f83Jesse Wilson } 14980b486724ca19b3c1c3c36334d06856330362f83Jesse Wilson 15029d763959dd79e8aeacf2674c074d708d596477aKenny Root public void testDecrypt_RC4() throws Exception { 15129d763959dd79e8aeacf2674c074d708d596477aKenny Root Cipher cipher = Cipher.getInstance("RC4"); 15229d763959dd79e8aeacf2674c074d708d596477aKenny Root cipher.init(Cipher.DECRYPT_MODE, rc4Key); 15329d763959dd79e8aeacf2674c074d708d596477aKenny Root InputStream in = new CipherInputStream(new ByteArrayInputStream(rc4CipherText), cipher); 15429d763959dd79e8aeacf2674c074d708d596477aKenny Root byte[] bytes = readAll(in); 15529d763959dd79e8aeacf2674c074d708d596477aKenny Root assertEquals(Arrays.toString(plainText.getBytes("UTF-8")), Arrays.toString(bytes)); 15629d763959dd79e8aeacf2674c074d708d596477aKenny Root } 15729d763959dd79e8aeacf2674c074d708d596477aKenny Root 15880b486724ca19b3c1c3c36334d06856330362f83Jesse Wilson public void testSkip() throws Exception { 159be43e6b8fa5623790492f317a056d738c5306232Kenny Root Cipher cipher = Cipher.getInstance("AES/CBC/PKCS5Padding"); 160be43e6b8fa5623790492f317a056d738c5306232Kenny Root cipher.init(Cipher.DECRYPT_MODE, key, iv); 161be43e6b8fa5623790492f317a056d738c5306232Kenny Root InputStream in = new CipherInputStream(new ByteArrayInputStream(aesCipherText), cipher); 162be43e6b8fa5623790492f317a056d738c5306232Kenny Root assertTrue(in.skip(5) >= 0); 16380b486724ca19b3c1c3c36334d06856330362f83Jesse Wilson } 16480b486724ca19b3c1c3c36334d06856330362f83Jesse Wilson 16580b486724ca19b3c1c3c36334d06856330362f83Jesse Wilson private byte[] readAll(InputStream in) throws IOException { 16680b486724ca19b3c1c3c36334d06856330362f83Jesse Wilson ByteArrayOutputStream out = new ByteArrayOutputStream(); 16780b486724ca19b3c1c3c36334d06856330362f83Jesse Wilson int count; 16880b486724ca19b3c1c3c36334d06856330362f83Jesse Wilson byte[] buffer = new byte[1024]; 16980b486724ca19b3c1c3c36334d06856330362f83Jesse Wilson while ((count = in.read(buffer)) != -1) { 17080b486724ca19b3c1c3c36334d06856330362f83Jesse Wilson out.write(buffer, 0, count); 17180b486724ca19b3c1c3c36334d06856330362f83Jesse Wilson } 17280b486724ca19b3c1c3c36334d06856330362f83Jesse Wilson return out.toByteArray(); 17380b486724ca19b3c1c3c36334d06856330362f83Jesse Wilson } 1749a4eb515a0b50c9ff5c57127a0ddcbd07dbcc312Kenny Root 1759a4eb515a0b50c9ff5c57127a0ddcbd07dbcc312Kenny Root public void testCipherInputStream_TruncatedInput_Failure() throws Exception { 1769a4eb515a0b50c9ff5c57127a0ddcbd07dbcc312Kenny Root Cipher cipher = Cipher.getInstance("AES/CBC/PKCS5Padding"); 177be43e6b8fa5623790492f317a056d738c5306232Kenny Root cipher.init(Cipher.DECRYPT_MODE, key, iv); 1789a4eb515a0b50c9ff5c57127a0ddcbd07dbcc312Kenny Root InputStream is = new CipherInputStream(new ByteArrayInputStream(new byte[31]), cipher); 1799a4eb515a0b50c9ff5c57127a0ddcbd07dbcc312Kenny Root is.read(new byte[4]); 1809a4eb515a0b50c9ff5c57127a0ddcbd07dbcc312Kenny Root is.close(); 1819a4eb515a0b50c9ff5c57127a0ddcbd07dbcc312Kenny Root } 18229d763959dd79e8aeacf2674c074d708d596477aKenny Root 18329d763959dd79e8aeacf2674c074d708d596477aKenny Root public void testCipherInputStream_NullInputStream_Failure() throws Exception { 18429d763959dd79e8aeacf2674c074d708d596477aKenny Root Cipher cipher = Cipher.getInstance("AES/CBC/PKCS5Padding"); 18529d763959dd79e8aeacf2674c074d708d596477aKenny Root cipher.init(Cipher.DECRYPT_MODE, key, iv); 18629d763959dd79e8aeacf2674c074d708d596477aKenny Root InputStream is = new CipherInputStream(null, cipher); 18729d763959dd79e8aeacf2674c074d708d596477aKenny Root try { 18829d763959dd79e8aeacf2674c074d708d596477aKenny Root is.read(); 18929d763959dd79e8aeacf2674c074d708d596477aKenny Root fail("Expected NullPointerException"); 19029d763959dd79e8aeacf2674c074d708d596477aKenny Root } catch (NullPointerException expected) { 19129d763959dd79e8aeacf2674c074d708d596477aKenny Root } 19229d763959dd79e8aeacf2674c074d708d596477aKenny Root 19329d763959dd79e8aeacf2674c074d708d596477aKenny Root byte[] buffer = new byte[128]; 19429d763959dd79e8aeacf2674c074d708d596477aKenny Root try { 19529d763959dd79e8aeacf2674c074d708d596477aKenny Root is.read(buffer); 19629d763959dd79e8aeacf2674c074d708d596477aKenny Root fail("Expected NullPointerException"); 19729d763959dd79e8aeacf2674c074d708d596477aKenny Root } catch (NullPointerException expected) { 19829d763959dd79e8aeacf2674c074d708d596477aKenny Root } 19929d763959dd79e8aeacf2674c074d708d596477aKenny Root 20029d763959dd79e8aeacf2674c074d708d596477aKenny Root try { 20129d763959dd79e8aeacf2674c074d708d596477aKenny Root is.read(buffer, 0, buffer.length); 20229d763959dd79e8aeacf2674c074d708d596477aKenny Root fail("Expected NullPointerException"); 20329d763959dd79e8aeacf2674c074d708d596477aKenny Root } catch (NullPointerException expected) { 20429d763959dd79e8aeacf2674c074d708d596477aKenny Root } 20529d763959dd79e8aeacf2674c074d708d596477aKenny Root } 20680b486724ca19b3c1c3c36334d06856330362f83Jesse Wilson} 207