diff --git a/Cargo.lock b/Cargo.lock index b3057d7..fca4f13 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -2155,31 +2155,6 @@ dependencies = [ "winapi", ] -[[package]] -name = "libsemaphore-rs" -version = "0.1.0" -dependencies = [ - "ark-bn254", - "ark-circom", - "ark-ec", - "ark-ff", - "ark-groth16", - "ark-relations", - "ark-std", - "color-eyre", - "ethers", - "ff_ce", - "hex", - "hex-literal", - "num-bigint 0.4.3", - "once_cell", - "poseidon-rs", - "proptest", - "rayon", - "serde", - "sha2 0.10.1", -] - [[package]] name = "lock_api" version = "0.4.6" @@ -3340,6 +3315,31 @@ dependencies = [ "libc", ] +[[package]] +name = "semaphore" +version = "0.1.0" +dependencies = [ + "ark-bn254", + "ark-circom", + "ark-ec", + "ark-ff", + "ark-groth16", + "ark-relations", + "ark-std", + "color-eyre", + "ethers", + "ff_ce", + "hex", + "hex-literal", + "num-bigint 0.4.3", + "once_cell", + "poseidon-rs", + "proptest", + "rayon", + "serde", + "sha2 0.10.1", +] + [[package]] name = "semver" version = "0.11.0" diff --git a/Cargo.toml b/Cargo.toml index 149f1dd..0a76f81 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -5,8 +5,6 @@ edition = "2021" authors = [ "Remco Bloemen ", "Philipp Sippl "] -edition = "2021" -build = "build.rs" homepage = "https://github.com/worldcoin/semaphore-rs" repository = "https://github.com/worldcoin/semaphore-rs" description = "Rust support library for Semaphore" diff --git a/cbindgen.toml b/cbindgen.toml new file mode 100644 index 0000000..ceb6a92 --- /dev/null +++ b/cbindgen.toml @@ -0,0 +1,8 @@ +language = "C" +autogen_warning = "// NOTE: Append the lines below to ios/Classes/GreeterPlugin.h" +#namespace = "ffi" +#include_guard = "CBINDGEN_BINDINGS_H" + +[defines] +"target_os = ios" = "TARGET_OS_IOS" +"target_os = macos" = "TARGET_OS_MACOS" \ No newline at end of file diff --git a/libsemaphore.h b/libsemaphore.h new file mode 100644 index 0000000..0e81b71 --- /dev/null +++ b/libsemaphore.h @@ -0,0 +1,73 @@ +// NOTE: Append the lines below to ios/Classes/GreeterPlugin.h + +typedef struct Identity Identity; + +/** + * Merkle tree with all leaf and intermediate hashes stored + */ +typedef struct MerkleTree_PoseidonHash MerkleTree_PoseidonHash; + +/** + * Merkle proof path, bottom to top. + */ +typedef struct Proof_Bn_Parameters Proof_Bn_Parameters; + +/** + * Merkle proof path, bottom to top. + */ +typedef struct Proof_PoseidonHash Proof_PoseidonHash; + +typedef struct MerkleTree_PoseidonHash PoseidonTree; + +/** + * Creates a new idenity and returns the object + */ +struct Identity *new_identity(const char *seed); + +/** + * Generates the identity commitment based on seed for identity + */ +char *generate_identity_commitment(struct Identity *identity); + +/** + * Generates nullifier hash based on identity and external nullifier + */ +char *generate_nullifier_hash(struct Identity *identity, const char *external_nullifier); + +/** + * Generates nullifier hash based on identity and external nullifier + */ +PoseidonTree *create_poseidon_tree(int depth); + +/** + * Generates nullifier hash based on identity and external nullifier + */ +void insert_leaf(PoseidonTree *tree, struct Identity *identity); + +/** + * Generates nullifier hash based on identity and external nullifier + */ +char *get_root(PoseidonTree *tree); + +/** + * Generates nullifier hash based on identity and external nullifier + */ +struct Proof_PoseidonHash *get_merkle_proof(PoseidonTree *tree, int leaf_idx); + +/** + * Generates nullifier hash based on identity and external nullifier + */ +struct Proof_Bn_Parameters *generate_proof(struct Identity *identity, + const char *external_nullifier, + const char *signal, + struct Proof_PoseidonHash *merkle_proof, + const char *zkey_path, + const char *wasm_path); + +int verify_proof(const char *root, + const char *external_nullifier, + const char *signal, + const char *nullifier, + struct Proof_Bn_Parameters *proof, + const char *zkey_path, + const char *wasm_path); diff --git a/src/lib.rs b/src/lib.rs index 9f2c231..c7e44a0 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -5,13 +5,24 @@ mod poseidon_tree; mod protocol; mod util; +use ark_bn254::Parameters; +use ark_ec::bn::Bn; +use ark_groth16::Proof; +use hex_literal::hex; +use num_bigint::{BigInt}; +use poseidon_tree::PoseidonHash; +use protocol::SnarkFileConfig; use std::{ ffi::{CStr, CString}, - os::raw::c_char, + os::raw::{c_char, c_int}, }; +use crate::{hash::Hash, poseidon_tree::PoseidonTree}; + +/// Creates a new idenity and returns the object #[no_mangle] -pub extern "C" fn generate_identity_commitment(seed: *const c_char) -> *mut c_char { +#[allow(clippy::missing_safety_doc)] +pub unsafe extern "C" fn new_identity(seed: *const c_char) -> *mut identity::Identity { let c_str = unsafe { CStr::from_ptr(seed) }; let seed = match c_str.to_str() { Err(_) => "there", @@ -19,7 +30,222 @@ pub extern "C" fn generate_identity_commitment(seed: *const c_char) -> *mut c_ch }; let id = identity::Identity::new(seed.as_bytes()); - CString::new(id.commitment().to_str_radix(10)) + let boxed: Box = Box::new(id); + Box::into_raw(boxed) +} + +/// Generates the identity commitment based on seed for identity +#[no_mangle] +#[allow(clippy::missing_safety_doc)] +pub unsafe extern "C" fn generate_identity_commitment( + identity: *mut identity::Identity, +) -> *mut c_char { + let identity = &*identity; + CString::new(identity.commitment().to_str_radix(10)) .unwrap() .into_raw() } + +/// Generates nullifier hash based on identity and external nullifier +#[no_mangle] +#[allow(clippy::missing_safety_doc)] +pub unsafe extern "C" fn generate_nullifier_hash( + identity: *mut identity::Identity, + external_nullifier: *const c_char, +) -> *mut c_char { + let identity = &*identity; + + let c_str = unsafe { CStr::from_ptr(external_nullifier) }; + let external_nullifier = match c_str.to_str() { + Err(_) => "there", + Ok(string) => string, + }; + + CString::new( + protocol::generate_nullifier_hash(identity, external_nullifier.as_bytes()).to_str_radix(10), + ) + .unwrap() + .into_raw() +} + +/// Generates nullifier hash based on identity and external nullifier +#[no_mangle] +#[allow(clippy::missing_safety_doc)] +pub unsafe extern "C" fn create_poseidon_tree(depth: c_int) -> *mut PoseidonTree { + const LEAF: Hash = Hash::from_bytes_be(hex!( + "0000000000000000000000000000000000000000000000000000000000000000" + )); + + let tree = PoseidonTree::new(depth as usize, LEAF); + + let boxed: Box = Box::new(tree); + Box::into_raw(boxed) +} + +/// Generates nullifier hash based on identity and external nullifier +#[no_mangle] +#[allow(clippy::missing_safety_doc)] +pub unsafe extern "C" fn insert_leaf(tree: *mut PoseidonTree, identity: *mut identity::Identity) { + let identity = &*identity; + let tree = unsafe { + assert!(!tree.is_null()); + &mut *tree + }; + + let (_, leaf) = identity.commitment().to_bytes_be(); + tree.set(0, leaf.into()); +} + +/// Generates nullifier hash based on identity and external nullifier +#[no_mangle] +#[allow(clippy::missing_safety_doc)] +pub unsafe extern "C" fn get_root(tree: *mut PoseidonTree) -> *mut c_char { + let tree = unsafe { + assert!(!tree.is_null()); + &mut *tree + }; + + let root: BigInt = tree.root().into(); + CString::new(root.to_str_radix(10)).unwrap().into_raw() +} + +/// Generates nullifier hash based on identity and external nullifier +#[no_mangle] +#[allow(clippy::missing_safety_doc)] +pub unsafe extern "C" fn get_merkle_proof( + tree: *mut PoseidonTree, + leaf_idx: c_int, +) -> *mut merkle_tree::Proof { + let tree = unsafe { + assert!(!tree.is_null()); + &mut *tree + }; + + let proof = tree.proof(leaf_idx as usize).expect("proof should exist"); + + let boxed: Box> = Box::new(proof); + Box::into_raw(boxed) +} + +/// Generates nullifier hash based on identity and external nullifier +#[no_mangle] +#[allow(clippy::missing_safety_doc)] +pub unsafe extern "C" fn generate_proof( + identity: *mut identity::Identity, + external_nullifier: *const c_char, + signal: *const c_char, + merkle_proof: *mut merkle_tree::Proof, + zkey_path: *const c_char, + wasm_path: *const c_char, +) -> *mut Proof> { + let c_str = unsafe { CStr::from_ptr(external_nullifier) }; + let external_nullifier = match c_str.to_str() { + Err(_) => "there", + Ok(string) => string, + }; + + let c_str = unsafe { CStr::from_ptr(signal) }; + let signal = match c_str.to_str() { + Err(_) => "there", + Ok(string) => string, + }; + + let c_str = unsafe { CStr::from_ptr(zkey_path) }; + let zkey_path = match c_str.to_str() { + Err(_) => "there", + Ok(string) => string, + }; + + let c_str = unsafe { CStr::from_ptr(wasm_path) }; + let wasm_path = match c_str.to_str() { + Err(_) => "there", + Ok(string) => string, + }; + + let config = SnarkFileConfig { + zkey: zkey_path.to_string(), + wasm: wasm_path.to_string(), + }; + + let identity = &*identity; + let merkle_proof = &*merkle_proof; + + let res = protocol::generate_proof( + &config, + identity, + merkle_proof, + external_nullifier.as_bytes(), + signal.as_bytes(), + ); + + let boxed: Box>> = Box::new(res.unwrap()); + Box::into_raw(boxed) +} + +#[no_mangle] +#[allow(clippy::missing_safety_doc)] +pub unsafe extern "C" fn verify_proof( + root: *const c_char, + external_nullifier: *const c_char, + signal: *const c_char, + nullifier: *const c_char, + proof: *mut Proof>, + zkey_path: *const c_char, + wasm_path: *const c_char, +) -> c_int { + let c_str = unsafe { CStr::from_ptr(root) }; + let root = match c_str.to_str() { + Err(_) => "there", + Ok(string) => string, + }; + + let c_str = unsafe { CStr::from_ptr(external_nullifier) }; + let external_nullifier = match c_str.to_str() { + Err(_) => "there", + Ok(string) => string, + }; + + let c_str = unsafe { CStr::from_ptr(signal) }; + let signal = match c_str.to_str() { + Err(_) => "there", + Ok(string) => string, + }; + + let c_str = unsafe { CStr::from_ptr(nullifier) }; + let nullifier = match c_str.to_str() { + Err(_) => "there", + Ok(string) => string, + }; + + let c_str = unsafe { CStr::from_ptr(zkey_path) }; + let zkey_path = match c_str.to_str() { + Err(_) => "there", + Ok(string) => string, + }; + + let c_str = unsafe { CStr::from_ptr(wasm_path) }; + let wasm_path = match c_str.to_str() { + Err(_) => "there", + Ok(string) => string, + }; + + let config = SnarkFileConfig { + zkey: zkey_path.to_string(), + wasm: wasm_path.to_string(), + }; + + let proof = &*proof; + + let root = BigInt::parse_bytes(root.as_bytes(), 10).unwrap(); + let nullifier = BigInt::parse_bytes(nullifier.as_bytes(), 10).unwrap(); + + protocol::verify_proof( + &config, + &root, + &nullifier, + signal.as_bytes(), + external_nullifier.as_bytes(), + proof, + ) + .unwrap() as i32 +} diff --git a/src/libsemaphore.h b/src/libsemaphore.h deleted file mode 100644 index b057200..0000000 --- a/src/libsemaphore.h +++ /dev/null @@ -1,3 +0,0 @@ -#include - -const char* generate_identity_commitment(const char* to); diff --git a/src/main.rs b/src/main.rs index ca761bc..6ba53fd 100644 --- a/src/main.rs +++ b/src/main.rs @@ -8,7 +8,7 @@ mod util; use hash::*; use hex_literal::hex; use identity::*; -use num_bigint::BigInt; +use num_bigint::{BigInt, Sign}; use poseidon_rs::Poseidon; use poseidon_tree::*; use protocol::*; @@ -38,12 +38,17 @@ fn main() { let signal = "xxx".as_bytes(); let external_nullifier = "appId".as_bytes(); - let nullifier_hash = generate_nullifier_hash(&external_nullifier, &id.nullifier); + let nullifier_hash = generate_nullifier_hash(&id, external_nullifier); dbg!(&nullifier_hash); - let proof = generate_proof(&id, &merkle_proof, &external_nullifier, &signal).unwrap(); + let config = SnarkFileConfig { + zkey: "./snarkfiles/semaphore.zkey".to_string(), + wasm: "./snarkfiles/semaphore.wasm".to_string(), + }; + + let proof = generate_proof(&config, &id, &merkle_proof, external_nullifier, signal).unwrap(); let success = - verify_proof(&root, &nullifier_hash, &signal, &external_nullifier, &proof).unwrap(); + verify_proof(&config, &root, &nullifier_hash, signal, external_nullifier, &proof).unwrap(); dbg!(success); } diff --git a/src/protocol.rs b/src/protocol.rs index 91c3d36..2218dd4 100644 --- a/src/protocol.rs +++ b/src/protocol.rs @@ -19,11 +19,13 @@ use crate::{ util::{bigint_to_fr, fr_to_bigint}, }; -static SNARK_FILES: &str = "./snarkfiles/"; -static ZKEY_FILE: &str = "semaphore.zkey"; -static WASM_FILE: &str = "semaphore.wasm"; static POSEIDON: Lazy = Lazy::new(Poseidon::new); +pub struct SnarkFileConfig { + pub zkey: String, + pub wasm: String, +} + /// Helper to merkle proof into a bigint vector /// TODO: we should create a From trait for this fn merkle_proof_to_vec(proof: &merkle_tree::Proof) -> Vec { @@ -50,11 +52,11 @@ pub fn hash_external_nullifier(nullifier: &[u8]) -> BigInt { } /// Generates the nullifier hash -pub fn generate_nullifier_hash(external_nullifier: &[u8], identity_nullifier: &BigInt) -> BigInt { +pub fn generate_nullifier_hash(identity: &Identity, external_nullifier: &[u8]) -> BigInt { let res = POSEIDON .hash(vec![ bigint_to_fr(&hash_external_nullifier(external_nullifier)), - bigint_to_fr(identity_nullifier), + bigint_to_fr(&identity.nullifier), ]) .unwrap(); fr_to_bigint(res) @@ -62,12 +64,13 @@ pub fn generate_nullifier_hash(external_nullifier: &[u8], identity_nullifier: &B /// Generates a semaphore proof pub fn generate_proof( + config: &SnarkFileConfig, identity: &Identity, merkle_proof: &merkle_tree::Proof, external_nullifier: &[u8], signal: &[u8], ) -> Result>, SynthesisError> { - let mut file = File::open(format!("{}{}", SNARK_FILES, ZKEY_FILE)).unwrap(); + let mut file = File::open(&config.zkey).unwrap(); let (params, matrices) = read_zkey(&mut file).unwrap(); let num_inputs = matrices.num_instance_variables; let num_constraints = matrices.num_constraints; @@ -97,7 +100,7 @@ pub fn generate_proof( use std::time::Instant; let now = Instant::now(); - let mut wtns = WitnessCalculator::new(format!("{}{}", SNARK_FILES, WASM_FILE)).unwrap(); + let mut wtns = WitnessCalculator::new(&config.wasm).unwrap(); let full_assignment = wtns .calculate_witness_element::(inputs, false) @@ -131,13 +134,14 @@ pub fn generate_proof( /// Verifies a given semaphore proof pub fn verify_proof( + config: &SnarkFileConfig, root: &BigInt, nullifier_hash: &BigInt, signal: &[u8], external_nullifier: &[u8], proof: &Proof>, ) -> Result { - let mut file = File::open(format!("{}{}", SNARK_FILES, ZKEY_FILE)).unwrap(); + let mut file = File::open(&config.zkey).unwrap(); let (params, _) = read_zkey(&mut file).unwrap(); let pvk = prepare_verifying_key(¶ms.vk);