From 2367e15363f5e6e444f957c14b98fdbd38125430 Mon Sep 17 00:00:00 2001 From: Nanak Nihal Singh Khalsa Date: Tue, 24 Jan 2023 15:50:20 -0500 Subject: [PATCH 01/45] fixed blake2 bug on my M1 mac --- Cargo.toml | 11 ++++++----- src/lib.rs | 41 ++++++++++++++++++++++++++--------------- 2 files changed, 32 insertions(+), 20 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index 3c87354..d3a0bb0 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -14,8 +14,9 @@ rand = "0.8" num = "0.4" num-bigint = {version = "0.4", features = ["rand"]} num-traits = "0.2.8" -blake-hash = {version="0.4.0", optional=true} -blake = {version="2.0.1", optional=true} +blake2 = "0.10.6" +# blake-hash = {version="0.4.0", optional=true} +# blake = {version="2.0.1", optional=true} generic-array = "0.14" poseidon-rs = "0.0.8" arrayref = "0.3.5" @@ -30,6 +31,6 @@ hex = "0.4" name = "bench_babyjubjub" harness = false -[features] -default = ["blake-hash"] -aarch64 = ["blake"] +# [features] +# default = ["blake-hash"] +# aarch64 = ["blake"] diff --git a/src/lib.rs b/src/lib.rs index 0039b7d..878f269 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -8,12 +8,13 @@ pub type Fr = poseidon_rs::Fr; // alias use arrayref::array_ref; -#[cfg(not(feature = "aarch64"))] -use blake_hash::Digest; // compatible version with Blake used at circomlib - -#[cfg(feature = "aarch64")] -extern crate blake; // compatible version with Blake used at circomlib +// #[cfg(not(feature = "aarch64"))] +// use blake_hash::Digest; // compatible version with Blake used at circomlib +// #[cfg(feature = "aarch64")] +// extern crate blake; // compatible version with Blake used at circomlib +use blake2::{Blake2b512, Blake2s256, Digest}; +// use hex_literal::hex; use std::cmp::min; use num_bigint::{BigInt, RandBigInt, Sign, ToBigInt}; @@ -223,19 +224,29 @@ pub fn decompress_point(bb: [u8; 32]) -> Result { Ok(Point { x: x_fr, y: y_fr }) } -#[cfg(not(feature = "aarch64"))] -fn blh(b: &[u8]) -> Vec { - let hash = blake_hash::Blake512::digest(b); - hash.to_vec() -} +// #[cfg(not(feature = "aarch64"))] +// fn blh(b: &[u8]) -> Vec { +// println!("hashing {:?} {:?}", b.len(), b); +// let debugggggggggme = blake_hash::Blake512::digest(b); +// println!("debugging {:?}", debugggggggggme); + +// let hash = blake_hash::Blake512::digest(b); +// hash.to_vec() +// } + +// #[cfg(feature = "aarch64")] +// fn blh(b: &[u8]) -> Vec { +// let mut hash = [0; 64]; +// blake::hash(512, b, &mut hash).unwrap(); +// hash.to_vec() +// } -#[cfg(feature = "aarch64")] fn blh(b: &[u8]) -> Vec { - let mut hash = [0; 64]; - blake::hash(512, b, &mut hash).unwrap(); - hash.to_vec() + let mut h = Blake2b512::new(); + h.update(b); + let digest = h.finalize(); + return digest[..].to_vec(); } - #[derive(Debug, Clone)] pub struct Signature { pub r_b8: Point, From acd19628c5df89c4d49fc20f4f0865fd9a490e71 Mon Sep 17 00:00:00 2001 From: Nanak Nihal Singh Khalsa Date: Tue, 24 Jan 2023 16:00:37 -0500 Subject: [PATCH 02/45] added important TODO --- src/lib.rs | 1 + 1 file changed, 1 insertion(+) diff --git a/src/lib.rs b/src/lib.rs index 878f269..826177a 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -242,6 +242,7 @@ pub fn decompress_point(bb: [u8; 32]) -> Result { // } fn blh(b: &[u8]) -> Vec { + println!("TODO: add test that this new blake512 function is giving correct output. Not doing so could have unlikely yet critical implications"); let mut h = Blake2b512::new(); h.update(b); let digest = h.finalize(); From 1df44527dff76781a9b04006bc1bd0452bb07273 Mon Sep 17 00:00:00 2001 From: Nanak Nihal Singh Khalsa Date: Wed, 25 Jan 2023 09:15:31 -0500 Subject: [PATCH 03/45] made poseidon public --- src/lib.rs | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/lib.rs b/src/lib.rs index 826177a..5b3bf9f 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -57,7 +57,7 @@ lazy_static! { ) .unwrap() >> 3; - static ref POSEIDON: poseidon_rs::Poseidon = Poseidon::new(); + pub static ref POSEIDON: poseidon_rs::Poseidon = Poseidon::new(); } #[derive(Clone, Debug)] @@ -248,6 +248,7 @@ fn blh(b: &[u8]) -> Vec { let digest = h.finalize(); return digest[..].to_vec(); } + #[derive(Debug, Clone)] pub struct Signature { pub r_b8: Point, From f9efededfb0a7393ee6d66e64c6fff106ebcd959 Mon Sep 17 00:00:00 2001 From: Nanak Nihal Singh Khalsa Date: Wed, 25 Jan 2023 18:44:33 -0500 Subject: [PATCH 04/45] fixed bug in cargo.toml --- Cargo.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Cargo.toml b/Cargo.toml index d3a0bb0..ce310fc 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -9,7 +9,7 @@ repository = "https://github.com/arnaucube/babyjubjub-rs" readme = "README.md" [dependencies] -ff = {package="ff_ce" , version= "0.11", features = ["derive"]} +ff = {package="ff_ce", version= "0.11", features = ["derive"]} rand = "0.8" num = "0.4" num-bigint = {version = "0.4", features = ["rand"]} From 491cfafd271f9f7a2a5e5716da565c0292893c6e Mon Sep 17 00:00:00 2001 From: Nanak Nihal Singh Khalsa Date: Wed, 25 Jan 2023 19:22:15 -0500 Subject: [PATCH 05/45] blh is public now --- src/lib.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/lib.rs b/src/lib.rs index 5b3bf9f..e11c45c 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -241,7 +241,7 @@ pub fn decompress_point(bb: [u8; 32]) -> Result { // hash.to_vec() // } -fn blh(b: &[u8]) -> Vec { +pub fn blh(b: &[u8]) -> Vec { println!("TODO: add test that this new blake512 function is giving correct output. Not doing so could have unlikely yet critical implications"); let mut h = Blake2b512::new(); h.update(b); From 6dd8028cdc08c4b30711812b1735c9c5ae019b6a Mon Sep 17 00:00:00 2001 From: Nanak Nihal Singh Khalsa Date: Thu, 26 Jan 2023 12:42:10 -0500 Subject: [PATCH 06/45] serializaion of singature and point --- Cargo.toml | 1 + src/lib.rs | 25 ++++++++++++++++++------- 2 files changed, 19 insertions(+), 7 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index ce310fc..0981117 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -21,6 +21,7 @@ generic-array = "0.14" poseidon-rs = "0.0.8" arrayref = "0.3.5" lazy_static = "1.4.0" +serde = { version = "1.0.152", features = ["derive"] } [dev-dependencies] criterion = "0.3" diff --git a/src/lib.rs b/src/lib.rs index e11c45c..32c3397 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -2,7 +2,7 @@ // For LICENSE check https://github.com/arnaucube/babyjubjub-rs use ff::*; - +use serde::{Serialize, Deserialize, ser::SerializeSeq}; use poseidon_rs::Poseidon; pub type Fr = poseidon_rs::Fr; // alias @@ -138,6 +138,17 @@ pub struct Point { pub y: Fr, } +impl Serialize for Point { + fn serialize(&self, serializer: S) -> Result + where + S: serde::Serializer { + let mut seq = serializer.serialize_seq(Some(2))?; + seq.serialize_element(&self.x.to_string())?; + seq.serialize_element(&self.y.to_string())?; + seq.end() + } +} + impl Point { pub fn projective(&self) -> PointProjective { PointProjective { @@ -252,14 +263,14 @@ pub fn blh(b: &[u8]) -> Vec { #[derive(Debug, Clone)] pub struct Signature { pub r_b8: Point, - pub s: BigInt, + pub s: String, } impl Signature { pub fn compress(&self) -> [u8; 64] { let mut b: Vec = Vec::new(); b.append(&mut self.r_b8.compress().to_vec()); - let (_, s_bytes) = self.s.to_bytes_le(); + let (_, s_bytes) = self.s.parse::().unwrap().to_bytes_le(); let mut s_32bytes: [u8; 32] = [0; 32]; let len = min(s_bytes.len(), s_32bytes.len()); s_32bytes[..len].copy_from_slice(&s_bytes[..len]); @@ -276,7 +287,7 @@ pub fn decompress_signature(b: &[u8; 64]) -> Result { let r_b8 = decompress_point(r_b8_bytes); match r_b8 { Result::Err(err) => Err(err), - Result::Ok(res) => Ok(Signature { r_b8: res, s }), + Result::Ok(res) => Ok(Signature { r_b8: res, s: s.to_string() }), } } @@ -345,13 +356,13 @@ impl PrivateKey { let hm_input = vec![r_b8.x, r_b8.y, a.x, a.y, msg_fr]; let hm = POSEIDON.hash(hm_input)?; - let mut s = &self.scalar_key() << 3; + let mut s: BigInt = &self.scalar_key() << 3; let hm_b = BigInt::parse_bytes(to_hex(&hm).as_bytes(), 16).unwrap(); s = hm_b * s; s = r + s; s %= &SUBORDER.clone(); - Ok(Signature { r_b8, s }) + Ok(Signature { r_b8, s: s.to_string() }) } #[allow(clippy::many_single_char_names)] @@ -415,7 +426,7 @@ pub fn verify(pk: Point, sig: Signature, msg: BigInt) -> bool { Result::Err(_) => return false, Result::Ok(hm) => hm, }; - let l = B8.mul_scalar(&sig.s); + let l = B8.mul_scalar(&sig.s.parse::().unwrap()); let hm_b = BigInt::parse_bytes(to_hex(&hm).as_bytes(), 16).unwrap(); let r = sig .r_b8 From b4374c5720362ab4f09d1972255260711678f420 Mon Sep 17 00:00:00 2001 From: Nanak Nihal Singh Khalsa Date: Thu, 26 Jan 2023 13:20:23 -0500 Subject: [PATCH 07/45] serialization of signatures --- src/lib.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/lib.rs b/src/lib.rs index 32c3397..f064a48 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -2,7 +2,7 @@ // For LICENSE check https://github.com/arnaucube/babyjubjub-rs use ff::*; -use serde::{Serialize, Deserialize, ser::SerializeSeq}; +use serde::{Serialize, ser::SerializeSeq}; use poseidon_rs::Poseidon; pub type Fr = poseidon_rs::Fr; // alias @@ -260,7 +260,7 @@ pub fn blh(b: &[u8]) -> Vec { return digest[..].to_vec(); } -#[derive(Debug, Clone)] +#[derive(Debug, Clone, Serialize)] pub struct Signature { pub r_b8: Point, pub s: String, From 83e3b7688e77a3079f205bc71bf3abf2747c04e2 Mon Sep 17 00:00:00 2001 From: Nanak Nihal Singh Khalsa Date: Thu, 26 Jan 2023 22:48:43 -0500 Subject: [PATCH 08/45] removed print statements --- src/lib.rs | 2 -- 1 file changed, 2 deletions(-) diff --git a/src/lib.rs b/src/lib.rs index f064a48..c9e36d3 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -253,7 +253,6 @@ pub fn decompress_point(bb: [u8; 32]) -> Result { // } pub fn blh(b: &[u8]) -> Vec { - println!("TODO: add test that this new blake512 function is giving correct output. Not doing so could have unlikely yet critical implications"); let mut h = Blake2b512::new(); h.update(b); let digest = h.finalize(); @@ -743,7 +742,6 @@ mod tests { // test signature & verification let msg = BigInt::from_bytes_le(Sign::Plus, &hex::decode("00010203040506070809").unwrap()); - println!("msg {:?}", msg.to_string()); let sig = sk.sign(msg.clone()).unwrap(); assert_eq!( sig.r_b8.x.to_string(), From 86c18f0757081820e2e7c67677425c3891856277 Mon Sep 17 00:00:00 2001 From: Nanak Nihal Singh Khalsa Date: Thu, 2 Feb 2023 13:02:57 -0500 Subject: [PATCH 09/45] added elgamal decryption (UNTESTED) --- src/lib.rs | 24 +++++++++++++++++++++++- 1 file changed, 23 insertions(+), 1 deletion(-) diff --git a/src/lib.rs b/src/lib.rs index c9e36d3..60dfc06 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -13,7 +13,7 @@ use arrayref::array_ref; // #[cfg(feature = "aarch64")] // extern crate blake; // compatible version with Blake used at circomlib -use blake2::{Blake2b512, Blake2s256, Digest}; +use blake2::{Blake2b512, Digest}; // use hex_literal::hex; use std::cmp::min; @@ -158,6 +158,13 @@ impl Point { } } + pub fn inverse(&self) -> Point { + Point { + x: self.x.inverse().unwrap(), + y: self.y + } + } + pub fn mul_scalar(&self, n: &BigInt) -> Point { let mut r: PointProjective = PointProjective { x: Fr::zero(), @@ -382,6 +389,21 @@ impl PrivateKey { let s = k + &sk_scalar * &h; Ok((r, s)) } + + // pub fn encrypt_elgamal(&self, msg: Point) -> [Point; 2] { + + // } + + pub fn decrypt_elgamal(&self, c1: Point, c2: Point) -> Point { + let shared_secret = c1.mul_scalar(&self.scalar_key()); + let msg = c2.projective() + .add( + &shared_secret.inverse().projective() + ) + .affine(); + msg + } + } pub fn schnorr_hash(pk: &Point, msg: BigInt, c: &Point) -> Result { From 047409e3b18379d7a145be4e252a79e1e649ade2 Mon Sep 17 00:00:00 2001 From: Nanak Nihal Singh Khalsa Date: Thu, 2 Feb 2023 17:29:21 -0500 Subject: [PATCH 10/45] ElGamal w/ test --- src/lib.rs | 177 ++++++++++++++++++++++++++++++++++------------------- 1 file changed, 113 insertions(+), 64 deletions(-) diff --git a/src/lib.rs b/src/lib.rs index 60dfc06..e274988 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -35,7 +35,7 @@ lazy_static! { b"21888242871839275222246405745257275088548364400416034343698204186575808495617",10 ) .unwrap(); - static ref B8: Point = Point { + pub static ref B8: Point = Point { x: Fr::from_str( "5299619240641551281634865583518297030282874472190772894086521144482721001553", ) @@ -159,8 +159,10 @@ impl Point { } pub fn inverse(&self) -> Point { + let mut x_inverse = Fr::zero(); + x_inverse.sub_assign(&self.x); Point { - x: self.x.inverse().unwrap(), + x: x_inverse, y: self.y } } @@ -297,6 +299,11 @@ pub fn decompress_signature(b: &[u8; 64]) -> Result { } } +pub struct ElGamalEncryption { + c1: Point, + c2: Point +} + pub struct PrivateKey { key: [u8; 32], } @@ -390,22 +397,33 @@ impl PrivateKey { Ok((r, s)) } - // pub fn encrypt_elgamal(&self, msg: Point) -> [Point; 2] { - - // } - - pub fn decrypt_elgamal(&self, c1: Point, c2: Point) -> Point { - let shared_secret = c1.mul_scalar(&self.scalar_key()); - let msg = c2.projective() - .add( - &shared_secret.inverse().projective() - ) - .affine(); - msg + pub fn decrypt_elgamal(&self, encrypted_point: ElGamalEncryption) -> Point { + let shared_secret = encrypted_point.c1.mul_scalar(&self.scalar_key()); + println!("Shared Secret {:?}", shared_secret); + // Subtract the shared secret + encrypted_point.c2.projective().add( + &shared_secret.inverse().projective() + ).affine() } } +// encrypts msg to public key to_pubkey, using some random scalar nonce +pub fn encrypt_elgamal(to_pubkey: Point, nonce: BigInt, msg: &Point) -> ElGamalEncryption { + let shared_secret = to_pubkey.mul_scalar(&nonce); + println!("Shared Secret 2 {:?}", shared_secret); + let public_nonce = B8.mul_scalar(&nonce); + // let msg_point = point_for_msg(msg); + let msg_plus_secret = msg.projective().add( + &shared_secret.projective() + ) + .affine(); + ElGamalEncryption { + c1: public_nonce, + c2: msg_plus_secret + } +} + pub fn schnorr_hash(pk: &Point, msg: BigInt, c: &Point) -> Result { if msg > Q.clone() { return Err("msg outside the Finite Field".to_string()); @@ -461,7 +479,37 @@ mod tests { use super::*; use ::hex; use rand::Rng; + use num_traits::FromPrimitive; + #[test] + fn test_neg() { + let some_point = B8.mul_scalar(&BigInt::from_u8(0x69).unwrap()); + let another_point = B8.mul_scalar(&BigInt::from_u8(0x99).unwrap()); + let mut some_point_x_inverse = Fr::zero(); + some_point_x_inverse.sub_assign(&some_point.x); + // assert_eq!(some_point_x_inverse, some_point.x.inverse().unwrap()); + assert!(some_point.equals(some_point.projective().affine())); + assert!(some_point.equals( + some_point.projective().add(&another_point.projective()).add( + &another_point.inverse().projective()) + .affine() + )); + + } + #[test] + fn test_elgamal() { + let some_point = B8.mul_scalar(&BigInt::from_u8(0x69).unwrap()); + let some_privkey = new_key(); + let some_point_encrypted = encrypt_elgamal( + some_privkey.public(), + BigInt::parse_bytes(b"ABCDEF123456789", 16).unwrap(), + &some_point + ); + let some_point_encrypted_decrypted = some_privkey.decrypt_elgamal(some_point_encrypted); + + assert_eq!(some_point.x, some_point_encrypted_decrypted.x); + assert_eq!(some_point.y, some_point_encrypted_decrypted.y); + } #[test] fn test_add_same_point() { let p: PointProjective = PointProjective { @@ -730,54 +778,55 @@ mod tests { assert_eq!(true, verification); } - #[test] - fn test_circomlib_testvector() { - let sk_raw_bytes = - hex::decode("0001020304050607080900010203040506070809000102030405060708090001") - .unwrap(); - - // test blake compatible with circomlib implementation - let h: Vec = blh(&sk_raw_bytes); - assert_eq!(hex::encode(h), "c992db23d6290c70ffcc02f7abeb00b9d00fa8b43e55d7949c28ba6be7545d3253882a61bd004a236ef1cdba01b27ba0aedfb08eefdbfb7c19657c880b43ddf1"); - - // test private key - let sk = PrivateKey::import( - hex::decode("0001020304050607080900010203040506070809000102030405060708090001") - .unwrap(), - ) - .unwrap(); - assert_eq!( - sk.scalar_key().to_string(), - "6466070937662820620902051049739362987537906109895538826186780010858059362905" - ); - - // test public key - let pk = sk.public(); - assert_eq!( - pk.x.to_string(), - "Fr(0x1d5ac1f31407018b7d413a4f52c8f74463b30e6ac2238220ad8b254de4eaa3a2)" - ); - assert_eq!( - pk.y.to_string(), - "Fr(0x1e1de8a908826c3f9ac2e0ceee929ecd0caf3b99b3ef24523aaab796a6f733c4)" - ); - - // test signature & verification - let msg = BigInt::from_bytes_le(Sign::Plus, &hex::decode("00010203040506070809").unwrap()); - let sig = sk.sign(msg.clone()).unwrap(); - assert_eq!( - sig.r_b8.x.to_string(), - "Fr(0x192b4e51adf302c8139d356d0e08e2404b5ace440ef41fc78f5c4f2428df0765)" - ); - assert_eq!( - sig.r_b8.y.to_string(), - "Fr(0x2202bebcf57b820863e0acc88970b6ca7d987a0d513c2ddeb42e3f5d31b4eddf)" - ); - assert_eq!( - sig.s.to_string(), - "1672775540645840396591609181675628451599263765380031905495115170613215233181" - ); - let v = verify(pk, sig, msg); - assert_eq!(v, true); - } + // Removed this because broke circom siganture compatability due to different blake hash function: + // #[test] + // fn test_circomlib_testvector() { + // let sk_raw_bytes = + // hex::decode("0001020304050607080900010203040506070809000102030405060708090001") + // .unwrap(); + + // // test blake compatible with circomlib implementation + // let h: Vec = blh(&sk_raw_bytes); + // assert_eq!(hex::encode(h), "c992db23d6290c70ffcc02f7abeb00b9d00fa8b43e55d7949c28ba6be7545d3253882a61bd004a236ef1cdba01b27ba0aedfb08eefdbfb7c19657c880b43ddf1"); + + // // test private key + // let sk = PrivateKey::import( + // hex::decode("0001020304050607080900010203040506070809000102030405060708090001") + // .unwrap(), + // ) + // .unwrap(); + // assert_eq!( + // sk.scalar_key().to_string(), + // "6466070937662820620902051049739362987537906109895538826186780010858059362905" + // ); + + // // test public key + // let pk = sk.public(); + // assert_eq!( + // pk.x.to_string(), + // "Fr(0x1d5ac1f31407018b7d413a4f52c8f74463b30e6ac2238220ad8b254de4eaa3a2)" + // ); + // assert_eq!( + // pk.y.to_string(), + // "Fr(0x1e1de8a908826c3f9ac2e0ceee929ecd0caf3b99b3ef24523aaab796a6f733c4)" + // ); + + // // test signature & verification + // let msg = BigInt::from_bytes_le(Sign::Plus, &hex::decode("00010203040506070809").unwrap()); + // let sig = sk.sign(msg.clone()).unwrap(); + // assert_eq!( + // sig.r_b8.x.to_string(), + // "Fr(0x192b4e51adf302c8139d356d0e08e2404b5ace440ef41fc78f5c4f2428df0765)" + // ); + // assert_eq!( + // sig.r_b8.y.to_string(), + // "Fr(0x2202bebcf57b820863e0acc88970b6ca7d987a0d513c2ddeb42e3f5d31b4eddf)" + // ); + // assert_eq!( + // sig.s.to_string(), + // "1672775540645840396591609181675628451599263765380031905495115170613215233181" + // ); + // let v = verify(pk, sig, msg); + // assert_eq!(v, true); + // } } From 05ce8d4d80bbbe75ace0e718be6032f657d3bff9 Mon Sep 17 00:00:00 2001 From: Nanak Nihal Singh Khalsa Date: Fri, 3 Feb 2023 15:10:25 -0500 Subject: [PATCH 11/45] made point public in struct --- src/lib.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/lib.rs b/src/lib.rs index e274988..1a7c0b1 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -300,8 +300,8 @@ pub fn decompress_signature(b: &[u8; 64]) -> Result { } pub struct ElGamalEncryption { - c1: Point, - c2: Point + pub c1: Point, + pub c2: Point } pub struct PrivateKey { From c1d0a309cdc39d327448ab87b56f4825c9e44020 Mon Sep 17 00:00:00 2001 From: Nanak Nihal Singh Khalsa Date: Fri, 3 Feb 2023 16:06:34 -0500 Subject: [PATCH 12/45] added on_curve and tested it --- src/lib.rs | 27 ++++++++++++++++++++++++++- 1 file changed, 26 insertions(+), 1 deletion(-) diff --git a/src/lib.rs b/src/lib.rs index 1a7c0b1..62ca8d6 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -204,6 +204,24 @@ impl Point { } false } + + pub fn on_curve(&self) -> bool { + let mut x2 = self.x.clone(); + let mut y2 = self.y.clone(); + x2.mul_assign(&self.x); + y2.mul_assign(&self.y); + // compute left hand side ax^2+y^2 + let mut lhs = x2.clone(); + lhs.mul_assign(&A); + lhs.add_assign(&y2); + // compute right hand side: x^2*y^2*d+1 + let mut rhs = x2.clone(); + rhs.mul_assign(&y2); + rhs.mul_assign(&D); + rhs.add_assign(&Fr::one()); + + lhs.eq(&rhs) + } } pub fn test_bit(b: &[u8], i: usize) -> bool { @@ -481,6 +499,14 @@ mod tests { use rand::Rng; use num_traits::FromPrimitive; + #[test] + fn test_on_curve() { + let some_point = Point { x: Fr::from_str("1234").unwrap(), y: Fr::from_str("5678").unwrap() }; + assert_eq!(B8.on_curve(), true); + assert_eq!(B8.mul_scalar(&12345.to_bigint().unwrap()).on_curve(), true); + assert_eq!(some_point.on_curve(), false); + + } #[test] fn test_neg() { let some_point = B8.mul_scalar(&BigInt::from_u8(0x69).unwrap()); @@ -488,7 +514,6 @@ mod tests { let mut some_point_x_inverse = Fr::zero(); some_point_x_inverse.sub_assign(&some_point.x); // assert_eq!(some_point_x_inverse, some_point.x.inverse().unwrap()); - assert!(some_point.equals(some_point.projective().affine())); assert!(some_point.equals( some_point.projective().add(&another_point.projective()).add( &another_point.inverse().projective()) From 9313800860e47233677edb155b2c9fe1f25a59ae Mon Sep 17 00:00:00 2001 From: Nanak Nihal Singh Khalsa Date: Fri, 3 Feb 2023 16:17:16 -0500 Subject: [PATCH 13/45] added check that inputs are on curve --- src/lib.rs | 3 +++ 1 file changed, 3 insertions(+) diff --git a/src/lib.rs b/src/lib.rs index 62ca8d6..797d33f 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -416,6 +416,9 @@ impl PrivateKey { } pub fn decrypt_elgamal(&self, encrypted_point: ElGamalEncryption) -> Point { + // Make sure inputs aren't bad (i imagine this check could be skipped for performance reasons, but it seems a sanity check here would be helpful) + assert!(encrypted_point.c1.on_curve() && encrypted_point.c2.on_curve()); + let shared_secret = encrypted_point.c1.mul_scalar(&self.scalar_key()); println!("Shared Secret {:?}", shared_secret); // Subtract the shared secret From 7e85e690893a2a9efb7ead3e36a6e2ef7a4cf029 Mon Sep 17 00:00:00 2001 From: Nanak Nihal Singh Khalsa Date: Fri, 3 Feb 2023 16:38:15 -0500 Subject: [PATCH 14/45] debug --- src/lib.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/lib.rs b/src/lib.rs index 797d33f..fc5d025 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -316,7 +316,7 @@ pub fn decompress_signature(b: &[u8; 64]) -> Result { Result::Ok(res) => Ok(Signature { r_b8: res, s: s.to_string() }), } } - +#[derive(Debug)] pub struct ElGamalEncryption { pub c1: Point, pub c2: Point From 3b7e9c6acf524ff8a8f9874931869b90009e8781 Mon Sep 17 00:00:00 2001 From: Nanak Nihal Singh Khalsa Date: Fri, 3 Feb 2023 16:46:34 -0500 Subject: [PATCH 15/45] made encryption arguments pointers --- src/lib.rs | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/lib.rs b/src/lib.rs index fc5d025..6c24b2f 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -430,7 +430,7 @@ impl PrivateKey { } // encrypts msg to public key to_pubkey, using some random scalar nonce -pub fn encrypt_elgamal(to_pubkey: Point, nonce: BigInt, msg: &Point) -> ElGamalEncryption { +pub fn encrypt_elgamal(to_pubkey: &Point, nonce: &BigInt, msg: &Point) -> ElGamalEncryption { let shared_secret = to_pubkey.mul_scalar(&nonce); println!("Shared Secret 2 {:?}", shared_secret); let public_nonce = B8.mul_scalar(&nonce); @@ -529,8 +529,8 @@ mod tests { let some_point = B8.mul_scalar(&BigInt::from_u8(0x69).unwrap()); let some_privkey = new_key(); let some_point_encrypted = encrypt_elgamal( - some_privkey.public(), - BigInt::parse_bytes(b"ABCDEF123456789", 16).unwrap(), + &some_privkey.public(), + &BigInt::parse_bytes(b"ABCDEF123456789", 16).unwrap(), &some_point ); let some_point_encrypted_decrypted = some_privkey.decrypt_elgamal(some_point_encrypted); From 57a557e68238595ebc84c08870a3fec2da092f30 Mon Sep 17 00:00:00 2001 From: Nanak Nihal Singh Khalsa Date: Fri, 3 Feb 2023 16:48:49 -0500 Subject: [PATCH 16/45] removed println --- src/lib.rs | 2 -- 1 file changed, 2 deletions(-) diff --git a/src/lib.rs b/src/lib.rs index 6c24b2f..f36be71 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -418,9 +418,7 @@ impl PrivateKey { pub fn decrypt_elgamal(&self, encrypted_point: ElGamalEncryption) -> Point { // Make sure inputs aren't bad (i imagine this check could be skipped for performance reasons, but it seems a sanity check here would be helpful) assert!(encrypted_point.c1.on_curve() && encrypted_point.c2.on_curve()); - let shared_secret = encrypted_point.c1.mul_scalar(&self.scalar_key()); - println!("Shared Secret {:?}", shared_secret); // Subtract the shared secret encrypted_point.c2.projective().add( &shared_secret.inverse().projective() From 0299b2e0bb58454d79e3914d4441056df68f1ed6 Mon Sep 17 00:00:00 2001 From: Nanak Nihal Singh Khalsa Date: Fri, 3 Feb 2023 16:49:59 -0500 Subject: [PATCH 17/45] removed another println --- src/lib.rs | 1 - 1 file changed, 1 deletion(-) diff --git a/src/lib.rs b/src/lib.rs index f36be71..7f9c70e 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -430,7 +430,6 @@ impl PrivateKey { // encrypts msg to public key to_pubkey, using some random scalar nonce pub fn encrypt_elgamal(to_pubkey: &Point, nonce: &BigInt, msg: &Point) -> ElGamalEncryption { let shared_secret = to_pubkey.mul_scalar(&nonce); - println!("Shared Secret 2 {:?}", shared_secret); let public_nonce = B8.mul_scalar(&nonce); // let msg_point = point_for_msg(msg); let msg_plus_secret = msg.projective().add( From 305a8ce043fb31491441ee0f101b80ecfdf219d4 Mon Sep 17 00:00:00 2001 From: Nanak Nihal Singh Khalsa Date: Sat, 4 Feb 2023 15:40:07 -0500 Subject: [PATCH 18/45] working on message to point --- Cargo.toml | 2 ++ src/lib.rs | 30 +++++++++++++++++++++++++++++- 2 files changed, 31 insertions(+), 1 deletion(-) diff --git a/Cargo.toml b/Cargo.toml index 0981117..63f2cae 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -22,6 +22,8 @@ poseidon-rs = "0.0.8" arrayref = "0.3.5" lazy_static = "1.4.0" serde = { version = "1.0.152", features = ["derive"] } +bytes = "1.4.0" +rust-gmp = "0.5.0" [dev-dependencies] criterion = "0.3" diff --git a/src/lib.rs b/src/lib.rs index 7f9c70e..21f0b14 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -3,6 +3,7 @@ use ff::*; use serde::{Serialize, ser::SerializeSeq}; +use bytes::{BytesMut, BufMut}; use poseidon_rs::Poseidon; pub type Fr = poseidon_rs::Fr; // alias @@ -15,7 +16,7 @@ use arrayref::array_ref; // extern crate blake; // compatible version with Blake used at circomlib use blake2::{Blake2b512, Digest}; // use hex_literal::hex; -use std::cmp::min; +use std::{cmp::min, io::Bytes}; use num_bigint::{BigInt, RandBigInt, Sign, ToBigInt}; use num_traits::One; @@ -205,6 +206,33 @@ impl Point { false } + // // Use a variation of the Koblitz method + // pub fn from_msg_vartime(msg: &[u8; 28]) -> Point { + // } + + pub fn from_msg(msg: &[u8; 28]) -> Point { + // This is the largest point that can fit BabyJubJub curve while still allowing 8 extra bytes, as long as those bytes are less than f0000001 + // Babyjubjub r parameter is 0x30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f0000001 + assert!( + BigInt::from_bytes_be(Sign::Plus, msg) + < + BigInt::parse_bytes(b"30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f0000001",16).unwrap() + ); + let mut acc: u32 = 0; + let mut pt: Point; + let mut is_residue: bool = false; + let mut on_curve: bool = false; + while (acc <= 0xf0000001) && !on_curve { + let acc_bytes: [u8; 4] = acc.to_be_bytes(); + // let mut buff: ArrayVec::<[u8; 32]> = concat_bytes!()[msg, acc_bytes]); + let mut buf = BytesMut::with_capacity(32); + buf.put_slice(msg); + buf.put_u32(acc); + println!("bytes {:?}", buf); + } + Point {x:Fr::zero(), y:Fr::zero()} + } + pub fn on_curve(&self) -> bool { let mut x2 = self.x.clone(); let mut y2 = self.y.clone(); From aace3068e0a4e3acf0754d1f60582b1da83cbb09 Mon Sep 17 00:00:00 2001 From: Nanak Nihal Singh Khalsa Date: Sat, 4 Feb 2023 19:20:12 -0500 Subject: [PATCH 19/45] koblitz variation implemented but not yet tested --- src/lib.rs | 86 +++++++++++++++++++++++++++++++++++++++++------------- 1 file changed, 65 insertions(+), 21 deletions(-) diff --git a/src/lib.rs b/src/lib.rs index 21f0b14..6ef5ccd 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -206,31 +206,75 @@ impl Point { false } - // // Use a variation of the Koblitz method - // pub fn from_msg_vartime(msg: &[u8; 28]) -> Point { - // } - - pub fn from_msg(msg: &[u8; 28]) -> Point { + // Use a variation of the Koblitz method + pub fn from_msg_vartime(msg: BigInt/*msg: &[u8; 28]*/) -> Point { // This is the largest point that can fit BabyJubJub curve while still allowing 8 extra bytes, as long as those bytes are less than f0000001 // Babyjubjub r parameter is 0x30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f0000001 - assert!( - BigInt::from_bytes_be(Sign::Plus, msg) - < - BigInt::parse_bytes(b"30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f0000001",16).unwrap() - ); - let mut acc: u32 = 0; - let mut pt: Point; - let mut is_residue: bool = false; + // assert!( + // BigInt::from_bytes_be(Sign::Plus, msg) + // < + // BigInt::parse_bytes(b"30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f00000",16).unwrap() + // ); + // let mut acc: u32 = 0; + // let mut pt: Point; + // let mut is_residue: bool = false; + // let mut on_curve: bool = false; + // while (acc <= 0xf0000001) && !on_curve { + // let acc_bytes: [u8; 4] = acc.to_be_bytes(); + // // let mut buff: ArrayVec::<[u8; 32]> = concat_bytes!()[msg, acc_bytes]); + // let mut buf = BytesMut::with_capacity(32); + // buf.put_slice(msg); + // buf.put_u32(acc); + // Fr::from_str("123").unwrap().legendre() + // println!("bytes {:?}", buf); + // } + + // Koblitz decoding method, adapted for this curve: + // message m must be < r/10000 + // Try finding a point with y value m*10000+0, m*10000+1, .... m*10000+5617 (5617 are last four digits of prime r) + // There is an approximately 1/(2^1000) chance no point will be encodable, + // since each y value has probability of about 1/2 of being on the curve + let MAX_MSG: BigInt = BigInt::parse_bytes( + b"2188824287183927522224640574525727508854836440041603434369820418657580849",10 // Prime r but missing last 4 digits + ).unwrap(); + let ACC_UNDER = 5617; // Last four digits of prime r. MAX_MSG * 10000 + ACC_UNDER = r + assert!(msg <= MAX_MSG); + let mut acc: u16 = 0; let mut on_curve: bool = false; - while (acc <= 0xf0000001) && !on_curve { - let acc_bytes: [u8; 4] = acc.to_be_bytes(); - // let mut buff: ArrayVec::<[u8; 32]> = concat_bytes!()[msg, acc_bytes]); - let mut buf = BytesMut::with_capacity(32); - buf.put_slice(msg); - buf.put_u32(acc); - println!("bytes {:?}", buf); + // Start with message * 10000 as x coordinate + let mut x: Fr = Fr::from_str(&msg.to_str_radix(10)).unwrap(); + let mut y: Option = None; + x.mul_assign(&Fr::from_str("10000").unwrap()); + + let one = Fr::one(); + // let m10000 = 1000.to_bigint().unwrap() * msg; + while (acc < ACC_UNDER) && !on_curve { + // If x is on curve, calculate what y^2 should be, by (ax^2 - 1) / (dx^2 - 1) + let mut x2 = x.clone(); + x2.mul_assign(&x); + + // Numerator will be (ax^2 - 1) and denominator will be (dx^2 - 1) + let mut numerator = x2; + let mut denominator = x2.clone(); + + numerator.mul_assign(&A); + denominator.mul_assign(&D); + + numerator.sub_assign(&one); + denominator.sub_assign(&one); + + // If the point is on the curve, numerator/denominator will be y^2. Check whether numerator/denominator is a quadratic residue: + numerator.mul_assign(&denominator.inverse().unwrap()); // Note: this is no longer a numerator since it was divided in this step + if let LegendreSymbol::QuadraticResidue = numerator.legendre() { + on_curve=true; + y = numerator.sqrt(); + } else { + acc += 1; + } } - Point {x:Fr::zero(), y:Fr::zero()} + // Unwrap y since we can't be 100% sure at compile-time it will have been found; it may still be a None value! + Point {x:x, y:y.unwrap()} + } pub fn on_curve(&self) -> bool { From 0f64158364b008c697858329b9b03904b87bc52a Mon Sep 17 00:00:00 2001 From: Nanak Nihal Singh Khalsa Date: Sat, 4 Feb 2023 19:39:27 -0500 Subject: [PATCH 20/45] Koblitz method variant passing tests --- src/lib.rs | 17 +++++++++++++++++ 1 file changed, 17 insertions(+) diff --git a/src/lib.rs b/src/lib.rs index 6ef5ccd..141d690 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -270,6 +270,7 @@ impl Point { y = numerator.sqrt(); } else { acc += 1; + x.add_assign(&Fr::one()); } } // Unwrap y since we can't be 100% sure at compile-time it will have been found; it may still be a None value! @@ -578,6 +579,22 @@ mod tests { assert_eq!(B8.mul_scalar(&12345.to_bigint().unwrap()).on_curve(), true); assert_eq!(some_point.on_curve(), false); + } + #[test] + fn test_from_msg_vartime() { + let MAX_MSG: BigInt = BigInt::parse_bytes( + b"2188824287183927522224640574525727508854836440041603434369820418657580849",10 // Prime r but missing last 4 digits + ).unwrap(); + + let msg = 123456789.to_bigint().unwrap(); + assert!(Point::from_msg_vartime(msg).on_curve()); + + // Try with some more random numbers -- it's extremely unlikely to get lucky will with valid points 20 times in a row if it's not always producing valid points + for n in 0..20 { + let m = rand::thread_rng().gen_bigint_range(&0.to_bigint().unwrap() , &MAX_MSG); + assert!(Point::from_msg_vartime(m).on_curve()); + } + } #[test] fn test_neg() { From 63ab2f26f36d010f7f585e0b1011709d63e6cf5d Mon Sep 17 00:00:00 2001 From: Nanak Nihal Singh Khalsa Date: Sat, 4 Feb 2023 19:41:32 -0500 Subject: [PATCH 21/45] cleaned code a bit --- src/lib.rs | 31 ++++--------------------------- 1 file changed, 4 insertions(+), 27 deletions(-) diff --git a/src/lib.rs b/src/lib.rs index 141d690..ac1812c 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -206,34 +206,12 @@ impl Point { false } - // Use a variation of the Koblitz method - pub fn from_msg_vartime(msg: BigInt/*msg: &[u8; 28]*/) -> Point { - // This is the largest point that can fit BabyJubJub curve while still allowing 8 extra bytes, as long as those bytes are less than f0000001 - // Babyjubjub r parameter is 0x30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f0000001 - // assert!( - // BigInt::from_bytes_be(Sign::Plus, msg) - // < - // BigInt::parse_bytes(b"30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f00000",16).unwrap() - // ); - // let mut acc: u32 = 0; - // let mut pt: Point; - // let mut is_residue: bool = false; - // let mut on_curve: bool = false; - // while (acc <= 0xf0000001) && !on_curve { - // let acc_bytes: [u8; 4] = acc.to_be_bytes(); - // // let mut buff: ArrayVec::<[u8; 32]> = concat_bytes!()[msg, acc_bytes]); - // let mut buf = BytesMut::with_capacity(32); - // buf.put_slice(msg); - // buf.put_u32(acc); - // Fr::from_str("123").unwrap().legendre() - // println!("bytes {:?}", buf); - // } - - // Koblitz decoding method, adapted for this curve: + // Koblitz decoding method, adapted for this curve: // message m must be < r/10000 // Try finding a point with y value m*10000+0, m*10000+1, .... m*10000+5617 (5617 are last four digits of prime r) // There is an approximately 1/(2^1000) chance no point will be encodable, // since each y value has probability of about 1/2 of being on the curve + pub fn from_msg_vartime(msg: BigInt) -> Point { let MAX_MSG: BigInt = BigInt::parse_bytes( b"2188824287183927522224640574525727508854836440041603434369820418657580849",10 // Prime r but missing last 4 digits ).unwrap(); @@ -245,9 +223,8 @@ impl Point { let mut x: Fr = Fr::from_str(&msg.to_str_radix(10)).unwrap(); let mut y: Option = None; x.mul_assign(&Fr::from_str("10000").unwrap()); - let one = Fr::one(); - // let m10000 = 1000.to_bigint().unwrap() * msg; + while (acc < ACC_UNDER) && !on_curve { // If x is on curve, calculate what y^2 should be, by (ax^2 - 1) / (dx^2 - 1) let mut x2 = x.clone(); @@ -270,7 +247,7 @@ impl Point { y = numerator.sqrt(); } else { acc += 1; - x.add_assign(&Fr::one()); + x.add_assign(&one); } } // Unwrap y since we can't be 100% sure at compile-time it will have been found; it may still be a None value! From 63400ce7560a6dda2a11a7416644ab729e45df83 Mon Sep 17 00:00:00 2001 From: Nanak Nihal Singh Khalsa Date: Sat, 4 Feb 2023 20:58:19 -0500 Subject: [PATCH 22/45] encdoding message to/from point works and passes tests --- src/lib.rs | 70 +++++++++++++++++++++++++++++++++++++++++------------- 1 file changed, 53 insertions(+), 17 deletions(-) diff --git a/src/lib.rs b/src/lib.rs index ac1812c..bd02f64 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -59,6 +59,17 @@ lazy_static! { .unwrap() >> 3; pub static ref POSEIDON: poseidon_rs::Poseidon = Poseidon::new(); + + // MAX_MSG is maximum message length that can be encoded into a point. This will be Q / KOBLITZ_NUMBER + pub static ref MAX_MSG: BigInt = BigInt::parse_bytes( + b"21888242871839275222246405745257275088548364400416034343698204186575808495617",10 + ) + .unwrap() + >> 10; + // An arbitrary number for Koblitz method of encoding string to point. 1024 is convenient compared to original 1000 to do bitshifts instead of multiplications/divisions + pub static ref KOBLITZ_NUMBER: Fr = Fr::from_str("1024").unwrap(); + pub static ref KOBLITZ_NUMBER_INV: Fr = Fr::from_str("1024").unwrap().inverse().unwrap(); + } #[derive(Clone, Debug)] @@ -207,24 +218,21 @@ impl Point { } // Koblitz decoding method, adapted for this curve: - // message m must be < r/10000 - // Try finding a point with y value m*10000+0, m*10000+1, .... m*10000+5617 (5617 are last four digits of prime r) - // There is an approximately 1/(2^1000) chance no point will be encodable, + // message m must be < r/(2^10) //using 2^10=1024 instead of Koblitz' parameter of 1000, so arithmetic can happen easily mod 2. This shouldn't have any (negative) impact: https://crypto.stackexchange.com/questions/103132/encoding-a-message-as-points-on-elliptic-curve + // Try finding a point with y value m*1024+0, m*1024+1, .... m*1024+5617 (5617 are last four digits of prime r) + // There is an approximately 1/(2^1024) chance no point will be encodable, // since each y value has probability of about 1/2 of being on the curve - pub fn from_msg_vartime(msg: BigInt) -> Point { - let MAX_MSG: BigInt = BigInt::parse_bytes( - b"2188824287183927522224640574525727508854836440041603434369820418657580849",10 // Prime r but missing last 4 digits - ).unwrap(); - let ACC_UNDER = 5617; // Last four digits of prime r. MAX_MSG * 10000 + ACC_UNDER = r - assert!(msg <= MAX_MSG); + pub fn from_msg_vartime(msg: &BigInt) -> Point { + let ACC_UNDER = 1024; // Last four digits of prime r. MAX_MSG * 1024 + ACC_UNDER = r + assert!(msg <= &MAX_MSG); let mut acc: u16 = 0; let mut on_curve: bool = false; // Start with message * 10000 as x coordinate let mut x: Fr = Fr::from_str(&msg.to_str_radix(10)).unwrap(); let mut y: Option = None; - x.mul_assign(&Fr::from_str("10000").unwrap()); + x.mul_assign(&Fr::from_str(&ACC_UNDER.to_string()).unwrap()); let one = Fr::one(); - + while (acc < ACC_UNDER) && !on_curve { // If x is on curve, calculate what y^2 should be, by (ax^2 - 1) / (dx^2 - 1) let mut x2 = x.clone(); @@ -255,6 +263,15 @@ impl Point { } + // Converts a point to a message by dividing by 1024 (a.k.a. right-shifting by 10) + pub fn to_msg(&self) -> Fr { + let mut msg = self.x.clone().into_repr(); + // println!("{:?}", self.x.into_repr().shr(10)); + msg.shr(10); + Fr::from_repr(msg).unwrap() + + } + pub fn on_curve(&self) -> bool { let mut x2 = self.x.clone(); let mut y2 = self.y.clone(); @@ -559,20 +576,39 @@ mod tests { } #[test] fn test_from_msg_vartime() { - let MAX_MSG: BigInt = BigInt::parse_bytes( - b"2188824287183927522224640574525727508854836440041603434369820418657580849",10 // Prime r but missing last 4 digits - ).unwrap(); - let msg = 123456789.to_bigint().unwrap(); - assert!(Point::from_msg_vartime(msg).on_curve()); + assert!(Point::from_msg_vartime(&msg).on_curve()); // Try with some more random numbers -- it's extremely unlikely to get lucky will with valid points 20 times in a row if it's not always producing valid points for n in 0..20 { let m = rand::thread_rng().gen_bigint_range(&0.to_bigint().unwrap() , &MAX_MSG); - assert!(Point::from_msg_vartime(m).on_curve()); + assert!(Point::from_msg_vartime(&m).on_curve()); } } + + #[test] + fn test_from_to_msg() { + // Convert from msg to point back to msg and make sure it works: + let msg = 123456789.to_bigint().unwrap(); + assert!( + Point::from_msg_vartime(&msg).to_msg() + .eq( + &Fr::from_str(&msg.to_string()).unwrap() + ) + ); + + // Try with some more random numbers -- it's extremely unlikely to get lucky will with valid points 20 times in a row if it's not always producing valid points + for n in 0..20 { + let m = rand::thread_rng().gen_bigint_range(&0.to_bigint().unwrap() , &MAX_MSG); + assert!( + Point::from_msg_vartime(&m).to_msg() + .eq( + &Fr::from_str(&m.to_string()).unwrap() + ) + ); + } + } #[test] fn test_neg() { let some_point = B8.mul_scalar(&BigInt::from_u8(0x69).unwrap()); From b3c47101aa9636d0f3369935292e8c4c1888c6a8 Mon Sep 17 00:00:00 2001 From: Nanak Nihal Singh Khalsa Date: Sun, 5 Feb 2023 11:52:12 -0500 Subject: [PATCH 23/45] js-friendly string method --- src/lib.rs | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/src/lib.rs b/src/lib.rs index bd02f64..65d89ea 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -289,6 +289,13 @@ impl Point { lhs.eq(&rhs) } + + pub fn from_xy_strings(x: String, y: String) -> Point { + Point { + x: Fr::from_str(&x).unwrap(), + y: Fr::from_str(&y).unwrap() + } + } } pub fn test_bit(b: &[u8], i: usize) -> bool { From c10293fbc4673e2a161866a626ac919df2ceda99 Mon Sep 17 00:00:00 2001 From: Nanak Nihal Singh Khalsa Date: Sun, 5 Feb 2023 13:45:55 -0500 Subject: [PATCH 24/45] to_dec_string() --- Cargo.toml | 1 - src/lib.rs | 18 +++++++++++++++--- 2 files changed, 15 insertions(+), 4 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index 63f2cae..bfada65 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -23,7 +23,6 @@ arrayref = "0.3.5" lazy_static = "1.4.0" serde = { version = "1.0.152", features = ["derive"] } bytes = "1.4.0" -rust-gmp = "0.5.0" [dev-dependencies] criterion = "0.3" diff --git a/src/lib.rs b/src/lib.rs index 65d89ea..dbdf97e 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -2,6 +2,7 @@ // For LICENSE check https://github.com/arnaucube/babyjubjub-rs use ff::*; +use num::Num; use serde::{Serialize, ser::SerializeSeq}; use bytes::{BytesMut, BufMut}; use poseidon_rs::Poseidon; @@ -150,13 +151,24 @@ pub struct Point { pub y: Fr, } +pub trait ToDecimalString { + fn to_dec_string(&self) -> String; +} +impl ToDecimalString for Fr { + fn to_dec_string(&self) -> String { + let mut s = self.to_string(); + let hex_str = s[5..s.len()-1].to_string(); + BigInt::from_str_radix(&hex_str, 16).unwrap().to_string() + } +} + impl Serialize for Point { fn serialize(&self, serializer: S) -> Result where S: serde::Serializer { let mut seq = serializer.serialize_seq(Some(2))?; - seq.serialize_element(&self.x.to_string())?; - seq.serialize_element(&self.y.to_string())?; + seq.serialize_element(&self.x.to_dec_string())?; + seq.serialize_element(&self.y.to_dec_string())?; seq.end() } } @@ -390,7 +402,7 @@ pub fn decompress_signature(b: &[u8; 64]) -> Result { Result::Ok(res) => Ok(Signature { r_b8: res, s: s.to_string() }), } } -#[derive(Debug)] +#[derive(Debug, Serialize)] pub struct ElGamalEncryption { pub c1: Point, pub c2: Point From feb4b91ffe4a4c8d00f77362a6f9439b11da8f87 Mon Sep 17 00:00:00 2001 From: Nanak Nihal Singh Khalsa Date: Sun, 5 Feb 2023 16:06:30 -0500 Subject: [PATCH 25/45] renamed inverse to neg for more clarity --- src/lib.rs | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/src/lib.rs b/src/lib.rs index dbdf97e..f7e7396 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -182,7 +182,7 @@ impl Point { } } - pub fn inverse(&self) -> Point { + pub fn neg(&self) -> Point { let mut x_inverse = Fr::zero(); x_inverse.sub_assign(&self.x); Point { @@ -278,7 +278,6 @@ impl Point { // Converts a point to a message by dividing by 1024 (a.k.a. right-shifting by 10) pub fn to_msg(&self) -> Fr { let mut msg = self.x.clone().into_repr(); - // println!("{:?}", self.x.into_repr().shr(10)); msg.shr(10); Fr::from_repr(msg).unwrap() @@ -507,7 +506,7 @@ impl PrivateKey { let shared_secret = encrypted_point.c1.mul_scalar(&self.scalar_key()); // Subtract the shared secret encrypted_point.c2.projective().add( - &shared_secret.inverse().projective() + &shared_secret.neg().projective() ).affine() } @@ -637,7 +636,7 @@ mod tests { // assert_eq!(some_point_x_inverse, some_point.x.inverse().unwrap()); assert!(some_point.equals( some_point.projective().add(&another_point.projective()).add( - &another_point.inverse().projective()) + &another_point.neg().projective()) .affine() )); From 0b163704f86f7b8acd1ac49a70abf298315827fb Mon Sep 17 00:00:00 2001 From: Nanak Nihal Singh Khalsa Date: Mon, 6 Feb 2023 08:51:04 -0500 Subject: [PATCH 26/45] added traits for convenience --- src/lib.rs | 11 ++++++++++- 1 file changed, 10 insertions(+), 1 deletion(-) diff --git a/src/lib.rs b/src/lib.rs index f7e7396..ae5e99f 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -17,7 +17,7 @@ use arrayref::array_ref; // extern crate blake; // compatible version with Blake used at circomlib use blake2::{Blake2b512, Digest}; // use hex_literal::hex; -use std::{cmp::min, io::Bytes}; +use std::{cmp::min, io::Bytes, str::FromStr}; use num_bigint::{BigInt, RandBigInt, Sign, ToBigInt}; use num_traits::One; @@ -161,6 +161,15 @@ impl ToDecimalString for Fr { BigInt::from_str_radix(&hex_str, 16).unwrap().to_string() } } +pub trait FrToBigInt { + fn to_bigint(&self) -> BigInt; +} + +impl FrToBigInt for Fr { + fn to_bigint(&self) -> BigInt { + BigInt::from_str(&self.to_dec_string()).unwrap() + } +} impl Serialize for Point { fn serialize(&self, serializer: S) -> Result From 9c1a43797be65c10d3e9d2ff46a764e63f067f84 Mon Sep 17 00:00:00 2001 From: Nanak Nihal Singh Khalsa Date: Mon, 6 Feb 2023 09:00:01 -0500 Subject: [PATCH 27/45] refactored addition --- src/lib.rs | 31 ++++++++++++++----------------- 1 file changed, 14 insertions(+), 17 deletions(-) diff --git a/src/lib.rs b/src/lib.rs index ae5e99f..f0c27d8 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -191,6 +191,9 @@ impl Point { } } + pub fn add(&self, another_point: &Point) -> Point { + self.projective().add(&another_point.projective()).affine() + } pub fn neg(&self) -> Point { let mut x_inverse = Fr::zero(); x_inverse.sub_assign(&self.x); @@ -514,9 +517,7 @@ impl PrivateKey { assert!(encrypted_point.c1.on_curve() && encrypted_point.c2.on_curve()); let shared_secret = encrypted_point.c1.mul_scalar(&self.scalar_key()); // Subtract the shared secret - encrypted_point.c2.projective().add( - &shared_secret.neg().projective() - ).affine() + encrypted_point.c2.add(&shared_secret.neg()) } } @@ -526,10 +527,8 @@ pub fn encrypt_elgamal(to_pubkey: &Point, nonce: &BigInt, msg: &Point) -> ElGama let shared_secret = to_pubkey.mul_scalar(&nonce); let public_nonce = B8.mul_scalar(&nonce); // let msg_point = point_for_msg(msg); - let msg_plus_secret = msg.projective().add( - &shared_secret.projective() - ) - .affine(); + let msg_plus_secret = msg.add(&shared_secret); + ElGamalEncryption { c1: public_nonce, c2: msg_plus_secret @@ -554,9 +553,9 @@ pub fn verify_schnorr(pk: Point, m: BigInt, r: Point, s: BigInt) -> Result PrivateKey { @@ -581,9 +580,8 @@ pub fn verify(pk: Point, sig: Signature, msg: BigInt) -> bool { let hm_b = BigInt::parse_bytes(to_hex(&hm).as_bytes(), 16).unwrap(); let r = sig .r_b8 - .projective() - .add(&pk.mul_scalar(&(8.to_bigint().unwrap() * hm_b)).projective()); - l.equals(r.affine()) + .add(&pk.mul_scalar(&(8.to_bigint().unwrap() * hm_b))); + l.equals(r) } #[cfg(test)] @@ -644,9 +642,8 @@ mod tests { some_point_x_inverse.sub_assign(&some_point.x); // assert_eq!(some_point_x_inverse, some_point.x.inverse().unwrap()); assert!(some_point.equals( - some_point.projective().add(&another_point.projective()).add( - &another_point.neg().projective()) - .affine() + some_point.add(&another_point).add( + &another_point.neg()) )); } @@ -758,8 +755,8 @@ mod tests { .unwrap(), }; let res_m = p.mul_scalar(&3.to_bigint().unwrap()); - let res_a = p.projective().add(&p.projective()); - let res_a = res_a.add(&p.projective()).affine(); + let res_a = p.add(&p); + let res_a = res_a.add(&p); assert_eq!(res_m.x, res_a.x); assert_eq!( res_m.x, From 809d43625e717faa8cf5f09c9883aaa2d04b3b08 Mon Sep 17 00:00:00 2001 From: Nanak Nihal Singh Khalsa Date: Thu, 9 Feb 2023 13:01:45 -0500 Subject: [PATCH 28/45] added (no pun intended) sum feature --- src/lib.rs | 33 +++++++++++++++++++++++++++++++-- 1 file changed, 31 insertions(+), 2 deletions(-) diff --git a/src/lib.rs b/src/lib.rs index f0c27d8..b2159d5 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -2,6 +2,7 @@ // For LICENSE check https://github.com/arnaucube/babyjubjub-rs use ff::*; +use std::iter::Sum; use num::Num; use serde::{Serialize, ser::SerializeSeq}; use bytes::{BytesMut, BufMut}; @@ -161,16 +162,44 @@ impl ToDecimalString for Fr { BigInt::from_str_radix(&hex_str, 16).unwrap().to_string() } } -pub trait FrToBigInt { +pub trait FrBigIntConversion { + fn from_bigint(bi: &BigInt) -> Fr; fn to_bigint(&self) -> BigInt; } -impl FrToBigInt for Fr { +impl FrBigIntConversion for Fr { + // Note: this could probably be more efficient by converting bigint to raw repr to Fr + fn from_bigint(bi: &BigInt) -> Fr { + Fr::from_str(&bi.to_string()).unwrap() + } fn to_bigint(&self) -> BigInt { BigInt::from_str(&self.to_dec_string()).unwrap() } } +pub struct FrWrapper { + pub fr: Fr +} +impl FrWrapper { + pub fn from_fr(fr: Fr) -> FrWrapper { + FrWrapper { fr: fr } + } +} + +impl Sum for FrWrapper { + fn sum>(iter: I) -> Self { + iter.reduce( + |frw1,frw2| { + let mut tmp = frw1.fr.clone(); + tmp.add_assign(&frw2.fr); + FrWrapper { fr: tmp } + } + + ).unwrap() + } +} + + impl Serialize for Point { fn serialize(&self, serializer: S) -> Result where From a6946a16f993e56f9a7e45705a2ab0e89f971c3f Mon Sep 17 00:00:00 2001 From: Nanak Nihal Singh Khalsa Date: Fri, 10 Feb 2023 16:03:40 -0500 Subject: [PATCH 29/45] added new field Fl instead of Fr for suborder instead of order --- Cargo.toml | 3 ++- src/lib.rs | 53 +++++++++++++++++++++++++++++++++++++++++------------ 2 files changed, 43 insertions(+), 13 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index bfada65..87034c2 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -10,7 +10,8 @@ readme = "README.md" [dependencies] ff = {package="ff_ce", version= "0.11", features = ["derive"]} -rand = "0.8" +rand_new = {package="rand", version="0.8"} +rand = "0.4.6" num = "0.4" num-bigint = {version = "0.4", features = ["rand"]} num-traits = "0.2.8" diff --git a/src/lib.rs b/src/lib.rs index b2159d5..a1ab7b6 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -9,6 +9,17 @@ use bytes::{BytesMut, BufMut}; use poseidon_rs::Poseidon; pub type Fr = poseidon_rs::Fr; // alias +extern crate rand_new; +extern crate rand; +#[macro_use] +extern crate ff; + +// Create a new primefield for the subgroup defined by the base point, order Fl: +#[derive(PrimeField)] +#[PrimeFieldModulus = "2736030358979909402780800718157159386076813972158567259200215660948447373041"] +#[PrimeFieldGenerator = "7"] // TODO: double check this is a valid generator!!! +pub struct Fl(FpRepr); + use arrayref::array_ref; // #[cfg(not(feature = "aarch64"))] @@ -54,7 +65,7 @@ lazy_static! { .unwrap(); // SUBORDER = ORDER >> 3 - static ref SUBORDER: BigInt = &BigInt::parse_bytes( + pub static ref SUBORDER: BigInt = &BigInt::parse_bytes( b"21888242871839275222246405745257275088614511777268538073601725287587578984328", 10, ) @@ -69,8 +80,8 @@ lazy_static! { .unwrap() >> 10; // An arbitrary number for Koblitz method of encoding string to point. 1024 is convenient compared to original 1000 to do bitshifts instead of multiplications/divisions - pub static ref KOBLITZ_NUMBER: Fr = Fr::from_str("1024").unwrap(); - pub static ref KOBLITZ_NUMBER_INV: Fr = Fr::from_str("1024").unwrap().inverse().unwrap(); +// pub static ref KOBLITZ_NUMBER: Fr = Fr::from_str("1024").unwrap(); +// pub static ref KOBLITZ_NUMBER_INV: Fr = Fr::from_str("1024").unwrap().inverse().unwrap(); } @@ -162,12 +173,20 @@ impl ToDecimalString for Fr { BigInt::from_str_radix(&hex_str, 16).unwrap().to_string() } } -pub trait FrBigIntConversion { - fn from_bigint(bi: &BigInt) -> Fr; + +impl ToDecimalString for Fl { + fn to_dec_string(&self) -> String { + let mut s = self.to_string(); + let hex_str = s[5..s.len()-1].to_string(); + BigInt::from_str_radix(&hex_str, 16).unwrap().to_string() + } +} +pub trait FrBigIntConversion { + fn from_bigint(bi: &BigInt) -> T; fn to_bigint(&self) -> BigInt; } -impl FrBigIntConversion for Fr { +impl FrBigIntConversion for Fr { // Note: this could probably be more efficient by converting bigint to raw repr to Fr fn from_bigint(bi: &BigInt) -> Fr { Fr::from_str(&bi.to_string()).unwrap() @@ -177,6 +196,16 @@ impl FrBigIntConversion for Fr { } } +impl FrBigIntConversion for Fl { + // Note: this could probably be more efficient by converting bigint to raw repr to Fr + fn from_bigint(bi: &BigInt) -> Fl { + Fl::from_str(&bi.to_string()).unwrap() + } + fn to_bigint(&self) -> BigInt { + BigInt::from_str(&self.to_dec_string()).unwrap() + } +} + pub struct FrWrapper { pub fr: Fr } @@ -525,7 +554,7 @@ impl PrivateKey { #[allow(clippy::many_single_char_names)] pub fn sign_schnorr(&self, m: BigInt) -> Result<(Point, BigInt), String> { // random r - let mut rng = rand::thread_rng(); + let mut rng = rand_new::thread_rng(); let k = rng.gen_biguint(1024).to_bigint().unwrap(); // r = k·G @@ -589,7 +618,7 @@ pub fn verify_schnorr(pk: Point, m: BigInt, r: Point, s: BigInt) -> Result PrivateKey { // https://tools.ietf.org/html/rfc8032#section-5.1.5 - let mut rng = rand::thread_rng(); + let mut rng = rand_new::thread_rng(); let sk_raw = rng.gen_biguint(1024).to_bigint().unwrap(); let (_, sk_raw_bytes) = sk_raw.to_bytes_be(); PrivateKey::import(sk_raw_bytes[..32].to_vec()).unwrap() @@ -617,7 +646,7 @@ pub fn verify(pk: Point, sig: Signature, msg: BigInt) -> bool { mod tests { use super::*; use ::hex; - use rand::Rng; + use rand_new::Rng; use num_traits::FromPrimitive; #[test] @@ -635,7 +664,7 @@ mod tests { // Try with some more random numbers -- it's extremely unlikely to get lucky will with valid points 20 times in a row if it's not always producing valid points for n in 0..20 { - let m = rand::thread_rng().gen_bigint_range(&0.to_bigint().unwrap() , &MAX_MSG); + let m = rand_new::thread_rng().gen_bigint_range(&0.to_bigint().unwrap() , &MAX_MSG); assert!(Point::from_msg_vartime(&m).on_curve()); } @@ -654,7 +683,7 @@ mod tests { // Try with some more random numbers -- it's extremely unlikely to get lucky will with valid points 20 times in a row if it's not always producing valid points for n in 0..20 { - let m = rand::thread_rng().gen_bigint_range(&0.to_bigint().unwrap() , &MAX_MSG); + let m = rand_new::thread_rng().gen_bigint_range(&0.to_bigint().unwrap() , &MAX_MSG); assert!( Point::from_msg_vartime(&m).to_msg() .eq( @@ -907,7 +936,7 @@ mod tests { #[test] fn test_point_decompress_loop() { for _ in 0..5 { - let random_bytes = rand::thread_rng().gen::<[u8; 32]>(); + let random_bytes = rand_new::thread_rng().gen::<[u8; 32]>(); let sk_raw: BigInt = BigInt::from_bytes_le(Sign::Plus, &random_bytes[..]); let (_, sk_raw_bytes) = sk_raw.to_bytes_be(); let mut h: Vec = blh(&sk_raw_bytes); From e4a4a5cc4f46b18846557e69eb00db9e834256bf Mon Sep 17 00:00:00 2001 From: Nanak Nihal Singh Khalsa Date: Sat, 11 Feb 2023 19:10:13 +0100 Subject: [PATCH 30/45] point deserialization --- src/lib.rs | 54 +++++++++++++++++++++++++++++++++++++++++++++++++++++- 1 file changed, 53 insertions(+), 1 deletion(-) diff --git a/src/lib.rs b/src/lib.rs index a1ab7b6..e193d17 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -4,7 +4,9 @@ use ff::*; use std::iter::Sum; use num::Num; -use serde::{Serialize, ser::SerializeSeq}; +use std::fmt; +// use serde::{Serialize, ser::SerializeSeq, Deserialize}; +use serde::{Serialize, ser::SerializeSeq, de::Visitor, de::MapAccess, Deserialize, Deserializer}; use bytes::{BytesMut, BufMut}; use poseidon_rs::Poseidon; pub type Fr = poseidon_rs::Fr; // alias @@ -240,6 +242,56 @@ impl Serialize for Point { } } + +impl<'de> Deserialize<'de> for Point { + fn deserialize(deserializer: D) -> Result + where + D: Deserializer<'de> + { + //deserializer.deserialize_any(CustomVisitor) + deserializer.deserialize_map(PointVisitor) + } +} +// For deserialization: +struct PointVisitor; + +impl<'de> Visitor<'de> for PointVisitor { + type Value = Point; + + fn expecting(&self, formatter: &mut fmt::Formatter) -> fmt::Result { + write!(formatter, "a map with keys 'first' and 'second'") + } + + fn visit_map(self, mut map: M) -> Result + where + M: MapAccess<'de> + { + let mut x = None; + let mut y = None; + + while let Some(k) = map.next_key::<&str>()? { + if k == "x" { + let x_: String = map.next_value()?; + x = Fr::from_str(&x_); + } + else if k == "y" { + let y_: String = map.next_value()?; + y = Fr::from_str(&y_); + } + else { + return Err(serde::de::Error::custom(&format!("Invalid key: {}", k))); + } + } + + if x.is_none() || y.is_none() { + return Err(serde::de::Error::custom("Missing first or second")); + } + + Ok(Point { x: x.unwrap(), y: y.unwrap() }) + } +} + + impl Point { pub fn projective(&self) -> PointProjective { PointProjective { From 8c69310f2555f70da9ee3181378bb235fb7a019b Mon Sep 17 00:00:00 2001 From: Nanak Nihal Singh Khalsa Date: Sat, 11 Feb 2023 19:33:45 +0100 Subject: [PATCH 31/45] better deserialization and serialization for point --- src/lib.rs | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/src/lib.rs b/src/lib.rs index e193d17..1a4ebdf 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -2,11 +2,12 @@ // For LICENSE check https://github.com/arnaucube/babyjubjub-rs use ff::*; +use core::panic; use std::iter::Sum; use num::Num; use std::fmt; // use serde::{Serialize, ser::SerializeSeq, Deserialize}; -use serde::{Serialize, ser::SerializeSeq, de::Visitor, de::MapAccess, Deserialize, Deserializer}; +use serde::{Serialize, ser::SerializeStruct, de::Visitor, de::MapAccess, Deserialize, Deserializer}; use bytes::{BytesMut, BufMut}; use poseidon_rs::Poseidon; pub type Fr = poseidon_rs::Fr; // alias @@ -235,10 +236,10 @@ impl Serialize for Point { fn serialize(&self, serializer: S) -> Result where S: serde::Serializer { - let mut seq = serializer.serialize_seq(Some(2))?; - seq.serialize_element(&self.x.to_dec_string())?; - seq.serialize_element(&self.y.to_dec_string())?; - seq.end() + let mut state = serializer.serialize_struct("Point", 2)?; + state.serialize_field("x", &self.x.to_dec_string())?; + state.serialize_field("y", &self.y.to_dec_string())?; + state.end() } } @@ -248,7 +249,6 @@ impl<'de> Deserialize<'de> for Point { where D: Deserializer<'de> { - //deserializer.deserialize_any(CustomVisitor) deserializer.deserialize_map(PointVisitor) } } @@ -259,7 +259,7 @@ impl<'de> Visitor<'de> for PointVisitor { type Value = Point; fn expecting(&self, formatter: &mut fmt::Formatter) -> fmt::Result { - write!(formatter, "a map with keys 'first' and 'second'") + write!(formatter, "a map with keys 'x' and 'y'") } fn visit_map(self, mut map: M) -> Result @@ -269,7 +269,7 @@ impl<'de> Visitor<'de> for PointVisitor { let mut x = None; let mut y = None; - while let Some(k) = map.next_key::<&str>()? { + while let Some(k) = map.next_key::()? { if k == "x" { let x_: String = map.next_value()?; x = Fr::from_str(&x_); From 1557322cafb99a78aedaaf5fa15ef5b63f736c96 Mon Sep 17 00:00:00 2001 From: Nanak Nihal Singh Khalsa Date: Sat, 11 Feb 2023 19:53:17 +0100 Subject: [PATCH 32/45] added deserialization for the ElGamalEncryption Struct --- src/lib.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/lib.rs b/src/lib.rs index 1a4ebdf..33bc5a3 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -523,7 +523,7 @@ pub fn decompress_signature(b: &[u8; 64]) -> Result { Result::Ok(res) => Ok(Signature { r_b8: res, s: s.to_string() }), } } -#[derive(Debug, Serialize)] +#[derive(Debug, Serialize, Deserialize)] pub struct ElGamalEncryption { pub c1: Point, pub c2: Point From ff3b16b6e27f6f2b3a57af0de4e1424e74cacc06 Mon Sep 17 00:00:00 2001 From: Nanak Nihal Singh Khalsa Date: Sat, 4 Mar 2023 19:11:47 -0700 Subject: [PATCH 33/45] message is now converted to a point in the subgroup --- src/lib.rs | 66 +++++++++++++++++++++++++++++++++++++++--------------- 1 file changed, 48 insertions(+), 18 deletions(-) diff --git a/src/lib.rs b/src/lib.rs index 33bc5a3..1440d5a 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -62,7 +62,13 @@ lazy_static! { ) .unwrap(), }; - static ref ORDER: Fr = Fr::from_str( + + pub static ref O: Point = Point { + x: Fr::zero(), + y: Fr::one() + }; + + pub static ref ORDER: Fr = Fr::from_str( "21888242871839275222246405745257275088614511777268538073601725287587578984328", ) .unwrap(); @@ -356,18 +362,18 @@ impl Point { // Try finding a point with y value m*1024+0, m*1024+1, .... m*1024+5617 (5617 are last four digits of prime r) // There is an approximately 1/(2^1024) chance no point will be encodable, // since each y value has probability of about 1/2 of being on the curve - pub fn from_msg_vartime(msg: &BigInt) -> Point { + pub fn from_msg_vartime(msg: &BigInt) -> Option { let ACC_UNDER = 1024; // Last four digits of prime r. MAX_MSG * 1024 + ACC_UNDER = r assert!(msg <= &MAX_MSG); let mut acc: u16 = 0; - let mut on_curve: bool = false; + let mut in_subgroup: bool = false; // Start with message * 10000 as x coordinate let mut x: Fr = Fr::from_str(&msg.to_str_radix(10)).unwrap(); - let mut y: Option = None; + let mut y: Fr; x.mul_assign(&Fr::from_str(&ACC_UNDER.to_string()).unwrap()); let one = Fr::one(); - while (acc < ACC_UNDER) && !on_curve { + while (acc < ACC_UNDER) && !in_subgroup { // If x is on curve, calculate what y^2 should be, by (ax^2 - 1) / (dx^2 - 1) let mut x2 = x.clone(); x2.mul_assign(&x); @@ -384,16 +390,31 @@ impl Point { // If the point is on the curve, numerator/denominator will be y^2. Check whether numerator/denominator is a quadratic residue: numerator.mul_assign(&denominator.inverse().unwrap()); // Note: this is no longer a numerator since it was divided in this step + // match numerator.legendre() { + // LegendreSymbol::QuadraticResidue() => { + + // } + // _ => { + // acc += 1; + // x.add_assign(&one); + // } + // } + let mut on_curve: bool = false; if let LegendreSymbol::QuadraticResidue = numerator.legendre() { - on_curve=true; - y = numerator.sqrt(); - } else { - acc += 1; - x.add_assign(&one); - } + on_curve = true; + y = numerator.sqrt().unwrap(); + let pt = Point {x:x, y:y}; + if pt.in_subgroup() { + in_subgroup = true; + return Some(Point {x:x, y:y}) + } + } + acc += 1; + x.add_assign(&one); } - // Unwrap y since we can't be 100% sure at compile-time it will have been found; it may still be a None value! - Point {x:x, y:y.unwrap()} + return None + // // Unwrap y since we can't be 100% sure at compile-time it will have been found; it may still be a None value! + // Point {x:x, y:y.unwrap()} } @@ -423,6 +444,14 @@ impl Point { lhs.eq(&rhs) } + // This could be made more efficeint by using static ref to O + pub fn in_subgroup(&self) -> bool { + let should_be_zero = self.mul_scalar(&SUBORDER); + should_be_zero.equals({ + Point { x: Fr::zero(), y: Fr::one() } + }) + } + pub fn from_xy_strings(x: String, y: String) -> Point { Point { x: Fr::from_str(&x).unwrap(), @@ -712,12 +741,13 @@ mod tests { #[test] fn test_from_msg_vartime() { let msg = 123456789.to_bigint().unwrap(); - assert!(Point::from_msg_vartime(&msg).on_curve()); + assert!(Point::from_msg_vartime(&msg).unwrap().on_curve()); // Try with some more random numbers -- it's extremely unlikely to get lucky will with valid points 20 times in a row if it's not always producing valid points for n in 0..20 { let m = rand_new::thread_rng().gen_bigint_range(&0.to_bigint().unwrap() , &MAX_MSG); - assert!(Point::from_msg_vartime(&m).on_curve()); + assert!(Point::from_msg_vartime(&m).unwrap().on_curve()); + assert!(Point::from_msg_vartime(&m).unwrap().in_subgroup()); } } @@ -727,7 +757,7 @@ mod tests { // Convert from msg to point back to msg and make sure it works: let msg = 123456789.to_bigint().unwrap(); assert!( - Point::from_msg_vartime(&msg).to_msg() + Point::from_msg_vartime(&msg).unwrap().to_msg() .eq( &Fr::from_str(&msg.to_string()).unwrap() ) @@ -736,8 +766,8 @@ mod tests { // Try with some more random numbers -- it's extremely unlikely to get lucky will with valid points 20 times in a row if it's not always producing valid points for n in 0..20 { let m = rand_new::thread_rng().gen_bigint_range(&0.to_bigint().unwrap() , &MAX_MSG); - assert!( - Point::from_msg_vartime(&m).to_msg() + assert!( + Point::from_msg_vartime(&m).unwrap().to_msg() .eq( &Fr::from_str(&m.to_string()).unwrap() ) From f6ca288c7a8fe8aa7208c28c6ec0bcd6c5051182 Mon Sep 17 00:00:00 2001 From: Nanak Nihal Singh Khalsa Date: Wed, 5 Apr 2023 19:43:04 -0700 Subject: [PATCH 34/45] sanitize inputs --- src/lib.rs | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/src/lib.rs b/src/lib.rs index 1440d5a..5937308 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -653,7 +653,11 @@ impl PrivateKey { pub fn decrypt_elgamal(&self, encrypted_point: ElGamalEncryption) -> Point { // Make sure inputs aren't bad (i imagine this check could be skipped for performance reasons, but it seems a sanity check here would be helpful) - assert!(encrypted_point.c1.on_curve() && encrypted_point.c2.on_curve()); + assert!(encrypted_point.c1.on_curve(), "Error: C1 is not on the curve!"); + assert!(encrypted_point.c1.in_subgroup(), "Error: C1 is not in the subgroup!"); + assert!(encrypted_point.c2.on_curve(), "Error: C2 is not on the curve!"); + assert!(encrypted_point.c2.in_subgroup(), "Error: C2 is not in the subgroup!"); + let shared_secret = encrypted_point.c1.mul_scalar(&self.scalar_key()); // Subtract the shared secret encrypted_point.c2.add(&shared_secret.neg()) From ee96c573dfca705b2b0b52a1309b5ad4f73f8767 Mon Sep 17 00:00:00 2001 From: Nanak Nihal Singh Khalsa Date: Mon, 15 May 2023 01:16:55 -0400 Subject: [PATCH 35/45] added clonability --- src/lib.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/lib.rs b/src/lib.rs index 5937308..72d8801 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -552,7 +552,7 @@ pub fn decompress_signature(b: &[u8; 64]) -> Result { Result::Ok(res) => Ok(Signature { r_b8: res, s: s.to_string() }), } } -#[derive(Debug, Serialize, Deserialize)] +#[derive(Debug, Serialize, Deserialize, Clone)] pub struct ElGamalEncryption { pub c1: Point, pub c2: Point From 1561ed7b58138bfb434c438fd3377555f2f32aac Mon Sep 17 00:00:00 2001 From: Nanak Nihal Singh Khalsa Date: Fri, 19 May 2023 19:33:20 -0400 Subject: [PATCH 36/45] improved tests and elimitating warnings --- src/lib.rs | 69 ++++++++++++++++++++++++++++++++++-------------------- 1 file changed, 44 insertions(+), 25 deletions(-) diff --git a/src/lib.rs b/src/lib.rs index 72d8801..a123584 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -2,19 +2,18 @@ // For LICENSE check https://github.com/arnaucube/babyjubjub-rs use ff::*; -use core::panic; use std::iter::Sum; use num::Num; use std::fmt; // use serde::{Serialize, ser::SerializeSeq, Deserialize}; use serde::{Serialize, ser::SerializeStruct, de::Visitor, de::MapAccess, Deserialize, Deserializer}; -use bytes::{BytesMut, BufMut}; +// use bytes::{BytesMut, BufMut}; use poseidon_rs::Poseidon; pub type Fr = poseidon_rs::Fr; // alias extern crate rand_new; extern crate rand; -#[macro_use] +// #[macro_use] extern crate ff; // Create a new primefield for the subgroup defined by the base point, order Fl: @@ -32,7 +31,7 @@ use arrayref::array_ref; // extern crate blake; // compatible version with Blake used at circomlib use blake2::{Blake2b512, Digest}; // use hex_literal::hex; -use std::{cmp::min, io::Bytes, str::FromStr}; +use std::{cmp::min, str::FromStr}; use num_bigint::{BigInt, RandBigInt, Sign, ToBigInt}; use num_traits::One; @@ -52,6 +51,18 @@ lazy_static! { b"21888242871839275222246405745257275088548364400416034343698204186575808495617",10 ) .unwrap(); + + pub static ref G: Point = Point { + x: Fr::from_str( + "995203441582195749578291179787384436505546430278305826713579947235728471134", + ) + .unwrap(), + y: Fr::from_str( + "5472060717959818805561601436314318772137091100104008585924551046643952123905", + ) + .unwrap(), + }; + pub static ref B8: Point = Point { x: Fr::from_str( "5299619240641551281634865583518297030282874472190772894086521144482721001553", @@ -60,7 +71,7 @@ lazy_static! { y: Fr::from_str( "16950150798460657717958625567821834550301663161624707787222815936182638968203", ) - .unwrap(), + .unwrap(), }; pub static ref O: Point = Point { @@ -177,7 +188,7 @@ pub trait ToDecimalString { } impl ToDecimalString for Fr { fn to_dec_string(&self) -> String { - let mut s = self.to_string(); + let s = self.to_string(); let hex_str = s[5..s.len()-1].to_string(); BigInt::from_str_radix(&hex_str, 16).unwrap().to_string() } @@ -185,7 +196,7 @@ impl ToDecimalString for Fr { impl ToDecimalString for Fl { fn to_dec_string(&self) -> String { - let mut s = self.to_string(); + let s = self.to_string(); let hex_str = s[5..s.len()-1].to_string(); BigInt::from_str_radix(&hex_str, 16).unwrap().to_string() } @@ -363,17 +374,17 @@ impl Point { // There is an approximately 1/(2^1024) chance no point will be encodable, // since each y value has probability of about 1/2 of being on the curve pub fn from_msg_vartime(msg: &BigInt) -> Option { + #[allow(non_snake_case)] let ACC_UNDER = 1024; // Last four digits of prime r. MAX_MSG * 1024 + ACC_UNDER = r assert!(msg <= &MAX_MSG); let mut acc: u16 = 0; - let mut in_subgroup: bool = false; // Start with message * 10000 as x coordinate let mut x: Fr = Fr::from_str(&msg.to_str_radix(10)).unwrap(); let mut y: Fr; x.mul_assign(&Fr::from_str(&ACC_UNDER.to_string()).unwrap()); let one = Fr::one(); - while (acc < ACC_UNDER) && !in_subgroup { + while acc < ACC_UNDER { // If x is on curve, calculate what y^2 should be, by (ax^2 - 1) / (dx^2 - 1) let mut x2 = x.clone(); x2.mul_assign(&x); @@ -390,23 +401,12 @@ impl Point { // If the point is on the curve, numerator/denominator will be y^2. Check whether numerator/denominator is a quadratic residue: numerator.mul_assign(&denominator.inverse().unwrap()); // Note: this is no longer a numerator since it was divided in this step - // match numerator.legendre() { - // LegendreSymbol::QuadraticResidue() => { - - // } - // _ => { - // acc += 1; - // x.add_assign(&one); - // } - // } - let mut on_curve: bool = false; + if let LegendreSymbol::QuadraticResidue = numerator.legendre() { - on_curve = true; y = numerator.sqrt().unwrap(); - let pt = Point {x:x, y:y}; + let pt = Point {x, y}; if pt.in_subgroup() { - in_subgroup = true; - return Some(Point {x:x, y:y}) + return Some(Point {x, y}) } } acc += 1; @@ -736,11 +736,30 @@ mod tests { #[test] fn test_on_curve() { - let some_point = Point { x: Fr::from_str("1234").unwrap(), y: Fr::from_str("5678").unwrap() }; assert_eq!(B8.on_curve(), true); assert_eq!(B8.mul_scalar(&12345.to_bigint().unwrap()).on_curve(), true); + + let some_point = Point { x: Fr::from_str("1234").unwrap(), y: Fr::from_str("5678").unwrap() }; assert_eq!(some_point.on_curve(), false); - + } + + #[test] + fn test_in_subgroup() { + assert_eq!(B8.in_subgroup(), true); + assert_eq!(B8.mul_scalar(&12345.to_bigint().unwrap()).in_subgroup(), true); + + assert_eq!(G.in_subgroup(), false); + assert_eq!(G.mul_scalar(&5.to_bigint().unwrap()).in_subgroup(), false); + assert_eq!(G.mul_scalar(&7.to_bigint().unwrap()).in_subgroup(), false); + + assert_eq!(G.mul_scalar(&8.to_bigint().unwrap()).in_subgroup(), true); + assert_eq!(G.mul_scalar(&16.to_bigint().unwrap()).in_subgroup(), true); + assert_eq!(G.mul_scalar(&8000.to_bigint().unwrap()).in_subgroup(), true); + + + + + } #[test] fn test_from_msg_vartime() { From 2e3e838c112dfd805987d4d20300b8af145b84fa Mon Sep 17 00:00:00 2001 From: Nanak Nihal Singh Khalsa Date: Sat, 20 May 2023 10:53:54 -0400 Subject: [PATCH 37/45] removed unused weird code --- src/lib.rs | 40 ++++++++++++++++++++-------------------- 1 file changed, 20 insertions(+), 20 deletions(-) diff --git a/src/lib.rs b/src/lib.rs index a123584..057510c 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -226,27 +226,27 @@ impl FrBigIntConversion for Fl { } } -pub struct FrWrapper { - pub fr: Fr -} -impl FrWrapper { - pub fn from_fr(fr: Fr) -> FrWrapper { - FrWrapper { fr: fr } - } -} - -impl Sum for FrWrapper { - fn sum>(iter: I) -> Self { - iter.reduce( - |frw1,frw2| { - let mut tmp = frw1.fr.clone(); - tmp.add_assign(&frw2.fr); - FrWrapper { fr: tmp } - } +// pub struct FrWrapper { +// pub fr: Fr +// } +// impl FrWrapper { +// pub fn from_fr(fr: Fr) -> FrWrapper { +// FrWrapper { fr: fr } +// } +// } - ).unwrap() - } -} +// impl Sum for FrWrapper { +// fn sum>(iter: I) -> Self { +// iter.reduce( +// |frw1,frw2| { +// let mut tmp = frw1.fr.clone(); +// tmp.add_assign(&frw2.fr); +// FrWrapper { fr: tmp } +// } + +// ).unwrap() +// } +// } impl Serialize for Point { From 5439c2a19f7628c8b82a1d7bcb30541e34a3e5a2 Mon Sep 17 00:00:00 2001 From: Nanak Nihal Singh Khalsa Date: Sun, 21 May 2023 15:21:13 -0400 Subject: [PATCH 38/45] fixed source of bug which was passing a negative BigInt to mul_scalar...better fix might be to force mul_scalar to only accept a BigUInt or to allow the logic in mul_scalar to work with a negative BigInt --- Cargo.toml | 2 +- src/lib.rs | 163 ++++++++++++++++++++++++++++++++++++++++++++++++++--- 2 files changed, 156 insertions(+), 9 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index 87034c2..6367269 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -10,7 +10,7 @@ readme = "README.md" [dependencies] ff = {package="ff_ce", version= "0.11", features = ["derive"]} -rand_new = {package="rand", version="0.8"} +rand_new = {package="rand", version="0.8.5"} rand = "0.4.6" num = "0.4" num-bigint = {version = "0.4", features = ["rand"]} diff --git a/src/lib.rs b/src/lib.rs index 057510c..d74a3bb 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -2,7 +2,8 @@ // For LICENSE check https://github.com/arnaucube/babyjubjub-rs use ff::*; -use std::iter::Sum; +use rand::ThreadRng; +use std::{iter::Sum, ops::{Neg, AddAssign}}; use num::Num; use std::fmt; // use serde::{Serialize, ser::SerializeSeq, Deserialize}; @@ -81,16 +82,19 @@ lazy_static! { pub static ref ORDER: Fr = Fr::from_str( "21888242871839275222246405745257275088614511777268538073601725287587578984328", - ) - .unwrap(); + ).unwrap(); + + pub static ref ORDER_BI: BigInt = BigInt::parse_bytes( + b"21888242871839275222246405745257275088614511777268538073601725287587578984328", + 10, + ).unwrap(); // SUBORDER = ORDER >> 3 pub static ref SUBORDER: BigInt = &BigInt::parse_bytes( b"21888242871839275222246405745257275088614511777268538073601725287587578984328", 10, - ) - .unwrap() - >> 3; + ).unwrap() + >> 3; pub static ref POSEIDON: poseidon_rs::Poseidon = Poseidon::new(); // MAX_MSG is maximum message length that can be encoded into a point. This will be Q / KOBLITZ_NUMBER @@ -248,7 +252,6 @@ impl FrBigIntConversion for Fl { // } // } - impl Serialize for Point { fn serialize(&self, serializer: S) -> Result where @@ -308,7 +311,7 @@ impl<'de> Visitor<'de> for PointVisitor { } } - +// For addition shorthand impl Point { pub fn projective(&self) -> PointProjective { PointProjective { @@ -727,6 +730,137 @@ pub fn verify(pk: Point, sig: Signature, msg: BigInt) -> bool { l.equals(r) } + +pub struct DLEQProof { + pub A: Point, + pub B: Point, + pub xA: Point, + pub xB: Point, + pub challenge: BigInt, + pub response: BigInt, +} +impl DLEQProof { + // Prove that ∃ x s.t. x*A = variable xA and x*B = variable xB + pub fn new(x: BigInt, point_A: Point, point_B: Point) -> DLEQProof { + let xA = point_A.mul_scalar(&x); + let xB = point_B.mul_scalar(&x); + let mut rng = rand_new::thread_rng(); + let k = rng.gen_biguint(512).to_bigint().unwrap() % Q.clone(); + let kA = point_A.mul_scalar(&k); + let kB = point_B.mul_scalar(&k); + + let challenge = DLEQProof::get_challenge(&point_A, &point_B, &xA, &xB, &kA, &kB); + + let k_field = Fr::from_str(&k.to_string()).unwrap(); + let x_field = Fr::from_str(&k.to_string()).unwrap(); + let challenge_field = Fr::from_str(&challenge.to_string()).unwrap(); + + let mut response_field = challenge_field.clone(); + response_field.negate(); + response_field.mul_assign(&x_field); + response_field.add_assign(&k_field); + // let response = Fr::from k - &challenge * &x) % Q.clone(); + // let response = BigInt::from_str("-10").unwrap();//(ORDER_BI.clone() + BigInt::from_str("100").unwrap()).neg(); + let response = response_field.to_bigint(); + println!("response: {:?}", response); + + // -7: + // let neg7 = "-7".parse::().unwrap(); + // let neg7_ = Q.clone() + &neg7; + // let product = B8.mul_scalar(&neg7); + // let product_ = B8.mul_scalar(&neg7_); + + // println!("\nproduct: {:?} ====================== {:?} \n", product.x, product_.x); + + // let cxA = xA.mul_scalar(&challenge); + // let cxA_ = point_A.mul_scalar(&challenge).mul_scalar(&x); + // println!("cxA: {:?} ====================== {:?} ", cxA, cxA_); + + let xchallenge = &x * &challenge; + let response_plus_xchallenge = &response + &xchallenge; + + let xchallengeA = point_A.mul_scalar(&xchallenge); + let responseA = point_A.mul_scalar(&response); + + let sum_method1 = responseA.add(&xchallengeA); + let sum_method2 = point_A.mul_scalar(&(response_plus_xchallenge)); + + // DELETE THIS: + // let kA1 = point_A.mul_scalar(&(&response + &challenge * &x)); + // let kA2 = point_A.mul_scalar(&response).add( + // &point_A.mul_scalar(&(&challenge * &x)) + // ); + // let _a = Q.clone() - BigInt::from_str("10").unwrap(); + // let _b = BigInt::from_str("2000").unwrap(); + // let _A = B8.mul_scalar(&_a); + // let _B = B8.mul_scalar(&_b); + // let _prod = _A.add(&_B); + // let __prod = B8.mul_scalar(&(_a+_b)); + // println!("\n\n\n\n\n\nprods: {:?} =================== {:?}\n\n\n\n", _prod.x, __prod.x); + // println!("one {:?}", point_A.mul_scalar(&BigInt::from_bytes_be(Sign::Plus,&[1])).x); + // println!("two {:?}", point_A.mul_scalar(&BigInt::from_bytes_be(Sign::Plus,&[2])).x); + // println!("one {:?}", point_A.mul_scalar(&BigInt::from_bytes_be(Sign::Plus,&[1])).x); + + // let kB_ = + // point_B.mul_scalar(&response).add( + // &xA.mul_scalar(&challenge) + // ); + // println!("kA==kA_? {:?}. kB==kB_? {:?}", kA_.equals(kA), kB_.equals(kB)); + // println!("1x: {:?}\n2x: {:?}", sum_method1.x, sum_method2.x); + println!("kA: {:?}. kB {:?}", kA, kB); + + DLEQProof { + A: point_A, + B: point_B, + xA, + xB, + challenge, + response + } + } + + pub fn verify(&self) -> bool { + // This should equal kA + let kA_ = + self.A.mul_scalar(&self.response).add( + &self.xA.mul_scalar(&self.challenge) + ); + // This should equal kB + let kB_ = + self.B.mul_scalar(&self.response).add( + &self.xB.mul_scalar(&self.challenge) + ); + println!("kA_: {:?}. kB_ {:?}", kA_, kB_); + // Check that kA_ == kA and kB_ == kB by recomputing the challenge and checking that it matches + let challenge = DLEQProof::get_challenge(&self.A, &self.B, &self.xA, &self.xB, &kA_, &kB_); + // println!("got challenge: {:?} which should equal {:?}", challenge, self.challenge); + return challenge == self.challenge; + } + // Generates randomness for DLEQ Fiat-Shamir transform + fn get_challenge(point_A: &Point, point_B: &Point, xA: &Point, xB: &Point, kA: &Point, kB: &Point) -> BigInt { + // This could probably be faster if we neglect either the x or y coordinate, but rn doing both to be safe until i study this more + let inputs: Vec = vec![ + point_A.x,point_A.y, + xA.x,xA.y, + point_B.x,point_B.y, + xB.x,xB.y, + kA.x,kA.y, + kB.x,kB.y + ]; + let input: Vec = inputs.iter().map(|f| { + let as_hex = to_hex(f); + hex::decode(as_hex).unwrap() + }) + .flatten() + .collect(); + + let challenge_hash = blh(&input); + // println!("{:?} is hash of input {:?}", challenge_hash, input); + BigInt::from_bytes_be(Sign::Plus, &challenge_hash.as_slice()) + % ORDER_BI.clone() + } +} + #[cfg(test)] mod tests { use super::*; @@ -734,6 +868,19 @@ mod tests { use rand_new::Rng; use num_traits::FromPrimitive; + #[test] + fn test_dleq() { + let a = BigInt::parse_bytes(b"123456789", 10).unwrap(); + let point_A = B8.mul_scalar(&a); + let b = BigInt::parse_bytes(b"55555555555555512345678944444444444", 10).unwrap(); + let point_B = B8.mul_scalar(&b); + + let mut rng = rand_new::thread_rng(); + let x = rng.gen_biguint(512).to_bigint().unwrap() % ORDER_BI.clone(); + let proof = DLEQProof::new(x, point_A, point_B); + println!("proof: {:?}", proof.verify()); + assert!(proof.verify()); + } #[test] fn test_on_curve() { assert_eq!(B8.on_curve(), true); From c0b4f7888f1fbfa20fbedbfbea41984e6b6f3c74 Mon Sep 17 00:00:00 2001 From: Nanak Nihal Singh Khalsa Date: Sun, 11 Jun 2023 14:33:21 -0400 Subject: [PATCH 39/45] dleq proof working --- src/lib.rs | 128 ++++++++++++++++++----------------------------------- 1 file changed, 42 insertions(+), 86 deletions(-) diff --git a/src/lib.rs b/src/lib.rs index d74a3bb..e7c64e2 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -3,7 +3,7 @@ use ff::*; use rand::ThreadRng; -use std::{iter::Sum, ops::{Neg, AddAssign}}; +use std::{iter::Sum, ops::{Neg, AddAssign}, fmt::Error}; use num::Num; use std::fmt; // use serde::{Serialize, ser::SerializeSeq, Deserialize}; @@ -213,6 +213,7 @@ pub trait FrBigIntConversion { impl FrBigIntConversion for Fr { // Note: this could probably be more efficient by converting bigint to raw repr to Fr fn from_bigint(bi: &BigInt) -> Fr { + println!("bi: {}", bi.to_string()); Fr::from_str(&bi.to_string()).unwrap() } fn to_bigint(&self) -> BigInt { @@ -736,108 +737,65 @@ pub struct DLEQProof { pub B: Point, pub xA: Point, pub xB: Point, - pub challenge: BigInt, - pub response: BigInt, + pub challenge: Fl, + pub response: Fl, } impl DLEQProof { - // Prove that ∃ x s.t. x*A = variable xA and x*B = variable xB - pub fn new(x: BigInt, point_A: Point, point_B: Point) -> DLEQProof { - let xA = point_A.mul_scalar(&x); - let xB = point_B.mul_scalar(&x); - let mut rng = rand_new::thread_rng(); - let k = rng.gen_biguint(512).to_bigint().unwrap() % Q.clone(); - let kA = point_A.mul_scalar(&k); - let kB = point_B.mul_scalar(&k); + // Prove x*A = variable xA and x*B = variable xB + pub fn new(x: Fl, point_A: Point, point_B: Point) -> Result { + let x_bigint = x.to_bigint(); - let challenge = DLEQProof::get_challenge(&point_A, &point_B, &xA, &xB, &kA, &kB); + // let modulus_overflowed = ORDER.clone(); // TODO: shouldn't this be the subgroup order? This is not order of Babyjubjub curve nor the subgroup; it's the order Babyjubjub is defined over + let modulus = SUBORDER.clone(); + // TODO: better error handling (not assert), make it more efficient too: + assert!(x_bigint < modulus); - let k_field = Fr::from_str(&k.to_string()).unwrap(); - let x_field = Fr::from_str(&k.to_string()).unwrap(); - let challenge_field = Fr::from_str(&challenge.to_string()).unwrap(); - - let mut response_field = challenge_field.clone(); - response_field.negate(); - response_field.mul_assign(&x_field); - response_field.add_assign(&k_field); - // let response = Fr::from k - &challenge * &x) % Q.clone(); - // let response = BigInt::from_str("-10").unwrap();//(ORDER_BI.clone() + BigInt::from_str("100").unwrap()).neg(); - let response = response_field.to_bigint(); - println!("response: {:?}", response); - - // -7: - // let neg7 = "-7".parse::().unwrap(); - // let neg7_ = Q.clone() + &neg7; - // let product = B8.mul_scalar(&neg7); - // let product_ = B8.mul_scalar(&neg7_); - - // println!("\nproduct: {:?} ====================== {:?} \n", product.x, product_.x); + // println!("modulus overflow? isn't it bigger than Fr's order: {:?}", modulus_overflowed); + let k_bigint = rand_new::thread_rng().gen_biguint(512).to_bigint().unwrap() % &modulus; + let k = Fl::from_bigint(&k_bigint); - // let cxA = xA.mul_scalar(&challenge); - // let cxA_ = point_A.mul_scalar(&challenge).mul_scalar(&x); - // println!("cxA: {:?} ====================== {:?} ", cxA, cxA_); + let xA = point_A.mul_scalar(&x_bigint); + let xB = point_B.mul_scalar(&x_bigint); - let xchallenge = &x * &challenge; - let response_plus_xchallenge = &response + &xchallenge; + let kA = point_A.mul_scalar(&k_bigint); + let kB = point_B.mul_scalar(&k_bigint); - let xchallengeA = point_A.mul_scalar(&xchallenge); - let responseA = point_A.mul_scalar(&response); - - let sum_method1 = responseA.add(&xchallengeA); - let sum_method2 = point_A.mul_scalar(&(response_plus_xchallenge)); - - // DELETE THIS: - // let kA1 = point_A.mul_scalar(&(&response + &challenge * &x)); - // let kA2 = point_A.mul_scalar(&response).add( - // &point_A.mul_scalar(&(&challenge * &x)) - // ); - // let _a = Q.clone() - BigInt::from_str("10").unwrap(); - // let _b = BigInt::from_str("2000").unwrap(); - // let _A = B8.mul_scalar(&_a); - // let _B = B8.mul_scalar(&_b); - // let _prod = _A.add(&_B); - // let __prod = B8.mul_scalar(&(_a+_b)); - // println!("\n\n\n\n\n\nprods: {:?} =================== {:?}\n\n\n\n", _prod.x, __prod.x); - // println!("one {:?}", point_A.mul_scalar(&BigInt::from_bytes_be(Sign::Plus,&[1])).x); - // println!("two {:?}", point_A.mul_scalar(&BigInt::from_bytes_be(Sign::Plus,&[2])).x); - // println!("one {:?}", point_A.mul_scalar(&BigInt::from_bytes_be(Sign::Plus,&[1])).x); - - // let kB_ = - // point_B.mul_scalar(&response).add( - // &xA.mul_scalar(&challenge) - // ); - // println!("kA==kA_? {:?}. kB==kB_? {:?}", kA_.equals(kA), kB_.equals(kB)); - // println!("1x: {:?}\n2x: {:?}", sum_method1.x, sum_method2.x); - println!("kA: {:?}. kB {:?}", kA, kB); - - DLEQProof { + let challenge = DLEQProof::get_challenge(&point_A, &point_B, &xA, &xB, &kA, &kB); + let mut challenge_x = challenge.clone(); challenge_x.mul_assign(&x); + // response = k - challenge * x; + let mut response = k.clone(); response.sub_assign(&challenge_x); + + Ok(DLEQProof { A: point_A, B: point_B, - xA, - xB, - challenge, - response - } + xA: xA, + xB: xB, + challenge: challenge, + response: response, + }) } pub fn verify(&self) -> bool { // This should equal kA let kA_ = - self.A.mul_scalar(&self.response).add( - &self.xA.mul_scalar(&self.challenge) + self.A.mul_scalar(&self.response.to_bigint()).add( + &self.xA.mul_scalar(&self.challenge.to_bigint()) ); + // This should equal kB let kB_ = - self.B.mul_scalar(&self.response).add( - &self.xB.mul_scalar(&self.challenge) + self.B.mul_scalar(&self.response.to_bigint()).add( + &self.xB.mul_scalar(&self.challenge.to_bigint()) ); - println!("kA_: {:?}. kB_ {:?}", kA_, kB_); - // Check that kA_ == kA and kB_ == kB by recomputing the challenge and checking that it matches + let challenge = DLEQProof::get_challenge(&self.A, &self.B, &self.xA, &self.xB, &kA_, &kB_); - // println!("got challenge: {:?} which should equal {:?}", challenge, self.challenge); + println!("got challenge: {:?} which should equal {:?}", challenge, self.challenge); + return challenge == self.challenge; + } // Generates randomness for DLEQ Fiat-Shamir transform - fn get_challenge(point_A: &Point, point_B: &Point, xA: &Point, xB: &Point, kA: &Point, kB: &Point) -> BigInt { + fn get_challenge(point_A: &Point, point_B: &Point, xA: &Point, xB: &Point, kA: &Point, kB: &Point) -> Fl { // This could probably be faster if we neglect either the x or y coordinate, but rn doing both to be safe until i study this more let inputs: Vec = vec![ point_A.x,point_A.y, @@ -855,9 +813,7 @@ impl DLEQProof { .collect(); let challenge_hash = blh(&input); - // println!("{:?} is hash of input {:?}", challenge_hash, input); - BigInt::from_bytes_be(Sign::Plus, &challenge_hash.as_slice()) - % ORDER_BI.clone() + Fl::from_bigint(&BigInt::from_bytes_be(Sign::Plus, &challenge_hash.as_slice())) } } @@ -876,8 +832,8 @@ mod tests { let point_B = B8.mul_scalar(&b); let mut rng = rand_new::thread_rng(); - let x = rng.gen_biguint(512).to_bigint().unwrap() % ORDER_BI.clone(); - let proof = DLEQProof::new(x, point_A, point_B); + let x = Fl::from_bigint(&rng.gen_biguint(512).to_bigint().unwrap()); + let proof = DLEQProof::new(x, point_A, point_B).unwrap(); println!("proof: {:?}", proof.verify()); assert!(proof.verify()); } From 44630c26d341c4c0c237be71d9e4bdc5cbaae128 Mon Sep 17 00:00:00 2001 From: Nanak Nihal Khalsa Date: Sun, 1 Oct 2023 15:13:08 +0100 Subject: [PATCH 40/45] compatability with previous blake hash --- Cargo.toml | 13 +++++++------ src/lib.rs | 53 ++++++++++++++++++++++++----------------------------- 2 files changed, 31 insertions(+), 35 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index 6367269..fc39e57 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -12,12 +12,13 @@ readme = "README.md" ff = {package="ff_ce", version= "0.11", features = ["derive"]} rand_new = {package="rand", version="0.8.5"} rand = "0.4.6" +# rand = "0.8" num = "0.4" num-bigint = {version = "0.4", features = ["rand"]} num-traits = "0.2.8" -blake2 = "0.10.6" -# blake-hash = {version="0.4.0", optional=true} -# blake = {version="2.0.1", optional=true} +# blake2 = "0.10.6" +blake-hash = {version="0.4.0", optional=true} +blake = {version="2.0.1", optional=true} generic-array = "0.14" poseidon-rs = "0.0.8" arrayref = "0.3.5" @@ -34,6 +35,6 @@ hex = "0.4" name = "bench_babyjubjub" harness = false -# [features] -# default = ["blake-hash"] -# aarch64 = ["blake"] +[features] +default = ["blake-hash"] +aarch64 = ["blake"] diff --git a/src/lib.rs b/src/lib.rs index e7c64e2..9946f70 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -6,15 +6,12 @@ use rand::ThreadRng; use std::{iter::Sum, ops::{Neg, AddAssign}, fmt::Error}; use num::Num; use std::fmt; -// use serde::{Serialize, ser::SerializeSeq, Deserialize}; use serde::{Serialize, ser::SerializeStruct, de::Visitor, de::MapAccess, Deserialize, Deserializer}; -// use bytes::{BytesMut, BufMut}; use poseidon_rs::Poseidon; pub type Fr = poseidon_rs::Fr; // alias extern crate rand_new; extern crate rand; -// #[macro_use] extern crate ff; // Create a new primefield for the subgroup defined by the base point, order Fl: @@ -25,13 +22,12 @@ pub struct Fl(FpRepr); use arrayref::array_ref; -// #[cfg(not(feature = "aarch64"))] -// use blake_hash::Digest; // compatible version with Blake used at circomlib +#[cfg(not(feature = "aarch64"))] +use blake_hash::Digest; // compatible version with Blake used at circomlib + +#[cfg(feature = "aarch64")] +extern crate blake; // compatible version with Blake used at circomlib -// #[cfg(feature = "aarch64")] -// extern crate blake; // compatible version with Blake used at circomlib -use blake2::{Blake2b512, Digest}; -// use hex_literal::hex; use std::{cmp::min, str::FromStr}; use num_bigint::{BigInt, RandBigInt, Sign, ToBigInt}; @@ -502,30 +498,29 @@ pub fn decompress_point(bb: [u8; 32]) -> Result { Ok(Point { x: x_fr, y: y_fr }) } -// #[cfg(not(feature = "aarch64"))] -// fn blh(b: &[u8]) -> Vec { -// println!("hashing {:?} {:?}", b.len(), b); -// let debugggggggggme = blake_hash::Blake512::digest(b); -// println!("debugging {:?}", debugggggggggme); +#[cfg(not(feature = "aarch64"))] +fn blh(b: &[u8]) -> Vec { + // println!("hashing {:?} {:?}", b.len(), b); + // let debugggggggggme = blake_hash::Blake512::digest(b); + // println!("debugging {:?}", debugggggggggme); + let hash = blake_hash::Blake512::digest(b); + hash.to_vec() +} -// let hash = blake_hash::Blake512::digest(b); -// hash.to_vec() -// } +#[cfg(feature = "aarch64")] +fn blh(b: &[u8]) -> Vec { + let mut hash = [0; 64]; + blake::hash(512, b, &mut hash).unwrap(); + hash.to_vec() +} -// #[cfg(feature = "aarch64")] -// fn blh(b: &[u8]) -> Vec { -// let mut hash = [0; 64]; -// blake::hash(512, b, &mut hash).unwrap(); -// hash.to_vec() +// pub fn blh(b: &[u8]) -> Vec { +// let mut h = Blake2b512::new(); +// h.update(b); +// let digest = h.finalize(); +// return digest[..].to_vec(); // } -pub fn blh(b: &[u8]) -> Vec { - let mut h = Blake2b512::new(); - h.update(b); - let digest = h.finalize(); - return digest[..].to_vec(); -} - #[derive(Debug, Clone, Serialize)] pub struct Signature { pub r_b8: Point, From f1f653f6284c29b06a6f9dd2bb2e02760c4bede6 Mon Sep 17 00:00:00 2001 From: Nanak Nihal Khalsa Date: Sun, 1 Oct 2023 15:36:56 +0100 Subject: [PATCH 41/45] more ready for a PR --- src/lib.rs | 175 ++++++++++++++++++++--------------------------------- 1 file changed, 67 insertions(+), 108 deletions(-) diff --git a/src/lib.rs b/src/lib.rs index 9946f70..143ecbe 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -2,8 +2,7 @@ // For LICENSE check https://github.com/arnaucube/babyjubjub-rs use ff::*; -use rand::ThreadRng; -use std::{iter::Sum, ops::{Neg, AddAssign}, fmt::Error}; +use std::fmt::Error; use num::Num; use std::fmt; use serde::{Serialize, ser::SerializeStruct, de::Visitor, de::MapAccess, Deserialize, Deserializer}; @@ -93,15 +92,12 @@ lazy_static! { >> 3; pub static ref POSEIDON: poseidon_rs::Poseidon = Poseidon::new(); - // MAX_MSG is maximum message length that can be encoded into a point. This will be Q / KOBLITZ_NUMBER + // MAX_MSG is maximum message length that can be encoded into a point pub static ref MAX_MSG: BigInt = BigInt::parse_bytes( b"21888242871839275222246405745257275088548364400416034343698204186575808495617",10 ) .unwrap() >> 10; - // An arbitrary number for Koblitz method of encoding string to point. 1024 is convenient compared to original 1000 to do bitshifts instead of multiplications/divisions -// pub static ref KOBLITZ_NUMBER: Fr = Fr::from_str("1024").unwrap(); -// pub static ref KOBLITZ_NUMBER_INV: Fr = Fr::from_str("1024").unwrap().inverse().unwrap(); } @@ -227,28 +223,6 @@ impl FrBigIntConversion for Fl { } } -// pub struct FrWrapper { -// pub fr: Fr -// } -// impl FrWrapper { -// pub fn from_fr(fr: Fr) -> FrWrapper { -// FrWrapper { fr: fr } -// } -// } - -// impl Sum for FrWrapper { -// fn sum>(iter: I) -> Self { -// iter.reduce( -// |frw1,frw2| { -// let mut tmp = frw1.fr.clone(); -// tmp.add_assign(&frw2.fr); -// FrWrapper { fr: tmp } -// } - -// ).unwrap() -// } -// } - impl Serialize for Point { fn serialize(&self, serializer: S) -> Result where @@ -368,11 +342,11 @@ impl Point { false } - // Koblitz decoding method, adapted for this curve: - // message m must be < r/(2^10) //using 2^10=1024 instead of Koblitz' parameter of 1000, so arithmetic can happen easily mod 2. This shouldn't have any (negative) impact: https://crypto.stackexchange.com/questions/103132/encoding-a-message-as-points-on-elliptic-curve - // Try finding a point with y value m*1024+0, m*1024+1, .... m*1024+5617 (5617 are last four digits of prime r) - // There is an approximately 1/(2^1024) chance no point will be encodable, - // since each y value has probability of about 1/2 of being on the curve + /// Koblitz decoding method, adapted for this curve: + /// message m must be < r/(2^10) //using 2^10=1024 instead of Koblitz' parameter of 1000, so arithmetic can happen easily mod 2. This shouldn't have any (negative) impact: https://crypto.stackexchange.com/questions/103132/encoding-a-message-as-points-on-elliptic-curve + /// Try finding a point with y value m*1024+0, m*1024+1, .... m*1024+5617 (5617 are last four digits of prime r) + /// There is an approximately 1/(2^1024) chance no point will be encodable, + /// since each y value has probability of about 1/2 of being on the curve pub fn from_msg_vartime(msg: &BigInt) -> Option { #[allow(non_snake_case)] let ACC_UNDER = 1024; // Last four digits of prime r. MAX_MSG * 1024 + ACC_UNDER = r @@ -413,19 +387,16 @@ impl Point { x.add_assign(&one); } return None - // // Unwrap y since we can't be 100% sure at compile-time it will have been found; it may still be a None value! - // Point {x:x, y:y.unwrap()} - } - // Converts a point to a message by dividing by 1024 (a.k.a. right-shifting by 10) + /// Converts a point to a message by dividing its x by 1024 (a.k.a. right-shifting by 10) pub fn to_msg(&self) -> Fr { let mut msg = self.x.clone().into_repr(); msg.shr(10); Fr::from_repr(msg).unwrap() } - + /// Checks that a point is on the BabyJubJub curve. Does not check the point is in the correct subgroup. pub fn on_curve(&self) -> bool { let mut x2 = self.x.clone(); let mut y2 = self.y.clone(); @@ -444,7 +415,7 @@ impl Point { lhs.eq(&rhs) } - // This could be made more efficeint by using static ref to O + /// Checks that a point's order is equal to the subgroup order. Does not check the point is on the curve. pub fn in_subgroup(&self) -> bool { let should_be_zero = self.mul_scalar(&SUBORDER); should_be_zero.equals({ @@ -500,9 +471,6 @@ pub fn decompress_point(bb: [u8; 32]) -> Result { #[cfg(not(feature = "aarch64"))] fn blh(b: &[u8]) -> Vec { - // println!("hashing {:?} {:?}", b.len(), b); - // let debugggggggggme = blake_hash::Blake512::digest(b); - // println!("debugging {:?}", debugggggggggme); let hash = blake_hash::Blake512::digest(b); hash.to_vec() } @@ -514,24 +482,17 @@ fn blh(b: &[u8]) -> Vec { hash.to_vec() } -// pub fn blh(b: &[u8]) -> Vec { -// let mut h = Blake2b512::new(); -// h.update(b); -// let digest = h.finalize(); -// return digest[..].to_vec(); -// } - #[derive(Debug, Clone, Serialize)] pub struct Signature { pub r_b8: Point, - pub s: String, + pub s: BigInt, } impl Signature { pub fn compress(&self) -> [u8; 64] { let mut b: Vec = Vec::new(); b.append(&mut self.r_b8.compress().to_vec()); - let (_, s_bytes) = self.s.parse::().unwrap().to_bytes_le(); + let (_, s_bytes) = self.s.to_bytes_le(); let mut s_32bytes: [u8; 32] = [0; 32]; let len = min(s_bytes.len(), s_32bytes.len()); s_32bytes[..len].copy_from_slice(&s_bytes[..len]); @@ -548,7 +509,7 @@ pub fn decompress_signature(b: &[u8; 64]) -> Result { let r_b8 = decompress_point(r_b8_bytes); match r_b8 { Result::Err(err) => Err(err), - Result::Ok(res) => Ok(Signature { r_b8: res, s: s.to_string() }), + Result::Ok(res) => Ok(Signature { r_b8: res, s }), } } #[derive(Debug, Serialize, Deserialize, Clone)] @@ -558,7 +519,7 @@ pub struct ElGamalEncryption { } pub struct PrivateKey { - key: [u8; 32], + pub key: [u8; 32], } impl PrivateKey { @@ -622,13 +583,13 @@ impl PrivateKey { let hm_input = vec![r_b8.x, r_b8.y, a.x, a.y, msg_fr]; let hm = POSEIDON.hash(hm_input)?; - let mut s: BigInt = &self.scalar_key() << 3; + let mut s = &self.scalar_key() << 3; let hm_b = BigInt::parse_bytes(to_hex(&hm).as_bytes(), 16).unwrap(); s = hm_b * s; s = r + s; s %= &SUBORDER.clone(); - Ok(Signature { r_b8, s: s.to_string() }) + Ok(Signature { r_b8, s: s }) } #[allow(clippy::many_single_char_names)] @@ -718,7 +679,7 @@ pub fn verify(pk: Point, sig: Signature, msg: BigInt) -> bool { Result::Err(_) => return false, Result::Ok(hm) => hm, }; - let l = B8.mul_scalar(&sig.s.parse::().unwrap()); + let l = B8.mul_scalar(&sig.s); let hm_b = BigInt::parse_bytes(to_hex(&hm).as_bytes(), 16).unwrap(); let r = sig .r_b8 @@ -740,7 +701,6 @@ impl DLEQProof { pub fn new(x: Fl, point_A: Point, point_B: Point) -> Result { let x_bigint = x.to_bigint(); - // let modulus_overflowed = ORDER.clone(); // TODO: shouldn't this be the subgroup order? This is not order of Babyjubjub curve nor the subgroup; it's the order Babyjubjub is defined over let modulus = SUBORDER.clone(); // TODO: better error handling (not assert), make it more efficient too: assert!(x_bigint < modulus); @@ -1190,55 +1150,54 @@ mod tests { assert_eq!(true, verification); } - // Removed this because broke circom siganture compatability due to different blake hash function: - // #[test] - // fn test_circomlib_testvector() { - // let sk_raw_bytes = - // hex::decode("0001020304050607080900010203040506070809000102030405060708090001") - // .unwrap(); - - // // test blake compatible with circomlib implementation - // let h: Vec = blh(&sk_raw_bytes); - // assert_eq!(hex::encode(h), "c992db23d6290c70ffcc02f7abeb00b9d00fa8b43e55d7949c28ba6be7545d3253882a61bd004a236ef1cdba01b27ba0aedfb08eefdbfb7c19657c880b43ddf1"); - - // // test private key - // let sk = PrivateKey::import( - // hex::decode("0001020304050607080900010203040506070809000102030405060708090001") - // .unwrap(), - // ) - // .unwrap(); - // assert_eq!( - // sk.scalar_key().to_string(), - // "6466070937662820620902051049739362987537906109895538826186780010858059362905" - // ); - - // // test public key - // let pk = sk.public(); - // assert_eq!( - // pk.x.to_string(), - // "Fr(0x1d5ac1f31407018b7d413a4f52c8f74463b30e6ac2238220ad8b254de4eaa3a2)" - // ); - // assert_eq!( - // pk.y.to_string(), - // "Fr(0x1e1de8a908826c3f9ac2e0ceee929ecd0caf3b99b3ef24523aaab796a6f733c4)" - // ); - - // // test signature & verification - // let msg = BigInt::from_bytes_le(Sign::Plus, &hex::decode("00010203040506070809").unwrap()); - // let sig = sk.sign(msg.clone()).unwrap(); - // assert_eq!( - // sig.r_b8.x.to_string(), - // "Fr(0x192b4e51adf302c8139d356d0e08e2404b5ace440ef41fc78f5c4f2428df0765)" - // ); - // assert_eq!( - // sig.r_b8.y.to_string(), - // "Fr(0x2202bebcf57b820863e0acc88970b6ca7d987a0d513c2ddeb42e3f5d31b4eddf)" - // ); - // assert_eq!( - // sig.s.to_string(), - // "1672775540645840396591609181675628451599263765380031905495115170613215233181" - // ); - // let v = verify(pk, sig, msg); - // assert_eq!(v, true); - // } + #[test] + fn test_circomlib_testvector() { + let sk_raw_bytes = + hex::decode("0001020304050607080900010203040506070809000102030405060708090001") + .unwrap(); + + // test blake compatible with circomlib implementation + let h: Vec = blh(&sk_raw_bytes); + assert_eq!(hex::encode(h), "c992db23d6290c70ffcc02f7abeb00b9d00fa8b43e55d7949c28ba6be7545d3253882a61bd004a236ef1cdba01b27ba0aedfb08eefdbfb7c19657c880b43ddf1"); + + // test private key + let sk = PrivateKey::import( + hex::decode("0001020304050607080900010203040506070809000102030405060708090001") + .unwrap(), + ) + .unwrap(); + assert_eq!( + sk.scalar_key().to_string(), + "6466070937662820620902051049739362987537906109895538826186780010858059362905" + ); + + // test public key + let pk = sk.public(); + assert_eq!( + pk.x.to_string(), + "Fr(0x1d5ac1f31407018b7d413a4f52c8f74463b30e6ac2238220ad8b254de4eaa3a2)" + ); + assert_eq!( + pk.y.to_string(), + "Fr(0x1e1de8a908826c3f9ac2e0ceee929ecd0caf3b99b3ef24523aaab796a6f733c4)" + ); + + // test signature & verification + let msg = BigInt::from_bytes_le(Sign::Plus, &hex::decode("00010203040506070809").unwrap()); + let sig = sk.sign(msg.clone()).unwrap(); + assert_eq!( + sig.r_b8.x.to_string(), + "Fr(0x192b4e51adf302c8139d356d0e08e2404b5ace440ef41fc78f5c4f2428df0765)" + ); + assert_eq!( + sig.r_b8.y.to_string(), + "Fr(0x2202bebcf57b820863e0acc88970b6ca7d987a0d513c2ddeb42e3f5d31b4eddf)" + ); + assert_eq!( + sig.s.to_string(), + "1672775540645840396591609181675628451599263765380031905495115170613215233181" + ); + let v = verify(pk, sig, msg); + assert_eq!(v, true); + } } From f68d8fbddde635752d94437f7a5e0c212d4649da Mon Sep 17 00:00:00 2001 From: Nanak Nihal Khalsa Date: Sun, 1 Oct 2023 15:42:28 +0100 Subject: [PATCH 42/45] ready for PR --- Cargo.toml | 3 +-- src/lib.rs | 4 ---- 2 files changed, 1 insertion(+), 6 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index fc39e57..d4a594a 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -14,9 +14,8 @@ rand_new = {package="rand", version="0.8.5"} rand = "0.4.6" # rand = "0.8" num = "0.4" -num-bigint = {version = "0.4", features = ["rand"]} +num-bigint = {version = "0.4", features = ["rand", "serde"]} num-traits = "0.2.8" -# blake2 = "0.10.6" blake-hash = {version="0.4.0", optional=true} blake = {version="2.0.1", optional=true} generic-array = "0.14" diff --git a/src/lib.rs b/src/lib.rs index 143ecbe..558ea7d 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -205,7 +205,6 @@ pub trait FrBigIntConversion { impl FrBigIntConversion for Fr { // Note: this could probably be more efficient by converting bigint to raw repr to Fr fn from_bigint(bi: &BigInt) -> Fr { - println!("bi: {}", bi.to_string()); Fr::from_str(&bi.to_string()).unwrap() } fn to_bigint(&self) -> BigInt { @@ -705,7 +704,6 @@ impl DLEQProof { // TODO: better error handling (not assert), make it more efficient too: assert!(x_bigint < modulus); - // println!("modulus overflow? isn't it bigger than Fr's order: {:?}", modulus_overflowed); let k_bigint = rand_new::thread_rng().gen_biguint(512).to_bigint().unwrap() % &modulus; let k = Fl::from_bigint(&k_bigint); @@ -744,7 +742,6 @@ impl DLEQProof { ); let challenge = DLEQProof::get_challenge(&self.A, &self.B, &self.xA, &self.xB, &kA_, &kB_); - println!("got challenge: {:?} which should equal {:?}", challenge, self.challenge); return challenge == self.challenge; @@ -789,7 +786,6 @@ mod tests { let mut rng = rand_new::thread_rng(); let x = Fl::from_bigint(&rng.gen_biguint(512).to_bigint().unwrap()); let proof = DLEQProof::new(x, point_A, point_B).unwrap(); - println!("proof: {:?}", proof.verify()); assert!(proof.verify()); } #[test] From ab62f12e9c5ae23eaba3c34c4a01253f1342002f Mon Sep 17 00:00:00 2001 From: Nanak Nihal Khalsa Date: Sun, 1 Oct 2023 16:02:12 +0100 Subject: [PATCH 43/45] changed order to bigint --- src/lib.rs | 7 ++----- 1 file changed, 2 insertions(+), 5 deletions(-) diff --git a/src/lib.rs b/src/lib.rs index 8941423..1f34727 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -75,11 +75,8 @@ lazy_static! { y: Fr::one() }; - pub static ref ORDER: Fr = Fr::from_str( - "21888242871839275222246405745257275088614511777268538073601725287587578984328", - ).unwrap(); - - pub static ref ORDER_BI: BigInt = BigInt::parse_bytes( + // Order expressed as a bigint (it is larger than the r modulus of the Fr struct) + pub static ref ORDER: BigInt = BigInt::parse_bytes( b"21888242871839275222246405745257275088614511777268538073601725287587578984328", 10, ).unwrap(); From 42cf9e638cb845d5560dea4c99ab869de4f6585e Mon Sep 17 00:00:00 2001 From: Nanak Nihal Khalsa Date: Fri, 27 Oct 2023 20:20:44 +0100 Subject: [PATCH 44/45] borrow instead of move --- src/lib.rs | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/src/lib.rs b/src/lib.rs index 1f34727..bcd705a 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -331,7 +331,7 @@ impl Point { r } - pub fn equals(&self, p: Point) -> bool { + pub fn equals(&self, p: &Point) -> bool { if self.x == p.x && self.y == p.y { return true; } @@ -415,7 +415,7 @@ impl Point { pub fn in_subgroup(&self) -> bool { let should_be_zero = self.mul_scalar(&SUBORDER); should_be_zero.equals({ - Point { x: Fr::zero(), y: Fr::one() } + &O }) } @@ -607,7 +607,7 @@ impl PrivateKey { Ok((r, s)) } - pub fn decrypt_elgamal(&self, encrypted_point: ElGamalEncryption) -> Point { + pub fn decrypt_elgamal(&self, encrypted_point: &ElGamalEncryption) -> Point { // Make sure inputs aren't bad (i imagine this check could be skipped for performance reasons, but it seems a sanity check here would be helpful) assert!(encrypted_point.c1.on_curve(), "Error: C1 is not on the curve!"); assert!(encrypted_point.c1.in_subgroup(), "Error: C1 is not in the subgroup!"); @@ -654,7 +654,7 @@ pub fn verify_schnorr(pk: Point, m: BigInt, r: Point, s: BigInt) -> Result PrivateKey { @@ -680,7 +680,7 @@ pub fn verify(pk: Point, sig: Signature, msg: BigInt) -> bool { let r = sig .r_b8 .add(&pk.mul_scalar(&(8.to_bigint().unwrap() * hm_b))); - l.equals(r) + l.equals(&r) } @@ -856,7 +856,7 @@ mod tests { some_point_x_inverse.sub_assign(&some_point.x); // assert_eq!(some_point_x_inverse, some_point.x.inverse().unwrap()); assert!(some_point.equals( - some_point.add(&another_point).add( + &some_point.add(&another_point).add( &another_point.neg()) )); @@ -870,7 +870,7 @@ mod tests { &BigInt::parse_bytes(b"ABCDEF123456789", 16).unwrap(), &some_point ); - let some_point_encrypted_decrypted = some_privkey.decrypt_elgamal(some_point_encrypted); + let some_point_encrypted_decrypted = some_privkey.decrypt_elgamal(&some_point_encrypted); assert_eq!(some_point.x, some_point_encrypted_decrypted.x); assert_eq!(some_point.y, some_point_encrypted_decrypted.y); From 506468522460a60680c53605ac48059a3106f348 Mon Sep 17 00:00:00 2001 From: Nanak Nihal Khalsa Date: Wed, 27 Dec 2023 14:58:33 -0500 Subject: [PATCH 45/45] wasm blake hash compatability (but circom compatability broken in wasm still) --- Cargo.toml | 17 ++++++++++++----- src/lib.rs | 29 +++++++++++++++++++++++------ 2 files changed, 35 insertions(+), 11 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index d4a594a..e6d1362 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -16,8 +16,6 @@ rand = "0.4.6" num = "0.4" num-bigint = {version = "0.4", features = ["rand", "serde"]} num-traits = "0.2.8" -blake-hash = {version="0.4.0", optional=true} -blake = {version="2.0.1", optional=true} generic-array = "0.14" poseidon-rs = "0.0.8" arrayref = "0.3.5" @@ -34,6 +32,15 @@ hex = "0.4" name = "bench_babyjubjub" harness = false -[features] -default = ["blake-hash"] -aarch64 = ["blake"] +[target.'cfg(not(any( target_arch = "aarch64", target_arch = "wasm32" )))'.dependencies] +blake-hash = {version="0.4.1" } + +[target.'cfg(target_arch = "aarch64")'.dependencies] +blake = { version = "2.0.1" } + +[target.'cfg(target_arch = "wasm32")'.dependencies] +blake2 = { version = "0.10.6" } + +# [features] +# default = ["blake-hash"] +# aarch64 = ["blake"] diff --git a/src/lib.rs b/src/lib.rs index bcd705a..7c7f113 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -21,12 +21,17 @@ pub struct Fl(FpRepr); use arrayref::array_ref; -#[cfg(not(feature = "aarch64"))] +#[cfg(not(any( target_arch = "aarch64", target_arch = "wasm32" )))] use blake_hash::Digest; // compatible version with Blake used at circomlib -#[cfg(feature = "aarch64")] +#[cfg( target_arch = "aarch64" )] extern crate blake; // compatible version with Blake used at circomlib + +#[cfg( target_arch = "wasm32" )] +use blake2::{Blake2b512, Blake2s256, Digest}; // NOT compatible with circomlib but it works on WASM + + use std::{cmp::min, str::FromStr}; use num_bigint::{BigInt, RandBigInt, Sign, ToBigInt}; @@ -465,19 +470,29 @@ pub fn decompress_point(bb: [u8; 32]) -> Result { Ok(Point { x: x_fr, y: y_fr }) } -#[cfg(not(feature = "aarch64"))] -fn blh(b: &[u8]) -> Vec { +#[cfg(not(any( target_arch = "aarch64", target_arch = "wasm32" )))] +pub fn blh(b: &[u8]) -> Vec { let hash = blake_hash::Blake512::digest(b); hash.to_vec() } -#[cfg(feature = "aarch64")] -fn blh(b: &[u8]) -> Vec { +#[cfg(target_arch = "aarch64")] +pub fn blh(b: &[u8]) -> Vec { let mut hash = [0; 64]; blake::hash(512, b, &mut hash).unwrap(); hash.to_vec() } +#[cfg(target_arch = "wasm32")] +/// This is incompatible with the circom version +/// TODO: find a BLAKE-512 that works on WASM +pub fn blh(b: &[u8]) -> Vec { + let mut hasher = Blake2b512::new(); + hasher.update(b); + hasher.finalize().to_vec() +} +// #[cfg(target_arch = "wasm32")] + #[derive(Debug, Clone, Serialize)] pub struct Signature { pub r_b8: Point, @@ -549,6 +564,8 @@ impl PrivateKey { } pub fn public(&self) -> Point { + println!("calling public"); + println!("scalar key {}", &self.scalar_key()); B8.mul_scalar(&self.scalar_key()) }