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