jdk/test/com/sun/crypto/provider/CICO/CICOSkipTest.java
author tyan
Wed, 23 Sep 2015 21:28:14 +0000
changeset 32766 2322e6c2a032
permissions -rw-r--r--
8048604: Tests for strong crypto ciphers Summary: SQE test co-location effort Reviewed-by: valeriep

/*
 * Copyright (c) 2007, 2015, 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 static java.lang.System.out;

import java.io.ByteArrayInputStream;
import java.io.IOException;
import java.security.InvalidAlgorithmParameterException;
import java.security.InvalidKeyException;
import java.security.NoSuchAlgorithmException;
import java.security.Provider;
import java.security.Security;
import java.security.spec.AlgorithmParameterSpec;
import java.security.spec.InvalidKeySpecException;
import javax.crypto.Cipher;
import javax.crypto.CipherInputStream;
import javax.crypto.KeyGenerator;
import javax.crypto.NoSuchPaddingException;
import javax.crypto.SecretKey;
import javax.crypto.SecretKeyFactory;
import javax.crypto.spec.IvParameterSpec;
import javax.crypto.spec.PBEKeySpec;
import javax.crypto.spec.PBEParameterSpec;

/*
 * @test
 * @bug 8048604
 * @summary This test verifies the assertion "The skip feature of Filter
 *    streams should be supported." for feature
 *    CipherInputStream and CipherOutputStream
 */
public class CICOSkipTest {
    /**
     * Block length.
     */
    private static final int BLOCK = 50;

    /**
     * Saving bytes length.
     */
    private static final int SAVE = 45;

    /**
     * Plain text length.
     */
    private static final int PLAIN_TEXT_LENGTH = 800;

    /**
     * Skip reading byte size. This should be same to BLOCK - SAVE
     */
    private static final int DISCARD = BLOCK - SAVE;

    private static final String[] ALGOS = {"DES", "DESede", "Blowfish"};
    private static final String[] MODES = {"ECB", "CBC", "CFB", "CFB32",
        "OFB", "OFB64", "PCBC"};
    private static final String[] PADDINGS = {"NoPadding", "Pkcs5Padding"};
    private static final String[] PBE_ALGOS = {"PBEWithMD5AndDES",
        "PBEWithMD5AndDES/CBC/PKCS5Padding"};

    public static void main(String[] args) throws Exception {
        // how many kinds of padding mode such as PKCS5padding and NoPadding
        for (String algo : ALGOS) {
            for (String mode : MODES) {
                int padKinds = 1;
                if (mode.equalsIgnoreCase("ECB")
                        || mode.equalsIgnoreCase("PCBC")
                        || mode.equalsIgnoreCase("CBC")) {
                    padKinds = PADDINGS.length;
                }
                // PKCS5padding is meaningful only for ECB, CBC, PCBC
                for (int k = 0; k < padKinds; k++) {
                    String info = algo + "/" + mode + "/" + PADDINGS[k];
                    try {
                        CipherGenerator cg = new CipherGenerator(algo, mode,
                                PADDINGS[k]);
                        for (ReadMethod model : ReadMethod.values()) {
                            runTest(cg.getPair(), info, model);
                        }
                    } catch (LengthLimitException exp) {
                        // skip this if this key length is larger than what's
                        // configured in the jce jurisdiction policy files
                        out.println(exp.getMessage() + " is expected.");
                    }
                }
            }
        }
        for (String pbeAlgo : PBE_ALGOS) {
            for (ReadMethod model : ReadMethod.values()) {
                System.out.println("Testing Algorithm : " + pbeAlgo
                        + " ReadMethod : " + model);
                runTest(new CipherGenerator(pbeAlgo).getPair(), pbeAlgo, model);
            }
        }
    }

    private static void runTest(Cipher[] pair, String info, ReadMethod whichRead)
            throws IOException {
        byte[] plainText = TestUtilities.generateBytes(PLAIN_TEXT_LENGTH);
        out.println("Testing: " + info + "/" + whichRead);
        try (ByteArrayInputStream baInput = new ByteArrayInputStream(plainText);
                CipherInputStream ciInput1 = new CipherInputStream(baInput,
                        pair[0]);
                CipherInputStream ciInput2 = new CipherInputStream(ciInput1,
                        pair[1]);) {
            // Skip 5 bytes after read 45 bytes and repeat until finish
            // (Read from the input and write to the output using 2 types
            // of buffering : byte[] and int)
            // So output has size:
            // (OVERALL/BLOCK)* SAVE = (800 / 50) * 45 = 720 bytes
            int numOfBlocks = plainText.length / BLOCK;

            // Output buffer.
            byte[] outputText = new byte[numOfBlocks * SAVE];
            int index = 0;
            for (int i = 0; i < numOfBlocks; i++) {
                index = whichRead.readByte(ciInput2, outputText, SAVE, index);
                // If available is more than expected discard byte size. Skip
                // discard bytes, otherwise try to read discard bytes by read.
                if (ciInput2.available() >= DISCARD) {
                    ciInput2.skip(DISCARD);
                } else {
                    for (int k = 0; k < DISCARD; k++) {
                        ciInput2.read();
                    }
                }
            }
            // Verify output is same as input
            if (!TestUtilities
                    .equalsBlockPartial(plainText, outputText, BLOCK, SAVE)) {
                throw new RuntimeException("Test failed with compare fail");
            }
        }
    }
}

class CipherGenerator {
    /**
     * Initialization vector  length.
     */
    private static final int IV_LENGTH = 8;

    private static final String PASSWD = "Sesame!(@#$%^&*)";

    private final Cipher[] pair = new Cipher[2];

    // For DES/DESede ciphers
    CipherGenerator(String algo, String mo, String pad)
            throws NoSuchAlgorithmException,
            InvalidAlgorithmParameterException, InvalidKeyException,
            NoSuchPaddingException, SecurityException, LengthLimitException {
        // Do initialization
        KeyGenerator kg = KeyGenerator.getInstance(algo);
        SecretKey key = kg.generateKey();
        if (key.getEncoded().length * 8 > Cipher.getMaxAllowedKeyLength(algo)) {
            // skip this if this key length is larger than what's
            // configured in the jce jurisdiction policy files
            throw new LengthLimitException(
                    "Skip this test if key length is larger than what's"
                    + "configured in the jce jurisdiction policy files");
        }
        AlgorithmParameterSpec aps = null;
        if (!mo.equalsIgnoreCase("ECB")) {
            byte[] iv = TestUtilities.generateBytes(IV_LENGTH);
            aps = new IvParameterSpec(iv);
        }
        initCiphers(algo + "/" + mo + "/" + pad, key, aps);
    }

    // For PBE ciphers
    CipherGenerator(String algo) throws NoSuchAlgorithmException,
            InvalidAlgorithmParameterException, InvalidKeyException,
            NoSuchPaddingException, InvalidKeySpecException {
        // Do initialization
        byte[] salt = TestUtilities.generateBytes(IV_LENGTH);
        int iterCnt = 6;
        SecretKeyFactory skf = SecretKeyFactory.getInstance(algo.split("/")[0]);
        SecretKey key = skf
                .generateSecret(new PBEKeySpec(PASSWD.toCharArray()));
        AlgorithmParameterSpec aps = new PBEParameterSpec(salt, iterCnt);
        initCiphers(algo, key, aps);
    }

    private void initCiphers(String algo, SecretKey key,
            AlgorithmParameterSpec aps) throws NoSuchAlgorithmException,
            NoSuchPaddingException, InvalidKeyException,
            InvalidAlgorithmParameterException {
        Provider provider = Security.getProvider("SunJCE");
        if (provider == null) {
            throw new RuntimeException("SunJCE provider does not exist.");
        }
        Cipher ci1 = Cipher.getInstance(algo, provider);
        ci1.init(Cipher.ENCRYPT_MODE, key, aps);
        pair[0] = ci1;
        Cipher ci2 = Cipher.getInstance(algo, provider);
        ci2.init(Cipher.DECRYPT_MODE, key, aps);
        pair[1] = ci2;
    }

    Cipher[] getPair() {
        return pair;
    }
}

enum ReadMethod {
    // read one byte at a time for save times
    READ_ONE_BYTE {
        @Override
        int readByte(CipherInputStream ciIn2, byte[] outputText, int save,
                int index) throws IOException {
            for (int j = 0; j < save; j++, index++) {
                int buffer0 = ciIn2.read();
                if (buffer0 != -1) {
                    outputText[index] = (byte) buffer0;
                } else {
                    break;
                }
            }
            return index;
        }
    },
    // read a chunk of save bytes if possible
    READ_BLOCK {
        @Override
        int readByte(CipherInputStream ciIn2, byte[] outputText, int save,
                int index) throws IOException {
            int len1 = ciIn2.read(outputText, index, save);
            out.println("Init: index=" + index + ",len=" + len1);
            // read more until save bytes
            index += len1;
            int len2 = 0;
            while (len1 != save && len2 != -1) {
                len2 = ciIn2.read(outputText, index, save - len1);
                out.println("Cont: index=" + index + ",len=" + len2);
                len1 += len2;
                index += len2;
            }
            return index;
        }
    };

    abstract int readByte(CipherInputStream ciIn2, byte[] outputText, int save,
            int index) throws IOException;
};

class LengthLimitException extends Exception {

    public LengthLimitException(String string) {
        super(string);
    }
}