From da166a81f02bb07ce3c039f76186eb0b4d6269c8 Mon Sep 17 00:00:00 2001 From: Davide Galassi Date: Tue, 28 May 2024 15:30:29 +0200 Subject: [PATCH] Bandersnatch Edwards suite. TE2SW and SW2TE --- src/ietf.rs | 12 ++- src/lib.rs | 10 +- src/pedersen.rs | 6 +- src/ring.rs | 34 ------ src/suites/bandersnatch.rs | 210 ++++++++++++++++++++++++++----------- src/testing.rs | 120 +++++++++++++++++++++ src/utils.rs | 71 ++++++++----- 7 files changed, 335 insertions(+), 128 deletions(-) create mode 100644 src/testing.rs diff --git a/src/ietf.rs b/src/ietf.rs index ca9c824..a91e2fc 100644 --- a/src/ietf.rs +++ b/src/ietf.rs @@ -20,7 +20,7 @@ pub struct Proof { pub s: ScalarField, } -impl CanonicalSerialize for Proof { +impl CanonicalSerialize for Proof { fn serialize_with_mode( &self, mut writer: W, @@ -41,7 +41,7 @@ impl CanonicalSerialize for Proof { } } -impl CanonicalDeserialize for Proof { +impl CanonicalDeserialize for Proof { fn deserialize_with_mode( mut reader: R, _compress_always: ark_serialize::Compress, @@ -61,7 +61,7 @@ impl CanonicalDeserialize for Proof { } } -impl ark_serialize::Valid for Proof { +impl ark_serialize::Valid for Proof { fn check(&self) -> Result<(), ark_serialize::SerializationError> { self.c.check()?; self.s.check()?; @@ -184,8 +184,10 @@ pub mod testing { #[cfg(test)] mod tests { use super::*; - use crate::utils::testing::{ - random_val, AffinePoint, Input, ScalarField, Secret, TestSuite, TEST_SEED, + use crate::testing::{ + random_val, + suite::{AffinePoint, Input, ScalarField, Secret, TestSuite}, + TEST_SEED, }; #[test] diff --git a/src/lib.rs b/src/lib.rs index 8870de3..feb6946 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -31,6 +31,9 @@ pub mod utils; #[cfg(feature = "ring")] pub mod ring; +#[cfg(test)] +mod testing; + pub mod prelude { pub use ark_ec; pub use ark_ff; @@ -60,6 +63,7 @@ pub const CUSTOM_SUITE_ID_FLAG: u8 = 0x80; /// Can be easily customized to implement more exotic VRF types by overwriting /// the default methods implementations. pub trait Suite: Copy + Clone { + // TODO: make this a byte array /// Suite identifier (aka `suite_string` in RFC-9381) const SUITE_ID: u8; @@ -258,7 +262,11 @@ impl Output { #[cfg(test)] mod tests { - use crate::utils::testing::*; + use crate::testing::{ + random_val, + suite::{Input, Public, Secret}, + TEST_SEED, + }; use ark_serialize::{CanonicalDeserialize, CanonicalSerialize}; #[test] diff --git a/src/pedersen.rs b/src/pedersen.rs index f2680ba..fc11f9e 100644 --- a/src/pedersen.rs +++ b/src/pedersen.rs @@ -116,8 +116,10 @@ impl PedersenVerifier for Public { #[cfg(test)] mod tests { use super::*; - use crate::utils::testing::{ - random_val, AffinePoint, BaseField, Input, Secret, TestSuite, TEST_SEED, + use crate::testing::{ + random_val, + suite::{AffinePoint, BaseField, Input, Secret, TestSuite}, + TEST_SEED, }; use ark_ff::MontFp; diff --git a/src/ring.rs b/src/ring.rs index 353e977..e0081d4 100644 --- a/src/ring.rs +++ b/src/ring.rs @@ -273,37 +273,3 @@ where self.pcs_params.check() } } - -#[cfg(test)] -mod tests { - use super::*; - use crate::suites::bandersnatch::{ring::RingContext, AffinePoint, Input, Secret}; - use crate::utils::testing::{random_val, random_vec, TEST_SEED}; - - #[test] - fn prove_verify_works() { - let rng = &mut ark_std::test_rng(); - let domain_size = 1024; - let ring_ctx = RingContext::new_random(domain_size, rng); - - let secret = Secret::from_seed(TEST_SEED); - let public = secret.public(); - let input = Input::from(random_val(Some(rng))); - let output = secret.output(input); - - let keyset_size = ring_ctx.piop_params.keyset_part_size; - - let prover_idx = 3; - let mut pks = random_vec::(keyset_size, Some(rng)); - pks[prover_idx] = public.0; - - let prover_key = ring_ctx.prover_key(pks.clone()); - let prover = ring_ctx.prover(prover_key, prover_idx); - let proof = secret.prove(input, output, b"foo", &prover); - - let verifier_key = ring_ctx.verifier_key(pks); - let verifier = ring_ctx.verifier(verifier_key); - let result = Public::verify(input, output, b"foo", &proof, &verifier); - assert!(result.is_ok()); - } -} diff --git a/src/suites/bandersnatch.rs b/src/suites/bandersnatch.rs index f559228..0876069 100644 --- a/src/suites/bandersnatch.rs +++ b/src/suites/bandersnatch.rs @@ -51,86 +51,178 @@ // GENERATOR_X = 18886178867200960497001835917649091219057080094937609519140440539760939937304 // GENERATOR_Y = 19188667384257783945677642223292697773471335439753913231509108946878080696678 +use crate::{pedersen::PedersenSuite, utils::arkworks_pending::*, *}; use ark_ff::MontFp; -use crate::pedersen::PedersenSuite; -use crate::*; +pub mod weierstrass { + use super::*; -#[derive(Debug, Copy, Clone)] -pub struct BandersnatchSha512; + #[derive(Debug, Copy, Clone)] + pub struct BandersnatchSha512; -suite_types!(BandersnatchSha512); + suite_types!(BandersnatchSha512); -impl Suite for BandersnatchSha512 { - const SUITE_ID: u8 = CUSTOM_SUITE_ID_FLAG | 0x03; - const CHALLENGE_LEN: usize = 32; + impl Suite for BandersnatchSha512 { + const SUITE_ID: u8 = CUSTOM_SUITE_ID_FLAG | 0x03; + const CHALLENGE_LEN: usize = 32; - type Affine = ark_ed_on_bls12_381_bandersnatch::SWAffine; - type Hasher = sha2::Sha512; -} + type Affine = ark_ed_on_bls12_381_bandersnatch::SWAffine; + type Hasher = sha2::Sha512; + } + + impl PedersenSuite for BandersnatchSha512 { + const BLINDING_BASE: AffinePoint = { + const X: BaseField = MontFp!( + "4956610287995045830459834427365747411162584416641336688940534788579455781570" + ); + const Y: BaseField = MontFp!( + "52360910621642801549936840538960627498114783432181489929217988668068368626761" + ); + AffinePoint::new_unchecked(X, Y) + }; + } -impl PedersenSuite for BandersnatchSha512 { - const BLINDING_BASE: AffinePoint = { - const X: BaseField = - MontFp!("4956610287995045830459834427365747411162584416641336688940534788579455781570"); - const Y: BaseField = MontFp!( - "52360910621642801549936840538960627498114783432181489929217988668068368626761" + #[cfg(feature = "ring")] + pub mod ring { + use super::*; + use crate::ring; + + impl ring::Pairing for ark_bls12_381::Bls12_381 {} + + impl ring::RingSuite for BandersnatchSha512 { + type Config = ark_ed_on_bls12_381_bandersnatch::SWConfig; + type Pairing = ark_bls12_381::Bls12_381; + + /// A point on the curve not belonging to the prime order subgroup. + /// + /// Found using `ring_proof::find_complement_point::()` function. + const COMPLEMENT_POINT: AffinePoint = { + const X: BaseField = MontFp!("0"); + const Y: BaseField = MontFp!( + "11982629110561008531870698410380659621661946968466267969586599013782997959645" + ); + AffinePoint::new_unchecked(X, Y) + }; + } + + pub type RingContext = ring::RingContext; + pub type VerifierKey = ring::VerifierKey; + pub type Prover = ring::Prover; + pub type Verifier = ring::Verifier; + pub type Proof = ring::Proof; + } + + // sage: q = 52435875175126190479447740508185965837690552500527637822603658699938581184513 + // sage: Fq = GF(q) + // sage: MONT_A = 29978822694968839326280996386011761570173833766074948509196803838190355340952 + // sage: MONT_B = 25465760566081946422412445027709227188579564747101592991722834452325077642517 + // sage: MONT_A/Fq(3) = 9992940898322946442093665462003920523391277922024982836398934612730118446984 + // sage: Fq(1)/MONT_B = 41180284393978236561320365279764246793818536543197771097409483252169927600582 + impl MapConfig for ark_ed_on_bls12_381_bandersnatch::BandersnatchConfig { + const MONT_A_OVER_THREE: ark_ed_on_bls12_381_bandersnatch::Fq = + MontFp!("9992940898322946442093665462003920523391277922024982836398934612730118446984"); + const MONT_B_INV: ark_ed_on_bls12_381_bandersnatch::Fq = MontFp!( + "41180284393978236561320365279764246793818536543197771097409483252169927600582" ); - AffinePoint::new_unchecked(X, Y) - }; + } + + #[cfg(test)] + mod test { + use super::*; + + // TODO: use macro to build all tests + #[test] + fn ietf_prove_verify() { + testing::ietf_prove_verify::(); + } + + #[test] + fn prove_verify_pedersen() { + testing::pedersen_prove_verify::(); + } + + #[cfg(feature = "ring")] + #[test] + fn ring_prove_verify() { + testing::ring_prove_verify::() + } + + #[test] + fn sw_to_te_roundtrip() { + use ark_ed_on_bls12_381_bandersnatch::BandersnatchConfig; + + let org_point = ::BLINDING_BASE; + + let te_point = map_sw_to_te::(&org_point).unwrap(); + // assert!(te_point.is_on_curve()); + + let sw_point = map_te_to_sw::(&te_point).unwrap(); + // assert!(sw_point.is_on_curve()); + + println!("{:?}", org_point); + println!("{:?}", te_point); + println!("{:?}", sw_point); + } + + #[test] + fn sw_to_te_roundtrip() { + use ark_ed_on_bls12_381_bandersnatch::BandersnatchConfig; + + let org_point = ::BLINDING_BASE; + + let te_point = map_sw_to_te::(&org_point).unwrap(); + // assert!(te_point.is_on_curve()); + + let sw_point = map_te_to_sw::(&te_point).unwrap(); + // assert!(sw_point.is_on_curve()); + + println!("{:?}", org_point); + println!("{:?}", te_point); + println!("{:?}", sw_point); + } + } } -#[cfg(feature = "ring")] -pub mod ring { +pub mod edwards { use super::*; - use crate::ring; - impl ring::Pairing for ark_bls12_381::Bls12_381 {} + #[derive(Debug, Copy, Clone)] + pub struct BandersnatchSha512Edwards; - impl ring::RingSuite for BandersnatchSha512 { - type Config = ark_ed_on_bls12_381_bandersnatch::SWConfig; - type Pairing = ark_bls12_381::Bls12_381; + suite_types!(BandersnatchSha512Edwards); - /// A point on the curve not belonging to the prime order subgroup. - /// - /// Found using `ring_proof::find_complement_point::()` function. - const COMPLEMENT_POINT: AffinePoint = { - const X: BaseField = MontFp!("0"); + impl Suite for BandersnatchSha512Edwards { + const SUITE_ID: u8 = CUSTOM_SUITE_ID_FLAG | 0x04; + const CHALLENGE_LEN: usize = 32; + + type Affine = ark_ed_on_bls12_381_bandersnatch::EdwardsAffine; + type Hasher = sha2::Sha512; + } + + impl PedersenSuite for BandersnatchSha512Edwards { + const BLINDING_BASE: AffinePoint = { + const X: BaseField = MontFp!( + "14576224270591906826192118712803723445031237947873156025406837473427562701854" + ); const Y: BaseField = MontFp!( - "11982629110561008531870698410380659621661946968466267969586599013782997959645" + "38436873314098705092845609371301773715650206984323659492499960072785679638442" ); AffinePoint::new_unchecked(X, Y) }; } - pub type RingContext = ring::RingContext; - pub type VerifierKey = ring::VerifierKey; - pub type Prover = ring::Prover; - pub type Verifier = ring::Verifier; - pub type Proof = ring::Proof; -} - -#[cfg(test)] -mod test { - use super::*; - use utils::testing::{random_val, TEST_SEED}; + #[cfg(test)] + mod test { + use super::*; - #[test] - fn prove_verify_works() { - use crate::pedersen::{PedersenProver, PedersenVerifier}; + #[test] + fn ietf_prove_verify() { + testing::ietf_prove_verify::(); + } - let secret = Secret::from_seed(TEST_SEED); - let input = Input::from(random_val(None)); - let output = secret.output(input); - - let (proof, blinding) = secret.prove(input, output, b"foo"); - - let result = Public::verify(input, output, b"foo", &proof); - assert!(result.is_ok()); - - assert_eq!( - proof.key_commitment(), - secret.public().0 + BandersnatchSha512::BLINDING_BASE * blinding - ); + #[test] + fn prove_verify_pedersen() { + testing::pedersen_prove_verify::(); + } } } diff --git a/src/testing.rs b/src/testing.rs new file mode 100644 index 0000000..4427fce --- /dev/null +++ b/src/testing.rs @@ -0,0 +1,120 @@ +#[cfg(not(feature = "std"))] +use ark_std::{vec, vec::Vec}; + +use crate::*; +use ark_std::{rand::RngCore, UniformRand}; + +pub const TEST_SEED: &[u8] = b"seed"; + +pub(crate) mod suite { + use super::*; + + #[derive(Debug, Copy, Clone, PartialEq)] + pub struct TestSuite; + + impl Suite for TestSuite { + const SUITE_ID: u8 = 0xFF; + const CHALLENGE_LEN: usize = 16; + + type Affine = ark_ed25519::EdwardsAffine; + type Hasher = sha2::Sha256; + } + + suite_types!(TestSuite); +} + +#[allow(unused)] +pub fn random_vec(n: usize, rng: Option<&mut dyn RngCore>) -> Vec { + let mut local_rng = ark_std::test_rng(); + let rng = rng.unwrap_or(&mut local_rng); + (0..n).map(|_| T::rand(rng)).collect() +} + +#[allow(unused)] +pub fn random_val(rng: Option<&mut dyn RngCore>) -> T { + let mut local_rng = ark_std::test_rng(); + let rng = rng.unwrap_or(&mut local_rng); + T::rand(rng) +} + +pub fn ietf_prove_verify() { + use crate::ietf::{IetfProver, IetfVerifier}; + + let secret = Secret::::from_seed(TEST_SEED); + let public = secret.public(); + let input = Input::from(random_val(None)); + let output = secret.output(input); + + let mut buf = Vec::new(); + public.serialize_compressed(&mut buf).unwrap(); + println!("{}", buf.len()); + + let proof = secret.prove(input, output, b"foo"); + let mut buf = Vec::new(); + proof.serialize_compressed(&mut buf).unwrap(); + println!("{}", buf.len()); + + let result = public.verify(input, output, b"foo", &proof); + assert!(result.is_ok()); +} + +pub fn pedersen_prove_verify() { + use crate::pedersen::{PedersenProver, PedersenVerifier}; + + let secret = Secret::::from_seed(TEST_SEED); + let input = Input::from(random_val(None)); + let output = secret.output(input); + + let (proof, blinding) = secret.prove(input, output, b"foo"); + let mut buf = Vec::new(); + secret.public().serialize_compressed(&mut buf).unwrap(); + println!("{}", buf.len()); + + let mut buf = Vec::new(); + proof.serialize_compressed(&mut buf).unwrap(); + println!("{}", buf.len()); + + let result = Public::verify(input, output, b"foo", &proof); + assert!(result.is_ok()); + + assert_eq!( + proof.key_commitment(), + (secret.public().0 + S::BLINDING_BASE * blinding).into() + ); +} + +#[cfg(feature = "ring")] +pub fn ring_prove_verify() +where + S::Config: ark_ec::short_weierstrass::SWCurveConfig + Clone, + ::BaseField: ark_ff::PrimeField, +{ + use crate::ring::{RingContext, RingProver, RingVerifier}; + + let rng = &mut ark_std::test_rng(); + let domain_size = 1024; + let ring_ctx = RingContext::::new_random(domain_size, rng); + + let secret = Secret::::from_seed(TEST_SEED); + let public = secret.public(); + let input = Input::from(random_val(Some(rng))); + let output = secret.output(input); + + let keyset_size = ring_ctx.piop_params.keyset_part_size; + + let prover_idx = 3; + let mut pks = random_vec::>(keyset_size, Some(rng)); + pks[prover_idx] = public.0; + + let prover_key = ring_ctx.prover_key(pks.clone()); + let prover = ring_ctx.prover(prover_key, prover_idx); + let proof = secret.prove(input, output, b"foo", &prover); + let mut buf = Vec::new(); + proof.serialize_compressed(&mut buf).unwrap(); + println!("RING PROOF LEN: {}", buf.len()); + + let verifier_key = ring_ctx.verifier_key(pks); + let verifier = ring_ctx.verifier(verifier_key); + let result = Public::verify(input, output, b"foo", &proof, &verifier); + assert!(result.is_ok()); +} diff --git a/src/utils.rs b/src/utils.rs index 5225056..badc75f 100644 --- a/src/utils.rs +++ b/src/utils.rs @@ -200,46 +200,63 @@ pub fn encode_scalar(sc: &ScalarField) -> Vec { buf } -#[cfg(test)] -pub(crate) mod testing { - use super::*; - use crate::*; - use ark_std::{rand::RngCore, UniformRand}; - - pub const TEST_SEED: &[u8] = b"seed"; - - #[derive(Debug, Copy, Clone, PartialEq)] - pub struct TestSuite; - - impl Suite for TestSuite { - const SUITE_ID: u8 = 0xFF; - const CHALLENGE_LEN: usize = 16; +// Pending Arkworks features. +pub(crate) mod arkworks_pending { + use ark_ec::{ + short_weierstrass::{Affine as WeierstrassAffine, SWCurveConfig}, + twisted_edwards::{Affine as EdwardsAffine, MontCurveConfig, TECurveConfig}, + CurveConfig, + }; + use ark_ff::{Field, One}; - type Affine = ark_ed25519::EdwardsAffine; - type Hasher = sha2::Sha256; + // Constants used in mapping TE form to SW form and vice versa + pub trait MapConfig: TECurveConfig + SWCurveConfig + MontCurveConfig { + const MONT_A_OVER_THREE: ::BaseField; + const MONT_B_INV: ::BaseField; } - suite_types!(TestSuite); - + // https://github.com/arkworks-rs/algebra/pull/804 #[allow(unused)] - pub fn random_vec(n: usize, rng: Option<&mut dyn RngCore>) -> Vec { - let mut local_rng = ark_std::test_rng(); - let rng = rng.unwrap_or(&mut local_rng); - (0..n).map(|_| T::rand(rng)).collect() + pub fn map_sw_to_te(point: &WeierstrassAffine) -> Option> { + // First map the point from SW to Montgomery + // (Bx - A/3, By) + let mx = ::COEFF_B * point.x - C::MONT_A_OVER_THREE; + let my = ::COEFF_B * point.y; + + // Then we map the TE point to Montgamory + // (x,y)↦(x/y,(x−1)/(x+1)) + let v_denom = my.inverse()?; + let x_p_1 = mx + <::BaseField as One>::one(); + let w_denom = x_p_1.inverse()?; + let v = mx * v_denom; + let w = (mx - <::BaseField as One>::one()) * w_denom; + + Some(EdwardsAffine::new_unchecked(v, w)) } #[allow(unused)] - pub fn random_val(rng: Option<&mut dyn RngCore>) -> T { - let mut local_rng = ark_std::test_rng(); - let rng = rng.unwrap_or(&mut local_rng); - T::rand(rng) + pub fn map_te_to_sw(point: &EdwardsAffine) -> Option> { + // Map from TE to Montgomery: (1+y)/(1-y), (1+y)/(x(1-y)) + let v_denom = <::BaseField as One>::one(); + let w_denom = point.x - point.x * point.y; + let v_denom_inv = v_denom.inverse()?; + let w_denom_inv = w_denom.inverse()?; + let v_w_num = <::BaseField as One>::one() + point.y; + let v = v_w_num * v_denom_inv; + let w = v_w_num * w_denom_inv; + + // Mapp Montgamory to SW: ((x+A/3)/B,y/B) + let x = C::MONT_B_INV * (v + C::MONT_A_OVER_THREE); + let y = C::MONT_B_INV * w; + + Some(WeierstrassAffine::new_unchecked(x, y)) } } #[cfg(test)] mod tests { use super::*; - use testing::TestSuite; + use crate::testing::suite::TestSuite; #[test] fn hash_to_curve_tai_works() {