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