diff --git a/app/android/app/build.gradle b/app/android/app/build.gradle index ac5afc673..8c0f9d983 100644 --- a/app/android/app/build.gradle +++ b/app/android/app/build.gradle @@ -140,6 +140,8 @@ dependencies { implementation("net.java.dev.jna:jna:5.13.0@aar") implementation 'org.jetbrains.kotlinx:kotlinx-coroutines-android' implementation project(':react-native-fs') + + implementation 'com.google.code.gson:gson:2.8.9' } apply from: file("../../node_modules/@react-native-community/cli-platform-android/native_modules.gradle"); applyNativeModulesAppBuildGradle(project) diff --git a/app/android/app/src/main/cpp/CMakeLists.txt b/app/android/app/src/main/cpp/CMakeLists.txt new file mode 100644 index 000000000..6ba7a2a4a --- /dev/null +++ b/app/android/app/src/main/cpp/CMakeLists.txt @@ -0,0 +1,28 @@ + +cmake_minimum_required(VERSION 3.22.1) + +project("proofofpassport") + +set(JDK_DIR "/Library/Java/JavaVirtualMachines/zulu-11.jdk/Contents/Home") + +# Include the JDK header files +include_directories(${JDK_DIR}/include) +include_directories(${JDK_DIR}/include/darwin) + +include_directories(include) + +link_directories(lib) + +add_library(rapidsnark SHARED IMPORTED) +set_target_properties(rapidsnark PROPERTIES IMPORTED_LOCATION ${CMAKE_SOURCE_DIR}/lib/librapidsnark.so) + + +add_library(proof_of_passport SHARED IMPORTED) +set_target_properties(proof_of_passport PROPERTIES IMPORTED_LOCATION ${CMAKE_SOURCE_DIR}/lib/libwitnesscalc_proof_of_passport.so) + +add_library(${CMAKE_PROJECT_NAME} SHARED + proofofpassport.cpp) + +target_link_libraries(${CMAKE_PROJECT_NAME} + rapidsnark proof_of_passport) + # android log fq fr gmp rapidsnark proof_of_passport rapidsnark-fr-fq) diff --git a/app/android/app/src/main/cpp/include/prover.h b/app/android/app/src/main/cpp/include/prover.h new file mode 100644 index 000000000..79d55f0dd --- /dev/null +++ b/app/android/app/src/main/cpp/include/prover.h @@ -0,0 +1,39 @@ +#ifndef PROVER_HPP +#define PROVER_HPP + +#ifdef __cplusplus +extern "C" { +#endif + +//Error codes returned by the functions. +#define PROVER_OK 0x0 +#define PROVER_ERROR 0x1 +#define PROVER_ERROR_SHORT_BUFFER 0x2 +#define PROVER_INVALID_WITNESS_LENGTH 0x3 + +/** + * Calculates buffer size to output public signals as json string + * @returns buffer size in bytes or 0 in case of an error + */ +unsigned long CalcPublicBufferSize(const void *zkey_buffer, unsigned long zkey_size); + +/** + * groth16_prover + * @return error code: + * PROVER_OK - in case of success + * PPOVER_ERROR - in case of an error + * PROVER_ERROR_SHORT_BUFFER - in case of a short buffer error, also updates proof_size and public_size with actual proof and public sizess + */ +int +groth16_prover(const void *zkey_buffer, unsigned long zkey_size, + const void *wtns_buffer, unsigned long wtns_size, + char *proof_buffer, unsigned long *proof_size, + char *public_buffer, unsigned long *public_size, + char *error_msg, unsigned long error_msg_maxsize); + +#ifdef __cplusplus +} +#endif + + +#endif // PROVER_HPP diff --git a/app/android/app/src/main/cpp/include/witnesscalc_proof_of_passport.h b/app/android/app/src/main/cpp/include/witnesscalc_proof_of_passport.h new file mode 100644 index 000000000..cb1d64529 --- /dev/null +++ b/app/android/app/src/main/cpp/include/witnesscalc_proof_of_passport.h @@ -0,0 +1,39 @@ +#ifndef WITNESSCALC_PROOFOFPASSPORT_H +#define WITNESSCALC_PROOFOFPASSPORT_H + + +#ifdef __cplusplus +extern "C" { +#endif + +#define WITNESSCALC_OK 0x0 +#define WITNESSCALC_ERROR 0x1 +#define WITNESSCALC_ERROR_SHORT_BUFFER 0x2 + +/** + * + * @return error code: + * WITNESSCALC_OK - in case of success. + * WITNESSCALC_ERROR - in case of an error. + * + * On success wtns_buffer is filled with witness data and + * wtns_size contains the number bytes copied to wtns_buffer. + * + * If wtns_buffer is too small then the function returns WITNESSCALC_ERROR_SHORT_BUFFER + * and the minimum size for wtns_buffer in wtns_size. + * + */ + +int +witnesscalc_proof_of_passport( + const char *circuit_buffer, unsigned long circuit_size, + const char *json_buffer, unsigned long json_size, + char *wtns_buffer, unsigned long *wtns_size, + char *error_msg, unsigned long error_msg_maxsize); + +#ifdef __cplusplus +} +#endif + + +#endif // WITNESSCALC_PROOFOFPASSPORT_H \ No newline at end of file diff --git a/app/android/app/src/main/cpp/lib/librapidsnark.so b/app/android/app/src/main/cpp/lib/librapidsnark.so new file mode 100644 index 000000000..f92a812f1 Binary files /dev/null and b/app/android/app/src/main/cpp/lib/librapidsnark.so differ diff --git a/app/android/app/src/main/cpp/lib/libwitnesscalc_proof_of_passport.so b/app/android/app/src/main/cpp/lib/libwitnesscalc_proof_of_passport.so new file mode 100755 index 000000000..8adfe0573 Binary files /dev/null and b/app/android/app/src/main/cpp/lib/libwitnesscalc_proof_of_passport.so differ diff --git a/app/android/app/src/main/cpp/proofofpassport.cpp b/app/android/app/src/main/cpp/proofofpassport.cpp new file mode 100644 index 000000000..5d4846f3f --- /dev/null +++ b/app/android/app/src/main/cpp/proofofpassport.cpp @@ -0,0 +1,95 @@ +#include "include/prover.h" +#include "include/witnesscalc_proof_of_passport.h" + +#include +#include + +using namespace std; + +extern "C" +JNIEXPORT jlong JNICALL +Java_com_proofofpassport_prover_ZKPTools_CalcPublicBufferSize(JNIEnv *env, jobject thiz, + jbyteArray zkey_buffer, jlong zkey_size) { + const void *zkeyBuffer = env->GetByteArrayElements(zkey_buffer, nullptr); + jlong result = CalcPublicBufferSize(zkeyBuffer, static_cast(zkey_size)); + env->ReleaseByteArrayElements(zkey_buffer, + reinterpret_cast(const_cast(zkeyBuffer)), 0); + return result; +} + +extern "C" +JNIEXPORT jint JNICALL +Java_com_proofofpassport_prover_ZKPTools_groth16_1prover(JNIEnv *env, jobject thiz, + jbyteArray zkey_buffer, jlong zkey_size, + jbyteArray wtns_buffer, jlong wtns_size, + jbyteArray proof_buffer, jlongArray proof_size, + jbyteArray public_buffer, + jlongArray public_size, jbyteArray error_msg, + jlong error_msg_max_size) { + const void *zkeyBuffer = env->GetByteArrayElements(zkey_buffer, nullptr); + const void *wtnsBuffer = env->GetByteArrayElements(wtns_buffer, nullptr); + char *proofBuffer = reinterpret_cast(env->GetByteArrayElements(proof_buffer, + nullptr)); + char *publicBuffer = reinterpret_cast(env->GetByteArrayElements(public_buffer, + nullptr)); + char *errorMsg = reinterpret_cast(env->GetByteArrayElements(error_msg, nullptr)); + + unsigned long proofSize = env->GetLongArrayElements(proof_size, nullptr)[0]; + unsigned long publicSize = env->GetLongArrayElements(public_size, nullptr)[0]; + + int result = groth16_prover(zkeyBuffer, static_cast(zkey_size), + wtnsBuffer, static_cast(wtns_size), + proofBuffer, &proofSize, + publicBuffer, &publicSize, + errorMsg, static_cast(error_msg_max_size)); + + env->SetLongArrayRegion(proof_size, 0, 1, reinterpret_cast(&proofSize)); + env->SetLongArrayRegion(public_size, 0, 1, reinterpret_cast(&publicSize)); + + env->ReleaseByteArrayElements(zkey_buffer, + reinterpret_cast(const_cast(zkeyBuffer)), 0); + env->ReleaseByteArrayElements(wtns_buffer, + reinterpret_cast(const_cast(wtnsBuffer)), 0); + env->ReleaseByteArrayElements(proof_buffer, reinterpret_cast(proofBuffer), 0); + env->ReleaseByteArrayElements(public_buffer, reinterpret_cast(publicBuffer), 0); + env->ReleaseByteArrayElements(error_msg, reinterpret_cast(errorMsg), 0); + + return result; +} + +extern "C" +JNIEXPORT jint JNICALL +Java_com_proofofpassport_prover_ZKPTools_witnesscalc_1proof_1of_1passport(JNIEnv *env, jobject thiz, + jbyteArray circuit_buffer, + jlong circuit_size, jbyteArray json_buffer, + jlong json_size, jbyteArray wtns_buffer, + jlongArray wtns_size, jbyteArray error_msg, + jlong error_msg_max_size) { + const char *circuitBuffer = reinterpret_cast(env->GetByteArrayElements( + circuit_buffer, nullptr)); + const char *jsonBuffer = reinterpret_cast(env->GetByteArrayElements(json_buffer, + nullptr)); + char *wtnsBuffer = reinterpret_cast(env->GetByteArrayElements(wtns_buffer, nullptr)); + char *errorMsg = reinterpret_cast(env->GetByteArrayElements(error_msg, nullptr)); + + unsigned long wtnsSize = env->GetLongArrayElements(wtns_size, nullptr)[0]; + + + int result = witnesscalc_proof_of_passport( + circuitBuffer, static_cast(circuit_size), + jsonBuffer, static_cast(json_size), + wtnsBuffer, &wtnsSize, + errorMsg, static_cast(error_msg_max_size)); + + // Set the result and release the resources + env->SetLongArrayRegion(wtns_size, 0, 1, reinterpret_cast(&wtnsSize)); + + env->ReleaseByteArrayElements(circuit_buffer, + reinterpret_cast(const_cast(circuitBuffer)), 0); + env->ReleaseByteArrayElements(json_buffer, + reinterpret_cast(const_cast(jsonBuffer)), 0); + env->ReleaseByteArrayElements(wtns_buffer, reinterpret_cast(wtnsBuffer), 0); + env->ReleaseByteArrayElements(error_msg, reinterpret_cast(errorMsg), 0); + + return result; +} diff --git a/app/android/app/src/main/java/com/awesomeproject/ProverModule.kt b/app/android/app/src/main/java/com/awesomeproject/ProverModule.kt index 2cd4c67ed..05c0dde6a 100644 --- a/app/android/app/src/main/java/com/awesomeproject/ProverModule.kt +++ b/app/android/app/src/main/java/com/awesomeproject/ProverModule.kt @@ -6,6 +6,8 @@ import com.facebook.react.bridge.ReactMethod import com.facebook.react.bridge.Promise import android.util.Log +import android.content.Context +import java.io.ByteArrayOutputStream import com.facebook.react.bridge.ReadableMap import uniffi.mopro.GenerateProofResult @@ -14,42 +16,41 @@ import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.launch import kotlinx.coroutines.withContext +import com.google.gson.Gson +import com.google.gson.GsonBuilder + +import com.proofofpassport.R + class ProverModule(reactContext: ReactApplicationContext) : ReactContextBaseJavaModule(reactContext) { private val TAG = "ProverModule" lateinit var res: GenerateProofResult - override fun getName(): String { return "Prover" } @ReactMethod - fun runInitAction(promise: Promise) { - // Launch a coroutine in the IO dispatcher for background tasks - CoroutineScope(Dispatchers.IO).launch { - try { - val startTime = System.currentTimeMillis() - uniffi.mopro.initializeMopro() - val endTime = System.currentTimeMillis() - val initTime = "init time: " + (endTime - startTime).toString() + " ms" - - // Since the promise needs to be resolved in the main thread - withContext(Dispatchers.Main) { - promise.resolve(initTime) - } - } catch (e: Exception) { - withContext(Dispatchers.Main) { - promise.reject(e) - } - } - } - } - - @ReactMethod - // fun runProveAction(inputs: ReadableMap, zkeypath: String, promise: Promise) { fun runProveAction(inputs: ReadableMap, promise: Promise) { Log.e(TAG, "inputs in provePassport kotlin: " + inputs.toString()) + val formattedInputs = mutableMapOf( + "mrz" to inputs.getArray("mrz")?.toArrayList()?.map { it.toString() }, + "reveal_bitmap" to inputs.getArray("reveal_bitmap")?.toArrayList()?.map { it.toString() }, + "dataHashes" to inputs.getArray("dataHashes")?.toArrayList()?.map { it.toString() }, + "datahashes_padded_length" to inputs.getArray("datahashes_padded_length")?.toArrayList()?.map { it.toString() }?.firstOrNull(), + "eContentBytes" to inputs.getArray("eContentBytes")?.toArrayList()?.map { it.toString() }, + "signature" to inputs.getArray("signature")?.toArrayList()?.map { it.toString() }, + "signatureAlgorithm" to inputs.getArray("signatureAlgorithm")?.toArrayList()?.map { it.toString() }?.firstOrNull(), + "pubkey" to inputs.getArray("pubkey")?.toArrayList()?.map { it.toString() }, + "pathIndices" to inputs.getArray("pathIndices")?.toArrayList()?.map { it.toString() }, + "siblings" to inputs.getArray("siblings")?.toArrayList()?.map { it.toString() }, + "root" to inputs.getArray("root")?.toArrayList()?.map { it.toString() }?.firstOrNull(), + "address" to inputs.getArray("address")?.toArrayList()?.map { it.toString() }?.firstOrNull(), + ) + + val gson = GsonBuilder().setPrettyPrinting().create() + Log.e(TAG, gson.toJson(formattedInputs)) + // working example // val inputs = mutableMapOf>( // "mrz" to listOf("97","91","95","31","88","80","60","70","82","65","84","65","86","69","82","78","73","69","82","60","60","70","76","79","82","69","78","84","60","72","85","71","85","69","83","60","74","69","65","78","60","60","60","60","60","60","60","60","60","49","57","72","65","51","52","56","50","56","52","70","82","65","48","48","48","55","49","57","49","77","50","57","49","50","48","57","53","60","60","60","60","60","60","60","60","60","60","60","60","60","60","48","50"), @@ -66,35 +67,258 @@ class ProverModule(reactContext: ReactApplicationContext) : ReactContextBaseJava // "address" to listOf("642829559307850963015472508762062935916233390536") // ) - val convertedInputs = mutableMapOf>() + // data class InputsPassport( + // val `in`: List, + // val currDateYear: Int, + // val currDateMonth: Int, + // val currDateDay: Int, + // val credValidYear: Int, + // val credValidMonth: Int, + // val credValidDay: Int, + // val ageLowerbound: Int + // ) - for ((key, value) in inputs.toHashMap()) { - val parsedArray = inputs.getArray(key)?.toArrayList()?.map { item -> - item.toString() - } ?: emptyList() - convertedInputs[key] = parsedArray - } + // val formattedInputs = InputsPassport( + // `in` = (dg1!!).toBitArray().toCharArray().map { it1 -> it1.digitToInt() }, + // currDateDay = current.dayOfMonth, + // credValidDay = nextDate.dayOfMonth, + // credValidMonth = nextDate.month.value, + // credValidYear = nextDate.year.toString().takeLast(2).toInt(), + // currDateMonth = current.month.value, + // currDateYear = current.year.toString().takeLast(2).toInt(), + // ageLowerbound = 18 + // ) - Log.e(TAG, "convertedInputs: $convertedInputs") + val jsonInputs = gson.toJson(formattedInputs).toByteArray() + val zkpTools = ZKPTools(reactApplicationContext) // <== same thing here + + // reactContext or context??? + val zkp: ZkProof = ZKPUseCase(reactApplicationContext).generateZKP( + R.raw.proof_of_passport_zkey, //zkeyID as Int + R.raw.proof_of_passport_dat, // datID as Int + jsonInputs, + zkpTools::witnesscalc_proof_of_passport + ) - val startTime = System.currentTimeMillis() - res = uniffi.mopro.generateProof2(convertedInputs) - val endTime = System.currentTimeMillis() - val provingTime = "proving time: " + (endTime - startTime).toString() + " ms" - Log.e(TAG, provingTime) + Log.e("ZKP", gson.toJson(zkp)) + + promise.resolve(zkp.toString()) + + + // val convertedInputs = mutableMapOf>() + + // for ((key, value) in inputs.toHashMap()) { + // val parsedArray = inputs.getArray(key)?.toArrayList()?.map { item -> + // item.toString() + // } ?: emptyList() + // convertedInputs[key] = parsedArray + // } + + // Log.e(TAG, "convertedInputs: $convertedInputs") + + // val startTime = System.currentTimeMillis() + // res = uniffi.mopro.generateProof2(convertedInputs) + // val endTime = System.currentTimeMillis() + // val provingTime = "proving time: " + (endTime - startTime).toString() + " ms" + // Log.e(TAG, provingTime) - Log.e(TAG, "res: " + res.toString()) + // Log.e(TAG, "res: " + res.toString()) - promise.resolve(res.toString()) - } - - @ReactMethod - fun runVerifyAction(promise: Promise) { - val startTime = System.currentTimeMillis() - val valid = "valid: " + uniffi.mopro.verifyProof2(res.proof, res.inputs).toString() - val endTime = System.currentTimeMillis() - val verifyingTime = "verifying time: " + (endTime - startTime).toString() + " ms" - Log.e(TAG, verifyingTime) - promise.resolve(valid) + // promise.resolve(res.toString()) } } + + +data class Proof( + val pi_a: List, + val pi_b: List>, + val pi_c: List, + val protocol: String, + var curve: String = "bn128" +) { + companion object { + fun fromJson(jsonString: String): Proof { + val json = Gson().fromJson(jsonString, Proof::class.java) + json.curve = getDefaultCurve() + return json + } + + private fun getDefaultCurve(): String { + return "bn128" + } + } +} + +data class ZkProof( + val proof: Proof, + val pub_signals: List +) + +class ZKPTools(val context: Context) { + external fun witnesscalc_proof_of_passport(circuitBuffer: ByteArray, + circuitSize: Long, + jsonBuffer: ByteArray, + jsonSize: Long, + wtnsBuffer: ByteArray, + wtnsSize: LongArray, + errorMsg: ByteArray, + errorMsgMaxSize: Long): Int + external fun CalcPublicBufferSize(zkeyBuffer: ByteArray, zkeySize: Long): Long + external fun groth16_prover( + zkeyBuffer: ByteArray, zkeySize: Long, + wtnsBuffer: ByteArray, wtnsSize: Long, + proofBuffer: ByteArray, proofSize: LongArray, + publicBuffer: ByteArray, publicSize: LongArray, + errorMsg: ByteArray, errorMsgMaxSize: Long + ): Int + + init { + System.loadLibrary("rapidsnark"); + System.loadLibrary("witnesscalc_proof_of_passport") // moonshot to avoid CMakeLists.txt and proofofpassport.cpp + // System.loadLibrary("proofofpassport") + } + + fun openRawResourceAsByteArray(resourceName: Int): ByteArray { + val inputStream = context.resources.openRawResource(resourceName) + val byteArrayOutputStream = ByteArrayOutputStream() + + try { + val buffer = ByteArray(1024) + var length: Int + + while (inputStream.read(buffer).also { length = it } != -1) { + byteArrayOutputStream.write(buffer, 0, length) + } + + return byteArrayOutputStream.toByteArray() + } finally { + byteArrayOutputStream.close() + inputStream.close() + } + } +} + +class ZKPUseCase(val context: Context) { + + fun generateZKP( + zkpId: Int, datFile: Int, inputs: ByteArray, proofFunction: ( + circuitBuffer: ByteArray, + circuitSize: Long, + jsonBuffer: ByteArray, + jsonSize: Long, + wtnsBuffer: ByteArray, + wtnsSize: LongArray, + errorMsg: ByteArray, + errorMsgMaxSize: Long + ) -> Int + ): ZkProof { + val zkpTool = ZKPTools(context) + val zkp = zkpTool.openRawResourceAsByteArray(zkpId) + val datFile = zkpTool.openRawResourceAsByteArray(datFile) + + val msg = ByteArray(256) + + val witnessLen = LongArray(1) + witnessLen[0] = 100 * 1024 * 1024 + + val byteArr = ByteArray(100 * 1024 * 1024) + + val res = proofFunction( + datFile, + datFile.size.toLong(), + inputs, + inputs.size.toLong(), + byteArr, + witnessLen, + msg, + 256 + ) + + Log.e("ZKPUseCase", "Witness gen res: $res") + Log.e("ZKPUseCase", "Witness gen return length: ${byteArr.size}") + Log.e("ZKPUseCase", "witnessLen: $witnessLen") + + if (res == 2) { + throw Exception("Not enough memory for zkp") + } + + if (res == 1) { + throw Exception("Error during zkp ${msg.decodeToString()}") + } + + // val pubData = ByteArray(4 *1024 *1024) + + + // val pubLen = LongArray(1) + // pubLen[0] = pubData.size.toLong() + + // val proofData = ByteArray(4*1024*1024) + // val proofLen = LongArray(1) + // proofLen[0] = proofData.size.toLong() + + // val witnessData = byteArr.copyOfRange(0, witnessLen[0].toInt()) + + // val verification = zkpTool.groth16_prover( + // zkp, + // zkp.size.toLong(), + // witnessData, + // witnessLen[0], + // proofData, + // proofLen, + // pubData, + // pubLen, + // msg, + // 256 + // ) + + // if (verification == 2) { + // throw Exception("Not enough memory for verification ${msg.decodeToString()}") + // } + + // if (verification == 1) { + // throw Exception("Error during verification ${msg.decodeToString()}") + // } + + // val proofDataZip = proofData.copyOfRange(0, proofLen[0].toInt()) + + // val index = findLastIndexOfSubstring( + // proofDataZip.toString(Charsets.UTF_8), + // "\"protocol\":\"groth16\"}" + // ) + // val indexPubData = findLastIndexOfSubstring( + // pubData.decodeToString(), + // "]" + // ) + + // val formatedPubData = pubData.decodeToString().slice(0..indexPubData) + + // val foramtedProof = proofDataZip.toString(Charsets.UTF_8).slice(0..index) + // val proof = Proof.fromJson(foramtedProof) + + // return ZkProof( + // proof = proof, + // pub_signals = getPubSignals(formatedPubData).toList() + // ) + return ZkProof( + proof = Proof.fromJson(""), + pub_signals = emptyList() + ) + } + + private fun findLastIndexOfSubstring(mainString: String, searchString: String): Int { + val index = mainString.lastIndexOf(searchString) + + if (index != -1) { + // If substring is found, calculate the last index of the substring + return index + searchString.length - 1 + } + + return -1 + } + + private fun getPubSignals(jsonString: String): List { + val gson = Gson() + val stringArray = gson.fromJson(jsonString, Array::class.java) + return stringArray.toList() + } +} diff --git a/app/witnesscalc/.gitignore b/app/witnesscalc/.gitignore index cf20fe3c6..d94b00aee 100644 --- a/app/witnesscalc/.gitignore +++ b/app/witnesscalc/.gitignore @@ -76,3 +76,5 @@ build_witnesscalc_ios package* .idea/ + +build_witnesscalc_android \ No newline at end of file