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