1 /*
2 * Copyright (C) 2008 The Android Open Source Project
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 * http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16
17 #define LOG_TAG "NativeBN"
18
19 #include <stdio.h>
20 #include <algorithm>
21 #include <memory>
22
23 #include <openssl/bn.h>
24 #include <openssl/crypto.h>
25 #include <openssl/err.h>
26
27 #include <nativehelper/JNIHelp.h>
28 #include <nativehelper/ScopedPrimitiveArray.h>
29 #include <nativehelper/ScopedUtfChars.h>
30 #include <nativehelper/jni_macros.h>
31
32 #include "JniException.h"
33
34 struct BN_CTX_Deleter {
operator ()BN_CTX_Deleter35 void operator()(BN_CTX* p) const {
36 BN_CTX_free(p);
37 }
38 };
39 typedef std::unique_ptr<BN_CTX, BN_CTX_Deleter> Unique_BN_CTX;
40
toBigNum(jlong address)41 static BIGNUM* toBigNum(jlong address) {
42 return reinterpret_cast<BIGNUM*>(static_cast<uintptr_t>(address));
43 }
44
45 // Exception handling: We follow the usual JNI convention of "throwing" an
46 // exception if anything goes wrong, and returning junk, typically null.
47 // The NativeBN_ routines should only be called from Java, or from code
48 // that immediately returns the result to Java, and thus the
49 // Java exception should be thrown before we ever see the junk.
50 // This null BNs should never become visible, and we do not have to deal with
51 // junk (nulls) as input.
throwException(JNIEnv * env)52 static void throwException(JNIEnv* env) {
53 long error = ERR_get_error();
54 // OpenSSL's error queue may contain multiple errors. Clean up after them.
55 ERR_clear_error();
56
57 if (error == 0) {
58 // An operation failed but did not push to the error queue. Throw a default
59 // exception.
60 jniThrowException(env, "java/lang/ArithmeticException", "Operation failed");
61 return;
62 }
63
64 char message[256];
65 ERR_error_string_n(error, message, sizeof(message));
66 int reason = ERR_GET_REASON(error);
67 if (reason == BN_R_DIV_BY_ZERO) {
68 jniThrowException(env, "java/lang/ArithmeticException", "BigInteger division by zero");
69 } else if (reason == BN_R_NO_INVERSE) {
70 jniThrowException(env, "java/lang/ArithmeticException", "BigInteger not invertible");
71 } else if (reason == ERR_R_MALLOC_FAILURE) {
72 jniThrowOutOfMemoryError(env, message);
73 } else {
74 jniThrowException(env, "java/lang/ArithmeticException", message);
75 }
76 }
77
NativeBN_BN_new(JNIEnv * env,jclass)78 static jlong NativeBN_BN_new(JNIEnv* env, jclass) {
79 jlong result = static_cast<jlong>(reinterpret_cast<uintptr_t>(BN_new()));
80 if (!result) {
81 throwException(env);
82 }
83 return result;
84 }
85
NativeBN_BN_free(JNIEnv *,jclass,jlong a)86 static void NativeBN_BN_free(JNIEnv*, jclass, jlong a) {
87 // Do nothing on a zero argument.
88 BN_free(toBigNum(a));
89 }
90
NativeBN_litEndInts2bn(JNIEnv * env,jclass,jintArray arr,int len,jboolean neg,jlong ret0)91 static void NativeBN_litEndInts2bn(JNIEnv* env, jclass, jintArray arr, int len, jboolean neg, jlong ret0) {
92 BIGNUM* ret = toBigNum(ret0);
93
94 ScopedIntArrayRO scopedArray(env, arr);
95
96 if (scopedArray.get() == NULL) {
97 return;
98 }
99
100 // We can simply interpret the little-endian integer stream as a
101 // little-endian byte stream and use BN_le2bn.
102 const uint8_t* tmpBytes = reinterpret_cast<const uint8_t *>(scopedArray.get());
103 size_t numBytes = len * sizeof(int);
104
105 if (!BN_le2bn(tmpBytes, numBytes, ret)) {
106 throwException(env);
107 }
108
109 BN_set_negative(ret, neg);
110 }
111
NativeBN_bn2litEndInts(JNIEnv * env,jclass,jlong a0)112 static jintArray NativeBN_bn2litEndInts(JNIEnv* env, jclass, jlong a0) {
113 BIGNUM* a = toBigNum(a0);
114
115 // The number of integers we need is BN_num_bytes(a) / sizeof(int), rounded up
116 int intLen = (BN_num_bytes(a) + sizeof(int) - 1) / sizeof(int);
117
118 // Allocate our result with the JNI boilerplate
119 jintArray result = env->NewIntArray(intLen);
120
121 if (result == NULL) {
122 throwException(env);
123 return NULL;
124 }
125
126 ScopedIntArrayRW ints(env, result);
127
128 unsigned int* uints = reinterpret_cast<unsigned int*>(ints.get());
129 if (uints == NULL) {
130 throwException(env);
131 return NULL;
132 }
133
134 // We can simply interpret a little-endian byte stream as a little-endian integer stream.
135 if (!BN_bn2le_padded(reinterpret_cast<uint8_t*>(uints), intLen * sizeof(int), a)) {
136 throwException(env);
137 return NULL;
138 }
139
140 return result;
141 }
142
NativeBN_BN_mul(JNIEnv * env,jclass,jlong r,jlong a,jlong b)143 static void NativeBN_BN_mul(JNIEnv* env, jclass, jlong r, jlong a, jlong b) {
144 Unique_BN_CTX ctx(BN_CTX_new());
145 BN_CTX* ctxp = ctx.get();
146 if (!ctxp || !BN_mul(toBigNum(r), toBigNum(a), toBigNum(b), ctxp)) {
147 throwException(env);
148 }
149 }
150
NativeBN_BN_div(JNIEnv * env,jclass,jlong q,jlong rem,jlong num,jlong divisor)151 static void NativeBN_BN_div(JNIEnv* env, jclass, jlong q, jlong rem, jlong num, jlong divisor) {
152 Unique_BN_CTX ctx(BN_CTX_new());
153 BN_CTX* ctxp = ctx.get();
154 if (!ctxp || !BN_div(toBigNum(q), toBigNum(rem), toBigNum(num), toBigNum(divisor), ctxp)) {
155 throwException(env);
156 }
157 }
158
NativeBN_BN_mod_exp(JNIEnv * env,jclass,jlong r,jlong a,jlong p,jlong m)159 static void NativeBN_BN_mod_exp(JNIEnv* env, jclass, jlong r, jlong a, jlong p, jlong m) {
160 Unique_BN_CTX ctx(BN_CTX_new());
161 BN_CTX* ctxp = ctx.get();
162 if (!ctxp || !BN_mod_exp(toBigNum(r), toBigNum(a), toBigNum(p), toBigNum(m), ctxp)) {
163 throwException(env);
164 }
165 }
166
167 static JNINativeMethod gMethods[] = {
168 NATIVE_METHOD(NativeBN, BN_div, "(JJJJ)V"),
169 NATIVE_METHOD(NativeBN, BN_free, "(J)V"),
170 NATIVE_METHOD(NativeBN, BN_mod_exp, "(JJJJ)V"),
171 NATIVE_METHOD(NativeBN, BN_mul, "(JJJ)V"),
172 NATIVE_METHOD(NativeBN, BN_new, "()J"),
173 NATIVE_METHOD(NativeBN, bn2litEndInts, "(J)[I"),
174 NATIVE_METHOD(NativeBN, litEndInts2bn, "([IIZJ)V"),
175 };
register_libcore_math_NativeBN(JNIEnv * env)176 void register_libcore_math_NativeBN(JNIEnv* env) {
177 jniRegisterNativeMethods(env, "libcore/math/NativeBN", gMethods, NELEM(gMethods));
178 }
179