libjbigi updates:

Added:
     nativeJbigiVersion()
     nativeGMPMajorVersion()
     nativeGMPMinorVersion()
     nativeGMPPatchVersion()
     nativeModInverse()
     nativeModPowCT()
   Support negative vaules in conversion functions
   Support negative base value in modPow()
   Throw ArithmeticException for bad arguments rather than crashing
   Switch to GMP 6.0.0
   New build targets
NativeBigInteger changes:
   Test improvements
   Version reporting
Crypto changes:
   Use constant time modPow() for signing and decryption
   Use native modInverse()
This commit is contained in:
zzz
2014-11-30 21:19:28 +00:00
parent 7ab6708a3c
commit a3c6cc1daa
11 changed files with 604 additions and 88 deletions

View File

@@ -33,6 +33,7 @@
<b>Platform:</b> <%=System.getProperty("os.name")%> <%=System.getProperty("os.arch")%> <%=System.getProperty("os.version")%><br>
<b>Processor:</b> <%=net.i2p.util.NativeBigInteger.cpuModel()%> (<%=net.i2p.util.NativeBigInteger.cpuType()%>)<br>
<b>Jbigi:</b> <%=net.i2p.util.NativeBigInteger.loadStatus()%><br>
<b>Jbigi version:</b> <%=net.i2p.util.NativeBigInteger.getJbigiVersion()%> GMP <%=net.i2p.util.NativeBigInteger.getLibGMPVersion()%><br>
<b>Encoding:</b> <%=System.getProperty("file.encoding")%><br>
<b>Charset:</b> <%=java.nio.charset.Charset.defaultCharset().name()%><br>
</p>

View File

@@ -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

View File

@@ -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

View File

@@ -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"

View File

@@ -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, &copy);
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 */

View File

@@ -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}"

View File

@@ -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

View File

@@ -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());

View File

@@ -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);

View File

@@ -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);

View File

@@ -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 &lt;= 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 &lt;= 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 &lt;= 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 &lt;= 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 &lt;= 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);