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  */
  40 
  41 enum ReadModel {
  42     READ, BUFFER_READ, MIX_READ
  43 }
  44 
  45 public class TestDigestIOStream {
  46 
  47     private static final int[] DATA_LEN_ARRAY = {
  48         1, 50, 2500, 125000, 6250000
  49     };
  50     private static final String[] ALGORITHM_ARRAY = {
  51         "MD2", "MD5", "SHA1", "SHA-224", "SHA-256", "SHA-384", "SHA-512"
  52     };
  53 
  54     private static byte[] data;
  55 
  56     private static MessageDigest md = null;
  57 
  58     public static void main(String argv[]) throws Exception {
  59         TestDigestIOStream test = new TestDigestIOStream();
  60         test.run();
  61     }
  62 
  63     public void run() throws Exception {
  64         for (String algorithm: ALGORITHM_ARRAY) {
  65 
  66             md = MessageDigest.getInstance(algorithm);
  67 
  68             for (int length: DATA_LEN_ARRAY) {
  69 
  70                 Random rdm = new Random();
  71                 data = new byte[length];
  72                 rdm.nextBytes(data);
  73 
  74                 if (!testMDChange(algorithm, length)) {
  75                     throw new RuntimeException("testMDChange failed at:"
  76                             + algorithm + "/" + length);
  77                 }
  78                 if (!testMDShare(algorithm, length)) {
  79                     throw new RuntimeException("testMDShare failed at:"
  80                             + algorithm + "/" + length);
  81                 }
  82                 for (ReadModel readModel: ReadModel.values()) {
  83                     // test Digest function when digest switch on
  84                     if (!testDigestOnOff(algorithm, readModel, true, length)) {
  85                         throw new RuntimeException("testDigestOn failed at:"
  86                                 + algorithm + "/" + length + "/" + readModel);
  87                     }
  88                     // test Digest function when digest switch off
  89                     if (!testDigestOnOff(algorithm, readModel, false, length)) {
  90                         throw new RuntimeException("testDigestOff failed at:"
  91                                 + algorithm + "/" + length + "/" + readModel);
  92                     }
  93                 }
  94             }
  95         }
  96         int testNumber = ALGORITHM_ARRAY.length * ReadModel.values().length
  97                 * DATA_LEN_ARRAY.length * 2 + ALGORITHM_ARRAY.length
  98                 * DATA_LEN_ARRAY.length * 2;
  99         out.println("All " + testNumber + " Tests Passed");
 100     }
 101 
 102     /**
 103      * Test DigestInputStream and DigestOutputStream digest function when digest
 104      * set on and off
 105      *
 106      * @param algo
 107      *            Message Digest algorithm
 108      * @param readModel
 109      *            which read method used(READ, BUFFER_READ, MIX_READ)
 110      * @param on
 111      *            digest switch(on and off)
 112      * @param dataLength
 113      *            plain test data length.
 114      * @exception Exception
 115      *                throw unexpected exception
 116      */
 117     public boolean testDigestOnOff(String algo, ReadModel readModel,
 118             boolean on, int dataLength) throws Exception {
 119 
 120         // Generate the DigestInputStream/DigestOutputStream object
 121         try (ByteArrayInputStream bais = new ByteArrayInputStream(data);
 122                 DigestInputStream dis = new DigestInputStream(bais,
 123                         MessageDigest.getInstance(algo));
 124                 ByteArrayOutputStream baos = new ByteArrayOutputStream();
 125                 DigestOutputStream dos = new DigestOutputStream(baos,
 126                         MessageDigest.getInstance(algo));
 127                 ByteArrayOutputStream baOut = new ByteArrayOutputStream();) {
 128 
 129             // Perform the update using all available/possible update methods
 130             int k = 0;
 131             byte[] buffer = new byte[5];
 132             boolean enDigest = true;
 133             // Make sure the digest function is on (default)
 134             dis.on(enDigest);
 135             dos.on(enDigest);
 136 
 137             switch (readModel) {
 138             case READ: // use only read()
 139                 while ((k = dis.read()) != -1) {
 140                     if (on) {
 141                         dos.write(k);
 142                     } else {
 143                         dos.write(k);
 144                         if (enDigest) {
 145                             baOut.write(k);
 146                         }
 147                         enDigest = !enDigest;
 148                         dos.on(enDigest);
 149                         dis.on(enDigest);
 150                     }
 151                 }
 152                 break;
 153             case BUFFER_READ: // use only read(byte[], int, int)
 154                 while ((k = dis.read(buffer, 0, buffer.length)) != -1) {
 155                     if (on) {
 156                         dos.write(buffer, 0, k);
 157                     } else {
 158                         dos.write(buffer, 0, k);
 159                         if (enDigest) {
 160                             baOut.write(buffer, 0, k);
 161                         }
 162                         enDigest = !enDigest;
 163                         dis.on(enDigest);
 164                         dos.on(enDigest);
 165                     }
 166                 }
 167                 break;
 168             case MIX_READ: // use both read() and read(byte[], int, int)
 169                 while ((k = dis.read()) != -1) {
 170                     if (on) {
 171                         dos.write(k);
 172                         if ((k = dis.read(buffer, 0, buffer.length)) != -1) {
 173                             dos.write(buffer, 0, k);
 174                         }
 175                     } else {
 176                         dos.write(k);
 177                         if (enDigest) {
 178                             baOut.write(k);
 179                         }
 180                         enDigest = !enDigest;
 181                         dis.on(enDigest);
 182                         dos.on(enDigest);
 183                         if ((k = dis.read(buffer, 0, buffer.length)) != -1) {
 184                             dos.write(buffer, 0, k);
 185                             if (enDigest) {
 186                                 baOut.write(buffer, 0, k);
 187                             }
 188                             enDigest = !enDigest;
 189                             dis.on(enDigest);
 190                             dos.on(enDigest);
 191                         }
 192                     }
 193                 }
 194                 break;
 195             default:
 196                 out.println("ERROR: Invalid read/write combination choice!");
 197                 return false;
 198             }
 199 
 200             // Get the output and the "correct" digest values
 201             byte[] output1 = dis.getMessageDigest().digest();
 202             byte[] output2 = dos.getMessageDigest().digest();
 203             byte[] standard;
 204             if (on) {
 205                 standard = md.digest(data);
 206             } else {
 207                 byte[] dataDigested = baOut.toByteArray();
 208                 standard = md.digest(dataDigested);
 209             }
 210 
 211             // Compare the output byte array value to the input data
 212             if (!MessageDigest.isEqual(data, baos.toByteArray())) {
 213                 out.println("ERROR of " + readModel
 214                         + ": output and input data unexpectedly changed");
 215                 return false;
 216             }
 217             // Compare generated digest values
 218             if (!MessageDigest.isEqual(output1, standard)
 219                     || !MessageDigest.isEqual(output2, standard)) {
 220                 out.println("ERROR" + readModel
 221                         + ": generated digest data unexpectedly changed");
 222                 return false;
 223             }
 224 
 225             return true;
 226         } catch (Exception ex) {
 227             out.println("testDigestOnOff failed at:" + algo + "/" + readModel
 228                     + "/" + dataLength + " with unexpected exception");
 229             throw ex;
 230         }
 231     }
 232 
 233     /**
 234      * Test DigestInputStream and DigestOutputStream digest function when Swap
 235      * the message digest engines between DigestIn/OutputStream
 236      *
 237      * @param algo
 238      *            Message Digest algorithm
 239      * @param dataLength
 240      *            plain test data length.
 241      * @exception Exception
 242      *                throw unexpected exception
 243      */
 244     public boolean testMDChange(String algo, int dataLength) throws Exception {
 245         // Generate the DigestInputStream/DigestOutputStream object
 246         MessageDigest mdIn = MessageDigest.getInstance(algo);
 247         MessageDigest mdOut = MessageDigest.getInstance(algo);
 248         try (ByteArrayInputStream bais = new ByteArrayInputStream(data);
 249                 DigestInputStream dis = new DigestInputStream(bais, mdIn);
 250                 ByteArrayOutputStream baos = new ByteArrayOutputStream();
 251                 DigestOutputStream dos = new DigestOutputStream(baos, mdOut);) {
 252 
 253             // Perform the update using all available/possible update methods
 254             int k = 0;
 255             byte[] buffer = new byte[10];
 256 
 257             // use both read() and read(byte[], int, int)
 258             while ((k = dis.read()) != -1) {
 259                 dos.write(k);
 260                 if ((k = dis.read(buffer, 0, buffer.length)) != -1) {
 261                     dos.write(buffer, 0, k);
 262                 }
 263 
 264                 // Swap the message digest engines between
 265                 // DigestIn/OutputStream objects
 266                 dis.setMessageDigest(mdOut);
 267                 dos.setMessageDigest(mdIn);
 268                 mdIn = dis.getMessageDigest();
 269                 mdOut = dos.getMessageDigest();
 270             }
 271 
 272             // Get the output and the "correct" digest values
 273             byte[] output1 = mdIn.digest();
 274             byte[] output2 = mdOut.digest();
 275             byte[] standard = md.digest(data);
 276 
 277             // Compare generated digest values
 278             return MessageDigest.isEqual(output1, standard)
 279                     && MessageDigest.isEqual(output2, standard);
 280         } catch (Exception ex) {
 281             out.println("testMDChange failed at:" + algo + "/" + dataLength
 282                     + " with unexpected exception");
 283             throw ex;
 284         }
 285     }
 286 
 287     /**
 288      * Test DigestInputStream and DigestOutputStream digest function when use
 289      * same message digest object.
 290      *
 291      * @param algo
 292      *            Message Digest algorithm
 293      * @param dataLength
 294      *            plain test data length.
 295      * @exception Exception
 296      *                throw unexpected exception
 297      */
 298     public boolean testMDShare(String algo, int dataLength) throws Exception {
 299         MessageDigest mdCommon = MessageDigest.getInstance(algo);
 300         // Generate the DigestInputStream/DigestOutputStream object
 301         try (ByteArrayInputStream bais = new ByteArrayInputStream(data);
 302                 DigestInputStream dis = new DigestInputStream(bais, mdCommon);
 303                 ByteArrayOutputStream baos = new ByteArrayOutputStream();
 304                 DigestOutputStream dos = new DigestOutputStream(baos, mdCommon);) {
 305 
 306             // Perform the update using all available/possible update methods
 307             int k = 0;
 308             byte[] buffer = new byte[10];
 309 
 310             // use both read() and read(byte[], int, int)
 311             while (k < data.length) {
 312                 int len = dis.read(buffer, 0, buffer.length);
 313                 if (len != -1) {
 314                     k += len;
 315                     if (k < data.length) {
 316                         dos.write(data[k]);
 317                         k++;
 318                         dis.skip(1);
 319                     }
 320                 }
 321             }
 322 
 323             // Get the output and the "correct" digest values
 324             byte[] output = mdCommon.digest();
 325             byte[] standard = md.digest(data);
 326 
 327             // Compare generated digest values
 328             return MessageDigest.isEqual(output, standard);
 329         } catch (Exception ex) {
 330             out.println("TestMDShare failed at:" + algo + "/" + dataLength
 331                     + " with unexpected exception");
 332             throw ex;
 333         }
 334     }
 335 }