| /* |
| * Copyright (c) 2003, 2017, 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.test.lib.RandomFactory; |
| import static java.lang.System.out; |
| |
| /** |
| * @test |
| * @bug 8050370 8156059 |
| * @summary MessageDigest tests with DigestIOStream |
| * @author Kevin Liu |
| * @key randomness |
| * @library /test/lib |
| * @build jdk.test.lib.RandomFactory |
| * @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; |
| } |
| } |
| } |