mirror of
https://github.com/openjdk/jdk.git
synced 2025-08-26 22:34:27 +02:00
8318756: Create better internal buffer for AEADs
Reviewed-by: djelinski
This commit is contained in:
parent
a9cb120d03
commit
dc9c77bebe
14 changed files with 1178 additions and 450 deletions
|
@ -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 {
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue