From 129795aa4cb76a2cde1fa96544258c359c9252ce Mon Sep 17 00:00:00 2001 From: zhenfei Date: Tue, 29 Jun 2021 21:04:08 -0400 Subject: [PATCH] Implement the Bandersnatch curve (#64) * impl bandersnatch * clean up * update changelog * Relocate the readme so they show up in the doc * Delete README.md * Relocate the changelog entry * rename & fmt Co-authored-by: Weikeng Chen --- CHANGELOG.md | 2 + Cargo.toml | 1 + README.md | 1 + ed_on_bls12_381/src/lib.rs | 2 +- ed_on_bls12_381_bandersnatch/Cargo.toml | 34 ++ .../src/constraints/curves.rs | 12 + .../src/constraints/fields.rs | 9 + .../src/constraints/mod.rs | 107 +++++ .../src/curves/mod.rs | 94 ++++ .../src/curves/tests.rs | 103 +++++ ed_on_bls12_381_bandersnatch/src/fields/fq.rs | 1 + ed_on_bls12_381_bandersnatch/src/fields/fr.rs | 115 +++++ .../src/fields/mod.rs | 8 + .../src/fields/tests.rs | 423 ++++++++++++++++++ ed_on_bls12_381_bandersnatch/src/lib.rs | 37 ++ 15 files changed, 948 insertions(+), 1 deletion(-) create mode 100644 ed_on_bls12_381_bandersnatch/Cargo.toml create mode 100644 ed_on_bls12_381_bandersnatch/src/constraints/curves.rs create mode 100644 ed_on_bls12_381_bandersnatch/src/constraints/fields.rs create mode 100644 ed_on_bls12_381_bandersnatch/src/constraints/mod.rs create mode 100644 ed_on_bls12_381_bandersnatch/src/curves/mod.rs create mode 100644 ed_on_bls12_381_bandersnatch/src/curves/tests.rs create mode 100644 ed_on_bls12_381_bandersnatch/src/fields/fq.rs create mode 100644 ed_on_bls12_381_bandersnatch/src/fields/fr.rs create mode 100644 ed_on_bls12_381_bandersnatch/src/fields/mod.rs create mode 100644 ed_on_bls12_381_bandersnatch/src/fields/tests.rs create mode 100644 ed_on_bls12_381_bandersnatch/src/lib.rs diff --git a/CHANGELOG.md b/CHANGELOG.md index 0acc9f1..24ca94a 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -19,6 +19,8 @@ ### Features +- [\#64](https://github.com/arkworks-rs/curves/pull/64) Implement the Bandersnatch curve, another twisted Edwards curve for BLS12-381. + ### Improvements ### Bug fixes diff --git a/Cargo.toml b/Cargo.toml index e3c83d6..ba7ae62 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -15,6 +15,7 @@ members = [ "bls12_381", "ed_on_bls12_381", + "ed_on_bls12_381_bandersnatch", "bn254", "ed_on_bn254", diff --git a/README.md b/README.md index 4f760ba..e0b2995 100644 --- a/README.md +++ b/README.md @@ -5,6 +5,7 @@ This repository contains implementations of some popular elliptic curves. The cu ### BLS12-381 and embedded curves * [`ark-bls12-381`](bls12_381): Implements the BLS12-381 pairing-friendly curve * [`ark-ed-on-bls12-381`](ed_on_bls12_381): Implements a Twisted Edwards curve atop the scalar field of BLS12-381 +* [`ark-ed-on-bls12-381-bandersnatch`](ed_on_bls12_381_bandersnatch): Implements Bandersnatch, another Twisted Edwards curve atop the scalar field of BLS12-381 ### BLS12-377 and related curves * [`ark-bls12-377`](bls12_377): Implements the BLS12-377 pairing-friendly curve diff --git a/ed_on_bls12_381/src/lib.rs b/ed_on_bls12_381/src/lib.rs index 3b429ba..aa6e78c 100644 --- a/ed_on_bls12_381/src/lib.rs +++ b/ed_on_bls12_381/src/lib.rs @@ -9,7 +9,7 @@ #![forbid(unsafe_code)] //! This library implements a twisted Edwards curve whose base field is the scalar field of the -//! curve BLS12-377. This allows defining cryptographic primitives that use elliptic curves over +//! curve BLS12-381. This allows defining cryptographic primitives that use elliptic curves over //! the scalar field of the latter curve. This curve was generated by Sean Bowe, and is also known //! as [Jubjub](https://github.com/zkcrypto/jubjub). //! diff --git a/ed_on_bls12_381_bandersnatch/Cargo.toml b/ed_on_bls12_381_bandersnatch/Cargo.toml new file mode 100644 index 0000000..af4c9e3 --- /dev/null +++ b/ed_on_bls12_381_bandersnatch/Cargo.toml @@ -0,0 +1,34 @@ +[package] +name = "ark-ed-on-bls12-381-bandersnatch" +version = "0.1.0" +authors = [ "zhenfei zhang", "arkworks contributors" ] +description = "Bandersnatch: a curve defined over the scalar field of the BLS12-381 curve" +repository = "https://github.com/zhenfeizhang/bandersnatch-rust" +keywords = ["cryptography", "finite-fields", "elliptic-curves" ] +categories = ["cryptography"] +include = ["Cargo.toml", "src", "README.md", "LICENSE-APACHE", "LICENSE-MIT"] +license = "MIT/Apache-2.0" +edition = "2018" + +[dependencies] +ark-ff = { version = "^0.3.0", default-features = false } +ark-ec = { version = "^0.3.0", default-features = false } +ark-std = { version = "^0.3.0", default-features = false } +ark-r1cs-std = { version = "^0.3.0", default-features = false, optional = true } +ark-bls12-381 = { version = "^0.3.0", default-features = false, features = [ "scalar_field" ] } + +[dev-dependencies] +ark-relations = { version = "^0.3.0", default-features = false } +ark-serialize = { version = "^0.3.0", default-features = false } +ark-algebra-test-templates = { version = "^0.3.0", default-features = false } +ark-curve-constraint-tests = { path = "../curve-constraint-tests", default-features = false } + +[features] +default = [] +std = [ + "ark-std/std", + "ark-ff/std", + "ark-ec/std", + "ark-bls12-381/std" +] +r1cs = ["ark-r1cs-std"] \ No newline at end of file diff --git a/ed_on_bls12_381_bandersnatch/src/constraints/curves.rs b/ed_on_bls12_381_bandersnatch/src/constraints/curves.rs new file mode 100644 index 0000000..9c9f783 --- /dev/null +++ b/ed_on_bls12_381_bandersnatch/src/constraints/curves.rs @@ -0,0 +1,12 @@ +use crate::*; +use ark_r1cs_std::groups::curves::twisted_edwards::AffineVar; + +use crate::constraints::FqVar; + +/// A variable that is the R1CS equivalent of `crate::EdwardsAffine`. +pub type EdwardsVar = AffineVar; + +#[test] +fn test() { + ark_curve_constraint_tests::curves::te_test::<_, EdwardsVar>().unwrap(); +} diff --git a/ed_on_bls12_381_bandersnatch/src/constraints/fields.rs b/ed_on_bls12_381_bandersnatch/src/constraints/fields.rs new file mode 100644 index 0000000..3f81d7a --- /dev/null +++ b/ed_on_bls12_381_bandersnatch/src/constraints/fields.rs @@ -0,0 +1,9 @@ +use ark_r1cs_std::fields::fp::FpVar; + +/// A variable that is the R1CS equivalent of `crate::Fq`. +pub type FqVar = FpVar; + +#[test] +fn test() { + ark_curve_constraint_tests::fields::field_test::<_, _, FqVar>().unwrap(); +} diff --git a/ed_on_bls12_381_bandersnatch/src/constraints/mod.rs b/ed_on_bls12_381_bandersnatch/src/constraints/mod.rs new file mode 100644 index 0000000..d107fa4 --- /dev/null +++ b/ed_on_bls12_381_bandersnatch/src/constraints/mod.rs @@ -0,0 +1,107 @@ +//! This module implements the R1CS equivalent of `ark_bandersnatch`. +//! +//! It implements field variables for `crate::Fq`, +//! and group variables for `crate::GroupProjective`. +//! +//! The field underlying these constraints is `crate::Fq`. +//! +//! # Examples +//! +//! One can perform standard algebraic operations on `FqVar`: +//! +//! ``` +//! # fn main() -> Result<(), ark_relations::r1cs::SynthesisError> { +//! use ark_std::UniformRand; +//! use ark_relations::r1cs::*; +//! use ark_r1cs_std::prelude::*; +//! use ark_ed_on_bls12_381_bandersnatch::{*, constraints::*}; +//! +//! let cs = ConstraintSystem::::new_ref(); +//! // This rng is just for test purposes; do not use it +//! // in real applications. +//! let mut rng = ark_std::test_rng(); +//! +//! // Generate some random `Fq` elements. +//! let a_native = Fq::rand(&mut rng); +//! let b_native = Fq::rand(&mut rng); +//! +//! // Allocate `a_native` and `b_native` as witness variables in `cs`. +//! let a = FqVar::new_witness(ark_relations::ns!(cs, "generate_a"), || Ok(a_native))?; +//! let b = FqVar::new_witness(ark_relations::ns!(cs, "generate_b"), || Ok(b_native))?; +//! +//! // Allocate `a_native` and `b_native` as constants in `cs`. This does not add any +//! // constraints or variables. +//! let a_const = FqVar::new_constant(ark_relations::ns!(cs, "a_as_constant"), a_native)?; +//! let b_const = FqVar::new_constant(ark_relations::ns!(cs, "b_as_constant"), b_native)?; +//! +//! let one = FqVar::one(); +//! let zero = FqVar::zero(); +//! +//! // Sanity check one + one = two +//! let two = &one + &one + &zero; +//! two.enforce_equal(&one.double()?)?; +//! +//! assert!(cs.is_satisfied()?); +//! +//! // Check that the value of &a + &b is correct. +//! assert_eq!((&a + &b).value()?, a_native + &b_native); +//! +//! // Check that the value of &a * &b is correct. +//! assert_eq!((&a * &b).value()?, a_native * &b_native); +//! +//! // Check that operations on variables and constants are equivalent. +//! (&a + &b).enforce_equal(&(&a_const + &b_const))?; +//! assert!(cs.is_satisfied()?); +//! # Ok(()) +//! # } +//! ``` +//! +//! One can also perform standard algebraic operations on `EdwardsVar`: +//! +//! ``` +//! # fn main() -> Result<(), ark_relations::r1cs::SynthesisError> { +//! # use ark_std::UniformRand; +//! # use ark_relations::r1cs::*; +//! # use ark_r1cs_std::prelude::*; +//! # use ark_ed_on_bls12_381_bandersnatch::{*, constraints::*}; +//! +//! # let cs = ConstraintSystem::::new_ref(); +//! # let mut rng = ark_std::test_rng(); +//! +//! // Generate some random `Edwards` elements. +//! let a_native = EdwardsProjective::rand(&mut rng); +//! let b_native = EdwardsProjective::rand(&mut rng); +//! +//! // Allocate `a_native` and `b_native` as witness variables in `cs`. +//! let a = EdwardsVar::new_witness(ark_relations::ns!(cs, "a"), || Ok(a_native))?; +//! let b = EdwardsVar::new_witness(ark_relations::ns!(cs, "b"), || Ok(b_native))?; +//! +//! // Allocate `a_native` and `b_native` as constants in `cs`. This does not add any +//! // constraints or variables. +//! let a_const = EdwardsVar::new_constant(ark_relations::ns!(cs, "a_as_constant"), a_native)?; +//! let b_const = EdwardsVar::new_constant(ark_relations::ns!(cs, "b_as_constant"), b_native)?; +//! +//! // This returns the identity of `Edwards`. +//! let zero = EdwardsVar::zero(); +//! +//! // Sanity check one + one = two +//! let two_a = &a + &a + &zero; +//! two_a.enforce_equal(&a.double()?)?; +//! +//! assert!(cs.is_satisfied()?); +//! +//! // Check that the value of &a + &b is correct. +//! assert_eq!((&a + &b).value()?, a_native + &b_native); +//! +//! // Check that operations on variables and constants are equivalent. +//! (&a + &b).enforce_equal(&(&a_const + &b_const))?; +//! assert!(cs.is_satisfied()?); +//! # Ok(()) +//! # } +//! ``` + +mod curves; +mod fields; + +pub use curves::*; +pub use fields::*; diff --git a/ed_on_bls12_381_bandersnatch/src/curves/mod.rs b/ed_on_bls12_381_bandersnatch/src/curves/mod.rs new file mode 100644 index 0000000..cb70cca --- /dev/null +++ b/ed_on_bls12_381_bandersnatch/src/curves/mod.rs @@ -0,0 +1,94 @@ +use crate::{Fq, Fr}; +use ark_ec::{ + models::{ModelParameters, MontgomeryModelParameters, TEModelParameters}, + twisted_edwards_extended::{GroupAffine, GroupProjective}, +}; +use ark_ff::{field_new, Field}; +#[cfg(test)] +mod tests; + +pub type EdwardsAffine = GroupAffine; +pub type EdwardsProjective = GroupProjective; + +/// `banersnatch` is a twisted Edwards curve. These curves have equations of the +/// form: ax² + y² = 1 - dx²y². +/// over some base finite field Fq. +/// +/// banersnatch's curve equation: -5x² + y² = 1 - dx²y² +/// +/// q = 52435875175126190479447740508185965837690552500527637822603658699938581184513. +/// +/// a = 52435875175126190479447740508185965837690552500527637822603658699938581184508. +/// d = (138827208126141220649022263972958607803/ +/// 171449701953573178309673572579671231137) mod q +/// = 45022363124591815672509500913686876175488063829319466900776701791074614335719. +/// +/// Sage script to calculate these: +/// +/// ```text +/// q = 52435875175126190479447740508185965837690552500527637822603658699938581184513 +/// Fq = GF(q) +/// d = (Fq(138827208126141220649022263972958607803)/Fq(171449701953573178309673572579671231137)) +/// ``` +/// These parameters and the sage script obtained from: +/// +#[derive(Clone, Default, PartialEq, Eq)] +pub struct EdwardsParameters; + +impl ModelParameters for EdwardsParameters { + type BaseField = Fq; + type ScalarField = Fr; +} + +impl TEModelParameters for EdwardsParameters { + /// COEFF_A = -1 + #[rustfmt::skip] + const COEFF_A: Fq = field_new!(Fq, "-5"); + + /// COEFF_D = (138827208126141220649022263972958607803/ + /// 171449701953573178309673572579671231137) mod q + #[rustfmt::skip] + const COEFF_D: Fq = field_new!(Fq, "45022363124591815672509500913686876175488063829319466900776701791074614335719"); + + /// COFACTOR = 4 + const COFACTOR: &'static [u64] = &[4]; + + /// COFACTOR^(-1) mod r = + /// 9831726595336160714896451345284868594481866920080427688839802480047265754601 + #[rustfmt::skip] + const COFACTOR_INV: Fr = field_new!(Fr, "9831726595336160714896451345284868594481866920080427688839802480047265754601"); + + /// AFFINE_GENERATOR_COEFFS = (GENERATOR_X, GENERATOR_Y) + const AFFINE_GENERATOR_COEFFS: (Self::BaseField, Self::BaseField) = (GENERATOR_X, GENERATOR_Y); + + type MontgomeryModelParameters = EdwardsParameters; + + /// Multiplication by `a` is multiply by `-5`. + #[inline(always)] + fn mul_by_a(elem: &Self::BaseField) -> Self::BaseField { + let t = (*elem).double().double(); + -(t + *elem) + } +} + +impl MontgomeryModelParameters for EdwardsParameters { + /// COEFF_A = 29978822694968839326280996386011761570173833766074948509196803838190355340952 + #[rustfmt::skip] + const COEFF_A: Fq = field_new!(Fq, "29978822694968839326280996386011761570173833766074948509196803838190355340952"); + /// COEFF_B = 25465760566081946422412445027709227188579564747101592991722834452325077642517 + #[rustfmt::skip] + const COEFF_B: Fq = field_new!(Fq, "25465760566081946422412445027709227188579564747101592991722834452325077642517"); + + type TEModelParameters = EdwardsParameters; +} + +// using the generator from bench.py (in affine form) +// P = BandersnatchPoint( +// 13738737789055671334382939318077718462576533426798874551591468520593954805549, +// 11575885077368931610486103676191793534029821920164915325066801506752632626968, +// 14458123306641001284399433086015669988340559992755622870694102351476334505845, +// C) +#[rustfmt::skip] +const GENERATOR_X: Fq = field_new!(Fq, "29627151942733444043031429156003786749302466371339015363120350521834195802525"); +#[rustfmt::skip] +const GENERATOR_Y: Fq = field_new!(Fq, "27488387519748396681411951718153463804682561779047093991696427532072116857978"); diff --git a/ed_on_bls12_381_bandersnatch/src/curves/tests.rs b/ed_on_bls12_381_bandersnatch/src/curves/tests.rs new file mode 100644 index 0000000..92ee746 --- /dev/null +++ b/ed_on_bls12_381_bandersnatch/src/curves/tests.rs @@ -0,0 +1,103 @@ +use crate::*; +use ark_algebra_test_templates::{curves::*, groups::*}; +use ark_ec::{AffineCurve, ProjectiveCurve}; +use ark_ff::{bytes::FromBytes, Zero}; +use ark_std::{rand::Rng, str::FromStr, test_rng}; + +#[test] +fn test_projective_curve() { + curve_tests::(); + + edwards_tests::(); +} + +#[test] +fn test_projective_group() { + let mut rng = test_rng(); + let a = rng.gen(); + let b = rng.gen(); + for _i in 0..100 { + group_test::(a, b); + } +} + +#[test] +fn test_affine_group() { + let mut rng = test_rng(); + let a: EdwardsAffine = rng.gen(); + let b: EdwardsAffine = rng.gen(); + for _i in 0..100 { + group_test::(a, b); + } +} + +#[test] +fn test_generator() { + let generator = EdwardsAffine::prime_subgroup_generator(); + assert!(generator.is_on_curve()); + assert!(generator.is_in_correct_subgroup_assuming_on_curve()); +} + +#[test] +fn test_conversion() { + let mut rng = test_rng(); + let a: EdwardsAffine = rng.gen(); + let b: EdwardsAffine = rng.gen(); + let a_b = { + use ark_ec::group::Group; + (a + &b).double().double() + }; + let a_b2 = (a.into_projective() + &b.into_projective()) + .double() + .double(); + assert_eq!(a_b, a_b2.into_affine()); + assert_eq!(a_b.into_projective(), a_b2); +} + +#[test] +fn test_scalar_multiplication() { + let f1 = Fr::from_str( + "4257185345094557079734489188109952172285839137338142340240392707284963971010", + ) + .unwrap(); + let f2 = Fr::from_str( + "1617998875791656082457755819308421023664764572929977389209373068350490665160", + ) + .unwrap(); + + let g = EdwardsAffine::from_str( + "(29627151942733444043031429156003786749302466371339015363120350521834195802525, \ + 27488387519748396681411951718153463804682561779047093991696427532072116857978)", + ) + .unwrap(); + let f1f2g = EdwardsAffine::from_str( + "(16530491029447613915334753043669938793793987372416328257719459807614119987301, \ + 42481140308370805476764840229335460092474682686441442216596889726548353970772)", + ) + .unwrap(); + + assert!(!g.is_zero()); + assert!(!f1f2g.is_zero()); + + let f1g = g.mul(f1).into_affine(); + assert_eq!(g.mul(f1 * &f2).into_affine(), f1f2g); + assert_eq!(f1g.mul(f2).into_affine(), f1f2g); +} + +#[test] +fn test_bytes() { + let g_from_repr = EdwardsAffine::from_str( + "(29627151942733444043031429156003786749302466371339015363120350521834195802525, \ + 27488387519748396681411951718153463804682561779047093991696427532072116857978)", + ) + .unwrap(); + + let g_bytes = ark_ff::to_bytes![g_from_repr].unwrap(); + let g = EdwardsAffine::read(g_bytes.as_slice()).unwrap(); + assert_eq!(g_from_repr, g); +} + +#[test] +fn test_montgomery_conversion() { + montgomery_conversion_test::(); +} diff --git a/ed_on_bls12_381_bandersnatch/src/fields/fq.rs b/ed_on_bls12_381_bandersnatch/src/fields/fq.rs new file mode 100644 index 0000000..46c052b --- /dev/null +++ b/ed_on_bls12_381_bandersnatch/src/fields/fq.rs @@ -0,0 +1 @@ +pub use ark_bls12_381::{Fr as Fq, FrParameters as FqParameters}; diff --git a/ed_on_bls12_381_bandersnatch/src/fields/fr.rs b/ed_on_bls12_381_bandersnatch/src/fields/fr.rs new file mode 100644 index 0000000..83c2b4c --- /dev/null +++ b/ed_on_bls12_381_bandersnatch/src/fields/fr.rs @@ -0,0 +1,115 @@ +use ark_ff::{ + biginteger::BigInteger256 as BigInteger, + fields::{FftParameters, Fp256, Fp256Parameters, FpParameters}, +}; + +pub type Fr = Fp256; + +pub struct FrParameters; + +impl Fp256Parameters for FrParameters {} +impl FftParameters for FrParameters { + type BigInt = BigInteger; + + /// Let `N` be the size of the multiplicative group defined by the field. + /// Then `TWO_ADICITY` is the two-adicity of `N`, i.e. the integer `s` + /// such that `N = 2^s * t` for some odd integer `t`. + const TWO_ADICITY: u32 = 5; + + /// 2^s root of unity computed by GENERATOR^t + /// 4740934665446857387895054948191089665295030226009829406950782728666658007874 + #[rustfmt::skip] + const TWO_ADIC_ROOT_OF_UNITY: BigInteger = BigInteger([ + 0xa4dcdba087826b42, + 0x6e4ab162f57f862a, + 0xabc5492749348d6a, + 0xa7b462035f8c169, + ]); +} +impl FpParameters for FrParameters { + /// The modulus of the field. + /// MODULUS = 13108968793781547619861935127046491459309155893440570251786403306729687672801. + #[rustfmt::skip] + const MODULUS: BigInteger = BigInteger([ + 0x74fd06b52876e7e1, + 0xff8f870074190471, + 0x0cce760202687600, + 0x1cfb69d4ca675f52, + ]); + + /// The number of bits needed to represent the `Self::MODULUS`. + const MODULUS_BITS: u32 = 253; + + /// The number of bits that can be reliably stored. + /// (Should equal `SELF::MODULUS_BITS - 1`) + const CAPACITY: u32 = Self::MODULUS_BITS - 1; + + /// The number of bits that must be shaved from the beginning of + /// the representation when randomly sampling. + const REPR_SHAVE_BITS: u32 = 4; + + /// Let `M` be the power of 2^64 nearest to `Self::MODULUS_BITS`. Then + /// `R = M % Self::MODULUS`. + /// R = 10920338887063814464675503992315976178796737518116002025166357554075628257528 + #[rustfmt::skip] + const R: BigInteger = BigInteger([ + 0x5817ca56bc48c0f8, + 0x0383c7fc5f37dc74, + 0x998c4fefecbc4ff8, + 0x1824b159acc5056f, + ]); + + /// R2 = R^2 % Self::MODULUS + /// R2 = 4932290691328759802879919559207542894238895193980447506221046538067943049163 + #[rustfmt::skip] + const R2: BigInteger = BigInteger([ + 0xdbb4f5d658db47cb, + 0x40fa7ca27fecb938, + 0xaa9e6daec0055cea, + 0xae793ddb14aec7d + ]); + + /// INV = -MODULUS^{-1} mod 2^64 + /// INV = 17410672245482742751 + const INV: u64 = 0xf19f22295cc063df; + + /// A multiplicative generator of the field. + /// `Self::GENERATOR` is an element having multiplicative order + /// `Self::MODULUS - 1`. + /// n = 9962557815892774795293348142308860067333132192265356416788884706064406244838 + #[rustfmt::skip] + const GENERATOR: BigInteger = BigInteger([ + 0x56b6f3ab7b616de6, + 0x114f419d6c9083e5, + 0xbf518d217780c4b9, + 0x16069b9f45dbce7f, + ]); + + /// (Self::MODULUS - 1) / 2 + /// 6554484396890773809930967563523245729654577946720285125893201653364843836400 + const MODULUS_MINUS_ONE_DIV_TWO: BigInteger = BigInteger([ + 0xba7e835a943b73f0, + 0x7fc7c3803a0c8238, + 0x06673b0101343b00, + 0xe7db4ea6533afa9, + ]); + + /// t for 2^s * t = MODULUS - 1, and t coprime to 2. + /// t = 409655274805673363120685472720202858103411121670017820368325103335302739775 + /// = (modulus-1)/2^5 + const T: BigInteger = BigInteger([ + 0x8ba7e835a943b73f, + 0x07fc7c3803a0c823, + 0x906673b0101343b0, + 0xe7db4ea6533afa, + ]); + + /// (t - 1) / 2 + /// = 204827637402836681560342736360101429051705560835008910184162551667651369887 + const T_MINUS_ONE_DIV_TWO: BigInteger = BigInteger([ + 0xc5d3f41ad4a1db9f, + 0x03fe3e1c01d06411, + 0x483339d80809a1d8, + 0x73eda753299d7d, + ]); +} diff --git a/ed_on_bls12_381_bandersnatch/src/fields/mod.rs b/ed_on_bls12_381_bandersnatch/src/fields/mod.rs new file mode 100644 index 0000000..63d081e --- /dev/null +++ b/ed_on_bls12_381_bandersnatch/src/fields/mod.rs @@ -0,0 +1,8 @@ +pub mod fq; +pub mod fr; + +pub use fq::*; +pub use fr::*; + +#[cfg(all(feature = "ed_on_bls12_381_bandersnatch", test))] +mod tests; diff --git a/ed_on_bls12_381_bandersnatch/src/fields/tests.rs b/ed_on_bls12_381_bandersnatch/src/fields/tests.rs new file mode 100644 index 0000000..b971721 --- /dev/null +++ b/ed_on_bls12_381_bandersnatch/src/fields/tests.rs @@ -0,0 +1,423 @@ +use crate::{Fq, Fr}; +use ark_algebra_test_templates::fields::*; +use ark_ff::{ + biginteger::BigInteger256 as BigInteger, + bytes::{FromBytes, ToBytes}, + fields::{Field, LegendreSymbol::*, SquareRootField}, + One, Zero, +}; +use ark_std::{rand::Rng, str::FromStr, test_rng}; + +#[test] +fn test_fr() { + let mut rng = test_rng(); + let a: Fr = rng.gen(); + let b: Fr = rng.gen(); + field_test(a, b); + primefield_test::(); +} + +#[test] +fn test_fq() { + let mut rng = test_rng(); + let a: Fq = rng.gen(); + let b: Fq = rng.gen(); + field_test(a, b); + primefield_test::(); +} + +#[test] +fn test_fq_add() { + let f1 = Fq::from_str( + "18386742314266644595564329008376577163854043021652781768352795308532764650733", + ) + .unwrap(); + let f2 = Fq::from_str( + "39786307610986038981023499868190793548353538256264351797285876981647142458383", + ) + .unwrap(); + let f3 = Fq::from_str( + "5737174750126493097140088368381404874517028777389495743035013590241325924603", + ) + .unwrap(); + assert!(!f1.is_zero()); + assert!(!f2.is_zero()); + assert!(!f3.is_zero()); + assert_eq!(f1 + &f2, f3); +} + +#[test] +fn test_fq_add_one() { + let f1 = Fq::from_str( + "4946875394261337176810256604189376311946643975348516311606738923340201185904", + ) + .unwrap(); + let f2 = Fq::from_str( + "4946875394261337176810256604189376311946643975348516311606738923340201185905", + ) + .unwrap(); + assert!(!f1.is_zero()); + assert!(!f2.is_zero()); + assert_eq!(f1 + &Fq::one(), f2); +} + +#[test] +fn test_fq_mul() { + let f1 = Fq::from_str( + "24703123148064348394273033316595937198355721297494556079070134653139656190956", + ) + .unwrap(); + let f2 = Fq::from_str( + "38196797080882758914424853878212529985425118523754343117256179679117054302131", + ) + .unwrap(); + let f3 = Fq::from_str( + "38057113854472161555556064369220825628027487067886761874351491955834635348140", + ) + .unwrap(); + assert!(!f1.is_zero()); + assert!(!f2.is_zero()); + assert!(!f3.is_zero()); + assert_eq!(f1 * &f2, f3); +} + +#[test] +fn test_fq_triple_mul() { + let f1 = Fq::from_str( + "23834398828139479510988224171342199299644042568628082836691700490363123893905", + ) + .unwrap(); + let f2 = Fq::from_str( + "48343809612844640454129919255697536258606705076971130519928764925719046689317", + ) + .unwrap(); + let f3 = Fq::from_str( + "22704845471524346880579660022678666462201713488283356385810726260959369106033", + ) + .unwrap(); + let f4 = Fq::from_str( + "18897508522635316277030308074760673440128491438505204942623624791502972539393", + ) + .unwrap(); + assert!(!f1.is_zero()); + assert!(!f2.is_zero()); + assert!(!f3.is_zero()); + assert_eq!(f1 * &f2 * &f3, f4); +} + +#[test] +fn test_fq_div() { + let f1 = Fq::from_str( + "31892744363926593013886463524057935370302352424137349660481695792871889573091", + ) + .unwrap(); + let f2 = Fq::from_str( + "47695868328933459965610498875668250916462767196500056002116961816137113470902", + ) + .unwrap(); + let f3 = Fq::from_str( + "29049672724678710659792141917402891276693777283079976086581207190825261000580", + ) + .unwrap(); + assert!(!f1.is_zero()); + assert!(!f2.is_zero()); + assert!(!f3.is_zero()); + assert_eq!(f1 / &f2, f3); +} + +#[test] +fn test_fq_sub() { + let f1 = Fq::from_str( + "18695869713129401390241150743745601908470616448391638969502807001833388904079", + ) + .unwrap(); + let f2 = Fq::from_str( + "10105476028534616828778879109836101003805485072436929139123765141153277007373", + ) + .unwrap(); + let f3 = Fq::from_str( + "8590393684594784561462271633909500904665131375954709830379041860680111896706", + ) + .unwrap(); + assert!(!f1.is_zero()); + assert!(!f2.is_zero()); + assert!(!f3.is_zero()); + assert_eq!(f1 - &f2, f3); +} + +#[test] +fn test_fq_double_in_place() { + let mut f1 = Fq::from_str( + "29729289787452206300641229002276778748586801323231253291984198106063944136114", + ) + .unwrap(); + let f3 = Fq::from_str( + "7022704399778222121834717496367591659483050145934868761364737512189307087715", + ) + .unwrap(); + assert!(!f1.is_zero()); + assert!(!f3.is_zero()); + f1.double_in_place(); + assert_eq!(f1, f3); +} + +#[test] +fn test_fq_double_in_place_thrice() { + let mut f1 = Fq::from_str( + "32768907806651393940832831055386272949401004221411141755415956893066040832473", + ) + .unwrap(); + let f3 = Fq::from_str( + "52407761752706389608871686410346320244445823769178582752913020344774001921732", + ) + .unwrap(); + assert!(!f1.is_zero()); + assert!(!f3.is_zero()); + f1.double_in_place(); + f1.double_in_place(); + f1.double_in_place(); + assert_eq!(f1, f3); +} + +#[test] +fn test_fq_generate_random_ed_on_bls12_381_point() { + let d = Fq::from_str( + "19257038036680949359750312669786877991949435402254120286184196891950884077233", + ) + .unwrap(); + let y = Fq::from_str( + "20269054604167148422407276086932743904275456233139568486008667107872965128512", + ) + .unwrap(); + let x2 = Fq::from_str( + "35041048504708632193693740149219726446678304552734087046982753200179718192840", + ) + .unwrap(); + + let computed_y2 = y.square(); + let y2 = Fq::from_str( + "22730681238307918419349440108285755984465605552827817317611903495170775437833", + ) + .unwrap(); + assert_eq!(y2, computed_y2); + + let computed_dy2 = d * &computed_y2; + let dy2 = Fq::from_str( + "24720347560552809545835752815204882739669031262711919770503096707526812943411", + ) + .unwrap(); + assert_eq!(dy2, computed_dy2); + + let computed_divisor = computed_dy2 + &Fq::one(); + let divisor = Fq::from_str( + "24720347560552809545835752815204882739669031262711919770503096707526812943412", + ) + .unwrap(); + assert_eq!(divisor, computed_divisor); + + let computed_x2 = (computed_y2 - &Fq::one()) / &computed_divisor; + assert_eq!(x2, computed_x2); + + let x = Fq::from_str( + "15337652609730546173818014678723269532482775720866471265774032070871608223361", + ) + .unwrap(); + let computed_x = computed_x2.sqrt().unwrap(); + assert_eq!(computed_x.square(), x2); + assert_eq!(x, computed_x); + + fn add<'a>(curr: (Fq, Fq), other: &'a (Fq, Fq)) -> (Fq, Fq) { + let y1y2 = curr.1 * &other.1; + let x1x2 = curr.0 * &other.0; + let d = Fq::from_str( + "19257038036680949359750312669786877991949435402254120286184196891950884077233", + ) + .unwrap(); + let dx1x2y1y2 = d * &y1y2 * &x1x2; + + let d1 = Fq::one() + &dx1x2y1y2; + let d2 = Fq::one() - &dx1x2y1y2; + + let x1y2 = curr.0 * &other.1; + let y1x2 = curr.1 * &other.0; + + let x = (x1y2 + &y1x2) / &d1; + let y = (y1y2 + &x1x2) / &d2; + + (x, y) + } + + let result = add((x, y), &(x, y)); + let result = add(result, &result); + let result = add(result, &result); + + let point_x = Fq::from_str( + "47259664076168047050113154262636619161204477920503059672059915868534495873964", + ) + .unwrap(); + let point_y = Fq::from_str( + "19016409245280491801573912449420132838852726543024859389273314249842195919690", + ) + .unwrap(); + assert_eq!((point_x, point_y), result); +} + +#[test] +fn test_fq_square_in_place() { + let mut f1 = Fq::from_str( + "34864651240005695523200639428464570946052769938774601449735727714436878540682", + ) + .unwrap(); + let f3 = + Fq::from_str("213133100629336594719108316042277780359104840987226496279264105585804377948") + .unwrap(); + assert!(!f1.is_zero()); + assert!(!f3.is_zero()); + f1.square_in_place(); + assert_eq!(f1, f3); +} + +#[test] +fn test_fq_sqrt() { + let f1 = Fq::from_str( + "10875927553327821418567659853801220899541454800710193788767706167237535308235", + ) + .unwrap(); + let f3 = Fq::from_str( + "10816221372957505053219354782681292880545918527618367765651802809826238616708", + ) + .unwrap(); + assert_eq!(f1.sqrt().unwrap(), f3); +} + +#[test] +fn test_fq_from_str() { + let f1_from_repr = Fq::from(BigInteger([ + 0xab8a2535947d1a77, + 0x9ba74cbfda0bbcda, + 0xe928b59724d60baf, + 0x1cccaaeb9bb1680a, + ])); + let f1 = Fq::from_str( + "13026376210409056429264774981357153555336288129100724591327877625017068755575", + ) + .unwrap(); + let f2_from_repr = Fq::from(BigInteger([ + 0x97e9103775d2f35c, + 0xbe6756b6c587544b, + 0x6ee38c3afd88ef4b, + 0x2bacd150f540c677, + ])); + let f2 = Fq::from_str( + "19754794831832707859764530223239420866832328728734160755396495950822165902172", + ) + .unwrap(); + assert_eq!(f1_from_repr, f1); + assert_eq!(f2_from_repr, f2); +} + +#[test] +fn test_fq_legendre() { + assert_eq!(QuadraticResidue, Fq::one().legendre()); + assert_eq!(Zero, Fq::zero().legendre()); + + let e = BigInteger([ + 0x0dbc5349cd5664da, + 0x8ac5b6296e3ae29d, + 0x127cb819feceaa3b, + 0x3a6b21fb03867191, + ]); + assert_eq!(QuadraticResidue, Fq::from(e).legendre()); + let e = BigInteger([ + 0x96341aefd047c045, + 0x9b5f4254500a4d65, + 0x1ee08223b68ac240, + 0x31d9cd545c0ec7c6, + ]); + assert_eq!(QuadraticNonResidue, Fq::from(e).legendre()); +} + +#[test] +fn test_fq_bytes() { + let f1_from_repr = Fq::from(BigInteger([ + 0xab8a2535947d1a77, + 0x9ba74cbfda0bbcda, + 0xe928b59724d60baf, + 0x1cccaaeb9bb1680a, + ])); + + let mut f1_bytes = [0u8; 32]; + f1_from_repr.write(f1_bytes.as_mut()).unwrap(); + + let f1 = Fq::read(f1_bytes.as_ref()).unwrap(); + assert_eq!(f1_from_repr, f1); +} + +#[test] +fn test_fr_add() { + let f1 = Fr::from(BigInteger([ + 0xc81265fb4130fe0c, + 0xb308836c14e22279, + 0x699e887f96bff372, + 0x84ecc7e76c11ad, + ])); + let f2 = Fr::from(BigInteger([ + 0x71875719b422efb8, + 0x0043658e68a93612, + 0x9fa756be2011e833, + 0xaa2b2cb08dac497, + ])); + let f3 = Fr::from(BigInteger([ + 0x3999bd14f553edc4, + 0xb34be8fa7d8b588c, + 0x0945df3db6d1dba5, + 0xb279f92f046d645, + ])); + assert_eq!(f1 + &f2, f3); +} + +#[test] +fn test_fr_mul() { + let f1 = Fr::from(BigInteger([ + 0xc81265fb4130fe0c, + 0xb308836c14e22279, + 0x699e887f96bff372, + 0x84ecc7e76c11ad, + ])); + let f2 = Fr::from(BigInteger([ + 0x71875719b422efb8, + 0x43658e68a93612, + 0x9fa756be2011e833, + 0xaa2b2cb08dac497, + ])); + let f3 = Fr::from(BigInteger([ + 0xbe3e50c164fe3381, + 0x5ac45bc180974585, + 0x1c234ad6dcdc70c9, + 0x15a75fba99bc8ad, + ])); + assert_eq!(f1 * &f2, f3); +} + +#[test] +fn test_fr_bytes() { + let f1_from_repr = Fr::from(BigInteger([ + 0xc81265fb4130fe0c, + 0xb308836c14e22279, + 0x699e887f96bff372, + 0x84ecc7e76c11ad, + ])); + + let mut f1_bytes = [0u8; 32]; + f1_from_repr.write(f1_bytes.as_mut()).unwrap(); + + let f1 = Fr::read(f1_bytes.as_ref()).unwrap(); + assert_eq!(f1_from_repr, f1); +} + +#[test] +fn test_fr_from_str() { + let f100_from_repr = Fr::from(BigInteger([0x64, 0, 0, 0])); + let f100 = Fr::from_str("100").unwrap(); + assert_eq!(f100_from_repr, f100); +} diff --git a/ed_on_bls12_381_bandersnatch/src/lib.rs b/ed_on_bls12_381_bandersnatch/src/lib.rs new file mode 100644 index 0000000..93f4ab6 --- /dev/null +++ b/ed_on_bls12_381_bandersnatch/src/lib.rs @@ -0,0 +1,37 @@ +#![cfg_attr(not(feature = "std"), no_std)] +#![deny( + warnings, + unused, + future_incompatible, + nonstandard_style, + rust_2018_idioms +)] +#![forbid(unsafe_code)] + +//! This library implements the Bendersnatch curve, a twisted Edwards curve +//! whose base field is the scalar field of the curve BLS12-381. This allows +//! defining cryptographic primitives that use elliptic curves over the scalar +//! field of the latter curve. This curve was generated by Simon Masson from Anoma, +//! and Antonio Sanso from Ethereum Foundation, and is also known as [bandersnatch](https://ethresear.ch/t/introducing-bandersnatch-a-fast-elliptic-curve-built-over-the-bls12-381-scalar-field/9957). +//! +//! See [here](https://github.com/asanso/Bandersnatch/blob/main/README.md) for the specification of the curve. +//! There was also a Python implementation [here](https://github.com/asanso/Bandersnatch/). +//! +//! Curve information: +//! * Base field: q = +//! 52435875175126190479447740508185965837690552500527637822603658699938581184513 +//! * Scalar field: r = +//! 13108968793781547619861935127046491459309155893440570251786403306729687672801 +//! * Valuation(q - 1, 2) = 32 +//! * Valuation(r - 1, 2) = 5 +//! * Curve equation: ax^2 + y^2 =1 + dx^2y^2, where +//! * a = -5 +//! * d = 45022363124591815672509500913686876175488063829319466900776701791074614335719 + +#[cfg(feature = "r1cs")] +pub mod constraints; +mod curves; +mod fields; + +pub use curves::*; +pub use fields::*;