* Crypto: Implement and then comment out an alternate

AES-256/CBC implementation using the JVM crypto libs,
            and tests, it isn't faster
This commit is contained in:
zzz
2011-07-31 13:30:53 +00:00
parent 1bf86aff0b
commit 29ce3c7c8b

View File

@ -11,6 +11,12 @@ package net.i2p.crypto;
import java.security.InvalidKeyException; import java.security.InvalidKeyException;
// for using system version
//import java.security.GeneralSecurityException;
//import javax.crypto.Cipher;
//import javax.crypto.spec.IvParameterSpec;
//import javax.crypto.spec.SecretKeySpec;
import net.i2p.I2PAppContext; import net.i2p.I2PAppContext;
import net.i2p.data.ByteArray; import net.i2p.data.ByteArray;
import net.i2p.data.DataHelper; import net.i2p.data.DataHelper;
@ -34,18 +40,47 @@ public class CryptixAESEngine extends AESEngine {
private static final ByteCache _prevCache = ByteCache.getInstance(16, 16); private static final ByteCache _prevCache = ByteCache.getInstance(16, 16);
/**** see comments for main() below
private static final boolean USE_SYSTEM_AES;
static {
boolean systemOK = false;
try {
systemOK = Cipher.getMaxAllowedKeyLength("AES") >= 256;
} catch (GeneralSecurityException gse) {
// a NoSuchAlgorithmException
} catch (NoSuchMethodError nsme) {
// JamVM, gij
try {
Cipher cipher = Cipher.getInstance("AES/CBC/NoPadding");
SecretKeySpec key = new SecretKeySpec(new byte[32], "AES");
cipher.init(Cipher.ENCRYPT_MODE, key);
systemOK = true;
} catch (GeneralSecurityException gse) {
}
}
USE_SYSTEM_AES = systemOK;
//System.out.println("Using system AES? " + systemOK);
}
****/
public CryptixAESEngine(I2PAppContext context) { public CryptixAESEngine(I2PAppContext context) {
super(context); super(context);
//_cache = new CryptixAESKeyCache(); //_cache = new CryptixAESKeyCache();
} }
/** @param length must be a multiple of 16 */ /**
* @param iv must be 16 bytes
* @param length must be a multiple of 16
*/
@Override @Override
public void encrypt(byte payload[], int payloadIndex, byte out[], int outIndex, SessionKey sessionKey, byte iv[], int length) { public void encrypt(byte payload[], int payloadIndex, byte out[], int outIndex, SessionKey sessionKey, byte iv[], int length) {
encrypt(payload, payloadIndex, out, outIndex, sessionKey, iv, 0, length); encrypt(payload, payloadIndex, out, outIndex, sessionKey, iv, 0, length);
} }
/** @param length must be a multiple of 16 */ /**
* @param iv must be 16 bytes
* @param length must be a multiple of 16
*/
@Override @Override
public void encrypt(byte payload[], int payloadIndex, byte out[], int outIndex, SessionKey sessionKey, byte iv[], int ivOffset, int length) { public void encrypt(byte payload[], int payloadIndex, byte out[], int outIndex, SessionKey sessionKey, byte iv[], int ivOffset, int length) {
if ( (payload == null) || (out == null) || (sessionKey == null) || (iv == null) ) if ( (payload == null) || (out == null) || (sessionKey == null) || (iv == null) )
@ -65,6 +100,22 @@ public class CryptixAESEngine extends AESEngine {
return; return;
} }
/****
if (USE_SYSTEM_AES) {
try {
SecretKeySpec key = new SecretKeySpec(sessionKey.getData(), "AES");
IvParameterSpec ivps = new IvParameterSpec(iv, ivOffset, 16);
Cipher cipher = Cipher.getInstance("AES/CBC/NoPadding");
cipher.init(Cipher.ENCRYPT_MODE, key, ivps, _context.random());
cipher.doFinal(payload, payloadIndex, length, out, outIndex);
return;
} catch (GeneralSecurityException gse) {
if (_log.shouldLog(Log.WARN))
_log.warn("Java encrypt fail", gse);
}
}
****/
int numblock = length / 16; int numblock = length / 16;
DataHelper.xor(iv, ivOffset, payload, payloadIndex, out, outIndex, 16); DataHelper.xor(iv, ivOffset, payload, payloadIndex, out, outIndex, 16);
@ -79,6 +130,7 @@ public class CryptixAESEngine extends AESEngine {
public void decrypt(byte payload[], int payloadIndex, byte out[], int outIndex, SessionKey sessionKey, byte iv[], int length) { public void decrypt(byte payload[], int payloadIndex, byte out[], int outIndex, SessionKey sessionKey, byte iv[], int length) {
decrypt(payload, payloadIndex, out, outIndex, sessionKey, iv, 0, length); decrypt(payload, payloadIndex, out, outIndex, sessionKey, iv, 0, length);
} }
@Override @Override
public void decrypt(byte payload[], int payloadIndex, byte out[], int outIndex, SessionKey sessionKey, byte iv[], int ivOffset, int length) { public void decrypt(byte payload[], int payloadIndex, byte out[], int outIndex, SessionKey sessionKey, byte iv[], int ivOffset, int length) {
if ((iv== null) || (payload == null) || (payload.length <= 0) || (sessionKey == null) ) if ((iv== null) || (payload == null) || (payload.length <= 0) || (sessionKey == null) )
@ -95,6 +147,22 @@ public class CryptixAESEngine extends AESEngine {
return ; return ;
} }
/****
if (USE_SYSTEM_AES) {
try {
SecretKeySpec key = new SecretKeySpec(sessionKey.getData(), "AES");
IvParameterSpec ivps = new IvParameterSpec(iv, ivOffset, 16);
Cipher cipher = Cipher.getInstance("AES/CBC/NoPadding");
cipher.init(Cipher.DECRYPT_MODE, key, ivps, _context.random());
cipher.doFinal(payload, payloadIndex, length, out, outIndex);
return;
} catch (GeneralSecurityException gse) {
if (_log.shouldLog(Log.WARN))
_log.warn("Java decrypt fail", gse);
}
}
****/
int numblock = length / 16; int numblock = length / 16;
if (length % 16 != 0) numblock++; if (length % 16 != 0) numblock++;
@ -167,37 +235,89 @@ public class CryptixAESEngine extends AESEngine {
CryptixRijndael_Algorithm.blockDecrypt(payload, rv, inIndex, outIndex, sessionKey.getPreparedKey(), 16); CryptixRijndael_Algorithm.blockDecrypt(payload, rv, inIndex, outIndex, sessionKey.getPreparedKey(), 16);
} }
/******** /******
private static final int MATCH_RUNS = 5000;
private static final int TIMING_RUNS = 10000;
******/
/**
* Test results 10K timing runs.
* July 2011 eeepc.
* Not worth enabling System version.
* And we can't get rid of Cryptix because AES-256 is unavailable
* in several JVMs.
* Make USE_SYSTEM_AES above non-final to run this.
*<pre>
* JVM Cryptix (ms) System (ms)
* Sun 8662 n/a
* OpenJDK 8616 8510
* Harmony 14732 16986
* JamVM 50013 761494 (!)
* gij 51130 761693 (!)
* jrockit 9780 n/a
*</pre>
*
*/
/*******
public static void main(String args[]) { public static void main(String args[]) {
I2PAppContext ctx = new I2PAppContext(); I2PAppContext ctx = I2PAppContext.getGlobalContext();
try { try {
boolean canTestSystem = USE_SYSTEM_AES;
if (!canTestSystem)
System.out.println("System AES 256 not available, testing Cryptix only");
testEDBlock(ctx); testEDBlock(ctx);
testEDBlock2(ctx); testEDBlock2(ctx);
testED(ctx);
testED2(ctx); testED2(ctx);
if (canTestSystem) {
System.out.println("Start Cryptix vs. System verification run of " + MATCH_RUNS);
for (int i = 0; i < MATCH_RUNS; i++) {
testED(ctx, false, true);
testED(ctx, true, false);
}
}
System.out.println("Start Cryptix run of " + TIMING_RUNS);
long start = System.currentTimeMillis();
for (int i = 0; i < TIMING_RUNS; i++) {
testED(ctx, false, false);
}
System.out.println("Cryptix took " + (System.currentTimeMillis() - start));
if (canTestSystem) {
System.out.println("Start System run of " + TIMING_RUNS);
start = System.currentTimeMillis();
for (int i = 0; i < TIMING_RUNS; i++) {
testED(ctx, true, true);
}
System.out.println("System took " + (System.currentTimeMillis() - start));
}
//testFake(ctx); //testFake(ctx);
//testNull(ctx); //testNull(ctx);
} catch (Exception e) { } catch (Exception e) {
e.printStackTrace(); e.printStackTrace();
} }
try { Thread.sleep(5*1000); } catch (InterruptedException ie) {} //try { Thread.sleep(5*1000); } catch (InterruptedException ie) {}
} }
private static void testED(I2PAppContext ctx) {
private static final byte[] _iv = new byte[16];
private static byte[] _orig = new byte[1024];
private static byte[] _encrypted = new byte[1024];
private static byte[] _decrypted = new byte[1024];
private static void testED(I2PAppContext ctx, boolean systemEnc, boolean systemDec) {
SessionKey key = ctx.keyGenerator().generateSessionKey(); SessionKey key = ctx.keyGenerator().generateSessionKey();
byte iv[] = new byte[16]; ctx.random().nextBytes(_iv);
byte orig[] = new byte[128]; ctx.random().nextBytes(_orig);
byte encrypted[] = new byte[128];
byte decrypted[] = new byte[128];
ctx.random().nextBytes(iv);
ctx.random().nextBytes(orig);
CryptixAESEngine aes = new CryptixAESEngine(ctx); CryptixAESEngine aes = new CryptixAESEngine(ctx);
aes.encrypt(orig, 0, encrypted, 0, key, iv, orig.length); USE_SYSTEM_AES = systemEnc;
aes.decrypt(encrypted, 0, decrypted, 0, key, iv, encrypted.length); aes.encrypt(_orig, 0, _encrypted, 0, key, _iv, _orig.length);
if (!DataHelper.eq(decrypted,orig)) USE_SYSTEM_AES = systemDec;
aes.decrypt(_encrypted, 0, _decrypted, 0, key, _iv, _encrypted.length);
if (!DataHelper.eq(_decrypted,_orig))
throw new RuntimeException("full D(E(orig)) != orig"); throw new RuntimeException("full D(E(orig)) != orig");
else //else
System.out.println("full D(E(orig)) == orig"); // System.out.println("full D(E(orig)) == orig");
} }
// this verifies decryption in-place
private static void testED2(I2PAppContext ctx) { private static void testED2(I2PAppContext ctx) {
SessionKey key = ctx.keyGenerator().generateSessionKey(); SessionKey key = ctx.keyGenerator().generateSessionKey();
byte iv[] = new byte[16]; byte iv[] = new byte[16];
@ -213,6 +333,7 @@ public class CryptixAESEngine extends AESEngine {
else else
System.out.println("full D(E(orig)) == orig"); System.out.println("full D(E(orig)) == orig");
} }
private static void testFake(I2PAppContext ctx) { private static void testFake(I2PAppContext ctx) {
SessionKey key = ctx.keyGenerator().generateSessionKey(); SessionKey key = ctx.keyGenerator().generateSessionKey();
SessionKey wrongKey = ctx.keyGenerator().generateSessionKey(); SessionKey wrongKey = ctx.keyGenerator().generateSessionKey();
@ -230,6 +351,7 @@ public class CryptixAESEngine extends AESEngine {
else else
System.out.println("full D(E(orig)) != orig when we used the wrong key"); System.out.println("full D(E(orig)) != orig when we used the wrong key");
} }
private static void testNull(I2PAppContext ctx) { private static void testNull(I2PAppContext ctx) {
SessionKey key = ctx.keyGenerator().generateSessionKey(); SessionKey key = ctx.keyGenerator().generateSessionKey();
SessionKey wrongKey = ctx.keyGenerator().generateSessionKey(); SessionKey wrongKey = ctx.keyGenerator().generateSessionKey();
@ -249,6 +371,7 @@ public class CryptixAESEngine extends AESEngine {
throw new RuntimeException("full D(E(orig)) didn't fail when we used null!"); throw new RuntimeException("full D(E(orig)) didn't fail when we used null!");
} }
private static void testEDBlock(I2PAppContext ctx) { private static void testEDBlock(I2PAppContext ctx) {
SessionKey key = ctx.keyGenerator().generateSessionKey(); SessionKey key = ctx.keyGenerator().generateSessionKey();
byte iv[] = new byte[16]; byte iv[] = new byte[16];
@ -265,6 +388,7 @@ public class CryptixAESEngine extends AESEngine {
else else
System.out.println("block D(E(orig)) == orig"); System.out.println("block D(E(orig)) == orig");
} }
private static void testEDBlock2(I2PAppContext ctx) { private static void testEDBlock2(I2PAppContext ctx) {
SessionKey key = ctx.keyGenerator().generateSessionKey(); SessionKey key = ctx.keyGenerator().generateSessionKey();
byte iv[] = new byte[16]; byte iv[] = new byte[16];