1 /* 2 * Copyright (C) 2010 The Android Open Source Project 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); 5 * you may not use this file except in compliance with the License. 6 * You may obtain a copy of the License at 7 * 8 * http://www.apache.org/licenses/LICENSE-2.0 9 * 10 * Unless required by applicable law or agreed to in writing, software 11 * distributed under the License is distributed on an "AS IS" BASIS, 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 * See the License for the specific language governing permissions and 14 * limitations under the License. 15 */ 16 17 package libcore.javax.crypto; 18 19 import java.io.ByteArrayInputStream; 20 import java.io.ByteArrayOutputStream; 21 import java.io.FilterInputStream; 22 import java.io.IOException; 23 import java.io.InputStream; 24 import java.security.spec.AlgorithmParameterSpec; 25 import java.util.Arrays; 26 import javax.crypto.Cipher; 27 import javax.crypto.CipherInputStream; 28 import javax.crypto.SecretKey; 29 import javax.crypto.spec.IvParameterSpec; 30 import javax.crypto.spec.SecretKeySpec; 31 import junit.framework.TestCase; 32 33 public final class CipherInputStreamTest extends TestCase { 34 35 private final byte[] aesKeyBytes = { 36 (byte) 0x50, (byte) 0x98, (byte) 0xF2, (byte) 0xC3, (byte) 0x85, (byte) 0x23, 37 (byte) 0xA3, (byte) 0x33, (byte) 0x50, (byte) 0x98, (byte) 0xF2, (byte) 0xC3, 38 (byte) 0x85, (byte) 0x23, (byte) 0xA3, (byte) 0x33, 39 }; 40 41 private final byte[] aesIvBytes = { 42 (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, 43 (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, 44 (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, 45 }; 46 47 private final byte[] aesCipherText = { 48 (byte) 0x2F, (byte) 0x2C, (byte) 0x74, (byte) 0x31, (byte) 0xFF, (byte) 0xCC, 49 (byte) 0x28, (byte) 0x7D, (byte) 0x59, (byte) 0xBD, (byte) 0xE5, (byte) 0x0A, 50 (byte) 0x30, (byte) 0x7E, (byte) 0x6A, (byte) 0x4A 51 }; 52 53 private final byte[] rc4CipherText = { 54 (byte) 0x88, (byte) 0x01, (byte) 0xE3, (byte) 0x52, (byte) 0x7B 55 }; 56 57 private final String plainText = "abcde"; 58 private SecretKey key; 59 private SecretKey rc4Key; 60 private AlgorithmParameterSpec iv; 61 setUp()62 @Override protected void setUp() throws Exception { 63 key = new SecretKeySpec(aesKeyBytes, "AES"); 64 rc4Key = new SecretKeySpec(aesKeyBytes, "RC4"); 65 iv = new IvParameterSpec(aesIvBytes); 66 } 67 68 private static class MeasuringInputStream extends FilterInputStream { 69 private int totalRead; 70 MeasuringInputStream(InputStream in)71 protected MeasuringInputStream(InputStream in) { 72 super(in); 73 } 74 75 @Override read()76 public int read() throws IOException { 77 int c = super.read(); 78 totalRead++; 79 return c; 80 } 81 82 @Override read(byte[] buffer, int byteOffset, int byteCount)83 public int read(byte[] buffer, int byteOffset, int byteCount) throws IOException { 84 int numRead = super.read(buffer, byteOffset, byteCount); 85 if (numRead != -1) { 86 totalRead += numRead; 87 } 88 return numRead; 89 } 90 getTotalRead()91 public int getTotalRead() { 92 return totalRead; 93 } 94 } 95 testAvailable()96 public void testAvailable() throws Exception { 97 Cipher cipher = Cipher.getInstance("AES/CBC/PKCS5Padding"); 98 cipher.init(Cipher.DECRYPT_MODE, key, iv); 99 MeasuringInputStream in = new MeasuringInputStream(new ByteArrayInputStream(aesCipherText)); 100 InputStream cin = new CipherInputStream(in, cipher); 101 assertTrue(cin.read() != -1); 102 assertEquals(aesCipherText.length, in.getTotalRead()); 103 } 104 testDecrypt_NullInput_Discarded()105 public void testDecrypt_NullInput_Discarded() throws Exception { 106 Cipher cipher = Cipher.getInstance("AES/CBC/PKCS5Padding"); 107 cipher.init(Cipher.DECRYPT_MODE, key, iv); 108 InputStream in = new CipherInputStream(new ByteArrayInputStream(aesCipherText), cipher); 109 int discard = 3; 110 while (discard != 0) { 111 discard -= in.read(null, 0, discard); 112 } 113 byte[] bytes = readAll(in); 114 assertEquals(Arrays.toString(plainText.substring(3).getBytes("UTF-8")), 115 Arrays.toString(bytes)); 116 } 117 testEncrypt()118 public void testEncrypt() throws Exception { 119 Cipher cipher = Cipher.getInstance("AES/CBC/PKCS5Padding"); 120 cipher.init(Cipher.ENCRYPT_MODE, key, iv); 121 InputStream in = new CipherInputStream( 122 new ByteArrayInputStream(plainText.getBytes("UTF-8")), cipher); 123 byte[] bytes = readAll(in); 124 assertEquals(Arrays.toString(aesCipherText), Arrays.toString(bytes)); 125 126 // Reading again shouldn't throw an exception. 127 assertEquals(-1, in.read()); 128 } 129 testEncrypt_RC4()130 public void testEncrypt_RC4() throws Exception { 131 Cipher cipher = Cipher.getInstance("RC4"); 132 cipher.init(Cipher.ENCRYPT_MODE, rc4Key); 133 InputStream in = new CipherInputStream( 134 new ByteArrayInputStream(plainText.getBytes("UTF-8")), cipher); 135 byte[] bytes = readAll(in); 136 assertEquals(Arrays.toString(rc4CipherText), Arrays.toString(bytes)); 137 138 // Reading again shouldn't throw an exception. 139 assertEquals(-1, in.read()); 140 } 141 testDecrypt()142 public void testDecrypt() throws Exception { 143 Cipher cipher = Cipher.getInstance("AES/CBC/PKCS5Padding"); 144 cipher.init(Cipher.DECRYPT_MODE, key, iv); 145 InputStream in = new CipherInputStream(new ByteArrayInputStream(aesCipherText), cipher); 146 byte[] bytes = readAll(in); 147 assertEquals(Arrays.toString(plainText.getBytes("UTF-8")), Arrays.toString(bytes)); 148 } 149 testDecrypt_RC4()150 public void testDecrypt_RC4() throws Exception { 151 Cipher cipher = Cipher.getInstance("RC4"); 152 cipher.init(Cipher.DECRYPT_MODE, rc4Key); 153 InputStream in = new CipherInputStream(new ByteArrayInputStream(rc4CipherText), cipher); 154 byte[] bytes = readAll(in); 155 assertEquals(Arrays.toString(plainText.getBytes("UTF-8")), Arrays.toString(bytes)); 156 } 157 testSkip()158 public void testSkip() throws Exception { 159 Cipher cipher = Cipher.getInstance("AES/CBC/PKCS5Padding"); 160 cipher.init(Cipher.DECRYPT_MODE, key, iv); 161 InputStream in = new CipherInputStream(new ByteArrayInputStream(aesCipherText), cipher); 162 assertTrue(in.skip(5) >= 0); 163 } 164 readAll(InputStream in)165 private byte[] readAll(InputStream in) throws IOException { 166 ByteArrayOutputStream out = new ByteArrayOutputStream(); 167 int count; 168 byte[] buffer = new byte[1024]; 169 while ((count = in.read(buffer)) != -1) { 170 out.write(buffer, 0, count); 171 } 172 return out.toByteArray(); 173 } 174 testCipherInputStream_TruncatedInput_Failure()175 public void testCipherInputStream_TruncatedInput_Failure() throws Exception { 176 Cipher cipher = Cipher.getInstance("AES/CBC/PKCS5Padding"); 177 cipher.init(Cipher.DECRYPT_MODE, key, iv); 178 InputStream is = new CipherInputStream(new ByteArrayInputStream(new byte[31]), cipher); 179 is.read(new byte[4]); 180 is.close(); 181 } 182 testCipherInputStream_NullInputStream_Failure()183 public void testCipherInputStream_NullInputStream_Failure() throws Exception { 184 Cipher cipher = Cipher.getInstance("AES/CBC/PKCS5Padding"); 185 cipher.init(Cipher.DECRYPT_MODE, key, iv); 186 InputStream is = new CipherInputStream(null, cipher); 187 try { 188 is.read(); 189 fail("Expected NullPointerException"); 190 } catch (NullPointerException expected) { 191 } 192 193 byte[] buffer = new byte[128]; 194 try { 195 is.read(buffer); 196 fail("Expected NullPointerException"); 197 } catch (NullPointerException expected) { 198 } 199 200 try { 201 is.read(buffer, 0, buffer.length); 202 fail("Expected NullPointerException"); 203 } catch (NullPointerException expected) { 204 } 205 } 206 } 207