Mercurial > hg > jdk9-shenandoah > jdk
changeset 12440:06c8b6dc07a2
8048596: Tests for AEAD ciphers
Reviewed-by: valeriep
author | asmotrak |
---|---|
date | Sat, 25 Jul 2015 08:50:45 +0300 |
parents | 6b0ba4ed1ea4 |
children | 64b2be1b304c |
files | test/com/sun/crypto/provider/Cipher/AEAD/Encrypt.java test/com/sun/crypto/provider/Cipher/AEAD/GCMParameterSpecTest.java test/com/sun/crypto/provider/Cipher/AEAD/Helper.java test/com/sun/crypto/provider/Cipher/AEAD/KeyWrapper.java test/com/sun/crypto/provider/Cipher/AEAD/ReadWriteSkip.java test/com/sun/crypto/provider/Cipher/AEAD/SameBuffer.java test/com/sun/crypto/provider/Cipher/AEAD/SealedObjectTest.java test/com/sun/crypto/provider/Cipher/AEAD/WrongAAD.java |
diffstat | 8 files changed, 2115 insertions(+), 0 deletions(-) [+] |
line wrap: on
line diff
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/test/com/sun/crypto/provider/Cipher/AEAD/Encrypt.java Sat Jul 25 08:50:45 2015 +0300 @@ -0,0 +1,753 @@ +/* + * Copyright (c) 2007, 2015, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +import java.nio.ByteBuffer; +import java.security.AlgorithmParameters; +import java.security.Provider; +import java.security.Security; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.List; +import javax.crypto.SecretKey; +import javax.crypto.Cipher; +import javax.crypto.KeyGenerator; + +/* + * @test + * @bug 8048596 + * @summary AEAD encryption/decryption test + */ + +/* + * The test does the following: + * - create an input text and additional data + * - generate a secret key + * - instantiate a cipher according to the GCM transformation + * - generate an outputText using a single-part encryption/decryption + * in AEAD mode + * - perform 16 different combinations of multiple-part encryption/decryption + * operation in AEAD mode (in encryption mode new Cipher object is created + * and initialized with the same secret key and parameters) + * - check that all 17 results are equal + * + * Combinations: + * + * combination #1 + * updateAAD(byte[] src) + * update(byte[], int, int) + * doFinal(byte[], int, int) + * + * combination #2 + * updateAAD(byte[] src) + * update(byte[], int, int) + * doFinal(byte[], int, int, byte[], int) + * + * combination #3 + * updateAAD(byte[] src) + * update(byte[], int, int, byte[], int) + * doFinal(byte[], int, int) + * + * combination #4 + * updateAAD(byte[] src) + * update(byte[], int, int, byte[], int) + * doFinal(byte[], int, int, byte[], int) + * + * combination #5 - #8 are similar to #1 -#4, + * but with updateAAD(byte[] src, int offset, int len) + * + * combination #9 - #12 are similar to #1 - #4, + * but with updateAAD(ByteBuffer src) + * + * combination #13 - #16 are similar to #9 - #12 but with directly allocated + * ByteBuffer and update(ByteBuffer input, ByteBuffer output) + * + */ +public class Encrypt { + + private static final String ALGORITHMS[] = { "AES", "Rijndael" }; + private static final int KEY_STRENGTHS[] = { 128, 192, 256 }; + private static final int TEXT_LENGTHS[] = { 0, 256, 1024 }; + private static final int AAD_LENGTHS[] = { 0, 8, 128, 256, 1024 }; + private static final int ARRAY_OFFSET = 8; + + private final String transformation; + private final Provider provider; + private final SecretKey key; + private final int textLength; + private final int AADLength; + + /** + * @param provider Security provider + * @param algorithm Security algorithm to test + * @param mode The mode (GCM is only expected) + * @param padding Algorithm padding + * @param keyStrength key length + * @param textLength Plain text length + * @param AADLength Additional data length + */ + public Encrypt(Provider provider, String algorithm, String mode, + String padding, int keyStrength, int textLength, int AADLength) + throws Exception { + + // init a secret Key + KeyGenerator kg = KeyGenerator.getInstance(algorithm, provider); + kg.init(keyStrength); + key = kg.generateKey(); + + this.provider = provider; + this.transformation = algorithm + "/" + mode + "/" + padding; + this.textLength = textLength; + this.AADLength = AADLength; + } + + public static void main(String[] args) throws Exception { + Provider p = Security.getProvider("SunJCE"); + for (String alg : ALGORITHMS) { + for (int keyStrength : KEY_STRENGTHS) { + if (keyStrength > Cipher.getMaxAllowedKeyLength(alg)) { + // skip this if this key length is larger than what's + // configured in the JCE jurisdiction policy files + continue; + } + for (int textLength : TEXT_LENGTHS) { + for (int AADLength : AAD_LENGTHS) { + Encrypt test = new Encrypt(p, alg, + "GCM", "NoPadding", keyStrength, textLength, + AADLength); + Cipher cipher = test.createCipher(Cipher.ENCRYPT_MODE, + null); + AlgorithmParameters params = cipher.getParameters(); + test.doTest(params); + System.out.println("Test " + alg + ":" + + keyStrength + ":" + textLength + ":" + + AADLength + " passed"); + } + } + } + } + } + + public void doTest(AlgorithmParameters params) throws Exception { + System.out.println("Test transformation = " + transformation + + ", textLength = " + textLength + + ", AADLength = " + AADLength); + byte[] input = Helper.generateBytes(textLength); + byte[] AAD = Helper.generateBytes(AADLength); + byte[] result = execute(Cipher.ENCRYPT_MODE, AAD, input, params); + result = execute(Cipher.DECRYPT_MODE, AAD, result, params); + if (!Arrays.equals(input, result)) { + throw new RuntimeException("Test failed"); + } + System.out.println("Test passed"); + } + + /** + * Create a Cipher object for the requested encryption/decryption mode. + * + * @param mode encryption or decryption mode + * @return Cipher object initiated to perform requested mode operation + */ + private Cipher createCipher(int mode, AlgorithmParameters params) + throws Exception { + Cipher ci; + if (Cipher.ENCRYPT_MODE == mode) { + // create a new Cipher object for encryption + ci = Cipher.getInstance(transformation, provider); + + // initiate it with the saved parameters + if (params != null) { + ci.init(Cipher.ENCRYPT_MODE, key, params); + } else { + // initiate the cipher without parameters + ci.init(Cipher.ENCRYPT_MODE, key); + } + } else { + // it is expected that parameters already generated + // before decryption + ci = Cipher.getInstance(transformation, provider); + ci.init(Cipher.DECRYPT_MODE, key, params); + } + + return ci; + } + + /** + * Test AEAD combinations + * + * @param mode decryption or encryption + * @param AAD additional data for AEAD operations + * @param inputText plain text to decrypt/encrypt + * @return output text after encrypt/decrypt + */ + public byte[] execute(int mode, byte[] AAD, byte[] inputText, + AlgorithmParameters params) throws Exception { + + Cipher cipher = createCipher(mode, params); + + // results of each combination will be saved in the outputTexts + List<byte[]> outputTexts = new ArrayList<>(); + + // generate a standard outputText using a single-part en/de-cryption + cipher.updateAAD(AAD); + byte[] output = cipher.doFinal(inputText); + + // execute multiple-part encryption/decryption combinations + combination_1(outputTexts, mode, AAD, inputText, params); + combination_2(outputTexts, mode, AAD, inputText, params); + combination_3(outputTexts, mode, AAD, inputText, params); + combination_4(outputTexts, mode, AAD, inputText, params); + combination_5(outputTexts, mode, AAD, inputText, params); + combination_6(outputTexts, mode, AAD, inputText, params); + combination_7(outputTexts, mode, AAD, inputText, params); + combination_8(outputTexts, mode, AAD, inputText, params); + combination_9(outputTexts, mode, AAD, inputText, params); + combination_10(outputTexts, mode, AAD, inputText, params); + combination_11(outputTexts, mode, AAD, inputText, params); + combination_12(outputTexts, mode, AAD, inputText, params); + combination_13(outputTexts, mode, AAD, inputText, params); + combination_14(outputTexts, mode, AAD, inputText, params); + combination_15(outputTexts, mode, AAD, inputText, params); + combination_16(outputTexts, mode, AAD, inputText, params); + + for (int k = 0; k < outputTexts.size(); k++) { + if (!Arrays.equals(output, outputTexts.get(k))) { + throw new RuntimeException("Combination #" + k + " failed"); + } + } + return output; + } + + /* + * Execute multiple-part encryption/decryption combination #1: + * updateAAD(byte[] src) + * update(byte[], int, int) + * doFinal(byte[], int, int) + */ + private void combination_1(List<byte[]> results, int mode, byte[] AAD, + byte[] plainText, AlgorithmParameters params) throws Exception { + Cipher c = createCipher(mode, params); + c.updateAAD(AAD); + byte[] part11 = c.update(plainText, 0, plainText.length); + int part11_length = part11 == null ? 0 : part11.length; + byte[] part12 = c.doFinal(); + byte[] outputText1 = new byte[part11_length + part12.length]; + if (part11 != null) { + System.arraycopy(part11, 0, outputText1, 0, part11_length); + } + System.arraycopy(part12, 0, outputText1, part11_length, part12.length); + results.add(outputText1); + } + + /* + * Execute multiple-part encryption/decryption combination #2: + * updateAAD(byte[] src) + * update(byte[], int, int) + * doFinal(byte[], int, int, byte[], int) + */ + private void combination_2(List<byte[]> results, int mode, byte[] AAD, + byte[] plainText, AlgorithmParameters params) throws Exception { + Cipher c = createCipher(mode, params); + c.updateAAD(AAD); + int t = 0; + int offset = 0; + if (plainText.length > ARRAY_OFFSET) { + t = plainText.length - ARRAY_OFFSET; + offset = ARRAY_OFFSET; + } + byte[] part21 = c.update(plainText, 0, t); + byte[] part22 = new byte[c.getOutputSize(plainText.length)]; + int len2 = c.doFinal(plainText, t, offset, part22, 0); + int part21Length = part21 != null ? part21.length : 0; + byte[] outputText2 = new byte[part21Length + len2]; + if (part21 != null) { + System.arraycopy(part21, 0, outputText2, 0, part21Length); + } + System.arraycopy(part22, 0, outputText2, part21Length, len2); + results.add(outputText2); + } + + /* + * Execute multiple-part encryption/decryption combination #3 + * updateAAD(byte[] src) + * update(byte[], int, int, byte[], int) + * doFinal(byte[], int, int) + */ + private void combination_3(List<byte[]> results, int mode, byte[] AAD, + byte[] plainText, AlgorithmParameters params) throws Exception { + Cipher ci = createCipher(mode, params); + ci.updateAAD(AAD); + byte[] part31 = new byte[ci.getOutputSize(plainText.length)]; + int offset = plainText.length > ARRAY_OFFSET ? ARRAY_OFFSET : 0; + int len = ci.update(plainText, 0, plainText.length - offset, part31, 0); + byte[] part32 = ci.doFinal(plainText, plainText.length - offset, + offset); + byte[] outputText3 = new byte[len + part32.length]; + System.arraycopy(part31, 0, outputText3, 0, len); + System.arraycopy(part32, 0, outputText3, len, part32.length); + results.add(outputText3); + } + + /* + * Execute multiple-part encryption/decryption combination #4: + * updateAAD(byte[] src) + * update(byte[], int, int, byte[], int) + * doFinal(byte[], int, int, byte[], int) + */ + private void combination_4(List<byte[]> results, int mode, byte[] AAD, + byte[] plainText, AlgorithmParameters params) throws Exception { + Cipher ci = createCipher(mode, params); + ci.updateAAD(AAD); + byte[] part41 = new byte[ci.getOutputSize(plainText.length)]; + int offset = plainText.length > ARRAY_OFFSET ? ARRAY_OFFSET : 0; + int len = ci.update(plainText, 0, plainText.length - offset, part41, 0); + int rest4 = ci.doFinal(plainText, plainText.length - offset, offset, + part41, len); + byte[] outputText4 = new byte[len + rest4]; + System.arraycopy(part41, 0, outputText4, 0, outputText4.length); + results.add(outputText4); + } + + /* + * Execute multiple-part encryption/decryption combination #5: + * updateAAD(byte[] src, int offset, int len) + * update(byte[], int, int) + * doFinal(byte[], int, int) + */ + private void combination_5(List<byte[]> results, int mode, byte[] AAD, + byte[] plainText, AlgorithmParameters params) throws Exception { + Cipher c = createCipher(mode, params); + c.updateAAD(AAD, 0, AAD.length); + byte[] part51 = c.update(plainText, 0, plainText.length); + byte[] part52 = c.doFinal(); + int part51Length = part51 != null ? part51.length : 0; + byte[] outputText5 = new byte[part51Length + part52.length]; + if (part51 != null) { + System.arraycopy(part51, 0, outputText5, 0, part51Length); + } + System.arraycopy(part52, 0, outputText5, part51Length, part52.length); + results.add(outputText5); + } + + /* + * Execute multiple-part encryption/decryption combination #6: + * updateAAD(byte[] src, int offset, int len) + * updateAAD(byte[] src, int offset, int len) + * update(byte[], int, int) doFinal(byte[], int, int, byte[], int) + */ + private void combination_6(List<byte[]> results, int mode, byte[] AAD, + byte[] plainText, AlgorithmParameters params) throws Exception { + Cipher c = createCipher(mode, params); + c.updateAAD(AAD, 0, AAD.length / 2); + c.updateAAD(AAD, AAD.length / 2, AAD.length - AAD.length / 2); + int t = 0; + int offset = 0; + if (plainText.length > ARRAY_OFFSET) { + t = plainText.length - ARRAY_OFFSET; + offset = ARRAY_OFFSET; + } + byte[] part61 = c.update(plainText, 0, t); + byte[] part62 = new byte[c.getOutputSize(plainText.length)]; + int len = c.doFinal(plainText, t, offset, part62, 0); + int part61Length = part61 != null ? part61.length : 0; + byte[] outputText6 = new byte[part61Length + len]; + if (part61 != null) { + System.arraycopy(part61, 0, outputText6, 0, part61Length); + } + System.arraycopy(part62, 0, outputText6, part61Length, len); + results.add(outputText6); + } + + /* + * Execute multiple-part encryption/decryption combination #7 + * updateAAD(byte[] src, int offset, int len) + * updateAAD(byte[] src, src.length, 0) + * update(byte[], int, int, byte[], int) doFinal(byte[],int, int) + */ + private void combination_7(List<byte[]> results, int mode, byte[] AAD, + byte[] plainText, AlgorithmParameters params) throws Exception { + Cipher ci = createCipher(mode, params); + ci.updateAAD(AAD, 0, AAD.length); + ci.updateAAD(AAD, AAD.length, 0); + byte[] part71 = new byte[ci.getOutputSize(plainText.length)]; + int offset = plainText.length > ARRAY_OFFSET ? ARRAY_OFFSET : 0; + int len = ci.update(plainText, 0, plainText.length - offset, part71, 0); + byte[] part72 = ci.doFinal(plainText, plainText.length - offset, offset); + byte[] outputText7 = new byte[len + part72.length]; + System.arraycopy(part71, 0, outputText7, 0, len); + System.arraycopy(part72, 0, outputText7, len, part72.length); + results.add(outputText7); + } + + /* + * Execute multiple-part encryption/decryption combination #8: + * updateAAD(byte[] src, 0, 0) + * updateAAD(byte[] src, 0, src.length) + * update(byte[], int, int, byte[], int) + * doFinal(byte[], int, int, byte[], int) + */ + private void combination_8(List<byte[]> results, int mode, byte[] AAD, + byte[] plainText, AlgorithmParameters params) throws Exception { + Cipher ci = createCipher(mode, params); + ci.updateAAD(AAD, 0, 0); + ci.updateAAD(AAD, 0, AAD.length); + byte[] part81 = new byte[ci.getOutputSize(plainText.length)]; + int offset = plainText.length > ARRAY_OFFSET ? ARRAY_OFFSET : 0; + int len = ci.update(plainText, 0, plainText.length - offset, part81, 0); + int rest = ci.doFinal(plainText, plainText.length - offset, offset, + part81, len); + byte[] outputText8 = new byte[len + rest]; + System.arraycopy(part81, 0, outputText8, 0, outputText8.length); + results.add(outputText8); + } + + /* + * Execute multiple-part encryption/decryption combination #9: + * updateAAD(ByteBuffer src) + * update(byte[], int, int) doFinal(byte[], int, int) + */ + private void combination_9(List<byte[]> results, int mode, byte[] AAD, + byte[] plainText, AlgorithmParameters params) throws Exception { + + // prepare ByteBuffer to test + ByteBuffer buf = ByteBuffer.allocate(AAD.length); + buf.put(AAD); + buf.position(0); + buf.limit(AAD.length); + + // Get Cipher object and do the combination + Cipher c = createCipher(mode, params); + c.updateAAD(buf); + byte[] part91 = c.update(plainText, 0, plainText.length); + int part91_length = part91 == null ? 0 : part91.length; + byte[] part92 = c.doFinal(); + byte[] outputText9 = new byte[part91_length + part92.length]; + + // form result of the combination + if (part91 != null) { + System.arraycopy(part91, 0, outputText9, 0, part91_length); + } + System.arraycopy(part92, 0, outputText9, part91_length, part92.length); + results.add(outputText9); + } + + /* + * Execute multiple-part encryption/decryption combination #10: + * updateAAD(ByteBuffer src) + * updateAAD(ByteBuffer src) update(byte[], int, int) + * doFinal(byte[], int, int, byte[], int) + */ + private void combination_10(List<byte[]> results, int mode, byte[] AAD, + byte[] plainText, AlgorithmParameters params) throws Exception { + + // prepare ByteBuffer to test + ByteBuffer buf = ByteBuffer.allocate(AAD.length); + buf.put(AAD); + buf.position(0); + buf.limit(AAD.length / 2); + + // get a Cipher object and do the combination + Cipher c = createCipher(mode, params); + + // process the first half of AAD data + c.updateAAD(buf); + + // process the rest of AAD data + buf.limit(AAD.length); + c.updateAAD(buf); + + // prapare variables for the combination + int t = 0; + int offset = 0; + if (plainText.length > ARRAY_OFFSET) { + t = plainText.length - ARRAY_OFFSET; + offset = ARRAY_OFFSET; + } + + // encrypt the text + byte[] part10_1 = c.update(plainText, 0, t); + int part10_1_Length = part10_1 != null ? part10_1.length : 0; + byte[] part10_2 = new byte[c.getOutputSize(plainText.length)]; + int len2 = c.doFinal(plainText, t, offset, part10_2, 0); + + // form the combination's result + byte[] outputText10 = new byte[part10_1_Length + len2]; + if (part10_1 != null) { + System.arraycopy(part10_1, 0, outputText10, 0, part10_1_Length); + } + System.arraycopy(part10_2, 0, outputText10, part10_1_Length, len2); + results.add(outputText10); + } + + /* + * Execute multiple-part encryption/decryption combination #11 + * updateAAD(ByteBuffer src1) + * updateAAD(ByteBuffer src2) + * update(byte[],int, int, byte[], int) + * doFinal(byte[], int, int) + */ + private void combination_11(List<byte[]> results, int mode, byte[] AAD, + byte[] plainText, AlgorithmParameters params) throws Exception { + + // prepare ByteBuffer1 to test + ByteBuffer buf1 = ByteBuffer.allocate(AAD.length / 2); + buf1.put(AAD, 0, AAD.length / 2); + buf1.position(0); + buf1.limit(AAD.length / 2); + + // get a Cipher object and do combination + Cipher ci = createCipher(mode, params); + + // process the first half of AAD data + ci.updateAAD(buf1); + + // prepare ByteBuffer2 to test + ByteBuffer buf2 = ByteBuffer.allocate(AAD.length - AAD.length / 2); + buf2.put(AAD, AAD.length / 2, AAD.length - AAD.length / 2); + buf2.position(0); + buf2.limit(AAD.length - AAD.length / 2); + + // process the rest of AAD data + ci.updateAAD(buf2); + + // encrypt plain text + byte[] part11_1 = new byte[ci.getOutputSize(plainText.length)]; + int offset = plainText.length > ARRAY_OFFSET ? ARRAY_OFFSET : 0; + int len_11 = ci.update(plainText, 0, plainText.length - offset, + part11_1, 0); + byte[] part11_2 = ci.doFinal(plainText, plainText.length - offset, + offset); + byte[] outputText11 = new byte[len_11 + part11_2.length]; + System.arraycopy(part11_1, 0, outputText11, 0, len_11); + System.arraycopy(part11_2, 0, outputText11, len_11, part11_2.length); + results.add(outputText11); + } + + /* + * Execute multiple-part encryption/decryption combination #12: + * updateAAD(ByteBuffer src) + * updateAAD(ByteBuffer emptyByteBuffer) + * update(byte[], int, int, byte[], int) + * doFinal(byte[], int, int, byte[], int) + */ + private void combination_12(List<byte[]> results, int mode, byte[] AAD, + byte[] plainText, AlgorithmParameters params) throws Exception { + + // prepare ByteBuffer to test + ByteBuffer buf = ByteBuffer.allocate(AAD.length); + buf.put(AAD); + buf.position(0); + buf.limit(AAD.length); + Cipher ci = createCipher(mode, params); + ci.updateAAD(buf); + + // prepare an empty ByteBuffer + ByteBuffer emptyBuf = ByteBuffer.allocate(0); + emptyBuf.put(new byte[0]); + ci.updateAAD(emptyBuf); + byte[] part12_1 = new byte[ci.getOutputSize(plainText.length)]; + int offset = plainText.length > ARRAY_OFFSET ? ARRAY_OFFSET : 0; + int len12 = ci.update(plainText, 0, plainText.length - offset, + part12_1, 0); + int rest12 = ci.doFinal(plainText, plainText.length - offset, offset, + part12_1, len12); + byte[] outputText12 = new byte[len12 + rest12]; + System.arraycopy(part12_1, 0, outputText12, 0, outputText12.length); + results.add(outputText12); + } + + /* + * Execute multiple-part encryption/decryption combination #13: + * updateAAD(ByteBuffer src), where src is directly allocated + * update(ByteBuffer input, ByteBuffer out) + * doFinal(ByteBuffer input, ByteBuffer out) + */ + private void combination_13(List<byte[]> results, int mode, byte[] AAD, + byte[] plainText, AlgorithmParameters params) throws Exception { + Cipher c = createCipher(mode, params); + + // prepare ByteBuffer to test + ByteBuffer buf = ByteBuffer.allocateDirect(AAD.length); + buf.put(AAD); + buf.position(0); + buf.limit(AAD.length); + c.updateAAD(buf); + + // prepare buffers to encrypt/decrypt + ByteBuffer in = ByteBuffer.allocateDirect(plainText.length); + in.put(plainText); + in.position(0); + in.limit(plainText.length); + ByteBuffer output = ByteBuffer.allocateDirect( + c.getOutputSize(in.limit())); + output.position(0); + output.limit(c.getOutputSize(in.limit())); + + // process input text + c.update(in, output); + c.doFinal(in, output); + int resultSize = output.position(); + byte[] result13 = new byte[resultSize]; + output.position(0); + output.limit(resultSize); + output.get(result13, 0, resultSize); + results.add(result13); + } + + /* + * Execute multiple-part encryption/decryption combination #14: + * updateAAD(ByteBuffer src) updateAAD(ByteBuffer src), + * where src is directly allocated + * update(ByteBuffer input, ByteBuffer out) + * doFinal(ByteBuffer input, ByteBuffer out) + */ + private void combination_14(List<byte[]> results, int mode, byte[] AAD, + byte[] plainText, AlgorithmParameters params) throws Exception { + Cipher c = createCipher(mode, params); + // prepare ByteBuffer to test + ByteBuffer buf = ByteBuffer.allocateDirect(AAD.length); + buf.put(AAD); + + // process the first half of AAD data + buf.position(0); + buf.limit(AAD.length / 2); + c.updateAAD(buf); + + // process the rest of AAD data + buf.limit(AAD.length); + c.updateAAD(buf); + + // prepare buffers to encrypt/decrypt + ByteBuffer in = ByteBuffer.allocate(plainText.length); + in.put(plainText); + in.position(0); + in.limit(plainText.length); + ByteBuffer out = ByteBuffer.allocate(c.getOutputSize(in.limit())); + out.position(0); + out.limit(c.getOutputSize(in.limit())); + + // process input text + c.update(in, out); + c.doFinal(in, out); + int resultSize = out.position(); + byte[] result14 = new byte[resultSize]; + out.position(0); + out.limit(resultSize); + out.get(result14, 0, resultSize); + results.add(result14); + } + + /* + * Execute multiple-part encryption/decryption combination #15 + * updateAAD(ByteBuffer src1), where src1 is directly allocated + * updateAAD(ByteBuffer src2), where src2 is directly allocated + * doFinal(ByteBuffer input, ByteBuffer out) + */ + private void combination_15(List<byte[]> results, int mode, byte[] AAD, + byte[] plainText, AlgorithmParameters params) throws Exception { + Cipher c = createCipher(mode, params); + + // prepare ByteBuffer1 to test + ByteBuffer buf1 = ByteBuffer.allocateDirect(AAD.length / 2); + buf1.put(AAD, 0, AAD.length / 2); + buf1.position(0); + buf1.limit(AAD.length / 2); + + // process the first half of AAD data + c.updateAAD(buf1); + + // prepare ByteBuffer2 to test + ByteBuffer buf2 = ByteBuffer.allocateDirect( + AAD.length - AAD.length / 2); + buf2.put(AAD, AAD.length / 2, AAD.length - AAD.length / 2); + buf2.position(0); + buf2.limit(AAD.length - AAD.length / 2); + + // process the rest of AAD data + c.updateAAD(buf2); + + // prepare buffers to encrypt/decrypt + ByteBuffer in = ByteBuffer.allocateDirect(plainText.length); + in.put(plainText); + in.position(0); + in.limit(plainText.length); + ByteBuffer output = ByteBuffer.allocateDirect( + c.getOutputSize(in.limit())); + output.position(0); + output.limit(c.getOutputSize(in.limit())); + + // process input text + c.doFinal(in, output); + int resultSize = output.position(); + byte[] result15 = new byte[resultSize]; + output.position(0); + output.limit(resultSize); + output.get(result15, 0, resultSize); + results.add(result15); + } + + /* + * Execute multiple-part encryption/decryption combination #16: + * updateAAD(ByteBuffer src) + * updateAAD(ByteBuffer emptyByteBuffer) + * update(ByteBuffer input, ByteBuffer out) + * doFinal(EmptyByteBuffer, ByteBuffer out) + */ + private void combination_16(List<byte[]> results, int mode, byte[] AAD, + byte[] plainText, AlgorithmParameters params) throws Exception { + Cipher c = createCipher(mode, params); + + // prepare ByteBuffer to test + ByteBuffer buf = ByteBuffer.allocateDirect(AAD.length); + buf.put(AAD); + buf.position(0); + buf.limit(AAD.length); + c.updateAAD(buf); + + // prepare empty ByteBuffer + ByteBuffer emptyBuf = ByteBuffer.allocateDirect(0); + emptyBuf.put(new byte[0]); + c.updateAAD(emptyBuf); + + // prepare buffers to encrypt/decrypt + ByteBuffer in = ByteBuffer.allocateDirect(plainText.length); + in.put(plainText); + in.position(0); + in.limit(plainText.length); + ByteBuffer output = ByteBuffer.allocateDirect( + c.getOutputSize(in.limit())); + output.position(0); + output.limit(c.getOutputSize(in.limit())); + + // process input text with an empty buffer + c.update(in, output); + ByteBuffer emptyBuf2 = ByteBuffer.allocate(0); + emptyBuf2.put(new byte[0]); + c.doFinal(emptyBuf2, output); + int resultSize = output.position(); + byte[] result16 = new byte[resultSize]; + output.position(0); + output.limit(resultSize); + output.get(result16, 0, resultSize); + results.add(result16); + } +}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/test/com/sun/crypto/provider/Cipher/AEAD/GCMParameterSpecTest.java Sat Jul 25 08:50:45 2015 +0300 @@ -0,0 +1,218 @@ +/* + * Copyright (c) 2007, 2015, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +import java.security.NoSuchAlgorithmException; +import java.security.NoSuchProviderException; +import java.util.Arrays; +import javax.crypto.SecretKey; +import javax.crypto.Cipher; +import javax.crypto.KeyGenerator; +import javax.crypto.spec.GCMParameterSpec; + +/* + * @test + * @bug 8048596 + * @summary Check if GCMParameterSpec works as expected + */ +public class GCMParameterSpecTest { + + private static final int[] IV_LENGTHS = { 96, 8, 1024 }; + private static final int[] KEY_LENGTHS = { 128, 192, 256 }; + private static final int[] DATA_LENGTHS = { 0, 128, 1024 }; + private static final int[] AAD_LENGTHS = { 0, 128, 1024 }; + private static final int[] TAG_LENGTHS = { 128, 120, 112, 104, 96 }; + private static final int[] OFFSETS = { 0, 2, 5, 99 }; + private static final String TRANSFORMATION = "AES/GCM/NoPadding"; + private static final String TEMPLATE = "Test:\n tag = %d\n" + + " IV length = %d\n data length = %d\n" + + " AAD length = %d\n offset = %d\n keylength = %d\n"; + + private final byte[] IV; + private final byte[] IVO; + private final byte[] data; + private final byte[] AAD; + private final SecretKey key; + private final int tagLength; + private final int IVlength; + private final int offset; + + /** + * Initialize IV, IV with offset, plain text, AAD and SecretKey + * + * @param keyLength length of a secret key + * @param tagLength tag length + * @param IVlength IV length + * @param offset offset in a buffer for IV + * @param textLength plain text length + * @param AADLength AAD length + */ + public GCMParameterSpecTest(int keyLength, int tagLength, int IVlength, + int offset, int textLength, int AADLength) + throws NoSuchAlgorithmException, NoSuchProviderException { + this.tagLength = tagLength; // save tag length + this.IVlength = IVlength; // save IV length + this.offset = offset; // save IV offset + + // prepare IV + IV = Helper.generateBytes(IVlength); + + // prepare IV with offset + IVO = new byte[this.IVlength + this.offset]; + System.arraycopy(IV, 0, IVO, offset, this.IVlength); + + // prepare data + data = Helper.generateBytes(textLength); + + // prepare AAD + AAD = Helper.generateBytes(AADLength); + + // init a secret key + KeyGenerator kg = KeyGenerator.getInstance("AES", "SunJCE"); + kg.init(keyLength); + key = kg.generateKey(); + } + + /* + * Run the test for each key length, tag length, IV length, plain text + * length, AAD length and offset. + */ + public static void main(String[] args) throws Exception { + boolean success = true; + for (int k : KEY_LENGTHS) { + if (k > Cipher.getMaxAllowedKeyLength(TRANSFORMATION)) { + // skip this if this key length is larger than what's + // allowed in the jce jurisdiction policy files + continue; + } + for (int t : TAG_LENGTHS) { + for (int n : IV_LENGTHS) { + for (int p : DATA_LENGTHS) { + for (int a : AAD_LENGTHS) { + for (int o : OFFSETS) { + System.out.printf(TEMPLATE, t, n, p, a, o, k); + success &= new GCMParameterSpecTest( + k, t, n, o, p, a).doTest(); + } + } + } + } + } + } + + if (!success) { + throw new RuntimeException("At least one test case failed"); + } + } + + /* + * Run the test: + * - check if result of encryption of plain text is the same + * when parameters constructed with different GCMParameterSpec + * constructors are used + * - check if GCMParameterSpec.getTLen() is equal to actual tag length + * - check if ciphertext has the same length as plaintext + */ + private boolean doTest() throws Exception { + GCMParameterSpec spec1 = new GCMParameterSpec(tagLength, IV); + GCMParameterSpec spec2 = new GCMParameterSpec(tagLength, IVO, offset, + IVlength); + byte[] cipherText1 = getCipherTextBySpec(spec1); + if (cipherText1 == null) { + return false; + } + byte[] cipherText2 = getCipherTextBySpec(spec2); + if (cipherText2 == null) { + return false; + } + if (!Arrays.equals(cipherText1, cipherText2)) { + System.out.println("Cipher texts are different"); + return false; + } + if (spec1.getTLen() != spec2.getTLen()) { + System.out.println("Tag lengths are not equal"); + return false; + } + byte[] recoveredText1 = recoverCipherText(cipherText1, spec2); + if (recoveredText1 == null) { + return false; + } + byte[] recoveredText2 = recoverCipherText(cipherText2, spec1); + if (recoveredText2 == null) { + return false; + } + if (!Arrays.equals(recoveredText1, recoveredText2)) { + System.out.println("Recovered texts are different"); + return false; + } + if (!Arrays.equals(recoveredText1, data)) { + System.out.println("Recovered and original texts are not equal"); + return false; + } + + return true; + } + + /* + * Encrypt a plain text, and check if GCMParameterSpec.getIV() + * is equal to Cipher.getIV() + */ + private byte[] getCipherTextBySpec(GCMParameterSpec spec) throws Exception { + // init a cipher + Cipher cipher = createCipher(Cipher.ENCRYPT_MODE, spec); + cipher.updateAAD(AAD); + byte[] cipherText = cipher.doFinal(data); + + // check IVs + if (!Arrays.equals(cipher.getIV(), spec.getIV())) { + System.out.println("IV in parameters is incorrect"); + return null; + } + if (spec.getTLen() != (cipherText.length - data.length) * 8) { + System.out.println("Tag length is incorrect"); + return null; + } + return cipherText; + } + + private byte[] recoverCipherText(byte[] cipherText, GCMParameterSpec spec) + throws Exception { + // init a cipher + Cipher cipher = createCipher(Cipher.DECRYPT_MODE, spec); + + // check IVs + if (!Arrays.equals(cipher.getIV(), spec.getIV())) { + System.out.println("IV in parameters is incorrect"); + return null; + } + + cipher.updateAAD(AAD); + return cipher.doFinal(cipherText); + } + + private Cipher createCipher(int mode, GCMParameterSpec spec) + throws Exception { + Cipher cipher = Cipher.getInstance(TRANSFORMATION, "SunJCE"); + cipher.init(mode, key, spec); + return cipher; + } +}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/test/com/sun/crypto/provider/Cipher/AEAD/Helper.java Sat Jul 25 08:50:45 2015 +0300 @@ -0,0 +1,33 @@ +/* + * Copyright (c) 2015, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +public class Helper { + + public static byte[] generateBytes(int length) { + byte[] bytes = new byte[length]; + for (int i = 0; i < length; i++) { + bytes[i] = (byte) (i % 256); + } + return bytes; + } +}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/test/com/sun/crypto/provider/Cipher/AEAD/KeyWrapper.java Sat Jul 25 08:50:45 2015 +0300 @@ -0,0 +1,87 @@ +/* + * Copyright (c) 2007, 2015, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +import java.security.AlgorithmParameters; +import java.security.Key; +import java.util.Arrays; +import javax.crypto.SecretKey; +import javax.crypto.Cipher; +import javax.crypto.KeyGenerator; + +/* + * @test + * @bug 8048596 + * @summary Check if a key wrapper works properly with GCM mode + */ +public class KeyWrapper { + + static final String AES = "AES"; + static final String TRANSFORMATION = "AES/GCM/NoPadding"; + static final String PROVIDER = "SunJCE"; + static final int KEY_LENGTH = 128; + + public static void main(String argv[]) throws Exception { + doTest(PROVIDER, TRANSFORMATION); + } + + private static void doTest(String provider, String algo) throws Exception { + SecretKey key; + SecretKey keyToWrap; + + // init a secret Key + KeyGenerator kg = KeyGenerator.getInstance(AES, PROVIDER); + kg.init(KEY_LENGTH); + key = kg.generateKey(); + keyToWrap = kg.generateKey(); + + // initialization + Cipher cipher = Cipher.getInstance(algo, provider); + cipher.init(Cipher.WRAP_MODE, key); + AlgorithmParameters params = cipher.getParameters(); + + // wrap the key + byte[] keyWrapper = cipher.wrap(keyToWrap); + try { + // check if we can't wrap it again with the same key/IV + keyWrapper = cipher.wrap(keyToWrap); + throw new RuntimeException( + "FAILED: expected IllegalStateException hasn't " + + "been thrown "); + } catch (IllegalStateException ise) { + System.out.println(ise.getMessage()); + System.out.println("Expected exception"); + } + + // unwrap the key + cipher.init(Cipher.UNWRAP_MODE, key, params); + cipher.unwrap(keyWrapper, algo, Cipher.SECRET_KEY); + + // check if we can unwrap second time + Key unwrapKey = cipher.unwrap(keyWrapper, algo, Cipher.SECRET_KEY); + + if (!Arrays.equals(keyToWrap.getEncoded(), unwrapKey.getEncoded())) { + throw new RuntimeException( + "FAILED: original and unwrapped keys are not equal"); + } + } +}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/test/com/sun/crypto/provider/Cipher/AEAD/ReadWriteSkip.java Sat Jul 25 08:50:45 2015 +0300 @@ -0,0 +1,317 @@ +/* + * Copyright (c) 2007, 2015, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +import java.io.ByteArrayInputStream; +import java.io.ByteArrayOutputStream; +import java.io.IOException; +import java.security.GeneralSecurityException; +import java.security.NoSuchAlgorithmException; +import java.util.Arrays; +import javax.crypto.Cipher; +import javax.crypto.CipherInputStream; +import javax.crypto.CipherOutputStream; +import javax.crypto.KeyGenerator; +import javax.crypto.SecretKey; + +/* + * @test + * @bug 8048596 + * @summary Test CICO AEAD read/write/skip operations + */ +public class ReadWriteSkip { + + static enum BufferType { + BYTE_ARRAY_BUFFERING, INT_BYTE_BUFFERING + } + + static final int KEY_LENGTHS[] = {128, 192, 256}; + static final int TXT_LENGTHS[] = {800, 0}; + static final int AAD_LENGTHS[] = {0, 100}; + static final int BLOCK = 50; + static final int SAVE = 45; + static final int DISCARD = BLOCK - SAVE; + static final String PROVIDER = "SunJCE"; + static final String AES = "AES"; + static final String GCM = "GCM"; + static final String PADDING = "NoPadding"; + static final String TRANSFORM = AES + "/" + GCM + "/" + PADDING; + + final SecretKey key; + final byte[] plaintext; + final byte[] AAD; + final int textLength;; + final int keyLength; + Cipher encryptCipher; + Cipher decryptCipher; + CipherInputStream ciInput; + + public static void main(String[] args) throws Exception { + boolean success = true; + for (int keyLength : KEY_LENGTHS) { + if (keyLength > Cipher.getMaxAllowedKeyLength(TRANSFORM)) { + // skip this if this key length is larger than what's + // configured in the jce jurisdiction policy files + continue; + } + for (int textLength : TXT_LENGTHS) { + for (int AADLength : AAD_LENGTHS) { + System.out.println("Key length = " + keyLength + + ", text length = " + textLength + + ", AAD length = " + AADLength); + try { + run(keyLength, textLength, AADLength); + System.out.println("Test case passed"); + } catch (Exception e) { + System.out.println("Test case failed: " + e); + success = false; + } + } + } + } + + if (!success) { + throw new RuntimeException("At least one test case failed"); + } + + System.out.println("Test passed"); + } + + ReadWriteSkip(int keyLength, int textLength, int AADLength) + throws Exception { + this.keyLength = keyLength; + this.textLength = textLength; + + // init AAD + this.AAD = Helper.generateBytes(AADLength); + + // init a secret Key + KeyGenerator kg = KeyGenerator.getInstance(AES, PROVIDER); + kg.init(this.keyLength); + this.key = kg.generateKey(); + + this.plaintext = Helper.generateBytes(textLength); + } + + final void doTest(BufferType type) throws Exception { + // init ciphers + encryptCipher = createCipher(Cipher.ENCRYPT_MODE); + decryptCipher = createCipher(Cipher.DECRYPT_MODE); + + // init cipher input stream + ciInput = new CipherInputStream(new ByteArrayInputStream(plaintext), + encryptCipher); + + runTest(type); + } + + void runTest(BufferType type) throws Exception {} + + private Cipher createCipher(int mode) throws GeneralSecurityException { + Cipher cipher = Cipher.getInstance(TRANSFORM, PROVIDER); + if (mode == Cipher.ENCRYPT_MODE) { + cipher.init(Cipher.ENCRYPT_MODE, key); + } else { + if (encryptCipher != null) { + cipher.init(Cipher.DECRYPT_MODE, key, + encryptCipher.getParameters()); + } else { + throw new RuntimeException("Can't create a cipher"); + } + } + cipher.updateAAD(AAD); + return cipher; + } + + /* + * Run test cases + */ + static void run(int keyLength, int textLength, int AADLength) + throws Exception { + new ReadWriteTest(keyLength, textLength, AADLength) + .doTest(BufferType.BYTE_ARRAY_BUFFERING); + new ReadWriteTest(keyLength, textLength, AADLength) + .doTest(BufferType.INT_BYTE_BUFFERING); + new SkipTest(keyLength, textLength, AADLength) + .doTest(BufferType.BYTE_ARRAY_BUFFERING); + new SkipTest(keyLength, textLength, AADLength) + .doTest(BufferType.INT_BYTE_BUFFERING); + } + + static void check(byte[] first, byte[] second) { + if (!Arrays.equals(first, second)) { + throw new RuntimeException("Arrays are not equal"); + } + } + + /* + * CICO AEAD read/write functional test. + * + * Check if encrypt/decrypt operations work correctly. + * + * Test scenario: + * - initializes plain text + * - for given AEAD algorithm instantiates encrypt and decrypt Ciphers + * - instantiates CipherInputStream with the encrypt Cipher + * - instantiates CipherOutputStream with the decrypt Cipher + * - performs reading from the CipherInputStream (encryption data) + * and writing to the CipherOutputStream (decryption). As a result, + * output of the CipherOutputStream should be equal + * with original plain text + * - check if the original plain text is equal to output + * of the CipherOutputStream + * - if it is equal the test passes, otherwise it fails + */ + static class ReadWriteTest extends ReadWriteSkip { + + public ReadWriteTest(int keyLength, int textLength, int AADLength) + throws Exception { + super(keyLength, textLength, AADLength); + } + + @Override + public void runTest(BufferType bufType) throws IOException, + GeneralSecurityException { + + ByteArrayOutputStream baOutput = new ByteArrayOutputStream(); + try (CipherOutputStream ciOutput = new CipherOutputStream(baOutput, + decryptCipher)) { + if (bufType == BufferType.BYTE_ARRAY_BUFFERING) { + doByteTest(ciOutput); + } else { + doIntTest(ciOutput); + } + } + + check(plaintext, baOutput.toByteArray()); + } + + /* + * Implements byte array buffering type test case + */ + public void doByteTest(CipherOutputStream out) throws IOException { + byte[] buffer = Helper.generateBytes(textLength + 1); + int len = ciInput.read(buffer); + while (len != -1) { + out.write(buffer, 0, len); + len = ciInput.read(buffer); + } + } + + /* + * Implements integer buffering type test case + */ + public void doIntTest(CipherOutputStream out) throws IOException { + int buffer = ciInput.read(); + while (buffer != -1) { + out.write(buffer); + buffer = ciInput.read(); + } + } + } + + /* + * CICO AEAD SKIP functional test. + * + * Checks if the encrypt/decrypt operations work correctly + * when skip() method is used. + * + * Test scenario: + * - initializes a plain text + * - initializes ciphers + * - initializes cipher streams + * - split plain text to TEXT_SIZE/BLOCK blocks + * - read from CipherInputStream2 one block at time + * - the last DISCARD = BLOCK - SAVE bytes are skipping for each block + * - therefore, plain text data go through CipherInputStream1 (encrypting) + * and CipherInputStream2 (decrypting) + * - as a result, output should equal to the original text + * except DISCART byte for each block + * - check result buffers + */ + static class SkipTest extends ReadWriteSkip { + + private final int numberOfBlocks; + private final byte[] outputText; + + public SkipTest(int keyLength, int textLength, int AADLength) + throws Exception { + super(keyLength, textLength, AADLength); + numberOfBlocks = this.textLength / BLOCK; + outputText = new byte[numberOfBlocks * SAVE]; + } + + private void doByteTest(int blockNum, CipherInputStream cis) + throws IOException { + int index = blockNum * SAVE; + int len = cis.read(outputText, index, SAVE); + index += len; + int read = 0; + while (len != SAVE && read != -1) { + read = cis.read(outputText, index, SAVE - len); + len += read; + index += read; + } + } + + private void doIntTest(int blockNum, CipherInputStream cis) + throws IOException { + int i = blockNum * SAVE; + for (int j = 0; j < SAVE && i < outputText.length; j++, i++) { + int b = cis.read(); + if (b != -1) { + outputText[i] = (byte) b; + } + } + } + + @Override + public void runTest(BufferType type) throws IOException, + NoSuchAlgorithmException { + try (CipherInputStream cis = new CipherInputStream(ciInput, + decryptCipher)) { + for (int i = 0; i < numberOfBlocks; i++) { + if (type == BufferType.BYTE_ARRAY_BUFFERING) { + doByteTest(i, cis); + } else { + doIntTest(i, cis); + } + if (cis.available() >= DISCARD) { + cis.skip(DISCARD); + } else { + for (int k = 0; k < DISCARD; k++) { + cis.read(); + } + } + } + } + + byte[] expectedText = new byte[numberOfBlocks * SAVE]; + for (int m = 0; m < numberOfBlocks; m++) { + for (int n = 0; n < SAVE; n++) { + expectedText[m * SAVE + n] = plaintext[m * BLOCK + n]; + } + } + check(expectedText, outputText); + } + } +}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/test/com/sun/crypto/provider/Cipher/AEAD/SameBuffer.java Sat Jul 25 08:50:45 2015 +0300 @@ -0,0 +1,423 @@ +/* + * Copyright (c) 2007, 2015, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +import java.nio.ByteBuffer; +import java.security.AlgorithmParameters; +import java.security.Provider; +import java.security.Security; +import javax.crypto.SecretKey; +import javax.crypto.Cipher; +import javax.crypto.KeyGenerator; +import javax.crypto.spec.GCMParameterSpec; + +/* + * @test + * @bug 8048596 + * @summary Check if AEAD operations work correctly when buffers used + * for storing plain text and cipher text are overlapped or the same + */ +public class SameBuffer { + + private static final String PROVIDER = "SunJCE"; + private static final String AES = "AES"; + private static final String GCM = "GCM"; + private static final String PADDING = "NoPadding"; + private static final int OFFSET = 2; + private static final int OFFSETS = 4; + private static final int KEY_LENGTHS[] = { 128, 192, 256 }; + private static final int TEXT_LENGTHS[] = { 0, 1024 }; + private static final int AAD_LENGTHS[] = { 0, 1024 }; + + private final Provider provider; + private final SecretKey key; + private final String transformation; + private final int textLength; + private final int AADLength; + + /** + * Constructor of the test + * + * @param provider security provider + * @param keyStrength key length + * @param textLength length of data + * @param AADLength AAD length + */ + public SameBuffer(Provider provider, String algorithm, String mode, + String padding, int keyStrength, int textLength, int AADLength) + throws Exception { + + // init a secret key + KeyGenerator kg = KeyGenerator.getInstance(algorithm, provider); + kg.init(keyStrength); + key = kg.generateKey(); + + this.transformation = algorithm + "/" + mode + "/" + padding; + this.provider = provider; + this.textLength = textLength; + this.AADLength = AADLength; + } + + public static void main(String[] args) throws Exception { + Provider p = Security.getProvider(PROVIDER); + for (int keyLength : KEY_LENGTHS) { + for (int textLength : TEXT_LENGTHS) { + for (int AADLength : AAD_LENGTHS) { + for (int i = 0; i < OFFSETS; i++) { + // try different offsets + int offset = i * OFFSET; + runTest(p, AES, GCM, PADDING, keyLength, textLength, + AADLength, offset); + } + } + } + } + } + + /* + * Run single test case with given parameters + */ + static void runTest(Provider p, String algo, String mode, + String padding, int keyLength, int textLength, int AADLength, + int offset) throws Exception { + System.out.println("Testing " + keyLength + " key length; " + + textLength + " text lenght; " + AADLength + " AAD length; " + + offset + " offset"); + if (keyLength > Cipher.getMaxAllowedKeyLength(algo)) { + // skip this if this key length is larger than what's + // configured in the jce jurisdiction policy files + return; + } + SameBuffer test = new SameBuffer(p, algo, mode, + padding, keyLength, textLength, AADLength); + + /* + * There are four test cases: + * 1. AAD and text are placed in separated byte arrays + * 2. AAD and text are placed in the same byte array + * 3. AAD and text are placed in separated byte buffers + * 4. AAD and text are placed in the same byte buffer + */ + Cipher ci = test.createCipher(Cipher.ENCRYPT_MODE, null); + AlgorithmParameters params = ci.getParameters(); + test.doTestWithSeparateArrays(offset, params); + test.doTestWithSameArrays(offset, params); + test.doTestWithSeparatedBuffer(offset, params); + test.doTestWithSameBuffer(offset, params); + } + + /* + * Run the test in case when AAD and text are placed in separated byte + * arrays. + */ + private void doTestWithSeparateArrays(int offset, + AlgorithmParameters params) throws Exception { + // prepare buffers to test + Cipher c = createCipher(Cipher.ENCRYPT_MODE, params); + int outputLength = c.getOutputSize(textLength); + int outputBufSize = outputLength + offset * 2; + + byte[] inputText = Helper.generateBytes(outputBufSize); + byte[] AAD = Helper.generateBytes(AADLength); + + // do the test + runGCMWithSeparateArray(Cipher.ENCRYPT_MODE, AAD, inputText, offset * 2, + textLength, offset, params); + int tagLength = c.getParameters() + .getParameterSpec(GCMParameterSpec.class).getTLen() / 8; + runGCMWithSeparateArray(Cipher.DECRYPT_MODE, AAD, inputText, offset, + textLength + tagLength, offset, params); + } + + /** + * Run the test in case when AAD and text are placed in the same byte + * array. + */ + private void doTestWithSameArrays(int offset, AlgorithmParameters params) + throws Exception { + // prepare buffers to test + Cipher c = createCipher(Cipher.ENCRYPT_MODE, params); + int outputLength = c.getOutputSize(textLength); + int outputBufSize = AADLength + outputLength + offset * 2; + + byte[] AAD_and_text = Helper.generateBytes(outputBufSize); + + // do the test + runGCMWithSameArray(Cipher.ENCRYPT_MODE, AAD_and_text, AADLength + offset, + textLength, params); + int tagLength = c.getParameters() + .getParameterSpec(GCMParameterSpec.class).getTLen() / 8; + runGCMWithSameArray(Cipher.DECRYPT_MODE, AAD_and_text, AADLength + offset, + textLength + tagLength, params); + } + + /* + * Run the test in case when AAD and text are placed in separated ByteBuffer + */ + private void doTestWithSeparatedBuffer(int offset, + AlgorithmParameters params) throws Exception { + // prepare AAD byte buffers to test + byte[] AAD = Helper.generateBytes(AADLength); + ByteBuffer AAD_Buf = ByteBuffer.allocate(AADLength); + AAD_Buf.put(AAD, 0, AAD.length); + AAD_Buf.flip(); + + // prepare text byte buffer to encrypt/decrypt + Cipher c = createCipher(Cipher.ENCRYPT_MODE, params); + int outputLength = c.getOutputSize(textLength); + int outputBufSize = outputLength + offset; + byte[] inputText = Helper.generateBytes(outputBufSize); + ByteBuffer plainTextBB = ByteBuffer.allocateDirect(inputText.length); + plainTextBB.put(inputText); + plainTextBB.position(offset); + plainTextBB.limit(offset + textLength); + + // do test + runGCMWithSeparateBuffers(Cipher.ENCRYPT_MODE, AAD_Buf, plainTextBB, offset, + textLength, params); + int tagLength = c.getParameters() + .getParameterSpec(GCMParameterSpec.class).getTLen() / 8; + plainTextBB.position(offset); + plainTextBB.limit(offset + textLength + tagLength); + runGCMWithSeparateBuffers(Cipher.DECRYPT_MODE, AAD_Buf, plainTextBB, offset, + textLength + tagLength, params); + } + + /* + * Run the test in case when AAD and text are placed in the same ByteBuffer + */ + private void doTestWithSameBuffer(int offset, AlgorithmParameters params) + throws Exception { + // calculate output length + Cipher c = createCipher(Cipher.ENCRYPT_MODE, params); + int outputLength = c.getOutputSize(textLength); + + // prepare byte buffer contained AAD and plain text + int bufSize = AADLength + offset + outputLength; + byte[] AAD_and_Text = Helper.generateBytes(bufSize); + ByteBuffer AAD_and_Text_Buf = ByteBuffer.allocate(bufSize); + AAD_and_Text_Buf.put(AAD_and_Text, 0, AAD_and_Text.length); + + // do test + runGCMWithSameBuffer(Cipher.ENCRYPT_MODE, AAD_and_Text_Buf, offset, + textLength, params); + int tagLength = c.getParameters() + .getParameterSpec(GCMParameterSpec.class).getTLen() / 8; + AAD_and_Text_Buf.limit(AADLength + offset + textLength + tagLength); + runGCMWithSameBuffer(Cipher.DECRYPT_MODE, AAD_and_Text_Buf, offset, + textLength + tagLength, params); + + } + + /* + * Execute GCM encryption/decryption of a text placed in a byte array. + * AAD is placed in the separated byte array. + * Data are processed twice: + * - in a separately allocated buffer + * - in the text buffer + * Check if two results are equal + */ + private void runGCMWithSeparateArray(int mode, byte[] AAD, byte[] text, + int txtOffset, int lenght, int offset, AlgorithmParameters params) + throws Exception { + // first, generate the cipher text at an allocated buffer + Cipher cipher = createCipher(mode, params); + cipher.updateAAD(AAD); + byte[] outputText = cipher.doFinal(text, txtOffset, lenght); + + // new cipher for encrypt operation + Cipher anotherCipher = createCipher(mode, params); + anotherCipher.updateAAD(AAD); + + // next, generate cipher text again at the same buffer of plain text + int myoff = offset; + int off = anotherCipher.update(text, txtOffset, lenght, text, myoff); + anotherCipher.doFinal(text, myoff + off); + + // check if two resutls are equal + if (!isEqual(text, myoff, outputText, 0, outputText.length)) { + throw new RuntimeException("Two results not equal, mode:" + mode); + } + } + + /* + * Execute GCM encrption/decryption of a text. The AAD and text to process + * are placed in the same byte array. Data are processed twice: + * - in a separetly allocated buffer + * - in a buffer that shares content of the AAD_and_Text_BA + * Check if two results are equal + */ + private void runGCMWithSameArray(int mode, byte[] array, int txtOffset, + int length, AlgorithmParameters params) throws Exception { + // first, generate cipher text at an allocated buffer + Cipher cipher = createCipher(mode, params); + cipher.updateAAD(array, 0, AADLength); + byte[] outputText = cipher.doFinal(array, txtOffset, length); + + // new cipher for encrypt operation + Cipher anotherCipher = createCipher(mode, params); + anotherCipher.updateAAD(array, 0, AADLength); + + // next, generate cipher text again at the same buffer of plain text + int off = anotherCipher.update(array, txtOffset, length, + array, txtOffset); + anotherCipher.doFinal(array, txtOffset + off); + + // check if two results are equal or not + if (!isEqual(array, txtOffset, outputText, 0, + outputText.length)) { + throw new RuntimeException( + "Two results are not equal, mode:" + mode); + } + } + + /* + * Execute GCM encryption/decryption of textBB. AAD and text to process are + * placed in different byte buffers. Data are processed twice: + * - in a separately allocated buffer + * - in a buffer that shares content of the textBB + * Check if results are equal + */ + private void runGCMWithSeparateBuffers(int mode, ByteBuffer buffer, + ByteBuffer textBB, int txtOffset, int dataLength, + AlgorithmParameters params) throws Exception { + // take offset into account + textBB.position(txtOffset); + textBB.mark(); + + // first, generate the cipher text at an allocated buffer + Cipher cipher = createCipher(mode, params); + cipher.updateAAD(buffer); + buffer.flip(); + ByteBuffer outBB = ByteBuffer.allocateDirect( + cipher.getOutputSize(dataLength)); + + cipher.doFinal(textBB, outBB);// get cipher text in outBB + outBB.flip(); + + // restore positions + textBB.reset(); + + // next, generate cipher text again in a buffer that shares content + Cipher anotherCipher = createCipher(mode, params); + anotherCipher.updateAAD(buffer); + buffer.flip(); + ByteBuffer buf2 = textBB.duplicate(); // buf2 shares textBuf context + buf2.limit(txtOffset + anotherCipher.getOutputSize(dataLength)); + int dataProcessed2 = anotherCipher.doFinal(textBB, buf2); + buf2.position(txtOffset); + buf2.limit(txtOffset + dataProcessed2); + + if (!buf2.equals(outBB)) { + throw new RuntimeException( + "Two results are not equal, mode:" + mode); + } + } + + /* + * Execute GCM encryption/decryption of text. AAD and a text to process are + * placed in the same buffer. Data is processed twice: + * - in a separately allocated buffer + * - in a buffer that shares content of the AAD_and_Text_BB + */ + private void runGCMWithSameBuffer(int mode, ByteBuffer buffer, + int txtOffset, int length, AlgorithmParameters params) + throws Exception { + + // allocate a separate buffer + Cipher cipher = createCipher(mode, params); + ByteBuffer outBB = ByteBuffer.allocateDirect( + cipher.getOutputSize(length)); + + // first, generate the cipher text at an allocated buffer + buffer.flip(); + buffer.limit(AADLength); + cipher.updateAAD(buffer); + buffer.limit(AADLength + txtOffset + length); + buffer.position(AADLength + txtOffset); + cipher.doFinal(buffer, outBB); + outBB.flip(); // cipher text in outBB + + // next, generate cipherText again in the same buffer + Cipher anotherCipher = createCipher(mode, params); + buffer.flip(); + buffer.limit(AADLength); + anotherCipher.updateAAD(buffer); + buffer.limit(AADLength + txtOffset + length); + buffer.position(AADLength + txtOffset); + + // share textBuf context + ByteBuffer buf2 = buffer.duplicate(); + buf2.limit(AADLength + txtOffset + anotherCipher.getOutputSize(length)); + int dataProcessed2 = anotherCipher.doFinal(buffer, buf2); + buf2.position(AADLength + txtOffset); + buf2.limit(AADLength + txtOffset + dataProcessed2); + + if (!buf2.equals(outBB)) { + throw new RuntimeException( + "Two results are not equal, mode:" + mode); + } + } + + private boolean isEqual(byte[] A, int offsetA, byte[] B, int offsetB, + int bytesToCompare) { + System.out.println("offsetA: " + offsetA + " offsetB: " + offsetA + + " bytesToCompare: " + bytesToCompare); + for (int i = 0; i < bytesToCompare; i++) { + int setA = i + offsetA; + int setB = i + offsetB; + if (setA > A.length - 1 || setB > B.length - 1 + || A[setA] != B[setB]) { + return false; + } + } + + return true; + } + + /* + * Creates a Cipher object for testing: for encryption it creates new Cipher + * based on previously saved parameters (it is prohibited to use the same + * Cipher twice for encription during GCM mode), or returns initiated + * existing Cipher. + */ + private Cipher createCipher(int mode, AlgorithmParameters params) + throws Exception { + Cipher cipher = Cipher.getInstance(transformation, provider); + if (Cipher.ENCRYPT_MODE == mode) { + // initiate it with the saved parameters + if (params != null) { + cipher.init(Cipher.ENCRYPT_MODE, key, params); + } else { + // intiate the cipher and save parameters + cipher.init(Cipher.ENCRYPT_MODE, key); + } + } else if (cipher != null) { + cipher.init(Cipher.DECRYPT_MODE, key, params); + } else { + throw new RuntimeException("Can't create cipher"); + } + + return cipher; + } + +}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/test/com/sun/crypto/provider/Cipher/AEAD/SealedObjectTest.java Sat Jul 25 08:50:45 2015 +0300 @@ -0,0 +1,110 @@ +/* + * Copyright (c) 2007, 2015, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +import java.security.AlgorithmParameters; +import java.util.Arrays; +import javax.crypto.SecretKey; +import javax.crypto.Cipher; +import javax.crypto.KeyGenerator; +import javax.crypto.SealedObject; + +/* + * @test + * @bug 8048596 + * @summary Check if the seal/unseal feature works properly in AEAD/GCM mode. + */ +public class SealedObjectTest { + + private static final String AES = "AES"; + private static final String TRANSFORMATION = "AES/GCM/NoPadding"; + private static final String PROVIDER = "SunJCE"; + private static final int KEY_LENGTH = 128; + + public static void main(String[] args) throws Exception { + doTest(); + } + + /* + * Run the test: + * - init a cipher with AES/GCM/NoPadding transformation + * - seal an object + * - check if we can't seal it again with the same key/IV + * - unseal the object using different methods of SealedObject class + * - check if the original and sealed objects are equal + */ + static void doTest() throws Exception { + // init a secret Key + KeyGenerator kg = KeyGenerator.getInstance(AES, PROVIDER); + kg.init(KEY_LENGTH); + SecretKey key = kg.generateKey(); + + // initialization + Cipher cipher = Cipher.getInstance(TRANSFORMATION, PROVIDER); + cipher.init(Cipher.ENCRYPT_MODE, key); + AlgorithmParameters params = cipher.getParameters(); + + // seal an object + SealedObject so = new SealedObject(key, cipher); + try { + // check if we can't seal it again with the same key/IV + so = new SealedObject(key, cipher); + throw new RuntimeException( + "FAILED: expected IllegalStateException hasn't " + + "been thrown"); + } catch (IllegalStateException ise) { + System.out.println("Expected exception when seal it again with" + + " the same key/IV: " + ise); + } + + // unseal the object using getObject(Cipher) and compare + cipher.init(Cipher.DECRYPT_MODE, key, params); + SecretKey unsealedKey = (SecretKey) so.getObject(cipher); + assertKeysSame(unsealedKey, key, "SealedObject.getObject(Cipher)"); + + // unseal the object using getObject(Key) and compare + unsealedKey = (SecretKey) so.getObject(key); + assertKeysSame(unsealedKey, key, "SealedObject.getObject(Key)"); + + // unseal the object using getObject(Key, String) and compare + unsealedKey = (SecretKey) so.getObject(key, PROVIDER); + + assertKeysSame(unsealedKey, key, + "SealedObject.getObject(Key, String)"); + } + + /** + * Compare two SecretKey objects. + * + * @param key1 first key + * @param key2 second key + * @param meth method that was used for unsealing the SecretKey object + * @return true if key1 and key2 are the same, false otherwise. + */ + static void assertKeysSame(SecretKey key1, SecretKey key2, String meth) { + if (!Arrays.equals(key1.getEncoded(), key2.getEncoded())) { + throw new RuntimeException( + "FAILED: original and unsealed objects aren't the same for " + + meth); + } + } +}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/test/com/sun/crypto/provider/Cipher/AEAD/WrongAAD.java Sat Jul 25 08:50:45 2015 +0300 @@ -0,0 +1,174 @@ +/* + * Copyright (c) 2007, 2015, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +import java.io.ByteArrayInputStream; +import java.io.ByteArrayOutputStream; +import java.io.IOException; +import java.security.AlgorithmParameters; +import java.security.InvalidAlgorithmParameterException; +import java.security.InvalidKeyException; +import java.security.NoSuchAlgorithmException; +import java.security.NoSuchProviderException; +import java.util.Arrays; +import javax.crypto.CipherInputStream; +import javax.crypto.CipherOutputStream; +import javax.crypto.NoSuchPaddingException; +import javax.crypto.SecretKey; +import javax.crypto.Cipher; +import javax.crypto.KeyGenerator; + +/* + * @test + * @bug 8048596 + * @summary Check if wrong or empty AAD is rejected + */ +public class WrongAAD { + + private static final String PROVIDER = "SunJCE"; + private static final String TRANSFORMATION = "AES/GCM/NoPadding"; + private static final int TEXT_SIZE = 800; + private static final int KEY_SIZE = 128; + private static final int AAD_SIZE = 128; + + private final SecretKey key; + private final byte[] plainText; + private final Cipher encryptCipher; + + public WrongAAD() throws Exception { + // init a secret key + KeyGenerator kg = KeyGenerator.getInstance("AES", PROVIDER); + kg.init(KEY_SIZE); + key = kg.generateKey(); + + // generate a plain text + plainText = Helper.generateBytes(TEXT_SIZE); + + // init AADs + byte[] AAD = Helper.generateBytes(AAD_SIZE); + + // init a cipher + encryptCipher = createCipher(Cipher.ENCRYPT_MODE, null); + encryptCipher.updateAAD(AAD); + } + + public static void main(String[] args) throws Exception { + WrongAAD test = new WrongAAD(); + test.decryptWithEmptyAAD(); + test.decryptWithWrongAAD(); + } + + /* + * Attempt to decrypt a cipher text using Cipher object + * initialized without AAD used for encryption. + */ + private void decryptWithEmptyAAD() throws Exception { + System.out.println("decryptWithEmptyAAD() started"); + // initialize it with empty AAD to get exception during decryption + Cipher decryptCipher = createCipher(Cipher.DECRYPT_MODE, + encryptCipher.getParameters()); + try (ByteArrayOutputStream baOutput = new ByteArrayOutputStream(); + CipherOutputStream ciOutput = new CipherOutputStream(baOutput, + decryptCipher)) { + if (decrypt(ciOutput, baOutput)) { + throw new RuntimeException( + "Decryption has been perfomed successfully in" + + " spite of the decrypt Cipher has NOT been" + + " initialized with AAD"); + } + } + System.out.println("decryptWithEmptyAAD() passed"); + } + + /* + * Attempt to decrypt the cipher text using Cipher object + * initialized with some fake AAD. + */ + private void decryptWithWrongAAD() throws Exception { + System.out.println("decrypt with wrong AAD"); + + // initialize it with wrong AAD to get an exception during decryption + Cipher decryptCipher = createCipher(Cipher.DECRYPT_MODE, + encryptCipher.getParameters()); + byte[] someAAD = Helper.generateBytes(AAD_SIZE + 1); + decryptCipher.updateAAD(someAAD); + + // init output stream + try (ByteArrayOutputStream baOutput = new ByteArrayOutputStream(); + CipherOutputStream ciOutput = new CipherOutputStream(baOutput, + decryptCipher);) { + if (decrypt(ciOutput, baOutput)) { + throw new RuntimeException( + "A decryption has been perfomed successfully in" + + " spite of the decrypt Cipher has been" + + " initialized with fake AAD"); + } + } + + System.out.println("Passed"); + } + + private boolean decrypt(CipherOutputStream ciOutput, + ByteArrayOutputStream baOutput) throws IOException { + try (ByteArrayInputStream baInput = new ByteArrayInputStream(plainText); + CipherInputStream ciInput = new CipherInputStream(baInput, + encryptCipher)) { + byte[] buffer = new byte[TEXT_SIZE]; + int len = ciInput.read(buffer); + + while (len != -1) { + ciOutput.write(buffer, 0, len); + len = ciInput.read(buffer); + } + ciOutput.flush(); + byte[] recoveredText = baOutput.toByteArray(); + System.out.println("recoveredText: " + new String(recoveredText)); + + /* + * See bug 8012900, AEADBadTagException is swalloed by CI/CO streams + * If recovered text is empty, than decryption failed + */ + if (recoveredText.length == 0) { + return false; + } + return Arrays.equals(plainText, recoveredText); + } catch (IllegalStateException e) { + System.out.println("Expected IllegalStateException: " + + e.getMessage()); + e.printStackTrace(System.out); + return false; + } + } + + private Cipher createCipher(int mode, AlgorithmParameters params) + throws NoSuchAlgorithmException, NoSuchProviderException, + NoSuchPaddingException, InvalidKeyException, + InvalidAlgorithmParameterException { + Cipher cipher = Cipher.getInstance(TRANSFORMATION, PROVIDER); + if (params != null) { + cipher.init(mode, key, params); + } else { + cipher.init(mode, key); + } + return cipher; + } +}