1/*
2 * Licensed to the Apache Software Foundation (ASF) under one or more
3 * contributor license agreements. See the NOTICE file distributed with this
4 * work for additional information regarding copyright ownership. The ASF
5 * licenses this file to You under the Apache License, Version 2.0 (the
6 * "License"); you may not use this file except in compliance with the License.
7 * You may obtain a copy of the License at
8 *
9 * http://www.apache.org/licenses/LICENSE-2.0
10 *
11 * Unless required by applicable law or agreed to in writing, software
12 * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
13 * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
14 * License for the specific language governing permissions and limitations under
15 * the License.
16 */
17
18/**
19 * @author Alexander Y. Kleymenov
20 * @version $Revision$
21 */
22
23package org.apache.harmony.crypto.tests.javax.crypto;
24
25import junit.framework.TestCase;
26
27import java.io.ByteArrayInputStream;
28import java.io.ByteArrayOutputStream;
29import java.io.IOException;
30import java.io.ObjectInputStream;
31import java.io.ObjectOutputStream;
32import java.io.Serializable;
33import java.security.InvalidKeyException;
34import java.security.Key;
35import java.security.NoSuchProviderException;
36import java.util.ArrayList;
37import java.util.Arrays;
38
39import javax.crypto.Cipher;
40import javax.crypto.IllegalBlockSizeException;
41import javax.crypto.KeyGenerator;
42import javax.crypto.NullCipher;
43import javax.crypto.SealedObject;
44import javax.crypto.spec.IvParameterSpec;
45import javax.crypto.spec.SecretKeySpec;
46
47import libcore.util.SerializationTester;
48
49/**
50 */
51public class SealedObjectTest extends TestCase {
52    class Mock_SealedObject extends SealedObject {
53        public Mock_SealedObject(Serializable object, Cipher c)
54                throws IOException, IllegalBlockSizeException {
55            super(object, c);
56        }
57
58        public byte[] get_encodedParams() {
59            return super.encodedParams;
60        }
61
62    }
63
64    /**
65     * readObject(ObjectInputStream s) method testing. Tests if the
66     * serialization/deserialization works correctly: object is serialized,
67     * deserialized, the content od deserialized object equals to the content of
68     * initial object.
69     */
70    public void testReadObject() throws Exception {
71        String secret = "secret string";
72        SealedObject so = new SealedObject(secret, new NullCipher());
73        ByteArrayOutputStream bos = new ByteArrayOutputStream();
74        ObjectOutputStream oos = new ObjectOutputStream(bos);
75        oos.writeObject(so);
76
77        ObjectInputStream ois = new ObjectInputStream(new ByteArrayInputStream(
78                bos.toByteArray()));
79
80        SealedObject so_des = (SealedObject) ois.readObject();
81        assertEquals("The secret content of deserialized object "
82                + "should be equal to the secret content of initial object",
83                secret, so_des.getObject(new NullCipher()));
84        assertEquals("The value returned by getAlgorithm() method of "
85                + "deserialized object should be equal to the value returned "
86                + "by getAlgorithm() method of initial object", so
87                .getAlgorithm(), so_des.getAlgorithm());
88    }
89
90    /**
91     * SealedObject(Serializable object, Cipher c) method testing. Tests if the
92     * NullPointerException is thrown in the case of null cipher.
93     */
94    public void testSealedObject1() throws Exception {
95        String secret = "secret string";
96        try {
97            new SealedObject(secret, null);
98            fail("NullPointerException should be thrown in the case "
99                    + "of null cipher.");
100        } catch (NullPointerException e) {
101        }
102
103        KeyGenerator kg = KeyGenerator.getInstance("DES");
104        Key key = kg.generateKey();
105
106        IvParameterSpec ips = new IvParameterSpec(new byte[] {
107                1, 2, 3, 4, 5, 6, 7, 8});
108
109        Cipher cipher = Cipher.getInstance("DES/CBC/PKCS5Padding");
110        cipher.init(Cipher.ENCRYPT_MODE, key, ips);
111
112        SealedObject so = new SealedObject(secret, cipher);
113
114        cipher = Cipher.getInstance("DES/CBC/NoPadding");
115        cipher.init(Cipher.ENCRYPT_MODE, key, ips);
116
117        try {
118            new SealedObject(secret, cipher);
119            fail("IllegalBlockSizeException expected");
120        } catch (IllegalBlockSizeException e) {
121            //expected
122        }
123    }
124
125    /**
126     * SealedObject(SealedObject so) method testing. Tests if the
127     * NullPointerException is thrown in the case of null SealedObject.
128     */
129    public void testSealedObject2() throws Exception {
130        try {
131            new SealedObject(null) {};
132            fail("NullPointerException should be thrown in the case "
133                    + "of null SealedObject.");
134        } catch (NullPointerException e) {
135        }
136
137        String secret = "secret string";
138        Cipher cipher = new NullCipher();
139        SealedObject so1 = new SealedObject(secret, cipher);
140        SealedObject so2 = new SealedObject(so1) {};
141
142        assertEquals("The secret content of the object should equals "
143                + "to the secret content of initial object.", secret, so2
144                .getObject(cipher));
145        assertEquals("The algorithm which was used to seal the object "
146                + "should be the same as the algorithm used to seal the "
147                + "initial object", so1.getAlgorithm(), so2.getAlgorithm());
148    }
149
150    /**
151     * getAlgorithm() method testing. Tests if the returned value equals to the
152     * corresponding value of Cipher object.
153     */
154    public void testGetAlgorithm() throws Exception {
155        String secret = "secret string";
156        String algorithm = "DES";
157        KeyGenerator kg = KeyGenerator.getInstance(algorithm);
158        Key key = kg.generateKey();
159
160        Cipher cipher = Cipher.getInstance(algorithm);
161        cipher.init(Cipher.ENCRYPT_MODE, key);
162        SealedObject so = new SealedObject(secret, cipher);
163
164        assertEquals("The algorithm name should be the same as used "
165                + "in cipher.", algorithm, so.getAlgorithm());
166    }
167
168    /**
169     * getObject(Key key) method testing. Tests if the object sealed with
170     * encryption algorithm and specified parameters can be retrieved by
171     * specifying the cryptographic key.
172     */
173    public void testGetObject1() throws Exception {
174        KeyGenerator kg = KeyGenerator.getInstance("DES");
175        Key key = kg.generateKey();
176
177        IvParameterSpec ips = new IvParameterSpec(new byte[] {
178                1, 2, 3, 4, 5, 6, 7, 8});
179
180        Cipher cipher = Cipher.getInstance("DES/CBC/PKCS5Padding");
181        cipher.init(Cipher.ENCRYPT_MODE, key, ips);
182
183        String secret = "secret string";
184        Mock_SealedObject so = new Mock_SealedObject(secret, cipher);
185
186        assertEquals("The returned object does not equals to the "
187                + "original object.", secret, so.getObject(key));
188
189        assertTrue("The encodedParams field of SealedObject object "
190                + "should contain the encoded algorithm parameters.", Arrays
191                .equals(so.get_encodedParams(), cipher.getParameters()
192                        .getEncoded()));
193        try {
194            so.getObject((Key)null);
195            fail("InvalidKeyException expected");
196        } catch (InvalidKeyException e) {
197            //expected
198        } catch (NullPointerException e) {
199            //also ok
200        }
201    }
202
203    /**
204     * getObject(Cipher c) method testing. Tests if the proper exception is
205     * thrown in the case of incorrect input parameters and if the object sealed
206     * with encryption algorithm and specified parameters can be retrieved by
207     * specifying the initialized Cipher object.
208     */
209    public void testGetObject2() throws Exception {
210        try {
211            new SealedObject("secret string", new NullCipher())
212                    .getObject((Cipher) null);
213            fail("NullPointerException should be thrown in the case of "
214                    + "null cipher.");
215        } catch (NullPointerException e) {
216        }
217
218        KeyGenerator kg = KeyGenerator.getInstance("DES");
219        Key key = kg.generateKey();
220
221        IvParameterSpec ips = new IvParameterSpec(new byte[] {
222                1, 2, 3, 4, 5, 6, 7, 8});
223
224        Cipher cipher = Cipher.getInstance("DES/CBC/PKCS5Padding");
225        cipher.init(Cipher.ENCRYPT_MODE, key, ips);
226
227        String secret = "secret string";
228        SealedObject so = new SealedObject(secret, cipher);
229
230        cipher.init(Cipher.DECRYPT_MODE, key, ips);
231        assertEquals("The returned object does not equals to the "
232                + "original object.", secret, so.getObject(cipher));
233
234        try {
235            so.getObject((Cipher)null);
236            fail("NullPointerException expected");
237        } catch (NullPointerException e) {
238            //expected
239        }
240    }
241
242    /**
243     * getObject(Key key, String provider) method testing. Tests if the proper
244     * exception is thrown in the case of incorrect input parameters and if the
245     * object sealed with encryption algorithm can be retrieved by specifying
246     * the cryptographic key and provider name.
247     */
248    public void testGetObject3() throws Exception {
249        try {
250            new SealedObject("secret string", new NullCipher()).getObject(
251                    new SecretKeySpec(new byte[] {0, 0, 0}, "algorithm"), null);
252            fail("IllegalArgumentException should be thrown in the case of "
253                    + "null provider.");
254        } catch (IllegalArgumentException e) {
255        }
256
257        try {
258            new SealedObject("secret string", new NullCipher()).getObject(
259                    new SecretKeySpec(new byte[] {0, 0, 0}, "algorithm"), "");
260            fail("IllegalArgumentException should be thrown in the case of "
261                    + "empty provider.");
262        } catch (IllegalArgumentException e) {
263        }
264
265        KeyGenerator kg = KeyGenerator.getInstance("DES");
266        Key key = kg.generateKey();
267
268        Cipher cipher = Cipher.getInstance("DES");
269        String provider = cipher.getProvider().getName();
270        cipher.init(Cipher.ENCRYPT_MODE, key);
271
272        String secret = "secret string";
273        SealedObject so = new SealedObject(secret, cipher);
274
275        cipher.init(Cipher.DECRYPT_MODE, key);
276        assertEquals("The returned object does not equals to the "
277                + "original object.", secret, so.getObject(key, provider));
278
279        kg = KeyGenerator.getInstance("DESede");
280        key = kg.generateKey();
281
282        try {
283            so.getObject(key, provider);
284            fail("InvalidKeyException expected");
285        } catch (InvalidKeyException e) {
286            //expected
287        }
288
289        try {
290            so.getObject(key, "Wrong provider name");
291            fail("NoSuchProviderException expected");
292        } catch (NoSuchProviderException e) {
293            //expected
294        }
295    }
296
297    // http://code.google.com/p/android/issues/detail?id=4834
298    public void testDeserialization() throws Exception {
299        // (Boilerplate so we can create SealedObject instances.)
300        KeyGenerator kg = KeyGenerator.getInstance("DES");
301        Key key = kg.generateKey();
302        Cipher cipher = Cipher.getInstance("DES");
303        cipher.init(Cipher.ENCRYPT_MODE, key);
304
305        // Incorrect use of readUnshared meant you couldn't have two SealedObjects
306        // with the same algorithm or parameters algorithm...
307        ArrayList<SealedObject> sealedObjects = new ArrayList<SealedObject>();
308        for (int i = 0; i < 10; ++i) {
309            sealedObjects.add(new SealedObject("hello", cipher));
310        }
311        String serializedForm = SerializationTester.serializeHex(sealedObjects);
312
313        // ...so this would throw "java.io.InvalidObjectException: Unshared read of back reference".
314        SerializationTester.deserializeHex(serializedForm);
315    }
316}
317