Skip to content

Commit 5084d09

Browse files
authored
feat: add bytes methods and using embeded TextEncoder and TextDecoder (#95)
* feat: add bytes methods and using embeded TextEncoder and TextDecoder * chore: remove extra .js * chore: fix types
1 parent a72ffa3 commit 5084d09

34 files changed

+918
-156
lines changed

README.md

+14-2
Original file line numberDiff line numberDiff line change
@@ -10,12 +10,12 @@
1010
## JSI
1111

1212

13-
If you want to use with `JSI` instead of `NativeModules` you need to set
13+
If you want to use with `NativeModules` instead of `JSI` you need to set
1414

1515
```typescript
1616
import RSA from "react-native-fast-rsa";
1717

18-
RSA.useJSI = true;
18+
RSA.useJSI = false;
1919
```
2020
if you need to use generate methods it is a good idea to disable it, because for now JSI will block your UI but it is faster compared to NativeModules
2121

@@ -78,15 +78,27 @@ class RSA {
7878
static decryptOAEP(message: string, label: string, hashName: Hash, privateKey: string): Promise<string>
7979
static decryptPKCS1v15(message: string, privateKey: string,): Promise<string>
8080

81+
static decryptOAEPBytes(message: Uint8Array, label: string, hashName: Hash, privateKey: string): Promise<Uint8Array>
82+
static decryptPKCS1v15Bytes(message: Uint8Array, privateKey: string,): Promise<Uint8Array>
83+
8184
static encryptOAEP(message: string,label: string, hashName: Hash, publicKey: string): Promise<string>
8285
static encryptPKCS1v15(message: string, publicKey: string): Promise<string>
8386

87+
static encryptOAEPBytes(message: Uint8Array,label: string, hashName: Hash, publicKey: string): Promise<Uint8Array>
88+
static encryptPKCS1v15Bytes(message: Uint8Array, publicKey: string): Promise<Uint8Array>
89+
8490
static signPSS(message: string, hashName: Hash, saltLengthName: SaltLength, privateKey: string): Promise<string>
8591
static signPKCS1v15(message: string, hashName: Hash, privateKey: string): Promise<string>
8692

93+
static signPSSBytes(message: Uint8Array, hashName: Hash, saltLengthName: SaltLength, privateKey: string): Promise<Uint8Array>
94+
static signPKCS1v15Bytes(message: Uint8Array, hashName: Hash, privateKey: string): Promise<Uint8Array>
95+
8796
static verifyPSS(signature: string, message: string, hashName: Hash, saltLengthName: SaltLength, publicKey: string): Promise<boolean>
8897
static verifyPKCS1v15(signature: string, message: string, hashName: Hash, publicKey: string): Promise<boolean>
8998

99+
static verifyPSSBytes(signature: Uint8Array, message: Uint8Array, hashName: Hash, saltLengthName: SaltLength, publicKey: string): Promise<boolean>
100+
static verifyPKCS1v15Bytes(signature: Uint8Array, message: Uint8Array, hashName: Hash, publicKey: string): Promise<boolean>
101+
90102
static hash(message: string, name: Hash): Promise<string>
91103
static base64(message: string): Promise<string>
92104

android/fast-rsa-adapter.cpp

+93
Original file line numberDiff line numberDiff line change
@@ -93,4 +93,97 @@ Java_com_fastrsa_FastRsaModule_callNative(JNIEnv* env,
9393
free(response);
9494

9595
return result;
96+
}
97+
98+
extern "C" JNIEXPORT jbyteArray JNICALL
99+
Java_com_fastrsa_FastRsaModule_encodeTextNative(JNIEnv* env, jobject thiz, jstring input, jstring encoding) {
100+
if (input == nullptr || encoding == nullptr) {
101+
jclass Exception = env->FindClass("java/lang/NullPointerException");
102+
env->ThrowNew(Exception, "Input parameters 'input' or 'encoding' cannot be null");
103+
return nullptr;
104+
}
105+
106+
// Convert Java Strings to C Strings
107+
const char* inputCStr = env->GetStringUTFChars(input, nullptr);
108+
const char* encodingCStr = env->GetStringUTFChars(encoding, nullptr);
109+
110+
if (inputCStr == nullptr || encodingCStr == nullptr) {
111+
jclass Exception = env->FindClass("java/lang/OutOfMemoryError");
112+
env->ThrowNew(Exception, "Failed to allocate memory for 'input' or 'encoding'");
113+
return nullptr;
114+
}
115+
116+
// Call the shared library function
117+
BytesReturn* response = RSAEncodeText(const_cast<char*>(inputCStr), const_cast<char*>(encodingCStr));
118+
119+
// Release allocated resources
120+
env->ReleaseStringUTFChars(input, inputCStr);
121+
env->ReleaseStringUTFChars(encoding, encodingCStr);
122+
123+
if (response->error != nullptr) {
124+
jclass Exception = env->FindClass("java/lang/Exception");
125+
env->ThrowNew(Exception, response->error);
126+
free(response);
127+
return nullptr;
128+
}
129+
130+
// Create a new byte array to return the encoded data
131+
jbyteArray result = env->NewByteArray(response->size);
132+
if (result == nullptr) {
133+
free(response);
134+
jclass Exception = env->FindClass("java/lang/OutOfMemoryError");
135+
env->ThrowNew(Exception, "Failed to allocate memory for result");
136+
return nullptr;
137+
}
138+
139+
env->SetByteArrayRegion(result, 0, response->size, reinterpret_cast<jbyte*>(response->message));
140+
free(response);
141+
142+
return result;
143+
}
144+
145+
extern "C" JNIEXPORT jstring JNICALL
146+
Java_com_fastrsa_FastRsaModule_decodeTextNative(JNIEnv* env, jobject thiz, jbyteArray input, jstring encoding,
147+
jint fatal, jint ignoreBOM, jint stream) {
148+
if (input == nullptr || encoding == nullptr) {
149+
jclass Exception = env->FindClass("java/lang/NullPointerException");
150+
env->ThrowNew(Exception, "Input parameters 'input' or 'encoding' cannot be null");
151+
return nullptr;
152+
}
153+
154+
// Convert Java Strings to C Strings
155+
const char* encodingCStr = env->GetStringUTFChars(encoding, nullptr);
156+
if (encodingCStr == nullptr) {
157+
jclass Exception = env->FindClass("java/lang/OutOfMemoryError");
158+
env->ThrowNew(Exception, "Failed to allocate memory for 'encoding'");
159+
return nullptr;
160+
}
161+
162+
// Convert Java byte array to C byte array
163+
jsize size = env->GetArrayLength(input);
164+
jbyte* inputBytes = env->GetByteArrayElements(input, nullptr);
165+
if (inputBytes == nullptr) {
166+
env->ReleaseStringUTFChars(encoding, encodingCStr);
167+
jclass Exception = env->FindClass("java/lang/OutOfMemoryError");
168+
env->ThrowNew(Exception, "Failed to allocate memory for 'input'");
169+
return nullptr;
170+
}
171+
172+
// Call the shared library function
173+
char* decodedString = RSADecodeText(inputBytes, size, const_cast<char*>(encodingCStr), fatal, ignoreBOM, stream);
174+
175+
// Release resources
176+
env->ReleaseStringUTFChars(encoding, encodingCStr);
177+
env->ReleaseByteArrayElements(input, inputBytes, JNI_ABORT);
178+
179+
if (decodedString == nullptr) {
180+
jclass Exception = env->FindClass("java/lang/Exception");
181+
env->ThrowNew(Exception, "Decoding failed");
182+
return nullptr;
183+
}
184+
185+
// Convert C string to Java string and return
186+
jstring result = env->NewStringUTF(decodedString);
187+
free(decodedString);
188+
return result;
96189
}

android/src/main/java/com/fastrsa/FastRsaModule.kt

+28
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,8 @@ internal class FastRsaModule(reactContext: ReactApplicationContext) :
1111
external fun initialize(jsContext: Long)
1212
external fun destruct();
1313
external fun callNative(name: String, payload: ByteArray): ByteArray;
14+
external fun encodeTextNative(input: String, encoding: String): ByteArray
15+
external fun decodeTextNative(input: ByteArray, encoding: String, fatal: Int, ignoreBOM: Int, stream: Int): String
1416

1517
companion object {
1618
init {
@@ -41,6 +43,32 @@ internal class FastRsaModule(reactContext: ReactApplicationContext) :
4143
}.start()
4244
}
4345

46+
@ReactMethod(isBlockingSynchronousMethod = true)
47+
fun encodeText(input: String, encoding: String): WritableArray {
48+
return try {
49+
val result = encodeTextNative(input, encoding)
50+
Arguments.createArray().apply {
51+
result.forEach { byteValue: Byte -> pushInt(byteValue.toInt() and 0xFF) }
52+
}
53+
} catch (e: Exception) {
54+
Log.e(TAG, "Encoding error", e)
55+
throw RuntimeException("ENCODE_ERROR: Failed to encode text")
56+
}
57+
}
58+
59+
@ReactMethod(isBlockingSynchronousMethod = true)
60+
fun decodeText(input: ReadableArray, encoding: String, fatal: Boolean, ignoreBOM: Boolean, stream: Boolean): String {
61+
return try {
62+
val bytes = ByteArray(input.size()) { index ->
63+
input.getInt(index).toByte()
64+
}
65+
decodeTextNative(bytes, encoding, if (fatal) 1 else 0, if (ignoreBOM) 1 else 0, if (stream) 1 else 0)
66+
} catch (e: Exception) {
67+
Log.e(TAG, "Decoding error", e)
68+
throw RuntimeException("DECODE_ERROR: Failed to decode text")
69+
}
70+
}
71+
4472
@ReactMethod(isBlockingSynchronousMethod = true)
4573
fun install(): Boolean {
4674
Log.d(TAG, "Attempting to install JSI bindings...")

android/src/main/jniLibs/arm64-v8a/librsa_bridge.h

+2
Original file line numberDiff line numberDiff line change
@@ -81,6 +81,8 @@ extern "C" {
8181
#endif
8282

8383
extern BytesReturn* RSABridgeCall(char* name, void* payload, int payloadSize);
84+
extern BytesReturn* RSAEncodeText(char* input, char* encoding);
85+
extern char* RSADecodeText(void* input, int size, char* encoding, int fatal, int ignoreBOM, int stream);
8486

8587
#ifdef __cplusplus
8688
}
Binary file not shown.

android/src/main/jniLibs/armeabi-v7a/librsa_bridge.h

+2
Original file line numberDiff line numberDiff line change
@@ -81,6 +81,8 @@ extern "C" {
8181
#endif
8282

8383
extern BytesReturn* RSABridgeCall(char* name, void* payload, int payloadSize);
84+
extern BytesReturn* RSAEncodeText(char* input, char* encoding);
85+
extern char* RSADecodeText(void* input, int size, char* encoding, int fatal, int ignoreBOM, int stream);
8486

8587
#ifdef __cplusplus
8688
}
Binary file not shown.

android/src/main/jniLibs/x86/librsa_bridge.h

+2
Original file line numberDiff line numberDiff line change
@@ -81,6 +81,8 @@ extern "C" {
8181
#endif
8282

8383
extern BytesReturn* RSABridgeCall(char* name, void* payload, int payloadSize);
84+
extern BytesReturn* RSAEncodeText(char* input, char* encoding);
85+
extern char* RSADecodeText(void* input, int size, char* encoding, int fatal, int ignoreBOM, int stream);
8486

8587
#ifdef __cplusplus
8688
}
637 KB
Binary file not shown.

android/src/main/jniLibs/x86_64/librsa_bridge.h

+2
Original file line numberDiff line numberDiff line change
@@ -81,6 +81,8 @@ extern "C" {
8181
#endif
8282

8383
extern BytesReturn* RSABridgeCall(char* name, void* payload, int payloadSize);
84+
extern BytesReturn* RSAEncodeText(char* input, char* encoding);
85+
extern char* RSADecodeText(void* input, int size, char* encoding, int fatal, int ignoreBOM, int stream);
8486

8587
#ifdef __cplusplus
8688
}
Binary file not shown.

cpp/librsa_bridge.h

+2
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,8 @@ typedef struct {
1010
extern "C" {
1111
#endif
1212
extern BytesReturn* RSABridgeCall(char* p0, void* p1, int p2);
13+
extern BytesReturn* RSAEncodeText(char* input, char* encoding);
14+
extern char* RSADecodeText(void* input, int size, char* encoding, int fatal, int ignoreBOM, int stream);
1315
#ifdef __cplusplus
1416
}
1517
#endif

cpp/react-native-fast-rsa.cpp

+86-1
Original file line numberDiff line numberDiff line change
@@ -5,14 +5,68 @@
55
#include <future>
66
#include <iostream>
77
#include <sstream>
8-
#include <future>
98
#include <thread>
109

1110
#include "librsa_bridge.h"
1211

1312
using namespace facebook;
1413

1514
namespace fastRSA {
15+
16+
jsi::Value encodeText(jsi::Runtime &runtime, const jsi::String &inputValue, const jsi::String &encodingValue) {
17+
std::string inputString = inputValue.utf8(runtime);
18+
std::string encodingString = encodingValue.utf8(runtime);
19+
20+
std::vector<char> mutableInput(inputString.begin(), inputString.end());
21+
mutableInput.push_back('\0');
22+
std::vector<char> mutableEncoding(encodingString.begin(), encodingString.end());
23+
mutableEncoding.push_back('\0');
24+
25+
auto response = RSAEncodeText(mutableInput.data(), mutableEncoding.data());
26+
if (response->error != nullptr) {
27+
std::string errorMessage(response->error);
28+
free(response);
29+
throw jsi::JSError(runtime, errorMessage);
30+
}
31+
32+
auto uint8ArrayConstructor = runtime.global().getPropertyAsFunction(runtime, "Uint8Array");
33+
jsi::Object uint8ArrayObject = uint8ArrayConstructor.callAsConstructor(runtime, response->size).getObject(runtime);
34+
jsi::ArrayBuffer arrayBuffer = uint8ArrayObject.getPropertyAsObject(runtime, "buffer").getArrayBuffer(runtime);
35+
memcpy(arrayBuffer.data(runtime), response->message, response->size);
36+
37+
free(response);
38+
return uint8ArrayObject;
39+
}
40+
41+
jsi::Value decodeText(jsi::Runtime &runtime, const jsi::Object &inputObject, const jsi::String &encodingValue,
42+
bool fatal, bool ignoreBOM, bool stream) {
43+
auto uint8ArrayConstructor = runtime.global().getPropertyAsFunction(runtime, "Uint8Array");
44+
if (!inputObject.instanceOf(runtime, uint8ArrayConstructor)) {
45+
throw jsi::JSError(runtime, "First argument must be a Uint8Array");
46+
}
47+
48+
// Get Uint8Array data
49+
jsi::ArrayBuffer arrayBuffer = inputObject.getPropertyAsObject(runtime, "buffer").getArrayBuffer(runtime);
50+
int byteOffset = inputObject.getProperty(runtime, "byteOffset").asNumber();
51+
int length = inputObject.getProperty(runtime, "byteLength").asNumber();
52+
53+
uint8_t *dataPointer = static_cast<uint8_t *>(arrayBuffer.data(runtime)) + byteOffset;
54+
55+
std::string encodingString = encodingValue.utf8(runtime);
56+
std::vector<char> mutableEncoding(encodingString.begin(), encodingString.end());
57+
mutableEncoding.push_back('\0');
58+
59+
char *decodedString = RSADecodeText(dataPointer, length, mutableEncoding.data(), fatal ? 1 : 0, ignoreBOM ? 1 : 0, stream ? 1 : 0);
60+
if (!decodedString) {
61+
throw jsi::JSError(runtime, "Failed to decode text");
62+
}
63+
64+
jsi::String result = jsi::String::createFromUtf8(runtime, decodedString);
65+
free(decodedString);
66+
return result;
67+
}
68+
69+
1670
jsi::Value call(jsi::Runtime &runtime, const jsi::String &nameValue,
1771
const jsi::Object &payloadObject) {
1872
// Extract and validate name
@@ -140,6 +194,8 @@ void install(jsi::Runtime &jsiRuntime) {
140194
reject.call(runtime, error.value());
141195
} catch (const std::exception &e) {
142196
reject.call(runtime, jsi::String::createFromUtf8(runtime, e.what()));
197+
} catch (...) {
198+
reject.call(runtime, jsi::String::createFromUtf8(runtime, "Unknown error occurred"));
143199
}
144200

145201
return jsi::Value::undefined();
@@ -152,6 +208,35 @@ void install(jsi::Runtime &jsiRuntime) {
152208
return promise;
153209
});
154210

211+
auto encodeTextFunc = jsi::Function::createFromHostFunction(
212+
jsiRuntime, jsi::PropNameID::forAscii(jsiRuntime, "encodeText"), 2,
213+
[](jsi::Runtime &runtime, const jsi::Value & /*thisValue*/, const jsi::Value *arguments, size_t count) -> jsi::Value {
214+
if (count != 2) {
215+
throw jsi::JSError(runtime, "encodeText expects exactly 2 arguments: (string input, string encoding)");
216+
}
217+
if (!arguments[0].isString() || !arguments[1].isString()) {
218+
throw jsi::JSError(runtime, "Both arguments must be strings");
219+
}
220+
return encodeText(runtime, arguments[0].getString(runtime), arguments[1].getString(runtime));
221+
});
222+
223+
auto decodeTextFunc = jsi::Function::createFromHostFunction(
224+
jsiRuntime, jsi::PropNameID::forAscii(jsiRuntime, "decodeText"), 5,
225+
[](jsi::Runtime &runtime, const jsi::Value & /*thisValue*/, const jsi::Value *arguments, size_t count) -> jsi::Value {
226+
if (count != 5) {
227+
throw jsi::JSError(runtime, "decodeText expects exactly 5 arguments: (Uint8Array input, string encoding, bool fatal, bool ignoreBOM, bool stream)");
228+
}
229+
if (!arguments[0].isObject() || !arguments[0].getObject(runtime).instanceOf(runtime, runtime.global().getPropertyAsFunction(runtime, "Uint8Array")) ||
230+
!arguments[1].isString() || !arguments[2].isBool() || !arguments[3].isBool() || !arguments[4].isBool()) {
231+
throw jsi::JSError(runtime, "Invalid argument types");
232+
}
233+
234+
return decodeText(runtime, arguments[0].getObject(runtime),
235+
arguments[1].getString(runtime), arguments[2].getBool(), arguments[3].getBool(), arguments[4].getBool());
236+
});
237+
238+
jsiRuntime.global().setProperty(jsiRuntime, "FastRSAEncodeText", std::move(encodeTextFunc));
239+
jsiRuntime.global().setProperty(jsiRuntime, "FastRSADecodeText", std::move(decodeTextFunc));
155240
jsiRuntime.global().setProperty(jsiRuntime, "FastRSACallPromise", std::move(bridgeCallPromise));
156241
jsiRuntime.global().setProperty(jsiRuntime, "FastRSACallSync", std::move(bridgeCallSync));
157242
}

0 commit comments

Comments
 (0)