From dc9c77bebe28570096345d3d22fd3ed10280acad Mon Sep 17 00:00:00 2001 From: Anthony Scarpino Date: Wed, 6 Dec 2023 18:09:10 +0000 Subject: [PATCH] 8318756: Create better internal buffer for AEADs Reviewed-by: djelinski --- .../crypto/provider/AEADBufferedStream.java | 108 +++++ .../sun/crypto/provider/ChaCha20Cipher.java | 291 ++++++++++++- .../crypto/provider/GaloisCounterMode.java | 92 ++--- ...GCMBufferTest.java => AEADBufferTest.java} | 391 ++++++++++-------- .../bench/javax/crypto/full/AESGCMBench.java | 96 +---- .../javax/crypto/full/AESGCMByteBuffer.java | 124 +----- .../bench/javax/crypto/full/BenchBase.java | 131 ++++++ .../javax/crypto/full/ByteBufferBase.java | 160 +++++++ .../javax/crypto/full/CC20P1305Bench.java | 49 +++ .../crypto/full/CC20P1305ByteBuffer.java | 50 +++ .../bench/javax/crypto/small/AESGCMBench.java | 20 +- .../javax/crypto/small/AESGCMByteBuffer.java | 20 +- .../javax/crypto/small/CC20P1305Bench.java | 48 +++ .../crypto/small/CC20P1305ByteBuffer.java | 48 +++ 14 files changed, 1178 insertions(+), 450 deletions(-) create mode 100644 src/java.base/share/classes/com/sun/crypto/provider/AEADBufferedStream.java rename test/jdk/com/sun/crypto/provider/Cipher/AEAD/{GCMBufferTest.java => AEADBufferTest.java} (69%) create mode 100644 test/micro/org/openjdk/bench/javax/crypto/full/BenchBase.java create mode 100644 test/micro/org/openjdk/bench/javax/crypto/full/ByteBufferBase.java create mode 100644 test/micro/org/openjdk/bench/javax/crypto/full/CC20P1305Bench.java create mode 100644 test/micro/org/openjdk/bench/javax/crypto/full/CC20P1305ByteBuffer.java create mode 100644 test/micro/org/openjdk/bench/javax/crypto/small/CC20P1305Bench.java create mode 100644 test/micro/org/openjdk/bench/javax/crypto/small/CC20P1305ByteBuffer.java diff --git a/src/java.base/share/classes/com/sun/crypto/provider/AEADBufferedStream.java b/src/java.base/share/classes/com/sun/crypto/provider/AEADBufferedStream.java new file mode 100644 index 00000000000..340458ca77e --- /dev/null +++ b/src/java.base/share/classes/com/sun/crypto/provider/AEADBufferedStream.java @@ -0,0 +1,108 @@ +/* + * Copyright (c) 2023, 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. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * 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. + */ + +package com.sun.crypto.provider; + +import jdk.internal.util.ArraysSupport; + +import java.io.ByteArrayOutputStream; +import java.nio.ByteBuffer; +import java.util.Arrays; +import java.util.HexFormat; + +/** + * This class extends ByteArrayOutputStream by optimizing internal buffering. + * It skips bounds checking, as the buffers are known and input previously + * checked. toByteArray() returns the internal buffer to avoid an extra copy. + * + * This uses `count` to determine the state of `buf`. `buf` can still + * point to an array while `count` equals zero. + */ +final class AEADBufferedStream extends ByteArrayOutputStream { + + /** + * Create an instance with the specified buffer + */ + + public AEADBufferedStream(int len) { + super(len); + } + + /** + * This method saves memory by returning the internal buffer. The calling + * method must use {@code size()} for the relevant data length as the + * returning byte[] maybe larger. + * + * @return internal buffer. + */ + public byte[] getBuffer() { + return buf; + } + + /** + * This method with expand the buffer if {@code count} + {@code len} + * is larger than the buffer byte[] length. + * @param len length to add to the current buffer + */ + private void checkCapacity(int len) { + int blen = buf.length; + // Create a new larger buffer and append the new data + if (blen < count + len) { + buf = Arrays.copyOf(buf, ArraysSupport.newLength(blen, len, blen)); + } + } + + /** + * Takes a ByteBuffer writing non-blocksize data directly to the internal + * buffer. + * @param src remaining non-blocksize ByteBuffer + */ + public void write(ByteBuffer src) { + int pos = src.position(); + int len = src.remaining(); + + if (src.hasArray()) { + write(src.array(), pos + src.arrayOffset(), len); + src.position(pos + len); + return; + } + + checkCapacity(len); + src.get(buf, count, len); + count += len; + } + + @Override + public void write(byte[] in, int offset, int len) { + checkCapacity(len); + System.arraycopy(in, offset, buf, count, len); + count += len; + } + + @Override + public String toString() { + return (count == 0 ? "null" : HexFormat.of().formatHex(buf)); + } +} diff --git a/src/java.base/share/classes/com/sun/crypto/provider/ChaCha20Cipher.java b/src/java.base/share/classes/com/sun/crypto/provider/ChaCha20Cipher.java index 857cf52d3b6..ed2fda5bf00 100644 --- a/src/java.base/share/classes/com/sun/crypto/provider/ChaCha20Cipher.java +++ b/src/java.base/share/classes/com/sun/crypto/provider/ChaCha20Cipher.java @@ -25,7 +25,6 @@ package com.sun.crypto.provider; -import java.io.ByteArrayOutputStream; import java.io.IOException; import java.lang.invoke.MethodHandles; import java.lang.invoke.VarHandle; @@ -64,7 +63,6 @@ abstract class ChaCha20Cipher extends CipherSpi { private static final int KS_MAX_LEN = 1024; private static final int KS_BLK_SIZE = 64; private static final int KS_SIZE_INTS = KS_BLK_SIZE / Integer.BYTES; - private static final int CIPHERBUF_BASE = 1024; // The initialization state of the cipher private boolean initialized; @@ -650,7 +648,7 @@ abstract class ChaCha20Cipher extends CipherSpi { try { engine.doUpdate(in, inOfs, inLen, out, 0); } catch (ShortBufferException | KeyException exc) { - throw new RuntimeException(exc); + throw new ProviderException(exc); } return out; @@ -681,11 +679,35 @@ abstract class ChaCha20Cipher extends CipherSpi { try { bytesUpdated = engine.doUpdate(in, inOfs, inLen, out, outOfs); } catch (KeyException ke) { - throw new RuntimeException(ke); + throw new ProviderException(ke); } return bytesUpdated; } + /** + * Update the currently running operation with additional data + * + * @param input the plaintext or ciphertext ByteBuffer + * @param output ByteBuffer that will hold the resulting data. This + * must be large enough to hold the resulting data. + * + * @return the length in bytes of the data written into the {@code out} + * buffer. + * + * @throws ShortBufferException if the buffer {@code out} does not have + * enough space to hold the resulting data. + */ + @Override + protected int engineUpdate(ByteBuffer input, ByteBuffer output) + throws ShortBufferException { + try { + return bufferCrypt(input, output, true); + } catch (AEADBadTagException e) { + // exception is never thrown by update ops + throw new AssertionError(e); + } + } + /** * Complete the currently running operation using any final * data provided by the caller. @@ -753,6 +775,118 @@ abstract class ChaCha20Cipher extends CipherSpi { return bytesUpdated; } + /** + * Complete the currently running operation using any final + * data provided by the caller. + * + * @param input the plaintext or ciphertext input bytebuffer. + * @param output ByteBuffer that will hold the resulting data. This + * must be large enough to hold the resulting data. + * + * @return the resulting plaintext or ciphertext bytes. + * + * @throws AEADBadTagException if, during decryption, the provided tag + * does not match the calculated tag. + */ + @Override + protected int engineDoFinal(ByteBuffer input, ByteBuffer output) + throws ShortBufferException, AEADBadTagException { + return bufferCrypt(input, output, false); + } + + /* + * Optimized version of bufferCrypt from CipherSpi.java. Direct + * ByteBuffers send to the engine code. + */ + private int bufferCrypt(ByteBuffer input, ByteBuffer output, + boolean isUpdate) throws ShortBufferException, AEADBadTagException { + if ((input == null) || (output == null)) { + throw new NullPointerException + ("Input and output buffers must not be null"); + } + int inPos = input.position(); + int inLimit = input.limit(); + int inLen = inLimit - inPos; + if (isUpdate && (inLen == 0)) { + return 0; + } + int outLenNeeded = engine.getOutputSize(inLen, !isUpdate); + + if (output.remaining() < outLenNeeded) { + throw new ShortBufferException("Need at least " + outLenNeeded + + " bytes of space in output buffer"); + } + + int total = 0; + + // Check if input bytebuffer is heap-backed + if (input.hasArray()) { + byte[] inArray = input.array(); + int inOfs = input.arrayOffset() + inPos; + + byte[] outArray; + // Check if output bytebuffer is heap-backed + if (output.hasArray()) { + outArray = output.array(); + int outPos = output.position(); + int outOfs = output.arrayOffset() + outPos; + + // check array address and offsets and use temp output buffer + // if output offset is larger than input offset and + // falls within the range of input data + boolean useTempOut = false; + if (inArray == outArray && + ((inOfs < outOfs) && (outOfs < inOfs + inLen))) { + useTempOut = true; + outArray = new byte[outLenNeeded]; + outOfs = 0; + } + try { + if (isUpdate) { + total = engine.doUpdate(inArray, inOfs, inLen, outArray, + outOfs); + } else { + total = engine.doFinal(inArray, inOfs, inLen, outArray, + outOfs); + } + } catch (KeyException e) { + throw new ProviderException(e); + } + if (useTempOut) { + output.put(outArray, outOfs, total); + } else { + // adjust output position manually + output.position(outPos + total); + } + } else { // if output is direct + if (isUpdate) { + outArray = engineUpdate(inArray, inOfs, inLen); + } else { + outArray = engineDoFinal(inArray, inOfs, inLen); + } + if (outArray != null && outArray.length != 0) { + output.put(outArray); + total = outArray.length; + } + } + // adjust input position manually + input.position(inLimit); + } else { // Bytebuffers are both direct + try { + if (isUpdate) { + return engine.doUpdate(input, output); + } + return engine.doFinal(input, output); + } catch (KeyException e) { + throw new ProviderException(e); + } + } + + return total; + } + + + /** * Wrap a {@code Key} using this Cipher's current encryption parameters. * @@ -1243,6 +1377,11 @@ abstract class ChaCha20Cipher extends CipherSpi { */ int doFinal(byte[] in, int inOff, int inLen, byte[] out, int outOff) throws ShortBufferException, AEADBadTagException, KeyException; + + int doUpdate(ByteBuffer input, ByteBuffer output) throws + ShortBufferException, KeyException; + int doFinal(ByteBuffer input, ByteBuffer output) throws + ShortBufferException, KeyException, AEADBadTagException; } private final class EngineStreamOnly implements ChaChaEngine { @@ -1285,6 +1424,22 @@ abstract class ChaCha20Cipher extends CipherSpi { int outOff) throws ShortBufferException, KeyException { return doUpdate(in, inOff, inLen, out, outOff); } + + @Override + public int doUpdate(ByteBuffer input, ByteBuffer output) throws + ShortBufferException, KeyException { + byte[] data = new byte[input.remaining()]; + input.get(data); + doUpdate(data, 0, data.length, data, 0); + output.put(data); + return data.length; + } + + @Override + public int doFinal(ByteBuffer input, ByteBuffer output) + throws ShortBufferException, KeyException { + return doUpdate(input, output); + } } private final class EngineAEADEnc implements ChaChaEngine { @@ -1348,11 +1503,32 @@ abstract class ChaCha20Cipher extends CipherSpi { aadDone = false; return inLen + TAG_LENGTH; } + + @Override + public int doUpdate(ByteBuffer input, ByteBuffer output) throws + ShortBufferException, KeyException { + byte[] data = new byte[input.remaining()]; + input.get(data); + doUpdate(data, 0, data.length, data, 0); + output.put(data); + return data.length; + } + + @Override + public int doFinal(ByteBuffer input, ByteBuffer output) throws + ShortBufferException, KeyException { + int len = input.remaining(); + byte[] data = new byte[len + TAG_LENGTH]; + input.get(data, 0, len); + doFinal(data, 0, len, data, 0); + output.put(data); + return data.length; + } } private final class EngineAEADDec implements ChaChaEngine { - private final ByteArrayOutputStream cipherBuf; + private AEADBufferedStream cipherBuf = null; private final byte[] tag; @Override @@ -1364,20 +1540,32 @@ abstract class ChaCha20Cipher extends CipherSpi { // size. return (isFinal ? Integer.max(Math.addExact((inLen - TAG_LENGTH), - cipherBuf.size()), 0) : 0); + getBufferedLength()), 0) : 0); + } + + private void initBuffer(int len) { + if (cipherBuf == null) { + cipherBuf = new AEADBufferedStream(len); + } + } + + private int getBufferedLength() { + if (cipherBuf != null) { + return cipherBuf.size(); + } + return 0; } private EngineAEADDec() throws InvalidKeyException { initAuthenticator(); initCounterValue = 1; counter = initCounterValue; - cipherBuf = new ByteArrayOutputStream(CIPHERBUF_BASE); tag = new byte[TAG_LENGTH]; } @Override public int doUpdate(byte[] in, int inOff, int inLen, byte[] out, - int outOff) { + int outOff) { if (initialized) { // If this is the first update since AAD updates, signal that // we're done processing AAD info and pad the AAD to a multiple @@ -1389,6 +1577,7 @@ abstract class ChaCha20Cipher extends CipherSpi { if (in != null) { Objects.checkFromIndexSize(inOff, inLen, in.length); + initBuffer(inLen); cipherBuf.write(in, inOff, inLen); } } else { @@ -1399,6 +1588,14 @@ abstract class ChaCha20Cipher extends CipherSpi { return 0; } + + @Override + public int doUpdate(ByteBuffer input, ByteBuffer output) { + initBuffer(input.remaining()); + cipherBuf.write(input); + return 0; + } + @Override public int doFinal(byte[] in, int inOff, int inLen, byte[] out, int outOff) throws ShortBufferException, AEADBadTagException, @@ -1406,7 +1603,7 @@ abstract class ChaCha20Cipher extends CipherSpi { byte[] ctPlusTag; int ctPlusTagLen; - if (cipherBuf.size() == 0 && inOff == 0) { + if (getBufferedLength() == 0) { // No previous data has been seen before doFinal, so we do // not need to hold any ciphertext in a buffer. We can // process it directly from the "in" parameter. @@ -1415,10 +1612,11 @@ abstract class ChaCha20Cipher extends CipherSpi { ctPlusTagLen = inLen; } else { doUpdate(in, inOff, inLen, out, outOff); - ctPlusTag = cipherBuf.toByteArray(); - ctPlusTagLen = ctPlusTag.length; + ctPlusTag = cipherBuf.getBuffer(); + inOff = 0; + ctPlusTagLen = cipherBuf.size(); + cipherBuf.reset(); } - cipherBuf.reset(); // There must at least be a tag length's worth of ciphertext // data in the buffered input. @@ -1436,19 +1634,80 @@ abstract class ChaCha20Cipher extends CipherSpi { // Calculate and compare the tag. Only do the decryption // if and only if the tag matches. - authFinalizeData(ctPlusTag, 0, ctLen, tag, 0); - long tagCompare = ((long)asLongView.get(ctPlusTag, ctLen) ^ + authFinalizeData(ctPlusTag, inOff, ctLen, tag, 0); + long tagCompare = ((long)asLongView.get(ctPlusTag, ctLen + inOff) ^ (long)asLongView.get(tag, 0)) | - ((long)asLongView.get(ctPlusTag, ctLen + Long.BYTES) ^ + ((long)asLongView.get(ctPlusTag, ctLen + inOff + Long.BYTES) ^ (long)asLongView.get(tag, Long.BYTES)); if (tagCompare != 0) { throw new AEADBadTagException("Tag mismatch"); } - chaCha20Transform(ctPlusTag, 0, ctLen, out, outOff); + chaCha20Transform(ctPlusTag, inOff, ctLen, out, outOff); aadDone = false; return ctLen; } + + @Override + public int doFinal(ByteBuffer input, ByteBuffer output) + throws ShortBufferException, AEADBadTagException, KeyException { + int len; + int inLen = input.remaining(); + byte[] ct = null, buf = null; + //buf = (getBufferedLength() == 0 ? null : cipherBuf.toByteArray()); + int bufLen = 0; + // The length of cipher text and tag + int ctLen = getBufferedLength() + inLen; + + if (ctLen < TAG_LENGTH) { + throw new AEADBadTagException("Input too short - need tag"); + } + + if (inLen < TAG_LENGTH) { + if (inLen > 0) { + doUpdate(input, output); + } + if (cipherBuf != null) { + ct = cipherBuf.getBuffer(); + } + len = ctLen; + } else { + if (cipherBuf != null) { + buf = cipherBuf.getBuffer(); + bufLen = cipherBuf.size(); + } + ct = new byte[inLen]; + input.get(ct, 0, inLen); + len = inLen; + } + doUpdate(null, 0, 0, null, 0); + + // If there is an internal buffer, calculate its tag contribution. + if (buf != null) { + dataLen = authUpdate(buf, 0, bufLen); + } + // Complete tag calculation + len -= TAG_LENGTH; + authFinalizeData(ct, 0, len, tag, 0); + // Check tag + if ((((long) asLongView.get(ct, len) ^ + (long) asLongView.get(tag, 0)) | + ((long) asLongView.get(ct, len + Long.BYTES) ^ + (long) asLongView.get(tag, Long.BYTES))) != 0) { + throw new AEADBadTagException("Tag mismatch"); + } + + // decrypt internal buffer in-place, then put it into the bytebuffer + if (buf != null) { + chaCha20Transform(buf, 0, bufLen, buf, 0); + output.put(buf, 0, bufLen); + } + // decrypt input buffer in-place, append it to the bytebuffer + chaCha20Transform(ct, 0, len, ct, 0); + output.put(ct, 0, len); + aadDone = false; + return ctLen - TAG_LENGTH; + } } public static final class ChaCha20Only extends ChaCha20Cipher { diff --git a/src/java.base/share/classes/com/sun/crypto/provider/GaloisCounterMode.java b/src/java.base/share/classes/com/sun/crypto/provider/GaloisCounterMode.java index bca020b74ed..8cfd7d13f12 100644 --- a/src/java.base/share/classes/com/sun/crypto/provider/GaloisCounterMode.java +++ b/src/java.base/share/classes/com/sun/crypto/provider/GaloisCounterMode.java @@ -40,8 +40,6 @@ import javax.crypto.IllegalBlockSizeException; import javax.crypto.NoSuchPaddingException; import javax.crypto.ShortBufferException; import javax.crypto.spec.GCMParameterSpec; -import java.io.ByteArrayOutputStream; -import java.io.IOException; import java.lang.invoke.MethodHandles; import java.lang.invoke.VarHandle; import java.nio.ByteBuffer; @@ -249,7 +247,7 @@ abstract class GaloisCounterMode extends CipherSpi { protected AlgorithmParameters engineGetParameters() { GCMParameterSpec spec; spec = new GCMParameterSpec(tagLenBytes * 8, - iv == null ? createIv(random) : iv.clone()); + iv == null ? createIv(random) : iv); // iv.clone() not necessary try { AlgorithmParameters params = AlgorithmParameters.getInstance("GCM", @@ -680,12 +678,12 @@ abstract class GaloisCounterMode extends CipherSpi { final int blockSize; // buffer for AAD data; if null, meaning update has been called - ByteArrayOutputStream aadBuffer = null; + AEADBufferedStream aadBuffer = null; int sizeOfAAD = 0; boolean aadProcessed = false; // buffer data for crypto operation - ByteArrayOutputStream ibuffer = null; + AEADBufferedStream ibuffer = null; // Original dst buffer if there was an overlap situation ByteBuffer originalDst = null; @@ -736,7 +734,7 @@ abstract class GaloisCounterMode extends CipherSpi { // Initialize internal data buffer, if not already. void initBuffer(int len) { if (ibuffer == null) { - ibuffer = new ByteArrayOutputStream(len); + ibuffer = new AEADBufferedStream(len); } } @@ -788,20 +786,6 @@ abstract class GaloisCounterMode extends CipherSpi { } } - /** - * The method takes two buffers to create one block of data. The - * difference with the other mergeBlock is this will calculate - * the bufLen from the existing 'buffer' length & offset - * - * This is only called when buffer length is less than a blockSize - * @return number of bytes used from 'in' - */ - int mergeBlock(byte[] buffer, int bufOfs, byte[] in, int inOfs, - int inLen, byte[] block) { - return mergeBlock(buffer, bufOfs, buffer.length - bufOfs, in, - inOfs, inLen, block); - } - /** * The method takes two buffers to create one block of data * @@ -822,7 +806,7 @@ abstract class GaloisCounterMode extends CipherSpi { } /** - * Continues a multi-part update of the Additional Authentication + * Continues a multipart update of the Additional Authentication * Data (AAD), using a subset of the provided buffer. All AAD must be * supplied before beginning operations on the ciphertext (via the * {@code update} and {@code doFinal} methods). @@ -843,7 +827,7 @@ abstract class GaloisCounterMode extends CipherSpi { if (aadBuffer == null) { if (sizeOfAAD == 0 && !aadProcessed) { - aadBuffer = new ByteArrayOutputStream(len); + aadBuffer = new AEADBufferedStream(len); } else { // update has already been called throw new IllegalStateException @@ -856,18 +840,17 @@ abstract class GaloisCounterMode extends CipherSpi { // Feed the AAD data to GHASH, pad if necessary void processAAD() { if (aadBuffer != null) { - if (aadBuffer.size() > 0) { - byte[] aad = aadBuffer.toByteArray(); - sizeOfAAD = aad.length; - - int lastLen = aad.length % blockSize; + sizeOfAAD = aadBuffer.size(); + if (sizeOfAAD > 0) { + byte[] aad = aadBuffer.getBuffer(); + int lastLen = sizeOfAAD % blockSize; if (lastLen != 0) { - ghash.update(aad, 0, aad.length - lastLen); + ghash.update(aad, 0, sizeOfAAD - lastLen); byte[] padded = expandToOneBlock(aad, - aad.length - lastLen, lastLen, blockSize); + sizeOfAAD - lastLen, lastLen, blockSize); ghash.update(padded); } else { - ghash.update(aad); + ghash.update(aad, 0, sizeOfAAD); } } aadBuffer = null; @@ -1147,7 +1130,7 @@ abstract class GaloisCounterMode extends CipherSpi { // if there is enough data in the ibuffer and 'in', encrypt it. if (bLen > 0) { - byte[] buffer = ibuffer.toByteArray(); + byte[] buffer = ibuffer.getBuffer(); // number of bytes not filling a block int remainder = blockSize - bLen; @@ -1167,7 +1150,7 @@ abstract class GaloisCounterMode extends CipherSpi { } } - // Encrypt the remaining blocks inside of 'in' + // Encrypt the remaining blocks inside 'in' if (inLen >= PARALLEL_LEN) { int r = GaloisCounterMode.implGCMCrypt(in, inOfs, inLen, out, outOfs, out, outOfs, gctr, ghash); @@ -1223,7 +1206,8 @@ abstract class GaloisCounterMode extends CipherSpi { // Check if there is enough 'src' and 'buffer' to fill a block if (src.remaining() >= remainder) { byte[] block = new byte[blockSize]; - ByteBuffer buffer = ByteBuffer.wrap(ibuffer.toByteArray()); + ByteBuffer buffer = ByteBuffer.wrap(ibuffer.getBuffer(), + 0, ibuffer.size()); buffer.get(block, 0, bLen); src.get(block, bLen, remainder); len += op.update(ByteBuffer.wrap(block, 0, blockSize), @@ -1251,14 +1235,8 @@ abstract class GaloisCounterMode extends CipherSpi { // Write the remaining bytes into the 'ibuffer' if (srcLen > 0) { initBuffer(srcLen); - byte[] b = new byte[srcLen]; - src.get(b); // remainder offset is based on original buffer length - try { - ibuffer.write(b); - } catch (IOException e) { - throw new RuntimeException(e); - } + ibuffer.write(src); } restoreDst(dst); @@ -1290,13 +1268,13 @@ abstract class GaloisCounterMode extends CipherSpi { // process what is in the ibuffer if (bLen > 0) { - byte[] buffer = ibuffer.toByteArray(); + byte[] buffer = ibuffer.getBuffer(); // Make a block if the remaining ibuffer and 'in' can make one. if (bLen + inLen >= blockSize) { int r; block = new byte[blockSize]; - r = mergeBlock(buffer, 0, in, inOfs, inLen, block); + r = mergeBlock(buffer, 0, ibuffer.size(), in, inOfs, inLen, block); inOfs += r; inLen -= r; op.update(block, 0, blockSize, out, outOfs); @@ -1353,7 +1331,8 @@ abstract class GaloisCounterMode extends CipherSpi { if (len > 0) { processed += doLastBlock(op, (ibuffer == null || ibuffer.size() == 0) ? null : - ByteBuffer.wrap(ibuffer.toByteArray()), src, dst); + ByteBuffer.wrap(ibuffer.getBuffer(), 0, + ibuffer.size()), src, dst); } // release buffer if needed @@ -1427,9 +1406,10 @@ abstract class GaloisCounterMode extends CipherSpi { tagLenBytes); } else { // tagOfs will be negative - byte[] buffer = ibuffer.toByteArray(); - tagOfs = mergeBlock(buffer, - buffer.length - (tagLenBytes - inLen), in, inOfs, inLen, + byte[] buffer = ibuffer.getBuffer(); + int ofs = ibuffer.size() - (tagLenBytes - inLen); + tagOfs = mergeBlock(buffer, ofs, ibuffer.size() - ofs, + in, inOfs, inLen, tag) - tagLenBytes; } } @@ -1475,15 +1455,8 @@ abstract class GaloisCounterMode extends CipherSpi { src.remaining(), null, 0); src.position(src.limit()); } else { - byte[] b = new byte[src.remaining()]; - src.get(b); - initBuffer(b.length); - try { - ibuffer.write(b); - } catch (IOException e) { - throw new ProviderException( - "Unable to add remaining input to the buffer", e); - } + initBuffer(src.remaining()); + ibuffer.write(src); } } return 0; @@ -1491,7 +1464,7 @@ abstract class GaloisCounterMode extends CipherSpi { /** * Use available data from ibuffer and 'in' to verify and decrypt the - * data. If the verification fails, the 'out' left to it's original + * data. If the verification fails, the 'out' left to its original * values if crypto was in-place; otherwise 'out' is zeroed */ @Override @@ -1551,7 +1524,7 @@ abstract class GaloisCounterMode extends CipherSpi { /** * Use available data from ibuffer and 'src' to verify and decrypt the - * data. If the verification fails, the 'dst' left to it's original + * data. If the verification fails, the 'dst' left to its original * values if crypto was in-place; otherwise 'dst' is zeroed */ @Override @@ -1568,7 +1541,8 @@ abstract class GaloisCounterMode extends CipherSpi { // Check if ibuffer has data if (getBufferedLength() != 0) { - buffer = ByteBuffer.wrap(ibuffer.toByteArray()); + buffer = ByteBuffer.wrap(ibuffer.getBuffer(), 0, + ibuffer.size()); len += buffer.remaining(); } @@ -1684,7 +1658,7 @@ abstract class GaloisCounterMode extends CipherSpi { } if (bLen > 0) { - buffer = ibuffer.toByteArray(); + buffer = ibuffer.getBuffer(); if (bLen >= PARALLEL_LEN) { len = GaloisCounterMode.implGCMCrypt(buffer, 0, bLen, diff --git a/test/jdk/com/sun/crypto/provider/Cipher/AEAD/GCMBufferTest.java b/test/jdk/com/sun/crypto/provider/Cipher/AEAD/AEADBufferTest.java similarity index 69% rename from test/jdk/com/sun/crypto/provider/Cipher/AEAD/GCMBufferTest.java rename to test/jdk/com/sun/crypto/provider/Cipher/AEAD/AEADBufferTest.java index dd414ad06da..4bf5a6263a9 100644 --- a/test/jdk/com/sun/crypto/provider/Cipher/AEAD/GCMBufferTest.java +++ b/test/jdk/com/sun/crypto/provider/Cipher/AEAD/AEADBufferTest.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2020, 2021, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2020, 2023, 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 @@ -34,16 +34,18 @@ import javax.crypto.Cipher; import javax.crypto.SecretKey; import javax.crypto.spec.GCMParameterSpec; +import javax.crypto.spec.IvParameterSpec; import javax.crypto.spec.SecretKeySpec; import java.io.ByteArrayOutputStream; import java.nio.ByteBuffer; import java.security.SecureRandom; +import java.security.spec.AlgorithmParameterSpec; import java.util.Arrays; import java.util.HashMap; import java.util.HexFormat; import java.util.List; -public class GCMBufferTest implements Cloneable { +public class AEADBufferTest implements Cloneable { // Data type for the operation enum dtype { BYTE, HEAP, DIRECT }; @@ -52,7 +54,7 @@ public class GCMBufferTest implements Cloneable { // List of enum values for order of operation List ops; - static final int AESBLOCK = 16; + static final String AES = "AES"; // The remaining input data length is inserted at the particular index // in sizes[] during execution. static final int REMAINDER = -1; @@ -71,20 +73,16 @@ public class GCMBufferTest implements Cloneable { int id; SecretKey key; byte[] iv; + int counter; // for CC20 byte[] pt; byte[] aad; byte[] ct; byte[] tag; + int blockSize; // 16 for GCM, 0 for CC20 Data(String keyalgo, int id, String key, String iv, byte[] pt, String aad, String ct, String tag) { - this.id = id; - this.key = new SecretKeySpec(HexToBytes(key), keyalgo); - this.iv = HexToBytes(iv); - this.pt = pt; - this.aad = HexToBytes(aad); - this.ct = HexToBytes(ct); - this.tag = HexToBytes(tag); + this(keyalgo, id, key, iv, 0,pt, aad, ct,tag); } Data(String keyalgo, int id, String key, String iv, String pt, String aad, @@ -92,21 +90,42 @@ public class GCMBufferTest implements Cloneable { this(keyalgo, id, key, iv, HexToBytes(pt), aad, ct, tag); } - Data(String keyalgo, int id, String key, int ptlen) { + Data(String keyalgo, int id, String key, String iv, int counter, byte[] pt, String aad, + String ct, String tag) { this.id = id; this.key = new SecretKeySpec(HexToBytes(key), keyalgo); - iv = new byte[16]; + this.iv = HexToBytes(iv); + this.counter = counter; + this.pt = pt; + this.aad = HexToBytes(aad); + this.ct = HexToBytes(ct); + this.tag = HexToBytes(tag); + this.blockSize = (keyalgo.equals(AES) ? 16 : 0); + } + + Data(String keyalgo, int id, String key, String iv, int counter, String pt, String aad, + String ct, String tag) { + this(keyalgo, id, key, iv, counter, HexToBytes(pt), aad, ct, tag); + } + + Data(String keyalgo, int id, String key, int ivLen, int ptlen) { + this.id = id; + this.key = new SecretKeySpec(HexToBytes(key), keyalgo); + iv = new byte[ivLen]; + counter = 0; pt = new byte[ptlen]; - tag = new byte[12]; + tag = new byte[16]; aad = new byte[0]; + boolean isGCM = keyalgo.equals(AES); + this.blockSize = (isGCM ? 16 : 0); byte[] tct = null; try { SecureRandom r = new SecureRandom(); r.nextBytes(iv); r.nextBytes(pt); - Cipher c = Cipher.getInstance("AES/GCM/NoPadding"); + Cipher c = Cipher.getInstance(isGCM ? "AES/GCM/NoPadding": "ChaCha20-Poly1305"); c.init(Cipher.ENCRYPT_MODE, this.key, - new GCMParameterSpec(tag.length * 8, this.iv)); + getAPS(keyalgo, tag.length, iv)); tct = c.doFinal(pt); } catch (Exception e) { throw new RuntimeException("Error in generating data for length " + @@ -117,7 +136,7 @@ public class GCMBufferTest implements Cloneable { System.arraycopy(tct, ct.length, tag, 0, tag.length); } - private static final byte[] HexToBytes(String hexVal) { + private static byte[] HexToBytes(String hexVal) { if (hexVal == null) { return new byte[0]; } @@ -133,15 +152,15 @@ public class GCMBufferTest implements Cloneable { * doFinal operation will occur. If multiple dtypes are * specified, the last is a doFinal, the others are updates. */ - GCMBufferTest(String algo, List ops) { + AEADBufferTest(String algo, List ops) { this.algo = algo; this.ops = ops; theoreticalCheck = true; dataSet = datamap.get(algo); } - public GCMBufferTest clone() throws CloneNotSupportedException{ - return (GCMBufferTest)super.clone(); + public AEADBufferTest clone() throws CloneNotSupportedException{ + return (AEADBufferTest)super.clone(); } /** @@ -150,7 +169,7 @@ public class GCMBufferTest implements Cloneable { * that index during execution. * @param sizes Data sizes for each dtype in the list. */ - GCMBufferTest dataSegments(int[] sizes) { + AEADBufferTest dataSegments(int[] sizes) { this.sizes = sizes; return this; } @@ -158,7 +177,7 @@ public class GCMBufferTest implements Cloneable { /** * Do not perform in-place operations */ - GCMBufferTest differentBufferOnly() { + AEADBufferTest differentBufferOnly() { this.same = false; return this; } @@ -167,7 +186,7 @@ public class GCMBufferTest implements Cloneable { * Enable incrementing through each data size available. This can only be * used when the List has more than one dtype entry. */ - GCMBufferTest incrementalSegments() { + AEADBufferTest incrementalSegments() { this.incremental = true; return this; } @@ -177,7 +196,7 @@ public class GCMBufferTest implements Cloneable { * * @param id id value for the test data to used in this test. */ - GCMBufferTest dataSet(int id) throws Exception { + AEADBufferTest dataSet(int id) throws Exception { for (Data d : datamap.get(algo)) { if (d.id == id) { dataSet = List.of(d); @@ -192,7 +211,7 @@ public class GCMBufferTest implements Cloneable { * @param offset value for inOfs and outOfs * @return */ - GCMBufferTest offset(int offset) { + AEADBufferTest offset(int offset) { this.inOfs = offset; this.outOfs = offset; return this; @@ -201,9 +220,8 @@ public class GCMBufferTest implements Cloneable { /** * Set the input offset * @param offset value for input offset - * @return */ - GCMBufferTest inOfs(int offset) { + AEADBufferTest inOfs(int offset) { this.inOfs = offset; return this; } @@ -211,9 +229,8 @@ public class GCMBufferTest implements Cloneable { /** * Set the output offset * @param offset value for output offset - * @return */ - GCMBufferTest outOfs(int offset) { + AEADBufferTest outOfs(int offset) { this.outOfs = offset; return this; } @@ -313,7 +330,7 @@ public class GCMBufferTest implements Cloneable { // Test with in-place buffers if (same) { System.err.println("\tinput len: " + input.length + " inOfs " + - inOfs + " outOfs " + outOfs + " in/out buffer: in-place"); + inOfs + " outOfs " + outOfs + " in/out buffer: in-place"); cryptoSameBuffer(true, data, input, output); } } @@ -335,31 +352,40 @@ public class GCMBufferTest implements Cloneable { // Test with in-place buffers if (same) { System.err.println("\tinput len: " + input.length + " inOfs " + - inOfs + " outOfs " + outOfs + " in-place: same"); + inOfs + " outOfs " + outOfs + " in-place: same"); cryptoSameBuffer(false, data, input, output); } } + static AlgorithmParameterSpec getAPS(String algo, int tLen, byte[] iv) { + return switch (algo) { + case "AES", "AES/GCM/NoPadding" -> + new GCMParameterSpec(tLen * 8, iv); + case "CC20", "ChaCha20-Poly1305" -> new IvParameterSpec(iv); + default -> null; + }; + } + /** * Perform cipher operation using different input and output buffers. * This method allows mixing of data types (byte, heap, direct). */ - void crypto(boolean encrypt, Data d, byte[] input, byte[] output) - throws Exception { + void crypto(boolean encrypt, Data d, byte[] input, byte[] output) + throws Exception { byte[] pt = new byte[input.length + inOfs]; System.arraycopy(input, 0, pt, inOfs, input.length); - byte[] expectedOut = new byte[output.length + outOfs]; - System.arraycopy(output, 0, expectedOut, outOfs, output.length); + byte[] expectedOut = new byte[output.length + outOfs]; + System.arraycopy(output, 0, expectedOut, outOfs, output.length); int plen = input.length / ops.size(); // partial input length int theoreticallen;// expected output length int dataoffset = 0; // offset of unconsumed data in pt int index = 0; // index of which op we are on int rlen; // result length - int pbuflen = 0; // plen remaining in the GCM internal buffers + int pbuflen = 0; // plen remaining in the internal buffers Cipher cipher = Cipher.getInstance(algo); cipher.init((encrypt ? Cipher.ENCRYPT_MODE : Cipher.DECRYPT_MODE), - d.key, new GCMParameterSpec(d.tag.length * 8, d.iv)); + d.key, getAPS(algo, d.tag.length, d.iv)); cipher.updateAAD(d.aad); ByteArrayOutputStream ba = new ByteArrayOutputStream(); @@ -382,10 +408,11 @@ public class GCMBufferTest implements Cloneable { /* * The theoretical limit is the length of the data sent to - * update() + any data might be setting in CipherCore or GCM + * update() + any data might be setting in CipherCore or AEAD * internal buffers % the block size. */ - theoreticallen = (plen + pbuflen) - ((plen + pbuflen) % AESBLOCK); + theoreticallen = (plen + pbuflen) - (d.blockSize > 0 ? + (plen + pbuflen) % d.blockSize : 0); // Update operations switch (v) { @@ -525,7 +552,8 @@ public class GCMBufferTest implements Cloneable { byte[] expectedOut = new byte[output.length + outOfs]; System.arraycopy(output, 0, expectedOut, outOfs, output.length); int plen = input.length / ops.size(); // partial input length - int theorticallen = plen - (plen % AESBLOCK); // output length + int theorticallen = plen - + (d.blockSize > 0 ? plen % d.blockSize : 0); // output length int dataoffset = 0; int index = 0; int rlen = 0; // result length @@ -533,7 +561,7 @@ public class GCMBufferTest implements Cloneable { Cipher cipher = Cipher.getInstance(algo); cipher.init((encrypt ? Cipher.ENCRYPT_MODE : Cipher.DECRYPT_MODE), - d.key, new GCMParameterSpec(d.tag.length * 8, d.iv)); + d.key, getAPS(algo, d.tag.length, d.iv)); cipher.updateAAD(d.aad); // Prepare data @@ -567,8 +595,8 @@ public class GCMBufferTest implements Cloneable { data, len + outOfs); } case HEAP, DIRECT -> { - theorticallen = bbin.remaining() - - (bbin.remaining() % AESBLOCK); + theorticallen = bbin.remaining() - (d.blockSize > 0 ? + bbin.remaining() % d.blockSize : 0); rlen = cipher.update(bbin, bbout); } default -> throw new Exception("Unknown op: " + v.name()); @@ -620,7 +648,7 @@ public class GCMBufferTest implements Cloneable { } } } - static void offsetTests(GCMBufferTest t) throws Exception { + static void offsetTests(AEADBufferTest t) throws Exception { t.clone().offset(2).test(); t.clone().inOfs(2).test(); // Test not designed for overlap situations @@ -628,108 +656,205 @@ public class GCMBufferTest implements Cloneable { } public static void main(String args[]) throws Exception { - GCMBufferTest t; + AEADBufferTest t; initTest(); + // **** GCM Tests + // Test single byte array - new GCMBufferTest("AES/GCM/NoPadding", List.of(dtype.BYTE)).test(); - offsetTests(new GCMBufferTest("AES/GCM/NoPadding", List.of(dtype.BYTE))); + new AEADBufferTest("AES/GCM/NoPadding", List.of(dtype.BYTE)).test(); + offsetTests(new AEADBufferTest("AES/GCM/NoPadding", List.of(dtype.BYTE))); // Test update-doFinal with byte arrays - new GCMBufferTest("AES/GCM/NoPadding", List.of(dtype.BYTE, dtype.BYTE)).test(); - offsetTests(new GCMBufferTest("AES/GCM/NoPadding", List.of(dtype.BYTE, dtype.BYTE))); + new AEADBufferTest("AES/GCM/NoPadding", List.of(dtype.BYTE, dtype.BYTE)).test(); + offsetTests(new AEADBufferTest("AES/GCM/NoPadding", List.of(dtype.BYTE, dtype.BYTE))); // Test update-update-doFinal with byte arrays - new GCMBufferTest("AES/GCM/NoPadding", + new AEADBufferTest("AES/GCM/NoPadding", List.of(dtype.BYTE, dtype.BYTE, dtype.BYTE)).test(); - offsetTests(new GCMBufferTest("AES/GCM/NoPadding", List.of(dtype.BYTE, dtype.BYTE, dtype.BYTE))); + offsetTests(new AEADBufferTest("AES/GCM/NoPadding", List.of(dtype.BYTE, dtype.BYTE, dtype.BYTE))); // Test single heap bytebuffer - new GCMBufferTest("AES/GCM/NoPadding", List.of(dtype.HEAP)).test(); - offsetTests(new GCMBufferTest("AES/GCM/NoPadding", List.of(dtype.HEAP))); + new AEADBufferTest("AES/GCM/NoPadding", List.of(dtype.HEAP)).test(); + offsetTests(new AEADBufferTest("AES/GCM/NoPadding", List.of(dtype.HEAP))); // Test update-doFinal with heap bytebuffer - new GCMBufferTest("AES/GCM/NoPadding", + new AEADBufferTest("AES/GCM/NoPadding", List.of(dtype.HEAP, dtype.HEAP)).test(); - offsetTests(new GCMBufferTest("AES/GCM/NoPadding", List.of(dtype.HEAP, dtype.HEAP))); + offsetTests(new AEADBufferTest("AES/GCM/NoPadding", List.of(dtype.HEAP, dtype.HEAP))); // Test update-update-doFinal with heap bytebuffer - new GCMBufferTest("AES/GCM/NoPadding", + new AEADBufferTest("AES/GCM/NoPadding", List.of(dtype.HEAP, dtype.HEAP, dtype.HEAP)).test(); - offsetTests(new GCMBufferTest("AES/GCM/NoPadding", List.of(dtype.HEAP, dtype.HEAP, dtype.HEAP))); + offsetTests(new AEADBufferTest("AES/GCM/NoPadding", List.of(dtype.HEAP, dtype.HEAP, dtype.HEAP))); // Test single direct bytebuffer - new GCMBufferTest("AES/GCM/NoPadding", List.of(dtype.DIRECT)).test(); - offsetTests(new GCMBufferTest("AES/GCM/NoPadding", List.of(dtype.DIRECT))); + new AEADBufferTest("AES/GCM/NoPadding", List.of(dtype.DIRECT)).test(); + offsetTests(new AEADBufferTest("AES/GCM/NoPadding", List.of(dtype.DIRECT))); // Test update-doFinal with direct bytebuffer - new GCMBufferTest("AES/GCM/NoPadding", + new AEADBufferTest("AES/GCM/NoPadding", List.of(dtype.DIRECT, dtype.DIRECT)).test(); - offsetTests(new GCMBufferTest("AES/GCM/NoPadding", + offsetTests(new AEADBufferTest("AES/GCM/NoPadding", List.of(dtype.DIRECT, dtype.DIRECT))); // Test update-update-doFinal with direct bytebuffer - new GCMBufferTest("AES/GCM/NoPadding", + new AEADBufferTest("AES/GCM/NoPadding", List.of(dtype.DIRECT, dtype.DIRECT, dtype.DIRECT)).test(); - offsetTests(new GCMBufferTest("AES/GCM/NoPadding", + offsetTests(new AEADBufferTest("AES/GCM/NoPadding", List.of(dtype.DIRECT, dtype.DIRECT, dtype.DIRECT))); // Test update-update-doFinal with byte arrays and preset data sizes - t = new GCMBufferTest("AES/GCM/NoPadding", + t = new AEADBufferTest("AES/GCM/NoPadding", List.of(dtype.BYTE, dtype.BYTE, dtype.BYTE)).dataSegments( - new int[] { 1, 1, GCMBufferTest.REMAINDER}); + new int[] { 1, 1, AEADBufferTest.REMAINDER}); t.clone().test(); offsetTests(t.clone()); // Test update-doFinal with a byte array and a direct bytebuffer - t = new GCMBufferTest("AES/GCM/NoPadding", + t = new AEADBufferTest("AES/GCM/NoPadding", List.of(dtype.BYTE, dtype.DIRECT)).differentBufferOnly(); t.clone().test(); offsetTests(t.clone()); // Test update-doFinal with a byte array and heap and direct bytebuffer - t = new GCMBufferTest("AES/GCM/NoPadding", + t = new AEADBufferTest("AES/GCM/NoPadding", List.of(dtype.BYTE, dtype.HEAP, dtype.DIRECT)).differentBufferOnly(); t.clone().test(); offsetTests(t.clone()); // Test update-doFinal with a direct bytebuffer and a byte array. - t = new GCMBufferTest("AES/GCM/NoPadding", + t = new AEADBufferTest("AES/GCM/NoPadding", List.of(dtype.DIRECT, dtype.BYTE)).differentBufferOnly(); t.clone().test(); offsetTests(t.clone()); // Test update-doFinal with a direct bytebuffer and a byte array with // preset data sizes. - t = new GCMBufferTest("AES/GCM/NoPadding", + t = new AEADBufferTest("AES/GCM/NoPadding", List.of(dtype.DIRECT, dtype.BYTE)).differentBufferOnly(). - dataSegments(new int[] { 20, GCMBufferTest.REMAINDER }); + dataSegments(new int[] { 20, AEADBufferTest.REMAINDER }); t.clone().test(); offsetTests(t.clone()); // Test update-update-doFinal with a direct and heap bytebuffer and a // byte array with preset data sizes. - t = new GCMBufferTest("AES/GCM/NoPadding", + t = new AEADBufferTest("AES/GCM/NoPadding", List.of(dtype.DIRECT, dtype.BYTE, dtype.HEAP)). differentBufferOnly().dataSet(5). - dataSegments(new int[] { 5000, 1000, GCMBufferTest.REMAINDER }); + dataSegments(new int[] { 5000, 1000, AEADBufferTest.REMAINDER }); t.clone().test(); offsetTests(t.clone()); // Test update-update-doFinal with byte arrays, incrementing through // every data size combination for the Data set 0 - new GCMBufferTest("AES/GCM/NoPadding", + new AEADBufferTest("AES/GCM/NoPadding", List.of(dtype.BYTE, dtype.BYTE, dtype.BYTE)).incrementalSegments(). dataSet(0).test(); // Test update-update-doFinal with direct bytebuffers, incrementing through // every data size combination for the Data set 0 - new GCMBufferTest("AES/GCM/NoPadding", + new AEADBufferTest("AES/GCM/NoPadding", List.of(dtype.DIRECT, dtype.DIRECT, dtype.DIRECT)). incrementalSegments().dataSet(0).test(); - new GCMBufferTest("AES/GCM/NoPadding", + new AEADBufferTest("AES/GCM/NoPadding", + List.of(dtype.DIRECT, dtype.DIRECT, dtype.DIRECT)). + dataSegments(new int[] { 49, 0, 2 }).dataSet(0).test(); + + // **** CC20P1305 Tests + + // Test single byte array + new AEADBufferTest("ChaCha20-Poly1305", List.of(dtype.BYTE)).test(); + offsetTests(new AEADBufferTest("ChaCha20-Poly1305", List.of(dtype.BYTE))); + // Test update-doFinal with byte arrays + new AEADBufferTest("ChaCha20-Poly1305", List.of(dtype.BYTE, dtype.BYTE)).test(); + offsetTests(new AEADBufferTest("ChaCha20-Poly1305", List.of(dtype.BYTE, dtype.BYTE))); + // Test update-update-doFinal with byte arrays + new AEADBufferTest("ChaCha20-Poly1305", + List.of(dtype.BYTE, dtype.BYTE, dtype.BYTE)).test(); + offsetTests(new AEADBufferTest("ChaCha20-Poly1305", List.of(dtype.BYTE, dtype.BYTE, dtype.BYTE))); + + // Test single heap bytebuffer + new AEADBufferTest("ChaCha20-Poly1305", List.of(dtype.HEAP)).test(); + offsetTests(new AEADBufferTest("ChaCha20-Poly1305", List.of(dtype.HEAP))); + // Test update-doFinal with heap bytebuffer + new AEADBufferTest("ChaCha20-Poly1305", + List.of(dtype.HEAP, dtype.HEAP)).test(); + offsetTests(new AEADBufferTest("ChaCha20-Poly1305", List.of(dtype.HEAP, dtype.HEAP))); + // Test update-update-doFinal with heap bytebuffer + new AEADBufferTest("ChaCha20-Poly1305", + List.of(dtype.HEAP, dtype.HEAP, dtype.HEAP)).test(); + offsetTests(new AEADBufferTest("ChaCha20-Poly1305", List.of(dtype.HEAP, dtype.HEAP, dtype.HEAP))); + + // Test single direct bytebuffer + new AEADBufferTest("ChaCha20-Poly1305", List.of(dtype.DIRECT)).test(); + offsetTests(new AEADBufferTest("ChaCha20-Poly1305", List.of(dtype.DIRECT))); + // Test update-doFinal with direct bytebuffer + new AEADBufferTest("ChaCha20-Poly1305", + List.of(dtype.DIRECT, dtype.DIRECT)).test(); + offsetTests(new AEADBufferTest("ChaCha20-Poly1305", + List.of(dtype.DIRECT, dtype.DIRECT))); + // Test update-update-doFinal with direct bytebuffer + new AEADBufferTest("ChaCha20-Poly1305", + List.of(dtype.DIRECT, dtype.DIRECT, dtype.DIRECT)).test(); + offsetTests(new AEADBufferTest("ChaCha20-Poly1305", + List.of(dtype.DIRECT, dtype.DIRECT, dtype.DIRECT))); + + // Test update-update-doFinal with byte arrays and preset data sizes + t = new AEADBufferTest("ChaCha20-Poly1305", + List.of(dtype.BYTE, dtype.BYTE, dtype.BYTE)).dataSegments( + new int[] { 1, 1, AEADBufferTest.REMAINDER}); + t.clone().test(); + offsetTests(t.clone()); + + // Test update-doFinal with a byte array and a direct bytebuffer + t = new AEADBufferTest("ChaCha20-Poly1305", + List.of(dtype.BYTE, dtype.DIRECT)).differentBufferOnly(); + t.clone().test(); + offsetTests(t.clone()); + // Test update-doFinal with a byte array and heap and direct bytebuffer + t = new AEADBufferTest("ChaCha20-Poly1305", + List.of(dtype.BYTE, dtype.HEAP, dtype.DIRECT)).differentBufferOnly(); + t.clone().test(); + offsetTests(t.clone()); + + // Test update-doFinal with a direct bytebuffer and a byte array. + t = new AEADBufferTest("ChaCha20-Poly1305", + List.of(dtype.DIRECT, dtype.BYTE)).differentBufferOnly(); + t.clone().test(); + offsetTests(t.clone()); + + // Test update-doFinal with a direct bytebuffer and a byte array with + // preset data sizes. + t = new AEADBufferTest("ChaCha20-Poly1305", + List.of(dtype.DIRECT, dtype.BYTE)).differentBufferOnly(). + dataSegments(new int[] { 20, AEADBufferTest.REMAINDER }); + t.clone().test(); + offsetTests(t.clone()); + // Test update-update-doFinal with a direct and heap bytebuffer and a + // byte array with preset data sizes. + t = new AEADBufferTest("ChaCha20-Poly1305", + List.of(dtype.DIRECT, dtype.BYTE, dtype.HEAP)). + differentBufferOnly().dataSet(1). + dataSegments(new int[] { 5000, 1000, AEADBufferTest.REMAINDER }); + t.clone().test(); + offsetTests(t.clone()); + + // Test update-update-doFinal with byte arrays, incrementing through + // every data size combination for the Data set 0 + new AEADBufferTest("ChaCha20-Poly1305", + List.of(dtype.BYTE, dtype.BYTE, dtype.BYTE)).incrementalSegments(). + dataSet(0).test(); + // Test update-update-doFinal with direct bytebuffers, incrementing through + // every data size combination for the Data set 0 + new AEADBufferTest("ChaCha20-Poly1305", + List.of(dtype.DIRECT, dtype.DIRECT, dtype.DIRECT)). + incrementalSegments().dataSet(0).test(); + + new AEADBufferTest("ChaCha20-Poly1305", List.of(dtype.DIRECT, dtype.DIRECT, dtype.DIRECT)). dataSegments(new int[] { 49, 0, 2 }).dataSet(0).test(); } // Test data static void initTest() { + datamap.put("AES/GCM/NoPadding", List.of( // GCM KAT - new Data("AES", 0, + new Data(AES, 0, "141f1ce91989b07e7eb6ae1dbd81ea5e", "49451da24bd6074509d3cebc2c0394c972e6934b45a1d91f3ce1d3ca69e19" + "4aa1958a7c21b6f21d530ce6d2cc5256a3f846b6f9d2f38df0102c4791e5" + @@ -744,100 +869,32 @@ public class GCMBufferTest implements Cloneable { "240446b28dc088abd42b0fc687f208190ff24c0548", "dbb93bbb56d0439cd09f620a57687f5d"), // GCM KAT - new Data("AES", 1, "11754cd72aec309bf52f7687212e8957", + new Data(AES, 1, "11754cd72aec309bf52f7687212e8957", "3c819d9a9bed087615030b65", - (String)null, null, null, + new byte[0], null, null, "250327c674aaf477aef2675748cf6971"), - // GCM KAT - new Data("AES", 2, "272f16edb81a7abbea887357a58c1917", - "794ec588176c703d3d2a7a07", - (String)null, null, null, - "b6e6f197168f5049aeda32dafbdaeb"), - // zero'd test data - new Data("AES", 3, "272f16edb81a7abbea887357a58c1917", - "794ec588176c703d3d2a7a07", new byte[256], null, - "15b461672153270e8ba1e6789f7641c5411f3e642abda731b6086f535c216457" + - "e87305bc59a1ff1f7e1e0bbdf302b75549b136606c67d7e5f71277aeca4bc670" + - "07a98f78e0cfa002ed183e62f07893ad31fe67aad1bb37e15b957a14d145f14f" + - "7483d041f2c3612ad5033155984470bdfc64d18df73c2745d92f28461bb09832" + - "33524811321ba87d213692825815dd13f528dba601a3c319cac6be9b48686c23" + - "a0ce23d5062916ea8827bbb243f585e446131489e951354c8ab24661f625c02e" + - "15536c5bb602244e98993ff745f3e523399b2059f0e062d8933fad2366e7e147" + - "510a931282bb0e3f635efe7bf05b1dd715f95f5858261b00735224256b6b3e80", - "08b3593840d4ed005f5234ae062a5c"), - // Random test data - new Data("AES", 4, "272f16edb81a7abbea887357a58c1917", - "794ec588176c703d3d2a7a07", - new byte[2075], null, - "15b461672153270e8ba1e6789f7641c5411f3e642abda731b6086f535c216457" + - "e87305bc59a1ff1f7e1e0bbdf302b75549b136606c67d7e5f71277aeca4bc670" + - "07a98f78e0cfa002ed183e62f07893ad31fe67aad1bb37e15b957a14d145f14f" + - "7483d041f2c3612ad5033155984470bdfc64d18df73c2745d92f28461bb09832" + - "33524811321ba87d213692825815dd13f528dba601a3c319cac6be9b48686c23" + - "a0ce23d5062916ea8827bbb243f585e446131489e951354c8ab24661f625c02e" + - "15536c5bb602244e98993ff745f3e523399b2059f0e062d8933fad2366e7e147" + - "510a931282bb0e3f635efe7bf05b1dd715f95f5858261b00735224256b6b3e80" + - "7364cb53ff6d4e88f928cf67ac70da127718a8a35542efbae9dd7567c818a074" + - "9a0c74bd69014639f59768bc55056d1166ea5523e8c66f9d78d980beb8f0d83b" + - "a9e2c5544b94dc3a1a4b6f0f95f897b010150e89ebcacf0daee3c2793d6501a0" + - "b58b411de273dee987e8e8cf8bb29ef2e7f655b46b55fabf64c6a4295e0d080b" + - "6a570ace90eb0fe0f5b5d878bdd90eddaa1150e4d5a6505b350aac814fe99615" + - "317ecd0516a464c7904011ef5922409c0d65b1e43b69d7c3293a8f7d3e9fbee9" + - "eb91ec0007a7d6f72e64deb675d459c5ba07dcfd58d08e6820b100465e6e04f0" + - "663e310584a00d36d23699c1bffc6afa094c75184fc7cde7ad35909c0f49f2f3" + - "fe1e6d745ab628d74ea56b757047de57ce18b4b3c71e8af31a6fac16189cb0a3" + - "a97a1bea447042ce382fcf726560476d759c24d5c735525ea26a332c2094408e" + - "671c7deb81d5505bbfd178f866a6f3a011b3cfdbe089b4957a790688028dfdf7" + - "9a096b3853f9d0d6d3feef230c7f5f46ffbf7486ebdaca5804dc5bf9d202415e" + - "e0d67b365c2f92a17ea740807e4f0b198b42b54f15faa9dff2c7c35d2cf8d72e" + - "b8f8b18875a2e7b5c43d1e0aa5139c461e8153c7f632895aa46ffe2b134e6a0d" + - "dfbf6a336e709adfe951bd52c4dfc7b07a15fb3888fc35b7e758922f87a104c4" + - "563c5c7839cfe5a7edbdb97264a7c4ebc90367b10cbe09dbf2390767ad7afaa8" + - "8fb46b39d3f55f216d2104e5cf040bf3d39b758bea28e2dbce576c808d17a8eb" + - "e2fd183ef42a774e39119dff1f539efeb6ad15d889dfcb0d54d0d4d4cc03c8d9" + - "aa6c9ebd157f5e7170183298d6a30ada8792dcf793d931e2a1eafccbc63c11c0" + - "c5c5ed60837f30017d693ccb294df392a8066a0594a56954aea7b78a16e9a11f" + - "4a8bc2104070a7319f5fab0d2c4ccad8ec5cd8f47c839179bfd54a7bf225d502" + - "cd0a318752fe763e8c09eb88fa57fc5399ad1f797d0595c7b8afdd23f13603e9" + - "6802192bb51433b7723f4e512bd4f799feb94b458e7f9792f5f9bd6733828f70" + - "a6b7ffbbc0bb7575021f081ec2a0d37fecd7cda2daec9a3a9d9dfe1c8034cead" + - "e4b56b581cc82bd5b74b2b30817967d9da33850336f171a4c68e2438e03f4b11" + - "96da92f01b3b7aeab795180ccf40a4b090b1175a1fc0b67c95f93105c3aef00e" + - "13d76cc402539192274fee703730cd0d1c5635257719cc96cacdbad00c6255e2" + - "bd40c775b43ad09599e84f2c3205d75a6661ca3f151183be284b354ce21457d1" + - "3ba65b9b2cdb81874bd14469c2008b3ddec78f7225ecc710cc70de7912ca6a6d" + - "348168322ab59fdafcf5c833bfa0ad4046f4b6da90e9f263db7079af592eda07" + - "5bf16c6b1a8346da9c292a48bf660860a4fc89eaef40bc132779938eca294569" + - "787c740af2b5a8de7f5e10ac750d1e3d0ef3ed168ba408a676e10b8a20bd4be8" + - "3e8336b45e54481726d73e1bd19f165a98e242aca0d8387f2dd22d02d74e23db" + - "4cef9a523587413e0a44d7e3260019a34d3a6b38426ae9fa4655be338d721970" + - "cb9fe76c073f26f9303093a033022cd2c62b2790bce633ba9026a1c93b6535f1" + - "1882bf5880e511b9e1b0b7d8f23a993aae5fd275faac3a5b4ccaf7c06b0b266a" + - "ee970a1e3a4cd7a41094f516960630534e692545b25a347c30e3f328bba4825f" + - "ed754e5525d846131ecba7ca120a6aeabc7bab9f59c890c80b7e31f9bc741591" + - "55d292433ce9558e104102f2cc63ee267c1c8333e841522707ea6d595cb802b9" + - "61697da77bbc4cb404ea62570ab335ebffa2023730732ac5ddba1c3dbb5be408" + - "3c50aea462c1ffa166d7cc3db4b742b747e81b452db2363e91374dee8c6b40f0" + - "e7fbf50e60eaf5cc5649f6bb553aae772c185026ceb052af088c545330a1ffbf" + - "50615b8c7247c6cd386afd7440654f4e15bcfae0c45442ec814fe88433a9d616" + - "ee6cc3f163f0d3d325526d05f25d3b37ad5eeb3ca77248ad86c9042b16c65554" + - "aebb6ad3e17b981492b13f42c5a5dc088e991da303e5a273fdbb8601aece4267" + - "47b01f6cb972e6da1743a0d7866cf206e95f23c6f8e337c901b9cd34a9a1fbbe" + - "1694f2c26b00dfa4d02c0d54540163e798fbdc9c25f30d6406f5b4c13f7ed619" + - "34e350f4059c13aa5e973307a9e3058917cda96fdd082e9c629ccfb2a9f98d12" + - "5c6e4703a7b0f348f5cdeb63cef2133d1c6c1a087591e0a2bca29d09c6565e66" + - "e91042f83b0e74e60a5d57562c23e2fbcd6599c29d7c19e47cf625c2ce24bb8a" + - "13f8e54041498437eec2cedd1e3d8e57a051baa962c0a62d70264d99c5ee716d" + - "5c8b9078db08c8b2c5613f464198a7aff43f76c5b4612b46a4f1cd2a494386c5" + - "7fd28f3d199f0ba8d8e39116cc7db16ce6188205ee49a9dce3d4fa32ea394919" + - "f6e91ef58b84d00b99596b4306c2d9f432d917bb4ac73384c42ae12adb4920d8" + - "c33a816febcb299dcddf3ec7a8eb6e04cdc90891c6e145bd9fc5f41dc4061a46" + - "9feba38545b64ec8203f386ceef52785619e991d274ae80af7e54af535e0b011" + - "5effdf847472992875e09398457604d04e0bb965db692c0cdcf11a", - "687cc09c89298491deb51061d709af"), // Randomly generated data at the time of execution. - new Data("AES", 5, "11754cd72aec309bf52f7687212e8957", 12345) - ) - ); + new Data(AES, 5, "11754cd72aec309bf52f7687212e8957", + 16, 12345))); + + datamap.put("ChaCha20-Poly1305", List.of( + new Data("CC20", 0, + "808182838485868788898a8b8c8d8e8f909192939495969798999a9b9c9d9e9f", + "070000004041424344454647", + 1, + "4c616469657320616e642047656e746c656d656e206f662074686520636c6173" + + "73206f66202739393a204966204920636f756c64206f6666657220796f75206f" + + "6e6c79206f6e652074697020666f7220746865206675747572652c2073756e73" + + "637265656e20776f756c642062652069742e", + "50515253c0c1c2c3c4c5c6c7", + "d31a8d34648e60db7b86afbc53ef7ec2a4aded51296e08fea9e2b5a736ee62d6" + + "3dbea45e8ca9671282fafb69da92728b1a71de0a9e060b2905d6a5b67ecd3b36" + + "92ddbd7f2d778b8c9803aee328091b58fab324e4fad675945585808b4831d7bc" + + "3ff4def08e4b7a9de576d26586cec64b61161ae10b59", + "4f09e26a7e902ecbd0600691"), + // Randomly generated data at the time of execution. + new Data("CC20", 1, + "808182838485868788898a8b8c8d8e8f909192939495969798999a9b9c9d9e9f", + 12, 12345))); } } diff --git a/test/micro/org/openjdk/bench/javax/crypto/full/AESGCMBench.java b/test/micro/org/openjdk/bench/javax/crypto/full/AESGCMBench.java index 9e5d4d8ad03..e46f50678ef 100644 --- a/test/micro/org/openjdk/bench/javax/crypto/full/AESGCMBench.java +++ b/test/micro/org/openjdk/bench/javax/crypto/full/AESGCMBench.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2015, 2019, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2015, 2023, 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 @@ -22,107 +22,31 @@ */ package org.openjdk.bench.javax.crypto.full; -import org.openjdk.jmh.annotations.Benchmark; import org.openjdk.jmh.annotations.Param; import org.openjdk.jmh.annotations.Setup; -import javax.crypto.Cipher; +import java.security.spec.AlgorithmParameterSpec; import javax.crypto.spec.GCMParameterSpec; -import javax.crypto.spec.SecretKeySpec; /** - * This performance tests runs AES/GCM encryption and decryption using byte[] - * as input and output buffers for single and multi-part testing. - * - * This test rotates the IV and creates a new GCMParameterSpec for each encrypt - * benchmark operation + * This performance tests runs AES/GCM encryption and decryption + * using input and output byte[] buffers with single and multi-part testing. */ -public class AESGCMBench extends CryptoBase { +public class AESGCMBench extends BenchBase { @Param({"128"}) - private int keyLength; + int keyLength; - @Param({"1024", "1500", "4096", "16384"}) - private int dataSize; + public static final int IV_MODULO = 16; - byte[] encryptedData; - byte[] in, out; - private Cipher encryptCipher; - private Cipher decryptCipher; - SecretKeySpec ks; - GCMParameterSpec gcm_spec; - byte[] iv; - - private static final int IV_BUFFER_SIZE = 32; - private static final int IV_MODULO = IV_BUFFER_SIZE - 16; - int iv_index = 0; - int updateLen = 0; - - private int next_iv_index() { - int r = iv_index; + public AlgorithmParameterSpec getNewSpec() { iv_index = (iv_index + 1) % IV_MODULO; - return r; + return new GCMParameterSpec(128, iv, iv_index, IV_MODULO); } @Setup public void setup() throws Exception { - setupProvider(); - - // Setup key material - byte[] keystring = fillSecureRandom(new byte[keyLength / 8]); - ks = new SecretKeySpec(keystring, "AES"); - iv = fillSecureRandom(new byte[IV_BUFFER_SIZE]); - gcm_spec = new GCMParameterSpec(96, iv, next_iv_index(), 16); - - // Setup Cipher classes - encryptCipher = makeCipher(prov, "AES/GCM/NoPadding"); - encryptCipher.init(Cipher.ENCRYPT_MODE, ks, gcm_spec); - decryptCipher = makeCipher(prov, "AES/GCM/NoPadding"); - decryptCipher.init(Cipher.DECRYPT_MODE, ks, - encryptCipher.getParameters(). - getParameterSpec(GCMParameterSpec.class)); - - // Setup input/output buffers - in = fillRandom(new byte[dataSize]); - encryptedData = new byte[encryptCipher.getOutputSize(in.length)]; - out = new byte[encryptedData.length]; - encryptCipher.doFinal(in, 0, in.length, encryptedData, 0); - updateLen = in.length / 2; - - } - - @Benchmark - public void encrypt() throws Exception { - gcm_spec = new GCMParameterSpec(96, iv, next_iv_index(), 16); - encryptCipher.init(Cipher.ENCRYPT_MODE, ks, gcm_spec); - encryptCipher.doFinal(in, 0, in.length, out, 0); - } - - @Benchmark - public void encryptMultiPart() throws Exception { - gcm_spec = new GCMParameterSpec(96, iv, next_iv_index(), 16); - encryptCipher.init(Cipher.ENCRYPT_MODE, ks, gcm_spec); - int outOfs = encryptCipher.update(in, 0, updateLen, out, 0); - encryptCipher.doFinal(in, updateLen, in.length - updateLen, - out, outOfs); - } - - @Benchmark - public void decrypt() throws Exception { - decryptCipher.init(Cipher.DECRYPT_MODE, ks, - encryptCipher.getParameters(). - getParameterSpec(GCMParameterSpec.class)); - decryptCipher.doFinal(encryptedData, 0, encryptedData.length, out, 0); - } - - @Benchmark - public void decryptMultiPart() throws Exception { - decryptCipher.init(Cipher.DECRYPT_MODE, ks, - encryptCipher.getParameters(). - getParameterSpec(GCMParameterSpec.class)); - decryptCipher.update(encryptedData, 0, updateLen, out, 0); - decryptCipher.doFinal(encryptedData, updateLen, - encryptedData.length - updateLen, out, 0); + init("AES/GCM/NoPadding", keyLength); } } diff --git a/test/micro/org/openjdk/bench/javax/crypto/full/AESGCMByteBuffer.java b/test/micro/org/openjdk/bench/javax/crypto/full/AESGCMByteBuffer.java index 3e07c6b6910..ce5e7e8358c 100644 --- a/test/micro/org/openjdk/bench/javax/crypto/full/AESGCMByteBuffer.java +++ b/test/micro/org/openjdk/bench/javax/crypto/full/AESGCMByteBuffer.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2021, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2021, 2023, 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 @@ -22,140 +22,32 @@ */ package org.openjdk.bench.javax.crypto.full; -import org.openjdk.jmh.annotations.Benchmark; import org.openjdk.jmh.annotations.Param; import org.openjdk.jmh.annotations.Setup; -import javax.crypto.Cipher; +import java.security.spec.AlgorithmParameterSpec; import javax.crypto.spec.GCMParameterSpec; -import javax.crypto.spec.SecretKeySpec; -import java.nio.ByteBuffer; /** * This performance tests runs AES/GCM encryption and decryption using heap and * direct ByteBuffers as input and output buffers for single and multi-part * operations. - * - * This test rotates the IV and creates a new GCMParameterSpec for each encrypt - * benchmark operation */ -public class AESGCMByteBuffer extends CryptoBase { +public class AESGCMByteBuffer extends ByteBufferBase { @Param({"128"}) - private int keyLength; + int keyLength; - @Param({"1024", "1500", "4096", "16384"}) - private int dataSize; + public static final int IV_MODULO = 16; - @Param({"direct", "heap"}) - private String dataMethod; - - byte[] data; - ByteBuffer encryptedData; - ByteBuffer in, out; - private Cipher encryptCipher; - private Cipher decryptCipher; - SecretKeySpec ks; - GCMParameterSpec gcm_spec; - byte[] iv; - - private static final int IV_BUFFER_SIZE = 32; - private static final int IV_MODULO = IV_BUFFER_SIZE - 16; - int iv_index = 0; - int updateLen = 0; - - private int next_iv_index() { - int r = iv_index; + public AlgorithmParameterSpec getNewSpec() { iv_index = (iv_index + 1) % IV_MODULO; - return r; + return new GCMParameterSpec(128, iv, iv_index, IV_MODULO); } @Setup public void setup() throws Exception { - setupProvider(); - - // Setup key material - byte[] keystring = fillSecureRandom(new byte[keyLength / 8]); - ks = new SecretKeySpec(keystring, "AES"); - iv = fillSecureRandom(new byte[IV_BUFFER_SIZE]); - gcm_spec = new GCMParameterSpec(96, iv, next_iv_index(), 16); - - // Setup Cipher classes - encryptCipher = makeCipher(prov, "AES/GCM/NoPadding"); - encryptCipher.init(Cipher.ENCRYPT_MODE, ks, gcm_spec); - decryptCipher = makeCipher(prov, "AES/GCM/NoPadding"); - decryptCipher.init(Cipher.DECRYPT_MODE, ks, - encryptCipher.getParameters(). - getParameterSpec(GCMParameterSpec.class)); - - // Setup input/output buffers - data = fillRandom(new byte[dataSize]); - if (dataMethod.equalsIgnoreCase("direct")) { - in = ByteBuffer.allocateDirect(data.length); - in.put(data); - in.flip(); - encryptedData = ByteBuffer.allocateDirect( - encryptCipher.getOutputSize(data.length)); - out = ByteBuffer.allocateDirect(encryptedData.capacity()); - } else if (dataMethod.equalsIgnoreCase("heap")) { - in = ByteBuffer.wrap(data); - encryptedData = ByteBuffer.allocate( - encryptCipher.getOutputSize(data.length)); - out = ByteBuffer.allocate(encryptedData.capacity()); - } - - encryptCipher.doFinal(in, encryptedData); - encryptedData.flip(); - in.flip(); - updateLen = in.remaining() / 2; + init("AES/GCM/NoPadding", keyLength); } - - @Benchmark - public void encrypt() throws Exception { - gcm_spec = new GCMParameterSpec(96, iv, next_iv_index(), 16); - encryptCipher.init(Cipher.ENCRYPT_MODE, ks, gcm_spec); - encryptCipher.doFinal(in, out); - out.flip(); - in.flip(); - } - - @Benchmark - public void encryptMultiPart() throws Exception { - gcm_spec = new GCMParameterSpec(96, iv, next_iv_index(), 16); - encryptCipher.init(Cipher.ENCRYPT_MODE, ks, gcm_spec); - in.limit(updateLen); - encryptCipher.update(in, out); - in.limit(in.capacity()); - encryptCipher.doFinal(in, out); - out.flip(); - in.flip(); - } - - @Benchmark - public void decrypt() throws Exception { - decryptCipher.init(Cipher.DECRYPT_MODE, ks, - encryptCipher.getParameters(). - getParameterSpec(GCMParameterSpec.class)); - decryptCipher.doFinal(encryptedData, out); - encryptedData.flip(); - out.flip(); - } - - @Benchmark - public void decryptMultiPart() throws Exception { - decryptCipher.init(Cipher.DECRYPT_MODE, ks, - encryptCipher.getParameters(). - getParameterSpec(GCMParameterSpec.class)); - - int len = encryptedData.remaining(); - encryptedData.limit(updateLen); - decryptCipher.update(encryptedData, out); - encryptedData.limit(len); - - decryptCipher.doFinal(encryptedData, out); - encryptedData.flip(); - out.flip(); - } - } diff --git a/test/micro/org/openjdk/bench/javax/crypto/full/BenchBase.java b/test/micro/org/openjdk/bench/javax/crypto/full/BenchBase.java new file mode 100644 index 00000000000..0c5df20d9cb --- /dev/null +++ b/test/micro/org/openjdk/bench/javax/crypto/full/BenchBase.java @@ -0,0 +1,131 @@ +/* + * Copyright (c) 2023, 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. + */ +package org.openjdk.bench.javax.crypto.full; + +import org.openjdk.jmh.annotations.Benchmark; +import org.openjdk.jmh.annotations.Param; +import org.openjdk.jmh.annotations.Setup; + +import java.security.spec.AlgorithmParameterSpec; +import javax.crypto.Cipher; +import javax.crypto.spec.SecretKeySpec; + +/** + * This is the common code for the AES/GCM and ChaCha20-Poly1305 performance + * tests. Encryption and decryption use byte[] as input and output buffers for + * single and multi-part testing. + * + * The IV rotates through a set buffer and creates a new AlgorithmParameterSpec + * for each encrypt benchmark operation. + */ + +public abstract class BenchBase extends CryptoBase { + // Defined by the test + String algorithm; + int keyLength = 256; + + // Default data sizes for full tests + @Param({"1024", "1500", "4096", "16384"}) + int dataSize; + + static final int IV_BUFFER_SIZE = 36; + public byte[] iv; + public int iv_index = 0; + private int updateLen = 0; + + private Cipher encryptCipher, decryptCipher; + private byte[] encryptedData, in, out; + private SecretKeySpec ks; + // Used for decryption to avoid repeated getParameter() calls + private AlgorithmParameterSpec spec; + + abstract AlgorithmParameterSpec getNewSpec(); + + // Configure setup with particular test parameters + public void init(String algorithm, int keyLength) throws Exception { + this.algorithm = algorithm; + this.keyLength = keyLength; + init(); + } + + // Configure setup with particular test parameters + public void init(String algorithm, int keyLength, int dataSize) + throws Exception { + this.dataSize = dataSize; + init(algorithm, keyLength); + } + + // Initalize test setup + private void init() throws Exception { + setupProvider(); + + // Setup key material + iv = fillSecureRandom(new byte[IV_BUFFER_SIZE]); + spec = getNewSpec(); + // CC20 doesn't care about the algorithm name on the key, but AES does. + ks = new SecretKeySpec(fillSecureRandom(new byte[keyLength / 8]), + "AES"); + + // Setup Cipher classes + encryptCipher = makeCipher(prov, algorithm); + encryptCipher.init(Cipher.ENCRYPT_MODE, ks, spec); + decryptCipher = makeCipher(prov, algorithm); + decryptCipher.init(Cipher.DECRYPT_MODE, ks, spec); + + // Setup input/output buffers + in = fillRandom(new byte[dataSize]); + encryptedData = new byte[encryptCipher.getOutputSize(in.length)]; + out = new byte[encryptedData.length]; + encryptCipher.doFinal(in, 0, in.length, encryptedData, 0); + updateLen = in.length / 2; + + } + + @Benchmark + public void encrypt() throws Exception { + encryptCipher.init(Cipher.ENCRYPT_MODE, ks, getNewSpec()); + encryptCipher.doFinal(in, 0, in.length, out, 0); + } + + @Benchmark + public void encryptMultiPart() throws Exception { + encryptCipher.init(Cipher.ENCRYPT_MODE, ks, getNewSpec()); + int outOfs = encryptCipher.update(in, 0, updateLen, out, 0); + encryptCipher.doFinal(in, updateLen, in.length - updateLen, + out, outOfs); + } + + @Benchmark + public void decrypt() throws Exception { + decryptCipher.init(Cipher.DECRYPT_MODE, ks, spec); + decryptCipher.doFinal(encryptedData, 0, encryptedData.length, out, 0); + } + + @Benchmark + public void decryptMultiPart() throws Exception { + decryptCipher.init(Cipher.DECRYPT_MODE, ks, spec); + decryptCipher.update(encryptedData, 0, updateLen, out, 0); + decryptCipher.doFinal(encryptedData, updateLen, + encryptedData.length - updateLen, out, 0); + } +} diff --git a/test/micro/org/openjdk/bench/javax/crypto/full/ByteBufferBase.java b/test/micro/org/openjdk/bench/javax/crypto/full/ByteBufferBase.java new file mode 100644 index 00000000000..79fa2bcbab3 --- /dev/null +++ b/test/micro/org/openjdk/bench/javax/crypto/full/ByteBufferBase.java @@ -0,0 +1,160 @@ +/* + * Copyright (c) 2023, 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. + */ +package org.openjdk.bench.javax.crypto.full; + +import org.openjdk.jmh.annotations.Benchmark; +import org.openjdk.jmh.annotations.Param; +import org.openjdk.jmh.annotations.Setup; + +import java.security.spec.AlgorithmParameterSpec; +import javax.crypto.Cipher; +import javax.crypto.spec.SecretKeySpec; +import java.nio.ByteBuffer; + +/** + * This is the common code for the AES/GCM and ChaCha20-Poly1305 performance + * tests. Encryption and decryption are run with input and output + * ByteBuffers, direct and heap, for single and multi-part testing. + * + * The IV rotates through a set buffer and creates a new AlgorithmParameterSpec + * for each encrypt benchmark operation. + */ + +public abstract class ByteBufferBase extends CryptoBase { + // Defined by the test + String algorithm; + int keyLength = 256; + + @Param({"1024", "1500", "4096", "16384"}) + int dataSize; + + @Param({"direct", "heap"}) + String dataMethod; + + static final int IV_BUFFER_SIZE = 36; + public byte[] iv; + public int iv_index = 0; + private int updateLen = 0; + + private Cipher encryptCipher, decryptCipher; + private ByteBuffer encryptedData, in, out; + private SecretKeySpec ks; + // Used for decryption to avoid repeated getParameter() calls + private AlgorithmParameterSpec spec; + + abstract AlgorithmParameterSpec getNewSpec(); + + // Configure setup with particular test parameters + public void init(String algorithm, int keyLength) throws Exception { + this.algorithm = algorithm; + this.keyLength = keyLength; + init(); + } + + // Configure setup with particular test parameters + public void init(String algorithm, int keyLength, int dataSize) + throws Exception { + this.dataSize = dataSize; + init(algorithm, keyLength); + } + + // Initalize test setup + private void init() throws Exception { + setupProvider(); + + // Setup key material + iv = fillSecureRandom(new byte[IV_BUFFER_SIZE]); + spec = getNewSpec(); + // CC20 doesn't care about the algorithm name on the key, but AES does. + ks = new SecretKeySpec(fillSecureRandom(new byte[keyLength / 8]), + "AES"); + + // Setup Cipher classes + encryptCipher = makeCipher(prov, algorithm); + encryptCipher.init(Cipher.ENCRYPT_MODE, ks, spec); + decryptCipher = makeCipher(prov, algorithm); + decryptCipher.init(Cipher.DECRYPT_MODE, ks, spec); + + // Setup input/output buffers + byte[] data = fillRandom(new byte[dataSize]); + if (dataMethod.equalsIgnoreCase("direct")) { + in = ByteBuffer.allocateDirect(data.length); + in.put(data); + in.flip(); + encryptedData = ByteBuffer.allocateDirect( + encryptCipher.getOutputSize(data.length)); + out = ByteBuffer.allocateDirect(encryptedData.capacity()); + } else if (dataMethod.equalsIgnoreCase("heap")) { + in = ByteBuffer.wrap(data); + encryptedData = ByteBuffer.allocate( + encryptCipher.getOutputSize(data.length)); + out = ByteBuffer.allocate(encryptedData.capacity()); + } + + encryptCipher.doFinal(in, encryptedData); + encryptedData.flip(); + in.flip(); + updateLen = in.remaining() / 2; + } + + @Benchmark + public void encrypt() throws Exception { + encryptCipher.init(Cipher.ENCRYPT_MODE, ks, getNewSpec()); + encryptCipher.doFinal(in, out); + out.flip(); + in.flip(); + } + + @Benchmark + public void encryptMultiPart() throws Exception { + encryptCipher.init(Cipher.ENCRYPT_MODE, ks, getNewSpec()); + in.limit(updateLen); + encryptCipher.update(in, out); + in.limit(in.capacity()); + encryptCipher.doFinal(in, out); + out.flip(); + in.flip(); + } + + @Benchmark + public void decrypt() throws Exception { + decryptCipher.init(Cipher.DECRYPT_MODE, ks, spec); + decryptCipher.doFinal(encryptedData, out); + encryptedData.flip(); + out.flip(); + } + + @Benchmark + public void decryptMultiPart() throws Exception { + decryptCipher.init(Cipher.DECRYPT_MODE, ks, spec); + + int len = encryptedData.remaining(); + encryptedData.limit(updateLen); + decryptCipher.update(encryptedData, out); + encryptedData.limit(len); + + decryptCipher.doFinal(encryptedData, out); + encryptedData.flip(); + out.flip(); + } +} diff --git a/test/micro/org/openjdk/bench/javax/crypto/full/CC20P1305Bench.java b/test/micro/org/openjdk/bench/javax/crypto/full/CC20P1305Bench.java new file mode 100644 index 00000000000..f6bc5c1052d --- /dev/null +++ b/test/micro/org/openjdk/bench/javax/crypto/full/CC20P1305Bench.java @@ -0,0 +1,49 @@ +/* + * Copyright (c) 2023, 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. + */ +package org.openjdk.bench.javax.crypto.full; + +import org.openjdk.jmh.annotations.Param; +import org.openjdk.jmh.annotations.Setup; + +import java.security.spec.AlgorithmParameterSpec; +import javax.crypto.spec.IvParameterSpec; + +/** + * This performance tests runs ChaCha20-Poly1305 encryption and decryption + * using input and output byte[] buffers with single and multi-part testing. + */ + +public class CC20P1305Bench extends BenchBase { + + public static final int IV_MODULO = 12; + + public AlgorithmParameterSpec getNewSpec() { + iv_index = (iv_index + 1) % IV_MODULO; + return new IvParameterSpec(iv, iv_index, IV_MODULO); + } + + @Setup + public void setup() throws Exception { + init("ChaCha20-Poly1305/None/NoPadding", keyLength); + } +} diff --git a/test/micro/org/openjdk/bench/javax/crypto/full/CC20P1305ByteBuffer.java b/test/micro/org/openjdk/bench/javax/crypto/full/CC20P1305ByteBuffer.java new file mode 100644 index 00000000000..326e2a6766d --- /dev/null +++ b/test/micro/org/openjdk/bench/javax/crypto/full/CC20P1305ByteBuffer.java @@ -0,0 +1,50 @@ +/* + * Copyright (c) 2023, 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. + */ +package org.openjdk.bench.javax.crypto.full; + +import org.openjdk.jmh.annotations.Param; +import org.openjdk.jmh.annotations.Setup; + +import java.security.spec.AlgorithmParameterSpec; +import javax.crypto.spec.IvParameterSpec; + +/** + * This performance tests runs ChaCha20-Poly1305 encryption and decryption + * using heap and direct ByteBuffers for input and output buffers with single + * and multi-part operations. + */ + +public class CC20P1305ByteBuffer extends ByteBufferBase { + + public static final int IV_MODULO = 12; + + public AlgorithmParameterSpec getNewSpec() { + iv_index = (iv_index + 1) % IV_MODULO; + return new IvParameterSpec(iv, iv_index, IV_MODULO); + } + + @Setup + public void setup() throws Exception { + init("ChaCha20-Poly1305/None/NoPadding", keyLength); + } +} diff --git a/test/micro/org/openjdk/bench/javax/crypto/small/AESGCMBench.java b/test/micro/org/openjdk/bench/javax/crypto/small/AESGCMBench.java index cfdc53ac704..49d64c7ca4a 100644 --- a/test/micro/org/openjdk/bench/javax/crypto/small/AESGCMBench.java +++ b/test/micro/org/openjdk/bench/javax/crypto/small/AESGCMBench.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2015, 2021, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2015, 2023, 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 @@ -23,14 +23,28 @@ package org.openjdk.bench.javax.crypto.small; import org.openjdk.jmh.annotations.Param; +import org.openjdk.jmh.annotations.Setup; + +import java.security.spec.AlgorithmParameterSpec; +import javax.crypto.spec.GCMParameterSpec; + +/** + * This small performance tests runs AES/GCM encryption and decryption + * using input and output byte[] buffers with single and multi-part testing. + * Only 1024 plaintext data length is tested. + */ public class AESGCMBench extends org.openjdk.bench.javax.crypto.full.AESGCMBench { @Param({"128"}) - private int keyLength; + int keyLength; @Param({"1024"}) - private int dataSize; + int dataSize; + @Setup + public void setup() throws Exception { + init("AES/GCM/NoPadding", keyLength, dataSize); + } } diff --git a/test/micro/org/openjdk/bench/javax/crypto/small/AESGCMByteBuffer.java b/test/micro/org/openjdk/bench/javax/crypto/small/AESGCMByteBuffer.java index 19fdab85bbb..baa41cd4ec8 100644 --- a/test/micro/org/openjdk/bench/javax/crypto/small/AESGCMByteBuffer.java +++ b/test/micro/org/openjdk/bench/javax/crypto/small/AESGCMByteBuffer.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2021, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2021, 2023, 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 @@ -23,14 +23,28 @@ package org.openjdk.bench.javax.crypto.small; import org.openjdk.jmh.annotations.Param; +import org.openjdk.jmh.annotations.Setup; + +import java.security.spec.AlgorithmParameterSpec; +import javax.crypto.spec.GCMParameterSpec; + +/** + * This small performance tests runs AES/GCM encryption and decryption + * using heap and direct ByteBuffers for input and output buffers with single + * and multi-part operations. Only 1024 plaintext data length is tested. + */ public class AESGCMByteBuffer extends org.openjdk.bench.javax.crypto.full.AESGCMByteBuffer { @Param({"128"}) - private int keyLength; + int keyLength; @Param({"1024"}) - private int dataSize; + int dataSize; + @Setup + public void setup() throws Exception { + init("AES/GCM/NoPadding", keyLength, dataSize); + } } \ No newline at end of file diff --git a/test/micro/org/openjdk/bench/javax/crypto/small/CC20P1305Bench.java b/test/micro/org/openjdk/bench/javax/crypto/small/CC20P1305Bench.java new file mode 100644 index 00000000000..b8b3d45fbc1 --- /dev/null +++ b/test/micro/org/openjdk/bench/javax/crypto/small/CC20P1305Bench.java @@ -0,0 +1,48 @@ +/* + * Copyright (c) 2023, 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. + */ +package org.openjdk.bench.javax.crypto.small; + +import org.openjdk.jmh.annotations.Param; +import org.openjdk.jmh.annotations.Setup; + +import java.security.spec.AlgorithmParameterSpec; +import javax.crypto.spec.IvParameterSpec; + +/** + * This small performance tests runs ChaCha20-Poly1305 encryption and decryption + * using input and output byte[] buffers with single and multi-part testing. + * Only 1024 plaintext data length is tested. + */ + +public class CC20P1305Bench extends + org.openjdk.bench.javax.crypto.full.CC20P1305Bench { + + @Param({"1024"}) + int dataSize; + + @Setup + public void setup() throws Exception { + init("ChaCha20-Poly1305/None/NoPadding", 256, dataSize); + } + +} diff --git a/test/micro/org/openjdk/bench/javax/crypto/small/CC20P1305ByteBuffer.java b/test/micro/org/openjdk/bench/javax/crypto/small/CC20P1305ByteBuffer.java new file mode 100644 index 00000000000..827b09c2f36 --- /dev/null +++ b/test/micro/org/openjdk/bench/javax/crypto/small/CC20P1305ByteBuffer.java @@ -0,0 +1,48 @@ +/* + * Copyright (c) 2023, 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. + */ +package org.openjdk.bench.javax.crypto.small; + +import org.openjdk.jmh.annotations.Param; +import org.openjdk.jmh.annotations.Setup; + +import java.security.spec.AlgorithmParameterSpec; +import javax.crypto.spec.IvParameterSpec; + +/** + * This small performance tests runs ChaCha20-Poly1305 encryption and decryption + * using heap and direct ByteBuffers for input and output buffers with single + * and multi-part operations. Only 1024 plaintext data length is tested. + */ + +public class CC20P1305ByteBuffer extends + org.openjdk.bench.javax.crypto.full.CC20P1305ByteBuffer { + + @Param({"1024"}) + int dataSize; + + @Setup + public void setup() throws Exception { + init("ChaCha20-Poly1305/None/NoPadding", 256, dataSize); + } + +}