Path: blob/master/test/jdk/com/sun/crypto/provider/Cipher/AEAD/SameBuffer.java
41161 views
/*1* Copyright (c) 2007, 2021, Oracle and/or its affiliates. All rights reserved.2* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.3*4* This code is free software; you can redistribute it and/or modify it5* under the terms of the GNU General Public License version 2 only, as6* published by the Free Software Foundation.7*8* This code is distributed in the hope that it will be useful, but WITHOUT9* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or10* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License11* version 2 for more details (a copy is included in the LICENSE file that12* accompanied this code).13*14* You should have received a copy of the GNU General Public License version15* 2 along with this work; if not, write to the Free Software Foundation,16* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.17*18* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA19* or visit www.oracle.com if you need additional information or have any20* questions.21*/2223import java.nio.ByteBuffer;24import java.security.AlgorithmParameters;25import java.security.Provider;26import java.security.Security;27import java.util.HexFormat;28import javax.crypto.SecretKey;29import javax.crypto.Cipher;30import javax.crypto.KeyGenerator;31import javax.crypto.spec.GCMParameterSpec;32import jdk.test.lib.Convert;3334/*35* @test36* @bug 804859637* @summary Check if AEAD operations work correctly when buffers used38* for storing plain text and cipher text are overlapped or the same39* @library /test/lib40*/41public class SameBuffer {4243private static final String PROVIDER = "SunJCE";44private static final String AES = "AES";45private static final String GCM = "GCM";46private static final String PADDING = "NoPadding";47private static final int OFFSET = 2;48private static final int OFFSETS = 4;49private static final int KEY_LENGTHS[] = { 128, 192, 256 };50private static final int TEXT_LENGTHS[] = { 0, 1024 };51private static final int AAD_LENGTHS[] = { 0, 1024 };5253private final Provider provider;54private final SecretKey key;55private final String transformation;56private final int textLength;57private final int AADLength;5859/**60* Constructor of the test61*62* @param provider security provider63* @param keyStrength key length64* @param textLength length of data65* @param AADLength AAD length66*/67public SameBuffer(Provider provider, String algorithm, String mode,68String padding, int keyStrength, int textLength, int AADLength)69throws Exception {7071// init a secret key72KeyGenerator kg = KeyGenerator.getInstance(algorithm, provider);73kg.init(keyStrength);74key = kg.generateKey();7576this.transformation = algorithm + "/" + mode + "/" + padding;77this.provider = provider;78this.textLength = textLength;79this.AADLength = AADLength;80}8182public static void main(String[] args) throws Exception {83Provider p = Security.getProvider(PROVIDER);84for (int keyLength : KEY_LENGTHS) {85for (int textLength : TEXT_LENGTHS) {86for (int AADLength : AAD_LENGTHS) {87for (int i = 0; i < OFFSETS; i++) {88// try different offsets89int offset = i * OFFSET;90runTest(p, AES, GCM, PADDING, keyLength, textLength,91AADLength, offset);92}93}94}95}96}9798/*99* Run single test case with given parameters100*/101static void runTest(Provider p, String algo, String mode,102String padding, int keyLength, int textLength, int AADLength,103int offset) throws Exception {104System.out.println("Testing " + keyLength + " key length; "105+ textLength + " text length; " + AADLength + " AAD length; "106+ offset + " offset");107if (keyLength > Cipher.getMaxAllowedKeyLength(algo)) {108// skip this if this key length is larger than what's109// configured in the jce jurisdiction policy files110return;111}112SameBuffer test = new SameBuffer(p, algo, mode,113padding, keyLength, textLength, AADLength);114115/*116* There are four test cases:117* 1. AAD and text are placed in separated byte arrays118* 2. AAD and text are placed in the same byte array119* 3. AAD and text are placed in separated byte buffers120* 4. AAD and text are placed in the same byte buffer121*/122Cipher ci = test.createCipher(Cipher.ENCRYPT_MODE, null);123AlgorithmParameters params = ci.getParameters();124test.doTestWithSeparateArrays(offset, params);125test.doTestWithSameArrays(offset, params);126test.doTestWithSeparatedBuffer(offset, params);127test.doTestWithSameBuffer(offset, params);128}129130/*131* Run the test in case when AAD and text are placed in separated byte132* arrays.133*/134private void doTestWithSeparateArrays(int offset,135AlgorithmParameters params) throws Exception {136// prepare buffers to test137Cipher c = createCipher(Cipher.ENCRYPT_MODE, params);138int outputLength = c.getOutputSize(textLength);139int outputBufSize = outputLength + offset * 2;140141byte[] inputText = Helper.generateBytes(outputBufSize);142byte[] AAD = Helper.generateBytes(AADLength);143144// do the test145runGCMWithSeparateArray(Cipher.ENCRYPT_MODE, AAD, inputText, offset * 2,146textLength, offset, params);147int tagLength = c.getParameters()148.getParameterSpec(GCMParameterSpec.class).getTLen() / 8;149runGCMWithSeparateArray(Cipher.DECRYPT_MODE, AAD, inputText, offset,150textLength + tagLength, offset, params);151}152153/**154* Run the test in case when AAD and text are placed in the same byte155* array.156*/157private void doTestWithSameArrays(int offset, AlgorithmParameters params)158throws Exception {159// prepare buffers to test160Cipher c = createCipher(Cipher.ENCRYPT_MODE, params);161int outputLength = c.getOutputSize(textLength);162int outputBufSize = AADLength + outputLength + offset * 2;163164byte[] AAD_and_text = Helper.generateBytes(outputBufSize);165166// do the test167runGCMWithSameArray(Cipher.ENCRYPT_MODE, AAD_and_text, AADLength + offset,168textLength, params);169int tagLength = c.getParameters()170.getParameterSpec(GCMParameterSpec.class).getTLen() / 8;171runGCMWithSameArray(Cipher.DECRYPT_MODE, AAD_and_text, AADLength + offset,172textLength + tagLength, params);173}174175/*176* Run the test in case when AAD and text are placed in separated ByteBuffer177*/178private void doTestWithSeparatedBuffer(int offset,179AlgorithmParameters params) throws Exception {180// prepare AAD byte buffers to test181byte[] AAD = Helper.generateBytes(AADLength);182ByteBuffer AAD_Buf = ByteBuffer.allocate(AADLength);183AAD_Buf.put(AAD, 0, AAD.length);184AAD_Buf.flip();185186// prepare text byte buffer to encrypt/decrypt187Cipher c = createCipher(Cipher.ENCRYPT_MODE, params);188int outputLength = c.getOutputSize(textLength);189int outputBufSize = outputLength + offset;190byte[] inputText = Helper.generateBytes(outputBufSize);191ByteBuffer plainTextBB = ByteBuffer.allocateDirect(inputText.length);192plainTextBB.put(inputText);193plainTextBB.position(offset);194plainTextBB.limit(offset + textLength);195196// do test197runGCMWithSeparateBuffers(Cipher.ENCRYPT_MODE, AAD_Buf, plainTextBB, offset,198textLength, params);199int tagLength = c.getParameters()200.getParameterSpec(GCMParameterSpec.class).getTLen() / 8;201plainTextBB.position(offset);202plainTextBB.limit(offset + textLength + tagLength);203runGCMWithSeparateBuffers(Cipher.DECRYPT_MODE, AAD_Buf, plainTextBB, offset,204textLength + tagLength, params);205}206207/*208* Run the test in case when AAD and text are placed in the same ByteBuffer209*/210private void doTestWithSameBuffer(int offset, AlgorithmParameters params)211throws Exception {212// calculate output length213Cipher c = createCipher(Cipher.ENCRYPT_MODE, params);214int outputLength = c.getOutputSize(textLength);215216// prepare byte buffer contained AAD and plain text217int bufSize = AADLength + offset + outputLength;218byte[] AAD_and_Text = Helper.generateBytes(bufSize);219ByteBuffer AAD_and_Text_Buf = ByteBuffer.allocate(bufSize);220AAD_and_Text_Buf.put(AAD_and_Text, 0, AAD_and_Text.length);221222// do test223runGCMWithSameBuffer(Cipher.ENCRYPT_MODE, AAD_and_Text_Buf, offset,224textLength, params);225int tagLength = c.getParameters()226.getParameterSpec(GCMParameterSpec.class).getTLen() / 8;227AAD_and_Text_Buf.limit(AADLength + offset + textLength + tagLength);228runGCMWithSameBuffer(Cipher.DECRYPT_MODE, AAD_and_Text_Buf, offset,229textLength + tagLength, params);230231}232233/*234* Execute GCM encryption/decryption of a text placed in a byte array.235* AAD is placed in the separated byte array.236* Data are processed twice:237* - in a separately allocated buffer238* - in the text buffer239* Check if two results are equal240*/241private void runGCMWithSeparateArray(int mode, byte[] AAD, byte[] text,242int txtOffset, int length, int offset, AlgorithmParameters params)243throws Exception {244// first, generate the cipher text at an allocated buffer245Cipher cipher = createCipher(mode, params);246cipher.updateAAD(AAD);247byte[] outputText = cipher.doFinal(text, txtOffset, length);248249// new cipher for encrypt operation250Cipher anotherCipher = createCipher(mode, params);251anotherCipher.updateAAD(AAD);252253// next, generate cipher text again at the same buffer of plain text254int myoff = offset;255int off = anotherCipher.update(text, txtOffset, length, text, myoff);256anotherCipher.doFinal(text, myoff + off);257258// check if two resutls are equal259if (!isEqual(text, myoff, outputText, 0, outputText.length)) {260System.err.println(261"\noutputText: len = " + outputText.length + " txtOffset = " + txtOffset + "\n" +262HexFormat.of().withUpperCase().formatHex(outputText) + "\n" +263"text: len = " + text.length + " myoff = " + myoff + "\n" +264HexFormat.of().withUpperCase().formatHex(text) + "\n" +265"length " + length);266System.err.println("tlen = " + params.getParameterSpec(GCMParameterSpec.class).getTLen() / 8);267throw new RuntimeException("Two results not equal, mode:" + mode);268}269}270271/*272* Execute GCM encrption/decryption of a text. The AAD and text to process273* are placed in the same byte array. Data are processed twice:274* - in a separetly allocated buffer275* - in a buffer that shares content of the AAD_and_Text_BA276* Check if two results are equal277*/278private void runGCMWithSameArray(int mode, byte[] array, int txtOffset,279int length, AlgorithmParameters params) throws Exception {280// first, generate cipher text at an allocated buffer281Cipher cipher = createCipher(mode, params);282cipher.updateAAD(array, 0, AADLength);283byte[] outputText = cipher.doFinal(array, txtOffset, length);284285// new cipher for encrypt operation286Cipher anotherCipher = createCipher(mode, params);287anotherCipher.updateAAD(array, 0, AADLength);288289// next, generate cipher text again at the same buffer of plain text290int off = anotherCipher.update(array, txtOffset, length,291array, txtOffset);292anotherCipher.doFinal(array, txtOffset + off);293294// check if two results are equal or not295if (!isEqual(array, txtOffset, outputText, 0,296outputText.length)) {297throw new RuntimeException(298"Two results are not equal, mode:" + mode);299}300}301302/*303* Execute GCM encryption/decryption of textBB. AAD and text to process are304* placed in different byte buffers. Data are processed twice:305* - in a separately allocated buffer306* - in a buffer that shares content of the textBB307* Check if results are equal308*/309private void runGCMWithSeparateBuffers(int mode, ByteBuffer buffer,310ByteBuffer textBB, int txtOffset, int dataLength,311AlgorithmParameters params) throws Exception {312// take offset into account313textBB.position(txtOffset);314textBB.mark();315316// first, generate the cipher text at an allocated buffer317Cipher cipher = createCipher(mode, params);318cipher.updateAAD(buffer);319buffer.flip();320ByteBuffer outBB = ByteBuffer.allocateDirect(321cipher.getOutputSize(dataLength));322323cipher.doFinal(textBB, outBB);// get cipher text in outBB324outBB.flip();325326// restore positions327textBB.reset();328329// next, generate cipher text again in a buffer that shares content330Cipher anotherCipher = createCipher(mode, params);331anotherCipher.updateAAD(buffer);332buffer.flip();333ByteBuffer buf2 = textBB.duplicate(); // buf2 shares textBuf context334buf2.limit(txtOffset + anotherCipher.getOutputSize(dataLength));335int dataProcessed2 = anotherCipher.doFinal(textBB, buf2);336buf2.position(txtOffset);337buf2.limit(txtOffset + dataProcessed2);338339if (!buf2.equals(outBB)) {340throw new RuntimeException(341"Two results are not equal, mode:" + mode);342}343}344345/*346* Execute GCM encryption/decryption of text. AAD and a text to process are347* placed in the same buffer. Data is processed twice:348* - in a separately allocated buffer349* - in a buffer that shares content of the AAD_and_Text_BB350*/351private void runGCMWithSameBuffer(int mode, ByteBuffer buffer,352int txtOffset, int length, AlgorithmParameters params)353throws Exception {354355// allocate a separate buffer356Cipher cipher = createCipher(mode, params);357ByteBuffer outBB = ByteBuffer.allocateDirect(358cipher.getOutputSize(length));359360// first, generate the cipher text at an allocated buffer361buffer.flip();362buffer.limit(AADLength);363cipher.updateAAD(buffer);364buffer.limit(AADLength + txtOffset + length);365buffer.position(AADLength + txtOffset);366cipher.doFinal(buffer, outBB);367outBB.flip(); // cipher text in outBB368369// next, generate cipherText again in the same buffer370Cipher anotherCipher = createCipher(mode, params);371buffer.flip();372buffer.limit(AADLength);373anotherCipher.updateAAD(buffer);374buffer.limit(AADLength + txtOffset + length);375buffer.position(AADLength + txtOffset);376377// share textBuf context378ByteBuffer buf2 = buffer.duplicate();379buf2.limit(AADLength + txtOffset + anotherCipher.getOutputSize(length));380int dataProcessed2 = anotherCipher.doFinal(buffer, buf2);381buf2.position(AADLength + txtOffset);382buf2.limit(AADLength + txtOffset + dataProcessed2);383384if (!buf2.equals(outBB)) {385throw new RuntimeException(386"Two results are not equal, mode:" + mode);387}388}389390private boolean isEqual(byte[] A, int offsetA, byte[] B, int offsetB,391int bytesToCompare) {392System.out.println("offsetA: " + offsetA + " offsetB: " + offsetA393+ " bytesToCompare: " + bytesToCompare);394for (int i = 0; i < bytesToCompare; i++) {395int setA = i + offsetA;396int setB = i + offsetB;397if (setA > A.length - 1 || setB > B.length - 1398|| A[setA] != B[setB]) {399System.err.println("i = " + i + " A[setA] = " + A[setA] +400" B[setB] = " + B[setB]);401return false;402}403}404405return true;406}407408/*409* Creates a Cipher object for testing: for encryption it creates new Cipher410* based on previously saved parameters (it is prohibited to use the same411* Cipher twice for encription during GCM mode), or returns initiated412* existing Cipher.413*/414private Cipher createCipher(int mode, AlgorithmParameters params)415throws Exception {416Cipher cipher = Cipher.getInstance(transformation, provider);417if (Cipher.ENCRYPT_MODE == mode) {418// initiate it with the saved parameters419if (params != null) {420cipher.init(Cipher.ENCRYPT_MODE, key, params);421} else {422// intiate the cipher and save parameters423cipher.init(Cipher.ENCRYPT_MODE, key);424}425} else if (cipher != null) {426cipher.init(Cipher.DECRYPT_MODE, key, params);427} else {428throw new RuntimeException("Can't create cipher");429}430431return cipher;432}433434}435436437