test/jdk/java/security/MessageDigest/TestDigestIOStream.java
author erikj
Tue, 12 Sep 2017 19:03:39 +0200
changeset 47216 71c04702a3d5
parent 45467 jdk/test/java/security/MessageDigest/TestDigestIOStream.java@99c87a16a8e4
permissions -rw-r--r--
8187443: Forest Consolidation: Move files to unified layout Reviewed-by: darcy, ihse

/*
 * 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;
        }
    }
}