8041787: Need new regressions tests for buffer handling for PBE algorithms

Reviewed-by: vinnie
This commit is contained in:
Rajan Halade 2015-07-15 18:12:48 +01:00 committed by Vinnie Ryan
parent 03788b946a
commit 9ba56436a1
9 changed files with 1224 additions and 0 deletions

View File

@ -0,0 +1,114 @@
/*
* Copyright (c) 2012, 2014, 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.PrintStream;
import java.security.AlgorithmParameters;
import java.security.InvalidKeyException;
import java.security.Provider;
import javax.crypto.Cipher;
import javax.crypto.SecretKeyFactory;
import javax.crypto.spec.PBEKeySpec;
/**
* Wrapper class to test a given AES-based PBE algorithm.
*
* @author Alexander Fomin
*/
public class AESPBEWrapper extends PBEWrapper {
private AlgorithmParameters pbeParams;
/**
* Constructor. Instantiate Cipher using the given AES-based PBE algorithms.
*
* @param p security Provider
* @param algo PKDF2 algorithm
* @param passwd password phrase
* @param out print stream
* @throws Exception all exceptions are thrown
*/
public AESPBEWrapper(Provider p, String algo, String passwd,
PrintStream out) throws Exception {
super(algo,
SecretKeyFactory.getInstance(algo, p).generateSecret(
new PBEKeySpec(passwd.toCharArray())),
Cipher.getInstance(algo, p), out);
}
/**
* Perform encryption/decryption operation (depending on the specified
* edMode) on the same byte buffer. Compare result with the result at an
* allocated buffer. If both results are equal - return true, otherwise
* return false.
*
* @param edMode specified mode
* @param inputText text to decrypt
* @param offset offset in the text
* @param len input length
* @return ture - test passed; false - test failed
*/
@Override
public boolean execute(int edMode, byte[] inputText, int offset, int len) {
try {
// init Cipher
if (Cipher.ENCRYPT_MODE == edMode) {
ci.init(Cipher.ENCRYPT_MODE, this.key);
pbeParams = ci.getParameters();
} else {
ci.init(Cipher.DECRYPT_MODE, this.key, pbeParams);
}
// First, generate the cipherText at an allocated buffer
byte[] outputText = ci.doFinal(inputText, offset, len);
// Second, generate cipherText again at the same buffer of plainText
int myoff = offset / 2;
int off = ci.update(inputText, offset, len, inputText, myoff);
ci.doFinal(inputText, myoff + off);
if (this.algo.endsWith("AES_256")) {
out.print("Expected exception uncaught, "
+ "keyStrength > 128 within " + this.algo);
return false;
}
// Compare to see whether the two results are the same or not
return equalsBlock(inputText, myoff, outputText, 0,
outputText.length);
} catch (Exception ex) {
if ((ex instanceof InvalidKeyException)
&& this.algo.endsWith("AES_256")) {
out.println("Expected InvalidKeyException exception: "
+ ex.getMessage());
return true;
}
out.println("Catch unexpected exception within " + algo);
ex.printStackTrace(out);
return false;
}
}
}

View File

@ -0,0 +1,110 @@
/*
* Copyright (c) 2012, 2014, 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.
*/
/*
* @author Valerie PENG
* @author Yun Ke
* @author Alexander Fomin
* @author rhalade
*/
import java.security.spec.AlgorithmParameterSpec;
import java.util.StringTokenizer;
import java.security.InvalidKeyException;
import java.security.Provider;
import java.io.PrintStream;
import javax.crypto.Cipher;
import javax.crypto.SecretKeyFactory;
import javax.crypto.spec.PBEKeySpec;
import javax.crypto.spec.PBEParameterSpec;
public class PBECipherWrapper extends PBEWrapper {
private final AlgorithmParameterSpec aps;
public PBECipherWrapper(
Provider p, String algo, String passwd, PrintStream out)
throws Exception {
super(algo,
SecretKeyFactory.getInstance(
new StringTokenizer(algo, "/").nextToken(), p).generateSecret(
new PBEKeySpec(passwd.toCharArray())),
Cipher.getInstance(algo, p), out);
int SALT_SIZE = 8;
aps = new PBEParameterSpec(generateSalt(SALT_SIZE), ITERATION_COUNT);
}
@Override
public boolean execute(int edMode, byte[] inputText, int offset,
int len) {
StringTokenizer st = new StringTokenizer(algo, "/");
String baseAlgo = st.nextToken().toUpperCase();
// Perform encryption or decryption depends on the specified edMode
try {
ci.init(edMode, key, aps);
// First, generate the cipherText at an allocated buffer
byte[] outputText = ci.doFinal(inputText, offset, len);
// Second, generate cipherText again at the same buffer of
// plainText
int myoff = offset / 2;
int off = ci.update(inputText, offset, len, inputText, myoff);
ci.doFinal(inputText, myoff + off);
if (baseAlgo.endsWith("TRIPLEDES")
|| baseAlgo.endsWith("AES_256")) {
out.print("Expected exception uncaught,"
+ "keyStrength > 128 within " + this.algo);
return false;
}
// Compare to see whether the two results are the same or not
boolean result = equalsBlock(inputText, myoff, outputText, 0,
outputText.length);
return result;
} catch (Exception ex) {
if ((ex instanceof InvalidKeyException)
&& (baseAlgo.endsWith("TRIPLEDES")
|| baseAlgo.endsWith("AES_256"))) {
out.println("Expected InvalidKeyException exception: "
+ ex.getMessage());
return true;
}
out.println("Catch unexpected exception within " + algo);
ex.printStackTrace(out);
return false;
}
}
}

View File

@ -0,0 +1,152 @@
/*
* Copyright (c) 2012, 2014, 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.
*/
/**
* @test
* @bug 8041787
* @library .
* @build PBEWrapper PBEWrapperCreator PBKDF2Wrapper AESPBEWrapper PBECipherWrapper
* @summary Verify that same encrypt/decrypt buffer can be used for PBE ciphers
* @author Alexander Fomin
* @author rhalade
* @run main PBESameBuffer
*/
import java.io.PrintStream;
import java.security.*;
import java.util.Random;
import javax.crypto.Cipher;
public class PBESameBuffer {
private static final String[] pbeAlgorithms = {
"pbeWithMD5ANDdes", "PBEWithMD5AndDES/CBC/PKCS5Padding",
"pbeWithMD5ANDtripledes", "PBEWithMD5AndTRIPLEDES/CBC/PKCS5Padding",
"PBEwithSHA1AndDESede", "PBEwithSHA1AndDESede/CBC/PKCS5Padding",
"PBEwithSHA1AndRC2_40", "PBEwithSHA1AndRC2_40/CBC/PKCS5Padding",
"PBEWithSHA1AndRC2_128", "PBEWithSHA1AndRC2_128/CBC/PKCS5Padding",
"PBEWithSHA1AndRC4_40", "PBEWithSHA1AndRC4_40/ECB/NoPadding",
"PBEWithSHA1AndRC4_128", "PBEWithSHA1AndRC4_128/ECB/NoPadding",
"PBEWithHmacSHA1AndAES_128",
"PBEWithHmacSHA224AndAES_128",
"PBEWithHmacSHA256AndAES_128",
"PBEWithHmacSHA384AndAES_128",
"PBEWithHmacSHA512AndAES_128",
"PBEWithHmacSHA1AndAES_256",
"PBEWithHmacSHA224AndAES_256",
"PBEWithHmacSHA256AndAES_256",
"PBEWithHmacSHA384AndAES_256",
"PBEWithHmacSHA512AndAES_256",
"PBKDF2WithHmacSHA1",
"PBKDF2WithHmacSHA224",
"PBKDF2WithHmacSHA256",
"PBKDF2WithHmacSHA384",
"PBKDF2WithHmacSHA512"
};
private static final String PBEPASS = "Hush, it's supposed to be a secret!";
private static final int INPUT_LENGTH = 800;
private static final int[] OFFSETS = {0, 1, 2, 3};
private static final int NUM_PAD_BYTES = 8;
private static final int PBKDF2_ADD_PAD_BYTES = 8;
private static int OUTPUT_OFFSET;
public static void main(String[] args) {
if (!(new PBESameBuffer().test(args, System.out))) {
throw new RuntimeException("Some PBE algorithm tests failed");
}
}
public boolean test(String[] args, PrintStream out) {
boolean result = true;
Provider p = Security.getProvider("SunJCE");
for (int loop : OFFSETS) {
OUTPUT_OFFSET = loop;
// generate input data
byte[] inputText = new byte[INPUT_LENGTH + NUM_PAD_BYTES
+ OUTPUT_OFFSET * 2 + PBKDF2_ADD_PAD_BYTES];
new Random().nextBytes(inputText);
for (String algorithm : pbeAlgorithms) {
out.println("=> Testing algorithm " + algorithm + " and offset "
+ OUTPUT_OFFSET + ":");
try {
// Initialize Cipher and key for this algorithm
PBEWrapper pbeCi = PBEWrapperCreator.createWrapper(p,
algorithm,
PBEPASS,
out);
// Encrypt
if ((pbeCi != null) && (!pbeCi.execute(Cipher.ENCRYPT_MODE,
inputText,
OUTPUT_OFFSET * 2,
INPUT_LENGTH))) {
result = false;
}
// PBKDF2 required 16 byte padding
int padLength = getPadLength(algorithm);
// Decrypt
// Note: inputText is implicitly padded by the above encrypt
// operation so decrypt operation can safely proceed
if ((pbeCi != null) && (!pbeCi.execute(Cipher.DECRYPT_MODE,
inputText,
OUTPUT_OFFSET,
INPUT_LENGTH + padLength))) {
result = false;
}
} catch (Exception ex) {
ex.printStackTrace(out);
result = false;
}
}
}
return result;
}
/**
* Get the padding length for the given algorithm
*
* @param theAlgName algorithm name
* @return padding length for the given algorithm
*/
private int getPadLength(String theAlgName) {
if (theAlgName.toUpperCase().contains("PBKDF2")) {
return NUM_PAD_BYTES + PBKDF2_ADD_PAD_BYTES;
}
if (theAlgName.toUpperCase().contains("AES")) {
return NUM_PAD_BYTES + PBKDF2_ADD_PAD_BYTES;
}
return NUM_PAD_BYTES;
}
}

View File

@ -0,0 +1,98 @@
/*
* Copyright (c) 2012, 2014, 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.PrintStream;
import java.util.Random;
import javax.crypto.Cipher;
import javax.crypto.SecretKey;
/**
* PBEWrapper is the abstract class for all concrete PBE Cipher wrappers. A
* PBEWrapper object encapsulates the information and behavior needed to test if
* the multiple-part encryption/decryption is performing by expected way on the
* same byte buffer.
*
* @author Alexandr Fomin
* @author rhalade
*/
public abstract class PBEWrapper {
protected final static int ITERATION_COUNT = 1000;
protected final SecretKey key;
protected final Cipher ci;
protected final String algo;
protected final PrintStream out;
public PBEWrapper(String pAlgo, SecretKey pKey, Cipher pCi,
PrintStream pOut ){
this.algo = pAlgo;
this.key = pKey;
this.ci = pCi;
this.out = pOut;
}
/**
* Abstract method need to be implemented in the subclasses.
*
* @param edMode Cipher mode - encrypt/decrypt
* @param inputText byte buffer to process
* @param offset offset in byte the inputText
* @param len length of byte to process in inputText
* @return true if cipher operation is successful, false otherwise
*/
public abstract boolean execute(int edMode, byte[] inputText, int offset,
int len);
/**
* An utility method to prepare "salt" for following Secret Key generation.
*
* @param numberOfBytes number of bytes in salt
* @return randomly generated byte array
*/
protected static byte[] generateSalt(int numberOfBytes) {
byte[] salt = new byte[numberOfBytes];
new Random().nextBytes(salt);
return salt;
}
/**
* An utility method to check if two byte arrays are equal
*
* @param b1 first byte array
* @param off1 offset to compare from in b1
* @param b2 second byte array
* @param off2 offset to compare from in b2
* @param len length to compare
* @return true of arrays are equal, false otherwise
*/
protected boolean equalsBlock(byte[] b1, int off1,
byte[] b2, int off2, int len) {
for (int i = off1, j = off2, k = 0; k < len; i++, j++, k++) {
if (b1[i] != b2[j]) {
return false;
}
}
return true;
}
}

View File

@ -0,0 +1,59 @@
/*
* Copyright (c) 2012, 2014, 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.security.Provider;
import java.io.PrintStream;
/**
* An utility class to create PBEWrapper object for the TestCipherSameBuffer
* test.
*
* @author Alexander Fomin
*/
public class PBEWrapperCreator {
private static final String PBKDF2 = "PBKDF2";
private static final String AES = "AES";
/**
* Create PBEWrapper for the TestCipherSameBuffer test using given
* parameters.
*
* @param p security provider
* @param algo algorithms to test
* @param passwd a password phrase
* @param out print stream object
* @return PBEWrapper in accordance to requested algorithm
* @throws Exception all exception are thrown.
*/
public static PBEWrapper createWrapper(Provider p, String algo,
String passwd, PrintStream out) throws Exception {
if (algo.toUpperCase().contains(PBKDF2)) {
return new PBKDF2Wrapper(p, algo, passwd, out);
} else if (algo.toUpperCase().contains(AES)) {
return new AESPBEWrapper(p, algo, passwd, out);
} else {
return new PBECipherWrapper(p, algo, passwd, out);
}
}
}

View File

@ -0,0 +1,114 @@
/*
* Copyright (c) 2012, 2014, 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.PrintStream;
import java.security.Provider;
import javax.crypto.Cipher;
import javax.crypto.SecretKeyFactory;
import javax.crypto.spec.IvParameterSpec;
import javax.crypto.spec.PBEKeySpec;
import javax.crypto.spec.SecretKeySpec;
/**
* Wrapper class to test a given SecretKeyFactory.PBKDF2 algorithm.
*
* @author Alexander Fomin
*/
public class PBKDF2Wrapper extends PBEWrapper {
private static final String CIPHER_TANSFORMATION = "AES/CBC/PKCS5Padding";
private static final int SALT_SIZE = 64;
private static final int PKDF2_DEFAULT_KEY_LEN = 128;
private static volatile byte[] iv;
/**
* PBKDF2Wrapper constructor. Instantiate Cipher using
* "AES/CBC/PKCS5Padding" transformation. Generate a secret key using given
* PKDF2 algorithms.
*
* @param p security Provider
* @param algo PKDF2 algorithm
* @param passwd password phrase
* @param out print stream
* @throws Exception all exceptions are thrown
*/
public PBKDF2Wrapper(Provider p, String algo, String passwd,
PrintStream out) throws Exception {
super(algo,
SecretKeyFactory.getInstance(algo, p).generateSecret(
new PBEKeySpec(passwd.toCharArray(),
generateSalt(SALT_SIZE), ITERATION_COUNT, PKDF2_DEFAULT_KEY_LEN)),
Cipher.getInstance(CIPHER_TANSFORMATION, p), out);
}
/**
* Perform encryption/decryption operation (depending on the specified
* edMode) on the same byte buffer. Compare result with the result at an
* allocated buffer. If both results are equal - return true, otherwise
* return false.
*
* @param edMode specified mode
* @param inputText text to decrypt
* @param offset offset in the text
* @param len input length
* @return ture - test passed; false - test failed
*/
@Override
public boolean execute(int edMode, byte[] inputText, int offset, int len) {
int needBytesForResult = -1;
String KEY_ALGORITHM = "AES";
try {
// init Cipher
if (Cipher.ENCRYPT_MODE == edMode) {
ci.init(Cipher.ENCRYPT_MODE, new SecretKeySpec(key.getEncoded(),
KEY_ALGORITHM));
iv = ci.getParameters().getParameterSpec(IvParameterSpec.class).
getIV();
} else {
ci.init(Cipher.DECRYPT_MODE,
new SecretKeySpec(key.getEncoded(), KEY_ALGORITHM),
new IvParameterSpec(iv));
}
// First, generate the cipherText at an allocated buffer
byte[] outputText = ci.doFinal(inputText, offset, len);
// Second, generate cipherText again at the same buffer of plainText
int myoff = offset / 2;
int off = ci.update(inputText, offset, len, inputText, myoff);
ci.doFinal(inputText, myoff + off);
// Compare to see whether the two results are the same or not
return equalsBlock(inputText, myoff, outputText, 0,
outputText.length);
} catch (Exception ex) {
out.println("Catch unexpected exception within " + algo
+ " " + edMode + ": " + ex.getMessage()
+ ". getOutputSize()" + "returned " + needBytesForResult);
ex.printStackTrace(out);
return false;
}
}
}

View File

@ -0,0 +1,248 @@
/*
* Copyright (c) 2012, 2014, 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.nio.ByteBuffer;
import java.security.InvalidKeyException;
import java.security.NoSuchAlgorithmException;
import java.security.SecureRandom;
import java.security.spec.InvalidKeySpecException;
import java.util.Random;
import javax.crypto.Mac;
import javax.crypto.SecretKey;
import javax.crypto.SecretKeyFactory;
import javax.crypto.spec.PBEKeySpec;
/**
* @test
* @bug 8041787
* @summary verify that Mac.update works with different size ByteBuffer
* @author Alexander Fomin
* @run main PBMacBuffer
*/
public class PBMacBuffer {
private final int LARGE_SIZE = 500000;
public static void main(String[] args) {
String[] PBMAC1Algorithms = {
"HmacPBESHA1",
"PBEWithHmacSHA1",
"PBEWithHmacSHA224",
"PBEWithHmacSHA256",
"PBEWithHmacSHA384",
"PBEWithHmacSHA512"
};
String[] PBKDF2Algorithms = {
"PBKDF2WithHmacSHA1",
"PBKDF2WithHmacSHA224",
"PBKDF2WithHmacSHA256",
"PBKDF2WithHmacSHA384",
"PBKDF2WithHmacSHA512"
};
PBMacBuffer testRunner = new PBMacBuffer();
boolean failed = false;
for (String thePBMacAlgo : PBMAC1Algorithms) {
for (String thePBKDF2Algo : PBKDF2Algorithms) {
System.out.println("Running test with " + thePBMacAlgo
+ " and " + thePBKDF2Algo + ":");
try {
if (!testRunner.doTest(thePBMacAlgo, thePBKDF2Algo)) {
failed = true;
}
} catch (NoSuchAlgorithmException | InvalidKeyException |
InvalidKeySpecException e) {
failed = true;
e.printStackTrace(System.out);
System.out.println("Test FAILED.");
}
}
}
if (failed) {
throw new RuntimeException("One or more tests failed....");
}
}
/**
* Tests Mac.update(ByteBuffer input) method. Three test cases are
* performed: - large ByteBuffer test case to test if the update() method
* process a large ByteBuffer correctly; - empty ByteBuffer test case to
* test if the update() method process an empty ByteBuffer correctly; - NULL
* ByteBuffer test case to test if the update() method throws expected
* IllegalArgumentException exception.
*
* @param theMacAlgo PBMAC algorithm to test
* @param thePBKDF2Algo PBKDF2 algorithm to test
* @return true - test passed; false - otherwise.
* @throws NoSuchAlgorithmException
* @throws InvalidKeyException
* @throws InvalidKeySpecException
* @see javax.crypto.Mac
*/
protected boolean doTest(String theMacAlgo, String thePBKDF2Algo)
throws NoSuchAlgorithmException, InvalidKeyException,
InvalidKeySpecException {
// obtain a SecretKey using PBKDF2
SecretKey key = getSecretKey(thePBKDF2Algo);
// Instantiate Mac object and init it with a SecretKey
Mac theMac = Mac.getInstance(theMacAlgo);
theMac.init(key);
// Do large ByteBuffer test case
if (!largeByteBufferTest(theMac)) {
System.out.println("Large ByteBuffer test case failed.");
return false;
}
// Do empty ByteBuffer test case
if (!emptyByteBufferTest(theMac)) {
System.out.println("Empty ByteBuffer test case failed.");
return false;
}
// Do null ByteBuffer test case
if (!nullByteBufferTest(theMac)) {
System.out.println("NULL ByteBuffer test case failed.");
return false;
}
return true;
}
/**
* Large ByteBuffer test case. Generate random ByteBuffer of LARGE_SIZE
* size. Performs MAC operation with the given Mac object (theMac
* parameter).Verifies the assertion "Upon return, the buffer's position
* will be equal to its limit; its limit will not have changed".
*
* @param theMac MAC object to test.
* @return true - test case passed; false - otherwise;
*/
protected boolean largeByteBufferTest(Mac theMac) {
ByteBuffer buf = generateRandomByteBuffer(LARGE_SIZE);
int limitBefore = buf.limit();
theMac.update(buf);
theMac.doFinal();
int limitAfter = buf.limit();
int positonAfter = buf.position();
if (limitAfter != limitBefore) {
System.out.println("FAIL: Buffer's limit has been chenged.");
return false;
}
if (positonAfter != limitAfter) {
System.out.println("FAIL: "
+ "Buffer's position isn't equal to its limit");
return false;
}
return true;
}
/**
* Empty ByteBuffer test case. Generates an empty ByteBuffer. Perform MAC
* operation. No exceptions are expected.
*
* @param theMac
* @return true - test case pass; exception otherwise
*/
protected boolean emptyByteBufferTest(Mac theMac) {
ByteBuffer buf = generateRandomByteBuffer(0);
theMac.update(buf);
theMac.doFinal();
return true;
}
/**
* NULL ByteBuffer test case. Pass NULL ByteBuffer to Mac.update(ByteBuffer
* buffer) method. An IllegalArgumentException expected.
*
* @param theMac Mac object to test.
* @return true - test case pass; false - otherwise.
*/
protected boolean nullByteBufferTest(Mac theMac) {
try {
ByteBuffer buf = null;
theMac.update(buf);
theMac.doFinal();
} catch (IllegalArgumentException e) {
// expected exception has been thrown
return true;
}
System.out.println("FAIL: "
+ "IllegalArgumentException hasn't been thrown as expected");
return false;
}
/**
* Get SecretKey for the given PBKDF2 algorithm.
*
* @param thePBKDF2Algorithm - PBKDF2 algorithm
* @return SecretKey according to thePBKDF2Algorithm
* @throws NoSuchAlgorithmException
* @throws InvalidKeySpecException
*/
protected SecretKey getSecretKey(String thePBKDF2Algorithm)
throws NoSuchAlgorithmException, InvalidKeySpecException {
// Prepare salt
byte[] salt = new byte[64]; // PKCS #5 v2.1 recommendation
new SecureRandom().nextBytes(salt);
// Generate secret key
PBEKeySpec pbeKeySpec = new PBEKeySpec(
"A #pwd# implied to be hidden!".toCharArray(),
salt, 1000, 128);
SecretKeyFactory keyFactory
= SecretKeyFactory.getInstance(thePBKDF2Algorithm);
return keyFactory.generateSecret(pbeKeySpec);
}
/**
* An utility method to generate a random ByteBuffer of the requested size.
*
* @param size size of the ByteBuffer.
* @return ByteBuffer populated random data;
*/
private ByteBuffer generateRandomByteBuffer(int size) {
// generate randome byte array
byte[] data = new byte[size];
new Random().nextBytes(data);
// create ByteBuffer
ByteBuffer bb = ByteBuffer.wrap(data);
return bb;
}
}

View File

@ -0,0 +1,212 @@
/*
* Copyright (c) 2012, 2014, 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.security.InvalidKeyException;
import java.security.NoSuchAlgorithmException;
import java.security.SecureRandom;
import java.security.spec.InvalidKeySpecException;
import javax.crypto.Mac;
import javax.crypto.SecretKey;
import javax.crypto.SecretKeyFactory;
import javax.crypto.spec.PBEKeySpec;
/**
* @test
* @bug 8041787
* @summary Check if doFinal and update operation result in same PBMac
* @author Alexander Fomin
* @run main PBMacDoFinalVsUpdate
*/
public class PBMacDoFinalVsUpdate {
public static void main(String[] args) {
String[] PBMAC1Algorithms = {
"HmacPBESHA1",
"PBEWithHmacSHA1",
"PBEWithHmacSHA224",
"PBEWithHmacSHA256",
"PBEWithHmacSHA384",
"PBEWithHmacSHA512"
};
String[] PBKDF2Algorithms = {
"PBKDF2WithHmacSHA1",
"PBKDF2WithHmacSHA224",
"PBKDF2WithHmacSHA256",
"PBKDF2WithHmacSHA384",
"PBKDF2WithHmacSHA512"
};
PBMacDoFinalVsUpdate testRunner = new PBMacDoFinalVsUpdate();
boolean failed = false;
for (String thePBMacAlgo : PBMAC1Algorithms) {
for (String thePBKDF2Algo : PBKDF2Algorithms) {
System.out.println("Running test with " + thePBMacAlgo
+ " and " + thePBKDF2Algo + ":");
try {
if (!testRunner.doTest(thePBMacAlgo, thePBKDF2Algo)) {
failed = true;
}
} catch (NoSuchAlgorithmException | InvalidKeyException |
InvalidKeySpecException e) {
failed = true;
e.printStackTrace(System.out);
System.out.println("Test FAILED.");
}
}
}
if (failed) {
throw new RuntimeException("One or more tests failed....");
}
}
/**
* Uses a random generator to initialize a message, instantiate a Mac object
* according to the given PBMAC1 algorithm, initialize the object with a
* SecretKey derived using PBKDF2 algorithm (see PKCS #5 v21, chapter 7.1),
* feed the message into the Mac object all at once and get the output MAC
* as result1. Reset the Mac object, chop the message into three pieces,
* feed into the Mac object sequentially, and get the output MAC as result2.
* Finally, compare result1 and result2 and see if they are the same.
*
* @param theMacAlgo PBMAC algorithm to test
* @param thePBKDF2Algo PBKDF2 algorithm to test
* @return true - the test is passed; false - otherwise.
* @throws NoSuchAlgorithmException
* @throws InvalidKeyException
* @throws InvalidKeySpecException
*/
protected boolean doTest(String theMacAlgo, String thePBKDF2Algo)
throws NoSuchAlgorithmException, InvalidKeyException,
InvalidKeySpecException {
int OFFSET = 5;
// Some message for which a MAC result will be calculated
byte[] plain = new byte[25];
new SecureRandom().nextBytes(plain);
// Form tail - is one of the three pieces
byte[] tail = new byte[plain.length - OFFSET];
System.arraycopy(plain, OFFSET, tail, 0, tail.length);
// Obtain a SecretKey using PBKDF2
SecretKey key = getSecretKey(thePBKDF2Algo);
// Instantiate Mac object and init it with a SecretKey and calc result1
Mac theMac = Mac.getInstance(theMacAlgo);
theMac.init(key);
byte[] result1 = theMac.doFinal(plain);
if (!isMacLengthExpected(theMacAlgo, result1.length)) {
return false;
}
// Reset Mac and calculate result2
theMac.reset();
theMac.update(plain[0]);
theMac.update(plain, 1, OFFSET - 1);
byte[] result2 = theMac.doFinal(tail);
// Return result
if (!java.util.Arrays.equals(result1, result2)) {
System.out.println("result1 and result2 are not the same:");
System.out.println("result1: " + dumpByteArray(result1));
System.out.println("result2: " + dumpByteArray(result2));
return false;
} else {
System.out.println("Resulted MAC with update and doFinal is same");
}
return true;
}
/**
* Get SecretKey for the given PBKDF2 algorithm.
*
* @param thePBKDF2Algorithm - PBKDF2 algorithm
* @return SecretKey according to thePBKDF2Algorithm
* @throws NoSuchAlgorithmException
* @throws InvalidKeySpecException
*/
protected SecretKey getSecretKey(String thePBKDF2Algorithm)
throws NoSuchAlgorithmException, InvalidKeySpecException {
// Prepare salt
byte[] salt = new byte[64]; // PKCS #5 v2.1 recommendation
new SecureRandom().nextBytes(salt);
// Generate secret key
PBEKeySpec pbeKeySpec = new PBEKeySpec(
"A #pwd# implied to be hidden!".toCharArray(),
salt, 1000, 128);
SecretKeyFactory keyFactory
= SecretKeyFactory.getInstance(thePBKDF2Algorithm);
return keyFactory.generateSecret(pbeKeySpec);
}
/**
* Check if the lengthToCheck is expected length for the given MACAlgo.
*
* @param MACAlgo PBMAC algorithm
* @param lengthToCheck the length of MAC need to check
* @return true - lengthToCheck is expected length for the MACAlgo; false -
* otherwise.
*/
protected boolean isMacLengthExpected(String MACAlgo, int lengthToCheck) {
java.util.regex.Pattern p = java.util.regex.Pattern.compile("(\\d+)",
java.util.regex.Pattern.CASE_INSENSITIVE);
java.util.regex.Matcher m = p.matcher(MACAlgo);
int val = 0;
if (m.find()) {
val = Integer.parseInt(m.group(1));
}
// HmacPBESHA1 should return MAC 20 byte length
if ((val == 1) && (lengthToCheck == 20)) {
return true;
}
return (val / 8) == lengthToCheck;
}
/**
* An utility method to dump a byte array for debug output.
*
* @param theByteArray the byte array to dump
* @return string representation of the theByteArray in Hex.
*/
protected String dumpByteArray(byte[] theByteArray) {
StringBuilder buf = new StringBuilder();
for (byte b : theByteArray) {
buf.append(Integer.toHexString(b));
}
return buf.toString();
}
}

View File

@ -0,0 +1,117 @@
/*
* Copyright (c) 2012, 2014, 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.PrintStream;
import java.security.NoSuchAlgorithmException;
import java.security.Provider;
import java.security.Security;
import javax.crypto.Cipher;
import javax.crypto.NoSuchPaddingException;
/**
* @test
* @bug 8041787
* @summary Verify that for PBEWithMD5AndDES cipher, only CBC mode and
* PKCS#5Padding is allowed
* @author Yun Ke
* @author Bill Situ
* @author Yu-Ching (Valerie) PENG
* @run main TestCipherKeyWrapperPBEKey
*/
public class TestCipherPBECons {
private static final String[] PBEAlgorithms = {"pbeWithMD5ANDdes",
"PBEWithMD5AndTripleDES"};
private static final String[] cipherModes = {"ECb", "cbC", "cFB", "Cfb32",
"OfB", "oFb64", "pCbC"};
private static final String[] cipherPaddings = {"Pkcs5Padding", "NoPaDDing"};
public static void main(String[] args) {
TestCipherPBECons test = new TestCipherPBECons();
Provider sunjce = Security.getProvider("SunJCE");
if (!test.runAll(sunjce, System.out)) {
throw new RuntimeException("One or more tests have failed....");
}
}
public boolean runAll(Provider p, PrintStream out) {
boolean finalResult = true;
for (String algorithm : PBEAlgorithms) {
for (String mode : cipherModes) {
for (String padding : cipherPaddings) {
out.println("Running test with " + algorithm
+ "/" + mode + "/" + padding);
try {
if (!runTest(p, algorithm, mode, padding, out)) {
finalResult = false;
out.println("STATUS: Failed");
} else {
out.println("STATUS: Passed");
}
} catch (Exception ex) {
finalResult = false;
ex.printStackTrace(out);
out.println("STATUS:Failed");
}
}
}
}
return finalResult;
}
public boolean runTest(Provider p, String algo, String mo, String pad,
PrintStream out) throws Exception {
try {
// Initialization
Cipher ci = Cipher.getInstance(algo + "/" + mo + "/" + pad, p);
// No exception thrown, must be of the right mode and right
// padding scheme
return (mo.equalsIgnoreCase("CBC"))
&& (pad.equalsIgnoreCase("PKCS5Padding"));
} catch (NoSuchAlgorithmException ex) {
if (p.getName().compareTo("SunJCE") == 0) {
if (!(mo.equalsIgnoreCase("CBC")
&& pad.equalsIgnoreCase("PKCS5Padding"))) {
out.println("NoSuchAlgorithmException is as expected");
return true;
}
}
out.println("Caught exception: " + ex.getMessage());
throw ex;
} catch (NoSuchPaddingException ex) {
if (mo.equalsIgnoreCase("CBC")
&& pad.equalsIgnoreCase("NoPadding")) {
out.println("NoSuchPaddingException is as expected");
return true;
} else {
out.println("Caught unexpected exception: " + ex.getMessage());
return false;
}
}
}
}