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