1/*
2 * Licensed to the Apache Software Foundation (ASF) under one or more
3 * contributor license agreements.  See the NOTICE file distributed with
4 * this work for additional information regarding copyright ownership.
5 * The ASF licenses this file to You under the Apache License, Version 2.0
6 * (the "License"); you may not use this file except in compliance with
7 * the License.  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,
13 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14 * See the License for the specific language governing permissions and
15 * limitations under the License.
16 */
17
18package org.apache.harmony.security.tests.java.security;
19
20import java.io.ByteArrayOutputStream;
21import java.io.DataOutputStream;
22import java.security.DigestException;
23import java.security.MessageDigest;
24import java.security.NoSuchAlgorithmException;
25import java.security.NoSuchProviderException;
26import java.security.Provider;
27import java.security.Security;
28import java.util.ArrayList;
29import java.util.Arrays;
30import java.util.HashMap;
31import java.util.List;
32import java.util.Map;
33import java.util.Map.Entry;
34
35import dalvik.system.VMRuntime;
36import sun.security.jca.Providers;
37
38public class MessageDigest2Test extends junit.framework.TestCase {
39
40    private static final String MESSAGEDIGEST_ID = "MessageDigest.";
41
42    private Map<Provider, List<String>> digestAlgs = new HashMap<Provider, List<String>>();
43
44    private static final byte[] AR1 = { 1, 2, 3, 4, 5, 6, 7, 8, 9, 0 };
45
46    private static final byte[] AR2 = { 1, 2, 3, 4, 5, 6, 7, 8, 9, 0 };
47
48    private static final String MESSAGE = "abc";
49
50    private static final byte[] MESSAGE_DIGEST = { -87, -103, 62, 54, 71, 6,
51            -127, 106, -70, 62, 37, 113, 120, 80, -62, 108, -100, -48, -40,
52            -99, };
53
54    private static final byte[] MESSAGE_DIGEST_63_As = { 3, -16, -97, 91, 21,
55            -118, 122, -116, -38, -39, 32, -67, -36, 41, -72, 28, 24, -91, 81,
56            -11, };
57
58    private static final byte[] MESSAGE_DIGEST_64_As = { 0, -104, -70, -126,
59            75, 92, 22, 66, 123, -41, -95, 18, 42, 90, 68, 42, 37, -20, 100,
60            77, };
61
62    private static final byte[] MESSAGE_DIGEST_65_As = { 17, 101, 83, 38, -57,
63            8, -41, 3, 25, -66, 38, 16, -24, -91, 125, -102, 91, -107, -99, 59, };
64
65    /**
66     * java.security.MessageDigest#MessageDigest(java.lang.String)
67     */
68    public void test_constructor() {
69        for (List<String> algorithms : digestAlgs.values()) {
70            for (String algorithm : algorithms) {
71                MessageDigestStub md = new MessageDigestStub(algorithm);
72                assertEquals(algorithm, md.getAlgorithm());
73                assertEquals(0, md.getDigestLength());
74                assertNull(md.getProvider());
75            }
76        }
77    }
78
79    /**
80     * java.security.MessageDigest#clone()
81     */
82    public void test_clone() throws Exception {
83        for (Entry<Provider, List<String>> e : digestAlgs.entrySet()) {
84            for (String algorithm : e.getValue()) {
85                MessageDigest d1 = MessageDigest.getInstance(algorithm, e.getKey().getName());
86                for (byte b = 0; b < 84; b++) {
87                    d1.update(b);
88                }
89
90                MessageDigest d2 = (MessageDigest) d1.clone();
91                d1.update((byte) 1);
92                d2.update((byte) 1);
93
94                assertTrue("cloned hash differs from original for algorithm " + algorithm,
95                           MessageDigest.isEqual(d1.digest(), d2.digest()));
96            }
97        }
98    }
99
100    private static final byte[] SHA_DATA_2 = { 70, -54, 124, 120, -29, 57, 56,
101            119, -108, -54, -97, -76, -97, -50, -63, -73, 2, 85, -53, -79, };
102
103    private void testSerializationSHA_DATA_2(MessageDigest sha) throws Exception {
104        sha.reset();
105        ByteArrayOutputStream out = new ByteArrayOutputStream();
106        DataOutputStream output = new DataOutputStream(out);
107
108        // Made up data
109        output.writeUTF("tests.api.java.security.MessageDigestTest$InitializerFieldsTest3");
110        output.writeInt(0); // class modifiers
111        output.writeUTF("java.io.Serializable"); // interfaces
112
113        // Fields
114        output.writeUTF("sub_toBeNotSerialized"); // name
115        output.writeInt(9); // modifiers
116        output.writeUTF("Ljava/lang/String;"); // signature
117
118        output.writeUTF("sub_toBeNotSerialized2"); // name
119        output.writeInt(9); // modifiers
120        output.writeUTF("Ljava/lang/String;"); // signature
121
122        output.writeUTF("sub_toBeSerialized"); // name
123        output.writeInt(1); // modifiers
124        output.writeUTF("Ljava/lang/String;"); // signature
125
126        output.writeUTF("sub_toBeSerialized3"); // name
127        output.writeInt(1); // modifiers
128        output.writeUTF("Ljava/lang/String;"); // signature
129
130        output.writeUTF("sub_toBeSerialized4"); // name
131        output.writeInt(1); // modifiers
132        output.writeUTF("Ljava/lang/String;"); // signature
133
134        output.writeUTF("sub_toBeSerialized5"); // name
135        output.writeInt(1); // modifiers
136        output.writeUTF("Ljava/lang/String;"); // signature
137
138        // clinit
139        output.writeUTF("<clinit>"); // name
140        output.writeInt(8); // modifiers
141        output.writeUTF("()V"); // signature
142
143        // constructors
144        output.writeUTF("<init>"); // name
145        output.writeInt(0); // modifiers
146        output.writeUTF("()V"); // signature
147
148        // methods
149        output.writeUTF("equals"); // name
150        output.writeInt(1); // modifiers
151        output.writeUTF("(Ljava.lang.Object;)Z"); // signature
152
153        output.flush();
154
155        byte[] data = out.toByteArray();
156        byte[] hash = sha.digest(data);
157        assertTrue("SHA_DATA_2 NOT ok", Arrays.equals(hash, SHA_DATA_2));
158    }
159
160    private static final byte[] SHA_DATA_1 = { 90, 36, 111, 106, -32, 38, 4,
161            126, 21, -51, 107, 45, -64, -68, -109, 112, -31, -46, 34, 115, };
162
163    private void testSerializationSHA_DATA_1(MessageDigest sha) throws Exception {
164        sha.reset();
165        ByteArrayOutputStream out = new ByteArrayOutputStream();
166        DataOutputStream output = new DataOutputStream(out);
167
168        // Made up data
169        output.writeUTF("tests.api.java.security.MessageDigestTest$OptionalDataNotRead");
170        // name
171        output.writeInt(0); // class modifiers
172        output.writeUTF("java.io.Serializable"); // interfaces
173
174        // Fields
175        output.writeUTF("class$0"); // name
176        output.writeInt(8); // modifiers
177        output.writeUTF("Ljava/lang/Class;"); // signature
178
179        output.writeUTF("field1"); // name
180        output.writeInt(2); // modifiers
181        output.writeUTF("I"); // signature
182
183        output.writeUTF("field2"); // name
184        output.writeInt(2); // modifiers
185        output.writeUTF("I"); // signature
186
187        // clinit
188        output.writeUTF("<clinit>"); // name
189        output.writeInt(8); // modifiers
190        output.writeUTF("()V"); // signature
191
192        // constructors
193        output.writeUTF("<init>"); // name
194        output.writeInt(1); // modifiers
195        output.writeUTF("()V"); // signature
196
197        output.flush();
198        byte[] data = out.toByteArray();
199        byte[] hash = sha.digest(data);
200        assertTrue("SHA_DATA_1 NOT ok", Arrays.equals(hash, SHA_DATA_1));
201    }
202
203    /**
204     * java.security.MessageDigest#digest()
205     */
206    public void test_digest() throws Exception {
207        MessageDigest sha = MessageDigest.getInstance("SHA");
208        assertNotNull(sha);
209        sha.update(MESSAGE.getBytes());
210        byte[] digest = sha.digest();
211        assertTrue("bug in SHA", MessageDigest.isEqual(digest, MESSAGE_DIGEST));
212
213        sha.reset();
214        for (int i = 0; i < 63; i++) {
215            // just under buffer capacity
216            sha.update((byte) 'a');
217        }
218        digest = sha.digest();
219        assertTrue("bug in SHA", MessageDigest.isEqual(digest, MESSAGE_DIGEST_63_As));
220
221        sha.reset();
222        for (int i = 0; i < 64; i++) {
223            // exact SHA buffer capacity
224            sha.update((byte) 'a');
225        }
226        digest = sha.digest();
227        assertTrue("bug in SHA", MessageDigest.isEqual(digest, MESSAGE_DIGEST_64_As));
228
229        sha.reset();
230        for (int i = 0; i < 65; i++) {
231            // just above SHA buffer capacity
232            sha.update((byte) 'a');
233        }
234        digest = sha.digest();
235        assertTrue("bug in SHA", MessageDigest.isEqual(digest, MESSAGE_DIGEST_65_As));
236
237        testSerializationSHA_DATA_1(sha);
238        testSerializationSHA_DATA_2(sha);
239    }
240
241    /**
242     * java.security.MessageDigest#digest(byte[])
243     */
244    public void test_digest$B() throws Exception {
245        for (Entry<Provider, List<String>> e : digestAlgs.entrySet()) {
246            for (String algorithm : e.getValue()) {
247                MessageDigest digest = MessageDigest.getInstance(algorithm, e.getKey().getName());
248                assertNotNull(digest);
249                digest.digest(AR1);
250            }
251        }
252    }
253
254    /**
255     * java.security.MessageDigest#digest(byte[], int, int)
256     */
257    public void test_digest$BII() throws Exception {
258        for (Entry<Provider, List<String>> e : digestAlgs.entrySet()) {
259            for (String algorithm : e.getValue()) {
260                MessageDigest digest = MessageDigest.getInstance(algorithm, e.getKey().getName());
261                assertNotNull(digest);
262                int len = digest.getDigestLength();
263                byte[] digestBytes = new byte[len];
264                digest.digest(digestBytes, 0, digestBytes.length);
265            }
266            try {
267                MessageDigest.getInstance("SHA").digest(new byte[] {}, Integer.MAX_VALUE, 755);
268                fail();
269            } catch (IllegalArgumentException expected) {
270            }
271        }
272    }
273
274    /**
275     * java.security.MessageDigest#update(byte[], int, int)
276     */
277    public void test_update$BII() throws Exception {
278        try {
279            MessageDigest.getInstance("SHA").update(new byte[] {},
280                                                    Integer.MAX_VALUE, Integer.MAX_VALUE);
281            fail();
282        } catch (IllegalArgumentException expected) {
283        }
284    }
285
286    /**
287     * java.security.MessageDigest#getAlgorithm()
288     */
289    public void test_getAlgorithm() throws Exception {
290        for (Entry<Provider, List<String>> e : digestAlgs.entrySet()) {
291            for (String algorithm : e.getValue()) {
292                MessageDigest md = MessageDigest.getInstance(algorithm, e.getKey().getName());
293                assertEquals(algorithm, md.getAlgorithm());
294            }
295        }
296    }
297
298    /**
299     * java.security.MessageDigest#getDigestLength()
300     */
301    public void test_getDigestLength() throws Exception {
302        for (Entry<Provider, List<String>> e : digestAlgs.entrySet()) {
303            for (String algorithm : e.getValue()) {
304                MessageDigest md = MessageDigest.getInstance(algorithm, e.getKey().getName());
305                assertTrue("length not ok", md.getDigestLength() > 0);
306            }
307        }
308    }
309
310    /**
311     * java.security.MessageDigest#getInstance(java.lang.String)
312     */
313    public void test_getInstanceLjava_lang_String() throws Exception {
314        for (Entry<Provider, List<String>> e : digestAlgs.entrySet()) {
315            for (String algorithm : e.getValue()) {
316                MessageDigest md = MessageDigest.getInstance(algorithm);
317                assertNotNull(md);
318            }
319        }
320
321        try {
322            MessageDigest.getInstance("UnknownDigest");
323            fail("expected NoSuchAlgorithmException");
324        } catch (NoSuchAlgorithmException expected) {
325        }
326    }
327
328    /**
329     * java.security.MessageDigest#getInstance(java.lang.String,
330     *        java.lang.String)
331     */
332    public void test_getInstanceLjava_lang_StringLjava_lang_String() throws Exception {
333        for (Entry<Provider, List<String>> e : digestAlgs.entrySet()) {
334            for (String algorithm : e.getValue()) {
335                MessageDigest md = MessageDigest.getInstance(algorithm, e.getKey().getName());
336                assertNotNull(md);
337            }
338        }
339
340        for (List<String> algorithms : digestAlgs.values()) {
341            for (String algorithm : algorithms) {
342                try {
343                    MessageDigest.getInstance(algorithm, "UnknownProvider");
344                    fail("expected NoSuchProviderException");
345                } catch (NoSuchProviderException expected) {
346                }
347            }
348        }
349
350        for (Provider provider : digestAlgs.keySet()) {
351            try {
352                MessageDigest.getInstance("UnknownDigest", provider.getName());
353                fail("expected NoSuchAlgorithmException");
354            } catch (NoSuchAlgorithmException expected) {
355            }
356        }
357
358        for (Provider provider : digestAlgs.keySet()) {
359            try {
360                MessageDigest.getInstance(null, provider.getName());
361                fail("expected NullPointerException");
362            } catch (NullPointerException expected) {
363            }
364        }
365
366        try {
367            MessageDigest.getInstance("AnyDigest", (String)null);
368            fail("expected IllegalArgumentException");
369        } catch (IllegalArgumentException expected) {
370        }
371    }
372
373    /**
374     * java.security.MessageDigest#getInstance(java.lang.String,
375     *        java.security.Provider)
376     */
377    public void test_getInstanceLjava_lang_StringLjava_security_Provider() throws Exception {
378        for (Entry<Provider, List<String>> e : digestAlgs.entrySet()) {
379            for (String algorithm : e.getValue()) {
380                MessageDigest md = MessageDigest.getInstance(algorithm, e.getKey().getName());
381                assertNotNull(md);
382            }
383        }
384
385        try {
386            MessageDigest.getInstance(null, new TestProvider());
387            fail("expected NullPointerException");
388        } catch (NullPointerException expected) {
389        }
390
391        try {
392            MessageDigest.getInstance("UnknownDigest", new TestProvider());
393            fail("expected NoSuchAlgorithmException");
394        } catch (NoSuchAlgorithmException expected) {
395        }
396
397        try {
398            MessageDigest.getInstance("AnyDigest", (Provider)null);
399            fail("expected IllegalArgumentException");
400        } catch (IllegalArgumentException expected) {
401        }
402    }
403
404    /**
405     * java.security.MessageDigest#getProvider()
406     */
407    public void test_getProvider() throws Exception {
408        for (Entry<Provider, List<String>> e : digestAlgs.entrySet()) {
409            for (String algorithm : e.getValue()) {
410                MessageDigest md = MessageDigest.getInstance(algorithm, e.getKey().getName());
411                assertNotNull("provider is null", md.getProvider());
412            }
413        }
414    }
415
416    /**
417     * java.security.MessageDigest#isEqual(byte[], byte[])
418     */
419    public void test_isEqual$B$B() {
420        assertTrue("isEqual is not correct", MessageDigest.isEqual(AR1, AR2));
421    }
422
423    /**
424     * java.security.MessageDigest#toString()
425     */
426    public void test_toString() throws Exception {
427        String str = MessageDigest.getInstance("SHA").toString();
428        assertNotNull("toString is null", str);
429    }
430
431    protected void setUp() throws Exception {
432        super.setUp();
433        Provider[] providers = Security.getProviders("MessageDigest.SHA");
434        for (Provider provider : providers) {
435            digestAlgs.put(provider, getDigestAlgorithms(provider));
436        }
437
438        // Allow access to deprecated BC algorithms in this test, so we can ensure they
439        // continue to work
440        Providers.setMaximumAllowableApiLevelForBcDeprecation(
441                VMRuntime.getRuntime().getTargetSdkVersion());
442    }
443
444    @Override
445    public void tearDown() throws Exception {
446        Providers.setMaximumAllowableApiLevelForBcDeprecation(
447                Providers.DEFAULT_MAXIMUM_ALLOWABLE_TARGET_API_LEVEL_FOR_BC_DEPRECATION);
448        super.tearDown();
449    }
450
451    /*
452     * Returns the digest algorithms that the given provider supports.
453     */
454    private List<String> getDigestAlgorithms(Provider provider) {
455        if (provider == null) {
456            fail("No digest algorithms were found");
457        }
458
459        List<String> algs = new ArrayList<String>();
460        for (Object key : provider.keySet()) {
461            String algorithm = (String) key;
462            if (algorithm.startsWith(MESSAGEDIGEST_ID) && !algorithm.contains(" ")) {
463                algs.add(algorithm.substring(MESSAGEDIGEST_ID.length()));
464            }
465        }
466
467        if (algs.size() == 0) {
468            fail("No digest algorithms were found");
469        }
470        return algs;
471    }
472
473    private class MessageDigestStub extends MessageDigest {
474        public MessageDigestStub(String algorithm) {
475            super(algorithm);
476        }
477
478        public byte[] engineDigest() {
479            return null;
480        }
481
482        public void engineReset() {
483
484        }
485
486        public void engineUpdate(byte input) {
487
488        }
489
490        public void engineUpdate(byte[] input, int offset, int len) {
491
492        }
493
494    }
495
496    private static class TestProvider extends Provider {
497
498        protected TestProvider() {
499            super("TestProvider", 1.0, "INFO");
500
501        }
502
503    }
504}
505