/* * Copyright (c) 2003, 2016, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it * under the terms of the GNU General Public License version 2 only, as * published by the Free Software Foundation. * * This code is distributed in the hope that it will be useful, but WITHOUT * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License * version 2 for more details (a copy is included in the LICENSE file that * accompanied this code). * * You should have received a copy of the GNU General Public License version * 2 along with this work; if not, write to the Free Software Foundation, * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. * * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA * or visit www.oracle.com if you need additional information or have any * questions. */ import java.io.ByteArrayInputStream; import java.io.ByteArrayOutputStream; import java.io.PrintStream; import java.security.DigestInputStream; import java.security.DigestOutputStream; import java.security.MessageDigest; import java.security.NoSuchAlgorithmException; import java.security.Security; import java.util.Arrays; import java.util.Random; import jdk.testlibrary.RandomFactory; import static java.lang.System.out; /** * @test * @bug 8050370 8156059 * @summary MessageDigest tests with DigestIOStream * @author Kevin Liu * @key randomness * @library /lib/testlibrary * @run main/timeout=180 TestDigestIOStream */ enum ReadModel { READ, BUFFER_READ, MIX_READ } public class TestDigestIOStream { private static final int[] DATA_LEN_ARRAY = { 1, 50, 2500, 125000, 6250000 }; private static final String[] ALGORITHM_ARRAY = { "MD2", "MD5", "SHA1", "SHA-224", "SHA-256", "SHA-384", "SHA-512", "SHA3-224", "SHA3-256", "SHA3-384", "SHA3-512" }; private static byte[] data; private static MessageDigest md = null; public static void main(String argv[]) throws Exception { TestDigestIOStream test = new TestDigestIOStream(); test.run(); } public void run() throws Exception { for (String algorithm : ALGORITHM_ARRAY) { try { md = MessageDigest.getInstance(algorithm); for (int length : DATA_LEN_ARRAY) { Random rdm = RandomFactory.getRandom(); data = new byte[length]; rdm.nextBytes(data); if (!testMDChange(algorithm, length)) { throw new RuntimeException("testMDChange failed at:" + algorithm + "/" + length); } if (!testMDShare(algorithm, length)) { throw new RuntimeException("testMDShare failed at:" + algorithm + "/" + length); } for (ReadModel readModel : ReadModel.values()) { // test Digest function when digest switch on if (!testDigestOnOff(algorithm, readModel, true, length)) { throw new RuntimeException( "testDigestOn failed at:" + algorithm + "/" + length + "/" + readModel); } // test Digest function when digest switch off if (!testDigestOnOff(algorithm, readModel, false, length)) { throw new RuntimeException( "testDigestOff failed at:" + algorithm + "/" + length + "/" + readModel); } } } } catch (NoSuchAlgorithmException nae) { if (algorithm.startsWith("SHA3") && !isSHA3supported()) { continue; } else { throw nae; } } } int testNumber = ALGORITHM_ARRAY.length * ReadModel.values().length * DATA_LEN_ARRAY.length * 2 + ALGORITHM_ARRAY.length * DATA_LEN_ARRAY.length * 2; out.println("All " + testNumber + " Tests Passed"); } // SHA-3 hash algorithms are only supported by "SUN" provider // and "OracleUcrypto" provider on Solaris 12.0 or later // This method checks if system supports SHA-3 private boolean isSHA3supported() { if (Security.getProvider("SUN") != null) { return true; } if (Security.getProvider("OracleUcrypto") != null && "SunOS".equals(System.getProperty("os.name")) && System.getProperty("os.version").compareTo("5.12") >= 0) { return true; } return false; } /** * Test DigestInputStream and DigestOutputStream digest function when digest * set on and off * * @param algo * Message Digest algorithm * @param readModel * which read method used(READ, BUFFER_READ, MIX_READ) * @param on * digest switch(on and off) * @param dataLength * plain test data length. * @exception Exception * throw unexpected exception */ public boolean testDigestOnOff(String algo, ReadModel readModel, boolean on, int dataLength) throws Exception { // Generate the DigestInputStream/DigestOutputStream object try (ByteArrayInputStream bais = new ByteArrayInputStream(data); DigestInputStream dis = new DigestInputStream(bais, MessageDigest.getInstance(algo)); ByteArrayOutputStream baos = new ByteArrayOutputStream(); DigestOutputStream dos = new DigestOutputStream(baos, MessageDigest.getInstance(algo)); ByteArrayOutputStream baOut = new ByteArrayOutputStream();) { // Perform the update using all available/possible update methods int k = 0; byte[] buffer = new byte[5]; boolean enDigest = true; // Make sure the digest function is on (default) dis.on(enDigest); dos.on(enDigest); switch (readModel) { case READ: // use only read() while ((k = dis.read()) != -1) { if (on) { dos.write(k); } else { dos.write(k); if (enDigest) { baOut.write(k); } enDigest = !enDigest; dos.on(enDigest); dis.on(enDigest); } } break; case BUFFER_READ: // use only read(byte[], int, int) while ((k = dis.read(buffer, 0, buffer.length)) != -1) { if (on) { dos.write(buffer, 0, k); } else { dos.write(buffer, 0, k); if (enDigest) { baOut.write(buffer, 0, k); } enDigest = !enDigest; dis.on(enDigest); dos.on(enDigest); } } break; case MIX_READ: // use both read() and read(byte[], int, int) while ((k = dis.read()) != -1) { if (on) { dos.write(k); if ((k = dis.read(buffer, 0, buffer.length)) != -1) { dos.write(buffer, 0, k); } } else { dos.write(k); if (enDigest) { baOut.write(k); } enDigest = !enDigest; dis.on(enDigest); dos.on(enDigest); if ((k = dis.read(buffer, 0, buffer.length)) != -1) { dos.write(buffer, 0, k); if (enDigest) { baOut.write(buffer, 0, k); } enDigest = !enDigest; dis.on(enDigest); dos.on(enDigest); } } } break; default: out.println("ERROR: Invalid read/write combination choice!"); return false; } // Get the output and the "correct" digest values byte[] output1 = dis.getMessageDigest().digest(); byte[] output2 = dos.getMessageDigest().digest(); byte[] standard; if (on) { standard = md.digest(data); } else { byte[] dataDigested = baOut.toByteArray(); standard = md.digest(dataDigested); } // Compare the output byte array value to the input data if (!MessageDigest.isEqual(data, baos.toByteArray())) { out.println("ERROR of " + readModel + ": output and input data unexpectedly changed"); return false; } // Compare generated digest values if (!MessageDigest.isEqual(output1, standard) || !MessageDigest.isEqual(output2, standard)) { out.println("ERROR" + readModel + ": generated digest data unexpectedly changed"); return false; } return true; } catch (Exception ex) { out.println("testDigestOnOff failed at:" + algo + "/" + readModel + "/" + dataLength + " with unexpected exception"); throw ex; } } /** * Test DigestInputStream and DigestOutputStream digest function when Swap * the message digest engines between DigestIn/OutputStream * * @param algo * Message Digest algorithm * @param dataLength * plain test data length. * @exception Exception * throw unexpected exception */ public boolean testMDChange(String algo, int dataLength) throws Exception { // Generate the DigestInputStream/DigestOutputStream object MessageDigest mdIn = MessageDigest.getInstance(algo); MessageDigest mdOut = MessageDigest.getInstance(algo); try (ByteArrayInputStream bais = new ByteArrayInputStream(data); DigestInputStream dis = new DigestInputStream(bais, mdIn); ByteArrayOutputStream baos = new ByteArrayOutputStream(); DigestOutputStream dos = new DigestOutputStream(baos, mdOut);) { // Perform the update using all available/possible update methods int k = 0; byte[] buffer = new byte[10]; // use both read() and read(byte[], int, int) while ((k = dis.read()) != -1) { dos.write(k); if ((k = dis.read(buffer, 0, buffer.length)) != -1) { dos.write(buffer, 0, k); } // Swap the message digest engines between // DigestIn/OutputStream objects dis.setMessageDigest(mdOut); dos.setMessageDigest(mdIn); mdIn = dis.getMessageDigest(); mdOut = dos.getMessageDigest(); } // Get the output and the "correct" digest values byte[] output1 = mdIn.digest(); byte[] output2 = mdOut.digest(); byte[] standard = md.digest(data); // Compare generated digest values return MessageDigest.isEqual(output1, standard) && MessageDigest.isEqual(output2, standard); } catch (Exception ex) { out.println("testMDChange failed at:" + algo + "/" + dataLength + " with unexpected exception"); throw ex; } } /** * Test DigestInputStream and DigestOutputStream digest function when use * same message digest object. * * @param algo * Message Digest algorithm * @param dataLength * plain test data length. * @exception Exception * throw unexpected exception */ public boolean testMDShare(String algo, int dataLength) throws Exception { MessageDigest mdCommon = MessageDigest.getInstance(algo); // Generate the DigestInputStream/DigestOutputStream object try (ByteArrayInputStream bais = new ByteArrayInputStream(data); DigestInputStream dis = new DigestInputStream(bais, mdCommon); ByteArrayOutputStream baos = new ByteArrayOutputStream(); DigestOutputStream dos = new DigestOutputStream(baos, mdCommon);) { // Perform the update using all available/possible update methods int k = 0; byte[] buffer = new byte[10]; // use both read() and read(byte[], int, int) while (k < data.length) { int len = dis.read(buffer, 0, buffer.length); if (len != -1) { k += len; if (k < data.length) { dos.write(data[k]); k++; dis.skip(1); } } } // Get the output and the "correct" digest values byte[] output = mdCommon.digest(); byte[] standard = md.digest(data); // Compare generated digest values return MessageDigest.isEqual(output, standard); } catch (Exception ex) { out.println("TestMDShare failed at:" + algo + "/" + dataLength + " with unexpected exception"); throw ex; } } }