From 8d9790fd77e00b6ba6adf9916e1d738599a47574 Mon Sep 17 00:00:00 2001
From: zzz <zzz@mail.i2p>
Date: Fri, 9 May 2014 15:28:54 +0000
Subject: [PATCH]  * CPUID:    - Add hasAES()    - Fix bugs in unused
 hasSSE3(), hasSSE41(), hasSSE42()  * Crypto: Use JVM AES when faster  *
 SystemVersion: Add isJava7() and isX86()

---
 .../support/CPUInformation/CPUIDCPUInfo.java  | 24 +++++-
 .../support/CPUInformation/CPUInfo.java       |  6 ++
 .../src/net/i2p/crypto/CryptixAESEngine.java  | 73 +++++++++++++------
 .../src/net/i2p/util/NativeBigInteger.java    |  3 +-
 core/java/src/net/i2p/util/SystemVersion.java | 23 ++++++
 5 files changed, 101 insertions(+), 28 deletions(-)

diff --git a/core/java/src/freenet/support/CPUInformation/CPUIDCPUInfo.java b/core/java/src/freenet/support/CPUInformation/CPUIDCPUInfo.java
index 07bf42f343..8515b2418b 100644
--- a/core/java/src/freenet/support/CPUInformation/CPUIDCPUInfo.java
+++ b/core/java/src/freenet/support/CPUInformation/CPUIDCPUInfo.java
@@ -2,9 +2,10 @@ package freenet.support.CPUInformation;
 
 /**
  *  Moved out of CPUID.java
+ *  Ref: http://en.wikipedia.org/wiki/CPUID
  *  @since 0.8.7
  */
-abstract class CPUIDCPUInfo
+abstract class CPUIDCPUInfo implements CPUInfo
 {
     protected static boolean isX64 = false;        
             
@@ -12,33 +13,48 @@ abstract class CPUIDCPUInfo
     {
         return CPUID.getCPUVendorID();
     }
+
     public boolean hasMMX()
     {
         return (CPUID.getEDXCPUFlags() & 0x800000) != 0; //EDX Bit 23
     }
+
     public boolean hasSSE(){
         return (CPUID.getEDXCPUFlags() & 0x2000000) != 0; //EDX Bit 25
     }
+
     public boolean hasSSE2()
     {
         return (CPUID.getEDXCPUFlags() & 0x4000000) != 0; //EDX Bit 26
     }
+
     public boolean hasSSE3()
     {
-        return (CPUID.getEDXCPUFlags() & 0x1) != 0; //ECX Bit 0
+        return (CPUID.getECXCPUFlags() & 0x1) != 0; //ECX Bit 0
     }
+
     public boolean hasSSE41()
     {
-        return (CPUID.getEDXCPUFlags() & 0x80000) != 0; //ECX Bit 19
+        return (CPUID.getECXCPUFlags() & 0x80000) != 0; //ECX Bit 19
     }
+
     public boolean hasSSE42()
     {
-        return (CPUID.getEDXCPUFlags() & 0x100000) != 0; //ECX Bit 20
+        return (CPUID.getECXCPUFlags() & 0x100000) != 0; //ECX Bit 20
     }
+
     public boolean hasSSE4A()
     {
         return (CPUID.getExtendedECXCPUFlags() & 0x40) != 0; //Extended ECX Bit 6
     }
+
+    /**
+     * @return true iff the CPU supports the AES-NI instruction set.
+     * @since 0.9.14
+     */
+    public boolean hasAES() {
+        return (CPUID.getECXCPUFlags() & 0x2000000) != 0; //ECX Bit 25
+    }
     
     public abstract boolean hasX64();
 }
diff --git a/core/java/src/freenet/support/CPUInformation/CPUInfo.java b/core/java/src/freenet/support/CPUInformation/CPUInfo.java
index e58d34beb3..472cde9e3a 100644
--- a/core/java/src/freenet/support/CPUInformation/CPUInfo.java
+++ b/core/java/src/freenet/support/CPUInformation/CPUInfo.java
@@ -62,4 +62,10 @@ public interface CPUInfo
      * @return true iff the CPU support the SSE4A instruction set.
      */
     public boolean hasSSE4A();
+
+    /**
+     * @return true iff the CPU supports the AES-NI instruction set.
+     * @since 0.9.14
+     */
+    public boolean hasAES();
 }
diff --git a/core/java/src/net/i2p/crypto/CryptixAESEngine.java b/core/java/src/net/i2p/crypto/CryptixAESEngine.java
index a5af3c2082..48e2de46e3 100644
--- a/core/java/src/net/i2p/crypto/CryptixAESEngine.java
+++ b/core/java/src/net/i2p/crypto/CryptixAESEngine.java
@@ -12,16 +12,21 @@ 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 java.security.GeneralSecurityException;
+import javax.crypto.Cipher;
+import javax.crypto.spec.IvParameterSpec;
+import javax.crypto.spec.SecretKeySpec;
+
+import freenet.support.CPUInformation.CPUID;
+import freenet.support.CPUInformation.CPUInfo;
+import freenet.support.CPUInformation.UnknownCPUException;
 
 import net.i2p.I2PAppContext;
 import net.i2p.data.DataHelper;
 import net.i2p.data.SessionKey;
 import net.i2p.util.Log;
 import net.i2p.util.SimpleByteCache;
+import net.i2p.util.SystemVersion;
 
 /** 
  * Wrapper for AES cypher operation using Cryptix's Rijndael implementation.  Implements
@@ -37,28 +42,47 @@ public class CryptixAESEngine extends AESEngine {
     // keys are now cached in the SessionKey objects
     //private CryptixAESKeyCache _cache;
     
-/**** 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
+        if (hasAESNI()) {
             try {
-                Cipher cipher = Cipher.getInstance("AES/CBC/NoPadding");
-                SecretKeySpec key = new SecretKeySpec(new byte[32], "AES");
-                cipher.init(Cipher.ENCRYPT_MODE, key);
-                systemOK = true;
+                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);
     }
-****/
+
+    /**
+     *  Do we have AES-NI support in the processor and JVM?
+     *  Only on 64-bit x86 Java 7 fast JVMs, with AES-NI support.
+     *  See comments in main() below.
+     *  @since 0.9.14
+     */
+    private static boolean hasAESNI() {
+        if (SystemVersion.isX86() && SystemVersion.is64Bit() && SystemVersion.isJava7() &&
+            !SystemVersion.isApache() && !SystemVersion.isGNU()) {
+            try {
+                return CPUID.getInfo().hasAES();
+            } catch (UnknownCPUException e) {
+                return false;
+            }
+        } else {
+            return false;
+        }
+    }
 
     /** */
     public CryptixAESEngine(I2PAppContext context) {
@@ -104,7 +128,6 @@ public class CryptixAESEngine extends AESEngine {
             return;
         }
 
-/****
         if (USE_SYSTEM_AES) {
             try {
                 SecretKeySpec key = new SecretKeySpec(sessionKey.getData(), "AES");
@@ -118,7 +141,6 @@ public class CryptixAESEngine extends AESEngine {
                     _log.warn("Java encrypt fail", gse);
             }
         }
-****/
 
         int numblock = length / 16;
         
@@ -159,7 +181,6 @@ public class CryptixAESEngine extends AESEngine {
             return ;
         }
 
-/****
         if (USE_SYSTEM_AES) {
             try {
                 SecretKeySpec key = new SecretKeySpec(sessionKey.getData(), "AES");
@@ -173,7 +194,6 @@ public class CryptixAESEngine extends AESEngine {
                     _log.warn("Java decrypt fail", gse);
             }
         }
-****/
 
         int numblock = length / 16;
         if (length % 16 != 0) {
@@ -261,8 +281,8 @@ public class CryptixAESEngine extends AESEngine {
     }
     
 /******
-    private static final int MATCH_RUNS = 5000;
-    private static final int TIMING_RUNS = 10000;
+    private static final int MATCH_RUNS = 11000;
+    private static final int TIMING_RUNS = 100000;
 ******/
 
     /**
@@ -282,6 +302,15 @@ public class CryptixAESEngine extends AESEngine {
      * jrockit	 9780		n/a
      *</pre>
      *
+     * Speed ups with AES-NI:
+     * May 2014 AMD Hexcore 100K runs:
+     *<pre>
+     *  JVM		Cryptix (ms)	System (ms)
+     * OpenJDK 6	3314		  5030
+     * OpenJDK 7	3285		  2476
+     *</pre>
+     *
+     *
      */
 /*******
     public static void main(String args[]) {
diff --git a/core/java/src/net/i2p/util/NativeBigInteger.java b/core/java/src/net/i2p/util/NativeBigInteger.java
index 954a8f6191..d970d76bdf 100644
--- a/core/java/src/net/i2p/util/NativeBigInteger.java
+++ b/core/java/src/net/i2p/util/NativeBigInteger.java
@@ -181,8 +181,7 @@ public class NativeBigInteger extends BigInteger {
      */
     private static final boolean _is64 = SystemVersion.is64Bit();
 
-    private static final boolean _isX86 = System.getProperty("os.arch").contains("86") ||
-	                                 System.getProperty("os.arch").equals("amd64");
+    private static final boolean _isX86 = SystemVersion.isX86();
 
     private static final boolean _isArm = SystemVersion.isARM();
 
diff --git a/core/java/src/net/i2p/util/SystemVersion.java b/core/java/src/net/i2p/util/SystemVersion.java
index 224b79031e..6411058db9 100644
--- a/core/java/src/net/i2p/util/SystemVersion.java
+++ b/core/java/src/net/i2p/util/SystemVersion.java
@@ -16,6 +16,8 @@ public abstract class SystemVersion {
     private static final boolean _isWin = System.getProperty("os.name").startsWith("Win");
     private static final boolean _isMac = System.getProperty("os.name").startsWith("Mac");
     private static final boolean _isArm = System.getProperty("os.arch").startsWith("arm");
+    private static final boolean _isX86 = System.getProperty("os.arch").contains("86") ||
+                                          System.getProperty("os.arch").equals("amd64");
     private static final boolean _isAndroid;
     private static final boolean _isApache;
     private static final boolean _isGNU;
@@ -23,6 +25,7 @@ public abstract class SystemVersion {
     private static final boolean _hasWrapper = System.getProperty("wrapper.version") != null;
 
     private static final boolean _oneDotSix;
+    private static final boolean _oneDotSeven;
     private static final int _androidSDK;
 
     static {
@@ -56,8 +59,10 @@ public abstract class SystemVersion {
 
         if (_isAndroid) {
             _oneDotSix = _androidSDK >= 9;
+            _oneDotSeven = _androidSDK >= 19;
         } else {
             _oneDotSix = VersionComparator.comp(System.getProperty("java.version"), "1.6") >= 0;
+            _oneDotSeven = VersionComparator.comp(System.getProperty("java.version"), "1.7") >= 0;
         }
     }
 
@@ -94,6 +99,13 @@ public abstract class SystemVersion {
         return _isArm;
     }
 
+    /**
+     *  @since 0.9.14
+     */
+    public static boolean isX86() {
+        return _isX86;
+    }
+
     /**
      *  Better than (new VersionComparator()).compare(System.getProperty("java.version"), "1.6") >= 0
      *  as it handles Android also, where java.version = "0".
@@ -104,6 +116,17 @@ public abstract class SystemVersion {
         return _oneDotSix;
     }
 
+    /**
+     *  Better than (new VersionComparator()).compare(System.getProperty("java.version"), "1.7") >= 0
+     *  as it handles Android also, where java.version = "0".
+     *
+     *  @return true if Java 1.7 or higher, or Android API 19 or higher
+     *  @since 0.9.14
+     */
+    public static boolean isJava7() {
+        return _oneDotSeven;
+    }
+
     /**
      * This isn't always correct.
      * http://stackoverflow.com/questions/807263/how-do-i-detect-which-kind-of-jre-is-installed-32bit-vs-64bit
-- 
GitLab