13
0
geforkt von Mirrors/Velocity

Make AES crypto operations use one buffer

All AES implementations being used are 'copy safe', where the source and
destination arrays may be the same. Lets save ourself a copy and reap
the performance wins!
Dieser Commit ist enthalten in:
Joe Hirschfeld 2019-10-19 19:20:18 -07:00
Ursprung bd35c6835e
Commit a16684564b
6 geänderte Dateien mit 29 neuen und 82 gelöschten Zeilen

Datei anzeigen

@ -35,50 +35,24 @@ public class JavaVelocityCipher implements VelocityCipher {
}
@Override
public void process(ByteBuf source, ByteBuf destination) throws ShortBufferException {
public void process(ByteBuf source) {
ensureNotDisposed();
int inBytes = source.readableBytes();
byte[] asBytes = ByteBufUtil.getBytes(source);
int outputSize = cipher.getOutputSize(inBytes);
byte[] outBuf = new byte[outputSize];
cipher.update(asBytes, 0, inBytes, outBuf);
destination.writeBytes(outBuf);
}
@Override
public ByteBuf process(ChannelHandlerContext ctx, ByteBuf source) throws ShortBufferException {
ensureNotDisposed();
int inBytes = source.readableBytes();
ByteBuf asHeapBuf = toHeap(source);
ByteBuf out = ctx.alloc().heapBuffer(cipher.getOutputSize(inBytes));
try {
out.writerIndex(
cipher.update(asHeapBuf.array(), asHeapBuf.arrayOffset() + asHeapBuf.readerIndex(),
inBytes, out.array(), out.arrayOffset() + out.writerIndex()));
return out;
} catch (ShortBufferException e) {
out.release();
throw e;
} finally {
asHeapBuf.release();
cipher.update(asBytes, 0, inBytes, asBytes);
} catch (ShortBufferException ex) {
/* This _really_ shouldn't happen - AES CFB8 will work in place.
If you run into this, that means that for whatever reason the Java Runtime has determined
that the output buffer needs more bytes than the input buffer. When we are working with
AES-CFB8, the output size is equal to the input size. See the problem? */
throw new AssertionError("Cipher update did not operate in place and requested a larger "
+ "buffer than the source buffer");
}
}
private static ByteBuf toHeap(ByteBuf src) {
if (src.hasArray()) {
return src.retain();
}
// Copy into a temporary heap buffer. We could use a local buffer, but Netty pools all buffers,
// so we'd lose more than we gain.
ByteBuf asHeapBuf = src.alloc().heapBuffer(src.readableBytes());
asHeapBuf.writeBytes(src);
return asHeapBuf;
}
@Override
public void dispose() {
disposed = true;

Datei anzeigen

@ -33,40 +33,14 @@ public class NativeVelocityCipher implements VelocityCipher {
}
@Override
public void process(ByteBuf source, ByteBuf destination) throws ShortBufferException {
public void process(ByteBuf source) {
ensureNotDisposed();
source.memoryAddress();
destination.memoryAddress();
// The exact amount we read in is also the amount we write out.
long base = source.memoryAddress() + source.readerIndex();
int len = source.readableBytes();
destination.ensureWritable(len);
impl.process(ctx, source.memoryAddress() + source.readerIndex(), len,
destination.memoryAddress() + destination.writerIndex(), encrypt);
source.skipBytes(len);
destination.writerIndex(destination.writerIndex() + len);
}
@Override
public ByteBuf process(ChannelHandlerContext ctx, ByteBuf source) throws ShortBufferException {
ensureNotDisposed();
source.memoryAddress(); // sanity check
int len = source.readableBytes();
ByteBuf out = ctx.alloc().directBuffer(len);
try {
impl.process(this.ctx, source.memoryAddress() + source.readerIndex(), len,
out.memoryAddress(), encrypt);
source.skipBytes(len);
out.writerIndex(len);
return out;
} catch (Exception e) {
out.release();
throw e;
}
impl.process(ctx, base, len, base, encrypt);
}
@Override

Datei anzeigen

@ -7,8 +7,5 @@ import io.netty.channel.ChannelHandlerContext;
import javax.crypto.ShortBufferException;
public interface VelocityCipher extends Disposable, Native {
void process(ByteBuf source, ByteBuf destination) throws ShortBufferException;
ByteBuf process(ChannelHandlerContext ctx, ByteBuf source) throws ShortBufferException;
void process(ByteBuf source);
}

Datei anzeigen

@ -56,20 +56,18 @@ class VelocityCipherTest {
VelocityCipher encrypt = factory.forEncryption(new SecretKeySpec(AES_KEY, "AES"));
ByteBuf source = bufSupplier.get();
ByteBuf dest = bufSupplier.get();
ByteBuf decryptionBuf = bufSupplier.get();
source.writeBytes(TEST_DATA);
ByteBuf workingBuf = source.copy();
try {
encrypt.process(source, dest);
decrypt.process(dest, decryptionBuf);
source.readerIndex(0);
assertTrue(ByteBufUtil.equals(source, decryptionBuf));
encrypt.process(workingBuf);
decrypt.process(workingBuf);
assertTrue(ByteBufUtil.equals(source, workingBuf));
} finally {
source.release();
dest.release();
decryptionBuf.release();
workingBuf.release();
decrypt.dispose();
encrypt.dispose();
}

Datei anzeigen

@ -20,9 +20,11 @@ public class MinecraftCipherDecoder extends ByteToMessageDecoder {
protected void decode(ChannelHandlerContext ctx, ByteBuf in, List<Object> out) throws Exception {
ByteBuf compatible = MoreByteBufUtils.ensureCompatible(ctx.alloc(), cipher, in);
try {
out.add(cipher.process(ctx, compatible));
} finally {
compatible.release();
cipher.process(compatible);
out.add(compatible);
} catch (Exception e) {
compatible.release(); // compatible will never be used if we throw an exception
throw e;
}
}

Datei anzeigen

@ -20,9 +20,11 @@ public class MinecraftCipherEncoder extends MessageToMessageEncoder<ByteBuf> {
protected void encode(ChannelHandlerContext ctx, ByteBuf msg, List<Object> out) throws Exception {
ByteBuf compatible = MoreByteBufUtils.ensureCompatible(ctx.alloc(), cipher, msg);
try {
out.add(cipher.process(ctx, compatible));
} finally {
compatible.release();
cipher.process(compatible);
out.add(compatible);
} catch (Exception e) {
compatible.release(); // compatible will never be used if we throw an exception
throw e;
}
}