Implement new, high-performance cipher in native code. Currently available only for Linux-x64, other platforms will fallback to Java cipher.
This commit is contained in:
parent
12ef019d69
commit
c70006a36c
3
native/compile-native.sh
Executable file
3
native/compile-native.sh
Executable file
@ -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
|
31
native/nb-configuration.xml
Normal file
31
native/nb-configuration.xml
Normal file
@ -0,0 +1,31 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<project-shared-configuration>
|
||||
<!--
|
||||
This file contains additional configuration written by modules in the NetBeans IDE.
|
||||
The configuration is intended to be shared among all the users of project and
|
||||
therefore it is assumed to be part of version control checkout.
|
||||
Without this configuration present, some functionality in the IDE may be limited or fail altogether.
|
||||
-->
|
||||
<properties xmlns="http://www.netbeans.org/ns/maven-properties-data/1">
|
||||
<!--
|
||||
Properties that influence various parts of the IDE, especially code formatting and the like.
|
||||
You can copy and paste the single properties, into the pom.xml file and the IDE will pick them up.
|
||||
That way multiple projects can share the same settings (useful for formatting rules for example).
|
||||
Any value defined here will override the pom.xml file value but is only applicable to the current project.
|
||||
-->
|
||||
<org-netbeans-modules-editor-indent.CodeStyle.usedProfile>project</org-netbeans-modules-editor-indent.CodeStyle.usedProfile>
|
||||
<org-netbeans-modules-editor-indent.text.x-java.CodeStyle.project.classDeclBracePlacement>NEW_LINE</org-netbeans-modules-editor-indent.text.x-java.CodeStyle.project.classDeclBracePlacement>
|
||||
<org-netbeans-modules-editor-indent.text.x-java.CodeStyle.project.otherBracePlacement>NEW_LINE</org-netbeans-modules-editor-indent.text.x-java.CodeStyle.project.otherBracePlacement>
|
||||
<org-netbeans-modules-editor-indent.text.x-java.CodeStyle.project.methodDeclBracePlacement>NEW_LINE</org-netbeans-modules-editor-indent.text.x-java.CodeStyle.project.methodDeclBracePlacement>
|
||||
<org-netbeans-modules-editor-indent.text.x-java.CodeStyle.project.spaceWithinMethodCallParens>true</org-netbeans-modules-editor-indent.text.x-java.CodeStyle.project.spaceWithinMethodCallParens>
|
||||
<org-netbeans-modules-editor-indent.text.x-java.CodeStyle.project.spaceWithinSwitchParens>true</org-netbeans-modules-editor-indent.text.x-java.CodeStyle.project.spaceWithinSwitchParens>
|
||||
<org-netbeans-modules-editor-indent.text.x-java.CodeStyle.project.spaceWithinCatchParens>true</org-netbeans-modules-editor-indent.text.x-java.CodeStyle.project.spaceWithinCatchParens>
|
||||
<org-netbeans-modules-editor-indent.text.x-java.CodeStyle.project.spaceWithinTryParens>true</org-netbeans-modules-editor-indent.text.x-java.CodeStyle.project.spaceWithinTryParens>
|
||||
<org-netbeans-modules-editor-indent.text.x-java.CodeStyle.project.spaceWithinSynchronizedParens>true</org-netbeans-modules-editor-indent.text.x-java.CodeStyle.project.spaceWithinSynchronizedParens>
|
||||
<org-netbeans-modules-editor-indent.text.x-java.CodeStyle.project.spaceWithinArrayInitBrackets>true</org-netbeans-modules-editor-indent.text.x-java.CodeStyle.project.spaceWithinArrayInitBrackets>
|
||||
<org-netbeans-modules-editor-indent.text.x-java.CodeStyle.project.spaceWithinParens>true</org-netbeans-modules-editor-indent.text.x-java.CodeStyle.project.spaceWithinParens>
|
||||
<org-netbeans-modules-editor-indent.text.x-java.CodeStyle.project.spaceWithinWhileParens>true</org-netbeans-modules-editor-indent.text.x-java.CodeStyle.project.spaceWithinWhileParens>
|
||||
<org-netbeans-modules-editor-indent.text.x-java.CodeStyle.project.spaceWithinIfParens>true</org-netbeans-modules-editor-indent.text.x-java.CodeStyle.project.spaceWithinIfParens>
|
||||
<org-netbeans-modules-editor-indent.text.x-java.CodeStyle.project.spaceWithinForParens>true</org-netbeans-modules-editor-indent.text.x-java.CodeStyle.project.spaceWithinForParens>
|
||||
</properties>
|
||||
</project-shared-configuration>
|
29
native/pom.xml
Normal file
29
native/pom.xml
Normal file
@ -0,0 +1,29 @@
|
||||
|
||||
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
|
||||
<modelVersion>4.0.0</modelVersion>
|
||||
|
||||
<parent>
|
||||
<groupId>net.md-5</groupId>
|
||||
<artifactId>bungeecord-parent</artifactId>
|
||||
<version>1.7-SNAPSHOT</version>
|
||||
<relativePath>../pom.xml</relativePath>
|
||||
</parent>
|
||||
|
||||
<groupId>net.md-5</groupId>
|
||||
<artifactId>bungeecord-native</artifactId>
|
||||
<version>1.7-SNAPSHOT</version>
|
||||
<packaging>jar</packaging>
|
||||
|
||||
<name>BungeeCord-Native</name>
|
||||
<description>Optional native code to speed up and enhance BungeeCord functionality.</description>
|
||||
|
||||
<dependencies>
|
||||
<dependency>
|
||||
<groupId>io.netty</groupId>
|
||||
<artifactId>netty-transport</artifactId>
|
||||
<version>${netty.version}</version>
|
||||
<scope>compile</scope>
|
||||
</dependency>
|
||||
</dependencies>
|
||||
</project>
|
56
native/src/main/c/NativeCipherImpl.c
Normal file
56
native/src/main/c/NativeCipherImpl.c
Normal file
@ -0,0 +1,56 @@
|
||||
#include "net_md_5_bungee_NativeCipherImpl.h"
|
||||
#include <openssl/aes.h>
|
||||
#include <jni.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#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);
|
||||
}
|
||||
}
|
37
native/src/main/c/net_md_5_bungee_NativeCipherImpl.h
Normal file
37
native/src/main/c/net_md_5_bungee_NativeCipherImpl.h
Normal file
@ -0,0 +1,37 @@
|
||||
/* DO NOT EDIT THIS FILE - it is machine generated */
|
||||
#include <jni.h>
|
||||
/* 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
|
21
native/src/main/java/net/md_5/bungee/BungeeCipher.java
Normal file
21
native/src/main/java/net/md_5/bungee/BungeeCipher.java
Normal file
@ -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;
|
||||
}
|
@ -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<byte[]> heapInLocal = new EmptyByteThreadLocal();
|
||||
private ThreadLocal<byte[]> 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 ) );
|
||||
}
|
||||
}
|
111
native/src/main/java/net/md_5/bungee/NativeCipher.java
Normal file
111
native/src/main/java/net/md_5/bungee/NativeCipher.java
Normal file
@ -0,0 +1,111 @@
|
||||
package net.md_5.bungee;
|
||||
|
||||
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();
|
||||
// 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
|
||||
if ( out.writableBytes() < length )
|
||||
{
|
||||
out.capacity( 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;
|
||||
}
|
||||
}
|
32
native/src/main/java/net/md_5/bungee/NativeCipherImpl.java
Normal file
32
native/src/main/java/net/md_5/bungee/NativeCipherImpl.java
Normal file
@ -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);
|
||||
}
|
BIN
native/src/main/resources/native-cipher.so
Executable file
BIN
native/src/main/resources/native-cipher.so
Executable file
Binary file not shown.
74
native/src/test/java/net/md_5/bungee/NativeCipherTest.java
Normal file
74
native/src/test/java/net/md_5/bungee/NativeCipherTest.java
Normal file
@ -0,0 +1,74 @@
|
||||
package net.md_5.bungee;
|
||||
|
||||
import io.netty.buffer.ByteBuf;
|
||||
import io.netty.buffer.Unpooled;
|
||||
import org.junit.Assert;
|
||||
import org.junit.Test;
|
||||
import javax.crypto.SecretKey;
|
||||
import javax.crypto.spec.SecretKeySpec;
|
||||
|
||||
public class NativeCipherTest
|
||||
{
|
||||
|
||||
private final byte[] plainBytes = "This is a test".getBytes();
|
||||
private final byte[] cipheredBytes = new byte[]
|
||||
{
|
||||
50, -7, 89, 1, -11, -32, -118, -48, -2, -72, 105, 97, -70, -81
|
||||
};
|
||||
private final SecretKey secret = new SecretKeySpec( new byte[ 16 ], "AES" );
|
||||
|
||||
@Test
|
||||
public void testOpenSSL() throws Exception
|
||||
{
|
||||
if ( NativeCipher.isSupported() )
|
||||
{
|
||||
boolean loaded = NativeCipher.load();
|
||||
Assert.assertTrue( "Native cipher failed to load!", loaded );
|
||||
|
||||
NativeCipher cipher = new NativeCipher();
|
||||
System.out.println( "Testing OpenSSL cipher..." );
|
||||
testACipher( cipher );
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testJDK() throws Exception
|
||||
{
|
||||
// Create JDK cipher
|
||||
BungeeCipher cipher = new FallbackCipher();
|
||||
|
||||
System.out.println( "Testing Java cipher..." );
|
||||
testACipher( cipher );
|
||||
}
|
||||
|
||||
/**
|
||||
* Hackish test which can test both native and fallback ciphers using direct
|
||||
* buffers.
|
||||
*/
|
||||
public void testACipher(BungeeCipher cipher) throws Exception
|
||||
{
|
||||
// Create input buf
|
||||
ByteBuf nativePlain = Unpooled.directBuffer( plainBytes.length );
|
||||
nativePlain.writeBytes( plainBytes );
|
||||
// Create expected buf
|
||||
ByteBuf nativeCiphered = Unpooled.directBuffer( cipheredBytes.length );
|
||||
nativeCiphered.writeBytes( cipheredBytes );
|
||||
// Create output buf
|
||||
ByteBuf out = Unpooled.directBuffer( plainBytes.length );
|
||||
|
||||
// Encrypt
|
||||
cipher.init( true, secret );
|
||||
cipher.cipher( nativePlain, out );
|
||||
Assert.assertEquals( nativeCiphered, out );
|
||||
|
||||
out.clear();
|
||||
|
||||
// Decrypt
|
||||
cipher.init( false, secret );
|
||||
cipher.cipher( nativeCiphered, out );
|
||||
nativePlain.resetReaderIndex();
|
||||
Assert.assertEquals( nativePlain, out );
|
||||
|
||||
System.out.println( "This cipher works correctly!" );
|
||||
}
|
||||
}
|
1
pom.xml
1
pom.xml
@ -44,6 +44,7 @@
|
||||
<module>protocol</module>
|
||||
<module>proxy</module>
|
||||
<module>query</module>
|
||||
<module>native</module>
|
||||
</modules>
|
||||
|
||||
<scm>
|
||||
|
@ -47,6 +47,12 @@
|
||||
<version>${project.version}</version>
|
||||
<scope>compile</scope>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>net.md-5</groupId>
|
||||
<artifactId>bungeecord-native</artifactId>
|
||||
<version>${project.version}</version>
|
||||
<scope>compile</scope>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>net.md-5</groupId>
|
||||
<artifactId>bungeecord-protocol</artifactId>
|
||||
|
@ -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." );
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -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
|
||||
|
@ -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" );
|
||||
|
@ -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<ByteBuf>
|
||||
{
|
||||
|
||||
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<Object> out) throws Exception
|
||||
{
|
||||
out.add( cipher.cipher( ctx, msg ) );
|
||||
}
|
||||
|
||||
@Override
|
||||
public void handlerRemoved(ChannelHandlerContext ctx) throws Exception
|
||||
{
|
||||
cipher.free();
|
||||
}
|
||||
}
|
@ -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<ByteBuf>
|
||||
{
|
||||
|
||||
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();
|
||||
}
|
||||
}
|
Loading…
Reference in New Issue
Block a user