From 94ee61cd35de95e63819cd64055d4914d02ef300 Mon Sep 17 00:00:00 2001 From: md_5 Date: Tue, 19 Nov 2013 07:16:06 +1100 Subject: [PATCH] Native cipher, with more smoke tests! --- native/compile-native.sh | 3 + native/nb-configuration.xml | 31 +++++ native/pom.xml | 29 +++++ native/src/main/c/NativeCipherImpl.c | 56 +++++++++ .../main/c/net_md_5_bungee_NativeCipherImpl.h | 37 ++++++ .../java/net/md_5/bungee/BungeeCipher.java | 21 ++++ .../java/net/md_5/bungee/FallbackCipher.java | 90 ++++++++------ .../java/net/md_5/bungee/NativeCipher.java | 113 ++++++++++++++++++ .../net/md_5/bungee/NativeCipherImpl.java | 32 +++++ native/src/main/resources/native-cipher.so | Bin 0 -> 8277 bytes .../net/md_5/bungee/NativeCipherTest.java | 74 ++++++++++++ pom.xml | 1 + proxy/pom.xml | 6 + .../main/java/net/md_5/bungee/BungeeCord.java | 8 ++ .../java/net/md_5/bungee/EncryptionUtil.java | 17 ++- .../bungee/connection/InitialHandler.java | 14 +-- .../netty/{ => cipher}/CipherDecoder.java | 19 +-- .../netty/{ => cipher}/CipherEncoder.java | 19 +-- 18 files changed, 501 insertions(+), 69 deletions(-) create mode 100755 native/compile-native.sh create mode 100644 native/nb-configuration.xml create mode 100644 native/pom.xml create mode 100644 native/src/main/c/NativeCipherImpl.c create mode 100644 native/src/main/c/net_md_5_bungee_NativeCipherImpl.h create mode 100644 native/src/main/java/net/md_5/bungee/BungeeCipher.java rename proxy/src/main/java/net/md_5/bungee/netty/CipherBase.java => native/src/main/java/net/md_5/bungee/FallbackCipher.java (65%) create mode 100644 native/src/main/java/net/md_5/bungee/NativeCipher.java create mode 100644 native/src/main/java/net/md_5/bungee/NativeCipherImpl.java create mode 100755 native/src/main/resources/native-cipher.so create mode 100644 native/src/test/java/net/md_5/bungee/NativeCipherTest.java rename proxy/src/main/java/net/md_5/bungee/netty/{ => cipher}/CipherDecoder.java (59%) rename proxy/src/main/java/net/md_5/bungee/netty/{ => cipher}/CipherEncoder.java (56%) diff --git a/native/compile-native.sh b/native/compile-native.sh new file mode 100755 index 000000000..99ba41d2b --- /dev/null +++ b/native/compile-native.sh @@ -0,0 +1,3 @@ +#!/bin/sh + +gcc -shared -fPIC -O3 -Werror -I/usr/lib/jvm/default-java/include/ src/main/c/NativeCipherImpl.c -o src/main/resources/native-cipher.so -lcrypto diff --git a/native/nb-configuration.xml b/native/nb-configuration.xml new file mode 100644 index 000000000..7e4659240 --- /dev/null +++ b/native/nb-configuration.xml @@ -0,0 +1,31 @@ + + + + + + project + NEW_LINE + NEW_LINE + NEW_LINE + true + true + true + true + true + true + true + true + true + true + + diff --git a/native/pom.xml b/native/pom.xml new file mode 100644 index 000000000..c6ec0fc52 --- /dev/null +++ b/native/pom.xml @@ -0,0 +1,29 @@ + + + 4.0.0 + + + net.md-5 + bungeecord-parent + 1.7-SNAPSHOT + ../pom.xml + + + net.md-5 + bungeecord-native + 1.7-SNAPSHOT + jar + + BungeeCord-Native + Optional native code to speed up and enhance BungeeCord functionality. + + + + io.netty + netty-transport + ${netty.version} + compile + + + diff --git a/native/src/main/c/NativeCipherImpl.c b/native/src/main/c/NativeCipherImpl.c new file mode 100644 index 000000000..41884025e --- /dev/null +++ b/native/src/main/c/NativeCipherImpl.c @@ -0,0 +1,56 @@ +#include "net_md_5_bungee_NativeCipherImpl.h" +#include +#include +#include +#include +#define BYTE unsigned char + +jlong Java_net_md_15_bungee_NativeCipherImpl_init +(JNIEnv* env, jobject obj, jbyteArray key) +{ + AES_KEY *aes_key = malloc(sizeof(AES_KEY)); + + jboolean isKeyCopy; + BYTE *key_bytes = (*env)->GetByteArrayElements(env, key, &isKeyCopy); + int key_length = (*env)->GetArrayLength(env, key) * 8; // in bits + + AES_set_encrypt_key(key_bytes, key_length, aes_key); + + if (isKeyCopy) { + (*env)->ReleaseByteArrayElements(env, key, (jbyte*)key_bytes, JNI_ABORT); + } + return (long) aes_key; +} +void Java_net_md_15_bungee_NativeCipherImpl_free +(JNIEnv* env, jobject obj, jlong key) +{ + free((AES_KEY*)key); +} +void Java_net_md_15_bungee_NativeCipherImpl_cipher +(JNIEnv* env, jobject obj, jboolean forEncryption, jlong key, jbyteArray iv, jlong in, jlong out, jint length) +{ + AES_KEY *aes_key = (AES_KEY*)key; + + size_t buffer_length = (size_t) length; + + BYTE *input = (BYTE*) in; + BYTE *output = (BYTE*) out; + + jboolean isCopy; + BYTE *iv_bytes = (*env)->GetByteArrayElements(env, iv, &isCopy); + + AES_cfb8_encrypt( + input, // input buffer + output, // output buffer + buffer_length, // readable bytes + aes_key, // encryption key + iv_bytes, // IV + NULL, // not needed + forEncryption ? AES_ENCRYPT : AES_DECRYPT // encryption mode + ); + + // IV has changed, let's copy it back + if (isCopy) { + (*env)->ReleaseByteArrayElements(env, iv, (jbyte*)iv_bytes, 0); + } +} diff --git a/native/src/main/c/net_md_5_bungee_NativeCipherImpl.h b/native/src/main/c/net_md_5_bungee_NativeCipherImpl.h new file mode 100644 index 000000000..87e30b034 --- /dev/null +++ b/native/src/main/c/net_md_5_bungee_NativeCipherImpl.h @@ -0,0 +1,37 @@ +/* DO NOT EDIT THIS FILE - it is machine generated */ +#include +/* Header for class net_md_5_bungee_NativeCipherImpl */ + +#ifndef _Included_net_md_5_bungee_NativeCipherImpl +#define _Included_net_md_5_bungee_NativeCipherImpl +#ifdef __cplusplus +extern "C" { +#endif +/* + * Class: net_md_5_bungee_NativeCipherImpl + * Method: init + * Signature: ([B)J + */ +JNIEXPORT jlong JNICALL Java_net_md_15_bungee_NativeCipherImpl_init + (JNIEnv *, jobject, jbyteArray); + +/* + * Class: net_md_5_bungee_NativeCipherImpl + * Method: free + * Signature: (J)V + */ +JNIEXPORT void JNICALL Java_net_md_15_bungee_NativeCipherImpl_free + (JNIEnv *, jobject, jlong); + +/* + * Class: net_md_5_bungee_NativeCipherImpl + * Method: cipher + * Signature: (ZJ[BJJI)V + */ +JNIEXPORT void JNICALL Java_net_md_15_bungee_NativeCipherImpl_cipher + (JNIEnv *, jobject, jboolean, jlong, jbyteArray, jlong, jlong, jint); + +#ifdef __cplusplus +} +#endif +#endif diff --git a/native/src/main/java/net/md_5/bungee/BungeeCipher.java b/native/src/main/java/net/md_5/bungee/BungeeCipher.java new file mode 100644 index 000000000..a165d5ad0 --- /dev/null +++ b/native/src/main/java/net/md_5/bungee/BungeeCipher.java @@ -0,0 +1,21 @@ +package net.md_5.bungee; + +import io.netty.buffer.ByteBuf; +import io.netty.channel.ChannelHandlerContext; +import javax.crypto.SecretKey; +import java.security.GeneralSecurityException; + +/** + * Class to expose cipher methods from either native or fallback Java cipher. + */ +public interface BungeeCipher +{ + + void init(boolean forEncryption, SecretKey key) throws GeneralSecurityException; + + void free(); + + void cipher(ByteBuf in, ByteBuf out) throws GeneralSecurityException; + + ByteBuf cipher(ChannelHandlerContext ctx, ByteBuf in) throws GeneralSecurityException; +} diff --git a/proxy/src/main/java/net/md_5/bungee/netty/CipherBase.java b/native/src/main/java/net/md_5/bungee/FallbackCipher.java similarity index 65% rename from proxy/src/main/java/net/md_5/bungee/netty/CipherBase.java rename to native/src/main/java/net/md_5/bungee/FallbackCipher.java index 5679afea2..3c9ee0786 100644 --- a/proxy/src/main/java/net/md_5/bungee/netty/CipherBase.java +++ b/native/src/main/java/net/md_5/bungee/FallbackCipher.java @@ -1,24 +1,17 @@ -package net.md_5.bungee.netty; +package net.md_5.bungee; import io.netty.buffer.ByteBuf; import io.netty.channel.ChannelHandlerContext; import javax.crypto.Cipher; +import javax.crypto.SecretKey; import javax.crypto.ShortBufferException; -import lombok.AccessLevel; -import lombok.NonNull; -import lombok.RequiredArgsConstructor; +import javax.crypto.spec.IvParameterSpec; +import java.security.GeneralSecurityException; -/** - * Class to expose an - * {@link #cipher(io.netty.buffer.ByteBuf, io.netty.buffer.ByteBuf)} method to - * aid in the efficient passing of ByteBuffers through a cipher. - */ -@RequiredArgsConstructor(access = AccessLevel.PROTECTED) -public class CipherBase +public class FallbackCipher implements BungeeCipher { - @NonNull - private final Cipher cipher; + private Cipher cipher; private ThreadLocal heapInLocal = new EmptyByteThreadLocal(); private ThreadLocal heapOutLocal = new EmptyByteThreadLocal(); @@ -32,6 +25,51 @@ public class CipherBase } } + public FallbackCipher() throws GeneralSecurityException + { + this.cipher = Cipher.getInstance( "AES/CFB8/NoPadding" ); + } + + @Override + public void init(boolean forEncryption, SecretKey key) throws GeneralSecurityException + { + int mode = forEncryption ? Cipher.ENCRYPT_MODE : Cipher.DECRYPT_MODE; + cipher.init( mode, key, new IvParameterSpec( key.getEncoded() ) ); + } + + @Override + public void cipher(ByteBuf in, ByteBuf out) throws ShortBufferException + { + int readableBytes = in.readableBytes(); + byte[] heapIn = bufToByte( in ); + + byte[] heapOut = heapOutLocal.get(); + int outputSize = cipher.getOutputSize( readableBytes ); + if ( heapOut.length < outputSize ) + { + heapOut = new byte[ outputSize ]; + heapOutLocal.set( heapOut ); + } + out.writeBytes( heapOut, 0, cipher.update( heapIn, 0, readableBytes, heapOut ) ); + } + + @Override + public ByteBuf cipher(ChannelHandlerContext ctx, ByteBuf in) throws ShortBufferException + { + int readableBytes = in.readableBytes(); + byte[] heapIn = bufToByte( in ); + + ByteBuf heapOut = ctx.alloc().heapBuffer( cipher.getOutputSize( readableBytes ) ); + heapOut.writerIndex( cipher.update( heapIn, 0, readableBytes, heapOut.array(), heapOut.arrayOffset() ) ); + + return heapOut; + } + + @Override + public void free() + { + } + private byte[] bufToByte(ByteBuf in) { byte[] heapIn = heapInLocal.get(); @@ -44,30 +82,4 @@ public class CipherBase in.readBytes( heapIn, 0, readableBytes ); return heapIn; } - - protected ByteBuf cipher(ChannelHandlerContext ctx, ByteBuf in) throws ShortBufferException - { - int readableBytes = in.readableBytes(); - byte[] heapIn = bufToByte( in ); - - ByteBuf heapOut = ctx.alloc().heapBuffer( cipher.getOutputSize( readableBytes ) ); - heapOut.writerIndex( cipher.update( heapIn, 0, readableBytes, heapOut.array(), heapOut.arrayOffset() ) ); - - return heapOut; - } - - protected void cipher(ByteBuf in, ByteBuf out) throws ShortBufferException - { - int readableBytes = in.readableBytes(); - byte[] heapIn = bufToByte( in ); - - byte[] heapOut = heapOutLocal.get(); - int outputSize = cipher.getOutputSize( readableBytes ); - if ( heapOut.length < outputSize ) - { - heapOut = new byte[ outputSize ]; - heapOutLocal.set( heapOut ); - } - out.writeBytes( heapOut, 0, cipher.update( heapIn, 0, readableBytes, heapOut ) ); - } } diff --git a/native/src/main/java/net/md_5/bungee/NativeCipher.java b/native/src/main/java/net/md_5/bungee/NativeCipher.java new file mode 100644 index 000000000..10e15bf84 --- /dev/null +++ b/native/src/main/java/net/md_5/bungee/NativeCipher.java @@ -0,0 +1,113 @@ +package net.md_5.bungee; + +import com.google.common.base.Preconditions; +import com.google.common.io.ByteStreams; +import io.netty.buffer.ByteBuf; +import io.netty.channel.ChannelHandlerContext; +import lombok.Getter; +import javax.crypto.SecretKey; +import java.io.File; +import java.io.FileOutputStream; +import java.io.InputStream; +import java.io.OutputStream; +import java.security.GeneralSecurityException; + +public class NativeCipher implements BungeeCipher +{ + + @Getter + private final NativeCipherImpl nativeCipher = new NativeCipherImpl(); + private boolean forEncryption; + private byte[] iv; + /*============================================================================*/ + private static boolean loaded; + + private long pointer; + + public static boolean isSupported() + { + return "Linux".equals( System.getProperty( "os.name" ) ) && "amd64".equals( System.getProperty( "os.arch" ) ); + } + + public static boolean load() + { + if ( !loaded && isSupported() ) + { + try ( InputStream lib = BungeeCipher.class.getClassLoader().getResourceAsStream( "native-cipher.so" ) ) + { + // Else we will create and copy it to a temp file + File temp = File.createTempFile( "bungeecord-native-cipher", ".so" ); + try ( OutputStream outputStream = new FileOutputStream( temp ) ) + { + ByteStreams.copy( lib, outputStream ); + System.load( temp.getPath() ); + } + loaded = true; + } catch ( Throwable t ) + { + } + } + + return loaded; + } + + public static boolean isLoaded() + { + return loaded; + } + + @Override + public void init(boolean forEncryption, SecretKey key) throws GeneralSecurityException + { + if ( pointer != 0 ) + { + nativeCipher.free( pointer ); + } + this.forEncryption = forEncryption; + this.iv = key.getEncoded(); // initialize the IV + this.pointer = nativeCipher.init( key.getEncoded() ); + } + + @Override + public void free() + { + if ( pointer != 0 ) + { + nativeCipher.free( pointer ); + pointer = 0; + } + } + + @Override + public void cipher(ByteBuf in, ByteBuf out) throws GeneralSecurityException + { + // Smoke tests + in.memoryAddress(); + out.memoryAddress(); + Preconditions.checkState( pointer != 0, "Invalid pointer to AES key!" ); + Preconditions.checkState( iv != null, "Invalid IV!" ); + + // Store how many bytes we can cipher + int length = in.readableBytes(); + // It is important to note that in AES CFB-8 mode, the number of read bytes, is the number of outputted bytes + out.ensureWritable( length ); + + // Cipher the bytes + nativeCipher.cipher( forEncryption, pointer, iv, in.memoryAddress() + in.readerIndex(), out.memoryAddress() + out.writerIndex(), length ); + + // Go to the end of the buffer, all bytes would of been read + in.readerIndex( in.writerIndex() ); + // Add the number of ciphered bytes to our position + out.writerIndex( out.writerIndex() + length ); + } + + @Override + public ByteBuf cipher(ChannelHandlerContext ctx, ByteBuf in) throws GeneralSecurityException + { + int readableBytes = in.readableBytes(); + ByteBuf heapOut = ctx.alloc().directBuffer( readableBytes ); // CFB8 + cipher( in, heapOut ); + + return heapOut; + } +} diff --git a/native/src/main/java/net/md_5/bungee/NativeCipherImpl.java b/native/src/main/java/net/md_5/bungee/NativeCipherImpl.java new file mode 100644 index 000000000..261a84ec4 --- /dev/null +++ b/native/src/main/java/net/md_5/bungee/NativeCipherImpl.java @@ -0,0 +1,32 @@ +package net.md_5.bungee; + +class NativeCipherImpl +{ + + /** + * Initializes the key. + * + * @param key the key to for encryption + * @return the pointer to key + */ + native long init(byte[] key); + + /** + * Frees the key. + * + * @param key the pointer to key + */ + native void free(long key); + + /** + * This method will encrypt some data in AES-CFB8 using the specified key. + * + * @param forEncryption encryption / decryption mode + * @param key the pointer to key + * @param iv the iv to use + * @param in the starting memory address for reading data + * @param out the starting memory address for writing data + * @param length the length of data to read / write + */ + native void cipher(boolean forEncryption, long key, byte[] iv, long in, long out, int length); +} diff --git a/native/src/main/resources/native-cipher.so b/native/src/main/resources/native-cipher.so new file mode 100755 index 0000000000000000000000000000000000000000..262be07b10cba6dcb019406ebf12adfc6b8fdaca GIT binary patch literal 8277 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 proxy query + native diff --git a/proxy/pom.xml b/proxy/pom.xml index 1369b8527..39eafecdf 100644 --- a/proxy/pom.xml +++ b/proxy/pom.xml @@ -47,6 +47,12 @@ ${project.version} compile + + net.md-5 + bungeecord-native + ${project.version} + compile + net.md-5 bungeecord-protocol diff --git a/proxy/src/main/java/net/md_5/bungee/BungeeCord.java b/proxy/src/main/java/net/md_5/bungee/BungeeCord.java index 11b560c5e..b30fdef18 100644 --- a/proxy/src/main/java/net/md_5/bungee/BungeeCord.java +++ b/proxy/src/main/java/net/md_5/bungee/BungeeCord.java @@ -158,6 +158,14 @@ public class BungeeCord extends ProxyServer logger.info( "Unable to initialize fancy terminal. To fix this on Windows, install the correct Microsoft Visual C++ 2008 Runtime" ); logger.info( "NOTE: This error is non crucial, and BungeeCord will still function correctly! Do not bug the author about it unless you are still unable to get it working" ); } + + if ( !NativeCipher.load() ) + { + logger.warning( "NOTE: Failed to load native code. Falling back to Java cipher." ); + } else + { + logger.info( "Native code loaded." ); + } } /** diff --git a/proxy/src/main/java/net/md_5/bungee/EncryptionUtil.java b/proxy/src/main/java/net/md_5/bungee/EncryptionUtil.java index eb7fe4f19..dabff27c1 100644 --- a/proxy/src/main/java/net/md_5/bungee/EncryptionUtil.java +++ b/proxy/src/main/java/net/md_5/bungee/EncryptionUtil.java @@ -12,7 +12,6 @@ import java.util.Arrays; import java.util.Random; import javax.crypto.Cipher; import javax.crypto.SecretKey; -import javax.crypto.spec.IvParameterSpec; import javax.crypto.spec.SecretKeySpec; import lombok.Getter; import net.md_5.bungee.protocol.packet.EncryptionResponse; @@ -64,11 +63,19 @@ public class EncryptionUtil return new SecretKeySpec( cipher.doFinal( resp.getSharedSecret() ), "AES" ); } - public static Cipher getCipher(int opMode, Key shared) throws GeneralSecurityException + public static BungeeCipher getCipher(boolean forEncryption, SecretKey shared) throws GeneralSecurityException { - Cipher cip = Cipher.getInstance( "AES/CFB8/NoPadding" ); - cip.init( opMode, shared, new IvParameterSpec( shared.getEncoded() ) ); - return cip; + BungeeCipher cipher; + if ( NativeCipher.isLoaded() ) + { + cipher = new NativeCipher(); + } else + { + cipher = new FallbackCipher(); + } + + cipher.init( forEncryption, shared ); + return cipher; } public static PublicKey getPubkey(EncryptionRequest request) throws GeneralSecurityException diff --git a/proxy/src/main/java/net/md_5/bungee/connection/InitialHandler.java b/proxy/src/main/java/net/md_5/bungee/connection/InitialHandler.java index c9afc3002..473fc0a5a 100644 --- a/proxy/src/main/java/net/md_5/bungee/connection/InitialHandler.java +++ b/proxy/src/main/java/net/md_5/bungee/connection/InitialHandler.java @@ -8,14 +8,10 @@ import java.security.MessageDigest; import java.util.ArrayList; import java.util.List; import java.util.logging.Level; -import javax.crypto.Cipher; import javax.crypto.SecretKey; import lombok.Getter; import lombok.RequiredArgsConstructor; -import net.md_5.bungee.BungeeCord; -import net.md_5.bungee.EncryptionUtil; -import net.md_5.bungee.UserConnection; -import net.md_5.bungee.Util; +import net.md_5.bungee.*; import net.md_5.bungee.api.Callback; import net.md_5.bungee.api.ChatColor; import net.md_5.bungee.api.ProxyServer; @@ -30,10 +26,10 @@ import net.md_5.bungee.api.event.ProxyPingEvent; import net.md_5.bungee.http.HttpClient; import net.md_5.bungee.netty.HandlerBoss; import net.md_5.bungee.netty.ChannelWrapper; -import net.md_5.bungee.netty.CipherDecoder; -import net.md_5.bungee.netty.CipherEncoder; import net.md_5.bungee.netty.PacketHandler; import net.md_5.bungee.netty.PipelineUtils; +import net.md_5.bungee.netty.cipher.CipherDecoder; +import net.md_5.bungee.netty.cipher.CipherEncoder; import net.md_5.bungee.protocol.DefinedPacket; import net.md_5.bungee.protocol.packet.Login; import net.md_5.bungee.protocol.packet.Handshake; @@ -286,9 +282,9 @@ public class InitialHandler extends PacketHandler implements PendingConnection Preconditions.checkState( thisState == State.ENCRYPT, "Not expecting ENCRYPT" ); sharedKey = EncryptionUtil.getSecret( encryptResponse, request ); - Cipher decrypt = EncryptionUtil.getCipher( Cipher.DECRYPT_MODE, sharedKey ); + BungeeCipher decrypt = EncryptionUtil.getCipher( false, sharedKey ); ch.addBefore( PipelineUtils.FRAME_DECODER, PipelineUtils.DECRYPT_HANDLER, new CipherDecoder( decrypt ) ); - Cipher encrypt = EncryptionUtil.getCipher( Cipher.ENCRYPT_MODE, sharedKey ); + BungeeCipher encrypt = EncryptionUtil.getCipher( true, sharedKey ); ch.addBefore( PipelineUtils.FRAME_PREPENDER, PipelineUtils.ENCRYPT_HANDLER, new CipherEncoder( encrypt ) ); String encName = URLEncoder.encode( InitialHandler.this.getName(), "UTF-8" ); diff --git a/proxy/src/main/java/net/md_5/bungee/netty/CipherDecoder.java b/proxy/src/main/java/net/md_5/bungee/netty/cipher/CipherDecoder.java similarity index 59% rename from proxy/src/main/java/net/md_5/bungee/netty/CipherDecoder.java rename to proxy/src/main/java/net/md_5/bungee/netty/cipher/CipherDecoder.java index e6fe7df92..7fcc323a7 100644 --- a/proxy/src/main/java/net/md_5/bungee/netty/CipherDecoder.java +++ b/proxy/src/main/java/net/md_5/bungee/netty/cipher/CipherDecoder.java @@ -1,24 +1,27 @@ -package net.md_5.bungee.netty; +package net.md_5.bungee.netty.cipher; import io.netty.buffer.ByteBuf; import io.netty.channel.ChannelHandlerContext; import io.netty.handler.codec.MessageToMessageDecoder; +import lombok.RequiredArgsConstructor; +import net.md_5.bungee.BungeeCipher; import java.util.List; -import javax.crypto.Cipher; +@RequiredArgsConstructor public class CipherDecoder extends MessageToMessageDecoder { - private final CipherBase cipher; - - public CipherDecoder(Cipher cipher) - { - this.cipher = new CipherBase( cipher ); - } + private final BungeeCipher cipher; @Override protected void decode(ChannelHandlerContext ctx, ByteBuf msg, List out) throws Exception { out.add( cipher.cipher( ctx, msg ) ); } + + @Override + public void handlerRemoved(ChannelHandlerContext ctx) throws Exception + { + cipher.free(); + } } diff --git a/proxy/src/main/java/net/md_5/bungee/netty/CipherEncoder.java b/proxy/src/main/java/net/md_5/bungee/netty/cipher/CipherEncoder.java similarity index 56% rename from proxy/src/main/java/net/md_5/bungee/netty/CipherEncoder.java rename to proxy/src/main/java/net/md_5/bungee/netty/cipher/CipherEncoder.java index 7e5c0ec60..df89b99bc 100644 --- a/proxy/src/main/java/net/md_5/bungee/netty/CipherEncoder.java +++ b/proxy/src/main/java/net/md_5/bungee/netty/cipher/CipherEncoder.java @@ -1,23 +1,26 @@ -package net.md_5.bungee.netty; +package net.md_5.bungee.netty.cipher; import io.netty.buffer.ByteBuf; import io.netty.channel.ChannelHandlerContext; import io.netty.handler.codec.MessageToByteEncoder; -import javax.crypto.Cipher; +import lombok.RequiredArgsConstructor; +import net.md_5.bungee.BungeeCipher; +@RequiredArgsConstructor public class CipherEncoder extends MessageToByteEncoder { - private final CipherBase cipher; - - public CipherEncoder(Cipher cipher) - { - this.cipher = new CipherBase( cipher ); - } + private final BungeeCipher cipher; @Override protected void encode(ChannelHandlerContext ctx, ByteBuf in, ByteBuf out) throws Exception { cipher.cipher( in, out ); } + + @Override + public void handlerRemoved(ChannelHandlerContext ctx) throws Exception + { + cipher.free(); + } }