Revert native cipher, causing SIGSEGV
This commit is contained in:
@@ -1,56 +0,0 @@
|
||||
#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);
|
||||
}
|
||||
}
|
@@ -1,37 +0,0 @@
|
||||
/* 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
|
@@ -1,21 +0,0 @@
|
||||
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,85 +0,0 @@
|
||||
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 javax.crypto.spec.IvParameterSpec;
|
||||
import java.security.GeneralSecurityException;
|
||||
|
||||
public class FallbackCipher implements BungeeCipher
|
||||
{
|
||||
|
||||
private Cipher cipher;
|
||||
private ThreadLocal<byte[]> heapInLocal = new EmptyByteThreadLocal();
|
||||
private ThreadLocal<byte[]> heapOutLocal = new EmptyByteThreadLocal();
|
||||
|
||||
private static class EmptyByteThreadLocal extends ThreadLocal<byte[]>
|
||||
{
|
||||
|
||||
@Override
|
||||
protected byte[] initialValue()
|
||||
{
|
||||
return new byte[ 0 ];
|
||||
}
|
||||
}
|
||||
|
||||
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();
|
||||
int readableBytes = in.readableBytes();
|
||||
if ( heapIn.length < readableBytes )
|
||||
{
|
||||
heapIn = new byte[ readableBytes ];
|
||||
heapInLocal.set( heapIn );
|
||||
}
|
||||
in.readBytes( heapIn, 0, readableBytes );
|
||||
return heapIn;
|
||||
}
|
||||
}
|
@@ -1,111 +0,0 @@
|
||||
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;
|
||||
}
|
||||
}
|
@@ -1,32 +0,0 @@
|
||||
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);
|
||||
}
|
Binary file not shown.
@@ -1,74 +0,0 @@
|
||||
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!" );
|
||||
}
|
||||
}
|
Reference in New Issue
Block a user