1/* 2 * Copyright (c) 2007, 2015, Oracle and/or its affiliates. All rights reserved. 3 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. 4 * 5 * This code is free software; you can redistribute it and/or modify it 6 * under the terms of the GNU General Public License version 2 only, as 7 * published by the Free Software Foundation. 8 * 9 * This code is distributed in the hope that it will be useful, but WITHOUT 10 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or 11 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License 12 * version 2 for more details (a copy is included in the LICENSE file that 13 * accompanied this code). 14 * 15 * You should have received a copy of the GNU General Public License version 16 * 2 along with this work; if not, write to the Free Software Foundation, 17 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. 18 * 19 * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA 20 * or visit www.oracle.com if you need additional information or have any 21 * questions. 22 */ 23 24import java.security.NoSuchAlgorithmException; 25import java.security.NoSuchProviderException; 26import java.util.Arrays; 27import javax.crypto.SecretKey; 28import javax.crypto.Cipher; 29import javax.crypto.KeyGenerator; 30import javax.crypto.spec.GCMParameterSpec; 31 32/* 33 * @test 34 * @bug 8048596 35 * @summary Check if GCMParameterSpec works as expected 36 */ 37public class GCMParameterSpecTest { 38 39 private static final int[] IV_LENGTHS = { 96, 8, 1024 }; 40 private static final int[] KEY_LENGTHS = { 128, 192, 256 }; 41 private static final int[] DATA_LENGTHS = { 0, 128, 1024 }; 42 private static final int[] AAD_LENGTHS = { 0, 128, 1024 }; 43 private static final int[] TAG_LENGTHS = { 128, 120, 112, 104, 96 }; 44 private static final int[] OFFSETS = { 0, 2, 5, 99 }; 45 private static final String TRANSFORMATION = "AES/GCM/NoPadding"; 46 private static final String TEMPLATE = "Test:\n tag = %d\n" 47 + " IV length = %d\n data length = %d\n" 48 + " AAD length = %d\n offset = %d\n keylength = %d\n"; 49 50 private final byte[] IV; 51 private final byte[] IVO; 52 private final byte[] data; 53 private final byte[] AAD; 54 private final SecretKey key; 55 private final int tagLength; 56 private final int IVlength; 57 private final int offset; 58 59 /** 60 * Initialize IV, IV with offset, plain text, AAD and SecretKey 61 * 62 * @param keyLength length of a secret key 63 * @param tagLength tag length 64 * @param IVlength IV length 65 * @param offset offset in a buffer for IV 66 * @param textLength plain text length 67 * @param AADLength AAD length 68 */ 69 public GCMParameterSpecTest(int keyLength, int tagLength, int IVlength, 70 int offset, int textLength, int AADLength) 71 throws NoSuchAlgorithmException, NoSuchProviderException { 72 this.tagLength = tagLength; // save tag length 73 this.IVlength = IVlength; // save IV length 74 this.offset = offset; // save IV offset 75 76 // prepare IV 77 IV = Helper.generateBytes(IVlength); 78 79 // prepare IV with offset 80 IVO = new byte[this.IVlength + this.offset]; 81 System.arraycopy(IV, 0, IVO, offset, this.IVlength); 82 83 // prepare data 84 data = Helper.generateBytes(textLength); 85 86 // prepare AAD 87 AAD = Helper.generateBytes(AADLength); 88 89 // init a secret key 90 KeyGenerator kg = KeyGenerator.getInstance("AES", "SunJCE"); 91 kg.init(keyLength); 92 key = kg.generateKey(); 93 } 94 95 /* 96 * Run the test for each key length, tag length, IV length, plain text 97 * length, AAD length and offset. 98 */ 99 public static void main(String[] args) throws Exception { 100 boolean success = true; 101 for (int k : KEY_LENGTHS) { 102 if (k > Cipher.getMaxAllowedKeyLength(TRANSFORMATION)) { 103 // skip this if this key length is larger than what's 104 // allowed in the jce jurisdiction policy files 105 continue; 106 } 107 for (int t : TAG_LENGTHS) { 108 for (int n : IV_LENGTHS) { 109 for (int p : DATA_LENGTHS) { 110 for (int a : AAD_LENGTHS) { 111 for (int o : OFFSETS) { 112 System.out.printf(TEMPLATE, t, n, p, a, o, k); 113 success &= new GCMParameterSpecTest( 114 k, t, n, o, p, a).doTest(); 115 } 116 } 117 } 118 } 119 } 120 } 121 122 if (!success) { 123 throw new RuntimeException("At least one test case failed"); 124 } 125 } 126 127 /* 128 * Run the test: 129 * - check if result of encryption of plain text is the same 130 * when parameters constructed with different GCMParameterSpec 131 * constructors are used 132 * - check if GCMParameterSpec.getTLen() is equal to actual tag length 133 * - check if ciphertext has the same length as plaintext 134 */ 135 private boolean doTest() throws Exception { 136 GCMParameterSpec spec1 = new GCMParameterSpec(tagLength, IV); 137 GCMParameterSpec spec2 = new GCMParameterSpec(tagLength, IVO, offset, 138 IVlength); 139 byte[] cipherText1 = getCipherTextBySpec(spec1); 140 if (cipherText1 == null) { 141 return false; 142 } 143 byte[] cipherText2 = getCipherTextBySpec(spec2); 144 if (cipherText2 == null) { 145 return false; 146 } 147 if (!Arrays.equals(cipherText1, cipherText2)) { 148 System.out.println("Cipher texts are different"); 149 return false; 150 } 151 if (spec1.getTLen() != spec2.getTLen()) { 152 System.out.println("Tag lengths are not equal"); 153 return false; 154 } 155 byte[] recoveredText1 = recoverCipherText(cipherText1, spec2); 156 if (recoveredText1 == null) { 157 return false; 158 } 159 byte[] recoveredText2 = recoverCipherText(cipherText2, spec1); 160 if (recoveredText2 == null) { 161 return false; 162 } 163 if (!Arrays.equals(recoveredText1, recoveredText2)) { 164 System.out.println("Recovered texts are different"); 165 return false; 166 } 167 if (!Arrays.equals(recoveredText1, data)) { 168 System.out.println("Recovered and original texts are not equal"); 169 return false; 170 } 171 172 return true; 173 } 174 175 /* 176 * Encrypt a plain text, and check if GCMParameterSpec.getIV() 177 * is equal to Cipher.getIV() 178 */ 179 private byte[] getCipherTextBySpec(GCMParameterSpec spec) throws Exception { 180 // init a cipher 181 Cipher cipher = createCipher(Cipher.ENCRYPT_MODE, spec); 182 cipher.updateAAD(AAD); 183 byte[] cipherText = cipher.doFinal(data); 184 185 // check IVs 186 if (!Arrays.equals(cipher.getIV(), spec.getIV())) { 187 System.out.println("IV in parameters is incorrect"); 188 return null; 189 } 190 if (spec.getTLen() != (cipherText.length - data.length) * 8) { 191 System.out.println("Tag length is incorrect"); 192 return null; 193 } 194 return cipherText; 195 } 196 197 private byte[] recoverCipherText(byte[] cipherText, GCMParameterSpec spec) 198 throws Exception { 199 // init a cipher 200 Cipher cipher = createCipher(Cipher.DECRYPT_MODE, spec); 201 202 // check IVs 203 if (!Arrays.equals(cipher.getIV(), spec.getIV())) { 204 System.out.println("IV in parameters is incorrect"); 205 return null; 206 } 207 208 cipher.updateAAD(AAD); 209 return cipher.doFinal(cipherText); 210 } 211 212 private Cipher createCipher(int mode, GCMParameterSpec spec) 213 throws Exception { 214 Cipher cipher = Cipher.getInstance(TRANSFORMATION, "SunJCE"); 215 cipher.init(mode, key, spec); 216 return cipher; 217 } 218} 219