mirror of
https://github.com/selfxyz/self.git
synced 2026-02-08 13:25:59 -05:00
building first draft of passport circuit with arkworks
This commit is contained in:
81
app/App.tsx
81
app/App.tsx
@@ -31,14 +31,14 @@ import {
|
||||
DEFAULT_ADDRESS,
|
||||
LOCAL_IP,
|
||||
} from '@env';
|
||||
import {PassportData} from './types/passportData';
|
||||
import {arraysAreEqual, dataHashesObjToArray} from './utils/utils';
|
||||
import {computeAndCheckEContent} from './utils/computeEContent';
|
||||
import {DataHash, PassportData} from './types/passportData';
|
||||
import {arraysAreEqual, bytesToBigDecimal, dataHashesObjToArray, formatAndConcatenateDataHashes, formatMrz, splitToWords} from './utils/utils';
|
||||
import {hash, toUnsignedByte} from './utils/computeEContent';
|
||||
|
||||
console.log('DEFAULT_PNUMBER', DEFAULT_PNUMBER);
|
||||
|
||||
const CACHE_DATA_IN_LOCAL_SERVER = true;
|
||||
const SKIP_SCAN = false;
|
||||
const SKIP_SCAN = true;
|
||||
|
||||
function App(): JSX.Element {
|
||||
const isDarkMode = useColorScheme() === 'dark';
|
||||
@@ -67,13 +67,17 @@ function App(): JSX.Element {
|
||||
|
||||
if (SKIP_SCAN && passportData === null) {
|
||||
console.log('skipping scan step...');
|
||||
fetch(`${LOCAL_IP}/passportData`)
|
||||
try {
|
||||
fetch(`${LOCAL_IP}/passportData`)
|
||||
.then(response => response.json())
|
||||
.then(data => {
|
||||
console.log('passport data fetched');
|
||||
setPassportData(data);
|
||||
setStep('scanCompleted');
|
||||
});
|
||||
} catch(e) {
|
||||
console.log('error fetching passport data', e);
|
||||
}
|
||||
}
|
||||
|
||||
async function handleResponse(response: any) {
|
||||
@@ -148,21 +152,52 @@ function App(): JSX.Element {
|
||||
return;
|
||||
}
|
||||
|
||||
// 1. Compute the eContent from the mrzInfo and the dataGroupHashes
|
||||
const computedEContent = computeAndCheckEContent(passportData);
|
||||
// 1. TODO check signature to make sure the proof will work
|
||||
|
||||
if (!arraysAreEqual(computedEContent, passportData.eContent)) {
|
||||
throw new Error(
|
||||
'computed eContent does not match the one from passportData',
|
||||
);
|
||||
// 2. Format all the data as inputs for the circuit
|
||||
const formattedMrz = formatMrz(passportData.mrz);
|
||||
const mrzHash = hash(formatMrz(passportData.mrz));
|
||||
const concatenatedDataHashes = formatAndConcatenateDataHashes(
|
||||
mrzHash,
|
||||
passportData.dataGroupHashes as DataHash[],
|
||||
);
|
||||
|
||||
const reveal_bitmap = Array.from({ length: 88 }, (_, i) => (i >= 16 && i <= 22) ? '1' : '0');
|
||||
|
||||
const inputs = {
|
||||
mrz: Array.from(formattedMrz).map(byte => String(byte)),
|
||||
reveal_bitmap: Array.from(reveal_bitmap).map(byte => String(byte)),
|
||||
dataHashes: Array.from(concatenatedDataHashes.map(toUnsignedByte)).map(byte => String(byte)),
|
||||
eContentBytes: Array.from(passportData.eContent.map(toUnsignedByte)).map(byte => String(byte)),
|
||||
signature: splitToWords(
|
||||
BigInt(bytesToBigDecimal(passportData.encryptedDigest)),
|
||||
BigInt(64),
|
||||
BigInt(32)
|
||||
),
|
||||
pubkey: splitToWords(
|
||||
BigInt(passportData.modulus),
|
||||
BigInt(64),
|
||||
BigInt(32)
|
||||
),
|
||||
address,
|
||||
}
|
||||
// 3. Generate a proof of passport
|
||||
|
||||
// 2. TODO : check RSA signature to make sure the proof will work
|
||||
// Did not manage to do it cuz js crypto libs suck
|
||||
|
||||
// 3. Format all the data as inputs for the circuit
|
||||
|
||||
// 4. Generate a proof of passport
|
||||
const start = Date.now();
|
||||
NativeModules.RNPassportReader.provePassport(inputs, (err: any, res: any) => {
|
||||
const end = Date.now();
|
||||
if (err) {
|
||||
console.error(err);
|
||||
setProofResult(
|
||||
"res:" + err.toString() + ' time elapsed: ' + (end - start) + 'ms',
|
||||
);
|
||||
} else {
|
||||
console.log(res);
|
||||
setProofResult(
|
||||
"res:" + res.toString() + ' time elapsed: ' + (end - start) + 'ms',
|
||||
);
|
||||
}
|
||||
});
|
||||
};
|
||||
|
||||
const handleMint = () => {
|
||||
@@ -271,19 +306,17 @@ function App(): JSX.Element {
|
||||
) : null}
|
||||
{step === 'proofGenerated' ? (
|
||||
<View style={styles.sectionContainer}>
|
||||
<Text style={styles.header}>Zero-knowledge proof generated</Text>
|
||||
<Text style={styles.sectionDescription}>Zero-knowledge proof generated</Text>
|
||||
<Text style={styles.header}>You can now mint your SBT</Text>
|
||||
<Button title="Mint Proof of Passport" onPress={handleMint} />
|
||||
</View>
|
||||
) : null}
|
||||
</View>
|
||||
<View style={styles.sectionContainer}>
|
||||
<View style={{...styles.sectionContainer, marginTop: 80}}>
|
||||
<Button title="Call rust though java" onPress={callRustLib} />
|
||||
<Text style={styles.header}>{result}</Text>
|
||||
</View>
|
||||
<View style={styles.sectionContainer}>
|
||||
<Button title="Prove" onPress={proveRust} />
|
||||
<Text style={styles.header}>{proofResult}</Text>
|
||||
<Text style={styles.sectionDescription}>{result}</Text>
|
||||
<Button title="Test sample proof with arkworks" onPress={proveRust} />
|
||||
<Text style={styles.sectionDescription}>{proofResult}</Text>
|
||||
</View>
|
||||
</ScrollView>
|
||||
</SafeAreaView>
|
||||
|
||||
@@ -1,21 +1,48 @@
|
||||
To build libhalo2_passport.so, run in /script:
|
||||
# Embassy App
|
||||
|
||||
Only Android right now, under heavy development
|
||||
|
||||
#### Requirements
|
||||
|
||||
Install `nodejs v18`
|
||||
|
||||
#### Installation
|
||||
|
||||
```bash
|
||||
yarn
|
||||
```
|
||||
|
||||
#### Add circuit build
|
||||
|
||||
Go to this [repo](https://github.com/0xturboblitz/circom-embassy) and build the circuit.
|
||||
Then, place `passport.r1cs` and `passport.wasm` in ark-circom-passport/passport/
|
||||
|
||||
#### Build native lib
|
||||
|
||||
In /script, run:
|
||||
```
|
||||
./build_rust.sh
|
||||
```
|
||||
It will cross-compile using the config in app/android/react-native-passport-reader/android/build.gradle
|
||||
This will build the libhalo2_circom_passport.so lib and copy it to the desired place to be used by the app.
|
||||
The config used is in android/react-native-passport-reader/android/build.gradle.
|
||||
You can go there to change the profile (`debug` or `release`)
|
||||
|
||||
|
||||
|
||||
You'll potentially need to set the rust-toolchain rust version as global default. Example:
|
||||
```
|
||||
rustup default 1.67.0
|
||||
```
|
||||
And install the targets like:
|
||||
And install the targets like this:
|
||||
```
|
||||
rustup target add aarch64-linux-android
|
||||
```
|
||||
|
||||
|
||||
Then:
|
||||
yarn start => a or i
|
||||
To run the server:
|
||||
```
|
||||
yarn start
|
||||
```
|
||||
Then press `a` for android or `i` for iOS
|
||||
|
||||
|
||||
|
||||
|
||||
@@ -1 +1,2 @@
|
||||
node_modules/
|
||||
android/src/main/jniLibs/arm64/libark_circom_passport.so
|
||||
@@ -56,9 +56,9 @@ dependencies {
|
||||
apply plugin: 'org.mozilla.rust-android-gradle.rust-android'
|
||||
|
||||
cargo {
|
||||
module = "../../../ark-circom-rsa" // this works
|
||||
module = "../../../ark-circom-passport" // this works
|
||||
// module = "/Users/turboblitz/code/my-code/passport-sbt/app/halo2-passport"
|
||||
libname = "ark_circom_rsa" // Or whatever matches Cargo.toml's [package] name.
|
||||
libname = "ark_circom_passport" // Or whatever matches Cargo.toml's [package] name.
|
||||
// targets = ["arm", "x86"] // failing
|
||||
targets = ["arm64", "darwin-aarch64"] // Those work
|
||||
apiLevel = 29
|
||||
|
||||
@@ -92,6 +92,7 @@ import com.google.gson.Gson;
|
||||
|
||||
import com.facebook.react.ReactPackage
|
||||
import com.facebook.react.bridge.NativeModule
|
||||
import com.facebook.react.bridge.ReadableNativeMap
|
||||
import com.facebook.react.bridge.ReactApplicationContext
|
||||
import com.facebook.react.bridge.ReactContextBaseJavaModule
|
||||
import com.facebook.react.bridge.ReactMethod
|
||||
@@ -571,7 +572,34 @@ class RNPassportReaderModule(private val reactContext: ReactApplicationContext)
|
||||
// Return the result to JavaScript through the callback
|
||||
callback.invoke(null, resultFromProof)
|
||||
}
|
||||
|
||||
external fun provePassport(
|
||||
mrz: List<String>,
|
||||
reveal_bitmap: List<String>,
|
||||
dataHashes: List<String>,
|
||||
eContentBytes: List<String>,
|
||||
signature: List<String>,
|
||||
pubkey: List<String>,
|
||||
address: String
|
||||
): Int
|
||||
|
||||
@ReactMethod
|
||||
fun provePassport(inputs: ReadableMap, callback: Callback) {
|
||||
Log.d(TAG, "inputsaaa: " + inputs.toString())
|
||||
|
||||
val mrz = inputs.getArray("mrz")?.toArrayList()?.map { it as String } ?: listOf()
|
||||
val reveal_bitmap = inputs.getArray("reveal_bitmap")?.toArrayList()?.map { it as String } ?: listOf()
|
||||
val data_hashes = inputs.getArray("dataHashes")?.toArrayList()?.map { it as String } ?: listOf()
|
||||
val e_content_bytes = inputs.getArray("eContentBytes")?.toArrayList()?.map { it as String } ?: listOf()
|
||||
val signature = inputs.getArray("signature")?.toArrayList()?.map { it as String } ?: listOf()
|
||||
val pubkey = inputs.getArray("pubkey")?.toArrayList()?.map { it as String } ?: listOf()
|
||||
val address = inputs.getString("address") ?: ""
|
||||
|
||||
val resultFromProof = provePassport(mrz, reveal_bitmap, data_hashes, e_content_bytes, signature, pubkey, address)
|
||||
|
||||
// Return the result to JavaScript through the callback
|
||||
callback.invoke(null, resultFromProof)
|
||||
}
|
||||
|
||||
companion object {
|
||||
private val TAG = RNPassportReaderModule::class.java.simpleName
|
||||
@@ -582,7 +610,7 @@ class RNPassportReaderModule(private val reactContext: ReactApplicationContext)
|
||||
private const val KEY_IS_SUPPORTED = "isSupported"
|
||||
var instance: RNPassportReaderModule? = null
|
||||
init {
|
||||
System.loadLibrary("ark_circom_rsa")
|
||||
System.loadLibrary("ark_circom_passport")
|
||||
}
|
||||
}
|
||||
}
|
||||
3
app/ark-circom-passport/.gitignore
vendored
Normal file
3
app/ark-circom-passport/.gitignore
vendored
Normal file
@@ -0,0 +1,3 @@
|
||||
/target
|
||||
rsa
|
||||
passport
|
||||
3027
app/ark-circom-passport/Cargo.lock
generated
Normal file
3027
app/ark-circom-passport/Cargo.lock
generated
Normal file
File diff suppressed because it is too large
Load Diff
22
app/ark-circom-passport/Cargo.toml
Normal file
22
app/ark-circom-passport/Cargo.toml
Normal file
@@ -0,0 +1,22 @@
|
||||
[package]
|
||||
name = "ark_circom_passport"
|
||||
version = "0.1.0"
|
||||
edition = "2021"
|
||||
|
||||
[lib]
|
||||
name = "ark_circom_passport"
|
||||
path = "src/passport.rs"
|
||||
crate-type = ["cdylib"]
|
||||
|
||||
|
||||
[dependencies]
|
||||
ark-circom = { git = "https://github.com/0xturboblitz/circom-compat.git" }
|
||||
|
||||
ark-bn254 = { version = "=0.4.0" }
|
||||
ark-groth16 = { version = "=0.4.0", default-features = false, features = ["parallel"] }
|
||||
ark-std = { version = "=0.4.0", default-features = false, features = ["parallel"] }
|
||||
ark-crypto-primitives = { version = "=0.4.0" }
|
||||
color-eyre = "=0.6.2"
|
||||
jni = "0.18" # Choose the version that best fits your needs
|
||||
log = "0.4"
|
||||
android_logger = "0.8"
|
||||
3
app/ark-circom-passport/rust-toolchain.toml
Normal file
3
app/ark-circom-passport/rust-toolchain.toml
Normal file
@@ -0,0 +1,3 @@
|
||||
[toolchain]
|
||||
channel = "stable"
|
||||
version = "1.67.0"
|
||||
286
app/ark-circom-passport/src/passport.rs
Normal file
286
app/ark-circom-passport/src/passport.rs
Normal file
@@ -0,0 +1,286 @@
|
||||
use ark_circom::{CircomBuilder, CircomConfig};
|
||||
use ark_std::rand::thread_rng;
|
||||
use color_eyre::Result;
|
||||
use std::os::raw::c_int;
|
||||
|
||||
use ark_bn254::Bn254;
|
||||
use ark_crypto_primitives::snark::SNARK;
|
||||
use ark_groth16::Groth16;
|
||||
|
||||
use std::time::Instant;
|
||||
use std::convert::TryInto;
|
||||
|
||||
type GrothBn = Groth16<Bn254>;
|
||||
|
||||
extern crate jni;
|
||||
use jni::objects::{JClass, JObject, JValue, JString};
|
||||
use jni::JNIEnv;
|
||||
|
||||
use log::Level;
|
||||
use android_logger::Config;
|
||||
|
||||
#[no_mangle]
|
||||
pub extern "C" fn Java_io_tradle_nfc_RNPassportReaderModule_callRustCode(
|
||||
env: JNIEnv,
|
||||
_: JClass,
|
||||
) -> jni::sys::jstring {
|
||||
android_logger::init_once(Config::default().with_min_level(Level::Trace));
|
||||
log::warn!("log before imports");
|
||||
|
||||
let current_dir = std::env::current_dir().unwrap();
|
||||
let path_str = current_dir.to_str().unwrap();
|
||||
let output = env.new_string(path_str).expect("Couldn't create java string!");
|
||||
output.into_inner()
|
||||
}
|
||||
|
||||
#[no_mangle]
|
||||
pub extern "C" fn Java_io_tradle_nfc_RNPassportReaderModule_proveRSAInRust(
|
||||
_: JNIEnv,
|
||||
_: JClass,
|
||||
) -> c_int {
|
||||
fn run() -> Result<u128, Box<dyn std::error::Error>> {
|
||||
println!("log before imports");
|
||||
const MAIN_WASM: &'static [u8] = include_bytes!("../rsa/main.wasm");
|
||||
const MAIN_R1CS: &'static [u8] = include_bytes!("../rsa/main.r1cs");
|
||||
|
||||
let cfg = CircomConfig::<Bn254>::from_bytes(MAIN_WASM, MAIN_R1CS)?;
|
||||
|
||||
let mut builder = CircomBuilder::new(cfg);
|
||||
|
||||
let signature: [u128; 32] = [
|
||||
4993543337487904319, 5039260395924778555,
|
||||
16044715263198697509, 6517674227143205114,
|
||||
9783381675666809188, 7797234981612410535,
|
||||
9712659746244703685, 8223984644219552691,
|
||||
5746171858797010138, 16352708903743190663,
|
||||
11557514992480971638, 13495509591487042457,
|
||||
11156826800435483355, 7934676927345641909,
|
||||
17671838456179191719, 15427313345670295171,
|
||||
3979639931302305273, 10870708508897347751,
|
||||
17325747030660864416, 4196229958717243275,
|
||||
8295837152932404523, 5206285193355768709,
|
||||
16500962385150574058, 45927554409508738,
|
||||
11056427006453546685, 3610340837562714815,
|
||||
2914954158206709664, 9941999032204203280,
|
||||
3682966980231699250, 1089954850805856847,
|
||||
12801803660741250853, 6643401487810361365
|
||||
];
|
||||
let modulus: [u128; 32] = [
|
||||
14637485623069577853, 7482098129440337882,
|
||||
9329095990282353414, 13124250581866537330,
|
||||
18349306516477384309, 3633589540637627345,
|
||||
756443621693602880, 9532268969225926567,
|
||||
10797289495421403158, 8716880397646489088,
|
||||
16390100705849925925, 4946748147388408397,
|
||||
5159237052852568257, 4383482229078465345,
|
||||
17440536203309797881, 9244726556354794825,
|
||||
13954964489103323762, 12859274108738823253,
|
||||
15430872548874177827, 8078236913810864353,
|
||||
13311543254088155939, 6627932043456339426,
|
||||
10937476704429447948, 4860889415451015006,
|
||||
4549761793924050171, 1117773587704762559,
|
||||
13984923195668836033, 5179232650854575709,
|
||||
16174751231280536837, 9625446134615655537,
|
||||
6169436660688221259, 13128400207083283532
|
||||
];
|
||||
let base_message: [u128; 32] = [
|
||||
3626324085499461436, 15137430623782848370,
|
||||
13410089559264023318, 7272337899472972005,
|
||||
0, 0,
|
||||
0, 0,
|
||||
0, 0,
|
||||
0, 0,
|
||||
0, 0,
|
||||
0, 0,
|
||||
0, 0,
|
||||
0, 0,
|
||||
0, 0,
|
||||
0, 0,
|
||||
0, 0,
|
||||
0, 0,
|
||||
0, 0,
|
||||
0, 0
|
||||
];
|
||||
|
||||
for &elem in signature.iter() {
|
||||
builder.push_input("signature", elem);
|
||||
}
|
||||
for &elem in modulus.iter() {
|
||||
builder.push_input("modulus", elem);
|
||||
}
|
||||
for &elem in base_message.iter() {
|
||||
builder.push_input("base_message", elem);
|
||||
}
|
||||
|
||||
// create an empty instance for setting it up
|
||||
let circom = builder.setup();
|
||||
|
||||
let mut rng = thread_rng();
|
||||
let params = GrothBn::generate_random_parameters_with_reduction(circom, &mut rng)?;
|
||||
|
||||
let circom = builder.build()?;
|
||||
println!("circuit built");
|
||||
|
||||
let inputs = circom.get_public_inputs().unwrap();
|
||||
|
||||
let start1 = Instant::now();
|
||||
|
||||
let proof = GrothBn::prove(¶ms, circom, &mut rng)?;
|
||||
let duration1 = start1.elapsed();
|
||||
println!("proof generated. Took: {:?}", duration1);
|
||||
|
||||
let start2 = Instant::now();
|
||||
|
||||
let pvk = GrothBn::process_vk(¶ms.vk).unwrap();
|
||||
|
||||
let verified = GrothBn::verify_with_processed_vk(&pvk, &inputs, &proof)?;
|
||||
let duration2 = start2.elapsed();
|
||||
println!("proof verified. Took: {:?}", duration2);
|
||||
|
||||
assert!(verified);
|
||||
|
||||
Ok(duration1.as_millis())
|
||||
}
|
||||
match run() {
|
||||
Ok(elapsed_millis) => elapsed_millis as i32, // Assuming the elapsed time will fit in an i32
|
||||
Err(_) => -1, // return -1 or some other error code when there's an error
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
#[no_mangle]
|
||||
pub extern "C" fn Java_io_tradle_nfc_RNPassportReaderModule_provePassport(
|
||||
env: JNIEnv,
|
||||
_: JClass,
|
||||
mrz: JObject,
|
||||
reveal_bitmap: JObject,
|
||||
data_hashes: JObject,
|
||||
e_content_bytes: JObject,
|
||||
signature: JObject,
|
||||
pubkey: JObject,
|
||||
address: JString,
|
||||
) -> c_int {
|
||||
|
||||
log::warn!("formatting inputsaaaa...");
|
||||
fn run_proof(
|
||||
mrz: JObject,
|
||||
reveal_bitmap: JObject,
|
||||
data_hashes: JObject,
|
||||
e_content_bytes: JObject,
|
||||
signature: JObject,
|
||||
pubkey: JObject,
|
||||
address: JString,
|
||||
env: JNIEnv
|
||||
) -> Result<u128, Box<dyn std::error::Error>> {
|
||||
android_logger::init_once(Config::default().with_min_level(Level::Trace));
|
||||
|
||||
log::warn!("formatting inputs...");
|
||||
log::warn!("mrz_veccccccc");
|
||||
|
||||
let mrz_vec: Vec<String> = java_arraylist_to_rust_vec(&env, mrz)?;
|
||||
let reveal_bitmap_vec: Vec<String> = java_arraylist_to_rust_vec(&env, reveal_bitmap)?;
|
||||
let data_hashes_vec: Vec<String> = java_arraylist_to_rust_vec(&env, data_hashes)?;
|
||||
let e_content_bytes_vec: Vec<String> = java_arraylist_to_rust_vec(&env, e_content_bytes)?;
|
||||
let signature_vec: Vec<String> = java_arraylist_to_rust_vec(&env, signature)?;
|
||||
let pubkey_vec: Vec<String> = java_arraylist_to_rust_vec(&env, pubkey)?;
|
||||
let address_str: String = env.get_string(address)?.into();
|
||||
|
||||
log::warn!("mrz_vec {:?}", mrz_vec);
|
||||
log::warn!("reveal_bitmap_vec {:?}", reveal_bitmap_vec);
|
||||
log::warn!("data_hashes_vec {:?}", data_hashes_vec);
|
||||
log::warn!("e_content_bytes_vec {:?}", e_content_bytes_vec);
|
||||
log::warn!("signature_vec {:?}", signature_vec);
|
||||
log::warn!("pubkey_vec {:?}", pubkey_vec);
|
||||
log::warn!("address_str {:?}", address_str);
|
||||
|
||||
log::warn!("loading circuit...");
|
||||
const MAIN_WASM: &'static [u8] = include_bytes!("../passport/passport.wasm");
|
||||
const MAIN_R1CS: &'static [u8] = include_bytes!("../passport/passport.r1cs");
|
||||
log::warn!("circuit loaded");
|
||||
|
||||
let cfg = CircomConfig::<Bn254>::from_bytes(MAIN_WASM, MAIN_R1CS)?;
|
||||
|
||||
let mut builder = CircomBuilder::new(cfg);
|
||||
|
||||
mrz_vec.iter()
|
||||
.filter_map(|s| s.parse::<u128>().ok())
|
||||
.for_each(|n| builder.push_input("mrz", n));
|
||||
reveal_bitmap_vec.iter()
|
||||
.filter_map(|s| s.parse::<u128>().ok())
|
||||
.for_each(|n| builder.push_input("reveal_bitmap", n));
|
||||
data_hashes_vec.iter()
|
||||
.filter_map(|s| s.parse::<u128>().ok())
|
||||
.for_each(|n| builder.push_input("dataHashes", n));
|
||||
e_content_bytes_vec.iter()
|
||||
.filter_map(|s| s.parse::<u128>().ok())
|
||||
.for_each(|n| builder.push_input("eContentBytes", n));
|
||||
signature_vec.iter()
|
||||
.filter_map(|s| s.parse::<u128>().ok())
|
||||
.for_each(|n| builder.push_input("signature", n));
|
||||
pubkey_vec.iter()
|
||||
.filter_map(|s| s.parse::<u128>().ok())
|
||||
.for_each(|n| builder.push_input("pubkey", n));
|
||||
|
||||
// create an empty instance for setting it up
|
||||
let circom = builder.setup();
|
||||
|
||||
let mut rng = thread_rng();
|
||||
let params = GrothBn::generate_random_parameters_with_reduction(circom, &mut rng)?;
|
||||
|
||||
let circom = builder.build()?;
|
||||
println!("circuit built");
|
||||
|
||||
let inputs = circom.get_public_inputs().unwrap();
|
||||
|
||||
let start1 = Instant::now();
|
||||
|
||||
let proof = GrothBn::prove(¶ms, circom, &mut rng)?;
|
||||
let duration1 = start1.elapsed();
|
||||
println!("proof generated. Took: {:?}", duration1);
|
||||
|
||||
let start2 = Instant::now();
|
||||
|
||||
let pvk = GrothBn::process_vk(¶ms.vk).unwrap();
|
||||
|
||||
let verified = GrothBn::verify_with_processed_vk(&pvk, &inputs, &proof)?;
|
||||
let duration2 = start2.elapsed();
|
||||
println!("proof verified. Took: {:?}", duration2);
|
||||
|
||||
assert!(verified);
|
||||
|
||||
Ok(duration1.as_millis())
|
||||
}
|
||||
|
||||
match run_proof(
|
||||
mrz,
|
||||
reveal_bitmap,
|
||||
data_hashes,
|
||||
e_content_bytes,
|
||||
signature,
|
||||
pubkey,
|
||||
address,
|
||||
env
|
||||
) {
|
||||
Ok(elapsed_millis) => elapsed_millis as i32, // Assuming the elapsed time will fit in an i32
|
||||
Err(_) => -1, // return -1 or some other error code when there's an error
|
||||
}
|
||||
}
|
||||
|
||||
fn java_arraylist_to_rust_vec(env: &JNIEnv, java_list: JObject) -> Result<Vec<String>, jni::errors::Error> {
|
||||
let size = env.call_method(java_list, "size", "()I", &[])?.i()? as i32;
|
||||
let mut vec = Vec::with_capacity(size.try_into().unwrap());
|
||||
|
||||
for i in 0..size {
|
||||
let java_string = env.call_method(java_list, "get", "(I)Ljava/lang/Object;", &[JValue::from(i)])?.l()?;
|
||||
let rust_string: String = env.get_string(java_string.into())?.into();
|
||||
vec.push(rust_string);
|
||||
}
|
||||
|
||||
Ok(vec)
|
||||
}
|
||||
|
||||
|
||||
|
||||
// -message:"BpfCoordinator" -message:"MATCH" -message:"maximum" -message:"exception" level:WARN
|
||||
@@ -6,4 +6,12 @@ cd ../android
|
||||
cd ..
|
||||
|
||||
mkdir -p android/react-native-passport-reader/android/src/main/jniLibs/arm64/
|
||||
cp ark-circom-rsa/target/aarch64-linux-android/release/libark_circom_rsa.so android/react-native-passport-reader/android/src/main/jniLibs/arm64/
|
||||
if [ -f "ark-circom-passport/target/aarch64-linux-android/release/libark_circom_passport.so" ]; then
|
||||
echo copied release version
|
||||
cp ark-circom-passport/target/aarch64-linux-android/release/libark_circom_passport.so android/react-native-passport-reader/android/src/main/jniLibs/arm64/
|
||||
elif [ -f "ark-circom-passport/target/aarch64-linux-android/debug/libark_circom_passport.so" ]; then
|
||||
echo copied debug version
|
||||
cp ark-circom-passport/target/aarch64-linux-android/debug/libark_circom_passport.so android/react-native-passport-reader/android/src/main/jniLibs/arm64/
|
||||
fi
|
||||
|
||||
|
||||
|
||||
@@ -11,9 +11,10 @@ app.post('/post', (req: Request, res: Response) => {
|
||||
const data = req.body;
|
||||
fs.writeFile('passportData.json', JSON.stringify(data, null, 2), err => {
|
||||
if (err) {
|
||||
console.log(err);
|
||||
console.log("err posting new passportData", err);
|
||||
res.status(500).json({message: 'An error occurred while writing file'});
|
||||
} else {
|
||||
console.log("File written successfully");
|
||||
res.json({message: 'File written successfully'});
|
||||
}
|
||||
});
|
||||
@@ -22,9 +23,11 @@ app.post('/post', (req: Request, res: Response) => {
|
||||
app.get('/passportData', (req: Request, res: Response) => {
|
||||
fs.readFile('passportData.json', (err, data) => {
|
||||
if (err) {
|
||||
console.log("err fetching passportData", err);
|
||||
console.log(err);
|
||||
res.status(500).json({message: 'An error occurred while reading file'});
|
||||
} else {
|
||||
console.log("passportData fetched");
|
||||
res.json(JSON.parse(data.toString()));
|
||||
}
|
||||
});
|
||||
|
||||
@@ -9,36 +9,6 @@ import {
|
||||
} from './utils';
|
||||
import {sha256} from 'js-sha256';
|
||||
|
||||
export function computeAndCheckEContent(passportData: PassportData) {
|
||||
const mrz = assembleMrz(passportData.mrzInfo);
|
||||
const dataHashes = passportData.dataGroupHashes;
|
||||
const mrzHash = hash(formatMrz(mrz));
|
||||
|
||||
if (!arraysAreEqual(mrzHash, dataHashes[0][1])) {
|
||||
throw new Error('MRZ hash does not match data group hash 1');
|
||||
}
|
||||
|
||||
const concatenatedDataHashes = formatAndConcatenateDataHashes(
|
||||
mrzHash,
|
||||
dataHashes,
|
||||
);
|
||||
|
||||
if (
|
||||
!arraysAreEqual(
|
||||
concatenatedDataHashes,
|
||||
passportData.contentBytes.content.string,
|
||||
)
|
||||
) {
|
||||
throw new Error('Concatenated data hashes do not match content bytes');
|
||||
}
|
||||
|
||||
const concatenatedDataHashesHashDigest = hash(concatenatedDataHashes);
|
||||
|
||||
const timeOfSignature = findTimeOfSignature(passportData.eContentDecomposed);
|
||||
|
||||
return assembleEContent(concatenatedDataHashesHashDigest, timeOfSignature);
|
||||
}
|
||||
|
||||
// hash logic here because the one in utils.ts only works with node
|
||||
export function hash(bytesArray: number[]) {
|
||||
let unsignedBytesArray = bytesArray.map(toUnsignedByte);
|
||||
@@ -46,7 +16,7 @@ export function hash(bytesArray: number[]) {
|
||||
return hexToSignedBytes(hash);
|
||||
}
|
||||
|
||||
function hexToSignedBytes(hexString: string) {
|
||||
export function hexToSignedBytes(hexString: string) {
|
||||
let bytes = [];
|
||||
for (let i = 0; i < hexString.length - 1; i += 2) {
|
||||
let byte = parseInt(hexString.substr(i, 2), 16);
|
||||
@@ -55,6 +25,6 @@ function hexToSignedBytes(hexString: string) {
|
||||
return bytes;
|
||||
}
|
||||
|
||||
function toUnsignedByte(signedByte: number) {
|
||||
export function toUnsignedByte(signedByte: number) {
|
||||
return signedByte < 0 ? signedByte + 256 : signedByte;
|
||||
}
|
||||
|
||||
@@ -109,22 +109,20 @@ export function formatAndConcatenateDataHashes(
|
||||
// Let's replace the first array with the MRZ hash
|
||||
dataHashes.shift();
|
||||
dataHashes.unshift([1, mrzHash]);
|
||||
// Concaténons les dataHashes :
|
||||
const concatenatedDataHashes: number[] = [].concat(
|
||||
...dataHashes.map((dataHash: any) => {
|
||||
dataHash[1].unshift(...[48, 37, 2, 1, dataHash[0], 4, 32]);
|
||||
return dataHash[1];
|
||||
}),
|
||||
);
|
||||
|
||||
let concat: number[] = []
|
||||
|
||||
// Starting sequence. Should be the same for everybody, but not sure
|
||||
concatenatedDataHashes.unshift(
|
||||
...[
|
||||
48, -126, 1, 37, 2, 1, 0, 48, 11, 6, 9, 96, -122, 72, 1, 101, 3, 4, 2, 1,
|
||||
48, -126, 1, 17,
|
||||
],
|
||||
);
|
||||
return concatenatedDataHashes;
|
||||
concat.push(...[
|
||||
48, -126, 1, 37, 2, 1, 0, 48, 11, 6, 9, 96, -122, 72, 1, 101, 3, 4, 2, 1,
|
||||
48, -126, 1, 17,
|
||||
])
|
||||
|
||||
for(const dataHash of dataHashes) {
|
||||
concat.push(...[48, 37, 2, 1, dataHash[0], 4, 32, ...dataHash[1]])
|
||||
}
|
||||
|
||||
return concat;
|
||||
}
|
||||
|
||||
export function assembleEContent(
|
||||
@@ -160,3 +158,41 @@ export function assembleEContent(
|
||||
constructedEContent.push(...messageDigest);
|
||||
return constructedEContent;
|
||||
}
|
||||
|
||||
export const toBinaryString = (byte: any) => {
|
||||
const binary = (parseInt(byte, 10) & 0xFF).toString(2).padStart(8, '0');
|
||||
return binary;
|
||||
};
|
||||
|
||||
export function splitToWords(
|
||||
number: bigint,
|
||||
wordsize: bigint,
|
||||
numberElement: bigint
|
||||
) {
|
||||
let t = number
|
||||
const words: string[] = []
|
||||
for (let i = BigInt(0); i < numberElement; ++i) {
|
||||
const baseTwo = BigInt(2)
|
||||
|
||||
words.push(`${t % BigInt(Math.pow(Number(baseTwo), Number(wordsize)))}`)
|
||||
t = BigInt(t / BigInt(Math.pow(Number(BigInt(2)), Number(wordsize))))
|
||||
}
|
||||
if (!(t == BigInt(0))) {
|
||||
throw `Number ${number} does not fit in ${(
|
||||
wordsize * numberElement
|
||||
).toString()} bits`
|
||||
}
|
||||
return words
|
||||
}
|
||||
|
||||
export function bytesToBigDecimal(arr: number[]): string {
|
||||
let result = BigInt(0);
|
||||
for (let i = 0; i < arr.length; i++) {
|
||||
result = result * BigInt(256) + BigInt(arr[i] & 0xff);
|
||||
}
|
||||
return result.toString();
|
||||
}
|
||||
|
||||
export function hexToDecimal(hex: string): string {
|
||||
return BigInt(`0x${hex}`).toString();
|
||||
}
|
||||
Reference in New Issue
Block a user