diff --git a/.gitignore b/.gitignore index 807b0e52..4c780950 100644 --- a/.gitignore +++ b/.gitignore @@ -1,3 +1,4 @@ target/ Cargo.lock -/outputs \ No newline at end of file +/outputs +inputs/inputs.json \ No newline at end of file diff --git a/README.md b/README.md index fde041f5..cf1696dd 100644 --- a/README.md +++ b/README.md @@ -26,6 +26,13 @@ and build: cargo build ``` +setup input file +``` + +cp ./inputs/inputs-template.json ./inputs/inputs.json +``` + +### Preprocessing phase run(by groth16): ``` cargo run --bin main groth16 ./inputs/inputs.json @@ -35,6 +42,20 @@ or run(by marlin): cargo run --bin main marlin ./inputs/inputs.json ``` +### Online phase +setup output folder +``` +mkdir ./outputs +mkdir ./outputs/0 +mkdir ./outputs/1 +mkdir ./outputs/2 +``` + +run online phase +``` +./run_online.zsh +``` + ## Tests ``` @@ -133,6 +154,9 @@ impl MySecretInputCircuit { See [this](https://github.com/arkworks-rs/r1cs-tutorial/) to learn more about how to specify constraints. +### how to specify mpc calculation +online mpc calculations are specified in circuits/circuit.rs. Defaultly, MySimpleCircuit is used. Constraints is specified in same way as input_circuit.rs. + ## Technical Details ### Generating secret sharing of inputs and ZKP verification diff --git a/arkworks/crypto-primitives/src/crh/pedersen/mod.rs b/arkworks/crypto-primitives/src/crh/pedersen/mod.rs index 64b81563..79843abb 100644 --- a/arkworks/crypto-primitives/src/crh/pedersen/mod.rs +++ b/arkworks/crypto-primitives/src/crh/pedersen/mod.rs @@ -39,9 +39,9 @@ impl CRH { generators_powers } - pub fn generator_powers(num_powers: usize, rng: &mut R) -> Vec { + pub fn generator_powers(num_powers: usize, _rng: &mut R) -> Vec { let mut cur_gen_powers = Vec::with_capacity(num_powers); - let mut base = C::pub_rand(rng); + let mut base = C::prime_subgroup_generator(); for _ in 0..num_powers { cur_gen_powers.push(base); base.double_in_place(); diff --git a/inputs/inputs.json b/inputs/inputs-template.json similarity index 100% rename from inputs/inputs.json rename to inputs/inputs-template.json diff --git a/src/main.rs b/src/main.rs index d455dfea..8cb13c46 100644 --- a/src/main.rs +++ b/src/main.rs @@ -27,7 +27,7 @@ use std::io::Write as Otherwrite; use structopt::StructOpt; use crate::circuits::*; -use crate::serialize::write_to_file; +use crate::serialize::{write_r, write_to_file}; #[derive(Debug, StructOpt)] #[structopt(name = "example", about = "An example of StructOpt usage.")] @@ -104,10 +104,10 @@ fn main() -> Result<(), Box> { // // initialize phase let zkpopk_parameters = preprocessing::zkpopk::Parameters::new( 1, - 2, + 3, std::convert::Into::::into(FrParameters::MODULUS) / 2_u32, 1, - 6, + 9, 2, ); @@ -127,7 +127,7 @@ fn main() -> Result<(), Box> { let e_alpha = she::Ciphertext::rand(&pk, &mut rng, &she_parameters); - let (_r_bracket, _r_angle) = + let (r_bracket, r_angle) = preprocessing::pair(&e_alpha, &pk, &sk, &zkpopk_parameters, &she_parameters); // // triple phase @@ -195,7 +195,7 @@ fn main() -> Result<(), Box> { let output_file_path = "./outputs/outputs.json"; - write_to_file(h_x, output_file_path, "hex_commitment")?; + write_to_file(vec![("hex_commitment".to_string(), h_x)], output_file_path)?; // deserialize let mut output_file = File::open(output_file_path).expect("Failed to open file"); @@ -220,5 +220,10 @@ fn main() -> Result<(), Box> { ark_ec::models::twisted_edwards_extended::GroupAffine::deserialize(reader).unwrap(); assert_eq!(h_x, deserialized_h_x); + + // save to file + // , [r] for input share + write_r(3, r_angle, r_bracket).unwrap(); + Ok(()) } diff --git a/src/online.rs b/src/online.rs index 1a49e4c5..cec6e018 100644 --- a/src/online.rs +++ b/src/online.rs @@ -1,8 +1,8 @@ use ark_bls12_377::Fr; use ark_crypto_primitives::CommitmentScheme; -use ark_ff::{BigInteger, PrimeField}; +use ark_ff::{BigInteger, FpParameters, PrimeField}; use ark_marlin::IndexProverKey; -use ark_serialize::Read; +use ark_serialize::{CanonicalDeserialize, Read}; use ark_std::test_rng; use mpc_algebra::Reveal; @@ -40,6 +40,34 @@ struct ArgInput { z: u128, } +#[derive(Debug, Deserialize)] +struct PairPhase { + r0_angle_mac: String, + r0_angle_public_modifier: String, + r0_angle_share: String, + r0_bracket_mac: String, + r0_bracket_mac_0: String, + r0_bracket_mac_1: String, + r0_bracket_mac_2: String, + r0_bracket_share: String, + r1_angle_mac: String, + r1_angle_public_modifier: String, + r1_angle_share: String, + r1_bracket_mac: String, + r1_bracket_mac_0: String, + r1_bracket_mac_1: String, + r1_bracket_mac_2: String, + r1_bracket_share: String, + r2_angle_mac: String, + r2_angle_public_modifier: String, + r2_angle_share: String, + r2_bracket_mac: String, + r2_bracket_mac_0: String, + r2_bracket_mac_1: String, + r2_bracket_mac_2: String, + r2_bracket_share: String, +} + enum ZkSnark { Groth16, Marlin, @@ -89,26 +117,96 @@ fn main() -> Result<(), Box> { // online calculation // TODO: Separate the following part in preprocessing. + + // load pair phase data + // deserialize + let online_setup_file_path = format!("./outputs/{}/online_setup.json", opt.id); + let mut online_setup_file = File::open(online_setup_file_path).expect("Failed to open file"); + + let mut output_string = String::new(); + online_setup_file + .read_to_string(&mut output_string) + .expect("Failed to read file"); + + let output_data: PairPhase = serde_json::from_str(&output_string).unwrap(); + + let (r0, r1, r2) = { + let remove_prefix_string = + if let Some(stripped) = output_data.r0_angle_share.strip_prefix("0x") { + stripped.to_string() + } else { + output_data.r0_angle_share.clone() + }; + + let remove_prefix_string1 = + if let Some(stripped) = output_data.r0_angle_share.strip_prefix("0x") { + stripped.to_string() + } else { + output_data.r1_angle_share.clone() + }; + + let remove_prefix_string2 = + if let Some(stripped) = output_data.r0_angle_share.strip_prefix("0x") { + stripped.to_string() + } else { + output_data.r2_angle_share.clone() + }; + + let reader: &[u8] = &hex::decode(remove_prefix_string).unwrap(); + + let deserialized_r0_angle_share: Fr = Fr::deserialize(reader).unwrap(); + + let reader: &[u8] = &hex::decode(remove_prefix_string1).unwrap(); + + let deserialized_r1_angle_share: Fr = Fr::deserialize(reader).unwrap(); + + let reader: &[u8] = &hex::decode(remove_prefix_string2).unwrap(); + + let deserialized_r2_angle_share: Fr = Fr::deserialize(reader).unwrap(); + + ( + deserialized_r0_angle_share, + deserialized_r1_angle_share, + deserialized_r2_angle_share, + ) + }; + + let sum_r0 = MFr::from_add_shared(r0).reveal(); + let sum_r1 = MFr::from_add_shared(r1).reveal(); + let sum_r2 = MFr::from_add_shared(r2).reveal(); + let shared_input = match Net::party_id() { 0 => { vec![ - MFr::from_add_shared(Fr::from(data.x)), - MFr::from_add_shared(Fr::from(0)), - MFr::from_add_shared(Fr::from(0)), + MFr::from_add_shared( + Fr::from(data.x) - sum_r0 + + r0 + + Fr::from(ark_ed_on_bls12_377::FrParameters::MODULUS), + ), + MFr::from_add_shared(r1), + MFr::from_add_shared(r2), ] } 1 => { vec![ - MFr::from_add_shared(Fr::from(0)), - MFr::from_add_shared(Fr::from(data.y)), - MFr::from_add_shared(Fr::from(0)), + MFr::from_add_shared(r0), + MFr::from_add_shared( + Fr::from(data.y) - sum_r1 + + r1 + + Fr::from(ark_ed_on_bls12_377::FrParameters::MODULUS), + ), + MFr::from_add_shared(r2), ] } 2 => { vec![ - MFr::from_add_shared(Fr::from(0)), - MFr::from_add_shared(Fr::from(0)), - MFr::from_add_shared(Fr::from(data.z)), + MFr::from_add_shared(r0), + MFr::from_add_shared(r1), + MFr::from_add_shared( + Fr::from(data.z) - sum_r2 + + r2 + + Fr::from(ark_ed_on_bls12_377::FrParameters::MODULUS), + ), ] } _ => panic!("invalid party id"), diff --git a/src/preprocessing.rs b/src/preprocessing.rs index e4feb21f..87b5b715 100644 --- a/src/preprocessing.rs +++ b/src/preprocessing.rs @@ -544,6 +544,22 @@ pub struct AngleShares { mac: Vec, } +impl AngleShares { + pub fn separetion(&self) -> Vec<(Vec, Vec<Plaintext>, Vec<Plaintext>)> { + let peer_num = self.share.len(); + + let mut result = Vec::new(); + for peer in 0..peer_num { + result.push(( + self.clone().public_modifier.vals, + self.share[peer].clone().vals, + self.mac[peer].clone().vals, + )); + } + result + } +} + impl Add<Plaintexts> for AngleShares { type Output = AngleShares; fn add(self, rhs: Plaintexts) -> Self::Output { @@ -599,6 +615,29 @@ pub struct BracketShares { mac: Vec<(Plaintexts, Vec<Plaintexts>)>, } +impl BracketShares { + pub fn separetion(&self) -> Vec<(Vec<Plaintext>, (Vec<Plaintext>, Vec<Vec<Plaintext>>))> { + let peer_num = self.share.len(); + + let mut result = Vec::new(); + for peer in 0..peer_num { + result.push(( + self.share[peer].clone().vals, + ( + self.mac[peer].clone().0.vals, + self.mac[peer] + .clone() + .1 + .iter() + .map(|x| x.clone().vals) + .collect(), + ), + )); + } + result + } +} + fn bracket( m_vec: Vec<Plaintexts>, e_m: Ciphertext, @@ -795,7 +834,7 @@ pub fn pair( // step 1 let r_vec: Vec<Plaintexts> = (0..n) - .map(|_| Plaintexts::rand(she_params, &mut rng)) + .map(|_| Plaintexts::restricted_rand(she_params, &mut rng)) .collect(); // step 2 diff --git a/src/serialize.rs b/src/serialize.rs index 169254b3..fb66c7f7 100644 --- a/src/serialize.rs +++ b/src/serialize.rs @@ -1,10 +1,12 @@ use ark_serialize::CanonicalSerialize; use hex::ToHex; -use serde_json::json; +use serde_json::Value; use std::{fmt::Write, fs::File}; use std::io::Write as Otherwrite; +use crate::preprocessing::{AngleShares, BracketShares}; + // exanple: file_path = "./outputs/serialized_result.json" fn create_file(file_path: &str) -> Result<File, std::io::Error> { // ./outputs/name.json @@ -18,24 +20,10 @@ fn write_data(file: &mut File, data: &[u8]) -> Result<(), std::io::Error> { } pub fn write_to_file<T: CanonicalSerialize>( - data: T, + datas: Vec<(String, T)>, file_path: &str, - variable_name: &str, ) -> Result<(), Box<dyn std::error::Error>> { - // serialize commitment - let mut byte = Vec::new(); - - data.serialize(&mut byte).unwrap(); - - // convert from Vec<u8> to HEX string - let hex_string = byte.encode_hex::<String>(); - - let mut prefixed_hex_string = String::new(); - write!(prefixed_hex_string, "0x{}", hex_string).unwrap(); - - // create JSON object - let json_data = json!({ variable_name: prefixed_hex_string }); - + // crate file let create_file_result = create_file(file_path); match create_file_result { @@ -49,6 +37,27 @@ pub fn write_to_file<T: CanonicalSerialize>( let mut file = create_file_result.unwrap(); + // create JSON object + let processed_data = datas + .iter() + .map(|(variable_name, data)| { + // serialize data + let mut byte = Vec::new(); + data.serialize(&mut byte).unwrap(); + + // convert from Vec<u8> to HEX string + let hex_string = byte.encode_hex::<String>(); + + let mut prefixed_hex_string = String::new(); + write!(prefixed_hex_string, "0x{}", hex_string).unwrap(); + + let value: Value = prefixed_hex_string.into(); + (variable_name, value) + }) + .collect::<Vec<_>>(); + + let json_data: Value = processed_data.into_iter().collect(); + let json_string = serde_json::to_string_pretty(&json_data).unwrap(); let write_result = write_data(&mut file, json_string.as_bytes()); @@ -64,15 +73,106 @@ pub fn write_to_file<T: CanonicalSerialize>( Ok(()) } +pub fn write_r( + peer_num: usize, + r_angle: AngleShares, + r_bracket: BracketShares, +) -> Result<(), std::io::Error> { + // TODO: Implement logic to handle variable `required_num` by filling or truncating the data as necessary. + let required_num = 3; + + // separation + let separated_angles = r_angle.separetion(); + let separated_brackets = r_bracket.separetion(); + + // check length + assert!(separated_angles[0].0.len() == required_num); + assert!(separated_brackets[0].0.len() == required_num); + + // write + for i in 0..peer_num { + let output_file_path = format!("./outputs/{}/online_setup.json", i); + + let mut write_datas = Vec::new(); + + for j in 0..required_num { + write_datas.push(( + format!("r{}_angle_public_modifier", j), + separated_angles[i].0[j], + )); + write_datas.push((format!("r{}_angle_share", j), separated_angles[i].1[j])); + write_datas.push((format!("r{}_angle_mac", j), separated_angles[i].2[j])); + + write_datas.push((format!("r{}_bracket_share", j), separated_brackets[i].0[j])); + + write_datas.push((format!("r{}_bracket_mac", j), separated_brackets[i].1 .0[j])); + for k in 0..peer_num { + write_datas.push(( + format!("r{}_bracket_mac_{}", j, k), + separated_brackets[i].1 .1[k][j], + )); + } + } + + write_to_file(write_datas, &output_file_path).unwrap(); + } + + Ok(()) +} + #[cfg(test)] mod tests { + use crate::{preprocessing, she}; + use super::*; - use ark_bls12_377::Fr; + use ark_bls12_377::{Fr, FrParameters}; + use ark_ff::FpParameters; + use ark_mnt4_753::FqParameters; #[test] #[ignore] fn test_serialize_field() { let a = Fr::from(2); + let b = Fr::from(3); + + let datas = vec![("test".to_string(), a), ("test".to_string(), b)]; + + write_to_file(datas, "./outputs/serialized_result.json").unwrap(); + } - write_to_file(a, "./outputs/serialized_result.json", "test").unwrap(); + #[test] + #[ignore] + fn test_write_r() { + // preprocessing + let mut rng = rand::thread_rng(); + // // initialize phase + let zkpopk_parameters = preprocessing::zkpopk::Parameters::new( + 1, + 3, + std::convert::Into::<num_bigint::BigUint>::into(FrParameters::MODULUS) / 2_u32, + 1, + 9, + 2, + ); + + let she_parameters = she::SHEParameters::new( + zkpopk_parameters.get_n(), + zkpopk_parameters.get_n(), + FrParameters::MODULUS.into(), + FqParameters::MODULUS.into(), + 3.2, + ); + + let _bracket_diag_alpha = preprocessing::initialize(&zkpopk_parameters, &she_parameters); + + // // pair phase + let sk = she::SecretKey::generate(&she_parameters, &mut rng); + let pk = sk.public_key_gen(&she_parameters, &mut rng); + + let e_alpha = she::Ciphertext::rand(&pk, &mut rng, &she_parameters); + + let (r_bracket, r_angle) = + preprocessing::pair(&e_alpha, &pk, &sk, &zkpopk_parameters, &she_parameters); + + write_r(3, r_angle, r_bracket).unwrap(); } } diff --git a/src/she/plaintext.rs b/src/she/plaintext.rs index 74d4f572..ea283950 100644 --- a/src/she/plaintext.rs +++ b/src/she/plaintext.rs @@ -28,6 +28,20 @@ impl Plaintexts { Plaintexts { vals: res } } + pub fn restricted_rand<T: Rng>(params: &SHEParameters, rng: &mut T) -> Plaintexts { + // TODO: make this more general + // currently: + // Lower Bound > maximum value of possible secret input (approximately 10,000 in this case) * number of participants + // Upper Bound * number of participants < period of the ScalarField of edwards_bls12_377 (≒10^75) + let upper_bound = 1000000000; + let lower_bound = 100000; + + let res = (0..params.s) + .map(|_| Plaintext::from(rng.gen_range(lower_bound..upper_bound))) + .collect(); + Plaintexts { vals: res } + } + pub fn encode(&self, params: &SHEParameters) -> Encodedtext { let remainders = self.vals.clone(); let moduli = cyclotomic_moduli(params.s);