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
18/**
19 * @author Vladimir N. Molotkov
20 */
21
22package org.apache.harmony.security.tests.java.security;
23
24import java.io.ByteArrayInputStream;
25import java.io.IOException;
26import java.io.InputStream;
27import java.security.DigestInputStream;
28import java.security.MessageDigest;
29import java.security.NoSuchAlgorithmException;
30import java.util.Arrays;
31
32import org.apache.harmony.security.tests.support.MDGoldenData;
33
34import junit.framework.TestCase;
35
36/**
37 * Tests for fields and methods of class <code>DigestInputStream</code>
38 */
39public class DigestInputStreamTest extends TestCase {
40
41    /**
42     * Message digest algorithm name used during testing
43     */
44    private static final String algorithmName[] = {
45            "SHA-1",
46            "SHA",
47            "SHA1",
48            "SHA-256",
49            "SHA-384",
50            "SHA-512",
51            "MD5",
52    };
53    /**
54     * Chunk size for read(byte, off, len) tests
55     */
56    private static final int CHUNK_SIZE = 32;
57    /**
58     * Test message for digest computations
59     */
60    private static final byte[] myMessage = MDGoldenData.getMessage();
61    /**
62     * The length of test message
63     */
64    private static final int MY_MESSAGE_LEN = myMessage.length;
65
66    /**
67     * Constructor for DigestInputStreamTest.
68     *
69     * @param name
70     */
71    public DigestInputStreamTest(String name) {
72        super(name);
73    }
74
75    //
76    // Tests
77    //
78
79    /**
80     * Test #1 for <code>DigestInputStream</code> constructor<br>
81     * <p/>
82     * Assertion: creates new <code>DigestInputStream</code> instance
83     * using valid parameters (both non <code>null</code>)
84     *
85     * @throws NoSuchAlgorithmException
86     */
87    public final void testDigestInputStream01() {
88        for (int i = 0; i < algorithmName.length; i++) {
89            try {
90                MessageDigest md = MessageDigest.getInstance(algorithmName[i]);
91                InputStream is = new ByteArrayInputStream(myMessage);
92                InputStream dis = new DigestInputStream(is, md);
93                assertTrue(dis instanceof DigestInputStream);
94                return;
95            } catch (NoSuchAlgorithmException e) {
96                // allowed failure
97            }
98        }
99        fail(getName() + ": no MessageDigest algorithms available - test not performed");
100    }
101
102    /**
103     * Test #2 for <code>DigestInputStream</code> constructor<br>
104     * <p/>
105     * Assertion: creates new <code>DigestInputStream</code> instance
106     * using valid parameters (both <code>null</code>)
107     */
108    public final void testDigestInputStream02() {
109        InputStream dis = new DigestInputStream(null, null);
110        assertTrue(dis instanceof DigestInputStream);
111    }
112
113    /**
114     * Test #1 for <code>read()</code> method<br>
115     * <p/>
116     * Assertion: returns the byte read<br>
117     * Assertion: updates associated digest<br>
118     */
119    public final void testRead01()
120            throws IOException {
121        for (int ii = 0; ii < algorithmName.length; ii++) {
122            try {
123                MessageDigest md = MessageDigest.getInstance(algorithmName[ii]);
124                InputStream is = new ByteArrayInputStream(myMessage);
125                DigestInputStream dis = new DigestInputStream(is, md);
126                for (int i = 0; i < MY_MESSAGE_LEN; i++) {
127                    // check that read() returns valid values
128                    assertTrue("retval", ((byte) dis.read() == myMessage[i]));
129                }
130                // check that associated digest has been updated properly
131                assertTrue("update",
132                        Arrays.equals(
133                                dis.getMessageDigest().digest(),
134                                MDGoldenData.getDigest(algorithmName[ii])));
135                return;
136            } catch (NoSuchAlgorithmException e) {
137                // allowed failure
138            }
139        }
140        fail(getName() + ": no MessageDigest algorithms available - test not performed");
141    }
142
143    /**
144     * Test #2 for <code>read()</code> method<br>
145     * <p/>
146     * Assertion: returns -1 if EOS had been
147     * reached but not read before method call<br>
148     * <p/>
149     * Assertion: must not update digest if EOS had been
150     * reached but not read before method call<br>
151     */
152    public final void testRead02()
153            throws IOException {
154        for (int ii = 0; ii < algorithmName.length; ii++) {
155            try {
156                MessageDigest md = MessageDigest.getInstance(algorithmName[ii]);
157                InputStream is = new ByteArrayInputStream(myMessage);
158                DigestInputStream dis = new DigestInputStream(is, md);
159                for (int i = 0; i < MY_MESSAGE_LEN; i++) {
160                    dis.read();
161                }
162                // check that subsequent read() calls return -1 (eos)
163                assertEquals("retval1", -1, dis.read());
164                assertEquals("retval2", -1, dis.read());
165                assertEquals("retval3", -1, dis.read());
166                // check that 3 previous read() calls did not update digest
167                assertTrue("update",
168                        Arrays.equals(dis.getMessageDigest().digest(),
169                                MDGoldenData.getDigest(algorithmName[ii])));
170                return;
171            } catch (NoSuchAlgorithmException e) {
172                // allowed failure
173            }
174        }
175        fail(getName() + ": no MessageDigest algorithms available - test not performed");
176    }
177
178    /**
179     * Test #3 for <code>read()</code> method<br>
180     * Test #1 for <code>on(boolean)</code> method<br>
181     * <p/>
182     * Assertion: <code>read()</code> must not update digest if it is off<br>
183     * Assertion: <code>on(boolean)</code> turns digest functionality on
184     * (if <code>true</code> passed as a parameter) or off (if <code>false</code>
185     * passed)
186     */
187    public final void testRead03()
188            throws IOException {
189        for (int ii = 0; ii < algorithmName.length; ii++) {
190            try {
191                MessageDigest md = MessageDigest.getInstance(algorithmName[ii]);
192                InputStream is = new ByteArrayInputStream(myMessage);
193                DigestInputStream dis = new DigestInputStream(is, md);
194
195                // turn digest off
196                dis.on(false);
197
198                for (int i = 0; i < MY_MESSAGE_LEN; i++) {
199                    dis.read();
200                }
201
202                // check that digest value has not been updated by read()
203                assertTrue(Arrays.equals(dis.getMessageDigest().digest(),
204                        MDGoldenData.getDigest(algorithmName[ii] + "_NU")));
205                return;
206            } catch (NoSuchAlgorithmException e) {
207                // allowed failure
208            }
209        }
210        fail(getName() + ": no MessageDigest algorithms available - test not performed");
211    }
212
213    /**
214     * Test #4 for <code>read()</code> method<br>
215     * <p/>
216     * Assertion: broken <code>DigestInputStream</code>instance:
217     * <code>InputStream</code> not set. <code>read()</code> must
218     * not work
219     */
220    public final void testRead04() throws IOException {
221        for (int ii = 0; ii < algorithmName.length; ii++) {
222            try {
223                MessageDigest md = MessageDigest.getInstance(algorithmName[ii]);
224                DigestInputStream dis = new DigestInputStream(null, md);
225                // must result in an exception
226                try {
227                    for (int i = 0; i < MY_MESSAGE_LEN; i++) {
228                        dis.read();
229                    }
230                } catch (Exception e) {
231                    return;
232                }
233
234                fail("InputStream not set. read() must not work");
235
236            } catch (NoSuchAlgorithmException e) {
237                // allowed failure
238            }
239        }
240        fail(getName() + ": no MessageDigest algorithms available - test not performed");
241    }
242
243    /**
244     * Test #5 for <code>read()</code> method<br>
245     * <p/>
246     * Assertion: broken <code>DigestInputStream</code>instance:
247     * associated <code>MessageDigest</code> not set.
248     * <code>read()</code> must not work when digest
249     * functionality is on
250     */
251    public final void testRead05() {
252        InputStream is = new ByteArrayInputStream(myMessage);
253        DigestInputStream dis = new DigestInputStream(is, null);
254
255        // must result in an exception
256        try {
257            for (int i = 0; i < MY_MESSAGE_LEN; i++) {
258                dis.read();
259            }
260            fail("read() must not work when digest functionality is on");
261        } catch (Exception e) {
262        }
263    }
264
265    /**
266     * Test #6 for <code>read()</code> method<br>
267     * Test #2 for <code>on(boolean)</code> method<br>
268     * <p/>
269     * Assertion: broken <code>DigestInputStream</code>instance:
270     * associated <code>MessageDigest</code> not set.
271     * <code>read()</code> must work when digest
272     * functionality is off
273     */
274    public final void testRead06()
275            throws IOException {
276        InputStream is = new ByteArrayInputStream(myMessage);
277        // construct object without digest
278        DigestInputStream dis = new DigestInputStream(is, null);
279        // set digest functionality to off
280        dis.on(false);
281        // the following must pass without any exception
282        for (int i = 0; i < MY_MESSAGE_LEN; i++) {
283            assertTrue((byte) dis.read() == myMessage[i]);
284        }
285    }
286
287    /**
288     * Test #1 for <code>read(byte[],int,int)</code> method<br>
289     * <p/>
290     * Assertion: returns the number of bytes read<br>
291     * <p/>
292     * Assertion: put bytes read into specified array at specified offset<br>
293     * <p/>
294     * Assertion: updates associated digest<br>
295     */
296    public final void testReadbyteArrayintint01()
297            throws IOException {
298        for (int ii = 0; ii < algorithmName.length; ii++) {
299            try {
300                MessageDigest md = MessageDigest.getInstance(algorithmName[ii]);
301                InputStream is = new ByteArrayInputStream(myMessage);
302                DigestInputStream dis = new DigestInputStream(is, md);
303                byte[] bArray = new byte[MY_MESSAGE_LEN];
304
305                // check that read(byte[],int,int) returns valid value
306                assertTrue("retval",
307                        dis.read(bArray, 0, bArray.length) == MY_MESSAGE_LEN);
308                // check that bArray has been filled properly
309                assertTrue("bArray", Arrays.equals(myMessage, bArray));
310                // check that associated digest has been updated properly
311                assertTrue("update", Arrays.equals(dis.getMessageDigest().digest(),
312                        MDGoldenData.getDigest(algorithmName[ii])));
313                return;
314            } catch (NoSuchAlgorithmException e) {
315                // allowed failure
316            }
317        }
318        fail(getName() + ": no MessageDigest algorithms available - test not performed");
319    }
320
321    /**
322     * Test #2 for <code>read(byte[],int,int)</code> method<br>
323     * <p/>
324     * Assertion: returns the number of bytes read<br>
325     * <p/>
326     * Assertion: put bytes read into specified array at specified offset<br>
327     * <p/>
328     * Assertion: updates associated digest<br>
329     */
330    public final void testReadbyteArrayintint02()
331            throws IOException {
332        // check precondition
333        assertEquals(0, MY_MESSAGE_LEN % CHUNK_SIZE);
334
335        for (int ii = 0; ii < algorithmName.length; ii++) {
336            try {
337                MessageDigest md = MessageDigest.getInstance(algorithmName[ii]);
338                InputStream is = new ByteArrayInputStream(myMessage);
339                DigestInputStream dis = new DigestInputStream(is, md);
340                byte[] bArray = new byte[MY_MESSAGE_LEN];
341
342                for (int i = 0; i < MY_MESSAGE_LEN / CHUNK_SIZE; i++) {
343                    // check that read(byte[],int,int) returns valid value
344                    assertTrue("retval",
345                            dis.read(bArray, i * CHUNK_SIZE, CHUNK_SIZE) == CHUNK_SIZE);
346                }
347                // check that bArray has been filled properly
348                assertTrue("bArray", Arrays.equals(myMessage, bArray));
349                // check that associated digest has been updated properly
350                assertTrue("update", Arrays.equals(dis.getMessageDigest().digest(),
351                        MDGoldenData.getDigest(algorithmName[ii])));
352                return;
353            } catch (NoSuchAlgorithmException e) {
354                // allowed failure
355            }
356        }
357        fail(getName() + ": no MessageDigest algorithms available - test not performed");
358    }
359
360
361    /**
362     * Test #3 for <code>read(byte[],int,int)</code> method<br>
363     * <p/>
364     * Assertion: returns the number of bytes read<br>
365     * <p/>
366     * Assertion: put bytes read into specified array at specified offset<br>
367     * <p/>
368     * Assertion: updates associated digest<br>
369     */
370    public final void testReadbyteArrayintint03()
371            throws IOException {
372        // check precondition
373        assertTrue(MY_MESSAGE_LEN % (CHUNK_SIZE + 1) != 0);
374
375        for (int ii = 0; ii < algorithmName.length; ii++) {
376            try {
377                MessageDigest md = MessageDigest.getInstance(algorithmName[ii]);
378                InputStream is = new ByteArrayInputStream(myMessage);
379                DigestInputStream dis = new DigestInputStream(is, md);
380                byte[] bArray = new byte[MY_MESSAGE_LEN];
381
382                for (int i = 0; i < MY_MESSAGE_LEN / (CHUNK_SIZE + 1); i++) {
383                    // check that read(byte[],int,int) returns valid value
384                    assertTrue("retval1",
385                            dis.read(bArray, i * (CHUNK_SIZE + 1), CHUNK_SIZE + 1) ==
386                                    CHUNK_SIZE + 1);
387                }
388
389                // check that last call returns right
390                // number of remaining bytes
391                assertTrue("retval2",
392                        dis.read(bArray,
393                                MY_MESSAGE_LEN / (CHUNK_SIZE + 1) * (CHUNK_SIZE + 1),
394                                MY_MESSAGE_LEN % (CHUNK_SIZE + 1)) ==
395                                (MY_MESSAGE_LEN % (CHUNK_SIZE + 1)));
396
397                // check that bArray has been filled properly
398                assertTrue("bArray", Arrays.equals(myMessage, bArray));
399                // check that associated digest has been updated properly
400                assertTrue("update", Arrays.equals(dis.getMessageDigest().digest(),
401                        MDGoldenData.getDigest(algorithmName[ii])));
402                return;
403            } catch (NoSuchAlgorithmException e) {
404                // allowed failure
405            }
406        }
407        fail(getName() + ": no MessageDigest algorithms available - test not performed");
408    }
409
410    /**
411     * Test #4 for <code>read(byte[],int,int)</code> method<br>
412     * <p/>
413     * Assertion: returns the number of bytes read<br>
414     * <p/>
415     * Assertion: updates associated digest<br>
416     */
417    public final void testReadbyteArrayintint04()
418            throws IOException {
419        for (int ii = 0; ii < algorithmName.length; ii++) {
420            try {
421                MessageDigest md = MessageDigest.getInstance(algorithmName[ii]);
422                InputStream is = new ByteArrayInputStream(myMessage);
423                DigestInputStream dis = new DigestInputStream(is, md);
424                byte[] bArray = new byte[MY_MESSAGE_LEN];
425                // read all but EOS
426                dis.read(bArray, 0, bArray.length);
427                // check that subsequent read(byte[],int,int) calls return -1 (EOS)
428                assertEquals("retval1", -1, dis.read(bArray, 0, 1));
429                assertEquals("retval2", -1, dis.read(bArray, 0, bArray.length));
430                assertEquals("retval3", -1, dis.read(bArray, 0, 1));
431                // check that 3 previous read() calls did not update digest
432                assertTrue("update",
433                        Arrays.equals(dis.getMessageDigest().digest(),
434                                MDGoldenData.getDigest(algorithmName[ii])));
435                return;
436            } catch (NoSuchAlgorithmException e) {
437                // allowed failure
438            }
439        }
440        fail(getName() + ": no MessageDigest algorithms available - test not performed");
441    }
442
443    /**
444     * Test #5 for <code>read(byte[],int,int)</code> method<br>
445     * <p/>
446     * Assertion: returns the number of bytes read<br>
447     * <p/>
448     * Assertion: put bytes read into specified array at specified offset<br>
449     * <p/>
450     * Assertion: does not update associated digest if
451     * digest functionality is off<br>
452     */
453    public final void testReadbyteArrayintint05()
454            throws IOException {
455        // check precondition
456        assertEquals(0, MY_MESSAGE_LEN % CHUNK_SIZE);
457
458        for (int ii = 0; ii < algorithmName.length; ii++) {
459            try {
460                MessageDigest md = MessageDigest.getInstance(algorithmName[ii]);
461                InputStream is = new ByteArrayInputStream(myMessage);
462                DigestInputStream dis = new DigestInputStream(is, md);
463                byte[] bArray = new byte[MY_MESSAGE_LEN];
464
465                // turn digest off
466                dis.on(false);
467
468                for (int i = 0; i < MY_MESSAGE_LEN / CHUNK_SIZE; i++) {
469                    dis.read(bArray, i * CHUNK_SIZE, CHUNK_SIZE);
470                }
471                // check that digest has not been updated
472                assertTrue(Arrays.equals(dis.getMessageDigest().digest(),
473                        MDGoldenData.getDigest(algorithmName[ii] + "_NU")));
474                return;
475            } catch (NoSuchAlgorithmException e) {
476                // allowed failure
477            }
478        }
479        fail(getName() + ": no MessageDigest algorithms available - test not performed");
480    }
481
482    /**
483     * Test for <code>getMessageDigest()</code> method<br>
484     * <p/>
485     * Assertion: returns associated message digest<br>
486     */
487    public final void testGetMessageDigest() {
488        for (int ii = 0; ii < algorithmName.length; ii++) {
489            try {
490                MessageDigest md = MessageDigest.getInstance(algorithmName[ii]);
491                DigestInputStream dis = new DigestInputStream(null, md);
492
493                assertTrue(dis.getMessageDigest() == md);
494                return;
495            } catch (NoSuchAlgorithmException e) {
496                // allowed failure
497            }
498        }
499        fail(getName() + ": no MessageDigest algorithms available - test not performed");
500    }
501
502
503    /**
504     * Test for <code>setMessageDigest()</code> method<br>
505     * <p/>
506     * Assertion: set associated message digest<br>
507     */
508    public final void testSetMessageDigest() {
509        for (int ii = 0; ii < algorithmName.length; ii++) {
510            try {
511                DigestInputStream dis = new DigestInputStream(null, null);
512                MessageDigest md = MessageDigest.getInstance(algorithmName[ii]);
513                dis.setMessageDigest(md);
514
515                assertTrue(dis.getMessageDigest() == md);
516                return;
517            } catch (NoSuchAlgorithmException e) {
518                // allowed failure
519            }
520        }
521        fail(getName() + ": no MessageDigest algorithms available - test not performed");
522    }
523
524    /**
525     * Test for <code>on()</code> method<br>
526     * Assertion: turns digest functionality on or off
527     */
528    public final void testOn() throws IOException {
529        for (int ii = 0; ii < algorithmName.length; ii++) {
530            try {
531                MessageDigest md = MessageDigest.getInstance(algorithmName[ii]);
532                InputStream is = new ByteArrayInputStream(myMessage);
533                DigestInputStream dis = new DigestInputStream(is, md);
534
535                // turn digest off
536                dis.on(false);
537
538                for (int i = 0; i < MY_MESSAGE_LEN - 1; i++) {
539                    dis.read();
540                }
541
542                // turn digest on
543                dis.on(true);
544
545                // read remaining byte
546                dis.read();
547
548                byte[] digest = dis.getMessageDigest().digest();
549
550                // check that digest value has been
551                // updated by the last read() call
552                assertFalse(
553                        Arrays.equals(digest, MDGoldenData.getDigest(algorithmName[ii])) ||
554                                Arrays.equals(digest, MDGoldenData.getDigest(algorithmName[ii] + "_NU")));
555                return;
556            } catch (NoSuchAlgorithmException e) {
557                // allowed failure
558            }
559        }
560        fail(getName() + ": no MessageDigest algorithms available - test not performed");
561    }
562
563    /**
564     * Test for <code>toString()</code> method<br>
565     * Assertion: returns <code>String</code> representation of this object
566     */
567    public final void testToString() {
568        for (int ii = 0; ii < algorithmName.length; ii++) {
569            try {
570                MessageDigest md = MessageDigest.getInstance(algorithmName[ii]);
571                InputStream is = new ByteArrayInputStream(myMessage);
572                DigestInputStream dis = new DigestInputStream(is, md);
573
574                assertNotNull(dis.toString());
575                return;
576            } catch (NoSuchAlgorithmException e) {
577                // allowed failure
578            }
579        }
580        fail(getName() + ": no MessageDigest algorithms available - test not performed");
581    }
582
583}
584