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 
  31 import java.security.*;
  32 import javax.crypto.*;
  33 import javax.crypto.spec.*;
  34 import java.math.*;
  35 
  36 import java.util.*;
  37 
  38 public 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