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 
24 import java.io.ByteArrayInputStream;
25 import java.io.ByteArrayOutputStream;
26 import java.io.IOException;
27 import java.security.GeneralSecurityException;
28 import java.security.NoSuchAlgorithmException;
29 import java.util.Arrays;
30 import javax.crypto.Cipher;
31 import javax.crypto.CipherInputStream;
32 import javax.crypto.CipherOutputStream;
33 import javax.crypto.KeyGenerator;
34 import javax.crypto.SecretKey;
35 
36 /*
37  * @test
38  * @bug 8048596
39  * @summary Test CICO AEAD read/write/skip operations
40  */
41 public class ReadWriteSkip {
42 
43     static enum BufferType {
44         BYTE_ARRAY_BUFFERING, INT_BYTE_BUFFERING
45     }
46 
47     static final int KEY_LENGTHS[] = {128, 192, 256};
48     static final int TXT_LENGTHS[] = {800, 0};
49     static final int AAD_LENGTHS[] = {0, 100};
50     static final int BLOCK = 50;
51     static final int SAVE = 45;
52     static final int DISCARD = BLOCK - SAVE;
53     static final String PROVIDER = "SunJCE";
54     static final String AES = "AES";
55     static final String GCM = "GCM";
56     static final String PADDING = "NoPadding";
57     static final String TRANSFORM = AES + "/" + GCM + "/" + PADDING;
58 
59     final SecretKey key;
60     final byte[] plaintext;
61     final byte[] AAD;
62     final int textLength;;
63     final int keyLength;
64     Cipher encryptCipher;
65     Cipher decryptCipher;
66     CipherInputStream ciInput;
67 
main(String[] args)68     public static void main(String[] args) throws Exception {
69         boolean success = true;
70         for (int keyLength : KEY_LENGTHS) {
71             if (keyLength > Cipher.getMaxAllowedKeyLength(TRANSFORM)) {
72                 // skip this if this key length is larger than what's
73                 // configured in the jce jurisdiction policy files
74                 continue;
75             }
76             for (int textLength : TXT_LENGTHS) {
77                 for (int AADLength : AAD_LENGTHS) {
78                     System.out.println("Key length = " + keyLength
79                             + ", text length = " + textLength
80                             + ", AAD length = " + AADLength);
81                     try {
82                         run(keyLength, textLength, AADLength);
83                         System.out.println("Test case passed");
84                     } catch (Exception e) {
85                         System.out.println("Test case failed: " + e);
86                         success = false;
87                     }
88                 }
89             }
90         }
91 
92         if (!success) {
93             throw new RuntimeException("At least one test case failed");
94         }
95 
96         System.out.println("Test passed");
97     }
98 
ReadWriteSkip(int keyLength, int textLength, int AADLength)99     ReadWriteSkip(int keyLength, int textLength, int AADLength)
100             throws Exception {
101         this.keyLength = keyLength;
102         this.textLength = textLength;
103 
104         // init AAD
105         this.AAD = Helper.generateBytes(AADLength);
106 
107         // init a secret Key
108         KeyGenerator kg = KeyGenerator.getInstance(AES, PROVIDER);
109         kg.init(this.keyLength);
110         this.key = kg.generateKey();
111 
112         this.plaintext = Helper.generateBytes(textLength);
113     }
114 
doTest(BufferType type)115     final void doTest(BufferType type) throws Exception {
116         // init ciphers
117         encryptCipher = createCipher(Cipher.ENCRYPT_MODE);
118         decryptCipher = createCipher(Cipher.DECRYPT_MODE);
119 
120         // init cipher input stream
121         ciInput = new CipherInputStream(new ByteArrayInputStream(plaintext),
122                 encryptCipher);
123 
124         runTest(type);
125     }
126 
runTest(BufferType type)127     void runTest(BufferType type) throws Exception {}
128 
createCipher(int mode)129     private Cipher createCipher(int mode) throws GeneralSecurityException {
130         Cipher cipher = Cipher.getInstance(TRANSFORM, PROVIDER);
131         if (mode == Cipher.ENCRYPT_MODE) {
132             cipher.init(Cipher.ENCRYPT_MODE, key);
133         } else {
134             if (encryptCipher != null) {
135                 cipher.init(Cipher.DECRYPT_MODE, key,
136                         encryptCipher.getParameters());
137             } else {
138                 throw new RuntimeException("Can't create a cipher");
139             }
140         }
141         cipher.updateAAD(AAD);
142         return cipher;
143     }
144 
145     /*
146      * Run test cases
147      */
run(int keyLength, int textLength, int AADLength)148     static void run(int keyLength, int textLength, int AADLength)
149             throws Exception {
150         new ReadWriteTest(keyLength, textLength, AADLength)
151                 .doTest(BufferType.BYTE_ARRAY_BUFFERING);
152         new ReadWriteTest(keyLength, textLength, AADLength)
153                 .doTest(BufferType.INT_BYTE_BUFFERING);
154         new SkipTest(keyLength, textLength, AADLength)
155                 .doTest(BufferType.BYTE_ARRAY_BUFFERING);
156         new SkipTest(keyLength, textLength, AADLength)
157                 .doTest(BufferType.INT_BYTE_BUFFERING);
158     }
159 
check(byte[] first, byte[] second)160     static void check(byte[] first, byte[] second) {
161         if (!Arrays.equals(first, second)) {
162             throw new RuntimeException("Arrays are not equal");
163         }
164     }
165 
166     /*
167      * CICO AEAD read/write functional test.
168      *
169      * Check if encrypt/decrypt operations work correctly.
170      *
171      * Test scenario:
172      *   - initializes plain text
173      *   - for given AEAD algorithm instantiates encrypt and decrypt Ciphers
174      *   - instantiates CipherInputStream with the encrypt Cipher
175      *   - instantiates CipherOutputStream with the decrypt Cipher
176      *   - performs reading from the CipherInputStream (encryption data)
177      *     and writing to the CipherOutputStream (decryption). As a result,
178      *     output of the CipherOutputStream should be equal
179      *     with original plain text
180      *   - check if the original plain text is equal to output
181      *     of the CipherOutputStream
182      *   - if it is equal the test passes, otherwise it fails
183      */
184     static class ReadWriteTest extends ReadWriteSkip {
185 
ReadWriteTest(int keyLength, int textLength, int AADLength)186         public ReadWriteTest(int keyLength, int textLength, int AADLength)
187                 throws Exception {
188             super(keyLength, textLength, AADLength);
189         }
190 
191         @Override
runTest(BufferType bufType)192         public void runTest(BufferType bufType) throws IOException,
193                 GeneralSecurityException {
194 
195             ByteArrayOutputStream baOutput = new ByteArrayOutputStream();
196             try (CipherOutputStream ciOutput = new CipherOutputStream(baOutput,
197                     decryptCipher)) {
198                 if (bufType == BufferType.BYTE_ARRAY_BUFFERING) {
199                     doByteTest(ciOutput);
200                 } else {
201                     doIntTest(ciOutput);
202                 }
203             }
204 
205             check(plaintext, baOutput.toByteArray());
206         }
207 
208         /*
209          * Implements byte array buffering type test case
210          */
doByteTest(CipherOutputStream out)211         public void doByteTest(CipherOutputStream out) throws IOException {
212             byte[] buffer = Helper.generateBytes(textLength + 1);
213             int len = ciInput.read(buffer);
214             while (len != -1) {
215                 out.write(buffer, 0, len);
216                 len = ciInput.read(buffer);
217             }
218         }
219 
220         /*
221          * Implements integer buffering type test case
222          */
doIntTest(CipherOutputStream out)223         public void doIntTest(CipherOutputStream out) throws IOException {
224             int buffer = ciInput.read();
225             while (buffer != -1) {
226                 out.write(buffer);
227                 buffer = ciInput.read();
228             }
229         }
230     }
231 
232     /*
233      * CICO AEAD SKIP functional test.
234      *
235      * Checks if the encrypt/decrypt operations work correctly
236      * when skip() method is used.
237      *
238      * Test scenario:
239      *   - initializes a plain text
240      *   - initializes ciphers
241      *   - initializes cipher streams
242      *   - split plain text to TEXT_SIZE/BLOCK blocks
243      *   - read from CipherInputStream2 one block at time
244      *   - the last DISCARD = BLOCK - SAVE bytes are skipping for each block
245      *   - therefore, plain text data go through CipherInputStream1 (encrypting)
246      *     and CipherInputStream2 (decrypting)
247      *   - as a result, output should equal to the original text
248      *     except DISCART byte for each block
249      *   - check result buffers
250      */
251     static class SkipTest extends ReadWriteSkip {
252 
253         private final int numberOfBlocks;
254         private final byte[] outputText;
255 
SkipTest(int keyLength, int textLength, int AADLength)256         public SkipTest(int keyLength, int textLength, int AADLength)
257                 throws Exception {
258             super(keyLength, textLength, AADLength);
259             numberOfBlocks = this.textLength / BLOCK;
260             outputText = new byte[numberOfBlocks * SAVE];
261         }
262 
doByteTest(int blockNum, CipherInputStream cis)263         private void doByteTest(int blockNum, CipherInputStream cis)
264                 throws IOException {
265             int index = blockNum * SAVE;
266             int len = cis.read(outputText, index, SAVE);
267             index += len;
268             int read = 0;
269             while (len != SAVE && read != -1) {
270                 read = cis.read(outputText, index, SAVE - len);
271                 len += read;
272                 index += read;
273             }
274         }
275 
doIntTest(int blockNum, CipherInputStream cis)276         private void doIntTest(int blockNum, CipherInputStream cis)
277                 throws IOException {
278             int i = blockNum * SAVE;
279             for (int j = 0; j < SAVE && i < outputText.length; j++, i++) {
280                 int b = cis.read();
281                 if (b != -1) {
282                     outputText[i] = (byte) b;
283                 }
284             }
285         }
286 
287         @Override
runTest(BufferType type)288         public void runTest(BufferType type) throws IOException,
289                 NoSuchAlgorithmException {
290             try (CipherInputStream cis = new CipherInputStream(ciInput,
291                     decryptCipher)) {
292                 for (int i = 0; i < numberOfBlocks; i++) {
293                     if (type == BufferType.BYTE_ARRAY_BUFFERING) {
294                         doByteTest(i, cis);
295                     } else {
296                         doIntTest(i, cis);
297                     }
298                     if (cis.available() >= DISCARD) {
299                         cis.skip(DISCARD);
300                     } else {
301                         for (int k = 0; k < DISCARD; k++) {
302                             cis.read();
303                         }
304                     }
305                 }
306             }
307 
308             byte[] expectedText = new byte[numberOfBlocks * SAVE];
309             for (int m = 0; m < numberOfBlocks; m++) {
310                 for (int n = 0; n < SAVE; n++) {
311                     expectedText[m * SAVE + n] = plaintext[m * BLOCK + n];
312                 }
313             }
314             check(expectedText, outputText);
315         }
316     }
317 }
318