219 lines
7.8 KiB
Java
219 lines
7.8 KiB
Java
|
/*
|
||
|
* 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 java.security.NoSuchAlgorithmException;
|
||
|
import java.security.NoSuchProviderException;
|
||
|
import java.util.Arrays;
|
||
|
import javax.crypto.SecretKey;
|
||
|
import javax.crypto.Cipher;
|
||
|
import javax.crypto.KeyGenerator;
|
||
|
import javax.crypto.spec.GCMParameterSpec;
|
||
|
|
||
|
/*
|
||
|
* @test
|
||
|
* @bug 8048596
|
||
|
* @summary Check if GCMParameterSpec works as expected
|
||
|
*/
|
||
|
public class GCMParameterSpecTest {
|
||
|
|
||
|
private static final int[] IV_LENGTHS = { 96, 8, 1024 };
|
||
|
private static final int[] KEY_LENGTHS = { 128, 192, 256 };
|
||
|
private static final int[] DATA_LENGTHS = { 0, 128, 1024 };
|
||
|
private static final int[] AAD_LENGTHS = { 0, 128, 1024 };
|
||
|
private static final int[] TAG_LENGTHS = { 128, 120, 112, 104, 96 };
|
||
|
private static final int[] OFFSETS = { 0, 2, 5, 99 };
|
||
|
private static final String TRANSFORMATION = "AES/GCM/NoPadding";
|
||
|
private static final String TEMPLATE = "Test:\n tag = %d\n"
|
||
|
+ " IV length = %d\n data length = %d\n"
|
||
|
+ " AAD length = %d\n offset = %d\n keylength = %d\n";
|
||
|
|
||
|
private final byte[] IV;
|
||
|
private final byte[] IVO;
|
||
|
private final byte[] data;
|
||
|
private final byte[] AAD;
|
||
|
private final SecretKey key;
|
||
|
private final int tagLength;
|
||
|
private final int IVlength;
|
||
|
private final int offset;
|
||
|
|
||
|
/**
|
||
|
* Initialize IV, IV with offset, plain text, AAD and SecretKey
|
||
|
*
|
||
|
* @param keyLength length of a secret key
|
||
|
* @param tagLength tag length
|
||
|
* @param IVlength IV length
|
||
|
* @param offset offset in a buffer for IV
|
||
|
* @param textLength plain text length
|
||
|
* @param AADLength AAD length
|
||
|
*/
|
||
|
public GCMParameterSpecTest(int keyLength, int tagLength, int IVlength,
|
||
|
int offset, int textLength, int AADLength)
|
||
|
throws NoSuchAlgorithmException, NoSuchProviderException {
|
||
|
this.tagLength = tagLength; // save tag length
|
||
|
this.IVlength = IVlength; // save IV length
|
||
|
this.offset = offset; // save IV offset
|
||
|
|
||
|
// prepare IV
|
||
|
IV = Helper.generateBytes(IVlength);
|
||
|
|
||
|
// prepare IV with offset
|
||
|
IVO = new byte[this.IVlength + this.offset];
|
||
|
System.arraycopy(IV, 0, IVO, offset, this.IVlength);
|
||
|
|
||
|
// prepare data
|
||
|
data = Helper.generateBytes(textLength);
|
||
|
|
||
|
// prepare AAD
|
||
|
AAD = Helper.generateBytes(AADLength);
|
||
|
|
||
|
// init a secret key
|
||
|
KeyGenerator kg = KeyGenerator.getInstance("AES", "SunJCE");
|
||
|
kg.init(keyLength);
|
||
|
key = kg.generateKey();
|
||
|
}
|
||
|
|
||
|
/*
|
||
|
* Run the test for each key length, tag length, IV length, plain text
|
||
|
* length, AAD length and offset.
|
||
|
*/
|
||
|
public static void main(String[] args) throws Exception {
|
||
|
boolean success = true;
|
||
|
for (int k : KEY_LENGTHS) {
|
||
|
if (k > Cipher.getMaxAllowedKeyLength(TRANSFORMATION)) {
|
||
|
// skip this if this key length is larger than what's
|
||
|
// allowed in the jce jurisdiction policy files
|
||
|
continue;
|
||
|
}
|
||
|
for (int t : TAG_LENGTHS) {
|
||
|
for (int n : IV_LENGTHS) {
|
||
|
for (int p : DATA_LENGTHS) {
|
||
|
for (int a : AAD_LENGTHS) {
|
||
|
for (int o : OFFSETS) {
|
||
|
System.out.printf(TEMPLATE, t, n, p, a, o, k);
|
||
|
success &= new GCMParameterSpecTest(
|
||
|
k, t, n, o, p, a).doTest();
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
if (!success) {
|
||
|
throw new RuntimeException("At least one test case failed");
|
||
|
}
|
||
|
}
|
||
|
|
||
|
/*
|
||
|
* Run the test:
|
||
|
* - check if result of encryption of plain text is the same
|
||
|
* when parameters constructed with different GCMParameterSpec
|
||
|
* constructors are used
|
||
|
* - check if GCMParameterSpec.getTLen() is equal to actual tag length
|
||
|
* - check if ciphertext has the same length as plaintext
|
||
|
*/
|
||
|
private boolean doTest() throws Exception {
|
||
|
GCMParameterSpec spec1 = new GCMParameterSpec(tagLength, IV);
|
||
|
GCMParameterSpec spec2 = new GCMParameterSpec(tagLength, IVO, offset,
|
||
|
IVlength);
|
||
|
byte[] cipherText1 = getCipherTextBySpec(spec1);
|
||
|
if (cipherText1 == null) {
|
||
|
return false;
|
||
|
}
|
||
|
byte[] cipherText2 = getCipherTextBySpec(spec2);
|
||
|
if (cipherText2 == null) {
|
||
|
return false;
|
||
|
}
|
||
|
if (!Arrays.equals(cipherText1, cipherText2)) {
|
||
|
System.out.println("Cipher texts are different");
|
||
|
return false;
|
||
|
}
|
||
|
if (spec1.getTLen() != spec2.getTLen()) {
|
||
|
System.out.println("Tag lengths are not equal");
|
||
|
return false;
|
||
|
}
|
||
|
byte[] recoveredText1 = recoverCipherText(cipherText1, spec2);
|
||
|
if (recoveredText1 == null) {
|
||
|
return false;
|
||
|
}
|
||
|
byte[] recoveredText2 = recoverCipherText(cipherText2, spec1);
|
||
|
if (recoveredText2 == null) {
|
||
|
return false;
|
||
|
}
|
||
|
if (!Arrays.equals(recoveredText1, recoveredText2)) {
|
||
|
System.out.println("Recovered texts are different");
|
||
|
return false;
|
||
|
}
|
||
|
if (!Arrays.equals(recoveredText1, data)) {
|
||
|
System.out.println("Recovered and original texts are not equal");
|
||
|
return false;
|
||
|
}
|
||
|
|
||
|
return true;
|
||
|
}
|
||
|
|
||
|
/*
|
||
|
* Encrypt a plain text, and check if GCMParameterSpec.getIV()
|
||
|
* is equal to Cipher.getIV()
|
||
|
*/
|
||
|
private byte[] getCipherTextBySpec(GCMParameterSpec spec) throws Exception {
|
||
|
// init a cipher
|
||
|
Cipher cipher = createCipher(Cipher.ENCRYPT_MODE, spec);
|
||
|
cipher.updateAAD(AAD);
|
||
|
byte[] cipherText = cipher.doFinal(data);
|
||
|
|
||
|
// check IVs
|
||
|
if (!Arrays.equals(cipher.getIV(), spec.getIV())) {
|
||
|
System.out.println("IV in parameters is incorrect");
|
||
|
return null;
|
||
|
}
|
||
|
if (spec.getTLen() != (cipherText.length - data.length) * 8) {
|
||
|
System.out.println("Tag length is incorrect");
|
||
|
return null;
|
||
|
}
|
||
|
return cipherText;
|
||
|
}
|
||
|
|
||
|
private byte[] recoverCipherText(byte[] cipherText, GCMParameterSpec spec)
|
||
|
throws Exception {
|
||
|
// init a cipher
|
||
|
Cipher cipher = createCipher(Cipher.DECRYPT_MODE, spec);
|
||
|
|
||
|
// check IVs
|
||
|
if (!Arrays.equals(cipher.getIV(), spec.getIV())) {
|
||
|
System.out.println("IV in parameters is incorrect");
|
||
|
return null;
|
||
|
}
|
||
|
|
||
|
cipher.updateAAD(AAD);
|
||
|
return cipher.doFinal(cipherText);
|
||
|
}
|
||
|
|
||
|
private Cipher createCipher(int mode, GCMParameterSpec spec)
|
||
|
throws Exception {
|
||
|
Cipher cipher = Cipher.getInstance(TRANSFORMATION, "SunJCE");
|
||
|
cipher.init(mode, key, spec);
|
||
|
return cipher;
|
||
|
}
|
||
|
}
|