package net.md_5.bungee.jni.cipher; import com.google.common.base.Preconditions; import io.netty.buffer.ByteBuf; import io.netty.channel.ChannelHandlerContext; import lombok.Getter; import javax.crypto.SecretKey; import java.security.GeneralSecurityException; public class NativeCipher implements BungeeCipher { @Getter private final NativeCipherImpl nativeCipher = new NativeCipherImpl(); /*============================================================================*/ private long ctx; @Override public void init(boolean forEncryption, SecretKey key) throws GeneralSecurityException { Preconditions.checkArgument( key.getEncoded().length == 16, "Invalid key size" ); free(); this.ctx = nativeCipher.init( forEncryption, key.getEncoded() ); } @Override public void free() { if ( ctx != 0 ) { nativeCipher.free( ctx ); ctx = 0; } } @Override public void cipher(ByteBuf in, ByteBuf out) throws GeneralSecurityException { // Smoke tests in.memoryAddress(); out.memoryAddress(); Preconditions.checkState( ctx != 0, "Invalid pointer to AES key!" ); // Store how many bytes we can cipher int length = in.readableBytes(); // Older OpenSSL versions will flip if length <= 0 if ( length <= 0 ) { return; } // 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( ctx, 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; } }