source: core/c/jbigi/jbigi/src/jbigi.c @ 126a4d8

Last change on this file since 126a4d8 was 126a4d8, checked in by zzz <zzz@…>, 4 years ago

jbigi: Fix GMP version reporting for shared library (ticket #1800)

  • Property mode set to 100644
File size: 13.4 KB
Line 
1#include <stdio.h>
2#include <string.h>
3#include <gmp.h>
4#include "jbigi.h"
5
6/******** prototypes */
7
8void convert_j2mp(JNIEnv* env, jbyteArray jvalue, mpz_t* mvalue);
9void convert_mp2j(JNIEnv* env, mpz_t mvalue, jbyteArray* jvalue);
10
11/*
12 * Versions:
13 *
14 * 1: Original version, with nativeModPow() and nativeDoubleValue()
15 *
16 * 2: (I2P 0.8.7)
17 *    Removed nativeDoubleValue()
18 *
19 * 3: (I2P 0.9.26)
20 *    Added:
21 *      nativeJbigiVersion()
22 *      nativeGMPMajorVersion()
23 *      nativeGMPMinorVersion()
24 *      nativeGMPPatchVersion()
25 *      nativeModInverse()
26 *      nativeModPowCT()
27 *    Support negative base value in modPow()
28 *    Throw ArithmeticException for bad arguments in modPow()
29 *
30 * 4: (I2P 0.9.27)
31 *    Fix nativeGMPMajorVersion(), nativeGMPMinorVersion(), and nativeGMPPatchVersion()
32 *    when built as a shared library
33 *
34 */
35#define JBIGI_VERSION 4
36
37/*****************************************
38 *****Native method implementations*******
39 *****************************************/
40
41/* since version 3 */
42JNIEXPORT jint JNICALL Java_net_i2p_util_NativeBigInteger_nativeJbigiVersion
43        (JNIEnv* env, jclass cls) {
44    return (jint) JBIGI_VERSION;
45}
46
47/* since version 3, fixed for dynamic builds in version 4 */
48JNIEXPORT jint JNICALL Java_net_i2p_util_NativeBigInteger_nativeGMPMajorVersion
49        (JNIEnv* env, jclass cls) {
50    int v = gmp_version[0] - '0';
51    return (jint) v;
52}
53
54/* since version 3, fixed for dynamic builds in version 4 */
55JNIEXPORT jint JNICALL Java_net_i2p_util_NativeBigInteger_nativeGMPMinorVersion
56        (JNIEnv* env, jclass cls) {
57    int v = 0;
58    if (strlen(gmp_version) > 2) {
59        v = gmp_version[2] - '0';
60    }
61    return (jint) v;
62}
63
64/* since version 3, fixed for dynamic builds in version 4 */
65JNIEXPORT jint JNICALL Java_net_i2p_util_NativeBigInteger_nativeGMPPatchVersion
66        (JNIEnv* env, jclass cls) {
67    int v = 0;
68    if (strlen(gmp_version) > 4) {
69        v = gmp_version[4] - '0';
70    }
71    return (jint) v;
72}
73
74/******** nativeModPow() */
75/*
76 * Class:     net_i2p_util_NativeBigInteger
77 * Method:    nativeModPow
78 * Signature: ([B[B[B)[B
79 *
80 * From the javadoc:
81 *
82 * calculate (base ^ exponent) % modulus.
83 * @param jbase big endian twos complement representation of the base
84 *             Negative values allowed as of version 3
85 * @param jexp big endian twos complement representation of the exponent
86 *             Must be greater than or equal to zero.
87 *             As of version 3, throws java.lang.ArithmeticException if < 0.
88 * @param jmod big endian twos complement representation of the modulus
89 *             Must be greater than zero.
90 *             As of version 3, throws java.lang.ArithmeticException if <= 0.
91 *             Prior to version 3, crashed the JVM if <= 0.
92 * @return big endian twos complement representation of (base ^ exponent) % modulus
93 * @throws java.lang.ArithmeticException if jmod is <= 0
94 */
95
96JNIEXPORT jbyteArray JNICALL Java_net_i2p_util_NativeBigInteger_nativeModPow
97        (JNIEnv* env, jclass cls, jbyteArray jbase, jbyteArray jexp, jbyteArray jmod) {
98        /* 1) Convert base, exponent, modulus into the format libgmp understands
99         * 2) Call libgmp's modPow.
100         * 3) Convert libgmp's result into a big endian twos complement number.
101         */
102
103        mpz_t mbase;
104        mpz_t mexp;
105        mpz_t mmod;
106        jbyteArray jresult;
107
108        convert_j2mp(env, jmod,  &mmod);
109        if (mpz_sgn(mmod) <= 0) {
110            mpz_clear(mmod);
111            jclass exc = (*env)->FindClass(env, "java/lang/ArithmeticException");
112            (*env)->ThrowNew(env, exc, "Modulus must be positive");
113            return 0;
114        }
115
116        // disallow negative exponents to avoid divide by zero exception if no inverse exists
117        convert_j2mp(env, jexp,  &mexp);
118        if (mpz_sgn(mexp) < 0) {
119            mpz_clears(mmod, mexp, NULL);
120            jclass exc = (*env)->FindClass(env, "java/lang/ArithmeticException");
121            (*env)->ThrowNew(env, exc, "Exponent cannot be negative");
122            return 0;
123        }
124
125        convert_j2mp(env, jbase, &mbase);
126 
127        /* Perform the actual powmod. We use mmod for the result because it is
128         * always at least as big as the result.
129         */
130        mpz_powm(mmod, mbase, mexp, mmod);
131
132        convert_mp2j(env, mmod, &jresult);
133
134        mpz_clears(mbase, mexp, mmod, NULL);
135
136        return jresult;
137}
138
139/******** nativeModPowCT() */
140/*
141 * Class:     net_i2p_util_NativeBigInteger
142 * Method:    nativeModPowCT
143 * Signature: ([B[B[B)[B
144 *
145 * Constant time version of nativeModPow()
146 *
147 * From the javadoc:
148 *
149 * calculate (base ^ exponent) % modulus.
150 * @param jbase big endian twos complement representation of the base
151 *             Negative values allowed.
152 * @param jexp big endian twos complement representation of the exponent
153 *             Must be positive.
154 * @param jmod big endian twos complement representation of the modulus
155 *             Must be positive and odd.
156 * @return big endian twos complement representation of (base ^ exponent) % modulus
157 * @throws java.lang.ArithmeticException if jmod or jexp is <= 0, or jmod is even.
158 * @since version 3
159 */
160
161JNIEXPORT jbyteArray JNICALL Java_net_i2p_util_NativeBigInteger_nativeModPowCT
162        (JNIEnv* env, jclass cls, jbyteArray jbase, jbyteArray jexp, jbyteArray jmod) {
163
164        mpz_t mbase;
165        mpz_t mexp;
166        mpz_t mmod;
167        jbyteArray jresult;
168
169        convert_j2mp(env, jmod,  &mmod);
170        if (mpz_sgn(mmod) <= 0) {
171            mpz_clear(mmod);
172            jclass exc = (*env)->FindClass(env, "java/lang/ArithmeticException");
173            (*env)->ThrowNew(env, exc, "Modulus must be positive");
174            return 0;
175        }
176        // disallow even modulus as specified in the GMP docs
177        if (mpz_odd_p(mmod) == 0) {
178            mpz_clear(mmod);
179            jclass exc = (*env)->FindClass(env, "java/lang/ArithmeticException");
180            (*env)->ThrowNew(env, exc, "Modulus must be odd");
181            return 0;
182        }
183
184        // disallow negative or zero exponents as specified in the GMP docs
185        convert_j2mp(env, jexp,  &mexp);
186        if (mpz_sgn(mexp) <= 0) {
187            mpz_clears(mmod, mexp, NULL);
188            jclass exc = (*env)->FindClass(env, "java/lang/ArithmeticException");
189            (*env)->ThrowNew(env, exc, "Exponent must be positive");
190            return 0;
191        }
192
193        convert_j2mp(env, jbase, &mbase);
194 
195        mpz_powm_sec(mmod, mbase, mexp, mmod);
196
197        convert_mp2j(env, mmod, &jresult);
198
199        mpz_clears(mbase, mexp, mmod, NULL);
200
201        return jresult;
202}
203
204/******** nativeModInverse() */
205/*
206 * Class:     net_i2p_util_NativeBigInteger
207 * Method:    nativeModInverse
208 * Signature: ([B[B)[B
209 *
210 * From the javadoc:
211 *
212 * calculate (base ^ -1) % modulus.
213 * @param jbase big endian twos complement representation of the base
214 *             Negative values allowed
215 * @param jmod big endian twos complement representation of the modulus
216 *             Zero or Negative values will throw a java.lang.ArithmeticException
217 * @return big endian twos complement representation of (base ^ exponent) % modulus
218 * @throws java.lang.ArithmeticException if jbase and jmod are not coprime or jmod is <= 0
219 * @since version 3
220 */
221
222JNIEXPORT jbyteArray JNICALL Java_net_i2p_util_NativeBigInteger_nativeModInverse
223        (JNIEnv* env, jclass cls, jbyteArray jbase, jbyteArray jmod) {
224
225        mpz_t mbase;
226        mpz_t mexp;
227        mpz_t mmod;
228        mpz_t mgcd;
229        jbyteArray jresult;
230
231        convert_j2mp(env, jmod,  &mmod);
232
233        if (mpz_sgn(mmod) <= 0) {
234            mpz_clear(mmod);
235            jclass exc = (*env)->FindClass(env, "java/lang/ArithmeticException");
236            (*env)->ThrowNew(env, exc, "Modulus must be positive");
237            return 0;
238        }
239
240        convert_j2mp(env, jbase, &mbase);
241        mpz_init_set_si(mexp, -1);
242 
243        /* We must protect the jvm by doing a gcd test first.
244         * If the arguments are not coprime, GMP will throw a divide by zero
245         * and crash the JVM.
246         * We could test in Java using BigInteger.gcd() but it is almost as slow
247         * as the Java modInverse() itself, thus defeating the point.
248         * Unfortunately, this almost doubles our time here too.
249         */
250        mpz_init(mgcd);
251        mpz_gcd(mgcd, mbase, mmod);
252        if (mpz_cmp_si(mgcd, 1) != 0) {
253            mpz_clears(mbase, mexp, mmod, mgcd, NULL);
254            jclass exc = (*env)->FindClass(env, "java/lang/ArithmeticException");
255            (*env)->ThrowNew(env, exc, "Not coprime in nativeModInverse()");
256            return 0;
257        }
258
259        /* Perform the actual powmod. We use mmod for the result because it is
260         * always at least as big as the result.
261         */
262        mpz_powm(mmod, mbase, mexp, mmod);
263
264        convert_mp2j(env, mmod, &jresult);
265
266        mpz_clears(mbase, mexp, mmod, mgcd, NULL);
267
268        return jresult;
269}
270
271/******** nativeNeg() */
272/* since version 3 */
273/*
274 * Class:     net_i2p_util_NativeBigInteger
275 * Method:    nativeNeg
276 * Signature: ([B)[B
277 *
278 * For testing of the conversion functions only!
279 *
280 * calculate n mod d
281 * @param n big endian twos complement representation
282 * @return big endian twos complement representation of -n
283 */
284
285/****
286JNIEXPORT jbyteArray JNICALL Java_net_i2p_util_NativeBigInteger_nativeNeg
287        (JNIEnv* env, jclass cls, jbyteArray jn) {
288
289        mpz_t mn;
290        jbyteArray jresult;
291
292        convert_j2mp(env, jn,  &mn);
293 
294        // result to mn
295        mpz_neg(mn, mn);
296
297        convert_mp2j(env, mn, &jresult);
298
299        mpz_clear(mn);
300
301        return jresult;
302}
303****/
304
305/******************************
306 *****Conversion methods*******
307 ******************************/
308
309/*Luckily we can use GMP's mpz_import() and mpz_export() functions to convert from/to
310 *BigInteger.toByteArray() representation.
311 */
312
313/******** convert_j2mp() */
314/*
315 * Initializes the GMP value with enough preallocated size, and converts the
316 * Java value into the GMP value. The value that mvalue points to should be
317 * uninitialized
318 *
319 * As of version 3, negative values are correctly converted.
320 */
321
322void convert_j2mp(JNIEnv* env, jbyteArray jvalue, mpz_t* mvalue)
323{
324        jsize size;
325        jbyte* jbuffer;
326        mpz_t mask;
327
328        size = (*env)->GetArrayLength(env, jvalue);
329        jbuffer = (*env)->GetByteArrayElements(env, jvalue, NULL);
330
331        mpz_init2(*mvalue, sizeof(jbyte) * 8 * size); //preallocate the size
332
333        /* void mpz_import(
334         *   mpz_t rop, size_t count, int order, int size, int endian,
335         *   size_t nails, const void *op);
336         *
337         * order = 1
338         *   order can be 1 for most significant word first or -1 for least
339         *   significant first.
340         * endian = 1
341         *   Within each word endian can be 1 for most significant byte first,
342         *   -1 for least significant first.
343         * nails = 0
344         *   The most significant nails bits of each word are skipped, this can
345         *   be 0 to use the full words.
346         */
347        mpz_import(*mvalue, size, 1, sizeof(jbyte), 1, 0, (void*)jbuffer);
348        if (jbuffer[0] < 0) {
349            // ones complement, making a negative number
350            mpz_com(*mvalue, *mvalue);
351            // construct the mask needed to get rid of the new high bit
352            mpz_init_set_ui(mask, 1);
353            mpz_mul_2exp(mask, mask, size * 8);
354            mpz_sub_ui(mask, mask, 1);
355            // mask off the high bits, making a postive number (the magnitude, off by one)
356            mpz_and(*mvalue, *mvalue, mask);
357            // release the mask
358            mpz_clear(mask);
359            // add one to get the correct magnitude
360            mpz_add_ui(*mvalue, *mvalue, 1);
361            // invert to a negative number
362            mpz_neg(*mvalue, *mvalue);
363        }
364        (*env)->ReleaseByteArrayElements(env, jvalue, jbuffer, JNI_ABORT);
365}
366
367/******** convert_mp2j() */
368/*
369 * Converts the GMP value into the Java value; Doesn't do anything else.
370 * Pads the resulting jbyte array with 0, so the twos complement value is always
371 * positive.
372 *
373 * As of version 3, negative values are correctly converted.
374 */
375
376void convert_mp2j(JNIEnv* env, mpz_t mvalue, jbyteArray* jvalue)
377{
378        // size_t not jsize to work with 64bit CPUs (do we need to update this
379        // elsewhere, and/or adjust memory alloc sizes?)
380        size_t size; 
381        jbyte* buffer;
382        jboolean copy;
383        int i;
384        int neg;
385
386        copy = JNI_FALSE;
387
388        neg = mpz_sgn(mvalue) < 0;
389        if (neg) {
390            // add 1...
391            // have to do this before we calculate the size!
392            mpz_add_ui(mvalue, mvalue, 1);
393        }
394
395        /* sizeinbase() + 7 => Ceil division */
396        size = (mpz_sizeinbase(mvalue, 2) + 7) / 8 + sizeof(jbyte);
397        *jvalue = (*env)->NewByteArray(env, size);
398
399        buffer = (*env)->GetByteArrayElements(env, *jvalue, &copy);
400        buffer[0] = 0x00;
401
402        if (!neg) {
403            mpz_export((void*)&buffer[1], NULL, 1, sizeof(jbyte), 1, 0, mvalue);
404        } else {
405            mpz_export((void*)&buffer[1], NULL, 1, sizeof(jbyte), 1, 0, mvalue);
406            // ... and invert the bits
407            // This could be done all in mpz, the reverse of the above
408            for (i = 0; i <= size; i++) {
409                buffer[i] = ~buffer[i];
410            }
411        }
412
413        /* mode has (supposedly) no effect if elems is not a copy of the
414         * elements in array
415         */
416        (*env)->ReleaseByteArrayElements(env, *jvalue, buffer, 0);
417        //mode has (supposedly) no effect if elems is not a copy of the elements in array
418}
Note: See TracBrowser for help on using the repository browser.