xref: /aosp_15_r20/libcore/luni/src/main/native/libcore_math_NativeBN.cpp (revision 89a6322812dc8573315e60046e7959c50dad91d4)
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