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.io.ByteArrayInputStream; 25import java.io.ByteArrayOutputStream; 26import java.io.IOException; 27import java.security.AlgorithmParameters; 28import java.security.InvalidAlgorithmParameterException; 29import java.security.InvalidKeyException; 30import java.security.NoSuchAlgorithmException; 31import java.security.NoSuchProviderException; 32import java.util.Arrays; 33import javax.crypto.CipherInputStream; 34import javax.crypto.CipherOutputStream; 35import javax.crypto.NoSuchPaddingException; 36import javax.crypto.SecretKey; 37import javax.crypto.Cipher; 38import javax.crypto.KeyGenerator; 39 40/* 41 * @test 42 * @bug 8048596 43 * @summary Check if wrong or empty AAD is rejected 44 */ 45public class WrongAAD { 46 47 private static final String PROVIDER = "SunJCE"; 48 private static final String TRANSFORMATION = "AES/GCM/NoPadding"; 49 private static final int TEXT_SIZE = 800; 50 private static final int KEY_SIZE = 128; 51 private static final int AAD_SIZE = 128; 52 53 private final SecretKey key; 54 private final byte[] plainText; 55 private final Cipher encryptCipher; 56 57 public WrongAAD() throws Exception { 58 // init a secret key 59 KeyGenerator kg = KeyGenerator.getInstance("AES", PROVIDER); 60 kg.init(KEY_SIZE); 61 key = kg.generateKey(); 62 63 // generate a plain text 64 plainText = Helper.generateBytes(TEXT_SIZE); 65 66 // init AADs 67 byte[] AAD = Helper.generateBytes(AAD_SIZE); 68 69 // init a cipher 70 encryptCipher = createCipher(Cipher.ENCRYPT_MODE, null); 71 encryptCipher.updateAAD(AAD); 72 } 73 74 public static void main(String[] args) throws Exception { 75 WrongAAD test = new WrongAAD(); 76 test.decryptWithEmptyAAD(); 77 test.decryptWithWrongAAD(); 78 } 79 80 /* 81 * Attempt to decrypt a cipher text using Cipher object 82 * initialized without AAD used for encryption. 83 */ 84 private void decryptWithEmptyAAD() throws Exception { 85 System.out.println("decryptWithEmptyAAD() started"); 86 // initialize it with empty AAD to get exception during decryption 87 Cipher decryptCipher = createCipher(Cipher.DECRYPT_MODE, 88 encryptCipher.getParameters()); 89 try (ByteArrayOutputStream baOutput = new ByteArrayOutputStream(); 90 CipherOutputStream ciOutput = new CipherOutputStream(baOutput, 91 decryptCipher)) { 92 if (decrypt(ciOutput, baOutput)) { 93 throw new RuntimeException( 94 "Decryption has been perfomed successfully in" 95 + " spite of the decrypt Cipher has NOT been" 96 + " initialized with AAD"); 97 } 98 } 99 System.out.println("decryptWithEmptyAAD() passed"); 100 } 101 102 /* 103 * Attempt to decrypt the cipher text using Cipher object 104 * initialized with some fake AAD. 105 */ 106 private void decryptWithWrongAAD() throws Exception { 107 System.out.println("decrypt with wrong AAD"); 108 109 // initialize it with wrong AAD to get an exception during decryption 110 Cipher decryptCipher = createCipher(Cipher.DECRYPT_MODE, 111 encryptCipher.getParameters()); 112 byte[] someAAD = Helper.generateBytes(AAD_SIZE + 1); 113 decryptCipher.updateAAD(someAAD); 114 115 // init output stream 116 try (ByteArrayOutputStream baOutput = new ByteArrayOutputStream(); 117 CipherOutputStream ciOutput = new CipherOutputStream(baOutput, 118 decryptCipher);) { 119 if (decrypt(ciOutput, baOutput)) { 120 throw new RuntimeException( 121 "A decryption has been perfomed successfully in" 122 + " spite of the decrypt Cipher has been" 123 + " initialized with fake AAD"); 124 } 125 } 126 127 System.out.println("Passed"); 128 } 129 130 private boolean decrypt(CipherOutputStream ciOutput, 131 ByteArrayOutputStream baOutput) throws IOException { 132 try (ByteArrayInputStream baInput = new ByteArrayInputStream(plainText); 133 CipherInputStream ciInput = new CipherInputStream(baInput, 134 encryptCipher)) { 135 byte[] buffer = new byte[TEXT_SIZE]; 136 int len = ciInput.read(buffer); 137 138 while (len != -1) { 139 ciOutput.write(buffer, 0, len); 140 len = ciInput.read(buffer); 141 } 142 ciOutput.flush(); 143 byte[] recoveredText = baOutput.toByteArray(); 144 System.out.println("recoveredText: " + new String(recoveredText)); 145 146 /* 147 * See bug 8012900, AEADBadTagException is swalloed by CI/CO streams 148 * If recovered text is empty, than decryption failed 149 */ 150 if (recoveredText.length == 0) { 151 return false; 152 } 153 return Arrays.equals(plainText, recoveredText); 154 } catch (IllegalStateException e) { 155 System.out.println("Expected IllegalStateException: " 156 + e.getMessage()); 157 e.printStackTrace(System.out); 158 return false; 159 } 160 } 161 162 private Cipher createCipher(int mode, AlgorithmParameters params) 163 throws NoSuchAlgorithmException, NoSuchProviderException, 164 NoSuchPaddingException, InvalidKeyException, 165 InvalidAlgorithmParameterException { 166 Cipher cipher = Cipher.getInstance(TRANSFORMATION, PROVIDER); 167 if (params != null) { 168 cipher.init(mode, key, params); 169 } else { 170 cipher.init(mode, key); 171 } 172 return cipher; 173 } 174} 175