1/* 2 * Copyright (c) 2014, 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 24/* 25 * @test 26 * @bug 8014374 27 * @summary Ensure that same key+iv can't be repeatedly used for encryption. 28 * @author Valerie Peng 29 */ 30 31import java.security.*; 32import javax.crypto.*; 33import javax.crypto.spec.*; 34import java.math.*; 35 36import java.util.*; 37 38public class TestGCMKeyAndIvCheck extends UcryptoTest { 39 40 private static final byte[] AAD = new byte[5]; 41 private static final byte[] PT = new byte[33]; 42 43 private static void checkISE(Cipher c) throws Exception { 44 // Subsequent encryptions should fail 45 try { 46 c.updateAAD(AAD); 47 throw new Exception("Should throw ISE for updateAAD()"); 48 } catch (IllegalStateException ise) { 49 // expected 50 } 51 52 try { 53 c.update(PT); 54 throw new Exception("Should throw ISE for update()"); 55 } catch (IllegalStateException ise) { 56 // expected 57 } 58 try { 59 c.doFinal(PT); 60 throw new Exception("Should throw ISE for doFinal()"); 61 } catch (IllegalStateException ise) { 62 // expected 63 } 64 } 65 66 public static void main(String[] args) throws Exception { 67 main(new TestGCMKeyAndIvCheck(), null); 68 } 69 70 public void doTest(Provider p) throws Exception { 71 Cipher c; 72 try { 73 c = Cipher.getInstance("AES/GCM/NoPadding", p); 74 } catch (NoSuchAlgorithmException nsae) { 75 System.out.println("Skipping Test due to No GCM support"); 76 return; 77 } 78 79 SecretKey key = new SecretKeySpec(new byte[16], "AES"); 80 // First try parameter-less init. 81 c.init(Cipher.ENCRYPT_MODE, key); 82 c.updateAAD(AAD); 83 byte[] ctPlusTag = c.doFinal(PT); 84 85 // subsequent encryption should fail unless re-init w/ different key+iv 86 checkISE(c); 87 88 // Validate the retrieved parameters against the IV and tag length. 89 AlgorithmParameters params = c.getParameters(); 90 if (params == null) { 91 throw new Exception("getParameters() should not return null"); 92 } 93 GCMParameterSpec spec = params.getParameterSpec(GCMParameterSpec.class); 94 if (spec.getTLen() != (ctPlusTag.length - PT.length)*8) { 95 throw new Exception("Parameters contains incorrect TLen value"); 96 } 97 if (!Arrays.equals(spec.getIV(), c.getIV())) { 98 throw new Exception("Parameters contains incorrect IV value"); 99 } 100 101 // Should be ok to use the same key+iv for decryption 102 c.init(Cipher.DECRYPT_MODE, key, params); 103 c.updateAAD(AAD); 104 byte[] recovered = c.doFinal(ctPlusTag); 105 if (!Arrays.equals(recovered, PT)) { 106 throw new Exception("decryption result mismatch"); 107 } 108 109 // Now try to encrypt again using the same key+iv; should fail also 110 try { 111 c.init(Cipher.ENCRYPT_MODE, key, params); 112 throw new Exception("Should throw exception when same key+iv is used"); 113 } catch (InvalidAlgorithmParameterException iape) { 114 // expected 115 } 116 117 // Now try to encrypt again using parameter-less init; should work 118 c.init(Cipher.ENCRYPT_MODE, key); 119 c.doFinal(PT); 120 121 // make sure a different iv is used 122 byte[] iv = c.getIV(); 123 if (Arrays.equals(spec.getIV(), iv)) { 124 throw new Exception("IV should be different now"); 125 } 126 127 // Now try to encrypt again using a different parameter; should work 128 byte[] rdm_iv = new byte[30]; 129 Random rdm = new Random(); 130 rdm.nextBytes(rdm_iv); 131 132 c.init(Cipher.ENCRYPT_MODE, key, new GCMParameterSpec(128, rdm_iv)); 133 c.updateAAD(AAD); 134 c.doFinal(PT); 135 // subsequent encryption should fail unless re-init w/ different key+iv 136 checkISE(c); 137 138 // Now try decryption twice in a row; no re-init required and 139 // same parameters is used. 140 c.init(Cipher.DECRYPT_MODE, key, params); 141 c.updateAAD(AAD); 142 recovered = c.doFinal(ctPlusTag); 143 144 c.updateAAD(AAD); 145 recovered = c.doFinal(ctPlusTag); 146 if (!Arrays.equals(recovered, PT)) { 147 throw new Exception("decryption result mismatch"); 148 } 149 150 // Now try decryption again and re-init using the same parameters 151 c.init(Cipher.DECRYPT_MODE, key, params); 152 c.updateAAD(AAD); 153 recovered = c.doFinal(ctPlusTag); 154 155 // init to decrypt w/o parameters; should fail with IKE as 156 // javadoc specified 157 try { 158 c.init(Cipher.DECRYPT_MODE, key); 159 throw new Exception("Should throw IKE for dec w/o params"); 160 } catch (InvalidKeyException ike) { 161 // expected 162 } 163 164 // Lastly, try encryption AND decryption w/ wrong type of parameters, 165 // e.g. IvParameterSpec 166 try { 167 c.init(Cipher.ENCRYPT_MODE, key, new IvParameterSpec(iv)); 168 throw new Exception("Should throw IAPE"); 169 } catch (InvalidAlgorithmParameterException iape) { 170 // expected 171 } 172 try { 173 c.init(Cipher.DECRYPT_MODE, key, new IvParameterSpec(iv)); 174 throw new Exception("Should throw IAPE"); 175 } catch (InvalidAlgorithmParameterException iape) { 176 // expected 177 } 178 179 System.out.println("Test Passed!"); 180 } 181} 182 183