1 /*
   2  * Copyright (c) 2003, 2014, Oracle and/or its affiliates. All rights reserved.
   3  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
   4  *
   5  * This code is free software; you can redistribute it and/or modify it
   6  * under the terms of the GNU General Public License version 2 only, as
   7  * published by the Free Software Foundation.
   8  *
   9  * This code is distributed in the hope that it will be useful, but WITHOUT
  10  * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
  11  * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
  12  * version 2 for more details (a copy is included in the LICENSE file that
  13  * accompanied this code).
  14  *
  15  * You should have received a copy of the GNU General Public License version
  16  * 2 along with this work; if not, write to the Free Software Foundation,
  17  * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
  18  *
  19  * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
  20  * or visit www.oracle.com if you need additional information or have any
  21  * questions.
  22  */
  23 
  24 import java.io.ByteArrayInputStream;
  25 import java.io.ByteArrayOutputStream;
  26 import java.io.PrintStream;
  27 import java.security.DigestInputStream;
  28 import java.security.DigestOutputStream;
  29 import java.security.MessageDigest;
  30 import java.util.Arrays;
  31 import java.util.Random;
  32 import static java.lang.System.out;
  33 
  34 /**
  35  * @test
  36  * @bug 8050370
  37  * @summary MessageDigest tests with DigestIOStream
  38  * @author Kevin Liu
  39  * @key randomness
  40  */
  41 
  42 enum ReadModel {
  43     READ, BUFFER_READ, MIX_READ
  44 }
  45 
  46 public class TestDigestIOStream {
  47 
  48     private static final int[] DATA_LEN_ARRAY = {
  49         1, 50, 2500, 125000, 6250000
  50     };
  51     private static final String[] ALGORITHM_ARRAY = {
  52         "MD2", "MD5", "SHA1", "SHA-224", "SHA-256", "SHA-384", "SHA-512"
  53     };
  54 
  55     private static byte[] data;
  56 
  57     private static MessageDigest md = null;
  58 
  59     public static void main(String argv[]) throws Exception {
  60         TestDigestIOStream test = new TestDigestIOStream();
  61         test.run();
  62     }
  63 
  64     public void run() throws Exception {
  65         for (String algorithm: ALGORITHM_ARRAY) {
  66 
  67             md = MessageDigest.getInstance(algorithm);
  68 
  69             for (int length: DATA_LEN_ARRAY) {
  70 
  71                 Random rdm = new Random();
  72                 data = new byte[length];
  73                 rdm.nextBytes(data);
  74 
  75                 if (!testMDChange(algorithm, length)) {
  76                     throw new RuntimeException("testMDChange failed at:"
  77                             + algorithm + "/" + length);
  78                 }
  79                 if (!testMDShare(algorithm, length)) {
  80                     throw new RuntimeException("testMDShare failed at:"
  81                             + algorithm + "/" + length);
  82                 }
  83                 for (ReadModel readModel: ReadModel.values()) {
  84                     // test Digest function when digest switch on
  85                     if (!testDigestOnOff(algorithm, readModel, true, length)) {
  86                         throw new RuntimeException("testDigestOn failed at:"
  87                                 + algorithm + "/" + length + "/" + readModel);
  88                     }
  89                     // test Digest function when digest switch off
  90                     if (!testDigestOnOff(algorithm, readModel, false, length)) {
  91                         throw new RuntimeException("testDigestOff failed at:"
  92                                 + algorithm + "/" + length + "/" + readModel);
  93                     }
  94                 }
  95             }
  96         }
  97         int testNumber = ALGORITHM_ARRAY.length * ReadModel.values().length
  98                 * DATA_LEN_ARRAY.length * 2 + ALGORITHM_ARRAY.length
  99                 * DATA_LEN_ARRAY.length * 2;
 100         out.println("All " + testNumber + " Tests Passed");
 101     }
 102 
 103     /**
 104      * Test DigestInputStream and DigestOutputStream digest function when digest
 105      * set on and off
 106      *
 107      * @param algo
 108      *            Message Digest algorithm
 109      * @param readModel
 110      *            which read method used(READ, BUFFER_READ, MIX_READ)
 111      * @param on
 112      *            digest switch(on and off)
 113      * @param dataLength
 114      *            plain test data length.
 115      * @exception Exception
 116      *                throw unexpected exception
 117      */
 118     public boolean testDigestOnOff(String algo, ReadModel readModel,
 119             boolean on, int dataLength) throws Exception {
 120 
 121         // Generate the DigestInputStream/DigestOutputStream object
 122         try (ByteArrayInputStream bais = new ByteArrayInputStream(data);
 123                 DigestInputStream dis = new DigestInputStream(bais,
 124                         MessageDigest.getInstance(algo));
 125                 ByteArrayOutputStream baos = new ByteArrayOutputStream();
 126                 DigestOutputStream dos = new DigestOutputStream(baos,
 127                         MessageDigest.getInstance(algo));
 128                 ByteArrayOutputStream baOut = new ByteArrayOutputStream();) {
 129 
 130             // Perform the update using all available/possible update methods
 131             int k = 0;
 132             byte[] buffer = new byte[5];
 133             boolean enDigest = true;
 134             // Make sure the digest function is on (default)
 135             dis.on(enDigest);
 136             dos.on(enDigest);
 137 
 138             switch (readModel) {
 139             case READ: // use only read()
 140                 while ((k = dis.read()) != -1) {
 141                     if (on) {
 142                         dos.write(k);
 143                     } else {
 144                         dos.write(k);
 145                         if (enDigest) {
 146                             baOut.write(k);
 147                         }
 148                         enDigest = !enDigest;
 149                         dos.on(enDigest);
 150                         dis.on(enDigest);
 151                     }
 152                 }
 153                 break;
 154             case BUFFER_READ: // use only read(byte[], int, int)
 155                 while ((k = dis.read(buffer, 0, buffer.length)) != -1) {
 156                     if (on) {
 157                         dos.write(buffer, 0, k);
 158                     } else {
 159                         dos.write(buffer, 0, k);
 160                         if (enDigest) {
 161                             baOut.write(buffer, 0, k);
 162                         }
 163                         enDigest = !enDigest;
 164                         dis.on(enDigest);
 165                         dos.on(enDigest);
 166                     }
 167                 }
 168                 break;
 169             case MIX_READ: // use both read() and read(byte[], int, int)
 170                 while ((k = dis.read()) != -1) {
 171                     if (on) {
 172                         dos.write(k);
 173                         if ((k = dis.read(buffer, 0, buffer.length)) != -1) {
 174                             dos.write(buffer, 0, k);
 175                         }
 176                     } else {
 177                         dos.write(k);
 178                         if (enDigest) {
 179                             baOut.write(k);
 180                         }
 181                         enDigest = !enDigest;
 182                         dis.on(enDigest);
 183                         dos.on(enDigest);
 184                         if ((k = dis.read(buffer, 0, buffer.length)) != -1) {
 185                             dos.write(buffer, 0, k);
 186                             if (enDigest) {
 187                                 baOut.write(buffer, 0, k);
 188                             }
 189                             enDigest = !enDigest;
 190                             dis.on(enDigest);
 191                             dos.on(enDigest);
 192                         }
 193                     }
 194                 }
 195                 break;
 196             default:
 197                 out.println("ERROR: Invalid read/write combination choice!");
 198                 return false;
 199             }
 200 
 201             // Get the output and the "correct" digest values
 202             byte[] output1 = dis.getMessageDigest().digest();
 203             byte[] output2 = dos.getMessageDigest().digest();
 204             byte[] standard;
 205             if (on) {
 206                 standard = md.digest(data);
 207             } else {
 208                 byte[] dataDigested = baOut.toByteArray();
 209                 standard = md.digest(dataDigested);
 210             }
 211 
 212             // Compare the output byte array value to the input data
 213             if (!MessageDigest.isEqual(data, baos.toByteArray())) {
 214                 out.println("ERROR of " + readModel
 215                         + ": output and input data unexpectedly changed");
 216                 return false;
 217             }
 218             // Compare generated digest values
 219             if (!MessageDigest.isEqual(output1, standard)
 220                     || !MessageDigest.isEqual(output2, standard)) {
 221                 out.println("ERROR" + readModel
 222                         + ": generated digest data unexpectedly changed");
 223                 return false;
 224             }
 225 
 226             return true;
 227         } catch (Exception ex) {
 228             out.println("testDigestOnOff failed at:" + algo + "/" + readModel
 229                     + "/" + dataLength + " with unexpected exception");
 230             throw ex;
 231         }
 232     }
 233 
 234     /**
 235      * Test DigestInputStream and DigestOutputStream digest function when Swap
 236      * the message digest engines between DigestIn/OutputStream
 237      *
 238      * @param algo
 239      *            Message Digest algorithm
 240      * @param dataLength
 241      *            plain test data length.
 242      * @exception Exception
 243      *                throw unexpected exception
 244      */
 245     public boolean testMDChange(String algo, int dataLength) throws Exception {
 246         // Generate the DigestInputStream/DigestOutputStream object
 247         MessageDigest mdIn = MessageDigest.getInstance(algo);
 248         MessageDigest mdOut = MessageDigest.getInstance(algo);
 249         try (ByteArrayInputStream bais = new ByteArrayInputStream(data);
 250                 DigestInputStream dis = new DigestInputStream(bais, mdIn);
 251                 ByteArrayOutputStream baos = new ByteArrayOutputStream();
 252                 DigestOutputStream dos = new DigestOutputStream(baos, mdOut);) {
 253 
 254             // Perform the update using all available/possible update methods
 255             int k = 0;
 256             byte[] buffer = new byte[10];
 257 
 258             // use both read() and read(byte[], int, int)
 259             while ((k = dis.read()) != -1) {
 260                 dos.write(k);
 261                 if ((k = dis.read(buffer, 0, buffer.length)) != -1) {
 262                     dos.write(buffer, 0, k);
 263                 }
 264 
 265                 // Swap the message digest engines between
 266                 // DigestIn/OutputStream objects
 267                 dis.setMessageDigest(mdOut);
 268                 dos.setMessageDigest(mdIn);
 269                 mdIn = dis.getMessageDigest();
 270                 mdOut = dos.getMessageDigest();
 271             }
 272 
 273             // Get the output and the "correct" digest values
 274             byte[] output1 = mdIn.digest();
 275             byte[] output2 = mdOut.digest();
 276             byte[] standard = md.digest(data);
 277 
 278             // Compare generated digest values
 279             return MessageDigest.isEqual(output1, standard)
 280                     && MessageDigest.isEqual(output2, standard);
 281         } catch (Exception ex) {
 282             out.println("testMDChange failed at:" + algo + "/" + dataLength
 283                     + " with unexpected exception");
 284             throw ex;
 285         }
 286     }
 287 
 288     /**
 289      * Test DigestInputStream and DigestOutputStream digest function when use
 290      * same message digest object.
 291      *
 292      * @param algo
 293      *            Message Digest algorithm
 294      * @param dataLength
 295      *            plain test data length.
 296      * @exception Exception
 297      *                throw unexpected exception
 298      */
 299     public boolean testMDShare(String algo, int dataLength) throws Exception {
 300         MessageDigest mdCommon = MessageDigest.getInstance(algo);
 301         // Generate the DigestInputStream/DigestOutputStream object
 302         try (ByteArrayInputStream bais = new ByteArrayInputStream(data);
 303                 DigestInputStream dis = new DigestInputStream(bais, mdCommon);
 304                 ByteArrayOutputStream baos = new ByteArrayOutputStream();
 305                 DigestOutputStream dos = new DigestOutputStream(baos, mdCommon);) {
 306 
 307             // Perform the update using all available/possible update methods
 308             int k = 0;
 309             byte[] buffer = new byte[10];
 310 
 311             // use both read() and read(byte[], int, int)
 312             while (k < data.length) {
 313                 int len = dis.read(buffer, 0, buffer.length);
 314                 if (len != -1) {
 315                     k += len;
 316                     if (k < data.length) {
 317                         dos.write(data[k]);
 318                         k++;
 319                         dis.skip(1);
 320                     }
 321                 }
 322             }
 323 
 324             // Get the output and the "correct" digest values
 325             byte[] output = mdCommon.digest();
 326             byte[] standard = md.digest(data);
 327 
 328             // Compare generated digest values
 329             return MessageDigest.isEqual(output, standard);
 330         } catch (Exception ex) {
 331             out.println("TestMDShare failed at:" + algo + "/" + dataLength
 332                     + " with unexpected exception");
 333             throw ex;
 334         }
 335     }
 336 }