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