/* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ /** * @author Vladimir N. Molotkov * @version $Revision$ */ package org.apache.harmony.security.tests.java.security; import java.io.ByteArrayOutputStream; import java.io.IOException; import java.io.OutputStream; import java.security.DigestOutputStream; import java.security.MessageDigest; import java.security.NoSuchAlgorithmException; import java.util.Arrays; import junit.framework.TestCase; import org.apache.harmony.security.tests.support.MDGoldenData; import org.apache.harmony.security.tests.support.MyMessageDigest1; import tests.support.Support_OutputStream; /** * Tests for fields and methods of class <code>DigestInputStream</code> * */ public class DigestOutputStreamTest extends TestCase { /** * Message digest algorithm name used during testing */ private static final String algorithmName[] = { "SHA-1", "SHA", "SHA1", "SHA-256", "SHA-384", "SHA-512", "MD5", }; /** * Chunk size for read(byte, off, len) tests */ private static final int CHUNK_SIZE = 32; /** * Test message for digest computations */ private static final byte[] myMessage = MDGoldenData.getMessage(); /** * The length of test message */ private static final int MY_MESSAGE_LEN = myMessage.length; // // Tests // /** * java.security.DigestOutputStream#DigestOutputStream(java.io.OutputStream, * java.security.MessageDigest) */ public void test_CtorLjava_io_OutputStreamLjava_security_MessageDigest() { // non-null parameters MessageDigest md = new MyMessageDigest1(); MyOutputStream out = new MyOutputStream(); MyDigestOutputStream dos = new MyDigestOutputStream(out, md); assertSame(out, dos.myOutputStream()); assertSame(md, dos.myMessageDigest()); // null parameters dos = new MyDigestOutputStream(null, null); assertNull(dos.myOutputStream()); assertNull(dos.myMessageDigest()); dos = new MyDigestOutputStream(null, md); assertNull(dos.myOutputStream()); assertNotNull(dos.myMessageDigest()); dos = new MyDigestOutputStream(out, null); assertNotNull(dos.myOutputStream()); assertNull(dos.myMessageDigest()); } /** * java.security.DigestOutputStream#getMessageDigest() */ public void test_getMessageDigest() { MessageDigest digest = new MyMessageDigest1(); OutputStream out = new MyOutputStream(); // non-null parameter DigestOutputStream dos = new DigestOutputStream(out, digest); assertSame(digest, dos.getMessageDigest()); // null parameter dos = new DigestOutputStream(out, null); assertNull("getMessageDigest should have returned null", dos.getMessageDigest()); } /** * java.security.DigestOutputStream#setMessageDigest(MessageDigest) */ public void test_setMessageDigestLjava_security_MessageDigest() { MessageDigest digest = new MyMessageDigest1(); OutputStream out = new MyOutputStream(); DigestOutputStream dos = new DigestOutputStream(out, null); // non-null parameter dos.setMessageDigest(digest); assertSame(digest, dos.getMessageDigest()); // null parameter dos.setMessageDigest(null); assertNull("getMessageDigest should have returned null", dos.getMessageDigest()); } /** * Test #1 for <code>write(int)</code> method<br> * * Assertion: writes the byte to the output stream<br> * Assertion: updates associated digest<br> */ public final void testWriteint01() throws IOException { for (int k=0; k<algorithmName.length; k++) { try { MessageDigest md = MessageDigest.getInstance(algorithmName[k]); ByteArrayOutputStream bos = new ByteArrayOutputStream(MY_MESSAGE_LEN); DigestOutputStream dos = new DigestOutputStream(bos, md); for (int i=0; i<MY_MESSAGE_LEN; i++) { dos.write(myMessage[i]); } // check that bytes have been written correctly assertTrue("write", Arrays.equals(MDGoldenData.getMessage(), bos.toByteArray())); // check that associated digest has been updated properly assertTrue("update", Arrays.equals(dos.getMessageDigest().digest(), MDGoldenData.getDigest(algorithmName[k]))); return; } catch (NoSuchAlgorithmException e) { // allowed failure } } fail(getName() + ": no MessageDigest algorithms available - test not performed"); } /** * Test #2 for <code>write(int)</code> method<br> * Test #1 for <code>on(boolean)</code> method<br> * * Assertion: <code>write(int)</code> must not update digest if it is off<br> * Assertion: <code>on(boolean)</code> turns digest functionality on * if <code>true</code> passed as a parameter or off if <code>false</code> * passed */ public final void testWriteint02() throws IOException { for (int k=0; k<algorithmName.length; k++) { try { MessageDigest md = MessageDigest.getInstance(algorithmName[k]); ByteArrayOutputStream bos = new ByteArrayOutputStream(MY_MESSAGE_LEN); DigestOutputStream dos = new DigestOutputStream(bos, md); // turn digest off dos.on(false); for (int i=0; i<MY_MESSAGE_LEN; i++) { dos.write(myMessage[i]); } // check that bytes have been written correctly assertTrue("write", Arrays.equals(MDGoldenData.getMessage(), bos.toByteArray())); // check that digest value has not been updated by write() assertTrue("update", Arrays.equals(dos.getMessageDigest().digest(), MDGoldenData.getDigest(algorithmName[k]+"_NU"))); return; } catch (NoSuchAlgorithmException e) { // allowed failure } } fail(getName() + ": no MessageDigest algorithms available - test not performed"); } /** * Test #3 for <code>write(int)</code> method<br> * * Assertion: broken <code>DigestOutputStream</code>instance: * <code>OutputStream</code> not set. <code>write(int)</code> must * not work */ public final void testWriteint03() throws IOException { for (int k=0; k<algorithmName.length; k++) { try { MessageDigest md = MessageDigest.getInstance(algorithmName[k]); DigestOutputStream dos = new DigestOutputStream(null, md); // must result in an exception try { for (int i=0; i<MY_MESSAGE_LEN; i++) { dos.write(myMessage[i]); } fail("OutputStream not set. write(int) must not work"); } catch (Exception e) { return; } } catch (NoSuchAlgorithmException e) { // allowed failure } } fail(getName() + ": no MessageDigest algorithms available - test not performed"); } /** * Test #4 for <code>write(int)</code> method<br> * * Assertion: broken <code>DigestOutputStream</code>instance: * associated <code>MessageDigest</code> not set. * <code>write(int)</code> must not work when digest * functionality is on */ public final void testWriteint04() throws IOException { OutputStream os = new ByteArrayOutputStream(MY_MESSAGE_LEN); DigestOutputStream dos = new DigestOutputStream(os, null); // must result in an exception try { for (int i=0; i<MY_MESSAGE_LEN; i++) { dos.write(myMessage[i]); } fail("OutputStream not set. write(int) must not work"); } catch (Exception e) { return; } } /** * Test #5 for <code>write(int)</code> method<br> * Test #2 for <code>on(boolean)</code> method<br> * * Assertion: broken <code>DigestOutputStream</code>instance: * associated <code>MessageDigest</code> not set. * <code>write(int)</code> must work when digest * functionality is off */ public final void testWriteint05() throws IOException { ByteArrayOutputStream bos = new ByteArrayOutputStream(MY_MESSAGE_LEN); DigestOutputStream dos = new DigestOutputStream(bos, null); // set digest functionality to off dos.on(false); // the following must pass without any exception for (int i=0; i<MY_MESSAGE_LEN; i++) { dos.write(myMessage[i]); } // check that bytes have been written correctly assertTrue(Arrays.equals(MDGoldenData.getMessage(), bos.toByteArray())); } /** * Test #1 for <code>write(byte[],int,int)</code> method<br> * * Assertion: put bytes into output stream<br> * * Assertion: updates associated digest<br> */ public final void test_write$BII_1() throws IOException { for (int k=0; k<algorithmName.length; k++) { try { ByteArrayOutputStream bos = new ByteArrayOutputStream(MY_MESSAGE_LEN); MessageDigest md = MessageDigest.getInstance(algorithmName[k]); DigestOutputStream dos = new DigestOutputStream(bos, md); // write message at once dos.write(myMessage, 0, MY_MESSAGE_LEN); // check write assertTrue("write", Arrays.equals(myMessage, bos.toByteArray())); // check that associated digest has been updated properly assertTrue("update", Arrays.equals(dos.getMessageDigest().digest(), MDGoldenData.getDigest(algorithmName[k]))); return; } catch (NoSuchAlgorithmException e) { // allowed failure } } fail(getName() + ": no MessageDigest algorithms available - test not performed"); } /** * Test #2 for <code>write(byte[],int,int)</code> method<br> * * Assertion: put bytes into output stream<br> * * Assertion: updates associated digest<br> */ public final void test_write$BII_2() throws IOException { // check precondition assertEquals(0, MY_MESSAGE_LEN % CHUNK_SIZE); for (int k=0; k<algorithmName.length; k++) { try { ByteArrayOutputStream bos = new ByteArrayOutputStream(MY_MESSAGE_LEN); MessageDigest md = MessageDigest.getInstance(algorithmName[k]); DigestOutputStream dos = new DigestOutputStream(bos, md); // write message by chunks for (int i=0; i<MY_MESSAGE_LEN/CHUNK_SIZE; i++) { dos.write(myMessage, i*CHUNK_SIZE, CHUNK_SIZE); } // check write assertTrue("write", Arrays.equals(myMessage, bos.toByteArray())); // check that associated digest has been updated properly assertTrue("update", Arrays.equals(dos.getMessageDigest().digest(), MDGoldenData.getDigest(algorithmName[k]))); return; } catch (NoSuchAlgorithmException e) { // allowed failure } } fail(getName() + ": no MessageDigest algorithms available - test not performed"); } /** * Test #3 for <code>write(byte[],int,int)</code> method<br> * * Assertion: put bytes into output stream<br> * * Assertion: updates associated digest<br> */ public final void test_write$BII_3() throws NoSuchAlgorithmException, IOException { // check precondition assertTrue(MY_MESSAGE_LEN % (CHUNK_SIZE+1) != 0); for (int k=0; k<algorithmName.length; k++) { try { ByteArrayOutputStream bos = new ByteArrayOutputStream(MY_MESSAGE_LEN); MessageDigest md = MessageDigest.getInstance(algorithmName[k]); DigestOutputStream dos = new DigestOutputStream(bos, md); // write message by chunks for (int i=0; i<MY_MESSAGE_LEN/(CHUNK_SIZE+1); i++) { dos.write(myMessage, i*(CHUNK_SIZE+1), CHUNK_SIZE+1); } // write remaining bytes dos.write(myMessage, MY_MESSAGE_LEN/(CHUNK_SIZE+1)*(CHUNK_SIZE+1), MY_MESSAGE_LEN % (CHUNK_SIZE+1)); // check write assertTrue("write", Arrays.equals(myMessage, bos.toByteArray())); // check that associated digest has been updated properly assertTrue("update", Arrays.equals(dos.getMessageDigest().digest(), MDGoldenData.getDigest(algorithmName[k]))); return; } catch (NoSuchAlgorithmException e) { // allowed failure } } fail(getName() + ": no MessageDigest algorithms available - test not performed"); } /** * Test #4 for <code>write(byte[],int,int)</code> method<br> * * Assertion: put bytes into output stream<br> * * Assertion: does not update associated digest if digest * functionality is off<br> */ public final void test_write$BII_4() throws NoSuchAlgorithmException, IOException { // check precondition assertEquals(0, MY_MESSAGE_LEN % CHUNK_SIZE); for (int k=0; k<algorithmName.length; k++) { try { ByteArrayOutputStream bos = new ByteArrayOutputStream(MY_MESSAGE_LEN); MessageDigest md = MessageDigest.getInstance(algorithmName[k]); DigestOutputStream dos = new DigestOutputStream(bos, md); // set digest functionality off dos.on(false); // write message by chunks for (int i=0; i<MY_MESSAGE_LEN/CHUNK_SIZE; i++) { dos.write(myMessage, i*CHUNK_SIZE, CHUNK_SIZE); } // check write assertTrue("write", Arrays.equals(myMessage, bos.toByteArray())); // check that associated digest has not been updated assertTrue("update", Arrays.equals(dos.getMessageDigest().digest(), MDGoldenData.getDigest(algorithmName[k]+"_NU"))); return; } catch (NoSuchAlgorithmException e) { // allowed failure } } fail(getName() + ": no MessageDigest algorithms available - test not performed"); } /** * java.security.DigestOutputStream#write(byte[], int, int) */ public void test_write$BII_6() throws Exception { // Regression form HARMONY-1091. MessageDigest md = new MyMessageDigest1(); byte[] bytes = new byte[] { 1, 2 }; DigestOutputStream dig = new DigestOutputStream( new ByteArrayOutputStream(), md); // buf == null try { dig.write(null, -1, 0); fail("No expected IllegalArgumentException"); } catch (IllegalArgumentException e) { } // offset + len > buf.length try { dig.write(bytes, 0, bytes.length + 1); fail("No expected IllegalArgumentException"); } catch (IllegalArgumentException e) { } // offset < 0 try { dig.write(bytes, -1, 1); fail("No expected IndexOutOfBoundsException"); } catch (IndexOutOfBoundsException e) { } // len < 0 try { dig.write(bytes, 0, -1); fail("No expected IndexOutOfBoundsException"); } catch (IndexOutOfBoundsException e) { } } /** * java.io.DigestOutputStream#write(byte[], int, int) */ public void test_write$BII_7() throws IOException, NoSuchAlgorithmException { Support_OutputStream sos = new Support_OutputStream(MY_MESSAGE_LEN); MessageDigest md = MessageDigest.getInstance(algorithmName[0]); DigestOutputStream dos = new DigestOutputStream(sos, md); dos.write(myMessage, 0, MY_MESSAGE_LEN); try { // Support_OutputStream throws an IOException if the internal // buffer is full, which it should be now. dos.write(myMessage, 0, MY_MESSAGE_LEN); fail("Test 1: IOException expected."); } catch (IOException e) { // Expected. } } /** * Test for <code>on()</code> method<br> * Assertion: turns digest functionality on or off */ public final void testOn() throws IOException { for (int k=0; k<algorithmName.length; k++) { try { ByteArrayOutputStream bos = new ByteArrayOutputStream(MY_MESSAGE_LEN); MessageDigest md = MessageDigest.getInstance(algorithmName[k]); DigestOutputStream dos = new DigestOutputStream(bos, md); // turn digest off dos.on(false); for (int i=0; i<MY_MESSAGE_LEN-1; i++) { dos.write(myMessage[i]); } // turn digest on dos.on(true); // read remaining byte dos.write(myMessage[MY_MESSAGE_LEN-1]); byte[] digest = dos.getMessageDigest().digest(); // check that digest value has been // updated by the last write(int) call assertFalse(Arrays.equals(digest,MDGoldenData.getDigest(algorithmName[k]))); assertFalse(Arrays.equals(digest,MDGoldenData.getDigest(algorithmName[k]+"_NU"))); return; } catch (NoSuchAlgorithmException e) { // allowed failure } } fail(getName() + ": no MessageDigest algorithms available - test not performed"); } /** * Test for <code>toString()</code> method<br> * Assertion: returns <code>String</code> representation of this object */ public final void testToString() throws NoSuchAlgorithmException { for (int k=0; k<algorithmName.length; k++) { try { ByteArrayOutputStream bos = new ByteArrayOutputStream(MY_MESSAGE_LEN); MessageDigest md = MessageDigest.getInstance(algorithmName[k]); DigestOutputStream dos = new DigestOutputStream(bos, md); assertNotNull(dos.toString()); return; } catch (NoSuchAlgorithmException e) { // allowed failure } } fail(getName() + ": no MessageDigest algorithms available - test not performed"); } /** * java.security.DigestOutputStream#on(boolean) */ public void test_onZ() throws Exception { // Test for method void java.security.DigestOutputStream.on(boolean) DigestOutputStream dos = new DigestOutputStream( new ByteArrayOutputStream(), MessageDigest.getInstance("SHA")); dos.on(false); byte digestArray[] = { 23, 43, 44 }; dos.write(digestArray, 1, 1); byte digestResult[] = dos.getMessageDigest().digest(); byte expected[] = { -38, 57, -93, -18, 94, 107, 75, 13, 50, 85, -65, -17, -107, 96, 24, -112, -81, -40, 7, 9 }; assertTrue("Digest did not return expected result.", Arrays.equals(digestResult, expected)); // now turn on processing and re-run dos.on(true); dos.write(digestArray, 1, 1); digestResult = dos.getMessageDigest().digest(); byte expected1[] = { -87, 121, -17, 16, -52, 111, 106, 54, -33, 107, -118, 50, 51, 7, -18, 59, -78, -30, -37, -100 }; assertTrue("Digest did not return expected result.", Arrays.equals(digestResult, expected1)); } /** * java.security.DigestOutputStream#write(byte[], int, int) */ public void test_write$BII_5() throws Exception { // Test for method void java.security.DigestOutputStream.write(byte [], // int, int) DigestOutputStream dos = new DigestOutputStream( new ByteArrayOutputStream(), MessageDigest.getInstance("SHA")); byte digestArray[] = { 23, 43, 44 }; dos.write(digestArray, 1, 1); byte digestResult[] = dos.getMessageDigest().digest(); byte expected[] = { -87, 121, -17, 16, -52, 111, 106, 54, -33, 107, -118, 50, 51, 7, -18, 59, -78, -30, -37, -100 }; assertTrue("Digest did not return expected result.", Arrays.equals(digestResult, expected)); } /** * java.security.DigestOutputStream#write(int) */ public void test_writeI() throws Exception { // Test for method void java.security.DigestOutputStream.write(int) DigestOutputStream dos = new DigestOutputStream( new ByteArrayOutputStream(), MessageDigest.getInstance("SHA")); dos.write((byte) 43); byte digestResult[] = dos.getMessageDigest().digest(); byte expected[] = { -87, 121, -17, 16, -52, 111, 106, 54, -33, 107, -118, 50, 51, 7, -18, 59, -78, -30, -37, -100 }; assertTrue("Digest did not return expected result.", Arrays.equals(digestResult, expected)); } private class MyOutputStream extends OutputStream { @Override public void write(int arg0) throws IOException { } } private class MyDigestOutputStream extends DigestOutputStream { public MyDigestOutputStream(OutputStream out, MessageDigest digest) { super(out, digest); } public MessageDigest myMessageDigest() { return digest; } public OutputStream myOutputStream() { return out; } } }