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.nio.ByteBuffer;
25import java.security.AlgorithmParameters;
26import java.security.Provider;
27import java.security.Security;
28import java.util.ArrayList;
29import java.util.Arrays;
30import java.util.List;
31import javax.crypto.SecretKey;
32import javax.crypto.Cipher;
33import javax.crypto.KeyGenerator;
34
35/*
36 * @test
37 * @bug 8048596
38 * @summary AEAD encryption/decryption test
39 */
40
41/*
42 * The test does the following:
43 *   - create an input text and additional data
44 *   - generate a secret key
45 *   - instantiate a cipher according to the GCM transformation
46 *   - generate an outputText using a single-part encryption/decryption
47 *     in AEAD mode
48 *   - perform 16 different combinations of multiple-part encryption/decryption
49 *     operation in AEAD mode (in encryption mode new Cipher object is created
50 *     and initialized with the same secret key and parameters)
51 *   - check that all 17 results are equal
52 *
53 * Combinations:
54 *
55 * combination #1
56 *   updateAAD(byte[] src)
57 *   update(byte[], int, int)
58 *   doFinal(byte[], int, int)
59 *
60 * combination #2
61 *   updateAAD(byte[] src)
62 *   update(byte[], int, int)
63 *   doFinal(byte[], int, int, byte[], int)
64 *
65 * combination #3
66 *   updateAAD(byte[] src)
67 *   update(byte[], int, int, byte[], int)
68 *   doFinal(byte[], int, int)
69 *
70 * combination #4
71 *   updateAAD(byte[] src)
72 *   update(byte[], int, int, byte[], int)
73 *   doFinal(byte[], int, int, byte[], int)
74 *
75 * combination #5 - #8 are similar to #1 -#4,
76 * but with updateAAD(byte[] src, int offset, int len)
77 *
78 * combination #9 - #12 are similar to #1 - #4,
79 * but with updateAAD(ByteBuffer src)
80 *
81 * combination #13 - #16 are similar to #9 - #12 but with directly allocated
82 * ByteBuffer and update(ByteBuffer input, ByteBuffer output)
83 *
84 */
85public class Encrypt {
86
87    private static final String ALGORITHMS[] = { "AES", "Rijndael" };
88    private static final int KEY_STRENGTHS[] = { 128, 192, 256 };
89    private static final int TEXT_LENGTHS[] = { 0, 256, 1024 };
90    private static final int AAD_LENGTHS[] = { 0, 8, 128, 256, 1024 };
91    private static final int ARRAY_OFFSET = 8;
92
93    private final String transformation;
94    private final Provider provider;
95    private final SecretKey key;
96    private final int textLength;
97    private final int AADLength;
98
99    /**
100     * @param provider Security provider
101     * @param algorithm Security algorithm to test
102     * @param mode The mode (GCM is only expected)
103     * @param padding Algorithm padding
104     * @param keyStrength key length
105     * @param textLength Plain text length
106     * @param AADLength Additional data length
107     */
108    public Encrypt(Provider provider, String algorithm, String mode,
109            String padding, int keyStrength, int textLength, int AADLength)
110            throws Exception {
111
112        // init a secret Key
113        KeyGenerator kg = KeyGenerator.getInstance(algorithm, provider);
114        kg.init(keyStrength);
115        key = kg.generateKey();
116
117        this.provider = provider;
118        this.transformation = algorithm + "/" + mode + "/" + padding;
119        this.textLength = textLength;
120        this.AADLength = AADLength;
121    }
122
123    public static void main(String[] args) throws Exception {
124        Provider p = Security.getProvider("SunJCE");
125        for (String alg : ALGORITHMS) {
126            for (int keyStrength : KEY_STRENGTHS) {
127                if (keyStrength > Cipher.getMaxAllowedKeyLength(alg)) {
128                    // skip this if this key length is larger than what's
129                    // configured in the JCE jurisdiction policy files
130                    continue;
131                }
132                for (int textLength : TEXT_LENGTHS) {
133                    for (int AADLength : AAD_LENGTHS) {
134                        Encrypt test = new Encrypt(p, alg,
135                                "GCM", "NoPadding", keyStrength, textLength,
136                                AADLength);
137                        Cipher cipher = test.createCipher(Cipher.ENCRYPT_MODE,
138                                null);
139                        AlgorithmParameters params = cipher.getParameters();
140                        test.doTest(params);
141                        System.out.println("Test " + alg + ":"
142                                + keyStrength + ":" + textLength + ":"
143                                + AADLength + " passed");
144                    }
145                }
146            }
147        }
148    }
149
150    public void doTest(AlgorithmParameters params) throws Exception {
151        System.out.println("Test transformation = " + transformation
152                + ", textLength = " + textLength
153                + ", AADLength = " + AADLength);
154        byte[] input = Helper.generateBytes(textLength);
155        byte[] AAD = Helper.generateBytes(AADLength);
156        byte[] result = execute(Cipher.ENCRYPT_MODE, AAD, input, params);
157        result = execute(Cipher.DECRYPT_MODE, AAD, result, params);
158        if (!Arrays.equals(input, result)) {
159            throw new RuntimeException("Test failed");
160        }
161        System.out.println("Test passed");
162    }
163
164    /**
165     * Create a Cipher object for the requested encryption/decryption mode.
166     *
167     * @param mode encryption or decryption mode
168     * @return Cipher object initiated to perform requested mode operation
169     */
170    private Cipher createCipher(int mode, AlgorithmParameters params)
171            throws Exception {
172        Cipher ci;
173        if (Cipher.ENCRYPT_MODE == mode) {
174            // create a new Cipher object for encryption
175            ci = Cipher.getInstance(transformation, provider);
176
177            // initiate it with the saved parameters
178            if (params != null) {
179                ci.init(Cipher.ENCRYPT_MODE, key, params);
180            } else {
181                // initiate the cipher without parameters
182                ci.init(Cipher.ENCRYPT_MODE, key);
183            }
184        } else {
185            // it is expected that parameters already generated
186            // before decryption
187            ci = Cipher.getInstance(transformation, provider);
188            ci.init(Cipher.DECRYPT_MODE, key, params);
189        }
190
191        return ci;
192    }
193
194    /**
195     * Test AEAD combinations
196     *
197     * @param mode decryption or encryption
198     * @param AAD additional data for AEAD operations
199     * @param inputText plain text to decrypt/encrypt
200     * @return output text after encrypt/decrypt
201     */
202    public byte[] execute(int mode, byte[] AAD, byte[] inputText,
203            AlgorithmParameters params) throws Exception {
204
205        Cipher cipher = createCipher(mode, params);
206
207        // results of each combination will be saved in the outputTexts
208        List<byte[]> outputTexts = new ArrayList<>();
209
210        // generate a standard outputText using a single-part en/de-cryption
211        cipher.updateAAD(AAD);
212        byte[] output = cipher.doFinal(inputText);
213
214        // execute multiple-part encryption/decryption combinations
215        combination_1(outputTexts, mode, AAD, inputText, params);
216        combination_2(outputTexts, mode, AAD, inputText, params);
217        combination_3(outputTexts, mode, AAD, inputText, params);
218        combination_4(outputTexts, mode, AAD, inputText, params);
219        combination_5(outputTexts, mode, AAD, inputText, params);
220        combination_6(outputTexts, mode, AAD, inputText, params);
221        combination_7(outputTexts, mode, AAD, inputText, params);
222        combination_8(outputTexts, mode, AAD, inputText, params);
223        combination_9(outputTexts, mode, AAD, inputText, params);
224        combination_10(outputTexts, mode, AAD, inputText, params);
225        combination_11(outputTexts, mode, AAD, inputText, params);
226        combination_12(outputTexts, mode, AAD, inputText, params);
227        combination_13(outputTexts, mode, AAD, inputText, params);
228        combination_14(outputTexts, mode, AAD, inputText, params);
229        combination_15(outputTexts, mode, AAD, inputText, params);
230        combination_16(outputTexts, mode, AAD, inputText, params);
231
232        for (int k = 0; k < outputTexts.size(); k++) {
233            if (!Arrays.equals(output, outputTexts.get(k))) {
234                throw new RuntimeException("Combination #" + k + " failed");
235            }
236        }
237        return output;
238    }
239
240    /*
241     * Execute multiple-part encryption/decryption combination #1:
242     *   updateAAD(byte[] src)
243     *   update(byte[], int, int)
244     *   doFinal(byte[], int, int)
245     */
246    private void combination_1(List<byte[]> results, int mode, byte[] AAD,
247            byte[] plainText, AlgorithmParameters params) throws Exception {
248        Cipher c = createCipher(mode, params);
249        c.updateAAD(AAD);
250        byte[] part11 = c.update(plainText, 0, plainText.length);
251        int part11_length = part11 == null ? 0 : part11.length;
252        byte[] part12 = c.doFinal();
253        byte[] outputText1 = new byte[part11_length + part12.length];
254        if (part11 != null) {
255            System.arraycopy(part11, 0, outputText1, 0, part11_length);
256        }
257        System.arraycopy(part12, 0, outputText1, part11_length, part12.length);
258        results.add(outputText1);
259    }
260
261    /*
262     * Execute multiple-part encryption/decryption combination #2:
263     *   updateAAD(byte[] src)
264     *   update(byte[], int, int)
265     *   doFinal(byte[], int, int, byte[], int)
266     */
267    private void combination_2(List<byte[]> results, int mode, byte[] AAD,
268            byte[] plainText, AlgorithmParameters params) throws Exception {
269        Cipher c = createCipher(mode, params);
270        c.updateAAD(AAD);
271        int t = 0;
272        int offset = 0;
273        if (plainText.length > ARRAY_OFFSET) {
274            t = plainText.length - ARRAY_OFFSET;
275            offset = ARRAY_OFFSET;
276        }
277        byte[] part21 = c.update(plainText, 0, t);
278        byte[] part22 = new byte[c.getOutputSize(plainText.length)];
279        int len2 = c.doFinal(plainText, t, offset, part22, 0);
280        int part21Length = part21 != null ? part21.length : 0;
281        byte[] outputText2 = new byte[part21Length + len2];
282        if (part21 != null) {
283            System.arraycopy(part21, 0, outputText2, 0, part21Length);
284        }
285        System.arraycopy(part22, 0, outputText2, part21Length, len2);
286        results.add(outputText2);
287    }
288
289    /*
290     * Execute multiple-part encryption/decryption combination #3
291     *   updateAAD(byte[] src)
292     *   update(byte[], int, int, byte[], int)
293     *   doFinal(byte[], int, int)
294     */
295    private void combination_3(List<byte[]> results, int mode, byte[] AAD,
296            byte[] plainText, AlgorithmParameters params) throws Exception {
297        Cipher ci = createCipher(mode, params);
298        ci.updateAAD(AAD);
299        byte[] part31 = new byte[ci.getOutputSize(plainText.length)];
300        int offset = plainText.length > ARRAY_OFFSET ? ARRAY_OFFSET : 0;
301        int len = ci.update(plainText, 0, plainText.length - offset, part31, 0);
302        byte[] part32 = ci.doFinal(plainText, plainText.length - offset,
303                offset);
304        byte[] outputText3 = new byte[len + part32.length];
305        System.arraycopy(part31, 0, outputText3, 0, len);
306        System.arraycopy(part32, 0, outputText3, len, part32.length);
307        results.add(outputText3);
308    }
309
310    /*
311     * Execute multiple-part encryption/decryption combination #4:
312     *   updateAAD(byte[] src)
313     *   update(byte[], int, int, byte[], int)
314     *   doFinal(byte[], int, int, byte[], int)
315     */
316    private void combination_4(List<byte[]> results, int mode, byte[] AAD,
317            byte[] plainText, AlgorithmParameters params) throws Exception {
318        Cipher ci = createCipher(mode, params);
319        ci.updateAAD(AAD);
320        byte[] part41 = new byte[ci.getOutputSize(plainText.length)];
321        int offset = plainText.length > ARRAY_OFFSET ? ARRAY_OFFSET : 0;
322        int len = ci.update(plainText, 0, plainText.length - offset, part41, 0);
323        int rest4 = ci.doFinal(plainText, plainText.length - offset, offset,
324                part41, len);
325        byte[] outputText4 = new byte[len + rest4];
326        System.arraycopy(part41, 0, outputText4, 0, outputText4.length);
327        results.add(outputText4);
328    }
329
330    /*
331     * Execute multiple-part encryption/decryption combination #5:
332     *   updateAAD(byte[] src, int offset, int len)
333     *   update(byte[], int, int)
334     *   doFinal(byte[], int, int)
335     */
336    private void combination_5(List<byte[]> results, int mode, byte[] AAD,
337            byte[] plainText, AlgorithmParameters params) throws Exception {
338        Cipher c = createCipher(mode, params);
339        c.updateAAD(AAD, 0, AAD.length);
340        byte[] part51 = c.update(plainText, 0, plainText.length);
341        byte[] part52 = c.doFinal();
342        int part51Length = part51 != null ? part51.length : 0;
343        byte[] outputText5 = new byte[part51Length + part52.length];
344        if (part51 != null) {
345            System.arraycopy(part51, 0, outputText5, 0, part51Length);
346        }
347        System.arraycopy(part52, 0, outputText5, part51Length, part52.length);
348        results.add(outputText5);
349    }
350
351    /*
352     * Execute multiple-part encryption/decryption combination #6:
353     *   updateAAD(byte[] src, int offset, int len)
354     *   updateAAD(byte[] src, int offset, int len)
355     *   update(byte[], int, int) doFinal(byte[], int, int, byte[], int)
356     */
357    private void combination_6(List<byte[]> results, int mode, byte[] AAD,
358            byte[] plainText, AlgorithmParameters params) throws Exception {
359        Cipher c = createCipher(mode, params);
360        c.updateAAD(AAD, 0, AAD.length / 2);
361        c.updateAAD(AAD, AAD.length / 2, AAD.length - AAD.length / 2);
362        int t = 0;
363        int offset = 0;
364        if (plainText.length > ARRAY_OFFSET) {
365            t = plainText.length - ARRAY_OFFSET;
366            offset = ARRAY_OFFSET;
367        }
368        byte[] part61 = c.update(plainText, 0, t);
369        byte[] part62 = new byte[c.getOutputSize(plainText.length)];
370        int len = c.doFinal(plainText, t, offset, part62, 0);
371        int part61Length = part61 != null ? part61.length : 0;
372        byte[] outputText6 = new byte[part61Length + len];
373        if (part61 != null) {
374            System.arraycopy(part61, 0, outputText6, 0, part61Length);
375        }
376        System.arraycopy(part62, 0, outputText6, part61Length, len);
377        results.add(outputText6);
378    }
379
380    /*
381     * Execute multiple-part encryption/decryption combination #7
382     *   updateAAD(byte[] src, int offset, int len)
383     *   updateAAD(byte[] src, src.length, 0)
384     *   update(byte[], int, int, byte[], int) doFinal(byte[],int, int)
385     */
386    private void combination_7(List<byte[]> results, int mode, byte[] AAD,
387            byte[] plainText, AlgorithmParameters params) throws Exception {
388        Cipher ci = createCipher(mode, params);
389        ci.updateAAD(AAD, 0, AAD.length);
390        ci.updateAAD(AAD, AAD.length, 0);
391        byte[] part71 = new byte[ci.getOutputSize(plainText.length)];
392        int offset = plainText.length > ARRAY_OFFSET ? ARRAY_OFFSET : 0;
393        int len = ci.update(plainText, 0, plainText.length - offset, part71, 0);
394        byte[] part72 = ci.doFinal(plainText, plainText.length - offset, offset);
395        byte[] outputText7 = new byte[len + part72.length];
396        System.arraycopy(part71, 0, outputText7, 0, len);
397        System.arraycopy(part72, 0, outputText7, len, part72.length);
398        results.add(outputText7);
399    }
400
401    /*
402     * Execute multiple-part encryption/decryption combination #8:
403     *   updateAAD(byte[] src, 0, 0)
404     *   updateAAD(byte[] src, 0, src.length)
405     *   update(byte[], int, int, byte[], int)
406     *   doFinal(byte[], int, int, byte[], int)
407     */
408    private void combination_8(List<byte[]> results, int mode, byte[] AAD,
409            byte[] plainText, AlgorithmParameters params) throws Exception {
410        Cipher ci = createCipher(mode, params);
411        ci.updateAAD(AAD, 0, 0);
412        ci.updateAAD(AAD, 0, AAD.length);
413        byte[] part81 = new byte[ci.getOutputSize(plainText.length)];
414        int offset = plainText.length > ARRAY_OFFSET ? ARRAY_OFFSET : 0;
415        int len = ci.update(plainText, 0, plainText.length - offset, part81, 0);
416        int rest = ci.doFinal(plainText, plainText.length - offset, offset,
417                part81, len);
418        byte[] outputText8 = new byte[len + rest];
419        System.arraycopy(part81, 0, outputText8, 0, outputText8.length);
420        results.add(outputText8);
421    }
422
423    /*
424     * Execute multiple-part encryption/decryption combination #9:
425     *   updateAAD(ByteBuffer src)
426     *   update(byte[], int, int) doFinal(byte[], int, int)
427     */
428    private void combination_9(List<byte[]> results, int mode, byte[] AAD,
429            byte[] plainText, AlgorithmParameters params) throws Exception {
430
431        // prepare ByteBuffer to test
432        ByteBuffer buf = ByteBuffer.allocate(AAD.length);
433        buf.put(AAD);
434        buf.position(0);
435        buf.limit(AAD.length);
436
437        // Get Cipher object and do the combination
438        Cipher c = createCipher(mode, params);
439        c.updateAAD(buf);
440        byte[] part91 = c.update(plainText, 0, plainText.length);
441        int part91_length = part91 == null ? 0 : part91.length;
442        byte[] part92 = c.doFinal();
443        byte[] outputText9 = new byte[part91_length + part92.length];
444
445        // form result of the combination
446        if (part91 != null) {
447            System.arraycopy(part91, 0, outputText9, 0, part91_length);
448        }
449        System.arraycopy(part92, 0, outputText9, part91_length, part92.length);
450        results.add(outputText9);
451    }
452
453    /*
454     * Execute multiple-part encryption/decryption combination #10:
455     *   updateAAD(ByteBuffer src)
456     *   updateAAD(ByteBuffer src) update(byte[], int, int)
457     *   doFinal(byte[], int, int, byte[], int)
458     */
459    private void combination_10(List<byte[]> results, int mode, byte[] AAD,
460            byte[] plainText, AlgorithmParameters params) throws Exception {
461
462        // prepare ByteBuffer to test
463        ByteBuffer buf = ByteBuffer.allocate(AAD.length);
464        buf.put(AAD);
465        buf.position(0);
466        buf.limit(AAD.length / 2);
467
468        // get a Cipher object and do the combination
469        Cipher c = createCipher(mode, params);
470
471        // process the first half of AAD data
472        c.updateAAD(buf);
473
474        // process the rest of AAD data
475        buf.limit(AAD.length);
476        c.updateAAD(buf);
477
478        // prapare variables for the combination
479        int t = 0;
480        int offset = 0;
481        if (plainText.length > ARRAY_OFFSET) {
482            t = plainText.length - ARRAY_OFFSET;
483            offset = ARRAY_OFFSET;
484        }
485
486        // encrypt the text
487        byte[] part10_1 = c.update(plainText, 0, t);
488        int part10_1_Length = part10_1 != null ? part10_1.length : 0;
489        byte[] part10_2 = new byte[c.getOutputSize(plainText.length)];
490        int len2 = c.doFinal(plainText, t, offset, part10_2, 0);
491
492        // form the combination's result
493        byte[] outputText10 = new byte[part10_1_Length + len2];
494        if (part10_1 != null) {
495            System.arraycopy(part10_1, 0, outputText10, 0, part10_1_Length);
496        }
497        System.arraycopy(part10_2, 0, outputText10, part10_1_Length, len2);
498        results.add(outputText10);
499    }
500
501    /*
502     * Execute multiple-part encryption/decryption combination #11
503     *   updateAAD(ByteBuffer src1)
504     *   updateAAD(ByteBuffer src2)
505     *   update(byte[],int, int, byte[], int)
506     *   doFinal(byte[], int, int)
507     */
508    private void combination_11(List<byte[]> results, int mode, byte[] AAD,
509            byte[] plainText, AlgorithmParameters params) throws Exception {
510
511        // prepare ByteBuffer1 to test
512        ByteBuffer buf1 = ByteBuffer.allocate(AAD.length / 2);
513        buf1.put(AAD, 0, AAD.length / 2);
514        buf1.position(0);
515        buf1.limit(AAD.length / 2);
516
517        // get a Cipher object and do combination
518        Cipher ci = createCipher(mode, params);
519
520        // process the first half of AAD data
521        ci.updateAAD(buf1);
522
523        // prepare ByteBuffer2 to test
524        ByteBuffer buf2 = ByteBuffer.allocate(AAD.length - AAD.length / 2);
525        buf2.put(AAD, AAD.length / 2, AAD.length - AAD.length / 2);
526        buf2.position(0);
527        buf2.limit(AAD.length - AAD.length / 2);
528
529        // process the rest of AAD data
530        ci.updateAAD(buf2);
531
532        // encrypt plain text
533        byte[] part11_1 = new byte[ci.getOutputSize(plainText.length)];
534        int offset = plainText.length > ARRAY_OFFSET ? ARRAY_OFFSET : 0;
535        int len_11 = ci.update(plainText, 0, plainText.length - offset,
536                part11_1, 0);
537        byte[] part11_2 = ci.doFinal(plainText, plainText.length - offset,
538                offset);
539        byte[] outputText11 = new byte[len_11 + part11_2.length];
540        System.arraycopy(part11_1, 0, outputText11, 0, len_11);
541        System.arraycopy(part11_2, 0, outputText11, len_11, part11_2.length);
542        results.add(outputText11);
543    }
544
545    /*
546     * Execute multiple-part encryption/decryption combination #12:
547     *   updateAAD(ByteBuffer src)
548     *   updateAAD(ByteBuffer emptyByteBuffer)
549     *   update(byte[], int, int, byte[], int)
550     *   doFinal(byte[], int, int, byte[], int)
551     */
552    private void combination_12(List<byte[]> results, int mode, byte[] AAD,
553            byte[] plainText, AlgorithmParameters params) throws Exception {
554
555        // prepare ByteBuffer to test
556        ByteBuffer buf = ByteBuffer.allocate(AAD.length);
557        buf.put(AAD);
558        buf.position(0);
559        buf.limit(AAD.length);
560        Cipher ci = createCipher(mode, params);
561        ci.updateAAD(buf);
562
563        // prepare an empty ByteBuffer
564        ByteBuffer emptyBuf = ByteBuffer.allocate(0);
565        emptyBuf.put(new byte[0]);
566        ci.updateAAD(emptyBuf);
567        byte[] part12_1 = new byte[ci.getOutputSize(plainText.length)];
568        int offset = plainText.length > ARRAY_OFFSET ? ARRAY_OFFSET : 0;
569        int len12 = ci.update(plainText, 0, plainText.length - offset,
570                part12_1, 0);
571        int rest12 = ci.doFinal(plainText, plainText.length - offset, offset,
572                part12_1, len12);
573        byte[] outputText12 = new byte[len12 + rest12];
574        System.arraycopy(part12_1, 0, outputText12, 0, outputText12.length);
575        results.add(outputText12);
576    }
577
578    /*
579     * Execute multiple-part encryption/decryption combination #13:
580     *   updateAAD(ByteBuffer src), where src is directly allocated
581     *   update(ByteBuffer input, ByteBuffer out)
582     *   doFinal(ByteBuffer input, ByteBuffer out)
583     */
584    private void combination_13(List<byte[]> results, int mode, byte[] AAD,
585            byte[] plainText, AlgorithmParameters params) throws Exception {
586        Cipher c = createCipher(mode, params);
587
588        // prepare ByteBuffer to test
589        ByteBuffer buf = ByteBuffer.allocateDirect(AAD.length);
590        buf.put(AAD);
591        buf.position(0);
592        buf.limit(AAD.length);
593        c.updateAAD(buf);
594
595        // prepare buffers to encrypt/decrypt
596        ByteBuffer in = ByteBuffer.allocateDirect(plainText.length);
597        in.put(plainText);
598        in.position(0);
599        in.limit(plainText.length);
600        ByteBuffer output = ByteBuffer.allocateDirect(
601                c.getOutputSize(in.limit()));
602        output.position(0);
603        output.limit(c.getOutputSize(in.limit()));
604
605        // process input text
606        c.update(in, output);
607        c.doFinal(in, output);
608        int resultSize = output.position();
609        byte[] result13 = new byte[resultSize];
610        output.position(0);
611        output.limit(resultSize);
612        output.get(result13, 0, resultSize);
613        results.add(result13);
614    }
615
616    /*
617     * Execute multiple-part encryption/decryption combination #14:
618     *   updateAAD(ByteBuffer src) updateAAD(ByteBuffer src),
619     *       where src is directly allocated
620     *   update(ByteBuffer input, ByteBuffer out)
621     *   doFinal(ByteBuffer input, ByteBuffer out)
622     */
623    private void combination_14(List<byte[]> results, int mode, byte[] AAD,
624            byte[] plainText, AlgorithmParameters params) throws Exception {
625        Cipher c = createCipher(mode, params);
626        // prepare ByteBuffer to test
627        ByteBuffer buf = ByteBuffer.allocateDirect(AAD.length);
628        buf.put(AAD);
629
630        // process the first half of AAD data
631        buf.position(0);
632        buf.limit(AAD.length / 2);
633        c.updateAAD(buf);
634
635        // process the rest of AAD data
636        buf.limit(AAD.length);
637        c.updateAAD(buf);
638
639        // prepare buffers to encrypt/decrypt
640        ByteBuffer in = ByteBuffer.allocate(plainText.length);
641        in.put(plainText);
642        in.position(0);
643        in.limit(plainText.length);
644        ByteBuffer out = ByteBuffer.allocate(c.getOutputSize(in.limit()));
645        out.position(0);
646        out.limit(c.getOutputSize(in.limit()));
647
648        // process input text
649        c.update(in, out);
650        c.doFinal(in, out);
651        int resultSize = out.position();
652        byte[] result14 = new byte[resultSize];
653        out.position(0);
654        out.limit(resultSize);
655        out.get(result14, 0, resultSize);
656        results.add(result14);
657    }
658
659    /*
660     * Execute multiple-part encryption/decryption combination #15
661     *   updateAAD(ByteBuffer src1), where src1 is directly allocated
662     *   updateAAD(ByteBuffer src2), where src2 is directly allocated
663     *   doFinal(ByteBuffer input, ByteBuffer out)
664     */
665    private void combination_15(List<byte[]> results, int mode, byte[] AAD,
666            byte[] plainText, AlgorithmParameters params) throws Exception {
667        Cipher c = createCipher(mode, params);
668
669        // prepare ByteBuffer1 to test
670        ByteBuffer buf1 = ByteBuffer.allocateDirect(AAD.length / 2);
671        buf1.put(AAD, 0, AAD.length / 2);
672        buf1.position(0);
673        buf1.limit(AAD.length / 2);
674
675        // process the first half of AAD data
676        c.updateAAD(buf1);
677
678        // prepare ByteBuffer2 to test
679        ByteBuffer buf2 = ByteBuffer.allocateDirect(
680                AAD.length - AAD.length / 2);
681        buf2.put(AAD, AAD.length / 2, AAD.length - AAD.length / 2);
682        buf2.position(0);
683        buf2.limit(AAD.length - AAD.length / 2);
684
685        // process the rest of AAD data
686        c.updateAAD(buf2);
687
688        // prepare buffers to encrypt/decrypt
689        ByteBuffer in = ByteBuffer.allocateDirect(plainText.length);
690        in.put(plainText);
691        in.position(0);
692        in.limit(plainText.length);
693        ByteBuffer output = ByteBuffer.allocateDirect(
694                c.getOutputSize(in.limit()));
695        output.position(0);
696        output.limit(c.getOutputSize(in.limit()));
697
698        // process input text
699        c.doFinal(in, output);
700        int resultSize = output.position();
701        byte[] result15 = new byte[resultSize];
702        output.position(0);
703        output.limit(resultSize);
704        output.get(result15, 0, resultSize);
705        results.add(result15);
706    }
707
708    /*
709     * Execute multiple-part encryption/decryption combination #16:
710     *   updateAAD(ByteBuffer src)
711     *   updateAAD(ByteBuffer emptyByteBuffer)
712     *   update(ByteBuffer input, ByteBuffer out)
713     *   doFinal(EmptyByteBuffer, ByteBuffer out)
714     */
715    private void combination_16(List<byte[]> results, int mode, byte[] AAD,
716            byte[] plainText, AlgorithmParameters params) throws Exception {
717        Cipher c = createCipher(mode, params);
718
719        // prepare ByteBuffer to test
720        ByteBuffer buf = ByteBuffer.allocateDirect(AAD.length);
721        buf.put(AAD);
722        buf.position(0);
723        buf.limit(AAD.length);
724        c.updateAAD(buf);
725
726        // prepare empty ByteBuffer
727        ByteBuffer emptyBuf = ByteBuffer.allocateDirect(0);
728        emptyBuf.put(new byte[0]);
729        c.updateAAD(emptyBuf);
730
731        // prepare buffers to encrypt/decrypt
732        ByteBuffer in = ByteBuffer.allocateDirect(plainText.length);
733        in.put(plainText);
734        in.position(0);
735        in.limit(plainText.length);
736        ByteBuffer output = ByteBuffer.allocateDirect(
737                c.getOutputSize(in.limit()));
738        output.position(0);
739        output.limit(c.getOutputSize(in.limit()));
740
741        // process input text with an empty buffer
742        c.update(in, output);
743        ByteBuffer emptyBuf2 = ByteBuffer.allocate(0);
744        emptyBuf2.put(new byte[0]);
745        c.doFinal(emptyBuf2, output);
746        int resultSize = output.position();
747        byte[] result16 = new byte[resultSize];
748        output.position(0);
749        output.limit(resultSize);
750        output.get(result16, 0, resultSize);
751        results.add(result16);
752    }
753}
754