// ped92.rs use rand::{Rng, AsByteSliceMut}; use pairing::{Engine, CurveProjective}; use ff::{Rand, Field, PrimeField}; use std::fmt; use util::is_vec_g1_equal; use serde::{Serialize, Deserialize}; use util; #[derive(Clone)] pub struct CSParams { pub g: E::G1, pub h: E::G1, } #[derive(Clone, Serialize, Deserialize)] #[serde(bound(serialize = "::G1: serde::Serialize"))] #[serde(bound(deserialize = "::G1: serde::Deserialize<'de>"))] pub struct Commitment { pub c: E::G1, } impl PartialEq for Commitment { fn eq(&self, other: &Commitment) -> bool { self.c == other.c } } #[derive(Clone, Serialize, Deserialize)] #[serde(bound(serialize = "::G1: serde::Serialize"))] #[serde(bound(deserialize = "::G1: serde::Deserialize<'de>"))] pub struct CSMultiParams { pub pub_bases: Vec } impl PartialEq for CSMultiParams { fn eq(&self, other: &CSMultiParams) -> bool { is_vec_g1_equal::(&self.pub_bases, &other.pub_bases) } } impl CSMultiParams { pub fn from_slice<'de>(ser_gs: &'de [u8], g_len: usize, num_elems: usize) -> Self where ::G1: serde::Deserialize<'de> { let mut pub_bases: Vec = Vec::new(); let mut start_pos = 0; let mut end_pos = g_len; for _ in 0 .. num_elems { let g: E::G1 = serde_json::from_slice(&ser_gs[start_pos .. end_pos]).unwrap(); start_pos = end_pos; end_pos += g_len; pub_bases.push(g); } return CSMultiParams { pub_bases}; } } impl fmt::Display for CSMultiParams { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { let mut y_str = String::new(); let mut i = 0; for y in self.pub_bases.iter() { y_str = format!("{}\n{} => {}", y_str, i, y); i += 1; } write!(f, "CSMultiParams : (\n{}\n)", y_str) } } impl fmt::Display for Commitment { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { write!(f, "Commitment : (c={})", &self.c) } } impl CSParams { /* Implements the setup algorithm for the Pedersen92 commitment scheme */ pub fn setup(rng: &mut R) -> Self { let g = E::G1::rand(rng); let h = E::G1::rand(rng); CSParams { g, h } } /* commit(pk, msg) -> cm where - pk is the public key generated from setup() - msg is the message structure for the commitment scheme - cm is the output commitment message for the given message */ pub fn commit(&self, rng: &mut R, m: E::Fr, R: Option) -> Commitment { let r = R.unwrap_or(E::Fr::rand(rng)); // c = g^m * h^r let mut c = self.g.clone(); c.mul_assign(m.clone()); let mut h = self.h.clone(); h.mul_assign(r.clone()); c.add_assign(&h); Commitment { c } } /* decommit(csp, cm, msg) -> bool where - cm is the commitment - m is the message to validate - outputs T/F for whether the cm is a valid commitment to the msg */ pub fn decommit(&self, cm: &Commitment, m: &E::Fr, r: &E::Fr) -> bool { let mut dm = self.g.clone(); dm.mul_assign(m.clone()); let mut h = self.h.clone(); h.mul_assign(r.clone()); dm.add_assign(&h); dm == cm.c } } impl CSMultiParams { /* Implements the setup algorithm for the Pedersen92 commitment scheme over a vector of messages of length len. */ pub fn setup_gen_params(rng: &mut R, len: usize) -> Self { let mut p: Vec = Vec::new(); // 1 extra base element for the random parameter for _i in 0..len + 1 { p.push(E::G1::rand(rng)); } CSMultiParams { pub_bases: p } } pub fn commit(&self, x: &Vec, r: &E::Fr) -> Commitment { // c = g1^m1 * ... * gn^mn * h^r let mut c = self.pub_bases[0].clone(); let p_len = self.pub_bases.len(); c.mul_assign(r.clone()); //println!("commit => x.len = {}, p.len = {}", x.len(), p_len); for i in 0..x.len() { if (i < p_len) { let mut basis = self.pub_bases[i + 1]; basis.mul_assign(x[i]); c.add_assign(&basis); } } Commitment { c } } pub fn extend_commit(&self, com: &Commitment, x: &E::Fr) -> Commitment { // c = com * gn+1 ^ x let len = self.pub_bases.len(); let mut c = self.pub_bases[len-1].clone(); c.mul_assign(x.clone()); c.add_assign(&com.c); return Commitment { c }; } pub fn remove_commit(&self, com: &Commitment, x: &E::Fr) -> Commitment { // c = com * gn+1 ^ x let len = self.pub_bases.len(); let mut c = self.pub_bases[len-1].clone(); let xx = x.clone(); c.mul_assign(xx); c.negate(); c.add_assign(&com.c); return Commitment { c }; } pub fn decommit(&self, cm: &Commitment, x: &Vec, r: &E::Fr) -> bool { let l = x.len(); // pub_base[0] => h, x[0] => r let mut dc = self.pub_bases[0].clone(); dc.mul_assign(r.clone()); for i in 0..l { let mut basis = self.pub_bases[i+1]; basis.mul_assign(x[i]); dc.add_assign(&basis); } return dc == cm.c; } } #[derive(Clone, Serialize, Deserialize, Debug)] #[serde(bound(serialize = "::Fr: serde::Serialize, \ ::G1: serde::Serialize, \ ::G2: serde::Serialize" ))] #[serde(bound(deserialize = "::Fr: serde::Deserialize<'de>, \ ::G1: serde::Deserialize<'de>, \ ::G2: serde::Deserialize<'de>" ))] pub struct CommitmentProof { pub T: E::G1, pub z: Vec, } impl CommitmentProof { pub fn new(csprng: &mut R, com_params: &CSMultiParams, com: &E::G1, wallet: &Vec, r: &E::Fr, reveal_index: &Vec) -> Self { let mut rt = Vec::new(); for i in 0..wallet.len() + 1 { if reveal_index.contains(&i) { rt.push(E::Fr::zero()); } else { rt.push(E::Fr::rand(csprng)); } } let (Tvals, t) = CommitmentProof::::prove_commitment::(csprng, com_params, wallet, Some(rt)); // compute the challenge let x: Vec = vec![Tvals, com.clone()]; let challenge = util::hash_g1_to_fr::(&x); // compute the response CommitmentProof::::prove_response(wallet, r, Tvals, &t, &challenge) } pub fn prove_commitment(csprng: &mut R, com_params: &CSMultiParams, wallet: &Vec, tOptional: Option>) -> (E::G1, Vec) { let mut Tvals = E::G1::zero(); assert!(wallet.len() <= com_params.pub_bases.len()); let mut t = tOptional.unwrap_or(Vec::::with_capacity(wallet.len() + 1)); // aspects of wallet being revealed for i in 0..wallet.len() + 1 { if t.len() == i { t.push(E::Fr::rand(csprng)); } let ti = t[i].clone(); let mut gt = com_params.pub_bases[i].clone(); gt.mul_assign(ti.into_repr()); Tvals.add_assign(>); } (Tvals, t) } pub fn prove_response(wallet: &Vec, r: &E::Fr, Tvals: E::G1, t: &Vec, challenge: &E::Fr) -> CommitmentProof { let mut z: Vec = Vec::new(); let mut z0 = r.clone(); z0.mul_assign(&challenge); z0.add_assign(&t[0]); z.push(z0); for i in 1..t.len() { let mut zi = wallet[i - 1].clone(); zi.mul_assign(&challenge); zi.add_assign(&t[i]); z.push(zi); } CommitmentProof { T: Tvals, // commitment challenge z: z, // response values } } pub fn verify_proof(&self, com_params: &CSMultiParams, com: &::G1, challenge: &E::Fr, revealOption: Option>>) -> bool { let mut comc = com.clone(); let T = self.T.clone(); comc.mul_assign(challenge.into_repr()); comc.add_assign(&T); let mut x = E::G1::zero(); let reveal = revealOption.unwrap_or(vec!{}); let mut revealBool = true; for i in 0..self.z.len() { let mut base = com_params.pub_bases[i].clone(); base.mul_assign(self.z[i].into_repr()); x.add_assign(&base); if reveal.len() > i && reveal[i].is_some() { let mut el = reveal[i].unwrap(); el.mul_assign(&challenge.clone()); revealBool = revealBool && self.z[i] == el; } } revealBool && comc == x } } #[cfg(test)] mod tests { use super::*; use pairing::bls12_381::{Bls12, Fr, G1}; use rand::thread_rng; use ff::Field; use wallet::Wallet; #[test] fn commit_one_message_works() { let rng = &mut thread_rng(); let csp = CSParams::::setup(rng); let m1 = Fr::rand(rng); let mut m2 = m1.clone(); m2.add_assign(&Fr::one()); let r = Fr::rand(rng); let c = csp.commit(rng, m1, Some(r)); assert_eq!(csp.decommit(&c, &m1, &r), true); assert_eq!(csp.decommit(&c, &m2, &r), false); } #[test] fn commit_n_message_works() { let rng = &mut thread_rng(); let len = 3; let csp = CSMultiParams::::setup_gen_params(rng, len); let mut m: Vec = Vec::new(); for _i in 0..len { m.push(Fr::rand(rng)); } let r = Fr::rand(rng); let c = csp.commit(&m, &r); assert_eq!(csp.decommit(&c, &m, &r), true); let mut r1 = r.clone(); r1.add_assign(&Fr::one()); assert_eq!(csp.decommit(&c, &m, &r1), false); } #[test] fn commit_variable_messages_works() { let rng = &mut thread_rng(); let len = 5; let csp = CSMultiParams::::setup_gen_params(rng, len); let mut m1: Vec = Vec::new(); for _i in 0..len-1 { m1.push(Fr::rand(rng)); } let extra_m = Fr::rand(rng); let r = Fr::rand(rng); let c1 = csp.commit(&m1, &r); assert_eq!(csp.decommit(&c1, &m1, &r), true); let mut r1 = r.clone(); r1.add_assign(&Fr::one()); assert_eq!(csp.decommit(&c1, &m1, &r1), false); // let's add another message let mut m2 = m1.clone(); m2.push(extra_m); let c2 = csp.commit(&m2, &r); assert_eq!(csp.decommit(&c2, &m2, &r), true); } #[test] fn test_csp_basic_serialize() { let rng = &mut rand::thread_rng(); let len = 5; let csp = CSMultiParams::::setup_gen_params(rng, len); let serialized = serde_json::to_string(&csp).unwrap(); let _csp_des: CSMultiParams = serde_json::from_str(&serialized).unwrap(); } #[test] fn test_proof_commitment() { let rng = &mut rand::thread_rng(); let channelId = Fr::rand(rng); let wpk = Fr::rand(rng); let t = Fr::rand(rng); let bc = rng.gen_range(100, 1000); let bm = rng.gen_range(100, 1000); let wallet = Wallet:: { channelId: channelId, wpk: wpk, bc: bc, bm: bm, close: None }; let comParams = CSMultiParams::setup_gen_params(rng, 4); let com = comParams.commit(&wallet.as_fr_vec().clone(), &t); let proof = CommitmentProof::::new(rng, &comParams, &com.c, &wallet.as_fr_vec(), &t, &vec!{}); let xvec: Vec = vec![proof.T.clone(), com.c]; let challenge = util::hash_g1_to_fr::(&xvec); assert_eq!(proof.verify_proof(&comParams, &com.c, &challenge, None), true); } #[test] fn test_cs_multiparam_serialization() { let mut vec: Vec = Vec::new(); let bin_g1= vec![132, 83, 99, 124, 75, 72, 15, 109, 12, 94, 84, 103, 1, 58, 160, 232, 190, 23, 119, 195, 112, 161, 152, 141, 178, 29, 141, 61, 227, 246, 215, 157, 140, 190, 100, 18, 248, 141, 57, 222, 12, 209, 191, 158, 143, 155, 87, 255]; let bin_g2 = vec![140, 16, 244, 244, 135, 28, 18, 94, 46, 64, 233, 195, 218, 147, 238, 170, 46, 164, 50, 92, 234, 117, 61, 158, 64, 226, 153, 38, 127, 168, 49, 125, 177, 183, 74, 164, 138, 128, 168, 84, 137, 67, 21, 179, 124, 88, 194, 239]; let bin_g3 = vec![147, 174, 242, 238, 231, 127, 9, 120, 16, 9, 191, 238, 60, 57, 106, 34, 198, 62, 28, 183, 77, 170, 27, 116, 36, 75, 242, 26, 242, 23, 213, 31, 186, 21, 141, 219, 59, 104, 247, 118, 56, 95, 183, 124, 103, 83, 93, 154]; let ser_g1 = util::encode_as_hexstring(&bin_g1); let ser_g2 = util::encode_as_hexstring(&bin_g2); let ser_g3 = util::encode_as_hexstring(&bin_g3); let str_g1 = ser_g1.as_bytes(); let str_g2 = ser_g2.as_bytes(); let str_g3 = ser_g3.as_bytes(); vec.extend(str_g1); vec.extend(str_g2); vec.extend(str_g3); let rec_csparams = CSMultiParams::::from_slice(&vec.as_slice(), str_g1.len(), 3); println!("CS params: {:?}", rec_csparams.pub_bases); let ser_cs = serde_json::to_string(&rec_csparams).unwrap(); println!("Serialized: {:}", ser_cs); let rec_g1_str = serde_json::to_string(&rec_csparams.pub_bases[0]).unwrap(); assert_eq!(rec_g1_str, "\"8453637c4b480f6d0c5e5467013aa0e8be1777c370a1988db21d8d3de3f6d79d8cbe6412f88d39de0cd1bf9e8f9b57ff\""); let rec_g2_str = serde_json::to_string(&rec_csparams.pub_bases[1]).unwrap(); assert_eq!(rec_g2_str, "\"8c10f4f4871c125e2e40e9c3da93eeaa2ea4325cea753d9e40e299267fa8317db1b74aa48a80a854894315b37c58c2ef\""); let rec_g3_str = serde_json::to_string(&rec_csparams.pub_bases[2]).unwrap(); assert_eq!(rec_g3_str, "\"93aef2eee77f09781009bfee3c396a22c63e1cb74daa1b74244bf21af217d51fba158ddb3b68f776385fb77c67535d9a\""); } }