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.ByteArrayOutputStream;
26import java.io.IOException;
27import java.io.OutputStream;
28import java.security.DigestOutputStream;
29import java.security.MessageDigest;
30import java.security.NoSuchAlgorithmException;
31import java.util.Arrays;
32import junit.framework.TestCase;
33import org.apache.harmony.security.tests.support.MDGoldenData;
34import org.apache.harmony.security.tests.support.MyMessageDigest1;
35import tests.support.Support_OutputStream;
36
37/**
38 * Tests for fields and methods of class <code>DigestInputStream</code>
39 *
40 */
41public class DigestOutputStreamTest 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     * java.security.DigestOutputStream#DigestOutputStream(java.io.OutputStream,
74     *        java.security.MessageDigest)
75     */
76    public void test_CtorLjava_io_OutputStreamLjava_security_MessageDigest() {
77
78        // non-null parameters
79        MessageDigest md = new MyMessageDigest1();
80        MyOutputStream out = new MyOutputStream();
81
82        MyDigestOutputStream dos = new MyDigestOutputStream(out, md);
83        assertSame(out, dos.myOutputStream());
84        assertSame(md, dos.myMessageDigest());
85
86        // null parameters
87        dos = new MyDigestOutputStream(null, null);
88        assertNull(dos.myOutputStream());
89        assertNull(dos.myMessageDigest());
90
91        dos = new MyDigestOutputStream(null, md);
92        assertNull(dos.myOutputStream());
93        assertNotNull(dos.myMessageDigest());
94
95        dos = new MyDigestOutputStream(out, null);
96        assertNotNull(dos.myOutputStream());
97        assertNull(dos.myMessageDigest());
98    }
99
100    /**
101     * java.security.DigestOutputStream#getMessageDigest()
102     */
103    public void test_getMessageDigest() {
104
105        MessageDigest digest = new MyMessageDigest1();
106        OutputStream out = new MyOutputStream();
107
108        // non-null parameter
109        DigestOutputStream dos = new DigestOutputStream(out, digest);
110        assertSame(digest, dos.getMessageDigest());
111
112        // null parameter
113        dos = new DigestOutputStream(out, null);
114        assertNull("getMessageDigest should have returned null", dos.getMessageDigest());
115    }
116
117    /**
118     * java.security.DigestOutputStream#setMessageDigest(MessageDigest)
119     */
120    public void test_setMessageDigestLjava_security_MessageDigest() {
121
122        MessageDigest digest = new MyMessageDigest1();
123        OutputStream out = new MyOutputStream();
124
125        DigestOutputStream dos = new DigestOutputStream(out, null);
126
127        // non-null parameter
128        dos.setMessageDigest(digest);
129        assertSame(digest, dos.getMessageDigest());
130
131        // null parameter
132        dos.setMessageDigest(null);
133        assertNull("getMessageDigest should have returned null", dos.getMessageDigest());
134    }
135
136
137    /**
138     * Test #1 for <code>write(int)</code> method<br>
139     *
140     * Assertion: writes the byte to the output stream<br>
141     * Assertion: updates associated digest<br>
142     */
143    public final void testWriteint01()
144        throws IOException {
145        for (int k=0; k<algorithmName.length; k++) {
146            try {
147                MessageDigest md = MessageDigest.getInstance(algorithmName[k]);
148                ByteArrayOutputStream bos = new ByteArrayOutputStream(MY_MESSAGE_LEN);
149                DigestOutputStream dos = new DigestOutputStream(bos, md);
150                for (int i=0; i<MY_MESSAGE_LEN; i++) {
151                    dos.write(myMessage[i]);
152                }
153                // check that bytes have been written correctly
154                assertTrue("write", Arrays.equals(MDGoldenData.getMessage(), bos.toByteArray()));
155                // check that associated digest has been updated properly
156                assertTrue("update", Arrays.equals(dos.getMessageDigest().digest(),
157                                                   MDGoldenData.getDigest(algorithmName[k])));
158                return;
159            } catch (NoSuchAlgorithmException e) {
160                // allowed failure
161            }
162        }
163        fail(getName() + ": no MessageDigest algorithms available - test not performed");
164    }
165
166    /**
167     * Test #2 for <code>write(int)</code> method<br>
168     * Test #1 for <code>on(boolean)</code> method<br>
169     *
170     * Assertion: <code>write(int)</code> must not update digest if it is off<br>
171     * Assertion: <code>on(boolean)</code> turns digest functionality on
172     * if <code>true</code> passed as a parameter or off if <code>false</code>
173     * passed
174     */
175    public final void testWriteint02()
176        throws IOException {
177        for (int k=0; k<algorithmName.length; k++) {
178            try {
179                MessageDigest md = MessageDigest.getInstance(algorithmName[k]);
180                ByteArrayOutputStream bos = new ByteArrayOutputStream(MY_MESSAGE_LEN);
181                DigestOutputStream dos = new DigestOutputStream(bos, md);
182
183                // turn digest off
184                dos.on(false);
185
186                for (int i=0; i<MY_MESSAGE_LEN; i++) {
187                    dos.write(myMessage[i]);
188                }
189
190                // check that bytes have been written correctly
191                assertTrue("write", Arrays.equals(MDGoldenData.getMessage(), bos.toByteArray()));
192                // check that digest value has not been updated by write()
193                assertTrue("update", Arrays.equals(dos.getMessageDigest().digest(),
194                                                   MDGoldenData.getDigest(algorithmName[k]+"_NU")));
195                return;
196            } catch (NoSuchAlgorithmException e) {
197                // allowed failure
198            }
199        }
200        fail(getName() + ": no MessageDigest algorithms available - test not performed");
201    }
202
203    /**
204     * Test #3 for <code>write(int)</code> method<br>
205     *
206     * Assertion: broken <code>DigestOutputStream</code>instance:
207     * <code>OutputStream</code> not set. <code>write(int)</code> must
208     * not work
209     */
210    public final void testWriteint03() throws IOException {
211        for (int k=0; k<algorithmName.length; k++) {
212            try {
213                MessageDigest md = MessageDigest.getInstance(algorithmName[k]);
214                DigestOutputStream dos = new DigestOutputStream(null, md);
215                // must result in an exception
216                try {
217                    for (int i=0; i<MY_MESSAGE_LEN; i++) {
218                        dos.write(myMessage[i]);
219                    }
220                    fail("OutputStream not set. write(int) must not work");
221                } catch (Exception e) {
222                    return;
223                }
224            } catch (NoSuchAlgorithmException e) {
225                // allowed failure
226            }
227        }
228        fail(getName() + ": no MessageDigest algorithms available - test not performed");
229    }
230
231    /**
232     * Test #4 for <code>write(int)</code> method<br>
233     *
234     * Assertion: broken <code>DigestOutputStream</code>instance:
235     * associated <code>MessageDigest</code> not set.
236     * <code>write(int)</code> must not work when digest
237     * functionality is on
238     */
239    public final void testWriteint04() throws IOException {
240        OutputStream os = new ByteArrayOutputStream(MY_MESSAGE_LEN);
241        DigestOutputStream dos = new DigestOutputStream(os, null);
242
243        // must result in an exception
244        try {
245            for (int i=0; i<MY_MESSAGE_LEN; i++) {
246                dos.write(myMessage[i]);
247            }
248            fail("OutputStream not set. write(int) must not work");
249        } catch (Exception e) {
250            return;
251        }
252    }
253
254    /**
255     * Test #5 for <code>write(int)</code> method<br>
256     * Test #2 for <code>on(boolean)</code> method<br>
257     *
258     * Assertion: broken <code>DigestOutputStream</code>instance:
259     * associated <code>MessageDigest</code> not set.
260     * <code>write(int)</code> must work when digest
261     * functionality is off
262     */
263    public final void testWriteint05() throws IOException {
264        ByteArrayOutputStream bos = new ByteArrayOutputStream(MY_MESSAGE_LEN);
265        DigestOutputStream dos = new DigestOutputStream(bos, null);
266        // set digest functionality to off
267        dos.on(false);
268        // the following must pass without any exception
269        for (int i=0; i<MY_MESSAGE_LEN; i++) {
270            dos.write(myMessage[i]);
271        }
272        // check that bytes have been written correctly
273        assertTrue(Arrays.equals(MDGoldenData.getMessage(), bos.toByteArray()));
274    }
275
276    /**
277     * Test #1 for <code>write(byte[],int,int)</code> method<br>
278     *
279     * Assertion: put bytes into output stream<br>
280     *
281     * Assertion: updates associated digest<br>
282     */
283    public final void test_write$BII_1() throws IOException {
284        for (int k=0; k<algorithmName.length; k++) {
285            try {
286                ByteArrayOutputStream bos = new ByteArrayOutputStream(MY_MESSAGE_LEN);
287                MessageDigest md = MessageDigest.getInstance(algorithmName[k]);
288                DigestOutputStream dos = new DigestOutputStream(bos, md);
289
290                // write message at once
291                dos.write(myMessage, 0, MY_MESSAGE_LEN);
292
293                // check write
294                assertTrue("write", Arrays.equals(myMessage, bos.toByteArray()));
295                // check that associated digest has been updated properly
296                assertTrue("update", Arrays.equals(dos.getMessageDigest().digest(),
297                                                   MDGoldenData.getDigest(algorithmName[k])));
298                return;
299            } catch (NoSuchAlgorithmException e) {
300                // allowed failure
301            }
302        }
303        fail(getName() + ": no MessageDigest algorithms available - test not performed");
304    }
305
306    /**
307     * Test #2 for <code>write(byte[],int,int)</code> method<br>
308     *
309     * Assertion: put bytes into output stream<br>
310     *
311     * Assertion: updates associated digest<br>
312     */
313    public final void test_write$BII_2() throws IOException {
314        // check precondition
315        assertEquals(0, MY_MESSAGE_LEN % CHUNK_SIZE);
316        for (int k=0; k<algorithmName.length; k++) {
317            try {
318
319                ByteArrayOutputStream bos = new ByteArrayOutputStream(MY_MESSAGE_LEN);
320                MessageDigest md = MessageDigest.getInstance(algorithmName[k]);
321                DigestOutputStream dos = new DigestOutputStream(bos, md);
322
323                // write message by chunks
324                for (int i=0; i<MY_MESSAGE_LEN/CHUNK_SIZE; i++) {
325                    dos.write(myMessage, i*CHUNK_SIZE, CHUNK_SIZE);
326                }
327                // check write
328                assertTrue("write", Arrays.equals(myMessage, bos.toByteArray()));
329                // check that associated digest has been updated properly
330                assertTrue("update", Arrays.equals(dos.getMessageDigest().digest(),
331                        MDGoldenData.getDigest(algorithmName[k])));
332                return;
333            } catch (NoSuchAlgorithmException e) {
334                // allowed failure
335            }
336        }
337        fail(getName() + ": no MessageDigest algorithms available - test not performed");
338    }
339
340
341    /**
342     * Test #3 for <code>write(byte[],int,int)</code> method<br>
343     *
344     * Assertion: put bytes into output stream<br>
345     *
346     * Assertion: updates associated digest<br>
347     */
348    public final void test_write$BII_3()
349        throws NoSuchAlgorithmException,
350               IOException {
351        // check precondition
352        assertTrue(MY_MESSAGE_LEN % (CHUNK_SIZE+1) != 0);
353
354        for (int k=0; k<algorithmName.length; k++) {
355            try {
356                ByteArrayOutputStream bos = new ByteArrayOutputStream(MY_MESSAGE_LEN);
357                MessageDigest md = MessageDigest.getInstance(algorithmName[k]);
358                DigestOutputStream dos = new DigestOutputStream(bos, md);
359
360                // write message by chunks
361                for (int i=0; i<MY_MESSAGE_LEN/(CHUNK_SIZE+1); i++) {
362                    dos.write(myMessage, i*(CHUNK_SIZE+1), CHUNK_SIZE+1);
363                }
364                // write remaining bytes
365                dos.write(myMessage,
366                        MY_MESSAGE_LEN/(CHUNK_SIZE+1)*(CHUNK_SIZE+1),
367                        MY_MESSAGE_LEN % (CHUNK_SIZE+1));
368                // check write
369                assertTrue("write", Arrays.equals(myMessage, bos.toByteArray()));
370                // check that associated digest has been updated properly
371                assertTrue("update", Arrays.equals(dos.getMessageDigest().digest(),
372                        MDGoldenData.getDigest(algorithmName[k])));
373                return;
374            } catch (NoSuchAlgorithmException e) {
375                // allowed failure
376            }
377        }
378        fail(getName() + ": no MessageDigest algorithms available - test not performed");
379    }
380
381    /**
382     * Test #4 for <code>write(byte[],int,int)</code> method<br>
383     *
384     * Assertion: put bytes into output stream<br>
385     *
386     * Assertion: does not update associated digest if digest
387     * functionality is off<br>
388     */
389    public final void test_write$BII_4()
390        throws NoSuchAlgorithmException,
391               IOException {
392        // check precondition
393        assertEquals(0, MY_MESSAGE_LEN % CHUNK_SIZE);
394
395        for (int k=0; k<algorithmName.length; k++) {
396            try {
397                ByteArrayOutputStream bos = new ByteArrayOutputStream(MY_MESSAGE_LEN);
398                MessageDigest md = MessageDigest.getInstance(algorithmName[k]);
399                DigestOutputStream dos = new DigestOutputStream(bos, md);
400
401                // set digest functionality off
402                dos.on(false);
403
404                // write message by chunks
405                for (int i=0; i<MY_MESSAGE_LEN/CHUNK_SIZE; i++) {
406                    dos.write(myMessage, i*CHUNK_SIZE, CHUNK_SIZE);
407                }
408
409                // check write
410                assertTrue("write", Arrays.equals(myMessage, bos.toByteArray()));
411                // check that associated digest has not been updated
412                assertTrue("update", Arrays.equals(dos.getMessageDigest().digest(),
413                        MDGoldenData.getDigest(algorithmName[k]+"_NU")));
414                return;
415            } catch (NoSuchAlgorithmException e) {
416                // allowed failure
417            }
418        }
419        fail(getName() + ": no MessageDigest algorithms available - test not performed");
420    }
421
422    /**
423     * java.security.DigestOutputStream#write(byte[], int, int)
424     */
425    public void test_write$BII_6() throws Exception {
426
427        // Regression form HARMONY-1091.
428        MessageDigest md = new MyMessageDigest1();
429        byte[] bytes = new byte[] { 1, 2 };
430        DigestOutputStream dig = new DigestOutputStream(
431                new ByteArrayOutputStream(), md);
432        // buf == null
433        try {
434            dig.write(null, -1, 0);
435            fail("No expected IllegalArgumentException");
436        } catch (IllegalArgumentException e) {
437        }
438        // offset + len > buf.length
439        try {
440            dig.write(bytes, 0, bytes.length + 1);
441            fail("No expected IllegalArgumentException");
442        } catch (IllegalArgumentException e) {
443        }
444        // offset < 0
445        try {
446            dig.write(bytes, -1, 1);
447            fail("No expected IndexOutOfBoundsException");
448        } catch (IndexOutOfBoundsException e) {
449        }
450        // len < 0
451        try {
452            dig.write(bytes, 0, -1);
453            fail("No expected IndexOutOfBoundsException");
454        } catch (IndexOutOfBoundsException e) {
455        }
456    }
457
458    /**
459     * java.io.DigestOutputStream#write(byte[], int, int)
460     */
461    public void test_write$BII_7()
462        throws IOException, NoSuchAlgorithmException {
463        Support_OutputStream sos = new Support_OutputStream(MY_MESSAGE_LEN);
464        MessageDigest md = MessageDigest.getInstance(algorithmName[0]);
465        DigestOutputStream dos = new DigestOutputStream(sos, md);
466
467        dos.write(myMessage, 0, MY_MESSAGE_LEN);
468
469        try {
470            // Support_OutputStream throws an IOException if the internal
471            // buffer is full, which it should be now.
472            dos.write(myMessage, 0, MY_MESSAGE_LEN);
473            fail("Test 1: IOException expected.");
474        } catch (IOException e) {
475            // Expected.
476        }
477    }
478
479    /**
480     * Test for <code>on()</code> method<br>
481     * Assertion: turns digest functionality on or off
482     */
483    public final void testOn() throws IOException {
484        for (int k=0; k<algorithmName.length; k++) {
485            try {
486                ByteArrayOutputStream bos = new ByteArrayOutputStream(MY_MESSAGE_LEN);
487                MessageDigest md = MessageDigest.getInstance(algorithmName[k]);
488                DigestOutputStream dos = new DigestOutputStream(bos, md);
489
490                // turn digest off
491                dos.on(false);
492
493                for (int i=0; i<MY_MESSAGE_LEN-1; i++) {
494                    dos.write(myMessage[i]);
495                }
496
497                // turn digest on
498                dos.on(true);
499
500                // read remaining byte
501                dos.write(myMessage[MY_MESSAGE_LEN-1]);
502
503                byte[] digest = dos.getMessageDigest().digest();
504
505                // check that digest value has been
506                // updated by the last write(int) call
507                assertFalse(Arrays.equals(digest,MDGoldenData.getDigest(algorithmName[k])));
508                assertFalse(Arrays.equals(digest,MDGoldenData.getDigest(algorithmName[k]+"_NU")));
509                return;
510            } catch (NoSuchAlgorithmException e) {
511                // allowed failure
512            }
513        }
514        fail(getName() + ": no MessageDigest algorithms available - test not performed");
515    }
516
517    /**
518     * Test for <code>toString()</code> method<br>
519     * Assertion: returns <code>String</code> representation of this object
520     */
521    public final void testToString() throws NoSuchAlgorithmException {
522        for (int k=0; k<algorithmName.length; k++) {
523            try {
524                ByteArrayOutputStream bos = new ByteArrayOutputStream(MY_MESSAGE_LEN);
525                MessageDigest md = MessageDigest.getInstance(algorithmName[k]);
526                DigestOutputStream dos = new DigestOutputStream(bos, md);
527
528                assertNotNull(dos.toString());
529                return;
530            } catch (NoSuchAlgorithmException e) {
531                // allowed failure
532            }
533        }
534        fail(getName() + ": no MessageDigest algorithms available - test not performed");
535    }
536
537    /**
538     * java.security.DigestOutputStream#on(boolean)
539     */
540    public void test_onZ() throws Exception {
541        // Test for method void java.security.DigestOutputStream.on(boolean)
542        DigestOutputStream dos = new DigestOutputStream(
543                new ByteArrayOutputStream(), MessageDigest.getInstance("SHA"));
544        dos.on(false);
545        byte digestArray[] = { 23, 43, 44 };
546        dos.write(digestArray, 1, 1);
547        byte digestResult[] = dos.getMessageDigest().digest();
548        byte expected[] = { -38, 57, -93, -18, 94, 107, 75, 13, 50, 85,
549                -65, -17, -107, 96, 24, -112, -81, -40, 7, 9 };
550        assertTrue("Digest did not return expected result.",
551                   Arrays.equals(digestResult, expected));
552        // now turn on processing and re-run
553        dos.on(true);
554        dos.write(digestArray, 1, 1);
555        digestResult = dos.getMessageDigest().digest();
556        byte expected1[] = { -87, 121, -17, 16, -52, 111, 106, 54, -33,
557                107, -118, 50, 51, 7, -18, 59, -78, -30, -37, -100 };
558
559        assertTrue("Digest did not return expected result.",
560                   Arrays.equals(digestResult, expected1));
561    }
562
563    /**
564     * java.security.DigestOutputStream#write(byte[], int, int)
565     */
566    public void test_write$BII_5() throws Exception {
567        // Test for method void java.security.DigestOutputStream.write(byte [],
568        // int, int)
569            DigestOutputStream dos = new DigestOutputStream(
570                new ByteArrayOutputStream(), MessageDigest.getInstance("SHA"));
571            byte digestArray[] = { 23, 43, 44 };
572            dos.write(digestArray, 1, 1);
573            byte digestResult[] = dos.getMessageDigest().digest();
574            byte expected[] = { -87, 121, -17, 16, -52, 111, 106, 54, -33, 107,
575                    -118, 50, 51, 7, -18, 59, -78, -30, -37, -100 };
576
577            assertTrue("Digest did not return expected result.",
578                       Arrays.equals(digestResult, expected));
579    }
580
581    /**
582     * java.security.DigestOutputStream#write(int)
583     */
584    public void test_writeI() throws Exception {
585        // Test for method void java.security.DigestOutputStream.write(int)
586            DigestOutputStream dos = new DigestOutputStream(
587                new ByteArrayOutputStream(), MessageDigest.getInstance("SHA"));
588            dos.write((byte) 43);
589            byte digestResult[] = dos.getMessageDigest().digest();
590            byte expected[] = { -87, 121, -17, 16, -52, 111, 106, 54, -33, 107,
591                    -118, 50, 51, 7, -18, 59, -78, -30, -37, -100 };
592
593            assertTrue("Digest did not return expected result.",
594                       Arrays.equals(digestResult, expected));
595    }
596
597
598    private class MyOutputStream extends OutputStream {
599        @Override
600        public void write(int arg0) throws IOException {
601        }
602    }
603
604    private class MyDigestOutputStream extends DigestOutputStream {
605        public MyDigestOutputStream(OutputStream out, MessageDigest digest) {
606            super(out, digest);
607        }
608
609        public MessageDigest myMessageDigest() {
610            return digest;
611        }
612
613        public OutputStream myOutputStream() {
614            return out;
615        }
616    }
617}
618