diff --git a/apps/routerconsole/jsp/logs.jsp b/apps/routerconsole/jsp/logs.jsp index 99d518cbb..c8dc1c1d1 100644 --- a/apps/routerconsole/jsp/logs.jsp +++ b/apps/routerconsole/jsp/logs.jsp @@ -33,6 +33,7 @@ Platform: <%=System.getProperty("os.name")%> <%=System.getProperty("os.arch")%> <%=System.getProperty("os.version")%>
Processor: <%=net.i2p.util.NativeBigInteger.cpuModel()%> (<%=net.i2p.util.NativeBigInteger.cpuType()%>)
Jbigi: <%=net.i2p.util.NativeBigInteger.loadStatus()%>
+Jbigi version: <%=net.i2p.util.NativeBigInteger.getJbigiVersion()%> GMP <%=net.i2p.util.NativeBigInteger.getLibGMPVersion()%>
Encoding: <%=System.getProperty("file.encoding")%>
Charset: <%=java.nio.charset.Charset.defaultCharset().name()%>

diff --git a/core/c/jbigi/build-all.sh b/core/c/jbigi/build-all.sh index 04c3180fc..76d8b0821 100755 --- a/core/c/jbigi/build-all.sh +++ b/core/c/jbigi/build-all.sh @@ -1,4 +1,8 @@ #!/bin/sh +# +# NOTE: +# This script is not supported - see mbuild-all.sh +# case `uname -sr` in MINGW*) @@ -14,21 +18,27 @@ FreeBSD*) exit;; esac -VER=4.2.4 -echo "Extracting GMP Version $VER ..." -tar -xjf gmp-$VER.tar.bz2 -echo "Building..." -mkdir bin -mkdir lib -mkdir lib/net -mkdir lib/net/i2p -mkdir lib/net/i2p/util +VER=6.0.0 +TARVER=${VER}a +TAR=gmp-${TARVER}.tar.bz2 -for x in none pentium pentiummmx pentium2 pentium3 pentium4 k6 k62 k63 athlon geode pentiumm core2 +echo "Extracting GMP Version $VER ..." +tar -xjf $TAR +echo "Building..." +mkdir -p lib/net/i2p/util + +# +# look in configure file in gmp source for supported host CPUs, at about line 5000 +# +# +for x in \ + none pentium pentiummmx pentium2 pentium3 pentium4 k6 k62 k63 athlon geode pentiumm core2 \ + athlon64 k10 bobcat jaguar bulldozer piledriver steamroller excavator corei atom nano do - mkdir bin/$x + mkdir -p bin/$x cd bin/$x - ../../gmp-$VER/configure --build=$x + ../../gmp-$VER/configure --with-pic --build=$x + make clean make sh ../../build_jbigi.sh static case `uname -sr` in diff --git a/core/c/jbigi/build.sh b/core/c/jbigi/build.sh index 643014ff6..dcde001a3 100755 --- a/core/c/jbigi/build.sh +++ b/core/c/jbigi/build.sh @@ -22,7 +22,9 @@ mkdir -p lib bin/local # Use 4.3.2 32bit CPUs. # Use 5.0.2 64bit CPUs. -VER=4.3.2 +VER=6.0.0 +TARVER=${VER}a +TAR=gmp-${TARVER}.tar.bz2 # If JAVA_HOME isn't set, try to figure it out on our own [ -z $JAVA_HOME ] && . ../find-java-home @@ -37,13 +39,14 @@ set -e download_gmp () { +URL="https://gmplib.org/download/gmp/${TAR}" if [ $(which wget) ]; then - echo "Downloading ftp://ftp.gmplib.org/pub/gmp-${VER}/${TAR}" - wget -N --progress=dot ftp://ftp.gmplib.org/pub/gmp-${VER}/${TAR} + echo "Downloading $URL" + wget -N --progress=dot $URL else echo "ERROR: Cannot find wget." >&2 echo >&2 - echo "Please download ftp://ftp.gmplib.org/pub/gmp-${VER}/${TAR}" >&2 + echo "Please download $URL" >&2 echo "manually and rerun this script." >&2 exit 1 fi @@ -54,8 +57,6 @@ extract_gmp () tar -xjf ${TAR} > /dev/null 2>&1|| (rm -f ${TAR} && download_gmp && extract_gmp || exit 1) } -TAR=gmp-${VER}.tar.bz2 - if [ "$1" != "dynamic" -a ! -d gmp-${VER} ]; then if [ ! -f $TAR ]; then download_gmp diff --git a/core/c/jbigi/build_jbigi.sh b/core/c/jbigi/build_jbigi.sh index 1521392bb..d09446e09 100755 --- a/core/c/jbigi/build_jbigi.sh +++ b/core/c/jbigi/build_jbigi.sh @@ -5,7 +5,7 @@ [ -z "$CC" ] && CC="gcc" # If JAVA_HOME isn't set we'll try to figure it out -[ -z $JAVA_HOME ] && . ../find-java-home +[ -z $JAVA_HOME ] && . `dirname $0`/../find-java-home if [ ! -f "$JAVA_HOME/include/jni.h" ]; then echo "Cannot find jni.h! Looked in '$JAVA_HOME/include/jni.h'" echo "Please set JAVA_HOME to a java home that has the JNI" diff --git a/core/c/jbigi/jbigi/src/jbigi.c b/core/c/jbigi/jbigi/src/jbigi.c index f605c6a0e..f1d15d14a 100644 --- a/core/c/jbigi/jbigi/src/jbigi.c +++ b/core/c/jbigi/jbigi/src/jbigi.c @@ -7,11 +7,56 @@ void convert_j2mp(JNIEnv* env, jbyteArray jvalue, mpz_t* mvalue); void convert_mp2j(JNIEnv* env, mpz_t mvalue, jbyteArray* jvalue); +/* + * Versions: + * + * 1: Original version, with nativeModPow() and nativeDoubleValue() + * + * 2: (I2P 0.8.7) + * Removed nativeDoubleValue() + * + * 3: (I2P 0.9.18) + * Added: + * nativeJbigiVersion() + * nativeGMPMajorVersion() + * nativeGMPMinorVersion() + * nativeGMPPatchVersion() + * nativeModInverse() + * nativeModPowCT() + * Support negative base value in modPow() + * Throw ArithmeticException for bad arguments in modPow() + * + */ +#define JBIGI_VERSION 3 /***************************************** *****Native method implementations******* *****************************************/ +/* since version 3 */ +JNIEXPORT jint JNICALL Java_net_i2p_util_NativeBigInteger_nativeJbigiVersion + (JNIEnv* env, jclass cls) { + return (jint) JBIGI_VERSION; +} + +/* since version 3 */ +JNIEXPORT jint JNICALL Java_net_i2p_util_NativeBigInteger_nativeGMPMajorVersion + (JNIEnv* env, jclass cls) { + return (jint) __GNU_MP_VERSION; +} + +/* since version 3 */ +JNIEXPORT jint JNICALL Java_net_i2p_util_NativeBigInteger_nativeGMPMinorVersion + (JNIEnv* env, jclass cls) { + return (jint) __GNU_MP_VERSION_MINOR; +} + +/* since version 3 */ +JNIEXPORT jint JNICALL Java_net_i2p_util_NativeBigInteger_nativeGMPPatchVersion + (JNIEnv* env, jclass cls) { + return (jint) __GNU_MP_VERSION_PATCHLEVEL; +} + /******** nativeModPow() */ /* * Class: net_i2p_util_NativeBigInteger @@ -21,10 +66,17 @@ void convert_mp2j(JNIEnv* env, mpz_t mvalue, jbyteArray* jvalue); * From the javadoc: * * calculate (base ^ exponent) % modulus. - * @param curVal big endian twos complement representation of the base (but it must be positive) - * @param exponent big endian twos complement representation of the exponent - * @param modulus big endian twos complement representation of the modulus + * @param jbase big endian twos complement representation of the base + * Negative values allowed as of version 3 + * @param jexp big endian twos complement representation of the exponent + * Must be greater than or equal to zero. + * As of version 3, throws java.lang.ArithmeticException if < 0. + * @param jmod big endian twos complement representation of the modulus + * Must be greater than zero. + * As of version 3, throws java.lang.ArithmeticException if <= 0. + * Prior to version 3, crashed the JVM if <= 0. * @return big endian twos complement representation of (base ^ exponent) % modulus + * @throws java.lang.ArithmeticException if jmod is <= 0 */ JNIEXPORT jbyteArray JNICALL Java_net_i2p_util_NativeBigInteger_nativeModPow @@ -39,24 +91,203 @@ JNIEXPORT jbyteArray JNICALL Java_net_i2p_util_NativeBigInteger_nativeModPow mpz_t mmod; jbyteArray jresult; - convert_j2mp(env, jbase, &mbase); - convert_j2mp(env, jexp, &mexp); convert_j2mp(env, jmod, &mmod); + if (mpz_sgn(mmod) <= 0) { + mpz_clear(mmod); + jclass exc = (*env)->FindClass(env, "java/lang/ArithmeticException"); + (*env)->ThrowNew(env, exc, "Modulus must be positive"); + return 0; + } + + // disallow negative exponents to avoid divide by zero exception if no inverse exists + convert_j2mp(env, jexp, &mexp); + if (mpz_sgn(mexp) < 0) { + mpz_clears(mmod, mexp, 0); + jclass exc = (*env)->FindClass(env, "java/lang/ArithmeticException"); + (*env)->ThrowNew(env, exc, "Exponent cannot be negative"); + return 0; + } + + convert_j2mp(env, jbase, &mbase); - /* Perform the actual powmod. We use mmod for the result because it is + /* Perform the actual powmod. We use mmod for the result because it is * always at least as big as the result. */ mpz_powm(mmod, mbase, mexp, mmod); - convert_mp2j(env, mmod, &jresult); + convert_mp2j(env, mmod, &jresult); - mpz_clear(mbase); - mpz_clear(mexp); - mpz_clear(mmod); + mpz_clears(mbase, mexp, mmod, 0); return jresult; } +/******** nativeModPowCT() */ +/* + * Class: net_i2p_util_NativeBigInteger + * Method: nativeModPowCT + * Signature: ([B[B[B)[B + * + * Constant time version of nativeModPow() + * + * From the javadoc: + * + * calculate (base ^ exponent) % modulus. + * @param jbase big endian twos complement representation of the base + * Negative values allowed. + * @param jexp big endian twos complement representation of the exponent + * Must be positive. + * @param jmod big endian twos complement representation of the modulus + * Must be positive and odd. + * @return big endian twos complement representation of (base ^ exponent) % modulus + * @throws java.lang.ArithmeticException if jmod or jexp is <= 0, or jmod is even. + * @since version 3 + */ + +JNIEXPORT jbyteArray JNICALL Java_net_i2p_util_NativeBigInteger_nativeModPowCT + (JNIEnv* env, jclass cls, jbyteArray jbase, jbyteArray jexp, jbyteArray jmod) { + + mpz_t mbase; + mpz_t mexp; + mpz_t mmod; + jbyteArray jresult; + + convert_j2mp(env, jmod, &mmod); + if (mpz_sgn(mmod) <= 0) { + mpz_clear(mmod); + jclass exc = (*env)->FindClass(env, "java/lang/ArithmeticException"); + (*env)->ThrowNew(env, exc, "Modulus must be positive"); + return 0; + } + // disallow even modulus as specified in the GMP docs + if (mpz_odd_p(mmod) == 0) { + mpz_clear(mmod); + jclass exc = (*env)->FindClass(env, "java/lang/ArithmeticException"); + (*env)->ThrowNew(env, exc, "Modulus must be odd"); + return 0; + } + + // disallow negative or zero exponents as specified in the GMP docs + convert_j2mp(env, jexp, &mexp); + if (mpz_sgn(mexp) <= 0) { + mpz_clears(mmod, mexp, 0); + jclass exc = (*env)->FindClass(env, "java/lang/ArithmeticException"); + (*env)->ThrowNew(env, exc, "Exponent must be positive"); + return 0; + } + + convert_j2mp(env, jbase, &mbase); + + mpz_powm_sec(mmod, mbase, mexp, mmod); + + convert_mp2j(env, mmod, &jresult); + + mpz_clears(mbase, mexp, mmod, 0); + + return jresult; +} + +/******** nativeModInverse() */ +/* + * Class: net_i2p_util_NativeBigInteger + * Method: nativeModInverse + * Signature: ([B[B)[B + * + * From the javadoc: + * + * calculate (base ^ -1) % modulus. + * @param jbase big endian twos complement representation of the base + * Negative values allowed + * @param jmod big endian twos complement representation of the modulus + * Zero or Negative values will throw a java.lang.ArithmeticException + * @return big endian twos complement representation of (base ^ exponent) % modulus + * @throws java.lang.ArithmeticException if jbase and jmod are not coprime or jmod is <= 0 + * @since version 3 + */ + +JNIEXPORT jbyteArray JNICALL Java_net_i2p_util_NativeBigInteger_nativeModInverse + (JNIEnv* env, jclass cls, jbyteArray jbase, jbyteArray jmod) { + + mpz_t mbase; + mpz_t mexp; + mpz_t mmod; + mpz_t mgcd; + jbyteArray jresult; + + convert_j2mp(env, jmod, &mmod); + + if (mpz_sgn(mmod) <= 0) { + mpz_clear(mmod); + jclass exc = (*env)->FindClass(env, "java/lang/ArithmeticException"); + (*env)->ThrowNew(env, exc, "Modulus must be positive"); + return 0; + } + + convert_j2mp(env, jbase, &mbase); + mpz_init_set_si(mexp, -1); + + /* We must protect the jvm by doing a gcd test first. + * If the arguments are not coprime, GMP will throw a divide by zero + * and crash the JVM. + * We could test in Java using BigInteger.gcd() but it is almost as slow + * as the Java modInverse() itself, thus defeating the point. + * Unfortunately, this almost doubles our time here too. + */ + mpz_init(mgcd); + mpz_gcd(mgcd, mbase, mmod); + if (mpz_cmp_si(mgcd, 1) != 0) { + mpz_clears(mbase, mexp, mmod, mgcd, 0); + jclass exc = (*env)->FindClass(env, "java/lang/ArithmeticException"); + (*env)->ThrowNew(env, exc, "Not coprime in nativeModInverse()"); + return 0; + } + + /* Perform the actual powmod. We use mmod for the result because it is + * always at least as big as the result. + */ + mpz_powm(mmod, mbase, mexp, mmod); + + convert_mp2j(env, mmod, &jresult); + + mpz_clears(mbase, mexp, mmod, mgcd, 0); + + return jresult; +} + +/******** nativeNeg() */ +/* since version 3 */ +/* + * Class: net_i2p_util_NativeBigInteger + * Method: nativeNeg + * Signature: ([B)[B + * + * For testing of the conversion functions only! + * + * calculate n mod d + * @param n big endian twos complement representation + * @return big endian twos complement representation of -n + */ + +/**** +JNIEXPORT jbyteArray JNICALL Java_net_i2p_util_NativeBigInteger_nativeNeg + (JNIEnv* env, jclass cls, jbyteArray jn) { + + mpz_t mn; + jbyteArray jresult; + + convert_j2mp(env, jn, &mn); + + // result to mn + mpz_neg(mn, mn); + + convert_mp2j(env, mn, &jresult); + + mpz_clear(mn); + + return jresult; +} +****/ + /****************************** *****Conversion methods******* ******************************/ @@ -70,13 +301,15 @@ JNIEXPORT jbyteArray JNICALL Java_net_i2p_util_NativeBigInteger_nativeModPow * Initializes the GMP value with enough preallocated size, and converts the * Java value into the GMP value. The value that mvalue points to should be * uninitialized + * + * As of version 3, negative values are correctly converted. */ void convert_j2mp(JNIEnv* env, jbyteArray jvalue, mpz_t* mvalue) { jsize size; jbyte* jbuffer; - //int sign; + mpz_t mask; size = (*env)->GetArrayLength(env, jvalue); jbuffer = (*env)->GetByteArrayElements(env, jvalue, NULL); @@ -98,12 +331,22 @@ void convert_j2mp(JNIEnv* env, jbyteArray jvalue, mpz_t* mvalue) * be 0 to use the full words. */ mpz_import(*mvalue, size, 1, sizeof(jbyte), 1, 0, (void*)jbuffer); - /*Uncomment this to support negative integer values, - not tested though.. - sign = jbuffer[0] < 0?-1:1; - if(sign == -1) - mpz_neg(*mvalue,*mvalue); - */ + if (jbuffer[0] < 0) { + // ones complement, making a negative number + mpz_com(*mvalue, *mvalue); + // construct the mask needed to get rid of the new high bit + mpz_init_set_ui(mask, 1); + mpz_mul_2exp(mask, mask, size * 8); + mpz_sub_ui(mask, mask, 1); + // mask off the high bits, making a postive number (the magnitude, off by one) + mpz_and(*mvalue, *mvalue, mask); + // release the mask + mpz_clear(mask); + // add one to get the correct magnitude + mpz_add_ui(*mvalue, *mvalue, 1); + // invert to a negative number + mpz_neg(*mvalue, *mvalue); + } (*env)->ReleaseByteArrayElements(env, jvalue, jbuffer, JNI_ABORT); } @@ -112,6 +355,8 @@ void convert_j2mp(JNIEnv* env, jbyteArray jvalue, mpz_t* mvalue) * Converts the GMP value into the Java value; Doesn't do anything else. * Pads the resulting jbyte array with 0, so the twos complement value is always * positive. + * + * As of version 3, negative values are correctly converted. */ void convert_mp2j(JNIEnv* env, mpz_t mvalue, jbyteArray* jvalue) @@ -121,33 +366,39 @@ void convert_mp2j(JNIEnv* env, mpz_t mvalue, jbyteArray* jvalue) size_t size; jbyte* buffer; jboolean copy; - //int i; + int i; + int neg; copy = JNI_FALSE; + neg = mpz_sgn(mvalue) < 0; + if (neg) { + // add 1... + // have to do this before we calculate the size! + mpz_add_ui(mvalue, mvalue, 1); + } + /* sizeinbase() + 7 => Ceil division */ size = (mpz_sizeinbase(mvalue, 2) + 7) / 8 + sizeof(jbyte); *jvalue = (*env)->NewByteArray(env, size); buffer = (*env)->GetByteArrayElements(env, *jvalue, ©); buffer[0] = 0x00; - //Uncomment the comments below to support negative integer values, - //not very well-tested though.. - //if(mpz_sgn(mvalue) >=0){ - mpz_export((void*)&buffer[1], &size, 1, sizeof(jbyte), 1, 0, mvalue); - //}else{ - // mpz_add_ui(mvalue,mvalue,1); - // mpz_export((void*)&buffer[1], &size, 1, sizeof(jbyte), 1, 0, mvalue); - // for(i =0;i<=size;i++){ //This could be done more effectively - // buffer[i]=~buffer[i]; - // } - //} - /* mode has (supposedly) no effect if elems is not a copy of the + if (!neg) { + mpz_export((void*)&buffer[1], NULL, 1, sizeof(jbyte), 1, 0, mvalue); + } else { + mpz_export((void*)&buffer[1], NULL, 1, sizeof(jbyte), 1, 0, mvalue); + // ... and invert the bits + // This could be done all in mpz, the reverse of the above + for (i = 0; i <= size; i++) { + buffer[i] = ~buffer[i]; + } + } + + /* mode has (supposedly) no effect if elems is not a copy of the * elements in array */ (*env)->ReleaseByteArrayElements(env, *jvalue, buffer, 0); //mode has (supposedly) no effect if elems is not a copy of the elements in array } - -/******** eof */ diff --git a/core/c/jbigi/mbuild-all.sh b/core/c/jbigi/mbuild-all.sh index 1da36f67c..b9fb94447 100755 --- a/core/c/jbigi/mbuild-all.sh +++ b/core/c/jbigi/mbuild-all.sh @@ -1,10 +1,18 @@ #!/bin/sh +# +# Run with BITS=32 to generate 32-bit libs on a 64-bit platform +# On Ubuntu you will need sudo apt-get install gcc-multilib libc6-i386 libc6-dev-i386 +# # ON Solaris 11 (at least) this variable must be set. # Linux and *BSD will do the right thing. # #BITS=32 +# +# look in configure file in gmp source for supported host CPUs, at about line 5000 +# + # FIXME Is this all? DARWIN_PLATFORMS="core2 corei" MISC_DARWIN_PLATFORMS="powerpc powerpc64 powerpc64le powerpcle" @@ -36,9 +44,19 @@ MISC_MINGW_PLATFORMS="" # # Are there any other X86 platforms that work on i2p? Add them here. # - # Note! these build on 32bit as 32bit when operating as 32bit... -X86_64_PLATFORMS="atom athlon64 core2 corei nano pentium4" +# starting with k10 added for 6.0.0 +# As of GMP 6.0.0, libgmp 3, +# the following architectures appear to build to identical code: +# core2 corei +# bulldozer piledriver streamroller excavator +# bobcat jaguar +# k62 k63 +# viac32 pentium3 +# +# Even more duplicates are in 32-bit mode, so it doesn't pay to have everything for 32 bit. +# +X86_64_PLATFORMS="atom athlon64 core2 corei nano pentium4 k10 bobcat jaguar bulldozer piledriver steamroller excavator" # Note! these are 32bit _ONLY_ X86_PLATFORMS="pentium pentiummmx pentium2 pentium3 pentiumm k6 k62 k63 athlon geode viac3 viac32 ${X86_64_PLATFORMS}" diff --git a/core/java/src/net/i2p/crypto/CryptoConstants.java b/core/java/src/net/i2p/crypto/CryptoConstants.java index 21ebe3c1d..5518a4c70 100644 --- a/core/java/src/net/i2p/crypto/CryptoConstants.java +++ b/core/java/src/net/i2p/crypto/CryptoConstants.java @@ -39,20 +39,20 @@ import net.i2p.util.NativeBigInteger; * Primes for DSA: Generated by TheCrypto http://article.gmane.org/gmane.comp.security.invisiblenet.iip.devel/343 */ public class CryptoConstants { - public static final BigInteger dsap = new NativeBigInteger( + public static final NativeBigInteger dsap = new NativeBigInteger( "9c05b2aa960d9b97b8931963c9cc9e8c3026e9b8ed92fad0a69cc886d5bf8015fcadae31" + "a0ad18fab3f01b00a358de237655c4964afaa2b337e96ad316b9fb1cc564b5aec5b69a9f" + "f6c3e4548707fef8503d91dd8602e867e6d35d2235c1869ce2479c3b9d5401de04e0727f" + "b33d6511285d4cf29538d9e3b6051f5b22cc1c93", 16); - public static final BigInteger dsaq = new NativeBigInteger("a5dfc28fef4ca1e286744cd8eed9d29d684046b7", 16); - public static final BigInteger dsag = new NativeBigInteger( + public static final NativeBigInteger dsaq = new NativeBigInteger("a5dfc28fef4ca1e286744cd8eed9d29d684046b7", 16); + public static final NativeBigInteger dsag = new NativeBigInteger( "c1f4d27d40093b429e962d7223824e0bbc47e7c832a39236fc683af84889581075ff9082" + "ed32353d4374d7301cda1d23c431f4698599dda02451824ff369752593647cc3ddc197de" + "985e43d136cdcfc6bd5409cd2f450821142a5e6f8eb1c3ab5d0484b8129fcf17bce4f7f3" + "3321c3cb3dbb14a905e7b2b3e93be4708cbcc82", 16); - public static final BigInteger elgp = new NativeBigInteger("FFFFFFFFFFFFFFFFC90FDAA22168C234C4C6628B80DC1CD1" + public static final NativeBigInteger elgp = new NativeBigInteger("FFFFFFFFFFFFFFFFC90FDAA22168C234C4C6628B80DC1CD1" + "29024E088A67CC74020BBEA63B139B22514A08798E3404DD" + "EF9519B3CD3A431B302B0A6DF25F14374FE1356D6D51C245" + "E485B576625E7EC6F44C42E9A637ED6B0BFF5CB6F406B7ED" @@ -63,7 +63,7 @@ public class CryptoConstants { + "E39E772C180E86039B2783A2EC07A28FB5C55DF06F4C52C9" + "DE2BCBF6955817183995497CEA956AE515D2261898FA0510" + "15728E5A8AACAA68FFFFFFFFFFFFFFFF", 16); - public static final BigInteger elgg = new NativeBigInteger("2"); + public static final NativeBigInteger elgg = new NativeBigInteger("2"); /** * @since 0.9.9 diff --git a/core/java/src/net/i2p/crypto/DSAEngine.java b/core/java/src/net/i2p/crypto/DSAEngine.java index e4a8e6758..07118508d 100644 --- a/core/java/src/net/i2p/crypto/DSAEngine.java +++ b/core/java/src/net/i2p/crypto/DSAEngine.java @@ -234,7 +234,7 @@ public class DSAEngine { BigInteger s = new NativeBigInteger(1, sbytes); BigInteger r = new NativeBigInteger(1, rbytes); BigInteger y = new NativeBigInteger(1, verifyingKey.getData()); - BigInteger w = null; + BigInteger w; try { w = s.modInverse(CryptoConstants.dsaq); } catch (ArithmeticException ae) { @@ -405,13 +405,13 @@ public class DSAEngine { boolean ok = false; do { - k = new BigInteger(160, _context.random()); + k = new NativeBigInteger(160, _context.random()); ok = k.compareTo(CryptoConstants.dsaq) != 1; ok = ok && !k.equals(BigInteger.ZERO); //System.out.println("K picked (ok? " + ok + "): " + k.bitLength() + ": " + k.toString()); } while (!ok); - BigInteger r = CryptoConstants.dsag.modPow(k, CryptoConstants.dsap).mod(CryptoConstants.dsaq); + BigInteger r = CryptoConstants.dsag.modPowCT(k, CryptoConstants.dsap).mod(CryptoConstants.dsaq); BigInteger kinv = k.modInverse(CryptoConstants.dsaq); BigInteger M = new NativeBigInteger(1, hash.getData()); diff --git a/core/java/src/net/i2p/crypto/ECUtil.java b/core/java/src/net/i2p/crypto/ECUtil.java index 8d2228480..2dab51b72 100644 --- a/core/java/src/net/i2p/crypto/ECUtil.java +++ b/core/java/src/net/i2p/crypto/ECUtil.java @@ -52,7 +52,10 @@ class ECUtil { else if (s.equals(ECPoint.POINT_INFINITY)) return r; BigInteger prime = ((ECFieldFp) curve.getField()).getP(); - BigInteger slope = (r.getAffineY().subtract(s.getAffineY())).multiply(r.getAffineX().subtract(s.getAffineX()).modInverse(prime)).mod(prime); + // use NBI modInverse(); + BigInteger tmp = r.getAffineX().subtract(s.getAffineX()); + tmp = new NativeBigInteger(tmp); + BigInteger slope = (r.getAffineY().subtract(s.getAffineY())).multiply(tmp.modInverse(prime)).mod(prime); slope = new NativeBigInteger(slope); BigInteger xOut = (slope.modPow(TWO, prime).subtract(r.getAffineX())).subtract(s.getAffineX()).mod(prime); BigInteger yOut = s.getAffineY().negate().mod(prime); @@ -67,7 +70,10 @@ class ECUtil { BigInteger slope = (r.getAffineX().pow(2)).multiply(THREE); slope = slope.add(curve.getA()); BigInteger prime = ((ECFieldFp) curve.getField()).getP(); - slope = slope.multiply((r.getAffineY().multiply(TWO)).modInverse(prime)); + // use NBI modInverse(); + BigInteger tmp = r.getAffineY().multiply(TWO); + tmp = new NativeBigInteger(tmp); + slope = slope.multiply(tmp.modInverse(prime)); BigInteger xOut = slope.pow(2).subtract(r.getAffineX().multiply(TWO)).mod(prime); BigInteger yOut = (r.getAffineY().negate()).add(slope.multiply(r.getAffineX().subtract(xOut))).mod(prime); ECPoint out = new ECPoint(xOut, yOut); diff --git a/core/java/src/net/i2p/crypto/ElGamalEngine.java b/core/java/src/net/i2p/crypto/ElGamalEngine.java index a80e0a99e..7d566eee7 100644 --- a/core/java/src/net/i2p/crypto/ElGamalEngine.java +++ b/core/java/src/net/i2p/crypto/ElGamalEngine.java @@ -197,8 +197,8 @@ public class ElGamalEngine { // we use this buf first for Y, then for D, then for the hash byte[] buf = SimpleByteCache.acquire(257); System.arraycopy(encrypted, 0, buf, 0, 257); - BigInteger y = new NativeBigInteger(1, buf); - BigInteger ya = y.modPow(y1p, CryptoConstants.elgp); + NativeBigInteger y = new NativeBigInteger(1, buf); + BigInteger ya = y.modPowCT(y1p, CryptoConstants.elgp); System.arraycopy(encrypted, 257, buf, 0, 257); BigInteger d = new NativeBigInteger(1, buf); BigInteger m = ya.multiply(d); diff --git a/core/java/src/net/i2p/util/NativeBigInteger.java b/core/java/src/net/i2p/util/NativeBigInteger.java index e9f16d3c5..dacc143d5 100644 --- a/core/java/src/net/i2p/util/NativeBigInteger.java +++ b/core/java/src/net/i2p/util/NativeBigInteger.java @@ -97,6 +97,10 @@ import net.i2p.crypto.CryptoConstants; public class NativeBigInteger extends BigInteger { /** did we load the native lib correctly? */ private static boolean _nativeOk; + /** is native lib loaded and at least version 3? */ + private static boolean _nativeOk3; + private static int _jbigiVersion; + private static String _libGMPVersion = "unknown"; private static String _loadStatus = "uninitialized"; private static String _cpuModel = "uninitialized"; private static String _extractedResource; @@ -277,10 +281,118 @@ public class NativeBigInteger extends BigInteger { * big endian twos complement representation of the exponent * @param modulus * big endian twos complement representation of the modulus + * @throws ArithmeticException if modulus <= 0 (since libjbigi version3) * @return big endian twos complement representation of (base ^ exponent) % modulus */ - public native static byte[] nativeModPow(byte base[], byte exponent[], byte modulus[]); + private native static byte[] nativeModPow(byte base[], byte exponent[], byte modulus[]); + + /** + * calculate (base ^ exponent) % modulus. + * Constant Time. + * + * @param base + * big endian twos complement representation of the base (but it must be positive) + * @param exponent + * big endian twos complement representation of the exponent + * @param modulus + * big endian twos complement representation of the modulus + * @return big endian twos complement representation of (base ^ exponent) % modulus + * @throws ArithmeticException if modulus <= 0 + * @since 0.9.18 and libjbigi version 3 + */ + private native static byte[] nativeModPowCT(byte base[], byte exponent[], byte modulus[]); + + /** + * @since 0.9.18 and libjbigi version 3 + * @throws ArithmeticException + */ + private native static byte[] nativeModInverse(byte base[], byte d[]); + /** + * Only for testing jbigi's negative conversion functions! + * @since 0.9.18 + */ + //private native static byte[] nativeNeg(byte d[]); + + /** + * Get the jbigi version, only available since jbigi version 3 + * Caller must catch Throwable + * @since 0.9.18 + */ + private native static int nativeJbigiVersion(); + + /** + * Get the libmp version, only available since jbigi version 3 + * @since 0.9.18 + */ + private native static int nativeGMPMajorVersion(); + + /** + * Get the libmp version, only available since jbigi version 3 + * @since 0.9.18 + */ + private native static int nativeGMPMinorVersion(); + + /** + * Get the libmp version, only available since jbigi version 3 + * @since 0.9.18 + */ + private native static int nativeGMPPatchVersion(); + + /** + * Get the jbigi version + * @return 0 if no jbigi available, 2 if version not supported + * @since 0.9.18 + */ + private static int fetchJbigiVersion() { + if (!_nativeOk) + return 0; + try { + return nativeJbigiVersion(); + } catch (Throwable t) { + return 2; + } + } + + /** + * Set the jbigi and libgmp versions. Call after loading. + * Sets _jbigiVersion, _nativeOK3, and _libGMPVersion. + * @since 0.9.18 + */ + private static void setVersions() { + _jbigiVersion = fetchJbigiVersion(); + _nativeOk3 = _jbigiVersion > 2; + if (_nativeOk3) { + try { + int maj = nativeGMPMajorVersion(); + int min = nativeGMPMinorVersion(); + int pat = nativeGMPPatchVersion(); + _libGMPVersion = maj + "." + min + "." + pat; + } catch (Throwable t) { + warn("jbigi version " + _jbigiVersion + " but GMP version not available???", t); + } + } + warn("jbigi version: " + _jbigiVersion + "; GMP version: " + _libGMPVersion); + } + + /** + * Get the jbigi version + * @return 0 if no jbigi available, 2 if version info not supported + * @since 0.9.18 + */ + public static int getJbigiVersion() { + return _jbigiVersion; + } + + /** + * Get the libgmp version + * @return "unknown" if no jbigi available or if version not supported + * @since 0.9.18 + */ + public static String getLibGMPVersion() { + return _libGMPVersion; + } + private byte[] cachedBa; public NativeBigInteger(byte[] val) { @@ -316,17 +428,50 @@ public class NativeBigInteger extends BigInteger { this(integer.toByteArray()); } + /** + * @throws ArithmeticException if m <= 0 + */ @Override public BigInteger modPow(BigInteger exponent, BigInteger m) { - // jbigi.c convert_j2mp() and convert_mp2j() do NOT currently support negative numbers // Where negative or zero values aren't legal in modPow() anyway, avoid native, - // as the Java code will throw an exception rather than silently fail - if (_nativeOk && signum() >= 0 && exponent.signum() >= 0 && m.signum() > 0) + // as the Java code will throw an exception rather than silently fail or crash the JVM + // Negative values supported as of version 3 + if (_nativeOk3 || (_nativeOk && signum() >= 0 && exponent.signum() >= 0 && m.signum() > 0)) return new NativeBigInteger(nativeModPow(toByteArray(), exponent.toByteArray(), m.toByteArray())); else return super.modPow(exponent, m); } + /** + * @throws ArithmeticException if m <= 0 + * @since 0.9.18 and libjbigi version 3 + */ + public BigInteger modPowCT(BigInteger exponent, BigInteger m) { + if (_nativeOk3) + return new NativeBigInteger(nativeModPowCT(toByteArray(), exponent.toByteArray(), m.toByteArray())); + else + return modPow(exponent, m); + } + + /** + * @throws ArithmeticException if not coprime with m, or m <= 0 + * @since 0.9.18 and libjbigi version 3 + */ + @Override + public BigInteger modInverse(BigInteger m) { + // Where negative or zero values aren't legal in modInverse() anyway, avoid native, + // as the Java code will throw an exception rather than silently fail or crash the JVM + // Note that 'this' can be negative + // If this and m are not coprime, gmp will do a divide by zero exception and crash the JVM. + // super will throw an ArithmeticException +// if (_nativeOk3 && m.signum() > 0) + if (_nativeOk3) + return new NativeBigInteger(nativeModInverse(toByteArray(), m.toByteArray())); + else + return super.modInverse(m); + } + + /** caches */ @Override public byte[] toByteArray(){ if(cachedBa == null) //Since we are immutable it is safe to never update the cached ba after it has initially been generated @@ -378,12 +523,63 @@ public class NativeBigInteger extends BigInteger { */ public static void main(String args[]) { _doLog = true; - runModPowTest(100); + //if (_nativeOk3) + // testnegs(); + runModPowTest(100, 1); + if (_nativeOk3) { + System.out.println("ModPowCT test:"); + runModPowTest(100, 2); + System.out.println("ModInverse test:"); + runModPowTest(10000, 3); + } } - private static void runModPowTest(int numRuns) { + /** version >= 3 only */ +/**** + private static void testnegs() { + for (int i = -66000; i <= 66000; i++) { + testneg(i); + } + test(3, 11); + test(25, 4); + } + + private static void testneg(long a) { + NativeBigInteger ba = new NativeBigInteger(Long.toString(a)); + long r = ba.testNegate().longValue(); + if (r != 0 - a) + warn("FAIL Neg test " + a + " = " + r); + } + + private static void test(long a, long b) { + BigInteger ba = new NativeBigInteger(Long.toString(a)); + BigInteger bb = new NativeBigInteger(Long.toString(b)); + long r1 = a * b; + long r2 = ba.multiply(bb).longValue(); + if (r1 != r2) + warn("FAIL Mul test " + a + ' ' + b + " = " + r2); + r1 = a / b; + r2 = ba.divide(bb).longValue(); + if (r1 != r2) + warn("FAIL Div test " + a + ' ' + b + " = " + r2); + r1 = a % b; + r2 = ba.mod(bb).longValue(); + if (r1 != r2) + warn("FAIL Mod test " + a + ' ' + b + " = " + r2); + } + + private BigInteger testNegate() { + return new NativeBigInteger(nativeNeg(toByteArray())); + } + +****/ + + /** + * @parm mode 1: modPow; 2: modPowCT 3: modInverse + */ + private static void runModPowTest(int numRuns, int mode) { System.out.println("DEBUG: Warming up the random number generator..."); - SecureRandom rand = new SecureRandom(); + SecureRandom rand = RandomSource.getInstance(); rand.nextBoolean(); System.out.println("DEBUG: Random number generator warmed up"); @@ -392,22 +588,42 @@ public class NativeBigInteger extends BigInteger { byte[] _samplePrime = CryptoConstants.elgp.toByteArray(); BigInteger jg = new BigInteger(_sampleGenerator); + NativeBigInteger ng = new NativeBigInteger(_sampleGenerator); BigInteger jp = new BigInteger(_samplePrime); long totalTime = 0; long javaTime = 0; int runsProcessed = 0; + for (int i = 0; i < 1000; i++) { + // JIT warmup + BigInteger bi = new NativeBigInteger(16, rand); + if (mode == 1) + jg.modPow(bi, jp); + else if (mode == 2) + ng.modPowCT(bi, jp); + else + bi.modInverse(jp); + } for (runsProcessed = 0; runsProcessed < numRuns; runsProcessed++) { - BigInteger bi = new BigInteger(226, rand); // 2048, rand); // + BigInteger bi = new BigInteger(2048, rand); // 2048, rand); // NativeBigInteger g = new NativeBigInteger(_sampleGenerator); NativeBigInteger p = new NativeBigInteger(_samplePrime); NativeBigInteger k = new NativeBigInteger(1, bi.toByteArray()); - long beforeModPow = System.currentTimeMillis(); - BigInteger myValue = g.modPow(k, p); - long afterModPow = System.currentTimeMillis(); - BigInteger jval = jg.modPow(bi, jp); - long afterJavaModPow = System.currentTimeMillis(); + BigInteger myValue, jval; + long beforeModPow = System.nanoTime(); + if (mode == 1) + myValue = g.modPow(k, p); + else if (mode == 2) + myValue = g.modPowCT(bi, jp); + else + myValue = k.modInverse(p); + long afterModPow = System.nanoTime(); + if (mode != 3) + jval = jg.modPow(bi, jp); + else + jval = bi.modInverse(jp); + long afterJavaModPow = System.nanoTime(); totalTime += (afterModPow - beforeModPow); javaTime += (afterJavaModPow - afterModPow); @@ -415,26 +631,37 @@ public class NativeBigInteger extends BigInteger { System.err.println("ERROR: [" + runsProcessed + "]\tnative modPow != java modPow"); System.err.println("ERROR: native modPow value: " + myValue.toString()); System.err.println("ERROR: java modPow value: " + jval.toString()); - System.err.println("ERROR: run time: " + totalTime + "ms (" + (totalTime / (runsProcessed + 1)) + "ms each)"); break; - } else { - System.out.println("DEBUG: current run time: " + (afterModPow - beforeModPow) + "ms (total: " - + totalTime + "ms, " + (totalTime / (runsProcessed + 1)) + "ms each)"); + //} else if (mode == 1) { + // System.out.println(String.format("DEBUG: current run time: %7.3f ms (total: %9.3f ms, %7.3f ms each)", + // (afterModPow - beforeModPow) / 1000000d, + // totalTime / 1000000d, + // totalTime / (1000000d * (runsProcessed + 1)))); } } - System.out.println("INFO: run time: " + totalTime + "ms (" + (totalTime / (runsProcessed + 1)) + "ms each)"); + double dtotal = totalTime / 1000000f; + double djava = javaTime / 1000000f; + System.out.println(String.format("INFO: run time: %.3f ms (%.3f ms each)", + dtotal, dtotal / (runsProcessed + 1))); if (numRuns == runsProcessed) System.out.println("INFO: " + runsProcessed + " runs complete without any errors"); else System.out.println("ERROR: " + runsProcessed + " runs until we got an error"); if (_nativeOk) { - System.out.println("native run time: \t" + totalTime + "ms (" + (totalTime / (runsProcessed + 1)) - + "ms each)"); - System.out.println("java run time: \t" + javaTime + "ms (" + (javaTime / (runsProcessed + 1)) + "ms each)"); - System.out.println("native = " + ((totalTime * 100.0d) / javaTime) + "% of pure java time"); + System.out.println(String.format("Native run time: \t%9.3f ms (%7.3f ms each)", + dtotal, dtotal / (runsProcessed + 1))); + System.out.println(String.format("Java run time: \t%9.3f ms (%7.3f ms each)", + djava, djava / (runsProcessed + 1))); + System.out.println(String.format("Native = %.3f%% of pure Java time", + dtotal * 100.0d / djava)); + if (dtotal < djava) + System.out.println(String.format("Native is BETTER by a factor of %.3f -- YAY!", djava / dtotal)); + else + System.out.println(String.format("Native is WORSE by a factor of %.3f -- BOO!", dtotal / djava)); } else { - System.out.println("java run time: \t" + javaTime + "ms (" + (javaTime / (runsProcessed + 1)) + "ms each)"); + System.out.println(String.format("java run time: \t%.3f ms (%.3f ms each)", + djava, djava / (runsProcessed + 1))); System.out.println("However, we couldn't load the native library, so this doesn't test much"); } } @@ -497,6 +724,8 @@ public class NativeBigInteger extends BigInteger { if (!_nativeOk) { warn("Native BigInteger library jbigi not loaded - using pure Java - " + "poor performance may result - see http://i2p-projekt.i2p/jbigi for help"); + } else { + setVersions(); } } catch(Exception e) { warn("Native BigInteger library jbigi not loaded, using pure java", e);