From 29ce3c7c8bc40965a8e931150592ef9abd1119cb Mon Sep 17 00:00:00 2001 From: zzz Date: Sun, 31 Jul 2011 13:30:53 +0000 Subject: [PATCH] * Crypto: Implement and then comment out an alternate AES-256/CBC implementation using the JVM crypto libs, and tests, it isn't faster --- .../src/net/i2p/crypto/CryptixAESEngine.java | 160 ++++++++++++++++-- 1 file changed, 142 insertions(+), 18 deletions(-) diff --git a/core/java/src/net/i2p/crypto/CryptixAESEngine.java b/core/java/src/net/i2p/crypto/CryptixAESEngine.java index 350821e35..9e1be6506 100644 --- a/core/java/src/net/i2p/crypto/CryptixAESEngine.java +++ b/core/java/src/net/i2p/crypto/CryptixAESEngine.java @@ -11,6 +11,12 @@ package net.i2p.crypto; 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.data.ByteArray; import net.i2p.data.DataHelper; @@ -34,18 +40,47 @@ public class CryptixAESEngine extends AESEngine { 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) { super(context); //_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 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); } - /** @param length must be a multiple of 16 */ + /** + * @param iv must be 16 bytes + * @param length must be a multiple of 16 + */ @Override 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) ) @@ -65,6 +100,22 @@ public class CryptixAESEngine extends AESEngine { 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; 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) { decrypt(payload, payloadIndex, out, outIndex, sessionKey, iv, 0, length); } + @Override 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) ) @@ -95,6 +147,22 @@ public class CryptixAESEngine extends AESEngine { 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; if (length % 16 != 0) numblock++; @@ -167,37 +235,89 @@ public class CryptixAESEngine extends AESEngine { 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. + *
+     *  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
+     *
+ * + */ +/******* public static void main(String args[]) { - I2PAppContext ctx = new I2PAppContext(); + I2PAppContext ctx = I2PAppContext.getGlobalContext(); try { + boolean canTestSystem = USE_SYSTEM_AES; + if (!canTestSystem) + System.out.println("System AES 256 not available, testing Cryptix only"); testEDBlock(ctx); testEDBlock2(ctx); - testED(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); //testNull(ctx); } catch (Exception e) { 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(); - byte iv[] = new byte[16]; - byte orig[] = new byte[128]; - byte encrypted[] = new byte[128]; - byte decrypted[] = new byte[128]; - ctx.random().nextBytes(iv); - ctx.random().nextBytes(orig); + ctx.random().nextBytes(_iv); + ctx.random().nextBytes(_orig); CryptixAESEngine aes = new CryptixAESEngine(ctx); - aes.encrypt(orig, 0, encrypted, 0, key, iv, orig.length); - aes.decrypt(encrypted, 0, decrypted, 0, key, iv, encrypted.length); - if (!DataHelper.eq(decrypted,orig)) + USE_SYSTEM_AES = systemEnc; + aes.encrypt(_orig, 0, _encrypted, 0, key, _iv, _orig.length); + 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"); - else - System.out.println("full D(E(orig)) == orig"); + //else + // System.out.println("full D(E(orig)) == orig"); } + + // this verifies decryption in-place private static void testED2(I2PAppContext ctx) { SessionKey key = ctx.keyGenerator().generateSessionKey(); byte iv[] = new byte[16]; @@ -213,6 +333,7 @@ public class CryptixAESEngine extends AESEngine { else System.out.println("full D(E(orig)) == orig"); } + private static void testFake(I2PAppContext ctx) { SessionKey key = ctx.keyGenerator().generateSessionKey(); SessionKey wrongKey = ctx.keyGenerator().generateSessionKey(); @@ -230,6 +351,7 @@ public class CryptixAESEngine extends AESEngine { else System.out.println("full D(E(orig)) != orig when we used the wrong key"); } + private static void testNull(I2PAppContext ctx) { SessionKey key = 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!"); } + private static void testEDBlock(I2PAppContext ctx) { SessionKey key = ctx.keyGenerator().generateSessionKey(); byte iv[] = new byte[16]; @@ -265,6 +388,7 @@ public class CryptixAESEngine extends AESEngine { else System.out.println("block D(E(orig)) == orig"); } + private static void testEDBlock2(I2PAppContext ctx) { SessionKey key = ctx.keyGenerator().generateSessionKey(); byte iv[] = new byte[16];