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