package net.md_5.bungee; import net.md_5.bungee.jni.cipher.NativeCipher; import net.md_5.bungee.jni.cipher.JavaCipher; import net.md_5.bungee.jni.cipher.BungeeCipher; import io.netty.buffer.ByteBuf; import io.netty.buffer.Unpooled; import java.util.Random; import org.junit.Assert; import org.junit.Test; import org.junit.FixMethodOrder; import org.junit.runners.MethodSorters; import javax.crypto.SecretKey; import javax.crypto.spec.SecretKeySpec; import net.md_5.bungee.jni.NativeCode; @FixMethodOrder(MethodSorters.NAME_ASCENDING) 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" ); private static final int BENCHMARK_COUNT = 4096; // private static final NativeCode<BungeeCipher> factory = new NativeCode( "native-cipher", JavaCipher.class, NativeCipher.class ); @Test public void testOpenSSL() throws Exception { if ( NativeCode.isSupported() ) { boolean loaded = factory.load(); Assert.assertTrue( "Native cipher failed to load!", loaded ); NativeCipher cipher = new NativeCipher(); System.out.println( "Testing OpenSSL cipher..." ); testACipher( cipher ); } } @Test public void testOpenSSLBenchmark() throws Exception { if ( NativeCode.isSupported() ) { boolean loaded = factory.load(); Assert.assertTrue( "Native cipher failed to load!", loaded ); NativeCipher cipher = new NativeCipher(); System.out.println( "Benchmarking OpenSSL cipher..." ); testBenchmark( cipher ); } } @Test public void testJDK() throws Exception { // Create JDK cipher BungeeCipher cipher = new JavaCipher(); System.out.println( "Testing Java cipher..." ); testACipher( cipher ); } @Test public void testJDKBenchmark() throws Exception { // Create JDK cipher BungeeCipher cipher = new JavaCipher(); System.out.println( "Benchmarking Java cipher..." ); testBenchmark( 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!" ); } public void testBenchmark(BungeeCipher cipher) throws Exception { // Create input buf byte[] random = new byte[ 1 << 12 ]; new Random().nextBytes( random ); ByteBuf nativePlain = Unpooled.directBuffer(); nativePlain.writeBytes( random ); // Create output buf ByteBuf nativeCiphered = Unpooled.directBuffer( plainBytes.length ); // Encrypt cipher.init( true, secret ); long start = System.currentTimeMillis(); for ( int i = 0; i < BENCHMARK_COUNT; i++ ) { nativeCiphered.clear(); cipher.cipher( nativePlain, nativeCiphered ); nativePlain.readerIndex( 0 ); } System.out.println( String.format( "Encryption Iteration: %d, Elapsed: %d ms", BENCHMARK_COUNT, System.currentTimeMillis() - start ) ); // Create output buf ByteBuf out = Unpooled.directBuffer( plainBytes.length ); // Decrypt cipher.init( false, secret ); start = System.currentTimeMillis(); for ( int i = 0; i < BENCHMARK_COUNT; i++ ) { cipher.cipher( nativeCiphered, out ); nativeCiphered.readerIndex( 0 ); out.clear(); } System.out.println( String.format( "Decryption Iteration: %d, Elapsed: %d ms", BENCHMARK_COUNT, System.currentTimeMillis() - start ) ); } }