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