From 80d682ea931f94e45c4ad676b72d4584e0d2212f Mon Sep 17 00:00:00 2001 From: arnaucube Date: Sun, 2 Aug 2020 19:22:02 +0200 Subject: [PATCH] Update Schnorr to last changes, rm MiMC7 methods --- README.md | 7 +- benches/bench_babyjubjub.rs | 10 +- src/lib.rs | 192 +++++++++++++----------------------- 3 files changed, 79 insertions(+), 130 deletions(-) diff --git a/README.md b/README.md index d5dd137..2d29bbf 100644 --- a/README.md +++ b/README.md @@ -1,13 +1,14 @@ # babyjubjub-rs [![Crates.io](https://img.shields.io/crates/v/babyjubjub-rs.svg)](https://crates.io/crates/babyjubjub-rs) [![Build Status](https://travis-ci.org/arnaucube/babyjubjub-rs.svg?branch=master)](https://travis-ci.org/arnaucube/babyjubjub-rs) -BabyJubJub elliptic curve implementation in Rust. A twisted edwards curve embedded in the curve of BN128. +BabyJubJub elliptic curve implementation in Rust. A twisted edwards curve embedded in the curve of BN128/BN256. BabyJubJub curve explanation: https://medium.com/zokrates/efficient-ecc-in-zksnarks-using-zokrates-bd9ae37b8186 Uses: -- MiMC7 hash function: https://github.com/arnaucube/mimc-rs - Poseidon hash function https://github.com/arnaucube/poseidon-rs -Compatible with the BabyJubJub Go implementation from https://github.com/iden3/go-iden3-crypto +Compatible with the BabyJubJub implementations in: +- Go, from https://github.com/iden3/go-iden3-crypto +- circom & javascript, from https://github.com/iden3/circomlib ## Warning Doing this in my free time to get familiar with Rust, **do not use in production**. diff --git a/benches/bench_babyjubjub.rs b/benches/bench_babyjubjub.rs index 1583e12..67218bb 100644 --- a/benches/bench_babyjubjub.rs +++ b/benches/bench_babyjubjub.rs @@ -46,12 +46,10 @@ fn criterion_benchmark(c: &mut Criterion) { let sk = babyjubjub_rs::new_key(); let pk = sk.public().unwrap(); let msg = 5.to_bigint().unwrap(); - c.bench_function("sign_poseidon", |b| { - b.iter(|| sk.sign_poseidon(msg.clone())) - }); - let sig = sk.sign_poseidon(msg.clone()).unwrap(); - c.bench_function("verify_poseidon", |b| { - b.iter(|| babyjubjub_rs::verify_poseidon(pk.clone(), sig.clone(), msg.clone())) + c.bench_function("sign", |b| b.iter(|| sk.sign(msg.clone()))); + let sig = sk.sign(msg.clone()).unwrap(); + c.bench_function("verify", |b| { + b.iter(|| babyjubjub_rs::verify(pk.clone(), sig.clone(), msg.clone())) }); } diff --git a/src/lib.rs b/src/lib.rs index ca76297..2d8e4ba 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -1,3 +1,5 @@ +// WARNING still updating the code, it works, but is still in process the refactor. + extern crate rand; #[macro_use] extern crate ff; @@ -283,37 +285,10 @@ impl PrivateKey { Ok(pk.clone()) } - // pub fn sign_mimc(&self, msg: BigInt) -> Result { - // // https://tools.ietf.org/html/rfc8032#section-5.1.6 - // let mut hasher = Blake2b::new(); - // let (_, sk_bytes) = self.key.to_bytes_be(); - // hasher.input(sk_bytes); - // let mut h = hasher.result(); // h: hash(sk) - // // s: h[32:64] - // let s = GenericArray::::from_mut_slice(&mut h[32..64]); - // let (_, msg_bytes) = msg.to_bytes_be(); - // let r_bytes = utils::concatenate_arrays(s, &msg_bytes); - // let mut r = BigInt::from_bytes_be(Sign::Plus, &r_bytes[..]); - // r = utils::modulus(&r, &SUBORDER); - // let r8: Point = B8.mul_scalar(&r)?; - // let a = &self.public()?; - // - // let hm_input = vec![r8.x.clone(), r8.y.clone(), a.x.clone(), a.y.clone(), msg]; - // let mimc7 = Mimc7::new(); - // let hm = mimc7.hash(hm_input)?; - // - // let mut s = &self.key << 3; - // s = hm * s; - // s = r + s; - // s = s % &SUBORDER.clone(); - // - // Ok(Signature { - // r_b8: r8.clone(), - // s: s, - // }) - // } - - pub fn sign_poseidon(&self, msg: BigInt) -> Result { + pub fn sign(&self, msg: BigInt) -> Result { + if msg > Q.clone() { + return Err("msg outside the Finite Field".to_string()); + } // https://tools.ietf.org/html/rfc8032#section-5.1.6 let mut hasher = Blake2b::new(); let (_, sk_bytes) = self.key.to_bytes_be(); @@ -346,49 +321,48 @@ impl PrivateKey { }) } - // pub fn sign_schnorr(&self, m: Vec) -> Result<(Point, BigInt), String> { - // // random r - // let mut rng = rand::thread_rng(); - // let k = rng.gen_biguint(1024).to_bigint().unwrap(); - // - // // r = k·G - // let r = B8.mul_scalar(&k)?; - // - // // h = H(x, r, m) - // let pk = &self.public()?; - // let h = schnorr_hash(&pk, m, &r)?; - // - // // s= k+x·h - // let s = k + &self.key * &h; - // Ok((r, s)) - // } + pub fn sign_schnorr(&self, m: BigInt) -> Result<(Point, BigInt), String> { + // random r + let mut rng = rand6::thread_rng(); + let k = rng.gen_biguint(1024).to_bigint().unwrap(); + + // r = k·G + let r = B8.mul_scalar(&k)?; + + // h = H(x, r, m) + let pk = &self.public()?; + let h = schnorr_hash(&pk, m, &r)?; + + // s= k+x·h + let s = k + &self.key * &h; + Ok((r, s)) + } } -// pub fn schnorr_hash(pk: &Point, m: Vec, c: &Point) -> Result { -// let b: &mut Vec = &mut Vec::new(); -// -// // other option could be to do it without compressing the points, and concatenating x|y -// b.append(&mut pk.compress().to_vec()); -// b.append(&mut c.compress().to_vec()); -// b.append(&mut m.clone()); -// -// let poseidon = Poseidon::new(); -// let h = poseidon.hash_bytes(b.to_vec())?; -// println!("h {:?}", h.to_string()); -// Ok(h) -// } -// -// pub fn verify_schnorr(pk: Point, m: Vec, r: Point, s: BigInt) -> Result { -// // sG = s·G -// let sg = B8.mul_scalar(&s)?; -// -// // r + h · x -// let h = schnorr_hash(&pk, m, &r)?; -// let pk_h = pk.mul_scalar(&h)?; -// let right = r.add(&pk_h)?; -// -// Ok(sg.equals(right)) -// } +pub fn schnorr_hash(pk: &Point, msg: BigInt, c: &Point) -> Result { + if msg > Q.clone() { + return Err("msg outside the Finite Field".to_string()); + } + let msgFr: Fr = Fr::from_str(&msg.to_string()).unwrap(); + let hm_input = vec![pk.x.clone(), pk.y.clone(), c.x.clone(), c.y.clone(), msgFr]; + let poseidon = Poseidon::new(); + let h = poseidon.hash(hm_input)?; + println!("h {:?}", h.to_string()); + let hB = BigInt::parse_bytes(to_hex(&h).as_bytes(), 16).unwrap(); + Ok(hB) +} + +pub fn verify_schnorr(pk: Point, m: BigInt, r: Point, s: BigInt) -> Result { + // sG = s·G + let sg = B8.mul_scalar(&s)?; + + // r + h · x + let h = schnorr_hash(&pk, m, &r)?; + let pk_h = pk.mul_scalar(&h)?; + let right = r.projective().add(&pk_h.projective())?; + + Ok(sg.equals(right.affine())) +} pub fn new_key() -> PrivateKey { // https://tools.ietf.org/html/rfc8032#section-5.1.5 @@ -409,34 +383,10 @@ pub fn new_key() -> PrivateKey { PrivateKey { key: sk } } -// pub fn verify_mimc(pk: Point, sig: Signature, msg: BigInt) -> bool { -// let hm_input = vec![ -// sig.r_b8.x.clone(), -// sig.r_b8.y.clone(), -// pk.x.clone(), -// pk.y.clone(), -// msg, -// ]; -// let mimc7 = Mimc7::new(); -// let hm = match mimc7.hash(hm_input) { -// Result::Err(_) => return false, -// Result::Ok(hm) => hm, -// }; -// let l = match B8.mul_scalar(&sig.s) { -// Result::Err(_) => return false, -// Result::Ok(l) => l, -// }; -// let r = match sig -// .r_b8 -// .add(&pk.mul_scalar(&(8.to_bigint().unwrap() * hm)).unwrap()) -// { -// Result::Err(_) => return false, -// Result::Ok(r) => r, -// }; -// l.equals(r) -// } - -pub fn verify_poseidon(pk: Point, sig: Signature, msg: BigInt) -> bool { +pub fn verify(pk: Point, sig: Signature, msg: BigInt) -> bool { + if msg > Q.clone() { + return false; + } let (_, msg_bytes) = msg.to_bytes_be(); let msgFr: Fr = Fr::from_str(&msg.to_string()).unwrap(); let hm_input = vec![ @@ -628,22 +578,22 @@ mod tests { // } #[test] - fn test_new_key_sign_verify_poseidon_0() { + fn test_new_key_sign_verify_0() { let sk = new_key(); let pk = sk.public().unwrap(); let msg = 5.to_bigint().unwrap(); - let sig = sk.sign_poseidon(msg.clone()).unwrap(); - let v = verify_poseidon(pk, sig, msg); + let sig = sk.sign(msg.clone()).unwrap(); + let v = verify(pk, sig, msg); assert_eq!(v, true); } #[test] - fn test_new_key_sign_verify_poseidon_1() { + fn test_new_key_sign_verify_1() { let sk = new_key(); let pk = sk.public().unwrap(); let msg = BigInt::parse_bytes(b"123456789012345678901234567890", 10).unwrap(); - let sig = sk.sign_poseidon(msg.clone()).unwrap(); - let v = verify_poseidon(pk, sig, msg); + let sig = sk.sign(msg.clone()).unwrap(); + let v = verify(pk, sig, msg); assert_eq!(v, true); } @@ -739,7 +689,7 @@ mod tests { for i in 0..5 { let msg_raw = "123456".to_owned() + &i.to_string(); let msg = BigInt::parse_bytes(msg_raw.as_bytes(), 10).unwrap(); - let sig = sk.sign_poseidon(msg.clone()).unwrap(); + let sig = sk.sign(msg.clone()).unwrap(); let compressed_sig = sig.compress(); let decompressed_sig = decompress_signature(&compressed_sig).unwrap(); @@ -747,22 +697,22 @@ mod tests { assert_eq!(&sig.r_b8.y, &decompressed_sig.r_b8.y); assert_eq!(&sig.s, &decompressed_sig.s); - let v = verify_poseidon(pk.clone(), decompressed_sig, msg); + let v = verify(pk.clone(), decompressed_sig, msg); assert_eq!(v, true); } } - // #[test] - // fn test_schnorr_signature() { - // let sk = new_key(); - // let pk = sk.public().unwrap(); - // - // let msg: Vec = ("123456".to_owned() + &1.to_string()).as_bytes().to_vec(); - // let (s, e) = sk.sign_schnorr(msg.clone()).unwrap(); - // println!("s {:?}", s.x.to_string()); - // println!("s {:?}", s.y.to_string()); - // println!("e {:?}", e.to_string()); - // let verification = verify_schnorr(pk, msg, s, e).unwrap(); - // assert_eq!(true, verification); - // } + #[test] + fn test_schnorr_signature() { + let sk = new_key(); + let pk = sk.public().unwrap(); + + let msg = BigInt::parse_bytes(b"123456789012345678901234567890", 10).unwrap(); + let (s, e) = sk.sign_schnorr(msg.clone()).unwrap(); + println!("s {:?}", s.x.to_string()); + println!("s {:?}", s.y.to_string()); + println!("e {:?}", e.to_string()); + let verification = verify_schnorr(pk, msg, s, e).unwrap(); + assert_eq!(true, verification); + } }