From c7c37cc1284c88d8ab33271ba839d61523ac98b5 Mon Sep 17 00:00:00 2001 From: arnaucube Date: Mon, 10 Oct 2022 12:20:25 +0200 Subject: [PATCH] Abstract types to use arkworks generics --- Cargo.toml | 6 +- README.md | 4 +- src/lib.rs | 324 +++++++++++++++++++++++++++++++---------------------- 3 files changed, 196 insertions(+), 138 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index e4b5e09..5e433dd 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -2,8 +2,7 @@ name = "ark-ec-blind-signatures" version = "0.0.1" edition = "2021" - -# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html +repository = "https://github.com/aragonzkresearch/ark-ec-blind-signatures" [dependencies] ark-ff = { version = "^0.3.0", default-features = false } @@ -17,10 +16,11 @@ ark-crypto-primitives = { version = "^0.3.0", default-features = true, features # ark-sponge = { git = "https://github.com/arkworks-rs/sponge.git", rev = "41843d179dc4655869955297833d096d1962120f", default-features=true, features=["r1cs"] } arkworks-utils = { git = "https://github.com/webb-tools/arkworks-gadgets", name="arkworks-utils", features=["poseidon_bn254_x5_3"] } arkworks-native-gadgets = { git = "https://github.com/webb-tools/arkworks-gadgets", name="arkworks-native-gadgets"} +arkworks-r1cs-gadgets = { git = "https://github.com/webb-tools/arkworks-gadgets", name="arkworks-r1cs-gadgets"} ark-relations = { version = "^0.3.0", default-features = false } ark-snark = { version = "^0.3.0", default-features = false } ark-groth16 = { version = "^0.3.0" } tracing = { version = "0.1", default-features = false, features = [ "attributes" ] } tracing-subscriber = { version = "0.2" } lazy_static = "1.4.0" -rand = "0.8.4" +derivative = { version = "2.0", features = ["use_core"] } diff --git a/README.md b/README.md index 019b50e..1dad852 100644 --- a/README.md +++ b/README.md @@ -1,9 +1,9 @@ # ark-ec-blind-signatures Blind signatures over elliptic curve implementation (native & r1cs gadgets) using arkworks. -Blind signature over elliptic curves, based on *"[New Blind Signature Schemes Based on the (Elliptic Curve) Discrete Logarithm Problem](https://sci-hub.st/10.1109/iccke.2013.6682844)"* paper by Hamid Mala & Nafiseh Nezhadansari. +[Blind signature](https://en.wikipedia.org/wiki/Blind_signature) over elliptic curves, based on *"[New Blind Signature Schemes Based on the (Elliptic Curve) Discrete Logarithm Problem](https://sci-hub.st/10.1109/iccke.2013.6682844)"* paper by Hamid Mala & Nafiseh Nezhadansari. > Warning: experimental code, do not use in production. -Target: Groth16 over Bn254 (for Ethereum), so the curve used for the blind signatures is ed-on-bn254 ([BabyJubJub](https://github.com/barryWhiteHat/baby_jubjub)). +Target: Groth16 over Bn254 (for Ethereum), ed-on-bn254 ([BabyJubJub](https://github.com/barryWhiteHat/baby_jubjub)) for the signatures. diff --git a/src/lib.rs b/src/lib.rs index f5446fa..30e2939 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -1,18 +1,22 @@ -#[allow(non_snake_case)] -#[allow(clippy::many_single_char_names)] +#![allow(non_snake_case)] +#![allow(clippy::many_single_char_names)] -// pub type ConstraintF = ark_bn254::Fr; -// pub type ConstraintF = ark_ed_on_bn254::Fq; // base field -pub type ConstraintF = ark_ed_on_bn254::Fr; // scalar field +// #[cfg(feature="r1cs")] +pub mod constraints; + +use ark_ec::{ + models::twisted_edwards_extended::GroupAffine, AffineCurve, ProjectiveCurve, TEModelParameters, +}; -use ark_ec::{AffineCurve, ProjectiveCurve, TEModelParameters}; -use ark_ed_on_bn254::{EdwardsAffine, EdwardsParameters, EdwardsProjective, FqParameters, Fr}; use ark_ff::{ - to_bytes, BigInteger, BigInteger256, Field, Fp256, FpParameters, One, PrimeField, Zero, + to_bytes, BigInteger, BigInteger256, Field, Fp256, FpParameters, FromBytes, One, PrimeField, + Zero, }; +use ark_std::marker::PhantomData; use ark_std::rand::{CryptoRng, RngCore}; -use ark_std::UniformRand; +use ark_std::{ops::Mul, rand::Rng, UniformRand}; +use derivative::Derivative; // hash use arkworks_native_gadgets::poseidon; @@ -21,155 +25,200 @@ use arkworks_utils::{ bytes_matrix_to_f, bytes_vec_to_f, parse_vec, poseidon_params::setup_poseidon_params, Curve, }; -const GX: Fp256 = ::AFFINE_GENERATOR_COEFFS.0; -const GY: Fp256 = ::AFFINE_GENERATOR_COEFFS.1; - -#[macro_use] -extern crate lazy_static; - -lazy_static! { - static ref G_AFFINE: EdwardsAffine = EdwardsAffine::new(GX, GY); - static ref G: EdwardsProjective = G_AFFINE.into_projective(); -} - -// Fr modulus (bigendian) -const FR_MODULUS: BigInteger256 = BigInteger256::new([ - 0x677297DC392126F1, - 0xAB3EEDB83920EE0A, - 0x370A08B6D0302B0B, - 0x060C89CE5C263405, -]); - -// poseidon -pub fn poseidon_setup_params( - curve: Curve, - exp: i8, - width: u8, -) -> poseidon::PoseidonParameters { - let pos_data = setup_poseidon_params(curve, exp, width).unwrap(); - - let mds_f = bytes_matrix_to_f(&pos_data.mds); - let rounds_f = bytes_vec_to_f(&pos_data.rounds); +// WIP +use ark_ed_on_bn254::{ + EdwardsAffine, EdwardsParameters, EdwardsProjective, FqParameters, Fr, FrParameters, +}; - poseidon::PoseidonParameters { - mds_matrix: mds_f, - round_keys: rounds_f, - full_rounds: pos_data.full_rounds, - partial_rounds: pos_data.partial_rounds, - sbox: poseidon::sbox::PoseidonSbox(pos_data.exp), - width: pos_data.width, - } -} +pub type SecretKey = ::ScalarField; +pub type PublicKey = ::Affine; +pub type BlindedSignature = ::ScalarField; -pub struct PrivateKey(ConstraintF); -pub type PublicKey = EdwardsAffine; -pub type BlindedSignature = ConstraintF; -pub struct Signature { - s: ConstraintF, - r: EdwardsAffine, +// #[derive(Derivative)] +#[derive(Clone, Default, Debug)] +pub struct Signature { + s: C::ScalarField, // ScalarField == Fr + r: ::Affine, } #[derive(Debug)] -pub struct UserSecretData { - a: ConstraintF, - b: ConstraintF, - r: EdwardsAffine, +pub struct UserSecretData { + a: C::ScalarField, + b: C::ScalarField, + r: C::Affine, } -impl UserSecretData { - fn new_empty() -> Self { +impl UserSecretData { + fn new_empty(parameters: &Parameters) -> Self { UserSecretData { - a: ConstraintF::from(0), - b: ConstraintF::from(0), - r: G_AFFINE.clone(), // WIP + a: C::ScalarField::from(0 as u32), + b: C::ScalarField::from(0 as u32), + r: parameters.generator, // WIP } } } -pub fn new_sk(rng: &mut R) -> PrivateKey { - let sk: PrivateKey = PrivateKey(ConstraintF::rand(rng)); - sk +#[derive(Derivative)] +#[derivative(Clone(bound = "C: ProjectiveCurve"), Debug)] +pub struct Parameters { + pub generator: C::Affine, } -impl PrivateKey { - pub fn public(&self) -> PublicKey { - let pk: PublicKey = G.mul(self.0.into_repr()).into_affine(); - pk +pub struct BlindSigScheme { + _group: PhantomData, +} + +impl BlindSigScheme +where + C::ScalarField: PrimeField, + GroupAffine: From<::Affine>, // WIP +{ + pub fn setup() -> Parameters { + let generator = C::prime_subgroup_generator().into(); + Parameters { generator } } - pub fn blind_sign(&self, m_blinded: ConstraintF, k: ConstraintF) -> BlindedSignature { - self.0 * m_blinded + k + + // signer + pub fn keygen(parameters: &Parameters, rng: &mut R) -> (PublicKey, SecretKey) { + let secret_key = C::ScalarField::rand(rng); + let public_key = parameters.generator.mul(secret_key).into(); + (public_key, secret_key) } -} -pub fn new_request_params(rng: &mut R) -> (ConstraintF, EdwardsAffine) { - let k = ConstraintF::rand(rng); - let R = G.mul(k.into_repr()).into_affine(); - (k, R) -} + pub fn new_request_params( + parameters: &Parameters, + rng: &mut R, + ) -> (C::ScalarField, C::Affine) { + let k = C::ScalarField::rand(rng); + let R = parameters.generator.mul(k).into(); + (k, R) + } -fn new_blind_params(rng: &mut R, signer_r: EdwardsAffine) -> UserSecretData { - let mut u: UserSecretData = UserSecretData::new_empty(); - u.a = ConstraintF::rand(rng); - u.b = ConstraintF::rand(rng); + pub fn blind_sign( + sk: SecretKey, + k: C::ScalarField, + m_blinded: C::ScalarField, + ) -> BlindedSignature { + sk * m_blinded + k + } - // R = aR' + bG - let aR = signer_r.mul(u.a.into_repr()); - let bG = G.mul(u.b.into_repr()); - u.r = aR.into_affine() + bG.into_affine(); + // requester + pub fn new_blind_params( + parameters: &Parameters, + rng: &mut R, + signer_r: C::Affine, + ) -> UserSecretData + where + ::ScalarField: From, + { + let mut u: UserSecretData = UserSecretData::new_empty(parameters); + u.a = C::ScalarField::rand(rng); + u.b = C::ScalarField::rand(rng); + + // R = aR' + bG + let aR = signer_r.mul(u.a.into_repr()); + let bG = parameters.generator.mul(u.b.into_repr()); + u.r = aR.into_affine() + bG.into_affine(); + + let r = EdwardsAffine::from(u.r); // WIP + let one = BigInteger256::from(1u64); + let x_repr = r.x.into_repr(); + let modulus = <::Params as FpParameters>::MODULUS; + let modulus_repr = BigInteger256::try_from(modulus.into()).unwrap(); + + if !(x_repr >= one && x_repr < modulus_repr) { + return Self::new_blind_params(parameters, rng, signer_r); + } + u + } - // check that u.r.x can be safely converted into Fr, and if not, choose other u.a & u.b values - let x_repr = u.r.x.into_repr(); - if !(x_repr >= ConstraintF::one().into_repr() && x_repr < FR_MODULUS) { - return new_blind_params(rng, signer_r); + pub fn blind( + parameters: &Parameters, + rng: &mut R, + poseidon_hash: &poseidon::Poseidon, + m: C::ScalarField, + signer_r: C::Affine, + ) -> Result<(C::ScalarField, UserSecretData), ark_crypto_primitives::Error> + where + ::ScalarField: Mul>, + ::ScalarField: + From<<::ScalarField as Mul>>::Output>, + ::ScalarField: From, + { + let u = Self::new_blind_params(parameters, rng, signer_r); + // get X coordinate, as in new_blind_params we already checked that R.x is inside Fr and + // will not give None + let r = EdwardsAffine::from(u.r); // WIP + let x_fr = C::ScalarField::from(r.x.into_repr()); + + // m' = a^-1 rx h(m) + let h_m = poseidon_hash.hash(&[m])?; + let m_blinded = C::ScalarField::from(u.a.inverse().unwrap() * x_fr) * h_m; + + Ok((m_blinded, u)) } - return u; -} -pub fn blind( - rng: &mut R, - poseidon_hash: &poseidon::Poseidon, - m: ConstraintF, - signer_r: EdwardsAffine, -) -> Result<(ConstraintF, UserSecretData), ark_crypto_primitives::Error> { - let u = new_blind_params(rng, signer_r); - // use unwrap, as we already checked that R.x is inside Fr and will not give None - let x_fr = ConstraintF::from_repr(u.r.x.into_repr()).unwrap(); - - // m' = a^-1 rx h(m) - let h_m = poseidon_hash.hash(&[m])?; - let m_blinded = u.a.inverse().unwrap() * x_fr * h_m; - - Ok((m_blinded, u)) -} + pub fn unblind(s_blinded: C::ScalarField, u: UserSecretData) -> Signature { + // s = a s' + b + let s = u.a * s_blinded + u.b; + Signature { s, r: u.r } + } + + pub fn verify( + parameters: &Parameters, + poseidon_hash: &poseidon::Poseidon, + m: C::ScalarField, + s: Signature, + q: PublicKey, + ) -> bool + where + ::ScalarField: From, + { + let sG = parameters.generator.mul(s.s.into_repr()); + + let h_m = poseidon_hash.hash(&[m]).unwrap(); + + // check that s.R.x is in Fr + let r = EdwardsAffine::from(s.r); // WIP + let one = BigInteger256::from(1u64); + let x_repr = r.x.into_repr(); + let modulus = <::Params as FpParameters>::MODULUS; + let modulus_repr = BigInteger256::try_from(modulus.into()).unwrap(); + if !(x_repr >= one && x_repr < modulus_repr) { + return false; + } + // get s.R.x + let x_fr = C::ScalarField::from(r.x.into_repr()); + let right = s.r + q.mul((x_fr * h_m).into_repr()).into_affine(); -pub fn unblind(s_blinded: ConstraintF, u: UserSecretData) -> Signature { - // s = a s' + b - let s = u.a * s_blinded + u.b; - Signature { s, r: u.r } + sG.into_affine() == right + } } -pub fn verify( - poseidon_hash: &poseidon::Poseidon, - m: ConstraintF, - s: Signature, - q: PublicKey, -) -> bool { - let sG = G.mul(s.s.into_repr()); +// poseidon +pub fn poseidon_setup_params( + curve: Curve, + exp: i8, + width: u8, +) -> poseidon::PoseidonParameters { + let pos_data = setup_poseidon_params(curve, exp, width).unwrap(); - let h_m = poseidon_hash.hash(&[m]).unwrap(); + let mds_f = bytes_matrix_to_f(&pos_data.mds); + let rounds_f = bytes_vec_to_f(&pos_data.rounds); - let x_repr = s.r.x.into_repr(); - if !(x_repr >= ConstraintF::one().into_repr() && x_repr < FR_MODULUS) { - return false; // error, s.r.x does not fit in Fr + poseidon::PoseidonParameters { + mds_matrix: mds_f, + round_keys: rounds_f, + full_rounds: pos_data.full_rounds, + partial_rounds: pos_data.partial_rounds, + sbox: poseidon::sbox::PoseidonSbox(pos_data.exp), + width: pos_data.width, } - let x_fr = ConstraintF::from_repr(s.r.x.into_repr()).unwrap(); - let right = s.r + q.mul((x_fr * h_m).into_repr()).into_affine(); - - sG.into_affine() == right } #[cfg(test)] mod tests { use super::*; + pub type ConstraintF = ark_ed_on_bn254::Fr; // scalar field #[test] fn test_blind() { @@ -178,19 +227,28 @@ mod tests { let mut rng = ark_std::test_rng(); - let sk = new_sk(&mut rng); - let pk = sk.public(); + let params = BlindSigScheme::::setup(); + let (pk, sk) = BlindSigScheme::::keygen(¶ms, &mut rng); - let (k, signer_r) = new_request_params(&mut rng); + let (k, signer_r) = + BlindSigScheme::::new_request_params(¶ms, &mut rng); let m = ConstraintF::from(1234); - let (m_blinded, u) = blind(&mut rng, &poseidon_hash, m, signer_r).unwrap(); + let (m_blinded, u) = BlindSigScheme::::blind( + ¶ms, + &mut rng, + &poseidon_hash, + m, + signer_r, + ) + .unwrap(); - let s_blinded = sk.blind_sign(m_blinded, k); + let s_blinded = BlindSigScheme::::blind_sign(sk, k, m_blinded); - let s = unblind(s_blinded, u); + let s = BlindSigScheme::::unblind(s_blinded, u); - let verified = verify(&poseidon_hash, m, s, pk); + let verified = + BlindSigScheme::::verify(¶ms, &poseidon_hash, m, s, pk); assert!(verified); } }