package com.captainbern.minecraft.net.pipeline;
import io.netty.buffer.ByteBuf;
import io.netty.buffer.Unpooled;
import io.netty.channel.ChannelHandlerContext;
import io.netty.handler.codec.MessageToMessageCodec;
import javax.crypto.Cipher;
import javax.crypto.SecretKey;
import javax.crypto.ShortBufferException;
import javax.crypto.spec.IvParameterSpec;
import java.nio.ByteBuffer;
import java.security.GeneralSecurityException;
import java.util.List;
public class EncryptionHandler extends MessageToMessageCodec<ByteBuf, ByteBuf> {
private final PacketEncryptionHandler encrypter;
private final PacketEncryptionHandler decrypter;
public EncryptionHandler(SecretKey secretKey) {
try {
this.encrypter = new PacketEncryptionHandler(Cipher.ENCRYPT_MODE, secretKey);
this.decrypter = new PacketEncryptionHandler(Cipher.DECRYPT_MODE, secretKey);
} catch (GeneralSecurityException e) {
throw new RuntimeException("Failed to initialize the EncryptionHandler!", e);
}
}
@Override
protected void encode(ChannelHandlerContext channelHandlerContext, ByteBuf byteBuf, List<Object> list) throws Exception {
this.encrypter.handle(byteBuf, list);
}
@Override
protected void decode(ChannelHandlerContext channelHandlerContext, ByteBuf byteBuf, List<Object> list) throws Exception {
this.decrypter.handle(byteBuf, list);
}
private static class PacketEncryptionHandler {
private Cipher cipher;
public PacketEncryptionHandler(int cipherMode, SecretKey secretKey) throws GeneralSecurityException {
this.cipher = Cipher.getInstance("AES/CFB8/NoPadding");
this.cipher.init(cipherMode, secretKey, new IvParameterSpec(secretKey.getEncoded()));
}
public void handle(ByteBuf byteBuf, List<Object> list) {
ByteBuffer out = ByteBuffer.allocate(byteBuf.readableBytes());
try {
this.cipher.update(byteBuf.nioBuffer(), out);
} catch (ShortBufferException e) {
throw new RuntimeException("Encryption buffer was too short!", e);
}
out.flip();
list.add(Unpooled.wrappedBuffer(out));
}
}
}