Merge pull request #18 from zk-passport/addons

Addons
This commit is contained in:
turboblitz
2023-11-07 14:53:55 +03:00
committed by GitHub
12 changed files with 1973 additions and 149 deletions

View File

@@ -4,14 +4,10 @@ import {
ScrollView,
StatusBar,
StyleSheet,
Text,
useColorScheme,
View,
Button,
NativeModules,
DeviceEventEmitter,
TextInput,
ActivityIndicator,
} from 'react-native';
import {
@@ -21,6 +17,26 @@ import {
LearnMoreLinks,
ReloadInstructions,
} from 'react-native/Libraries/NewAppScreen';
import {
Text,
GluestackUIProvider,
Checkbox,
CheckboxIndicator,
CheckboxIcon,
CheckIcon,
CheckboxLabel,
Input,
InputField,
ButtonText,
ButtonIcon,
Button,
Spinner,
View,
ButtonSpinner,
} from "@gluestack-ui/themed"
import { config } from "@gluestack-ui/config" // Optional if you want to use default theme
// @ts-ignore
import PassportReader from 'react-native-passport-reader';
import {checkInputs, getFirstName} from './utils/checks';
@@ -32,15 +48,25 @@ import {
LOCAL_IP,
} from '@env';
import {DataHash, PassportData} from './types/passportData';
import {arraysAreEqual, bytesToBigDecimal, dataHashesObjToArray, formatAndConcatenateDataHashes, formatMrz, splitToWords} from './utils/utils';
import {arraysAreEqual, bytesToBigDecimal, dataHashesObjToArray, formatAndConcatenateDataHashes, formatDuration, formatMrz, formatProof, splitToWords} from './utils/utils';
import {hash, toUnsignedByte} from './utils/computeEContent';
console.log('DEFAULT_PNUMBER', DEFAULT_PNUMBER);
console.log('LOCAL_IP', LOCAL_IP);
const CACHE_DATA_IN_LOCAL_SERVER = true;
const CACHE_DATA_IN_LOCAL_SERVER = false;
const SKIP_SCAN = false;
const attributeToPosition = {
issuing_state: [2, 5],
name: [5, 44],
passport_number: [44, 52],
nationality: [54, 57],
date_of_birth: [57, 63],
gender: [64, 65],
expiry_date: [65, 71],
}
function App(): JSX.Element {
const isDarkMode = useColorScheme() === 'dark';
const [passportNumber, setPassportNumber] = useState(DEFAULT_PNUMBER ?? '');
@@ -49,9 +75,34 @@ function App(): JSX.Element {
const [address, setAddress] = useState(DEFAULT_ADDRESS ?? '');
const [passportData, setPassportData] = useState<PassportData | null>(null);
const [step, setStep] = useState('enterDetails');
const [result, setResult] = useState<string>('');
const [testResult, setTestResult] = useState<any>(null);
const [error, setError] = useState<any>(null);
const [generatingProof, setGeneratingProof] = useState<boolean>(false);
const [proofTime, setProofTime] = useState<number>(0);
const [totalTime, setTotalTime] = useState<number>(0);
const [proofResult, setProofResult] = useState<string>('');
const [minting, setMinting] = useState<boolean>(false);
const [disclosure, setDisclosure] = useState({
issuing_state: false,
name: false,
passport_number: false,
nationality: false,
date_of_birth: false,
gender: false,
expiry_date: false,
});
const handleDisclosureChange = (field: keyof typeof disclosure) => {
setDisclosure(
{...disclosure,
[field]: !disclosure[field]
});
};
const backgroundStyle = {
backgroundColor: isDarkMode ? Colors.darker : Colors.lighter,
};
@@ -84,7 +135,10 @@ function App(): JSX.Element {
async function handleResponse(response: any) {
const {
mrz,
signatureAlgorithm,
modulus,
curveName,
publicKeyQ,
dataGroupHashes,
eContent,
encryptedDigest,
@@ -92,14 +146,20 @@ function App(): JSX.Element {
const passportData: PassportData = {
mrz: mrz.replace(/\n/g, ''),
modulus: modulus,
signatureAlgorithm: signatureAlgorithm,
pubKey: {
modulus: modulus,
curveName: curveName,
publicKeyQ: publicKeyQ,
},
dataGroupHashes: dataHashesObjToArray(JSON.parse(dataGroupHashes)),
eContent: JSON.parse(eContent),
encryptedDigest: JSON.parse(encryptedDigest),
};
console.log('mrz', passportData.mrz);
console.log('modulus', passportData.modulus);
console.log('signatureAlgorithm', passportData.signatureAlgorithm);
console.log('pubKey', passportData.pubKey);
console.log('dataGroupHashes', passportData.dataGroupHashes);
console.log('eContent', passportData.eContent);
console.log('encryptedDigest', passportData.encryptedDigest);
@@ -153,6 +213,9 @@ function App(): JSX.Element {
return;
}
setGeneratingProof(true)
await new Promise(resolve => setTimeout(resolve, 10));
// 1. TODO check signature to make sure the proof will work
// 2. Format all the data as inputs for the circuit
@@ -163,11 +226,27 @@ function App(): JSX.Element {
passportData.dataGroupHashes as DataHash[],
);
const reveal_bitmap = Array.from({ length: 88 }, (_, i) => (i >= 16 && i <= 22) ? '1' : '0');
const reveal_bitmap = Array.from({ length: 88 }, (_) => '0');
for(const attribute in disclosure) {
if (disclosure[attribute as keyof typeof disclosure]) {
const [start, end] = attributeToPosition[attribute as keyof typeof attributeToPosition];
for(let i = start; i <= end; i++) {
reveal_bitmap[i] = '1';
}
}
}
if (!passportData.pubKey.modulus) {
console.log('ECDSA not supported for proof right now.');
setError('ECDSA not supported for proof right now.');
return;
}
const inputs = {
mrz: Array.from(formattedMrz).map(byte => String(byte)),
reveal_bitmap: Array.from(reveal_bitmap).map(byte => String(byte)),
reveal_bitmap: 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(
@@ -176,7 +255,7 @@ function App(): JSX.Element {
BigInt(32)
),
pubkey: splitToWords(
BigInt(passportData.modulus),
BigInt(passportData.pubKey.modulus),
BigInt(64),
BigInt(32)
),
@@ -187,35 +266,42 @@ function App(): JSX.Element {
const start = Date.now();
NativeModules.RNPassportReader.provePassport(inputs, (err: any, res: any) => {
const end = Date.now();
setGeneratingProof(false)
setStep('proofGenerated');
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',
setError(
"err: " + err.toString(),
);
return
}
console.log("res", res);
const parsedResponse = JSON.parse(res);
console.log('parsedResponse', parsedResponse);
console.log('parsedResponse.duration', parsedResponse.duration);
const deserializedProof = JSON.parse(parsedResponse.serialized_proof);
console.log('deserializedProof', deserializedProof);
const proofFormattedForSolidity = formatProof(deserializedProof);
console.log('proofFormattedForSolidity', proofFormattedForSolidity);
setProofTime(parsedResponse.duration);
setTotalTime(end - start);
setProofResult(JSON.stringify(proofFormattedForSolidity));
// les outputs publics vont être postés on-chain comment ?
});
};
const handleMint = () => {
setMinting(true)
// 5. Format the proof and publicInputs as calldata for the verifier contract
// 6. Call the verifier contract with the calldata
};
const callRustLib = async () => {
NativeModules.RNPassportReader.callRustLib((err: any, res: any) => {
if (err) {
console.error(err);
setResult(err.toString());
} else {
console.log(res); // Should log "5"
setResult(res.toString());
}
});
};
const proveRust = async () => {
@@ -236,91 +322,230 @@ function App(): JSX.Element {
});
};
const handleNative = async () => {
const value = await NativeModules.PassportReader.scanPassport('', '', '');
console.log(`native tells us ${value}`);
};
return (
<SafeAreaView style={backgroundStyle}>
<StatusBar
barStyle={isDarkMode ? 'light-content' : 'dark-content'}
backgroundColor={backgroundStyle.backgroundColor}
/>
<ScrollView
contentInsetAdjustmentBehavior="automatic"
style={backgroundStyle}>
<View
<GluestackUIProvider config={config}>
<SafeAreaView style={backgroundStyle}>
<StatusBar
barStyle={isDarkMode ? 'light-content' : 'dark-content'}
backgroundColor={backgroundStyle.backgroundColor}
/>
<ScrollView
contentInsetAdjustmentBehavior="automatic"
style={{
backgroundColor: isDarkMode ? Colors.black : Colors.white,
}}>
{step === 'enterDetails' ? (
<View style={styles.sectionContainer}>
<Text style={styles.header}>Enter Your Passport Details</Text>
<TextInput
style={styles.input}
onChangeText={setPassportNumber}
value={passportNumber}
placeholder="Passport Number"
/>
<TextInput
style={styles.input}
onChangeText={setDateOfBirth}
value={dateOfBirth}
placeholder="Date of Birth (YYMMDD)"
/>
<TextInput
style={styles.input}
onChangeText={setDateOfExpiry}
value={dateOfExpiry}
placeholder="Date of Expiry (YYMMDD)"
/>
<Button title="Scan Passport with NFC" onPress={scan} />
<Button title="Call native method" onPress={handleNative} />
</View>
) : null}
{step === 'scanning' ? (
<View style={styles.sectionContainer}>
<Text style={styles.header}>Put your phone on your passport</Text>
<ActivityIndicator
size="large"
color="#00ff00"
style={{marginTop: 20}}
/>
</View>
) : null}
{step === 'scanCompleted' ? (
<View style={styles.sectionContainer}>
<Text style={styles.header}>Connection successful</Text>
<Text style={styles.header}>
{passportData && `Hi ${getFirstName(passportData.mrz)} ! `}
</Text>
<Text style={styles.header}>Input your address or ens</Text>
<TextInput
style={styles.input}
onChangeText={setAddress}
value={address}
placeholder="Your Address or ens name"
/>
<Button title="Generate zk proof" onPress={handleProve} />
</View>
) : null}
{step === 'proofGenerated' ? (
<View style={styles.sectionContainer}>
<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, marginTop: 80}}>
<Button title="Call rust though java" onPress={callRustLib} />
<Text style={styles.sectionDescription}>{result}</Text>
<Button title="Test sample proof with arkworks" onPress={proveRust} />
<Text style={styles.sectionDescription}>{proofResult}</Text>
</View>
</ScrollView>
</SafeAreaView>
}}
>
<View>
{step === 'enterDetails' ? (
<View style={styles.sectionContainer}>
<Text style={styles.header}>Welcome to Proof of Passport</Text>
<Text style={{textAlign: "center", fontSize: 20, marginTop: 20, marginBottom: 20}}>Enter Your Passport Details</Text>
<Text>Passport Number</Text>
<Input
variant="outline"
size="md"
marginBottom={10}
marginTop={4}
>
<InputField
value={passportNumber}
onChangeText={setPassportNumber}
placeholder={DEFAULT_PNUMBER ?? 'Passport Number'}
/>
</Input>
<Text>Date of Birth</Text>
<Input
variant="outline"
size="md"
marginBottom={10}
marginTop={4}
>
<InputField
value={dateOfBirth}
onChangeText={setDateOfBirth}
placeholder={DEFAULT_DOB ?? "YYMMDD"}
/>
</Input>
<Text>Date of Expiry</Text>
<Input
variant="outline"
size="md"
marginBottom={10}
marginTop={4}
>
<InputField
value={dateOfExpiry}
onChangeText={setDateOfExpiry}
placeholder={DEFAULT_DOE ?? "YYMMDD"}
/>
</Input>
<Button
onPress={scan}
marginTop={10}
>
<ButtonText>Scan Passport with NFC</ButtonText>
{/* <ButtonIcon as={AddIcon} /> */}
</Button>
</View>
) : null}
{step === 'scanning' ? (
<View style={styles.sectionContainer}>
<Text style={styles.header}>Put your phone on your passport</Text>
<Spinner
size={60}
style={{marginTop: 70}}
/>
</View>
) : null}
{step === 'scanCompleted' && passportData ? (
<View style={styles.sectionContainer}>
<Text style={styles.header}>
Hi {getFirstName(passportData.mrz)}
</Text>
<View
marginTop={20}
marginBottom={20}
>
<Text
marginBottom={10}
>
What do you want to disclose ?
</Text>
{Object.keys(disclosure).map((key) => {
const keyy = key as keyof typeof disclosure;
const indexes = attributeToPosition[keyy];
const keyFormatted = keyy.replace(/_/g, ' ').split(' ').map(word => word.charAt(0).toUpperCase() + word.slice(1)).join(' ');
const mrzAttribute = passportData.mrz.slice(indexes[0], indexes[1])
const mrzAttributeFormatted = mrzAttribute.replace(/</g, ' ')
return (
<View key={key} margin={2} width={"$full"} flexDirection="row" justifyContent="space-between">
<View maxWidth={"$5/6"}>
<Text
style={{fontWeight: "bold"}}
>
{keyFormatted}:{" "}
</Text>
<Text>
{mrzAttributeFormatted}
</Text>
</View>
<Checkbox
value={key}
isChecked={disclosure[keyy]}
onChange={() => handleDisclosureChange(keyy)}
size="lg"
aria-label={key}
>
<CheckboxIndicator mr="$2">
<CheckboxIcon as={CheckIcon} />
</CheckboxIndicator>
</Checkbox>
</View>
)
})}
</View>
<Text>Enter your address or ens</Text>
<Input
variant="outline"
size="md"
marginBottom={10}
marginTop={4}
>
<InputField
value={address}
onChangeText={setAddress}
placeholder="Your Address or ens name"
/>
</Input>
{generatingProof ?
<Button
onPress={handleProve}
>
<ButtonSpinner mr="$1" />
<ButtonText>Generating zk proof</ButtonText>
</Button>
: <Button
onPress={handleProve}
>
<ButtonText>Generate zk proof</ButtonText>
</Button>
}
</View>
) : null}
{step === 'proofGenerated' ? (
<View style={styles.sectionContainer}>
<Text style={styles.header}>Zero-knowledge proof generated</Text>
<Text style={{fontWeight: "bold"}}>
Proof:
</Text>
<Text>
{proofResult}
</Text>
<Text>
<Text style={{ fontWeight: 'bold' }}>Proof Duration:</Text> {formatDuration(proofTime)}
</Text>
<Text>
<Text style={{ fontWeight: 'bold' }}>Total Duration:</Text> {formatDuration(totalTime)}
</Text>
{generatingProof ?
<Button
onPress={handleMint}
marginTop={10}
>
<ButtonSpinner mr="$1" />
<ButtonText>Minting Proof of Passport</ButtonText>
</Button>
: <Button
onPress={handleMint}
marginTop={10}
>
<ButtonText>Mint Proof of Passport</ButtonText>
</Button>
}
</View>
) : null}
</View>
<View style={{...styles.sectionContainer, marginTop: 80}}>
<Text style={{...styles.sectionDescription, textAlign: "center"}}>Test functions</Text>
<Button
onPress={async () => {
NativeModules.RNPassportReader.callRustLib((err: any, res: any) => {
if (err) {
console.error(err);
setTestResult(err);
} else {
console.log(res); // Should log "5"
setTestResult(res);
}
});
}}
marginTop={10}
>
<ButtonText>Call arkworks lib</ButtonText>
</Button>
{testResult && <Text>{testResult}</Text>}
<Button
onPress={proveRust}
marginTop={10}
>
<ButtonText>Generate sample proof with arkworks</ButtonText>
</Button>
{proofResult && <Text>{proofResult}</Text>}
{error && <Text>{error}</Text>}
</View>
</ScrollView>
</SafeAreaView>
</GluestackUIProvider>
);
}
@@ -342,17 +567,11 @@ const styles = StyleSheet.create({
fontWeight: '700',
},
header: {
fontSize: 24,
fontSize: 22,
fontWeight: 'bold',
textAlign: 'center',
marginTop: 20,
},
input: {
height: 40,
margin: 12,
borderWidth: 1,
padding: 10,
},
});
export default App;

View File

@@ -45,6 +45,9 @@ import org.bouncycastle.asn1.ASN1Primitive
import org.bouncycastle.asn1.ASN1Sequence
import org.bouncycastle.asn1.ASN1Set
import org.bouncycastle.asn1.x509.Certificate
import org.bouncycastle.jce.spec.ECNamedCurveSpec
import org.bouncycastle.jce.interfaces.ECPublicKey
import org.jmrtd.BACKey
import org.jmrtd.BACKeySpec
@@ -507,15 +510,40 @@ class RNPassportReaderModule(private val reactContext: ReactApplicationContext)
val eContentAsn1InputStream = ASN1InputStream(sodFile.eContent.inputStream())
val eContentDecomposed: ASN1Primitive = eContentAsn1InputStream.readObject()
val rsaPublicKey = sodFile.docSigningCertificate.publicKey as RSAPublicKey
val passport = Arguments.createMap()
passport.putString("mrz", mrzInfo.toString())
passport.putString("modulus", rsaPublicKey.modulus.toString())
passport.putString("signatureAlgorithm", sodFile.docSigningCertificate.sigAlgName) // this one is new
val publicKey = sodFile.docSigningCertificate.publicKey
if (publicKey is RSAPublicKey) {
passport.putString("modulus", publicKey.modulus.toString())
} else if (publicKey is ECPublicKey) {
// Handle the elliptic curve public key case
val w = publicKey.getW()
passport.putString("publicKeyW", w.toString())
val ecParams = publicKey.getParams()
passport.putInt("cofactor", ecParams.getCofactor())
passport.putString("curve", ecParams.getCurve().toString())
passport.putString("generator", ecParams.getGenerator().toString())
passport.putString("order", ecParams.getOrder().toString())
if (ecParams is ECNamedCurveSpec) {
passport.putString("curveName", ecParams.getName())
}
// Old one, probably wrong:
// passport.putString("curveName", (publicKey.parameters as ECNamedCurveSpec).name)
// // passport.putString("curveName", (publicKey.parameters.algorithm)) or maybe this
// passport.putString("publicKeyQ", publicKey.q.toString())
}
passport.putString("dataGroupHashes", gson.toJson(sodFile.dataGroupHashes))
passport.putString("eContent", gson.toJson(sodFile.eContent))
passport.putString("encryptedDigest", gson.toJson(sodFile.encryptedDigest))
// Another way to get signing time is to get into signedData.signerInfos, then search for the ICO identifier 1.2.840.113549.1.9.5
// passport.putString("signerInfos", gson.toJson(signedData.signerInfos))
@@ -581,7 +609,7 @@ class RNPassportReaderModule(private val reactContext: ReactApplicationContext)
signature: List<String>,
pubkey: List<String>,
address: String
): Int
): String
@ReactMethod
fun provePassport(inputs: ReadableMap, callback: Callback) {
@@ -594,9 +622,11 @@ class RNPassportReaderModule(private val reactContext: ReactApplicationContext)
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)
Log.d(TAG, "resultFromProof: " + resultFromProof.toString())
// Return the result to JavaScript through the callback
callback.invoke(null, resultFromProof)
}

View File

@@ -282,11 +282,15 @@ dependencies = [
"ark-bn254",
"ark-circom",
"ark-crypto-primitives",
"ark-ec",
"ark-groth16",
"ark-std",
"color-eyre",
"jni",
"log",
"serde",
"serde_derive",
"serde_json",
]
[[package]]

View File

@@ -16,7 +16,11 @@ 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" }
ark-ec = { version = "=0.4.1" }
color-eyre = "=0.6.2"
jni = "0.18" # Choose the version that best fits your needs
log = "0.4"
android_logger = "0.8"
android_logger = "0.8"
serde = "1.0"
serde_json = "1.0"
serde_derive = "1.0"

View File

@@ -5,7 +5,9 @@ use std::os::raw::c_int;
use ark_bn254::Bn254;
use ark_crypto_primitives::snark::SNARK;
use ark_groth16::Groth16;
use ark_groth16::{Groth16, Proof};
// use ark_ff::QuadExtField;
use ark_ec::AffineRepr;
use std::time::Instant;
use std::convert::TryInto;
@@ -15,21 +17,38 @@ type GrothBn = Groth16<Bn254>;
extern crate jni;
use jni::objects::{JClass, JObject, JValue, JString};
use jni::JNIEnv;
use jni::sys::jobject;
use jni::sys::jstring;
use log::Level;
use android_logger::Config;
extern crate serde;
extern crate serde_json;
use serde_json::json;
#[macro_use]
extern crate serde_derive;
#[no_mangle]
pub extern "C" fn Java_io_tradle_nfc_RNPassportReaderModule_callRustCode(
env: JNIEnv,
_: JClass,
) -> jni::sys::jstring {
) -> 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!");
let my_int: c_int = -1;
let my_str: String = "no_proof".to_string();
let combined = json!({
"my_int": my_int,
"my_str": my_str
});
let combined_str = combined.to_string();
let output = env.new_string(combined_str).expect("Couldn't create java string!");
output.into_inner()
}
@@ -62,7 +81,7 @@ pub extern "C" fn Java_io_tradle_nfc_RNPassportReaderModule_provePassport(
signature: JObject,
pubkey: JObject,
address: JString,
) -> c_int {
) -> jstring {
log::warn!("formatting inputsaaaa...");
fn run_proof(
@@ -74,7 +93,7 @@ pub extern "C" fn Java_io_tradle_nfc_RNPassportReaderModule_provePassport(
pubkey: JObject,
address: JString,
env: JNIEnv
) -> Result<u128, Box<dyn std::error::Error>> {
) -> Result<jstring, Box<dyn std::error::Error>> {
android_logger::init_once(Config::default().with_min_level(Level::Trace));
log::warn!("formatting inputs...");
@@ -138,6 +157,11 @@ pub extern "C" fn Java_io_tradle_nfc_RNPassportReaderModule_provePassport(
let start1 = Instant::now();
let proof = GrothBn::prove(&params, circom, &mut rng)?;
let proof_str = proof_to_proof_str(&proof);
let serialized_proof = serde_json::to_string(&proof_str).unwrap();
let duration1 = start1.elapsed();
println!("proof generated. Took: {:?}", duration1);
@@ -151,7 +175,15 @@ pub extern "C" fn Java_io_tradle_nfc_RNPassportReaderModule_provePassport(
assert!(verified);
Ok(duration1.as_millis())
let combined = json!({
"duration": duration1.as_millis(),
"serialized_proof": serialized_proof
});
let combined_str = combined.to_string();
let output = env.new_string(combined_str).expect("Couldn't create java string!");
Ok(output.into_inner())
}
match run_proof(
@@ -164,8 +196,8 @@ pub extern "C" fn Java_io_tradle_nfc_RNPassportReaderModule_provePassport(
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
Ok(output) => output,
Err(_) => env.new_string("error").expect("Couldn't create java string!").into_inner(),
}
}
@@ -182,6 +214,29 @@ fn java_arraylist_to_rust_vec(env: &JNIEnv, java_list: JObject) -> Result<Vec<St
Ok(vec)
}
// -message:"BpfCoordinator" -message:"MATCH" -message:"maximum" -message:"exception" level:WARN
#[derive(Debug)]
#[derive(Serialize)]
struct ProofStr {
a: (String, String),
b: ((String, String), (String, String)), // Represent each QuadExtField by two BaseFields
c: (String, String),
}
// -message:"BpfCoordinator" -message:"MATCH" -message:"maximum" -message:"exception" level:WARN
fn proof_to_proof_str(proof: &Proof<Bn254>) -> ProofStr {
let a_xy = proof.a.xy().unwrap();
let b_xy = proof.b.xy().unwrap();
let c_xy = proof.c.xy().unwrap();
let b_c0_c0 = b_xy.0.c0.to_string();
let b_c0_c1 = b_xy.0.c1.to_string();
let b_c1_c0 = b_xy.1.c0.to_string();
let b_c1_c1 = b_xy.1.c1.to_string();
ProofStr {
a: (a_xy.0.to_string(), a_xy.1.to_string()),
b: ((b_c0_c0, b_c0_c1), (b_c1_c0, b_c1_c1)),
c: (c_xy.0.to_string(), c_xy.1.to_string()),
}
}

30
app/countries.md Normal file
View File

@@ -0,0 +1,30 @@
Working:
- France
- Guy from ETH Global Paris (Moldavia ? Bulgaria ?)
- Malaysia
Crashing:
- Thailand
- Britain
E FATAL EXCEPTION: main
Process: com.awesomeproject, PID: 14479
java.lang.ClassCastException: org.bouncycastle.jcajce.provider.asymmetric.ec.BCECPublicKey cannot be cast to java.security.interfaces.RSAPublicKey
at io.tradle.nfc.RNPassportReaderModule$ReadTask.onPostExecute(RNPassportReaderModule.kt:510)
at io.tradle.nfc.RNPassportReaderModule$ReadTask.onPostExecute(RNPassportReaderModule.kt:238)
at android.os.AsyncTask.finish(AsyncTask.java:771)
at android.os.AsyncTask.access$900(AsyncTask.java:199)
at android.os.AsyncTask$InternalHandler.handleMessage(AsyncTask.java:788)
at android.os.Handler.dispatchMessage(Handler.java:106)
at android.os.Looper.loopOnce(Looper.java:226)
at android.os.Looper.loop(Looper.java:313)
at android.app.ActivityThread.main(ActivityThread.java:8751)
at java.lang.reflect.Method.invoke(Native Method)
at com.android.internal.os.RuntimeInit$MethodAndArgsCaller.run(RuntimeInit.java:571)
at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:1135)
2023-10-31 16:41:37.052 625-625 SurfaceFlinger pid-625 E Attempt to update InputPolicyFlags without permission ACCESS_SURFACE_FLINGER
2023-10-31 16:41:37.069 625-625 SurfaceFlinger pid-625 E Attempt to update InputPolicyFlags without permission ACCESS_SURFACE_FLINGER
2023-10-31 16:41:37.088 625-625 SurfaceFlinger pid-625 E Attempt to update InputPolicyFlags without permission ACCESS_SURFACE_FLINGER
2023-10-31 16:41:37.102 625-625 SurfaceFlinger pid-625 E Attempt to update InputPolicyFlags without permission ACCESS_SURFACE_FLINGER
2023-10-31 16:41:37.108 1377-2420 TaskStackL...erAbstract pid-1377 E onTaskSnapshotChanged calle

View File

@@ -1,5 +1,5 @@
{
"name": "AwesomeProject",
"name": "proof-of-passport",
"version": "0.0.1",
"private": true,
"scripts": {
@@ -10,6 +10,9 @@
"test": "jest"
},
"dependencies": {
"@gluestack-style/react": "^1.0.12",
"@gluestack-ui/config": "^1.0.3",
"@gluestack-ui/themed": "^1.0.11",
"body-parser": "^1.20.2",
"buffer": "^6.0.3",
"crypto-js": "^4.1.1",
@@ -19,7 +22,8 @@
"pvutils": "^1.1.3",
"react": "18.2.0",
"react-native": "0.72.3",
"react-native-passport-reader": "^1.0.3"
"react-native-passport-reader": "^1.0.3",
"react-native-svg": "13.4.0"
},
"devDependencies": {
"@babel/core": "^7.20.0",

View File

@@ -20,7 +20,8 @@ export type DataHash = [number, number[]];
export type PassportData = {
mrz: string;
modulus: string;
signatureAlgorithm: string;
pubKey: {modulus?: string, curveName?: string, publicKeyQ?: string};
dataGroupHashes: DataHash[];
eContent: number[];
encryptedDigest: number[];

View File

@@ -18,5 +18,6 @@ export function checkInputs(
export function getFirstName(mrz: string): string {
const names = mrz.split("<<");
const firstName = names[1].split("<")[0].trim();
return firstName || "Unknown";
const capitalized = firstName.charAt(0) + firstName.slice(1).toLowerCase();
return capitalized || "Unknown";
}

View File

@@ -195,4 +195,40 @@ export function bytesToBigDecimal(arr: number[]): string {
export function hexToDecimal(hex: string): string {
return BigInt(`0x${hex}`).toString();
}
export function formatDuration(durationInMs: number) {
const durationInSeconds = durationInMs / 1000;
const minutes = Math.floor((durationInSeconds % 3600) / 60);
const seconds = Math.floor(durationInSeconds % 60);
return minutes > 0 ? `${minutes}m ${seconds}s` : `${seconds}s`;
}
export function formatProof(proof: any) {
const formattedProof: { [key: string]: any } = {};
for (const key in proof) {
if (Object.hasOwnProperty.call(proof, key)) {
const element = proof[key];
if (key === 'b') {
// Special formatting for 'b'
formattedProof[key] = element.map((complex: string) => {
const matches = complex.match(/QuadExtField\(([^)]+)\)/);
if (matches && matches[1]) {
return matches[1].split(' + ').map(num => {
return num.replace(' * u', '').trim();
});
}
return [];
});
} else {
// Direct copy for 'a' and 'c'
formattedProof[key] = [...element];
}
}
}
return formattedProof;
}

File diff suppressed because it is too large Load Diff

View File

@@ -129,15 +129,15 @@ describe('Circuit tests', function () {
)).to.be.rejected;
})
it('should support selective disclosure', async function () {
it.only('should support selective disclosure', async function () {
const attributeToPosition = {
issuing_state: [2, 4],
name: [5, 43],
issuing_state: [2, 5],
name: [5, 44],
passport_number: [44, 52],
nationality: [54, 56],
nationality: [54, 57],
date_of_birth: [57, 63],
gender: [65],
expiry_date: [66, 72],
gender: [64, 65],
expiry_date: [65, 71],
}
const attributeToReveal = {
@@ -146,7 +146,7 @@ describe('Circuit tests', function () {
passport_number: false,
nationality: true,
date_of_birth: false,
gender:false,
gender: false,
expiry_date: false,
}
@@ -173,9 +173,10 @@ describe('Circuit tests', function () {
)
console.log('proof done');
console.log('proof:', proof);
const revealChars = publicSignals.slice(0, 88).map((byte: string) => String.fromCharCode(parseInt(byte, 10)))
// console.log('revealChars', revealChars)
console.log('revealChars', revealChars)
for(let i = 0; i < revealChars.length; i++) {
if (bitmap[i] == '1') {
@@ -197,7 +198,7 @@ describe('Circuit tests', function () {
}
});
// console.log('reveal', reveal)
console.log('reveal', reveal)
const vKey = JSON.parse(fs.readFileSync("build/verification_key.json"));
const verified = await groth16.verify(