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