diff --git a/CHANGELOG.md b/CHANGELOG.md index 3b2e71dd..d2672485 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -12,6 +12,10 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 - Remove scalar field generator [#100] - Remove canonical and canonical_derive dependency [#108] +### Changed + +- Separate Dusk's additions from the original crate [#109] + ## [0.11.3] - 2023-05-17 ### Changed @@ -186,6 +190,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 - Rename `S` to `TWO_ADACITY` and export it +[#109]: (https://github.com/dusk-network/bls12_381/issues/109) [#108]: (https://github.com/dusk-network/bls12_381/issues/108) [#100]: (https://github.com/dusk-network/bls12_381/issues/100) [#93]: (https://github.com/dusk-network/bls12_381/issues/93) diff --git a/src/choice.rs b/src/dusk/choice.rs similarity index 100% rename from src/choice.rs rename to src/dusk/choice.rs diff --git a/src/dusk/mod.rs b/src/dusk/mod.rs new file mode 100644 index 00000000..9e381055 --- /dev/null +++ b/src/dusk/mod.rs @@ -0,0 +1,10 @@ +// This Source Code Form is subject to the terms of the Mozilla Public +// License, v. 2.0. If a copy of the MPL was not distributed with this +// file, You can obtain one at http://mozilla.org/MPL/2.0/. +// +// Copyright (c) DUSK NETWORK. All rights reserved. + +pub(crate) mod choice; + +#[cfg(all(feature = "groups", feature = "alloc"))] +pub mod multiscalar_mul; diff --git a/src/multiscalar_mul.rs b/src/dusk/multiscalar_mul.rs similarity index 100% rename from src/multiscalar_mul.rs rename to src/dusk/multiscalar_mul.rs diff --git a/src/fp.rs b/src/fp.rs index 8866d6f1..1513defd 100644 --- a/src/fp.rs +++ b/src/fp.rs @@ -4,21 +4,15 @@ use core::convert::TryFrom; use core::fmt; use core::ops::{Add, AddAssign, Mul, MulAssign, Neg, Sub, SubAssign}; +use subtle::{Choice, ConditionallySelectable, ConstantTimeEq, CtOption}; -#[cfg(feature = "serde_req")] -use serde::{ - self, de::Visitor, ser::SerializeSeq, Deserialize, Deserializer, Serialize, Serializer, -}; +use crate::util::{adc, mac, sbb}; #[cfg(feature = "rkyv-impl")] use bytecheck::CheckBytes; #[cfg(feature = "rkyv-impl")] use rkyv::{Archive, Deserialize as RkyvDeserialize, Serialize as RkyvSerialize}; -use subtle::{Choice, ConditionallySelectable, ConstantTimeEq, CtOption}; - -use crate::util::{adc, mac, sbb}; - // The internal representation of this type is six 64-bit unsigned // integers in little-endian order. `Fp` values are always in // Montgomery form; i.e., Scalar(a) = aR mod p, with R = 2^384. @@ -59,60 +53,7 @@ impl Eq for Fp {} impl PartialEq for Fp { #[inline] fn eq(&self, other: &Self) -> bool { - self.ct_eq(other).unwrap_u8() == 1 - } -} - -#[cfg(feature = "serde_req")] -impl Serialize for Fp { - fn serialize(&self, serializer: S) -> Result - where - S: Serializer, - { - let mut tup = serializer.serialize_seq(Some(48))?; - let fp_as_bytes = self.to_bytes(); - for i in 0..48 { - tup.serialize_element(&fp_as_bytes[i])?; - } - tup.end() - } -} - -#[cfg(feature = "serde_req")] -impl<'de> Deserialize<'de> for Fp { - fn deserialize(deserializer: D) -> Result - where - D: Deserializer<'de>, - { - struct FpVisitor; - - impl<'de> Visitor<'de> for FpVisitor { - type Value = Fp; - - fn expecting(&self, formatter: &mut ::core::fmt::Formatter) -> ::core::fmt::Result { - formatter.write_str("a prover key with valid powers per points") - } - - fn visit_seq(self, mut seq: A) -> Result - where - A: serde::de::SeqAccess<'de>, - { - let mut bytes = [0u8; 48]; - for i in 0..48 { - bytes[i] = seq - .next_element()? - .ok_or(serde::de::Error::invalid_length(i, &"expected 48 bytes"))?; - } - let res = Fp::from_bytes(&bytes); - if res.is_some().unwrap_u8() == 1u8 { - return Ok(res.unwrap()); - } else { - return Err(serde::de::Error::custom(&"fp was not canonically encoded")); - } - } - } - - deserializer.deserialize_seq(FpVisitor) + bool::from(self.ct_eq(other)) } } @@ -131,35 +72,35 @@ impl ConditionallySelectable for Fp { /// p = 4002409555221667393417789825735904156556882819939007885332058136124031650490837864442687629129015664037894272559787 const MODULUS: [u64; 6] = [ - 0xb9feffffffffaaab, - 0x1eabfffeb153ffff, - 0x6730d2a0f6b0f624, - 0x64774b84f38512bf, - 0x4b1ba7b6434bacd7, - 0x1a0111ea397fe69a, + 0xb9fe_ffff_ffff_aaab, + 0x1eab_fffe_b153_ffff, + 0x6730_d2a0_f6b0_f624, + 0x6477_4b84_f385_12bf, + 0x4b1b_a7b6_434b_acd7, + 0x1a01_11ea_397f_e69a, ]; /// INV = -(p^{-1} mod 2^64) mod 2^64 -const INV: u64 = 0x89f3fffcfffcfffd; +const INV: u64 = 0x89f3_fffc_fffc_fffd; /// R = 2^384 mod p const R: Fp = Fp([ - 0x760900000002fffd, - 0xebf4000bc40c0002, - 0x5f48985753c758ba, - 0x77ce585370525745, - 0x5c071a97a256ec6d, - 0x15f65ec3fa80e493, + 0x7609_0000_0002_fffd, + 0xebf4_000b_c40c_0002, + 0x5f48_9857_53c7_58ba, + 0x77ce_5853_7052_5745, + 0x5c07_1a97_a256_ec6d, + 0x15f6_5ec3_fa80_e493, ]); /// R2 = 2^(384*2) mod p const R2: Fp = Fp([ - 0xf4df1f341c341746, - 0xa76e6a609d104f1, - 0x8de5476c4c95b6d5, - 0x67eb88a9939d83c0, - 0x9a793e85b519952d, - 0x11988fe592cae3aa, + 0xf4df_1f34_1c34_1746, + 0x0a76_e6a6_09d1_04f1, + 0x8de5_476c_4c95_b6d5, + 0x67eb_88a9_939d_83c0, + 0x9a79_3e85_b519_952d, + 0x1198_8fe5_92ca_e3aa, ]); impl<'a> Neg for &'a Fp { @@ -227,7 +168,7 @@ impl Fp { self.ct_eq(&Fp::zero()) } - /// Attempts to convert a little-endian byte representation of + /// Attempts to convert a big-endian byte representation of /// a scalar into an `Fp`, failing if the input is not canonical. pub fn from_bytes(bytes: &[u8; 48]) -> CtOption { let mut tmp = Fp([0, 0, 0, 0, 0, 0]); @@ -292,12 +233,12 @@ impl Fp { self.0[0], self.0[1], self.0[2], self.0[3], self.0[4], self.0[5], 0, 0, 0, 0, 0, 0, ); - let (_, borrow) = sbb(tmp.0[0], 0xdcff7fffffffd556, 0); - let (_, borrow) = sbb(tmp.0[1], 0x0f55ffff58a9ffff, borrow); - let (_, borrow) = sbb(tmp.0[2], 0xb39869507b587b12, borrow); - let (_, borrow) = sbb(tmp.0[3], 0xb23ba5c279c2895f, borrow); - let (_, borrow) = sbb(tmp.0[4], 0x258dd3db21a5d66b, borrow); - let (_, borrow) = sbb(tmp.0[5], 0x0d0088f51cbff34d, borrow); + let (_, borrow) = sbb(tmp.0[0], 0xdcff_7fff_ffff_d556, 0); + let (_, borrow) = sbb(tmp.0[1], 0x0f55_ffff_58a9_ffff, borrow); + let (_, borrow) = sbb(tmp.0[2], 0xb398_6950_7b58_7b12, borrow); + let (_, borrow) = sbb(tmp.0[3], 0xb23b_a5c2_79c2_895f, borrow); + let (_, borrow) = sbb(tmp.0[4], 0x258d_d3db_21a5_d66b, borrow); + let (_, borrow) = sbb(tmp.0[5], 0x0d00_88f5_1cbf_f34d, borrow); // If the element was smaller, the subtraction will underflow // producing a borrow value of 0xffff...ffff, otherwise it will @@ -314,11 +255,6 @@ impl Fp { Fp(v) } - /// Internal representation of `Fp` - pub const fn internal_repr(&self) -> &[u64; 6] { - &self.0 - } - /// Although this is labeled "vartime", it is only /// variable time with respect to the exponent. It /// is also not exposed in the public API. @@ -344,12 +280,12 @@ impl Fp { // so we check that we got the correct result at the end. let sqrt = self.pow_vartime(&[ - 0xee7fbfffffffeaab, - 0x7aaffffac54ffff, - 0xd9cc34a83dac3d89, - 0xd91dd2e13ce144af, - 0x92c6e9ed90d2eb35, - 0x680447a8e5ff9a6, + 0xee7f_bfff_ffff_eaab, + 0x07aa_ffff_ac54_ffff, + 0xd9cc_34a8_3dac_3d89, + 0xd91d_d2e1_3ce1_44af, + 0x92c6_e9ed_90d2_eb35, + 0x0680_447a_8e5f_f9a6, ]); CtOption::new(sqrt, sqrt.square().ct_eq(self)) @@ -362,12 +298,12 @@ impl Fp { pub fn invert(&self) -> CtOption { // Exponentiate by p - 2 let t = self.pow_vartime(&[ - 0xb9feffffffffaaa9, - 0x1eabfffeb153ffff, - 0x6730d2a0f6b0f624, - 0x64774b84f38512bf, - 0x4b1ba7b6434bacd7, - 0x1a0111ea397fe69a, + 0xb9fe_ffff_ffff_aaa9, + 0x1eab_fffe_b153_ffff, + 0x6730_d2a0_f6b0_f624, + 0x6477_4b84_f385_12bf, + 0x4b1b_a7b6_434b_acd7, + 0x1a01_11ea_397f_e69a, ]); CtOption::new(t, !self.is_zero()) @@ -676,321 +612,390 @@ impl Fp { } } -#[cfg(test)] -mod tests { - use super::*; - - #[test] - fn test_conditional_selection() { - let a = Fp([1, 2, 3, 4, 5, 6]); - let b = Fp([7, 8, 9, 10, 11, 12]); +#[test] +fn test_conditional_selection() { + let a = Fp([1, 2, 3, 4, 5, 6]); + let b = Fp([7, 8, 9, 10, 11, 12]); - assert_eq!( - ConditionallySelectable::conditional_select(&a, &b, Choice::from(0u8)), - a - ); - assert_eq!( - ConditionallySelectable::conditional_select(&a, &b, Choice::from(1u8)), - b - ); - } - - #[test] - fn test_equality() { - fn is_equal(a: &Fp, b: &Fp) -> bool { - let eq = a == b; - let ct_eq = a.ct_eq(&b); - - assert_eq!(eq, ct_eq.unwrap_u8() == 1); + assert_eq!( + ConditionallySelectable::conditional_select(&a, &b, Choice::from(0u8)), + a + ); + assert_eq!( + ConditionallySelectable::conditional_select(&a, &b, Choice::from(1u8)), + b + ); +} - eq - } +#[test] +fn test_equality() { + fn is_equal(a: &Fp, b: &Fp) -> bool { + let eq = a == b; + let ct_eq = a.ct_eq(&b); - assert!(is_equal(&Fp([1, 2, 3, 4, 5, 6]), &Fp([1, 2, 3, 4, 5, 6]))); + assert_eq!(eq, bool::from(ct_eq)); - assert!(!is_equal(&Fp([7, 2, 3, 4, 5, 6]), &Fp([1, 2, 3, 4, 5, 6]))); - assert!(!is_equal(&Fp([1, 7, 3, 4, 5, 6]), &Fp([1, 2, 3, 4, 5, 6]))); - assert!(!is_equal(&Fp([1, 2, 7, 4, 5, 6]), &Fp([1, 2, 3, 4, 5, 6]))); - assert!(!is_equal(&Fp([1, 2, 3, 7, 5, 6]), &Fp([1, 2, 3, 4, 5, 6]))); - assert!(!is_equal(&Fp([1, 2, 3, 4, 7, 6]), &Fp([1, 2, 3, 4, 5, 6]))); - assert!(!is_equal(&Fp([1, 2, 3, 4, 5, 7]), &Fp([1, 2, 3, 4, 5, 6]))); + eq } - #[test] - fn test_squaring() { - let a = Fp([ - 0xd215d2768e83191b, - 0x5085d80f8fb28261, - 0xce9a032ddf393a56, - 0x3e9c4fff2ca0c4bb, - 0x6436b6f7f4d95dfb, - 0x10606628ad4a4d90, - ]); - let b = Fp([ - 0x33d9c42a3cb3e235, - 0xdad11a094c4cd455, - 0xa2f144bd729aaeba, - 0xd4150932be9ffeac, - 0xe27bc7c47d44ee50, - 0x14b6a78d3ec7a560, - ]); - - assert_eq!(a.square(), b); - } + assert!(is_equal(&Fp([1, 2, 3, 4, 5, 6]), &Fp([1, 2, 3, 4, 5, 6]))); - #[test] - fn test_multiplication() { - let a = Fp([ - 0x397a38320170cd4, - 0x734c1b2c9e761d30, - 0x5ed255ad9a48beb5, - 0x95a3c6b22a7fcfc, - 0x2294ce75d4e26a27, - 0x13338bd870011ebb, - ]); - let b = Fp([ - 0xb9c3c7c5b1196af7, - 0x2580e2086ce335c1, - 0xf49aed3d8a57ef42, - 0x41f281e49846e878, - 0xe0762346c38452ce, - 0x652e89326e57dc0, - ]); - let c = Fp([ - 0xf96ef3d711ab5355, - 0xe8d459ea00f148dd, - 0x53f7354a5f00fa78, - 0x9e34a4f3125c5f83, - 0x3fbe0c47ca74c19e, - 0x1b06a8bbd4adfe4, - ]); - - assert_eq!(a * b, c); - } - - #[test] - fn test_addition() { - let a = Fp([ - 0x5360bb5978678032, - 0x7dd275ae799e128e, - 0x5c5b5071ce4f4dcf, - 0xcdb21f93078dbb3e, - 0xc32365c5e73f474a, - 0x115a2a5489babe5b, - ]); - let b = Fp([ - 0x9fd287733d23dda0, - 0xb16bf2af738b3554, - 0x3e57a75bd3cc6d1d, - 0x900bc0bd627fd6d6, - 0xd319a080efb245fe, - 0x15fdcaa4e4bb2091, - ]); - let c = Fp([ - 0x393442ccb58bb327, - 0x1092685f3bd547e3, - 0x3382252cab6ac4c9, - 0xf94694cb76887f55, - 0x4b215e9093a5e071, - 0xd56e30f34f5f853, - ]); + assert!(!is_equal(&Fp([7, 2, 3, 4, 5, 6]), &Fp([1, 2, 3, 4, 5, 6]))); + assert!(!is_equal(&Fp([1, 7, 3, 4, 5, 6]), &Fp([1, 2, 3, 4, 5, 6]))); + assert!(!is_equal(&Fp([1, 2, 7, 4, 5, 6]), &Fp([1, 2, 3, 4, 5, 6]))); + assert!(!is_equal(&Fp([1, 2, 3, 7, 5, 6]), &Fp([1, 2, 3, 4, 5, 6]))); + assert!(!is_equal(&Fp([1, 2, 3, 4, 7, 6]), &Fp([1, 2, 3, 4, 5, 6]))); + assert!(!is_equal(&Fp([1, 2, 3, 4, 5, 7]), &Fp([1, 2, 3, 4, 5, 6]))); +} - assert_eq!(a + b, c); - } +#[test] +fn test_squaring() { + let a = Fp([ + 0xd215_d276_8e83_191b, + 0x5085_d80f_8fb2_8261, + 0xce9a_032d_df39_3a56, + 0x3e9c_4fff_2ca0_c4bb, + 0x6436_b6f7_f4d9_5dfb, + 0x1060_6628_ad4a_4d90, + ]); + let b = Fp([ + 0x33d9_c42a_3cb3_e235, + 0xdad1_1a09_4c4c_d455, + 0xa2f1_44bd_729a_aeba, + 0xd415_0932_be9f_feac, + 0xe27b_c7c4_7d44_ee50, + 0x14b6_a78d_3ec7_a560, + ]); + + assert_eq!(a.square(), b); +} - #[test] - fn test_subtraction() { - let a = Fp([ - 0x5360bb5978678032, - 0x7dd275ae799e128e, - 0x5c5b5071ce4f4dcf, - 0xcdb21f93078dbb3e, - 0xc32365c5e73f474a, - 0x115a2a5489babe5b, - ]); - let b = Fp([ - 0x9fd287733d23dda0, - 0xb16bf2af738b3554, - 0x3e57a75bd3cc6d1d, - 0x900bc0bd627fd6d6, - 0xd319a080efb245fe, - 0x15fdcaa4e4bb2091, - ]); - let c = Fp([ - 0x6d8d33e63b434d3d, - 0xeb1282fdb766dd39, - 0x85347bb6f133d6d5, - 0xa21daa5a9892f727, - 0x3b256cfb3ad8ae23, - 0x155d7199de7f8464, - ]); +#[test] +fn test_multiplication() { + let a = Fp([ + 0x0397_a383_2017_0cd4, + 0x734c_1b2c_9e76_1d30, + 0x5ed2_55ad_9a48_beb5, + 0x095a_3c6b_22a7_fcfc, + 0x2294_ce75_d4e2_6a27, + 0x1333_8bd8_7001_1ebb, + ]); + let b = Fp([ + 0xb9c3_c7c5_b119_6af7, + 0x2580_e208_6ce3_35c1, + 0xf49a_ed3d_8a57_ef42, + 0x41f2_81e4_9846_e878, + 0xe076_2346_c384_52ce, + 0x0652_e893_26e5_7dc0, + ]); + let c = Fp([ + 0xf96e_f3d7_11ab_5355, + 0xe8d4_59ea_00f1_48dd, + 0x53f7_354a_5f00_fa78, + 0x9e34_a4f3_125c_5f83, + 0x3fbe_0c47_ca74_c19e, + 0x01b0_6a8b_bd4a_dfe4, + ]); + + assert_eq!(a * b, c); +} - assert_eq!(a - b, c); - } +#[test] +fn test_addition() { + let a = Fp([ + 0x5360_bb59_7867_8032, + 0x7dd2_75ae_799e_128e, + 0x5c5b_5071_ce4f_4dcf, + 0xcdb2_1f93_078d_bb3e, + 0xc323_65c5_e73f_474a, + 0x115a_2a54_89ba_be5b, + ]); + let b = Fp([ + 0x9fd2_8773_3d23_dda0, + 0xb16b_f2af_738b_3554, + 0x3e57_a75b_d3cc_6d1d, + 0x900b_c0bd_627f_d6d6, + 0xd319_a080_efb2_45fe, + 0x15fd_caa4_e4bb_2091, + ]); + let c = Fp([ + 0x3934_42cc_b58b_b327, + 0x1092_685f_3bd5_47e3, + 0x3382_252c_ab6a_c4c9, + 0xf946_94cb_7688_7f55, + 0x4b21_5e90_93a5_e071, + 0x0d56_e30f_34f5_f853, + ]); + + assert_eq!(a + b, c); +} - #[test] - fn test_negation() { - let a = Fp([ - 0x5360bb5978678032, - 0x7dd275ae799e128e, - 0x5c5b5071ce4f4dcf, - 0xcdb21f93078dbb3e, - 0xc32365c5e73f474a, - 0x115a2a5489babe5b, - ]); - let b = Fp([ - 0x669e44a687982a79, - 0xa0d98a5037b5ed71, - 0xad5822f2861a854, - 0x96c52bf1ebf75781, - 0x87f841f05c0c658c, - 0x8a6e795afc5283e, - ]); +#[test] +fn test_subtraction() { + let a = Fp([ + 0x5360_bb59_7867_8032, + 0x7dd2_75ae_799e_128e, + 0x5c5b_5071_ce4f_4dcf, + 0xcdb2_1f93_078d_bb3e, + 0xc323_65c5_e73f_474a, + 0x115a_2a54_89ba_be5b, + ]); + let b = Fp([ + 0x9fd2_8773_3d23_dda0, + 0xb16b_f2af_738b_3554, + 0x3e57_a75b_d3cc_6d1d, + 0x900b_c0bd_627f_d6d6, + 0xd319_a080_efb2_45fe, + 0x15fd_caa4_e4bb_2091, + ]); + let c = Fp([ + 0x6d8d_33e6_3b43_4d3d, + 0xeb12_82fd_b766_dd39, + 0x8534_7bb6_f133_d6d5, + 0xa21d_aa5a_9892_f727, + 0x3b25_6cfb_3ad8_ae23, + 0x155d_7199_de7f_8464, + ]); + + assert_eq!(a - b, c); +} - assert_eq!(-a, b); - } +#[test] +fn test_negation() { + let a = Fp([ + 0x5360_bb59_7867_8032, + 0x7dd2_75ae_799e_128e, + 0x5c5b_5071_ce4f_4dcf, + 0xcdb2_1f93_078d_bb3e, + 0xc323_65c5_e73f_474a, + 0x115a_2a54_89ba_be5b, + ]); + let b = Fp([ + 0x669e_44a6_8798_2a79, + 0xa0d9_8a50_37b5_ed71, + 0x0ad5_822f_2861_a854, + 0x96c5_2bf1_ebf7_5781, + 0x87f8_41f0_5c0c_658c, + 0x08a6_e795_afc5_283e, + ]); + + assert_eq!(-a, b); +} - #[test] - fn test_debug() { - assert_eq!( +#[test] +fn test_debug() { + assert_eq!( format!( "{:?}", - Fp([0x5360bb5978678032, 0x7dd275ae799e128e, 0x5c5b5071ce4f4dcf, 0xcdb21f93078dbb3e, 0xc32365c5e73f474a, 0x115a2a5489babe5b]) + Fp([ + 0x5360_bb59_7867_8032, + 0x7dd2_75ae_799e_128e, + 0x5c5b_5071_ce4f_4dcf, + 0xcdb2_1f93_078d_bb3e, + 0xc323_65c5_e73f_474a, + 0x115a_2a54_89ba_be5b, + ]) ), "0x104bf052ad3bc99bcb176c24a06a6c3aad4eaf2308fc4d282e106c84a757d061052630515305e59bdddf8111bfdeb704" ); - } +} - #[test] - fn test_from_bytes() { - let mut a = Fp([ - 0xdc906d9be3f95dc8, - 0x8755caf7459691a1, - 0xcff1a7f4e9583ab3, - 0x9b43821f849e2284, - 0xf57554f3a2974f3f, - 0x85dbea84ed47f79, - ]); +#[test] +fn test_from_bytes() { + let mut a = Fp([ + 0xdc90_6d9b_e3f9_5dc8, + 0x8755_caf7_4596_91a1, + 0xcff1_a7f4_e958_3ab3, + 0x9b43_821f_849e_2284, + 0xf575_54f3_a297_4f3f, + 0x085d_bea8_4ed4_7f79, + ]); + + for _ in 0..100 { + a = a.square(); + let tmp = a.to_bytes(); + let b = Fp::from_bytes(&tmp).unwrap(); + + assert_eq!(a, b); + } + + assert_eq!( + -Fp::one(), + Fp::from_bytes(&[ + 26, 1, 17, 234, 57, 127, 230, 154, 75, 27, 167, 182, 67, 75, 172, 215, 100, 119, 75, + 132, 243, 133, 18, 191, 103, 48, 210, 160, 246, 176, 246, 36, 30, 171, 255, 254, 177, + 83, 255, 255, 185, 254, 255, 255, 255, 255, 170, 170 + ]) + .unwrap() + ); - for _ in 0..100 { - a = a.square(); - let tmp = a.to_bytes(); - let b = Fp::from_bytes(&tmp).unwrap(); + assert!(bool::from( + Fp::from_bytes(&[ + 27, 1, 17, 234, 57, 127, 230, 154, 75, 27, 167, 182, 67, 75, 172, 215, 100, 119, 75, + 132, 243, 133, 18, 191, 103, 48, 210, 160, 246, 176, 246, 36, 30, 171, 255, 254, 177, + 83, 255, 255, 185, 254, 255, 255, 255, 255, 170, 170 + ]) + .is_none() + )); - assert_eq!(a, b); - } + assert!(bool::from(Fp::from_bytes(&[0xff; 48]).is_none())); +} - assert_eq!( - -Fp::one(), - Fp::from_bytes(&[ - 26, 1, 17, 234, 57, 127, 230, 154, 75, 27, 167, 182, 67, 75, 172, 215, 100, 119, - 75, 132, 243, 133, 18, 191, 103, 48, 210, 160, 246, 176, 246, 36, 30, 171, 255, - 254, 177, 83, 255, 255, 185, 254, 255, 255, 255, 255, 170, 170 - ]) - .unwrap() - ); +#[test] +fn test_sqrt() { + // a = 4 + let a = Fp::from_raw_unchecked([ + 0xaa27_0000_000c_fff3, + 0x53cc_0032_fc34_000a, + 0x478f_e97a_6b0a_807f, + 0xb1d3_7ebe_e6ba_24d7, + 0x8ec9_733b_bf78_ab2f, + 0x09d6_4551_3d83_de7e, + ]); + + assert_eq!( + // sqrt(4) = -2 + -a.sqrt().unwrap(), + // 2 + Fp::from_raw_unchecked([ + 0x3213_0000_0006_554f, + 0xb93c_0018_d6c4_0005, + 0x5760_5e0d_b0dd_bb51, + 0x8b25_6521_ed1f_9bcb, + 0x6cf2_8d79_0162_2c03, + 0x11eb_ab9d_bb81_e28c, + ]) + ); +} - assert!( - Fp::from_bytes(&[ - 27, 1, 17, 234, 57, 127, 230, 154, 75, 27, 167, 182, 67, 75, 172, 215, 100, 119, - 75, 132, 243, 133, 18, 191, 103, 48, 210, 160, 246, 176, 246, 36, 30, 171, 255, - 254, 177, 83, 255, 255, 185, 254, 255, 255, 255, 255, 170, 170 - ]) - .is_none() - .unwrap_u8() - == 1 - ); +#[test] +fn test_inversion() { + let a = Fp([ + 0x43b4_3a50_78ac_2076, + 0x1ce0_7630_46f8_962b, + 0x724a_5276_486d_735c, + 0x6f05_c2a6_282d_48fd, + 0x2095_bd5b_b4ca_9331, + 0x03b3_5b38_94b0_f7da, + ]); + let b = Fp([ + 0x69ec_d704_0952_148f, + 0x985c_cc20_2219_0f55, + 0xe19b_ba36_a9ad_2f41, + 0x19bb_16c9_5219_dbd8, + 0x14dc_acfd_fb47_8693, + 0x115f_f58a_fff9_a8e1, + ]); + + assert_eq!(a.invert().unwrap(), b); + assert!(bool::from(Fp::zero().invert().is_none())); +} - assert!(Fp::from_bytes(&[0xff; 48]).is_none().unwrap_u8() == 1); - } +#[test] +fn test_lexicographic_largest() { + assert!(!bool::from(Fp::zero().lexicographically_largest())); + assert!(!bool::from(Fp::one().lexicographically_largest())); + assert!(!bool::from( + Fp::from_raw_unchecked([ + 0xa1fa_ffff_fffe_5557, + 0x995b_fff9_76a3_fffe, + 0x03f4_1d24_d174_ceb4, + 0xf654_7998_c199_5dbd, + 0x778a_468f_507a_6034, + 0x0205_5993_1f7f_8103 + ]) + .lexicographically_largest() + )); + assert!(bool::from( + Fp::from_raw_unchecked([ + 0x1804_0000_0001_5554, + 0x8550_0005_3ab0_0001, + 0x633c_b57c_253c_276f, + 0x6e22_d1ec_31eb_b502, + 0xd391_6126_f2d1_4ca2, + 0x17fb_b857_1a00_6596, + ]) + .lexicographically_largest() + )); + assert!(bool::from( + Fp::from_raw_unchecked([ + 0x43f5_ffff_fffc_aaae, + 0x32b7_fff2_ed47_fffd, + 0x07e8_3a49_a2e9_9d69, + 0xeca8_f331_8332_bb7a, + 0xef14_8d1e_a0f4_c069, + 0x040a_b326_3eff_0206, + ]) + .lexicographically_largest() + )); +} - #[test] - fn test_sqrt() { - // a = 4 - let a = Fp::from_raw_unchecked([ - 0xaa270000000cfff3, - 0x53cc0032fc34000a, - 0x478fe97a6b0a807f, - 0xb1d37ebee6ba24d7, - 0x8ec9733bbf78ab2f, - 0x9d645513d83de7e, - ]); +mod dusk { + use super::*; - assert_eq!( - // sqrt(4) = -2 - -a.sqrt().unwrap(), - // 2 - Fp::from_raw_unchecked([ - 0x321300000006554f, - 0xb93c0018d6c40005, - 0x57605e0db0ddbb51, - 0x8b256521ed1f9bcb, - 0x6cf28d7901622c03, - 0x11ebab9dbb81e28c - ]) - ); + impl Fp { + /// Internal representation of `Fp` + pub const fn internal_repr(&self) -> &[u64; 6] { + &self.0 + } } - #[test] - fn test_inversion() { - let a = Fp([ - 0x43b43a5078ac2076, - 0x1ce0763046f8962b, - 0x724a5276486d735c, - 0x6f05c2a6282d48fd, - 0x2095bd5bb4ca9331, - 0x3b35b3894b0f7da, - ]); - let b = Fp([ - 0x69ecd7040952148f, - 0x985ccc2022190f55, - 0xe19bba36a9ad2f41, - 0x19bb16c95219dbd8, - 0x14dcacfdfb478693, - 0x115ff58afff9a8e1, - ]); + #[cfg(feature = "serde_req")] + use serde::{ + self, de::Visitor, ser::SerializeSeq, Deserialize, Deserializer, Serialize, Serializer, + }; - assert_eq!(a.invert().unwrap(), b); - assert!(Fp::zero().invert().is_none().unwrap_u8() == 1); + #[cfg(feature = "serde_req")] + impl Serialize for Fp { + fn serialize(&self, serializer: S) -> Result + where + S: Serializer, + { + let mut tup = serializer.serialize_seq(Some(48))?; + let fp_as_bytes = self.to_bytes(); + for i in 0..48 { + tup.serialize_element(&fp_as_bytes[i])?; + } + tup.end() + } } - #[test] - fn test_lexicographic_largest() { - assert!(!bool::from(Fp::zero().lexicographically_largest())); - assert!(!bool::from(Fp::one().lexicographically_largest())); - assert!(!bool::from( - Fp::from_raw_unchecked([ - 0xa1fafffffffe5557, - 0x995bfff976a3fffe, - 0x3f41d24d174ceb4, - 0xf6547998c1995dbd, - 0x778a468f507a6034, - 0x20559931f7f8103 - ]) - .lexicographically_largest() - )); - assert!(bool::from( - Fp::from_raw_unchecked([ - 0x1804000000015554, - 0x855000053ab00001, - 0x633cb57c253c276f, - 0x6e22d1ec31ebb502, - 0xd3916126f2d14ca2, - 0x17fbb8571a006596 - ]) - .lexicographically_largest() - )); - assert!(bool::from( - Fp::from_raw_unchecked([ - 0x43f5fffffffcaaae, - 0x32b7fff2ed47fffd, - 0x7e83a49a2e99d69, - 0xeca8f3318332bb7a, - 0xef148d1ea0f4c069, - 0x40ab3263eff0206 - ]) - .lexicographically_largest() - )); + #[cfg(feature = "serde_req")] + impl<'de> Deserialize<'de> for Fp { + fn deserialize(deserializer: D) -> Result + where + D: Deserializer<'de>, + { + struct FpVisitor; + + impl<'de> Visitor<'de> for FpVisitor { + type Value = Fp; + + fn expecting(&self, formatter: &mut ::core::fmt::Formatter) -> ::core::fmt::Result { + formatter.write_str("a prover key with valid powers per points") + } + + fn visit_seq(self, mut seq: A) -> Result + where + A: serde::de::SeqAccess<'de>, + { + let mut bytes = [0u8; 48]; + for i in 0..48 { + bytes[i] = seq + .next_element()? + .ok_or(serde::de::Error::invalid_length(i, &"expected 48 bytes"))?; + } + let res = Fp::from_bytes(&bytes); + if res.is_some().unwrap_u8() == 1u8 { + return Ok(res.unwrap()); + } else { + return Err(serde::de::Error::custom(&"fp was not canonically encoded")); + } + } + } + + deserializer.deserialize_seq(FpVisitor) + } } #[test] diff --git a/src/fp12.rs b/src/fp12.rs index e5b846ec..2336bbdf 100644 --- a/src/fp12.rs +++ b/src/fp12.rs @@ -143,20 +143,20 @@ impl Fp12 { let c1 = c1 * Fp6::from(Fp2 { c0: Fp::from_raw_unchecked([ - 0x7089552b319d465, - 0xc6695f92b50a8313, - 0x97e83cccd117228f, - 0xa35baecab2dc29ee, - 0x1ce393ea5daace4d, - 0x8f2220fb0fb66eb, + 0x0708_9552_b319_d465, + 0xc669_5f92_b50a_8313, + 0x97e8_3ccc_d117_228f, + 0xa35b_aeca_b2dc_29ee, + 0x1ce3_93ea_5daa_ce4d, + 0x08f2_220f_b0fb_66eb, ]), c1: Fp::from_raw_unchecked([ - 0xb2f66aad4ce5d646, - 0x5842a06bfc497cec, - 0xcf4895d42599d394, - 0xc11b9cba40a8e8d0, - 0x2e3813cbe5a0de89, - 0x110eefda88847faf, + 0xb2f6_6aad_4ce5_d646, + 0x5842_a06b_fc49_7cec, + 0xcf48_95d4_2599_d394, + 0xc11b_9cba_40a8_e8d0, + 0x2e38_13cb_e5a0_de89, + 0x110e_efda_8884_7faf, ]), }); @@ -254,397 +254,392 @@ impl<'a, 'b> Sub<&'b Fp12> for &'a Fp12 { impl_binops_additive!(Fp12, Fp12); impl_binops_multiplicative!(Fp12, Fp12); -#[cfg(test)] -mod tests { - use super::*; - - #[test] - fn test_arithmetic() { - use crate::fp::*; - use crate::fp2::*; - - let a = Fp12 { - c0: Fp6 { - c0: Fp2 { - c0: Fp::from_raw_unchecked([ - 0x47f9cb98b1b82d58, - 0x5fe911eba3aa1d9d, - 0x96bf1b5f4dd81db3, - 0x8100d27cc9259f5b, - 0xafa20b9674640eab, - 0x9bbcea7d8d9497d, - ]), - c1: Fp::from_raw_unchecked([ - 0x303cb98b1662daa, - 0xd93110aa0a621d5a, - 0xbfa9820c5be4a468, - 0xba3643ecb05a348, - 0xdc3534bb1f1c25a6, - 0x6c305bb19c0e1c1, - ]), - }, - c1: Fp2 { - c0: Fp::from_raw_unchecked([ - 0x46f9cb98b162d858, - 0xbe9109cf7aa1d57, - 0xc791bc55fece41d2, - 0xf84c57704e385ec2, - 0xcb49c1d9c010e60f, - 0xacdb8e158bfe3c8, - ]), - c1: Fp::from_raw_unchecked([ - 0x8aefcb98b15f8306, - 0x3ea1108fe4f21d54, - 0xcf79f69fa1b7df3b, - 0xe4f54aa1d16b1a3c, - 0xba5e4ef86105a679, - 0xed86c0797bee5cf, - ]), - }, - c2: Fp2 { - c0: Fp::from_raw_unchecked([ - 0xcee5cb98b15c2db4, - 0x71591082d23a1d51, - 0xd76230e944a17ca4, - 0xd19e3dd3549dd5b6, - 0xa972dc1701fa66e3, - 0x12e31f2dd6bde7d6, - ]), - c1: Fp::from_raw_unchecked([ - 0xad2acb98b1732d9d, - 0x2cfd10dd06961d64, - 0x7396b86c6ef24e8, - 0xbd76e2fdb1bfc820, - 0x6afea7f6de94d0d5, - 0x10994b0c5744c040, - ]), - }, +#[test] +fn test_arithmetic() { + use crate::fp::*; + use crate::fp2::*; + + let a = Fp12 { + c0: Fp6 { + c0: Fp2 { + c0: Fp::from_raw_unchecked([ + 0x47f9_cb98_b1b8_2d58, + 0x5fe9_11eb_a3aa_1d9d, + 0x96bf_1b5f_4dd8_1db3, + 0x8100_d27c_c925_9f5b, + 0xafa2_0b96_7464_0eab, + 0x09bb_cea7_d8d9_497d, + ]), + c1: Fp::from_raw_unchecked([ + 0x0303_cb98_b166_2daa, + 0xd931_10aa_0a62_1d5a, + 0xbfa9_820c_5be4_a468, + 0x0ba3_643e_cb05_a348, + 0xdc35_34bb_1f1c_25a6, + 0x06c3_05bb_19c0_e1c1, + ]), + }, + c1: Fp2 { + c0: Fp::from_raw_unchecked([ + 0x46f9_cb98_b162_d858, + 0x0be9_109c_f7aa_1d57, + 0xc791_bc55_fece_41d2, + 0xf84c_5770_4e38_5ec2, + 0xcb49_c1d9_c010_e60f, + 0x0acd_b8e1_58bf_e3c8, + ]), + c1: Fp::from_raw_unchecked([ + 0x8aef_cb98_b15f_8306, + 0x3ea1_108f_e4f2_1d54, + 0xcf79_f69f_a1b7_df3b, + 0xe4f5_4aa1_d16b_1a3c, + 0xba5e_4ef8_6105_a679, + 0x0ed8_6c07_97be_e5cf, + ]), + }, + c2: Fp2 { + c0: Fp::from_raw_unchecked([ + 0xcee5_cb98_b15c_2db4, + 0x7159_1082_d23a_1d51, + 0xd762_30e9_44a1_7ca4, + 0xd19e_3dd3_549d_d5b6, + 0xa972_dc17_01fa_66e3, + 0x12e3_1f2d_d6bd_e7d6, + ]), + c1: Fp::from_raw_unchecked([ + 0xad2a_cb98_b173_2d9d, + 0x2cfd_10dd_0696_1d64, + 0x0739_6b86_c6ef_24e8, + 0xbd76_e2fd_b1bf_c820, + 0x6afe_a7f6_de94_d0d5, + 0x1099_4b0c_5744_c040, + ]), }, - c1: Fp6 { - c0: Fp2 { - c0: Fp::from_raw_unchecked([ - 0x47f9cb98b1b82d58, - 0x5fe911eba3aa1d9d, - 0x96bf1b5f4dd81db3, - 0x8100d27cc9259f5b, - 0xafa20b9674640eab, - 0x9bbcea7d8d9497d, - ]), - c1: Fp::from_raw_unchecked([ - 0x303cb98b1662daa, - 0xd93110aa0a621d5a, - 0xbfa9820c5be4a468, - 0xba3643ecb05a348, - 0xdc3534bb1f1c25a6, - 0x6c305bb19c0e1c1, - ]), - }, - c1: Fp2 { - c0: Fp::from_raw_unchecked([ - 0x46f9cb98b162d858, - 0xbe9109cf7aa1d57, - 0xc791bc55fece41d2, - 0xf84c57704e385ec2, - 0xcb49c1d9c010e60f, - 0xacdb8e158bfe3c8, - ]), - c1: Fp::from_raw_unchecked([ - 0x8aefcb98b15f8306, - 0x3ea1108fe4f21d54, - 0xcf79f69fa1b7df3b, - 0xe4f54aa1d16b1a3c, - 0xba5e4ef86105a679, - 0xed86c0797bee5cf, - ]), - }, - c2: Fp2 { - c0: Fp::from_raw_unchecked([ - 0xcee5cb98b15c2db4, - 0x71591082d23a1d51, - 0xd76230e944a17ca4, - 0xd19e3dd3549dd5b6, - 0xa972dc1701fa66e3, - 0x12e31f2dd6bde7d6, - ]), - c1: Fp::from_raw_unchecked([ - 0xad2acb98b1732d9d, - 0x2cfd10dd06961d64, - 0x7396b86c6ef24e8, - 0xbd76e2fdb1bfc820, - 0x6afea7f6de94d0d5, - 0x10994b0c5744c040, - ]), - }, + }, + c1: Fp6 { + c0: Fp2 { + c0: Fp::from_raw_unchecked([ + 0x47f9_cb98_b1b8_2d58, + 0x5fe9_11eb_a3aa_1d9d, + 0x96bf_1b5f_4dd8_1db3, + 0x8100_d27c_c925_9f5b, + 0xafa2_0b96_7464_0eab, + 0x09bb_cea7_d8d9_497d, + ]), + c1: Fp::from_raw_unchecked([ + 0x0303_cb98_b166_2daa, + 0xd931_10aa_0a62_1d5a, + 0xbfa9_820c_5be4_a468, + 0x0ba3_643e_cb05_a348, + 0xdc35_34bb_1f1c_25a6, + 0x06c3_05bb_19c0_e1c1, + ]), + }, + c1: Fp2 { + c0: Fp::from_raw_unchecked([ + 0x46f9_cb98_b162_d858, + 0x0be9_109c_f7aa_1d57, + 0xc791_bc55_fece_41d2, + 0xf84c_5770_4e38_5ec2, + 0xcb49_c1d9_c010_e60f, + 0x0acd_b8e1_58bf_e3c8, + ]), + c1: Fp::from_raw_unchecked([ + 0x8aef_cb98_b15f_8306, + 0x3ea1_108f_e4f2_1d54, + 0xcf79_f69f_a1b7_df3b, + 0xe4f5_4aa1_d16b_1a3c, + 0xba5e_4ef8_6105_a679, + 0x0ed8_6c07_97be_e5cf, + ]), }, - }; - - let b = Fp12 { - c0: Fp6 { - c0: Fp2 { - c0: Fp::from_raw_unchecked([ - 0x47f9cb98b1b82d58, - 0x5fe911eba3aa1d9d, - 0x96bf1b5f4dd81db3, - 0x8100d272c9259f5b, - 0xafa20b9674640eab, - 0x9bbcea7d8d9497d, - ]), - c1: Fp::from_raw_unchecked([ - 0x303cb98b1662daa, - 0xd93110aa0a621d5a, - 0xbfa9820c5be4a468, - 0xba3643ecb05a348, - 0xdc3534bb1f1c25a6, - 0x6c305bb19c0e1c1, - ]), - }, - c1: Fp2 { - c0: Fp::from_raw_unchecked([ - 0x46f9cb98b162d858, - 0xbe9109cf7aa1d57, - 0xc791bc55fece41d2, - 0xf84c57704e385ec2, - 0xcb49c1d9c010e60f, - 0xacdb8e158bfe348, - ]), - c1: Fp::from_raw_unchecked([ - 0x8aefcb98b15f8306, - 0x3ea1108fe4f21d54, - 0xcf79f69fa1b7df3b, - 0xe4f54aa1d16b1a3c, - 0xba5e4ef86105a679, - 0xed86c0797bee5cf, - ]), - }, - c2: Fp2 { - c0: Fp::from_raw_unchecked([ - 0xcee5cb98b15c2db4, - 0x71591082d23a1d51, - 0xd76230e944a17ca4, - 0xd19e3dd3549dd5b6, - 0xa972dc1701fa66e3, - 0x12e31f2dd6bde7d6, - ]), - c1: Fp::from_raw_unchecked([ - 0xad2acb98b1732d9d, - 0x2cfd10dd06961d64, - 0x7396b86c6ef24e8, - 0xbd76e2fdb1bfc820, - 0x6afea7f6de94d0d5, - 0x10994b0c5744c040, - ]), - }, + c2: Fp2 { + c0: Fp::from_raw_unchecked([ + 0xcee5_cb98_b15c_2db4, + 0x7159_1082_d23a_1d51, + 0xd762_30e9_44a1_7ca4, + 0xd19e_3dd3_549d_d5b6, + 0xa972_dc17_01fa_66e3, + 0x12e3_1f2d_d6bd_e7d6, + ]), + c1: Fp::from_raw_unchecked([ + 0xad2a_cb98_b173_2d9d, + 0x2cfd_10dd_0696_1d64, + 0x0739_6b86_c6ef_24e8, + 0xbd76_e2fd_b1bf_c820, + 0x6afe_a7f6_de94_d0d5, + 0x1099_4b0c_5744_c040, + ]), }, - c1: Fp6 { - c0: Fp2 { - c0: Fp::from_raw_unchecked([ - 0x47f9cb98b1b82d58, - 0x5fe911eba3aa1d9d, - 0x96bf1b5f4dd21db3, - 0x8100d27cc9259f5b, - 0xafa20b9674640eab, - 0x9bbcea7d8d9497d, - ]), - c1: Fp::from_raw_unchecked([ - 0x303cb98b1662daa, - 0xd93110aa0a621d5a, - 0xbfa9820c5be4a468, - 0xba3643ecb05a348, - 0xdc3534bb1f1c25a6, - 0x6c305bb19c0e1c1, - ]), - }, - c1: Fp2 { - c0: Fp::from_raw_unchecked([ - 0x46f9cb98b162d858, - 0xbe9109cf7aa1d57, - 0xc791bc55fece41d2, - 0xf84c57704e385ec2, - 0xcb49c1d9c010e60f, - 0xacdb8e158bfe3c8, - ]), - c1: Fp::from_raw_unchecked([ - 0x8aefcb98b15f8306, - 0x3ea1108fe4f21d54, - 0xcf79f69fa117df3b, - 0xe4f54aa1d16b1a3c, - 0xba5e4ef86105a679, - 0xed86c0797bee5cf, - ]), - }, - c2: Fp2 { - c0: Fp::from_raw_unchecked([ - 0xcee5cb98b15c2db4, - 0x71591082d23a1d51, - 0xd76230e944a17ca4, - 0xd19e3dd3549dd5b6, - 0xa972dc1701fa66e3, - 0x12e31f2dd6bde7d6, - ]), - c1: Fp::from_raw_unchecked([ - 0xad2acb98b1732d9d, - 0x2cfd10dd06961d64, - 0x7396b86c6ef24e8, - 0xbd76e2fdb1bfc820, - 0x6afea7f6de94d0d5, - 0x10994b0c5744c040, - ]), - }, + }, + }; + + let b = Fp12 { + c0: Fp6 { + c0: Fp2 { + c0: Fp::from_raw_unchecked([ + 0x47f9_cb98_b1b8_2d58, + 0x5fe9_11eb_a3aa_1d9d, + 0x96bf_1b5f_4dd8_1db3, + 0x8100_d272_c925_9f5b, + 0xafa2_0b96_7464_0eab, + 0x09bb_cea7_d8d9_497d, + ]), + c1: Fp::from_raw_unchecked([ + 0x0303_cb98_b166_2daa, + 0xd931_10aa_0a62_1d5a, + 0xbfa9_820c_5be4_a468, + 0x0ba3_643e_cb05_a348, + 0xdc35_34bb_1f1c_25a6, + 0x06c3_05bb_19c0_e1c1, + ]), }, - }; - - let c = Fp12 { - c0: Fp6 { - c0: Fp2 { - c0: Fp::from_raw_unchecked([ - 0x47f9cb9871b82d58, - 0x5fe911eba3aa1d9d, - 0x96bf1b5f4dd81db3, - 0x8100d27cc9259f5b, - 0xafa20b9674640eab, - 0x9bbcea7d8d9497d, - ]), - c1: Fp::from_raw_unchecked([ - 0x303cb98b1662daa, - 0xd93110aa0a621d5a, - 0xbfa9820c5be4a468, - 0xba3643ecb05a348, - 0xdc3534bb1f1c25a6, - 0x6c305bb19c0e1c1, - ]), - }, - c1: Fp2 { - c0: Fp::from_raw_unchecked([ - 0x46f9cb98b162d858, - 0xbe9109cf7aa1d57, - 0x7791bc55fece41d2, - 0xf84c57704e385ec2, - 0xcb49c1d9c010e60f, - 0xacdb8e158bfe3c8, - ]), - c1: Fp::from_raw_unchecked([ - 0x8aefcb98b15f8306, - 0x3ea1108fe4f21d54, - 0xcf79f69fa1b7df3b, - 0xe4f54aa1d16b133c, - 0xba5e4ef86105a679, - 0xed86c0797bee5cf, - ]), - }, - c2: Fp2 { - c0: Fp::from_raw_unchecked([ - 0xcee5cb98b15c2db4, - 0x71591082d23a1d51, - 0xd76240e944a17ca4, - 0xd19e3dd3549dd5b6, - 0xa972dc1701fa66e3, - 0x12e31f2dd6bde7d6, - ]), - c1: Fp::from_raw_unchecked([ - 0xad2acb98b1732d9d, - 0x2cfd10dd06961d64, - 0x7396b86c6ef24e8, - 0xbd76e2fdb1bfc820, - 0x6afea7f6de94d0d5, - 0x10994b0c1744c040, - ]), - }, + c1: Fp2 { + c0: Fp::from_raw_unchecked([ + 0x46f9_cb98_b162_d858, + 0x0be9_109c_f7aa_1d57, + 0xc791_bc55_fece_41d2, + 0xf84c_5770_4e38_5ec2, + 0xcb49_c1d9_c010_e60f, + 0x0acd_b8e1_58bf_e348, + ]), + c1: Fp::from_raw_unchecked([ + 0x8aef_cb98_b15f_8306, + 0x3ea1_108f_e4f2_1d54, + 0xcf79_f69f_a1b7_df3b, + 0xe4f5_4aa1_d16b_1a3c, + 0xba5e_4ef8_6105_a679, + 0x0ed8_6c07_97be_e5cf, + ]), }, - c1: Fp6 { - c0: Fp2 { - c0: Fp::from_raw_unchecked([ - 0x47f9cb98b1b82d58, - 0x5fe911eba3aa1d9d, - 0x96bf1b5f4dd81db3, - 0x8100d27cc9259f5b, - 0xafa20b9674640eab, - 0x9bbcea7d8d9497d, - ]), - c1: Fp::from_raw_unchecked([ - 0x303cb98b1662daa, - 0xd93110aa0a621d5a, - 0xbfa9820c5be4a468, - 0xba3643ecb05a348, - 0xdc3534bb1f1c25a6, - 0x6c305bb19c0e1c1, - ]), - }, - c1: Fp2 { - c0: Fp::from_raw_unchecked([ - 0x46f9cb98b162d858, - 0xbe9109cf7aa1d57, - 0xc791bc55fece41d2, - 0xf84c57704e385ec2, - 0xcb49c1d3c010e60f, - 0xacdb8e158bfe3c8, - ]), - c1: Fp::from_raw_unchecked([ - 0x8aefcb98b15f8306, - 0x3ea1108fe4f21d54, - 0xcf79f69fa1b7df3b, - 0xe4f54aa1d16b1a3c, - 0xba5e4ef86105a679, - 0xed86c0797bee5cf, - ]), - }, - c2: Fp2 { - c0: Fp::from_raw_unchecked([ - 0xcee5cb98b15c2db4, - 0x71591082d23a1d51, - 0xd76230e944a17ca4, - 0xd19e3dd3549dd5b6, - 0xa972dc1701fa66e3, - 0x12e31f2dd6bde7d6, - ]), - c1: Fp::from_raw_unchecked([ - 0xad2acb98b1732d9d, - 0x2cfd10dd06961d64, - 0x7396b86c6ef24e8, - 0xbd76e2fdb1bfc820, - 0x6afea7f6de94d0d5, - 0x10994b0c57441040, - ]), - }, + c2: Fp2 { + c0: Fp::from_raw_unchecked([ + 0xcee5_cb98_b15c_2db4, + 0x7159_1082_d23a_1d51, + 0xd762_30e9_44a1_7ca4, + 0xd19e_3dd3_549d_d5b6, + 0xa972_dc17_01fa_66e3, + 0x12e3_1f2d_d6bd_e7d6, + ]), + c1: Fp::from_raw_unchecked([ + 0xad2a_cb98_b173_2d9d, + 0x2cfd_10dd_0696_1d64, + 0x0739_6b86_c6ef_24e8, + 0xbd76_e2fd_b1bf_c820, + 0x6afe_a7f6_de94_d0d5, + 0x1099_4b0c_5744_c040, + ]), }, - }; - - // because a and b and c are similar to each other and - // I was lazy, this is just some arbitrary way to make - // them a little more different - let a = &a.square().invert().unwrap().square() + &c; - let b = &b.square().invert().unwrap().square() + &a; - let c = &c.square().invert().unwrap().square() + &b; - - assert_eq!(a.square(), &a * &a); - assert_eq!(b.square(), &b * &b); - assert_eq!(c.square(), &c * &c); - - assert_eq!( - (a + b) * c.square(), - &(&(&c * &c) * &a) + &(&(&c * &c) * &b) - ); - - assert_eq!( - &a.invert().unwrap() * &b.invert().unwrap(), - (&a * &b).invert().unwrap() - ); - assert_eq!(&a.invert().unwrap() * &a, Fp12::one()); - - assert!(a != a.frobenius_map()); - assert_eq!( - a, - a.frobenius_map() - .frobenius_map() - .frobenius_map() - .frobenius_map() - .frobenius_map() - .frobenius_map() - .frobenius_map() - .frobenius_map() - .frobenius_map() - .frobenius_map() - .frobenius_map() - .frobenius_map() - ); - } + }, + c1: Fp6 { + c0: Fp2 { + c0: Fp::from_raw_unchecked([ + 0x47f9_cb98_b1b8_2d58, + 0x5fe9_11eb_a3aa_1d9d, + 0x96bf_1b5f_4dd2_1db3, + 0x8100_d27c_c925_9f5b, + 0xafa2_0b96_7464_0eab, + 0x09bb_cea7_d8d9_497d, + ]), + c1: Fp::from_raw_unchecked([ + 0x0303_cb98_b166_2daa, + 0xd931_10aa_0a62_1d5a, + 0xbfa9_820c_5be4_a468, + 0x0ba3_643e_cb05_a348, + 0xdc35_34bb_1f1c_25a6, + 0x06c3_05bb_19c0_e1c1, + ]), + }, + c1: Fp2 { + c0: Fp::from_raw_unchecked([ + 0x46f9_cb98_b162_d858, + 0x0be9_109c_f7aa_1d57, + 0xc791_bc55_fece_41d2, + 0xf84c_5770_4e38_5ec2, + 0xcb49_c1d9_c010_e60f, + 0x0acd_b8e1_58bf_e3c8, + ]), + c1: Fp::from_raw_unchecked([ + 0x8aef_cb98_b15f_8306, + 0x3ea1_108f_e4f2_1d54, + 0xcf79_f69f_a117_df3b, + 0xe4f5_4aa1_d16b_1a3c, + 0xba5e_4ef8_6105_a679, + 0x0ed8_6c07_97be_e5cf, + ]), + }, + c2: Fp2 { + c0: Fp::from_raw_unchecked([ + 0xcee5_cb98_b15c_2db4, + 0x7159_1082_d23a_1d51, + 0xd762_30e9_44a1_7ca4, + 0xd19e_3dd3_549d_d5b6, + 0xa972_dc17_01fa_66e3, + 0x12e3_1f2d_d6bd_e7d6, + ]), + c1: Fp::from_raw_unchecked([ + 0xad2a_cb98_b173_2d9d, + 0x2cfd_10dd_0696_1d64, + 0x0739_6b86_c6ef_24e8, + 0xbd76_e2fd_b1bf_c820, + 0x6afe_a7f6_de94_d0d5, + 0x1099_4b0c_5744_c040, + ]), + }, + }, + }; + + let c = Fp12 { + c0: Fp6 { + c0: Fp2 { + c0: Fp::from_raw_unchecked([ + 0x47f9_cb98_71b8_2d58, + 0x5fe9_11eb_a3aa_1d9d, + 0x96bf_1b5f_4dd8_1db3, + 0x8100_d27c_c925_9f5b, + 0xafa2_0b96_7464_0eab, + 0x09bb_cea7_d8d9_497d, + ]), + c1: Fp::from_raw_unchecked([ + 0x0303_cb98_b166_2daa, + 0xd931_10aa_0a62_1d5a, + 0xbfa9_820c_5be4_a468, + 0x0ba3_643e_cb05_a348, + 0xdc35_34bb_1f1c_25a6, + 0x06c3_05bb_19c0_e1c1, + ]), + }, + c1: Fp2 { + c0: Fp::from_raw_unchecked([ + 0x46f9_cb98_b162_d858, + 0x0be9_109c_f7aa_1d57, + 0x7791_bc55_fece_41d2, + 0xf84c_5770_4e38_5ec2, + 0xcb49_c1d9_c010_e60f, + 0x0acd_b8e1_58bf_e3c8, + ]), + c1: Fp::from_raw_unchecked([ + 0x8aef_cb98_b15f_8306, + 0x3ea1_108f_e4f2_1d54, + 0xcf79_f69f_a1b7_df3b, + 0xe4f5_4aa1_d16b_133c, + 0xba5e_4ef8_6105_a679, + 0x0ed8_6c07_97be_e5cf, + ]), + }, + c2: Fp2 { + c0: Fp::from_raw_unchecked([ + 0xcee5_cb98_b15c_2db4, + 0x7159_1082_d23a_1d51, + 0xd762_40e9_44a1_7ca4, + 0xd19e_3dd3_549d_d5b6, + 0xa972_dc17_01fa_66e3, + 0x12e3_1f2d_d6bd_e7d6, + ]), + c1: Fp::from_raw_unchecked([ + 0xad2a_cb98_b173_2d9d, + 0x2cfd_10dd_0696_1d64, + 0x0739_6b86_c6ef_24e8, + 0xbd76_e2fd_b1bf_c820, + 0x6afe_a7f6_de94_d0d5, + 0x1099_4b0c_1744_c040, + ]), + }, + }, + c1: Fp6 { + c0: Fp2 { + c0: Fp::from_raw_unchecked([ + 0x47f9_cb98_b1b8_2d58, + 0x5fe9_11eb_a3aa_1d9d, + 0x96bf_1b5f_4dd8_1db3, + 0x8100_d27c_c925_9f5b, + 0xafa2_0b96_7464_0eab, + 0x09bb_cea7_d8d9_497d, + ]), + c1: Fp::from_raw_unchecked([ + 0x0303_cb98_b166_2daa, + 0xd931_10aa_0a62_1d5a, + 0xbfa9_820c_5be4_a468, + 0x0ba3_643e_cb05_a348, + 0xdc35_34bb_1f1c_25a6, + 0x06c3_05bb_19c0_e1c1, + ]), + }, + c1: Fp2 { + c0: Fp::from_raw_unchecked([ + 0x46f9_cb98_b162_d858, + 0x0be9_109c_f7aa_1d57, + 0xc791_bc55_fece_41d2, + 0xf84c_5770_4e38_5ec2, + 0xcb49_c1d3_c010_e60f, + 0x0acd_b8e1_58bf_e3c8, + ]), + c1: Fp::from_raw_unchecked([ + 0x8aef_cb98_b15f_8306, + 0x3ea1_108f_e4f2_1d54, + 0xcf79_f69f_a1b7_df3b, + 0xe4f5_4aa1_d16b_1a3c, + 0xba5e_4ef8_6105_a679, + 0x0ed8_6c07_97be_e5cf, + ]), + }, + c2: Fp2 { + c0: Fp::from_raw_unchecked([ + 0xcee5_cb98_b15c_2db4, + 0x7159_1082_d23a_1d51, + 0xd762_30e9_44a1_7ca4, + 0xd19e_3dd3_549d_d5b6, + 0xa972_dc17_01fa_66e3, + 0x12e3_1f2d_d6bd_e7d6, + ]), + c1: Fp::from_raw_unchecked([ + 0xad2a_cb98_b173_2d9d, + 0x2cfd_10dd_0696_1d64, + 0x0739_6b86_c6ef_24e8, + 0xbd76_e2fd_b1bf_c820, + 0x6afe_a7f6_de94_d0d5, + 0x1099_4b0c_5744_1040, + ]), + }, + }, + }; + + // because a and b and c are similar to each other and + // I was lazy, this is just some arbitrary way to make + // them a little more different + let a = &a.square().invert().unwrap().square() + &c; + let b = &b.square().invert().unwrap().square() + &a; + let c = &c.square().invert().unwrap().square() + &b; + + assert_eq!(a.square(), &a * &a); + assert_eq!(b.square(), &b * &b); + assert_eq!(c.square(), &c * &c); + + assert_eq!( + (a + b) * c.square(), + &(&(&c * &c) * &a) + &(&(&c * &c) * &b) + ); + + assert_eq!( + &a.invert().unwrap() * &b.invert().unwrap(), + (&a * &b).invert().unwrap() + ); + assert_eq!(&a.invert().unwrap() * &a, Fp12::one()); + + assert!(a != a.frobenius_map()); + assert_eq!( + a, + a.frobenius_map() + .frobenius_map() + .frobenius_map() + .frobenius_map() + .frobenius_map() + .frobenius_map() + .frobenius_map() + .frobenius_map() + .frobenius_map() + .frobenius_map() + .frobenius_map() + .frobenius_map() + ); } diff --git a/src/fp2.rs b/src/fp2.rs index a6af1f65..f1facc32 100644 --- a/src/fp2.rs +++ b/src/fp2.rs @@ -4,11 +4,6 @@ use core::fmt; use core::ops::{Add, AddAssign, Mul, MulAssign, Neg, Sub, SubAssign}; use subtle::{Choice, ConditionallySelectable, ConstantTimeEq, CtOption}; -#[cfg(feature = "serde_req")] -use serde::{ - self, de::Visitor, ser::SerializeStruct, Deserialize, Deserializer, Serialize, Serializer, -}; - #[cfg(feature = "rkyv-impl")] use bytecheck::CheckBytes; #[cfg(feature = "rkyv-impl")] @@ -59,91 +54,6 @@ impl PartialEq for Fp2 { } } -#[cfg(feature = "serde_req")] -impl Serialize for Fp2 { - fn serialize(&self, serializer: S) -> Result - where - S: Serializer, - { - let mut fp2 = serializer.serialize_struct("struct Fp2", 2)?; - fp2.serialize_field("c0", &self.c0)?; - fp2.serialize_field("c1", &self.c1)?; - fp2.end() - } -} - -#[cfg(feature = "serde_req")] -impl<'de> Deserialize<'de> for Fp2 { - fn deserialize(deserializer: D) -> Result - where - D: Deserializer<'de>, - { - enum Field { - C0, - C1, - } - - impl<'de> Deserialize<'de> for Field { - fn deserialize(deserializer: D) -> Result - where - D: Deserializer<'de>, - { - struct FieldVisitor; - - impl<'de> Visitor<'de> for FieldVisitor { - type Value = Field; - - fn expecting( - &self, - formatter: &mut ::core::fmt::Formatter, - ) -> ::core::fmt::Result { - formatter.write_str("struct Fp2") - } - - fn visit_str(self, value: &str) -> Result - where - E: serde::de::Error, - { - match value { - "c0" => Ok(Field::C0), - "c1" => Ok(Field::C1), - _ => Err(serde::de::Error::unknown_field(value, FIELDS)), - } - } - } - - deserializer.deserialize_identifier(FieldVisitor) - } - } - - struct Fp2Visitor; - - impl<'de> Visitor<'de> for Fp2Visitor { - type Value = Fp2; - - fn expecting(&self, formatter: &mut ::core::fmt::Formatter) -> ::core::fmt::Result { - formatter.write_str("struct Fp2") - } - - fn visit_seq(self, mut seq: V) -> Result - where - V: serde::de::SeqAccess<'de>, - { - let c0 = seq - .next_element()? - .ok_or_else(|| serde::de::Error::invalid_length(0, &self))?; - let c1 = seq - .next_element()? - .ok_or_else(|| serde::de::Error::invalid_length(0, &self))?; - Ok(Fp2 { c0, c1 }) - } - } - - const FIELDS: &[&str] = &["c0", "c1"]; - deserializer.deserialize_struct("Fp2", FIELDS, Fp2Visitor) - } -} - impl ConditionallySelectable for Fp2 { fn conditional_select(a: &Self, b: &Self, choice: Choice) -> Self { Fp2 { @@ -335,12 +245,12 @@ impl Fp2 { CtOption::new(Fp2::zero(), self.is_zero()).or_else(|| { // a1 = self^((p - 3) / 4) let a1 = self.pow_vartime(&[ - 0xee7fbfffffffeaaa, - 0x7aaffffac54ffff, - 0xd9cc34a83dac3d89, - 0xd91dd2e13ce144af, - 0x92c6e9ed90d2eb35, - 0x680447a8e5ff9a6, + 0xee7f_bfff_ffff_eaaa, + 0x07aa_ffff_ac54_ffff, + 0xd9cc_34a8_3dac_3d89, + 0xd91d_d2e1_3ce1_44af, + 0x92c6_e9ed_90d2_eb35, + 0x0680_447a_8e5f_f9a6, ]); // alpha = a1^2 * self = self^((p - 3) / 2 + 1) = self^((p - 1) / 2) @@ -364,12 +274,12 @@ impl Fp2 { .or_else(|| { CtOption::new( (alpha + Fp2::one()).pow_vartime(&[ - 0xdcff7fffffffd555, - 0xf55ffff58a9ffff, - 0xb39869507b587b12, - 0xb23ba5c279c2895f, - 0x258dd3db21a5d66b, - 0xd0088f51cbff34d, + 0xdcff_7fff_ffff_d555, + 0x0f55_ffff_58a9_ffff, + 0xb398_6950_7b58_7b12, + 0xb23b_a5c2_79c2_895f, + 0x258d_d3db_21a5_d66b, + 0x0d00_88f5_1cbf_f34d, ]) * x0, Choice::from(1), ) @@ -422,545 +332,631 @@ impl Fp2 { } } -#[cfg(test)] -mod tests { - use super::*; +#[test] +fn test_conditional_selection() { + let a = Fp2 { + c0: Fp::from_raw_unchecked([1, 2, 3, 4, 5, 6]), + c1: Fp::from_raw_unchecked([7, 8, 9, 10, 11, 12]), + }; + let b = Fp2 { + c0: Fp::from_raw_unchecked([13, 14, 15, 16, 17, 18]), + c1: Fp::from_raw_unchecked([19, 20, 21, 22, 23, 24]), + }; + + assert_eq!( + ConditionallySelectable::conditional_select(&a, &b, Choice::from(0u8)), + a + ); + assert_eq!( + ConditionallySelectable::conditional_select(&a, &b, Choice::from(1u8)), + b + ); +} - #[test] - fn test_conditional_selection() { - let a = Fp2 { - c0: Fp::from_raw_unchecked([1, 2, 3, 4, 5, 6]), - c1: Fp::from_raw_unchecked([7, 8, 9, 10, 11, 12]), - }; - let b = Fp2 { - c0: Fp::from_raw_unchecked([13, 14, 15, 16, 17, 18]), - c1: Fp::from_raw_unchecked([19, 20, 21, 22, 23, 24]), - }; +#[test] +fn test_equality() { + fn is_equal(a: &Fp2, b: &Fp2) -> bool { + let eq = a == b; + let ct_eq = a.ct_eq(&b); - assert_eq!( - ConditionallySelectable::conditional_select(&a, &b, Choice::from(0u8)), - a - ); - assert_eq!( - ConditionallySelectable::conditional_select(&a, &b, Choice::from(1u8)), - b - ); + assert_eq!(eq, ct_eq.unwrap_u8() == 1); + + eq } - #[test] - fn test_equality() { - fn is_equal(a: &Fp2, b: &Fp2) -> bool { - let eq = a == b; - let ct_eq = a.ct_eq(&b); + assert!(is_equal( + &Fp2 { + c0: Fp::from_raw_unchecked([1, 2, 3, 4, 5, 6]), + c1: Fp::from_raw_unchecked([7, 8, 9, 10, 11, 12]), + }, + &Fp2 { + c0: Fp::from_raw_unchecked([1, 2, 3, 4, 5, 6]), + c1: Fp::from_raw_unchecked([7, 8, 9, 10, 11, 12]), + } + )); - assert_eq!(eq, ct_eq.unwrap_u8() == 1); + assert!(!is_equal( + &Fp2 { + c0: Fp::from_raw_unchecked([2, 2, 3, 4, 5, 6]), + c1: Fp::from_raw_unchecked([7, 8, 9, 10, 11, 12]), + }, + &Fp2 { + c0: Fp::from_raw_unchecked([1, 2, 3, 4, 5, 6]), + c1: Fp::from_raw_unchecked([7, 8, 9, 10, 11, 12]), + } + )); - eq + assert!(!is_equal( + &Fp2 { + c0: Fp::from_raw_unchecked([1, 2, 3, 4, 5, 6]), + c1: Fp::from_raw_unchecked([2, 8, 9, 10, 11, 12]), + }, + &Fp2 { + c0: Fp::from_raw_unchecked([1, 2, 3, 4, 5, 6]), + c1: Fp::from_raw_unchecked([7, 8, 9, 10, 11, 12]), } + )); +} - assert!(is_equal( - &Fp2 { - c0: Fp::from_raw_unchecked([1, 2, 3, 4, 5, 6]), - c1: Fp::from_raw_unchecked([7, 8, 9, 10, 11, 12]), - }, - &Fp2 { - c0: Fp::from_raw_unchecked([1, 2, 3, 4, 5, 6]), - c1: Fp::from_raw_unchecked([7, 8, 9, 10, 11, 12]), - } - )); - - assert!(!is_equal( - &Fp2 { - c0: Fp::from_raw_unchecked([2, 2, 3, 4, 5, 6]), - c1: Fp::from_raw_unchecked([7, 8, 9, 10, 11, 12]), - }, - &Fp2 { - c0: Fp::from_raw_unchecked([1, 2, 3, 4, 5, 6]), - c1: Fp::from_raw_unchecked([7, 8, 9, 10, 11, 12]), - } - )); - - assert!(!is_equal( - &Fp2 { - c0: Fp::from_raw_unchecked([1, 2, 3, 4, 5, 6]), - c1: Fp::from_raw_unchecked([2, 8, 9, 10, 11, 12]), - }, - &Fp2 { - c0: Fp::from_raw_unchecked([1, 2, 3, 4, 5, 6]), - c1: Fp::from_raw_unchecked([7, 8, 9, 10, 11, 12]), - } - )); - } +#[test] +fn test_squaring() { + let a = Fp2 { + c0: Fp::from_raw_unchecked([ + 0xc9a2_1831_63ee_70d4, + 0xbc37_70a7_196b_5c91, + 0xa247_f8c1_304c_5f44, + 0xb01f_c2a3_726c_80b5, + 0xe1d2_93e5_bbd9_19c9, + 0x04b7_8e80_020e_f2ca, + ]), + c1: Fp::from_raw_unchecked([ + 0x952e_a446_0462_618f, + 0x238d_5edd_f025_c62f, + 0xf6c9_4b01_2ea9_2e72, + 0x03ce_24ea_c1c9_3808, + 0x0559_50f9_45da_483c, + 0x010a_768d_0df4_eabc, + ]), + }; + let b = Fp2 { + c0: Fp::from_raw_unchecked([ + 0xa1e0_9175_a4d2_c1fe, + 0x8b33_acfc_204e_ff12, + 0xe244_15a1_1b45_6e42, + 0x61d9_96b1_b6ee_1936, + 0x1164_dbe8_667c_853c, + 0x0788_557a_cc7d_9c79, + ]), + c1: Fp::from_raw_unchecked([ + 0xda6a_87cc_6f48_fa36, + 0x0fc7_b488_277c_1903, + 0x9445_ac4a_dc44_8187, + 0x0261_6d5b_c909_9209, + 0xdbed_4677_2db5_8d48, + 0x11b9_4d50_76c7_b7b1, + ]), + }; + + assert_eq!(a.square(), b); +} - #[test] - fn test_squaring() { - let a = Fp2 { - c0: Fp::from_raw_unchecked([ - 0xc9a2183163ee70d4, - 0xbc3770a7196b5c91, - 0xa247f8c1304c5f44, - 0xb01fc2a3726c80b5, - 0xe1d293e5bbd919c9, - 0x4b78e80020ef2ca, - ]), - c1: Fp::from_raw_unchecked([ - 0x952ea4460462618f, - 0x238d5eddf025c62f, - 0xf6c94b012ea92e72, - 0x3ce24eac1c93808, - 0x55950f945da483c, - 0x10a768d0df4eabc, - ]), - }; - let b = Fp2 { - c0: Fp::from_raw_unchecked([ - 0xa1e09175a4d2c1fe, - 0x8b33acfc204eff12, - 0xe24415a11b456e42, - 0x61d996b1b6ee1936, - 0x1164dbe8667c853c, - 0x788557acc7d9c79, - ]), - c1: Fp::from_raw_unchecked([ - 0xda6a87cc6f48fa36, - 0xfc7b488277c1903, - 0x9445ac4adc448187, - 0x2616d5bc9099209, - 0xdbed46772db58d48, - 0x11b94d5076c7b7b1, - ]), - }; +#[test] +fn test_multiplication() { + let a = Fp2 { + c0: Fp::from_raw_unchecked([ + 0xc9a2_1831_63ee_70d4, + 0xbc37_70a7_196b_5c91, + 0xa247_f8c1_304c_5f44, + 0xb01f_c2a3_726c_80b5, + 0xe1d2_93e5_bbd9_19c9, + 0x04b7_8e80_020e_f2ca, + ]), + c1: Fp::from_raw_unchecked([ + 0x952e_a446_0462_618f, + 0x238d_5edd_f025_c62f, + 0xf6c9_4b01_2ea9_2e72, + 0x03ce_24ea_c1c9_3808, + 0x0559_50f9_45da_483c, + 0x010a_768d_0df4_eabc, + ]), + }; + let b = Fp2 { + c0: Fp::from_raw_unchecked([ + 0xa1e0_9175_a4d2_c1fe, + 0x8b33_acfc_204e_ff12, + 0xe244_15a1_1b45_6e42, + 0x61d9_96b1_b6ee_1936, + 0x1164_dbe8_667c_853c, + 0x0788_557a_cc7d_9c79, + ]), + c1: Fp::from_raw_unchecked([ + 0xda6a_87cc_6f48_fa36, + 0x0fc7_b488_277c_1903, + 0x9445_ac4a_dc44_8187, + 0x0261_6d5b_c909_9209, + 0xdbed_4677_2db5_8d48, + 0x11b9_4d50_76c7_b7b1, + ]), + }; + let c = Fp2 { + c0: Fp::from_raw_unchecked([ + 0xf597_483e_27b4_e0f7, + 0x610f_badf_811d_ae5f, + 0x8432_af91_7714_327a, + 0x6a9a_9603_cf88_f09e, + 0xf05a_7bf8_bad0_eb01, + 0x0954_9131_c003_ffae, + ]), + c1: Fp::from_raw_unchecked([ + 0x963b_02d0_f93d_37cd, + 0xc95c_e1cd_b30a_73d4, + 0x3087_25fa_3126_f9b8, + 0x56da_3c16_7fab_0d50, + 0x6b50_86b5_f4b6_d6af, + 0x09c3_9f06_2f18_e9f2, + ]), + }; + + assert_eq!(a * b, c); +} - assert_eq!(a.square(), b); - } +#[test] +fn test_addition() { + let a = Fp2 { + c0: Fp::from_raw_unchecked([ + 0xc9a2_1831_63ee_70d4, + 0xbc37_70a7_196b_5c91, + 0xa247_f8c1_304c_5f44, + 0xb01f_c2a3_726c_80b5, + 0xe1d2_93e5_bbd9_19c9, + 0x04b7_8e80_020e_f2ca, + ]), + c1: Fp::from_raw_unchecked([ + 0x952e_a446_0462_618f, + 0x238d_5edd_f025_c62f, + 0xf6c9_4b01_2ea9_2e72, + 0x03ce_24ea_c1c9_3808, + 0x0559_50f9_45da_483c, + 0x010a_768d_0df4_eabc, + ]), + }; + let b = Fp2 { + c0: Fp::from_raw_unchecked([ + 0xa1e0_9175_a4d2_c1fe, + 0x8b33_acfc_204e_ff12, + 0xe244_15a1_1b45_6e42, + 0x61d9_96b1_b6ee_1936, + 0x1164_dbe8_667c_853c, + 0x0788_557a_cc7d_9c79, + ]), + c1: Fp::from_raw_unchecked([ + 0xda6a_87cc_6f48_fa36, + 0x0fc7_b488_277c_1903, + 0x9445_ac4a_dc44_8187, + 0x0261_6d5b_c909_9209, + 0xdbed_4677_2db5_8d48, + 0x11b9_4d50_76c7_b7b1, + ]), + }; + let c = Fp2 { + c0: Fp::from_raw_unchecked([ + 0x6b82_a9a7_08c1_32d2, + 0x476b_1da3_39ba_5ba4, + 0x848c_0e62_4b91_cd87, + 0x11f9_5955_295a_99ec, + 0xf337_6fce_2255_9f06, + 0x0c3f_e3fa_ce8c_8f43, + ]), + c1: Fp::from_raw_unchecked([ + 0x6f99_2c12_73ab_5bc5, + 0x3355_1366_17a1_df33, + 0x8b0e_f74c_0aed_aff9, + 0x062f_9246_8ad2_ca12, + 0xe146_9770_738f_d584, + 0x12c3_c3dd_84bc_a26d, + ]), + }; + + assert_eq!(a + b, c); +} - #[test] - fn test_multiplication() { - let a = Fp2 { - c0: Fp::from_raw_unchecked([ - 0xc9a2183163ee70d4, - 0xbc3770a7196b5c91, - 0xa247f8c1304c5f44, - 0xb01fc2a3726c80b5, - 0xe1d293e5bbd919c9, - 0x4b78e80020ef2ca, - ]), - c1: Fp::from_raw_unchecked([ - 0x952ea4460462618f, - 0x238d5eddf025c62f, - 0xf6c94b012ea92e72, - 0x3ce24eac1c93808, - 0x55950f945da483c, - 0x10a768d0df4eabc, - ]), - }; - let b = Fp2 { - c0: Fp::from_raw_unchecked([ - 0xa1e09175a4d2c1fe, - 0x8b33acfc204eff12, - 0xe24415a11b456e42, - 0x61d996b1b6ee1936, - 0x1164dbe8667c853c, - 0x788557acc7d9c79, - ]), - c1: Fp::from_raw_unchecked([ - 0xda6a87cc6f48fa36, - 0xfc7b488277c1903, - 0x9445ac4adc448187, - 0x2616d5bc9099209, - 0xdbed46772db58d48, - 0x11b94d5076c7b7b1, - ]), - }; - let c = Fp2 { - c0: Fp::from_raw_unchecked([ - 0xf597483e27b4e0f7, - 0x610fbadf811dae5f, - 0x8432af917714327a, - 0x6a9a9603cf88f09e, - 0xf05a7bf8bad0eb01, - 0x9549131c003ffae, - ]), - c1: Fp::from_raw_unchecked([ - 0x963b02d0f93d37cd, - 0xc95ce1cdb30a73d4, - 0x308725fa3126f9b8, - 0x56da3c167fab0d50, - 0x6b5086b5f4b6d6af, - 0x9c39f062f18e9f2, - ]), - }; +#[test] +fn test_subtraction() { + let a = Fp2 { + c0: Fp::from_raw_unchecked([ + 0xc9a2_1831_63ee_70d4, + 0xbc37_70a7_196b_5c91, + 0xa247_f8c1_304c_5f44, + 0xb01f_c2a3_726c_80b5, + 0xe1d2_93e5_bbd9_19c9, + 0x04b7_8e80_020e_f2ca, + ]), + c1: Fp::from_raw_unchecked([ + 0x952e_a446_0462_618f, + 0x238d_5edd_f025_c62f, + 0xf6c9_4b01_2ea9_2e72, + 0x03ce_24ea_c1c9_3808, + 0x0559_50f9_45da_483c, + 0x010a_768d_0df4_eabc, + ]), + }; + let b = Fp2 { + c0: Fp::from_raw_unchecked([ + 0xa1e0_9175_a4d2_c1fe, + 0x8b33_acfc_204e_ff12, + 0xe244_15a1_1b45_6e42, + 0x61d9_96b1_b6ee_1936, + 0x1164_dbe8_667c_853c, + 0x0788_557a_cc7d_9c79, + ]), + c1: Fp::from_raw_unchecked([ + 0xda6a_87cc_6f48_fa36, + 0x0fc7_b488_277c_1903, + 0x9445_ac4a_dc44_8187, + 0x0261_6d5b_c909_9209, + 0xdbed_4677_2db5_8d48, + 0x11b9_4d50_76c7_b7b1, + ]), + }; + let c = Fp2 { + c0: Fp::from_raw_unchecked([ + 0xe1c0_86bb_bf1b_5981, + 0x4faf_c3a9_aa70_5d7e, + 0x2734_b5c1_0bb7_e726, + 0xb2bd_7776_af03_7a3e, + 0x1b89_5fb3_98a8_4164, + 0x1730_4aef_6f11_3cec, + ]), + c1: Fp::from_raw_unchecked([ + 0x74c3_1c79_9519_1204, + 0x3271_aa54_79fd_ad2b, + 0xc9b4_7157_4915_a30f, + 0x65e4_0313_ec44_b8be, + 0x7487_b238_5b70_67cb, + 0x0952_3b26_d0ad_19a4, + ]), + }; + + assert_eq!(a - b, c); +} - assert_eq!(a * b, c); - } +#[test] +fn test_negation() { + let a = Fp2 { + c0: Fp::from_raw_unchecked([ + 0xc9a2_1831_63ee_70d4, + 0xbc37_70a7_196b_5c91, + 0xa247_f8c1_304c_5f44, + 0xb01f_c2a3_726c_80b5, + 0xe1d2_93e5_bbd9_19c9, + 0x04b7_8e80_020e_f2ca, + ]), + c1: Fp::from_raw_unchecked([ + 0x952e_a446_0462_618f, + 0x238d_5edd_f025_c62f, + 0xf6c9_4b01_2ea9_2e72, + 0x03ce_24ea_c1c9_3808, + 0x0559_50f9_45da_483c, + 0x010a_768d_0df4_eabc, + ]), + }; + let b = Fp2 { + c0: Fp::from_raw_unchecked([ + 0xf05c_e7ce_9c11_39d7, + 0x6274_8f57_97e8_a36d, + 0xc4e8_d9df_c664_96df, + 0xb457_88e1_8118_9209, + 0x6949_13d0_8772_930d, + 0x1549_836a_3770_f3cf, + ]), + c1: Fp::from_raw_unchecked([ + 0x24d0_5bb9_fb9d_491c, + 0xfb1e_a120_c12e_39d0, + 0x7067_879f_c807_c7b1, + 0x60a9_269a_31bb_dab6, + 0x45c2_56bc_fd71_649b, + 0x18f6_9b5d_2b8a_fbde, + ]), + }; + + assert_eq!(-a, b); +} - #[test] - fn test_addition() { - let a = Fp2 { - c0: Fp::from_raw_unchecked([ - 0xc9a2183163ee70d4, - 0xbc3770a7196b5c91, - 0xa247f8c1304c5f44, - 0xb01fc2a3726c80b5, - 0xe1d293e5bbd919c9, - 0x4b78e80020ef2ca, - ]), - c1: Fp::from_raw_unchecked([ - 0x952ea4460462618f, - 0x238d5eddf025c62f, - 0xf6c94b012ea92e72, - 0x3ce24eac1c93808, - 0x55950f945da483c, - 0x10a768d0df4eabc, - ]), - }; - let b = Fp2 { - c0: Fp::from_raw_unchecked([ - 0xa1e09175a4d2c1fe, - 0x8b33acfc204eff12, - 0xe24415a11b456e42, - 0x61d996b1b6ee1936, - 0x1164dbe8667c853c, - 0x788557acc7d9c79, - ]), - c1: Fp::from_raw_unchecked([ - 0xda6a87cc6f48fa36, - 0xfc7b488277c1903, - 0x9445ac4adc448187, - 0x2616d5bc9099209, - 0xdbed46772db58d48, - 0x11b94d5076c7b7b1, - ]), - }; - let c = Fp2 { +#[test] +fn test_sqrt() { + // a = 1488924004771393321054797166853618474668089414631333405711627789629391903630694737978065425271543178763948256226639*u + 784063022264861764559335808165825052288770346101304131934508881646553551234697082295473567906267937225174620141295 + let a = Fp2 { + c0: Fp::from_raw_unchecked([ + 0x2bee_d146_27d7_f9e9, + 0xb661_4e06_660e_5dce, + 0x06c4_cc7c_2f91_d42c, + 0x996d_7847_4b7a_63cc, + 0xebae_bc4c_820d_574e, + 0x1886_5e12_d93f_d845, + ]), + c1: Fp::from_raw_unchecked([ + 0x7d82_8664_baf4_f566, + 0xd17e_6639_96ec_7339, + 0x679e_ad55_cb40_78d0, + 0xfe3b_2260_e001_ec28, + 0x3059_93d0_43d9_1b68, + 0x0626_f03c_0489_b72d, + ]), + }; + + assert_eq!(a.sqrt().unwrap().square(), a); + + // b = 5, which is a generator of the p - 1 order + // multiplicative subgroup + let b = Fp2 { + c0: Fp::from_raw_unchecked([ + 0x6631_0000_0010_5545, + 0x2114_0040_0eec_000d, + 0x3fa7_af30_c820_e316, + 0xc52a_8b8d_6387_695d, + 0x9fb4_e61d_1e83_eac5, + 0x005c_b922_afe8_4dc7, + ]), + c1: Fp::zero(), + }; + + assert_eq!(b.sqrt().unwrap().square(), b); + + // c = 25, which is a generator of the (p - 1) / 2 order + // multiplicative subgroup + let c = Fp2 { + c0: Fp::from_raw_unchecked([ + 0x44f6_0000_0051_ffae, + 0x86b8_0141_9948_0043, + 0xd715_9952_f1f3_794a, + 0x755d_6e3d_fe1f_fc12, + 0xd36c_d6db_5547_e905, + 0x02f8_c8ec_bf18_67bb, + ]), + c1: Fp::zero(), + }; + + assert_eq!(c.sqrt().unwrap().square(), c); + + // 2155129644831861015726826462986972654175647013268275306775721078997042729172900466542651176384766902407257452753362*u + 2796889544896299244102912275102369318775038861758288697415827248356648685135290329705805931514906495247464901062529 + // is nonsquare. + assert!(bool::from( + Fp2 { c0: Fp::from_raw_unchecked([ - 0x6b82a9a708c132d2, - 0x476b1da339ba5ba4, - 0x848c0e624b91cd87, - 0x11f95955295a99ec, - 0xf3376fce22559f06, - 0xc3fe3face8c8f43, + 0xc5fa_1bc8_fd00_d7f6, + 0x3830_ca45_4606_003b, + 0x2b28_7f11_04b1_02da, + 0xa7fb_30f2_8230_f23e, + 0x339c_db9e_e953_dbf0, + 0x0d78_ec51_d989_fc57, ]), c1: Fp::from_raw_unchecked([ - 0x6f992c1273ab5bc5, - 0x3355136617a1df33, - 0x8b0ef74c0aedaff9, - 0x62f92468ad2ca12, - 0xe1469770738fd584, - 0x12c3c3dd84bca26d, - ]), - }; + 0x27ec_4898_cf87_f613, + 0x9de1_394e_1abb_05a5, + 0x0947_f85d_c170_fc14, + 0x586f_bc69_6b61_14b7, + 0x2b34_75a4_077d_7169, + 0x13e1_c895_cc4b_6c22, + ]) + } + .sqrt() + .is_none() + )); +} - assert_eq!(a + b, c); - } +#[test] +fn test_inversion() { + let a = Fp2 { + c0: Fp::from_raw_unchecked([ + 0x1128_ecad_6754_9455, + 0x9e7a_1cff_3a4e_a1a8, + 0xeb20_8d51_e08b_cf27, + 0xe98a_d408_11f5_fc2b, + 0x736c_3a59_232d_511d, + 0x10ac_d42d_29cf_cbb6, + ]), + c1: Fp::from_raw_unchecked([ + 0xd328_e37c_c2f5_8d41, + 0x948d_f085_8a60_5869, + 0x6032_f9d5_6f93_a573, + 0x2be4_83ef_3fff_dc87, + 0x30ef_61f8_8f48_3c2a, + 0x1333_f55a_3572_5be0, + ]), + }; + + let b = Fp2 { + c0: Fp::from_raw_unchecked([ + 0x0581_a133_3d4f_48a6, + 0x5824_2f6e_f074_8500, + 0x0292_c955_349e_6da5, + 0xba37_721d_dd95_fcd0, + 0x70d1_6790_3aa5_dfc5, + 0x1189_5e11_8b58_a9d5, + ]), + c1: Fp::from_raw_unchecked([ + 0x0eda_09d2_d7a8_5d17, + 0x8808_e137_a7d1_a2cf, + 0x43ae_2625_c1ff_21db, + 0xf85a_c9fd_f7a7_4c64, + 0x8fcc_dda5_b8da_9738, + 0x08e8_4f0c_b32c_d17d, + ]), + }; + + assert_eq!(a.invert().unwrap(), b); + + assert!(bool::from(Fp2::zero().invert().is_none())); +} - #[test] - fn test_subtraction() { - let a = Fp2 { +#[test] +fn test_lexicographic_largest() { + assert!(!bool::from(Fp2::zero().lexicographically_largest())); + assert!(!bool::from(Fp2::one().lexicographically_largest())); + assert!(bool::from( + Fp2 { c0: Fp::from_raw_unchecked([ - 0xc9a2183163ee70d4, - 0xbc3770a7196b5c91, - 0xa247f8c1304c5f44, - 0xb01fc2a3726c80b5, - 0xe1d293e5bbd919c9, - 0x4b78e80020ef2ca, + 0x1128_ecad_6754_9455, + 0x9e7a_1cff_3a4e_a1a8, + 0xeb20_8d51_e08b_cf27, + 0xe98a_d408_11f5_fc2b, + 0x736c_3a59_232d_511d, + 0x10ac_d42d_29cf_cbb6, ]), c1: Fp::from_raw_unchecked([ - 0x952ea4460462618f, - 0x238d5eddf025c62f, - 0xf6c94b012ea92e72, - 0x3ce24eac1c93808, - 0x55950f945da483c, - 0x10a768d0df4eabc, + 0xd328_e37c_c2f5_8d41, + 0x948d_f085_8a60_5869, + 0x6032_f9d5_6f93_a573, + 0x2be4_83ef_3fff_dc87, + 0x30ef_61f8_8f48_3c2a, + 0x1333_f55a_3572_5be0, ]), - }; - let b = Fp2 { - c0: Fp::from_raw_unchecked([ - 0xa1e09175a4d2c1fe, - 0x8b33acfc204eff12, - 0xe24415a11b456e42, - 0x61d996b1b6ee1936, - 0x1164dbe8667c853c, - 0x788557acc7d9c79, - ]), - c1: Fp::from_raw_unchecked([ - 0xda6a87cc6f48fa36, - 0xfc7b488277c1903, - 0x9445ac4adc448187, - 0x2616d5bc9099209, - 0xdbed46772db58d48, - 0x11b94d5076c7b7b1, + } + .lexicographically_largest() + )); + assert!(!bool::from( + Fp2 { + c0: -Fp::from_raw_unchecked([ + 0x1128_ecad_6754_9455, + 0x9e7a_1cff_3a4e_a1a8, + 0xeb20_8d51_e08b_cf27, + 0xe98a_d408_11f5_fc2b, + 0x736c_3a59_232d_511d, + 0x10ac_d42d_29cf_cbb6, + ]), + c1: -Fp::from_raw_unchecked([ + 0xd328_e37c_c2f5_8d41, + 0x948d_f085_8a60_5869, + 0x6032_f9d5_6f93_a573, + 0x2be4_83ef_3fff_dc87, + 0x30ef_61f8_8f48_3c2a, + 0x1333_f55a_3572_5be0, ]), - }; - let c = Fp2 { + } + .lexicographically_largest() + )); + assert!(!bool::from( + Fp2 { c0: Fp::from_raw_unchecked([ - 0xe1c086bbbf1b5981, - 0x4fafc3a9aa705d7e, - 0x2734b5c10bb7e726, - 0xb2bd7776af037a3e, - 0x1b895fb398a84164, - 0x17304aef6f113cec, + 0x1128_ecad_6754_9455, + 0x9e7a_1cff_3a4e_a1a8, + 0xeb20_8d51_e08b_cf27, + 0xe98a_d408_11f5_fc2b, + 0x736c_3a59_232d_511d, + 0x10ac_d42d_29cf_cbb6, ]), - c1: Fp::from_raw_unchecked([ - 0x74c31c7995191204, - 0x3271aa5479fdad2b, - 0xc9b471574915a30f, - 0x65e40313ec44b8be, - 0x7487b2385b7067cb, - 0x9523b26d0ad19a4, + c1: Fp::zero(), + } + .lexicographically_largest() + )); + assert!(bool::from( + Fp2 { + c0: -Fp::from_raw_unchecked([ + 0x1128_ecad_6754_9455, + 0x9e7a_1cff_3a4e_a1a8, + 0xeb20_8d51_e08b_cf27, + 0xe98a_d408_11f5_fc2b, + 0x736c_3a59_232d_511d, + 0x10ac_d42d_29cf_cbb6, ]), - }; - - assert_eq!(a - b, c); - } + c1: Fp::zero(), + } + .lexicographically_largest() + )); +} - #[test] - fn test_negation() { - let a = Fp2 { - c0: Fp::from_raw_unchecked([ - 0xc9a2183163ee70d4, - 0xbc3770a7196b5c91, - 0xa247f8c1304c5f44, - 0xb01fc2a3726c80b5, - 0xe1d293e5bbd919c9, - 0x4b78e80020ef2ca, - ]), - c1: Fp::from_raw_unchecked([ - 0x952ea4460462618f, - 0x238d5eddf025c62f, - 0xf6c94b012ea92e72, - 0x3ce24eac1c93808, - 0x55950f945da483c, - 0x10a768d0df4eabc, - ]), - }; - let b = Fp2 { - c0: Fp::from_raw_unchecked([ - 0xf05ce7ce9c1139d7, - 0x62748f5797e8a36d, - 0xc4e8d9dfc66496df, - 0xb45788e181189209, - 0x694913d08772930d, - 0x1549836a3770f3cf, - ]), - c1: Fp::from_raw_unchecked([ - 0x24d05bb9fb9d491c, - 0xfb1ea120c12e39d0, - 0x7067879fc807c7b1, - 0x60a9269a31bbdab6, - 0x45c256bcfd71649b, - 0x18f69b5d2b8afbde, - ]), - }; +#[cfg(feature = "serde_req")] +mod dusk { + use super::*; - assert_eq!(-a, b); + use serde::{ + self, de::Visitor, ser::SerializeStruct, Deserialize, Deserializer, Serialize, Serializer, + }; + + impl Serialize for Fp2 { + fn serialize(&self, serializer: S) -> Result + where + S: Serializer, + { + let mut fp2 = serializer.serialize_struct("struct Fp2", 2)?; + fp2.serialize_field("c0", &self.c0)?; + fp2.serialize_field("c1", &self.c1)?; + fp2.end() + } } - #[test] - fn test_sqrt() { - // a = 1488924004771393321054797166853618474668089414631333405711627789629391903630694737978065425271543178763948256226639*u + 784063022264861764559335808165825052288770346101304131934508881646553551234697082295473567906267937225174620141295 - let a = Fp2 { - c0: Fp::from_raw_unchecked([ - 0x2beed14627d7f9e9, - 0xb6614e06660e5dce, - 0x6c4cc7c2f91d42c, - 0x996d78474b7a63cc, - 0xebaebc4c820d574e, - 0x18865e12d93fd845, - ]), - c1: Fp::from_raw_unchecked([ - 0x7d828664baf4f566, - 0xd17e663996ec7339, - 0x679ead55cb4078d0, - 0xfe3b2260e001ec28, - 0x305993d043d91b68, - 0x626f03c0489b72d, - ]), - }; - - assert_eq!(a.sqrt().unwrap().square(), a); - - // b = 5, which is a generator of the p - 1 order - // multiplicative subgroup - let b = Fp2 { - c0: Fp::from_raw_unchecked([ - 0x6631000000105545, - 0x211400400eec000d, - 0x3fa7af30c820e316, - 0xc52a8b8d6387695d, - 0x9fb4e61d1e83eac5, - 0x5cb922afe84dc7, - ]), - c1: Fp::zero(), - }; + impl<'de> Deserialize<'de> for Fp2 { + fn deserialize(deserializer: D) -> Result + where + D: Deserializer<'de>, + { + enum Field { + C0, + C1, + } - assert_eq!(b.sqrt().unwrap().square(), b); + impl<'de> Deserialize<'de> for Field { + fn deserialize(deserializer: D) -> Result + where + D: Deserializer<'de>, + { + struct FieldVisitor; + + impl<'de> Visitor<'de> for FieldVisitor { + type Value = Field; + + fn expecting( + &self, + formatter: &mut ::core::fmt::Formatter, + ) -> ::core::fmt::Result { + formatter.write_str("struct Fp2") + } - // c = 25, which is a generator of the (p - 1) / 2 order - // multiplicative subgroup - let c = Fp2 { - c0: Fp::from_raw_unchecked([ - 0x44f600000051ffae, - 0x86b8014199480043, - 0xd7159952f1f3794a, - 0x755d6e3dfe1ffc12, - 0xd36cd6db5547e905, - 0x2f8c8ecbf1867bb, - ]), - c1: Fp::zero(), - }; + fn visit_str(self, value: &str) -> Result + where + E: serde::de::Error, + { + match value { + "c0" => Ok(Field::C0), + "c1" => Ok(Field::C1), + _ => Err(serde::de::Error::unknown_field(value, FIELDS)), + } + } + } - assert_eq!(c.sqrt().unwrap().square(), c); - - // 2155129644831861015726826462986972654175647013268275306775721078997042729172900466542651176384766902407257452753362*u + 2796889544896299244102912275102369318775038861758288697415827248356648685135290329705805931514906495247464901062529 - // is nonsquare. - assert!(bool::from( - Fp2 { - c0: Fp::from_raw_unchecked([ - 0xc5fa1bc8fd00d7f6, - 0x3830ca454606003b, - 0x2b287f1104b102da, - 0xa7fb30f28230f23e, - 0x339cdb9ee953dbf0, - 0xd78ec51d989fc57 - ]), - c1: Fp::from_raw_unchecked([ - 0x27ec4898cf87f613, - 0x9de1394e1abb05a5, - 0x947f85dc170fc14, - 0x586fbc696b6114b7, - 0x2b3475a4077d7169, - 0x13e1c895cc4b6c22 - ]) + deserializer.deserialize_identifier(FieldVisitor) + } } - .sqrt() - .is_none() - )); - } - #[test] - fn test_inversion() { - let a = Fp2 { - c0: Fp::from_raw_unchecked([ - 0x1128ecad67549455, - 0x9e7a1cff3a4ea1a8, - 0xeb208d51e08bcf27, - 0xe98ad40811f5fc2b, - 0x736c3a59232d511d, - 0x10acd42d29cfcbb6, - ]), - c1: Fp::from_raw_unchecked([ - 0xd328e37cc2f58d41, - 0x948df0858a605869, - 0x6032f9d56f93a573, - 0x2be483ef3fffdc87, - 0x30ef61f88f483c2a, - 0x1333f55a35725be0, - ]), - }; + struct Fp2Visitor; - let b = Fp2 { - c0: Fp::from_raw_unchecked([ - 0x581a1333d4f48a6, - 0x58242f6ef0748500, - 0x292c955349e6da5, - 0xba37721ddd95fcd0, - 0x70d167903aa5dfc5, - 0x11895e118b58a9d5, - ]), - c1: Fp::from_raw_unchecked([ - 0xeda09d2d7a85d17, - 0x8808e137a7d1a2cf, - 0x43ae2625c1ff21db, - 0xf85ac9fdf7a74c64, - 0x8fccdda5b8da9738, - 0x8e84f0cb32cd17d, - ]), - }; - - assert_eq!(a.invert().unwrap(), b); + impl<'de> Visitor<'de> for Fp2Visitor { + type Value = Fp2; - assert!(Fp2::zero().invert().is_none().unwrap_u8() == 1); - } + fn expecting(&self, formatter: &mut ::core::fmt::Formatter) -> ::core::fmt::Result { + formatter.write_str("struct Fp2") + } - #[test] - fn test_lexicographic_largest() { - assert!(!bool::from(Fp2::zero().lexicographically_largest())); - assert!(!bool::from(Fp2::one().lexicographically_largest())); - assert!(bool::from( - Fp2 { - c0: Fp::from_raw_unchecked([ - 0x1128ecad67549455, - 0x9e7a1cff3a4ea1a8, - 0xeb208d51e08bcf27, - 0xe98ad40811f5fc2b, - 0x736c3a59232d511d, - 0x10acd42d29cfcbb6, - ]), - c1: Fp::from_raw_unchecked([ - 0xd328e37cc2f58d41, - 0x948df0858a605869, - 0x6032f9d56f93a573, - 0x2be483ef3fffdc87, - 0x30ef61f88f483c2a, - 0x1333f55a35725be0, - ]), - } - .lexicographically_largest() - )); - assert!(!bool::from( - Fp2 { - c0: -Fp::from_raw_unchecked([ - 0x1128ecad67549455, - 0x9e7a1cff3a4ea1a8, - 0xeb208d51e08bcf27, - 0xe98ad40811f5fc2b, - 0x736c3a59232d511d, - 0x10acd42d29cfcbb6, - ]), - c1: -Fp::from_raw_unchecked([ - 0xd328e37cc2f58d41, - 0x948df0858a605869, - 0x6032f9d56f93a573, - 0x2be483ef3fffdc87, - 0x30ef61f88f483c2a, - 0x1333f55a35725be0, - ]), - } - .lexicographically_largest() - )); - assert!(!bool::from( - Fp2 { - c0: Fp::from_raw_unchecked([ - 0x1128ecad67549455, - 0x9e7a1cff3a4ea1a8, - 0xeb208d51e08bcf27, - 0xe98ad40811f5fc2b, - 0x736c3a59232d511d, - 0x10acd42d29cfcbb6, - ]), - c1: Fp::zero(), - } - .lexicographically_largest() - )); - assert!(bool::from( - Fp2 { - c0: -Fp::from_raw_unchecked([ - 0x1128ecad67549455, - 0x9e7a1cff3a4ea1a8, - 0xeb208d51e08bcf27, - 0xe98ad40811f5fc2b, - 0x736c3a59232d511d, - 0x10acd42d29cfcbb6, - ]), - c1: Fp::zero(), + fn visit_seq(self, mut seq: V) -> Result + where + V: serde::de::SeqAccess<'de>, + { + let c0 = seq + .next_element()? + .ok_or_else(|| serde::de::Error::invalid_length(0, &self))?; + let c1 = seq + .next_element()? + .ok_or_else(|| serde::de::Error::invalid_length(0, &self))?; + Ok(Fp2 { c0, c1 }) + } } - .lexicographically_largest() - )); + + const FIELDS: &[&str] = &["c0", "c1"]; + deserializer.deserialize_struct("Fp2", FIELDS, Fp2Visitor) + } } #[test] - #[cfg(feature = "serde_req")] fn fp2_serde_roundtrip() { use bincode; diff --git a/src/fp6.rs b/src/fp6.rs index a55c94c0..1112f8d6 100644 --- a/src/fp6.rs +++ b/src/fp6.rs @@ -10,11 +10,6 @@ use bytecheck::CheckBytes; #[cfg(feature = "rkyv-impl")] use rkyv::{Archive, Deserialize as RkyvDeserialize, Serialize as RkyvSerialize}; -#[cfg(feature = "serde_req")] -use serde::{ - self, de::Visitor, ser::SerializeStruct, Deserialize, Deserializer, Serialize, Serializer, -}; - /// This represents an element $c_0 + c_1 v + c_2 v^2$ of $\mathbb{F}_{p^6} = \mathbb{F}_{p^2} / v^3 - u - 1$. #[cfg_attr(feature = "rkyv-impl", derive(Archive, RkyvSerialize, RkyvDeserialize))] #[cfg_attr(feature = "rkyv-impl", archive_attr(derive(CheckBytes)))] @@ -70,97 +65,6 @@ impl fmt::Debug for Fp6 { } } -#[cfg(feature = "serde_req")] -impl Serialize for Fp6 { - fn serialize(&self, serializer: S) -> Result - where - S: Serializer, - { - let mut fp2 = serializer.serialize_struct("struct Fp6", 3)?; - fp2.serialize_field("c0", &self.c0)?; - fp2.serialize_field("c1", &self.c1)?; - fp2.serialize_field("c2", &self.c2)?; - fp2.end() - } -} - -#[cfg(feature = "serde_req")] -impl<'de> Deserialize<'de> for Fp6 { - fn deserialize(deserializer: D) -> Result - where - D: Deserializer<'de>, - { - enum Field { - C0, - C1, - C2, - } - - impl<'de> Deserialize<'de> for Field { - fn deserialize(deserializer: D) -> Result - where - D: Deserializer<'de>, - { - struct FieldVisitor; - - impl<'de> Visitor<'de> for FieldVisitor { - type Value = Field; - - fn expecting( - &self, - formatter: &mut ::core::fmt::Formatter, - ) -> ::core::fmt::Result { - formatter.write_str("struct Fp6") - } - - fn visit_str(self, value: &str) -> Result - where - E: serde::de::Error, - { - match value { - "c0" => Ok(Field::C0), - "c1" => Ok(Field::C1), - "c2" => Ok(Field::C2), - _ => Err(serde::de::Error::unknown_field(value, FIELDS)), - } - } - } - - deserializer.deserialize_identifier(FieldVisitor) - } - } - - struct Fp6Visitor; - - impl<'de> Visitor<'de> for Fp6Visitor { - type Value = Fp6; - - fn expecting(&self, formatter: &mut ::core::fmt::Formatter) -> ::core::fmt::Result { - formatter.write_str("struct Fp6") - } - - fn visit_seq(self, mut seq: V) -> Result - where - V: serde::de::SeqAccess<'de>, - { - let c0 = seq - .next_element()? - .ok_or_else(|| serde::de::Error::invalid_length(0, &self))?; - let c1 = seq - .next_element()? - .ok_or_else(|| serde::de::Error::invalid_length(0, &self))?; - let c2 = seq - .next_element()? - .ok_or_else(|| serde::de::Error::invalid_length(0, &self))?; - Ok(Fp6 { c0, c1, c2 }) - } - } - - const FIELDS: &[&str] = &["c0", "c1", "c2"]; - deserializer.deserialize_struct("Fp6", FIELDS, Fp6Visitor) - } -} - impl ConditionallySelectable for Fp6 { #[inline(always)] fn conditional_select(a: &Self, b: &Self, choice: Choice) -> Self { @@ -468,203 +372,294 @@ impl<'a, 'b> Sub<&'b Fp6> for &'a Fp6 { impl_binops_additive!(Fp6, Fp6); impl_binops_multiplicative!(Fp6, Fp6); -#[cfg(test)] -mod tests { +#[test] +fn test_arithmetic() { + use crate::fp::*; + + let a = Fp6 { + c0: Fp2 { + c0: Fp::from_raw_unchecked([ + 0x47f9_cb98_b1b8_2d58, + 0x5fe9_11eb_a3aa_1d9d, + 0x96bf_1b5f_4dd8_1db3, + 0x8100_d27c_c925_9f5b, + 0xafa2_0b96_7464_0eab, + 0x09bb_cea7_d8d9_497d, + ]), + c1: Fp::from_raw_unchecked([ + 0x0303_cb98_b166_2daa, + 0xd931_10aa_0a62_1d5a, + 0xbfa9_820c_5be4_a468, + 0x0ba3_643e_cb05_a348, + 0xdc35_34bb_1f1c_25a6, + 0x06c3_05bb_19c0_e1c1, + ]), + }, + c1: Fp2 { + c0: Fp::from_raw_unchecked([ + 0x46f9_cb98_b162_d858, + 0x0be9_109c_f7aa_1d57, + 0xc791_bc55_fece_41d2, + 0xf84c_5770_4e38_5ec2, + 0xcb49_c1d9_c010_e60f, + 0x0acd_b8e1_58bf_e3c8, + ]), + c1: Fp::from_raw_unchecked([ + 0x8aef_cb98_b15f_8306, + 0x3ea1_108f_e4f2_1d54, + 0xcf79_f69f_a1b7_df3b, + 0xe4f5_4aa1_d16b_1a3c, + 0xba5e_4ef8_6105_a679, + 0x0ed8_6c07_97be_e5cf, + ]), + }, + c2: Fp2 { + c0: Fp::from_raw_unchecked([ + 0xcee5_cb98_b15c_2db4, + 0x7159_1082_d23a_1d51, + 0xd762_30e9_44a1_7ca4, + 0xd19e_3dd3_549d_d5b6, + 0xa972_dc17_01fa_66e3, + 0x12e3_1f2d_d6bd_e7d6, + ]), + c1: Fp::from_raw_unchecked([ + 0xad2a_cb98_b173_2d9d, + 0x2cfd_10dd_0696_1d64, + 0x0739_6b86_c6ef_24e8, + 0xbd76_e2fd_b1bf_c820, + 0x6afe_a7f6_de94_d0d5, + 0x1099_4b0c_5744_c040, + ]), + }, + }; + + let b = Fp6 { + c0: Fp2 { + c0: Fp::from_raw_unchecked([ + 0xf120_cb98_b16f_d84b, + 0x5fb5_10cf_f3de_1d61, + 0x0f21_a5d0_69d8_c251, + 0xaa1f_d62f_34f2_839a, + 0x5a13_3515_7f89_913f, + 0x14a3_fe32_9643_c247, + ]), + c1: Fp::from_raw_unchecked([ + 0x3516_cb98_b16c_82f9, + 0x926d_10c2_e126_1d5f, + 0x1709_e01a_0cc2_5fba, + 0x96c8_c960_b825_3f14, + 0x4927_c234_207e_51a9, + 0x18ae_b158_d542_c44e, + ]), + }, + c1: Fp2 { + c0: Fp::from_raw_unchecked([ + 0xbf0d_cb98_b169_82fc, + 0xa679_10b7_1d1a_1d5c, + 0xb7c1_47c2_b8fb_06ff, + 0x1efa_710d_47d2_e7ce, + 0xed20_a79c_7e27_653c, + 0x02b8_5294_dac1_dfba, + ]), + c1: Fp::from_raw_unchecked([ + 0x9d52_cb98_b180_82e5, + 0x621d_1111_5176_1d6f, + 0xe798_8260_3b48_af43, + 0x0ad3_1637_a4f4_da37, + 0xaeac_737c_5ac1_cf2e, + 0x006e_7e73_5b48_b824, + ]), + }, + c2: Fp2 { + c0: Fp::from_raw_unchecked([ + 0xe148_cb98_b17d_2d93, + 0x94d5_1104_3ebe_1d6c, + 0xef80_bca9_de32_4cac, + 0xf77c_0969_2827_95b1, + 0x9dc1_009a_fbb6_8f97, + 0x0479_3199_9a47_ba2b, + ]), + c1: Fp::from_raw_unchecked([ + 0x253e_cb98_b179_d841, + 0xc78d_10f7_2c06_1d6a, + 0xf768_f6f3_811b_ea15, + 0xe424_fc9a_ab5a_512b, + 0x8cd5_8db9_9cab_5001, + 0x0883_e4bf_d946_bc32, + ]), + }, + }; + + let c = Fp6 { + c0: Fp2 { + c0: Fp::from_raw_unchecked([ + 0x6934_cb98_b176_82ef, + 0xfa45_10ea_194e_1d67, + 0xff51_313d_2405_877e, + 0xd0cd_efcc_2e8d_0ca5, + 0x7bea_1ad8_3da0_106b, + 0x0c8e_97e6_1845_be39, + ]), + c1: Fp::from_raw_unchecked([ + 0x4779_cb98_b18d_82d8, + 0xb5e9_1144_4daa_1d7a, + 0x2f28_6bda_a653_2fc2, + 0xbca6_94f6_8bae_ff0f, + 0x3d75_e6b8_1a3a_7a5d, + 0x0a44_c3c4_98cc_96a3, + ]), + }, + c1: Fp2 { + c0: Fp::from_raw_unchecked([ + 0x8b6f_cb98_b18a_2d86, + 0xe8a1_1137_3af2_1d77, + 0x3710_a624_493c_cd2b, + 0xa94f_8828_0ee1_ba89, + 0x2c8a_73d6_bb2f_3ac7, + 0x0e4f_76ea_d7cb_98aa, + ]), + c1: Fp::from_raw_unchecked([ + 0xcf65_cb98_b186_d834, + 0x1b59_112a_283a_1d74, + 0x3ef8_e06d_ec26_6a95, + 0x95f8_7b59_9214_7603, + 0x1b9f_00f5_5c23_fb31, + 0x125a_2a11_16ca_9ab1, + ]), + }, + c2: Fp2 { + c0: Fp::from_raw_unchecked([ + 0x135b_cb98_b183_82e2, + 0x4e11_111d_1582_1d72, + 0x46e1_1ab7_8f10_07fe, + 0x82a1_6e8b_1547_317d, + 0x0ab3_8e13_fd18_bb9b, + 0x1664_dd37_55c9_9cb8, + ]), + c1: Fp::from_raw_unchecked([ + 0xce65_cb98_b131_8334, + 0xc759_0fdb_7c3a_1d2e, + 0x6fcb_8164_9d1c_8eb3, + 0x0d44_004d_1727_356a, + 0x3746_b738_a7d0_d296, + 0x136c_144a_96b1_34fc, + ]), + }, + }; + + assert_eq!(a.square(), &a * &a); + assert_eq!(b.square(), &b * &b); + assert_eq!(c.square(), &c * &c); + + assert_eq!( + (a + b) * c.square(), + &(&(&c * &c) * &a) + &(&(&c * &c) * &b) + ); + + assert_eq!( + &a.invert().unwrap() * &b.invert().unwrap(), + (&a * &b).invert().unwrap() + ); + assert_eq!(&a.invert().unwrap() * &a, Fp6::one()); +} + +#[cfg(feature = "serde_req")] +mod dusk { use super::*; + use serde::{ + self, de::Visitor, ser::SerializeStruct, Deserialize, Deserializer, Serialize, Serializer, + }; + + impl Serialize for Fp6 { + fn serialize(&self, serializer: S) -> Result + where + S: Serializer, + { + let mut fp2 = serializer.serialize_struct("struct Fp6", 3)?; + fp2.serialize_field("c0", &self.c0)?; + fp2.serialize_field("c1", &self.c1)?; + fp2.serialize_field("c2", &self.c2)?; + fp2.end() + } + } - #[test] - fn test_arithmetic() { - use crate::fp::*; + impl<'de> Deserialize<'de> for Fp6 { + fn deserialize(deserializer: D) -> Result + where + D: Deserializer<'de>, + { + enum Field { + C0, + C1, + C2, + } - let a = Fp6 { - c0: Fp2 { - c0: Fp::from_raw_unchecked([ - 0x47f9cb98b1b82d58, - 0x5fe911eba3aa1d9d, - 0x96bf1b5f4dd81db3, - 0x8100d27cc9259f5b, - 0xafa20b9674640eab, - 0x9bbcea7d8d9497d, - ]), - c1: Fp::from_raw_unchecked([ - 0x303cb98b1662daa, - 0xd93110aa0a621d5a, - 0xbfa9820c5be4a468, - 0xba3643ecb05a348, - 0xdc3534bb1f1c25a6, - 0x6c305bb19c0e1c1, - ]), - }, - c1: Fp2 { - c0: Fp::from_raw_unchecked([ - 0x46f9cb98b162d858, - 0xbe9109cf7aa1d57, - 0xc791bc55fece41d2, - 0xf84c57704e385ec2, - 0xcb49c1d9c010e60f, - 0xacdb8e158bfe3c8, - ]), - c1: Fp::from_raw_unchecked([ - 0x8aefcb98b15f8306, - 0x3ea1108fe4f21d54, - 0xcf79f69fa1b7df3b, - 0xe4f54aa1d16b1a3c, - 0xba5e4ef86105a679, - 0xed86c0797bee5cf, - ]), - }, - c2: Fp2 { - c0: Fp::from_raw_unchecked([ - 0xcee5cb98b15c2db4, - 0x71591082d23a1d51, - 0xd76230e944a17ca4, - 0xd19e3dd3549dd5b6, - 0xa972dc1701fa66e3, - 0x12e31f2dd6bde7d6, - ]), - c1: Fp::from_raw_unchecked([ - 0xad2acb98b1732d9d, - 0x2cfd10dd06961d64, - 0x7396b86c6ef24e8, - 0xbd76e2fdb1bfc820, - 0x6afea7f6de94d0d5, - 0x10994b0c5744c040, - ]), - }, - }; + impl<'de> Deserialize<'de> for Field { + fn deserialize(deserializer: D) -> Result + where + D: Deserializer<'de>, + { + struct FieldVisitor; + + impl<'de> Visitor<'de> for FieldVisitor { + type Value = Field; + + fn expecting( + &self, + formatter: &mut ::core::fmt::Formatter, + ) -> ::core::fmt::Result { + formatter.write_str("struct Fp6") + } - let b = Fp6 { - c0: Fp2 { - c0: Fp::from_raw_unchecked([ - 0xf120cb98b16fd84b, - 0x5fb510cff3de1d61, - 0xf21a5d069d8c251, - 0xaa1fd62f34f2839a, - 0x5a1335157f89913f, - 0x14a3fe329643c247, - ]), - c1: Fp::from_raw_unchecked([ - 0x3516cb98b16c82f9, - 0x926d10c2e1261d5f, - 0x1709e01a0cc25fba, - 0x96c8c960b8253f14, - 0x4927c234207e51a9, - 0x18aeb158d542c44e, - ]), - }, - c1: Fp2 { - c0: Fp::from_raw_unchecked([ - 0xbf0dcb98b16982fc, - 0xa67910b71d1a1d5c, - 0xb7c147c2b8fb06ff, - 0x1efa710d47d2e7ce, - 0xed20a79c7e27653c, - 0x2b85294dac1dfba, - ]), - c1: Fp::from_raw_unchecked([ - 0x9d52cb98b18082e5, - 0x621d111151761d6f, - 0xe79882603b48af43, - 0xad31637a4f4da37, - 0xaeac737c5ac1cf2e, - 0x6e7e735b48b824, - ]), - }, - c2: Fp2 { - c0: Fp::from_raw_unchecked([ - 0xe148cb98b17d2d93, - 0x94d511043ebe1d6c, - 0xef80bca9de324cac, - 0xf77c0969282795b1, - 0x9dc1009afbb68f97, - 0x47931999a47ba2b, - ]), - c1: Fp::from_raw_unchecked([ - 0x253ecb98b179d841, - 0xc78d10f72c061d6a, - 0xf768f6f3811bea15, - 0xe424fc9aab5a512b, - 0x8cd58db99cab5001, - 0x883e4bfd946bc32, - ]), - }, - }; + fn visit_str(self, value: &str) -> Result + where + E: serde::de::Error, + { + match value { + "c0" => Ok(Field::C0), + "c1" => Ok(Field::C1), + "c2" => Ok(Field::C2), + _ => Err(serde::de::Error::unknown_field(value, FIELDS)), + } + } + } - let c = Fp6 { - c0: Fp2 { - c0: Fp::from_raw_unchecked([ - 0x6934cb98b17682ef, - 0xfa4510ea194e1d67, - 0xff51313d2405877e, - 0xd0cdefcc2e8d0ca5, - 0x7bea1ad83da0106b, - 0xc8e97e61845be39, - ]), - c1: Fp::from_raw_unchecked([ - 0x4779cb98b18d82d8, - 0xb5e911444daa1d7a, - 0x2f286bdaa6532fc2, - 0xbca694f68baeff0f, - 0x3d75e6b81a3a7a5d, - 0xa44c3c498cc96a3, - ]), - }, - c1: Fp2 { - c0: Fp::from_raw_unchecked([ - 0x8b6fcb98b18a2d86, - 0xe8a111373af21d77, - 0x3710a624493ccd2b, - 0xa94f88280ee1ba89, - 0x2c8a73d6bb2f3ac7, - 0xe4f76ead7cb98aa, - ]), - c1: Fp::from_raw_unchecked([ - 0xcf65cb98b186d834, - 0x1b59112a283a1d74, - 0x3ef8e06dec266a95, - 0x95f87b5992147603, - 0x1b9f00f55c23fb31, - 0x125a2a1116ca9ab1, - ]), - }, - c2: Fp2 { - c0: Fp::from_raw_unchecked([ - 0x135bcb98b18382e2, - 0x4e11111d15821d72, - 0x46e11ab78f1007fe, - 0x82a16e8b1547317d, - 0xab38e13fd18bb9b, - 0x1664dd3755c99cb8, - ]), - c1: Fp::from_raw_unchecked([ - 0xce65cb98b1318334, - 0xc7590fdb7c3a1d2e, - 0x6fcb81649d1c8eb3, - 0xd44004d1727356a, - 0x3746b738a7d0d296, - 0x136c144a96b134fc, - ]), - }, - }; + deserializer.deserialize_identifier(FieldVisitor) + } + } - assert_eq!(a.square(), &a * &a); - assert_eq!(b.square(), &b * &b); - assert_eq!(c.square(), &c * &c); + struct Fp6Visitor; - assert_eq!( - (a + b) * c.square(), - &(&(&c * &c) * &a) + &(&(&c * &c) * &b) - ); + impl<'de> Visitor<'de> for Fp6Visitor { + type Value = Fp6; - assert_eq!( - &a.invert().unwrap() * &b.invert().unwrap(), - (&a * &b).invert().unwrap() - ); - assert_eq!(&a.invert().unwrap() * &a, Fp6::one()); + fn expecting(&self, formatter: &mut ::core::fmt::Formatter) -> ::core::fmt::Result { + formatter.write_str("struct Fp6") + } + + fn visit_seq(self, mut seq: V) -> Result + where + V: serde::de::SeqAccess<'de>, + { + let c0 = seq + .next_element()? + .ok_or_else(|| serde::de::Error::invalid_length(0, &self))?; + let c1 = seq + .next_element()? + .ok_or_else(|| serde::de::Error::invalid_length(0, &self))?; + let c2 = seq + .next_element()? + .ok_or_else(|| serde::de::Error::invalid_length(0, &self))?; + Ok(Fp6 { c0, c1, c2 }) + } + } + + const FIELDS: &[&str] = &["c0", "c1", "c2"]; + deserializer.deserialize_struct("Fp6", FIELDS, Fp6Visitor) + } } #[test] - #[cfg(feature = "serde_req")] fn fp6_serde_roundtrip() { use bincode; diff --git a/src/g1.rs b/src/g1.rs index c2225204..61a5717a 100644 --- a/src/g1.rs +++ b/src/g1.rs @@ -1,17 +1,15 @@ //! This module provides an implementation of the $\mathbb{G}_1$ group of BLS12-381. -use crate::choice; -use crate::fp::Fp; -use crate::BlsScalar; - use core::borrow::Borrow; use core::iter::Sum; use core::ops::{Add, AddAssign, Mul, MulAssign, Neg, Sub, SubAssign}; + use dusk_bytes::{Error as BytesError, HexDebug, Serializable}; use subtle::{Choice, ConditionallySelectable, ConstantTimeEq, CtOption}; -#[cfg(feature = "serde_req")] -use serde::{de::Visitor, Deserialize, Deserializer, Serialize, Serializer}; +use crate::choice; +use crate::fp::Fp; +use crate::BlsScalar; #[cfg(feature = "rkyv-impl")] use bytecheck::CheckBytes; @@ -63,150 +61,6 @@ impl From for G1Affine { } } -impl Serializable<48> for G1Affine { - type Error = BytesError; - - /// Serializes this element into compressed form. See [`notes::serialization`](crate::notes::serialization) - /// for details about how group elements are serialized. - fn to_bytes(&self) -> [u8; Self::SIZE] { - // Strictly speaking, self.x is zero already when self.infinity is true, but - // to guard against implementation mistakes we do not assume this. - let mut res = Fp::conditional_select(&self.x, &Fp::zero(), self.infinity.into()).to_bytes(); - - // This point is in compressed form, so we set the most significant bit. - res[0] |= 1u8 << 7; - - // Is this point at infinity? If so, set the second-most significant bit. - res[0] |= u8::conditional_select(&0u8, &(1u8 << 6), self.infinity.into()); - - // Is the y-coordinate the lexicographically largest of the two associated with the - // x-coordinate? If so, set the third-most significant bit so long as this is not - // the point at infinity. - res[0] |= u8::conditional_select( - &0u8, - &(1u8 << 5), - (!Choice::from(self.infinity)) & self.y.lexicographically_largest(), - ); - - res - } - - /// Attempts to deserialize a compressed element. See [`notes::serialization`](crate::notes::serialization) - /// for details about how group elements are serialized. - fn from_bytes(buf: &[u8; Self::SIZE]) -> Result { - // We already know the point is on the curve because this is established - // by the y-coordinate recovery procedure in from_compressed_unchecked(). - - let compression_flag_set = Choice::from((buf[0] >> 7) & 1); - let infinity_flag_set = Choice::from((buf[0] >> 6) & 1); - let sort_flag_set = Choice::from((buf[0] >> 5) & 1); - - // Attempt to obtain the x-coordinate - let x = { - let mut tmp = [0; Self::SIZE]; - tmp.copy_from_slice(&buf[..Self::SIZE]); - - // Mask away the flag bits - tmp[0] &= 0b0001_1111; - - Fp::from_bytes(&tmp) - }; - - let x: Option = x - .and_then(|x| { - // If the infinity flag is set, return the value assuming - // the x-coordinate is zero and the sort bit is not set. - // - // Otherwise, return a recovered point (assuming the correct - // y-coordinate can be found) so long as the infinity flag - // was not set. - CtOption::new( - G1Affine::identity(), - infinity_flag_set & // Infinity flag should be set - compression_flag_set & // Compression flag should be set - (!sort_flag_set) & // Sort flag should not be set - x.is_zero(), // The x-coordinate should be zero - ) - .or_else(|| { - // Recover a y-coordinate given x by y = sqrt(x^3 + 4) - ((x.square() * x) + B).sqrt().and_then(|y| { - // Switch to the correct y-coordinate if necessary. - let y = Fp::conditional_select( - &y, - &-y, - y.lexicographically_largest() ^ sort_flag_set, - ); - - CtOption::new( - G1Affine { - x, - y, - infinity: infinity_flag_set.into(), - }, - (!infinity_flag_set) & // Infinity flag should not be set - compression_flag_set, // Compression flag should be set - ) - }) - }) - }) - .and_then(|p| CtOption::new(p, p.is_torsion_free())) - .into(); - - x.ok_or(BytesError::InvalidData) - } -} - -#[cfg(feature = "serde_req")] -impl Serialize for G1Affine { - fn serialize(&self, serializer: S) -> Result - where - S: Serializer, - { - use serde::ser::SerializeTuple; - let mut tup = serializer.serialize_tuple(G1Affine::SIZE)?; - for byte in self.to_bytes().iter() { - tup.serialize_element(byte)?; - } - tup.end() - } -} - -#[cfg(feature = "serde_req")] -impl<'de> Deserialize<'de> for G1Affine { - fn deserialize(deserializer: D) -> Result - where - D: Deserializer<'de>, - { - struct G1AffineVisitor; - - impl<'de> Visitor<'de> for G1AffineVisitor { - type Value = G1Affine; - - fn expecting(&self, formatter: &mut ::core::fmt::Formatter) -> ::core::fmt::Result { - formatter.write_str("a 48-byte cannonical compressed G1Affine point from Bls12_381") - } - - fn visit_seq(self, mut seq: A) -> Result - where - A: serde::de::SeqAccess<'de>, - { - let mut bytes = [0u8; G1Affine::SIZE]; - for i in 0..G1Affine::SIZE { - bytes[i] = seq - .next_element()? - .ok_or(serde::de::Error::invalid_length(i, &"expected 48 bytes"))?; - } - - G1Affine::from_bytes(&bytes).map_err(|_| { - serde::de::Error::custom(&"compressed G1Affine was not canonically encoded") - }) - } - } - - deserializer.deserialize_tuple(G1Affine::SIZE, G1AffineVisitor) - } -} - impl ConstantTimeEq for G1Affine { fn ct_eq(&self, other: &Self) -> Choice { // The only cases in which two points are equal are @@ -312,18 +166,15 @@ impl_binops_additive!(G1Projective, G1Affine); impl_binops_additive_specify_output!(G1Affine, G1Projective, G1Projective); const B: Fp = Fp::from_raw_unchecked([ - 0xaa270000000cfff3, - 0x53cc0032fc34000a, - 0x478fe97a6b0a807f, - 0xb1d37ebee6ba24d7, - 0x8ec9733bbf78ab2f, - 0x9d645513d83de7e, + 0xaa27_0000_000c_fff3, + 0x53cc_0032_fc34_000a, + 0x478f_e97a_6b0a_807f, + 0xb1d3_7ebe_e6ba_24d7, + 0x8ec9_733b_bf78_ab2f, + 0x09d6_4551_3d83_de7e, ]); impl G1Affine { - /// Bytes size of the raw representation - pub const RAW_SIZE: usize = 97; - /// Returns the identity of the group: the point at infinity. pub fn identity() -> G1Affine { G1Affine { @@ -338,81 +189,25 @@ impl G1Affine { pub fn generator() -> G1Affine { G1Affine { x: Fp::from_raw_unchecked([ - 0x5cb38790fd530c16, - 0x7817fc679976fff5, - 0x154f95c7143ba1c1, - 0xf0ae6acdf3d0e747, - 0xedce6ecc21dbf440, - 0x120177419e0bfb75, + 0x5cb3_8790_fd53_0c16, + 0x7817_fc67_9976_fff5, + 0x154f_95c7_143b_a1c1, + 0xf0ae_6acd_f3d0_e747, + 0xedce_6ecc_21db_f440, + 0x1201_7741_9e0b_fb75, ]), y: Fp::from_raw_unchecked([ - 0xbaac93d50ce72271, - 0x8c22631a7918fd8e, - 0xdd595f13570725ce, - 0x51ac582950405194, - 0xe1c8c3fad0059c0, - 0xbbc3efc5008a26a, + 0xbaac_93d5_0ce7_2271, + 0x8c22_631a_7918_fd8e, + 0xdd59_5f13_5707_25ce, + 0x51ac_5829_5040_5194, + 0x0e1c_8c3f_ad00_59c0, + 0x0bbc_3efc_5008_a26a, ]), infinity: 0u8.into(), } } - /// Raw bytes representation - /// - /// The intended usage of this function is for trusted sets of data where performance is - /// critical. - /// - /// For secure serialization, check `to_bytes` - pub fn to_raw_bytes(&self) -> [u8; Self::RAW_SIZE] { - let mut bytes = [0u8; Self::RAW_SIZE]; - let chunks = bytes.chunks_mut(8); - - self.x - .internal_repr() - .iter() - .chain(self.y.internal_repr().iter()) - .zip(chunks) - .for_each(|(n, c)| c.copy_from_slice(&n.to_le_bytes())); - - bytes[Self::RAW_SIZE - 1] = self.infinity.into(); - - bytes - } - - /// Create a `G1Affine` from a set of bytes created by `G1Affine::to_raw_bytes`. - /// - /// No check is performed and no constant time is granted. The expected usage of this function - /// is for trusted bytes where performance is critical. - /// - /// For secure serialization, check `from_bytes` - /// - /// After generating the point, you can check `is_on_curve` and `is_torsion_free` to grant its - /// security - pub unsafe fn from_slice_unchecked(bytes: &[u8]) -> Self { - let mut x = [0u64; 6]; - let mut y = [0u64; 6]; - let mut z = [0u8; 8]; - - bytes - .chunks_exact(8) - .zip(x.iter_mut().chain(y.iter_mut())) - .for_each(|(c, n)| { - z.copy_from_slice(c); - *n = u64::from_le_bytes(z); - }); - - let x = Fp::from_raw_unchecked(x); - let y = Fp::from_raw_unchecked(y); - - let infinity = if bytes.len() >= Self::RAW_SIZE { - bytes[Self::RAW_SIZE - 1].into() - } else { - 0u8.into() - }; - - Self { x, y, infinity } - } - /// Returns true if this element is the identity (the point at infinity). #[inline] pub fn is_identity(&self) -> Choice { @@ -602,20 +397,20 @@ impl G1Projective { pub fn generator() -> G1Projective { G1Projective { x: Fp::from_raw_unchecked([ - 0x5cb38790fd530c16, - 0x7817fc679976fff5, - 0x154f95c7143ba1c1, - 0xf0ae6acdf3d0e747, - 0xedce6ecc21dbf440, - 0x120177419e0bfb75, + 0x5cb3_8790_fd53_0c16, + 0x7817_fc67_9976_fff5, + 0x154f_95c7_143b_a1c1, + 0xf0ae_6acd_f3d0_e747, + 0xedce_6ecc_21db_f440, + 0x1201_7741_9e0b_fb75, ]), y: Fp::from_raw_unchecked([ - 0xbaac93d50ce72271, - 0x8c22631a7918fd8e, - 0xdd595f13570725ce, - 0x51ac582950405194, - 0xe1c8c3fad0059c0, - 0xbbc3efc5008a26a, + 0xbaac_93d5_0ce7_2271, + 0x8c22_631a_7918_fd8e, + 0xdd59_5f13_5707_25ce, + 0x51ac_5829_5040_5194, + 0x0e1c_8c3f_ad00_59c0, + 0x0bbc_3efc_5008_a26a, ]), z: Fp::one(), } @@ -655,16 +450,19 @@ impl G1Projective { /// Adds this point to another point. pub fn add(&self, rhs: &G1Projective) -> G1Projective { - // This Jacobian point addition technique is based on the implementation in libsecp256k1, - // which assumes that rhs has z=1. Let's address the case of zero z-coordinates generally. + // This Jacobian point addition technique is based on the implementation + // in libsecp256k1, which assumes that rhs has z=1. Let's address the + // case of zero z-coordinates generally. - // If self is the identity, return rhs. Otherwise, return self. The other cases will be - // predicated on neither self nor rhs being the identity. + // If self is the identity, return rhs. Otherwise, return self. The + // other cases will be predicated on neither self nor rhs being the + // identity. let f1 = self.is_identity(); let res = G1Projective::conditional_select(self, rhs, f1); let f2 = rhs.is_identity(); - // If neither are the identity but x1 = x2 and y1 != y2, then return the identity + // If neither are the identity but x1 = x2 and y1 != y2, then return + // the identity let z = rhs.z.square(); let u1 = self.x * z; let z = z * rhs.z; @@ -684,8 +482,9 @@ impl G1Projective { let tt = u1 * m_alt; let rr = rr + tt; - // Correct for x1 != x2 but y1 = -y2, which can occur because p - 1 is divisible by 3. - // libsecp256k1 does this by substituting in an alternative (defined) expression for lambda. + // Correct for x1 != x2 but y1 = -y2, which can occur because p - 1 is + // divisible by 3. libsecp256k1 does this by substituting in an + // alternative (defined) expression for lambda. let degenerate = m.is_zero() & rr.is_zero(); let rr_alt = s1 + s1; let m_alt = m_alt + u1; @@ -698,7 +497,8 @@ impl G1Projective { let n = n.square(); let n = Fp::conditional_select(&n, &m, degenerate); let t = rr_alt.square(); - let z3 = m_alt * self.z * rhs.z; // We allow rhs.z != 1, so we must account for this. + // We allow rhs.z != 1, so we must account for this. + let z3 = m_alt * self.z * rhs.z; let z3 = z3 + z3; let q = -q; let t = t + q; @@ -724,16 +524,19 @@ impl G1Projective { /// Adds this point to another point in the affine model. pub fn add_mixed(&self, rhs: &G1Affine) -> G1Projective { - // This Jacobian point addition technique is based on the implementation in libsecp256k1, - // which assumes that rhs has z=1. Let's address the case of zero z-coordinates generally. + // This Jacobian point addition technique is based on the implementation + // in libsecp256k1, which assumes that rhs has z=1. Let's address the + // case of zero z-coordinates generally. - // If self is the identity, return rhs. Otherwise, return self. The other cases will be - // predicated on neither self nor rhs being the identity. + // If self is the identity, return rhs. Otherwise, return self. The + // other cases will be predicated on neither self nor rhs being the + // identity. let f1 = self.is_identity(); let res = G1Projective::conditional_select(self, &G1Projective::from(rhs), f1); let f2 = rhs.is_identity(); - // If neither are the identity but x1 = x2 and y1 != y2, then return the identity + // If neither are the identity but x1 = x2 and y1 != y2, then return the + // identity let u1 = self.x; let s1 = self.y; let z = self.z.square(); @@ -751,8 +554,9 @@ impl G1Projective { let tt = u1 * m_alt; let rr = rr + tt; - // Correct for x1 != x2 but y1 = -y2, which can occur because p - 1 is divisible by 3. - // libsecp256k1 does this by substituting in an alternative (defined) expression for lambda. + // Correct for x1 != x2 but y1 = -y2, which can occur because p - 1 is + // divisible by 3. libsecp256k1 does this by substituting in an + // alternative (defined) expression for lambda. let degenerate = m.is_zero() & rr.is_zero(); let rr_alt = s1 + s1; let m_alt = m_alt + u1; @@ -896,638 +700,849 @@ impl G1Projective { } } -#[cfg(test)] -mod tests { - use super::*; +#[test] +fn test_beta() { + assert_eq!( + BETA, + Fp::from_bytes(&[ + 0x00u8, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x5f, 0x19, 0x67, 0x2f, 0xdf, 0x76, + 0xce, 0x51, 0xba, 0x69, 0xc6, 0x07, 0x6a, 0x0f, 0x77, 0xea, 0xdd, 0xb3, 0xa9, 0x3b, + 0xe6, 0xf8, 0x96, 0x88, 0xde, 0x17, 0xd8, 0x13, 0x62, 0x0a, 0x00, 0x02, 0x2e, 0x01, + 0xff, 0xff, 0xff, 0xfe, 0xff, 0xfe + ]) + .unwrap() + ); + assert_ne!(BETA, Fp::one()); + assert_ne!(BETA * BETA, Fp::one()); + assert_eq!(BETA * BETA * BETA, Fp::one()); +} - #[test] - fn test_beta() { - assert_eq!( - BETA, - Fp::from_bytes(&[ - 0x00u8, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x5f, 0x19, 0x67, 0x2f, 0xdf, - 0x76, 0xce, 0x51, 0xba, 0x69, 0xc6, 0x07, 0x6a, 0x0f, 0x77, 0xea, 0xdd, 0xb3, 0xa9, - 0x3b, 0xe6, 0xf8, 0x96, 0x88, 0xde, 0x17, 0xd8, 0x13, 0x62, 0x0a, 0x00, 0x02, 0x2e, - 0x01, 0xff, 0xff, 0xff, 0xfe, 0xff, 0xfe - ]) - .unwrap() - ); - assert_ne!(BETA, Fp::one()); - assert_ne!(BETA * BETA, Fp::one()); - assert_eq!(BETA * BETA * BETA, Fp::one()); - } +#[test] +fn test_is_on_curve() { + assert!(bool::from(G1Affine::identity().is_on_curve())); + assert!(bool::from(G1Affine::generator().is_on_curve())); + assert!(bool::from(G1Projective::identity().is_on_curve())); + assert!(bool::from(G1Projective::generator().is_on_curve())); + + let z = Fp::from_raw_unchecked([ + 0xba7a_fa1f_9a6f_e250, + 0xfa0f_5b59_5eaf_e731, + 0x3bdc_4776_94c3_06e7, + 0x2149_be4b_3949_fa24, + 0x64aa_6e06_49b2_078c, + 0x12b1_08ac_3364_3c3e, + ]); + + let gen = G1Affine::generator(); + let mut test = G1Projective { + x: gen.x * (z.square()), + y: gen.y * (z.square() * z), + z, + }; + + assert!(bool::from(test.is_on_curve())); + + test.x = z; + assert!(!bool::from(test.is_on_curve())); +} - #[test] - fn test_is_on_curve() { - assert!(bool::from(G1Affine::identity().is_on_curve())); - assert!(bool::from(G1Affine::generator().is_on_curve())); - assert!(bool::from(G1Projective::identity().is_on_curve())); - assert!(bool::from(G1Projective::generator().is_on_curve())); - - let z = Fp::from_raw_unchecked([ - 0xba7afa1f9a6fe250, - 0xfa0f5b595eafe731, - 0x3bdc477694c306e7, - 0x2149be4b3949fa24, - 0x64aa6e0649b2078c, - 0x12b108ac33643c3e, - ]); +#[test] +fn test_affine_point_equality() { + let a = G1Affine::generator(); + let b = G1Affine::identity(); - let gen = G1Affine::generator(); - let mut test = G1Projective { - x: gen.x * (z.square()), - y: gen.y * (z.square() * z), - z, - }; + assert!(a == a); + assert!(b == b); + assert!(a != b); + assert!(b != a); +} - assert!(bool::from(test.is_on_curve())); +#[test] +fn test_projective_point_equality() { + let a = G1Projective::generator(); + let b = G1Projective::identity(); + + assert!(a == a); + assert!(b == b); + assert!(a != b); + assert!(b != a); + + let z = Fp::from_raw_unchecked([ + 0xba7a_fa1f_9a6f_e250, + 0xfa0f_5b59_5eaf_e731, + 0x3bdc_4776_94c3_06e7, + 0x2149_be4b_3949_fa24, + 0x64aa_6e06_49b2_078c, + 0x12b1_08ac_3364_3c3e, + ]); + + let mut c = G1Projective { + x: a.x * (z.square()), + y: a.y * (z.square() * z), + z, + }; + assert!(bool::from(c.is_on_curve())); + + assert!(a == c); + assert!(b != c); + assert!(c == a); + assert!(c != b); + + c.y = -c.y; + assert!(bool::from(c.is_on_curve())); + + assert!(a != c); + assert!(b != c); + assert!(c != a); + assert!(c != b); + + c.y = -c.y; + c.x = z; + assert!(!bool::from(c.is_on_curve())); + assert!(a != b); + assert!(a != c); + assert!(b != c); +} - test.x = z; - assert!(!bool::from(test.is_on_curve())); - } +#[test] +fn test_conditionally_select_affine() { + let a = G1Affine::generator(); + let b = G1Affine::identity(); - #[test] - fn test_affine_point_equality() { - let a = G1Affine::generator(); - let b = G1Affine::identity(); - - assert!(a == a); - assert!(b == b); - assert!(a != b); - assert!(b != a); - } - - #[test] - fn test_projective_point_equality() { - let a = G1Projective::generator(); - let b = G1Projective::identity(); + assert_eq!(G1Affine::conditional_select(&a, &b, Choice::from(0u8)), a); + assert_eq!(G1Affine::conditional_select(&a, &b, Choice::from(1u8)), b); +} - assert!(a == a); - assert!(b == b); - assert!(a != b); - assert!(b != a); - - let z = Fp::from_raw_unchecked([ - 0xba7afa1f9a6fe250, - 0xfa0f5b595eafe731, - 0x3bdc477694c306e7, - 0x2149be4b3949fa24, - 0x64aa6e0649b2078c, - 0x12b108ac33643c3e, - ]); +#[test] +fn test_conditionally_select_projective() { + let a = G1Projective::generator(); + let b = G1Projective::identity(); + + assert_eq!( + G1Projective::conditional_select(&a, &b, Choice::from(0u8)), + a + ); + assert_eq!( + G1Projective::conditional_select(&a, &b, Choice::from(1u8)), + b + ); +} - let mut c = G1Projective { - x: a.x * (z.square()), - y: a.y * (z.square() * z), - z, - }; - assert!(bool::from(c.is_on_curve())); +#[test] +fn test_projective_to_affine() { + let a = G1Projective::generator(); + let b = G1Projective::identity(); + + assert!(bool::from(G1Affine::from(a).is_on_curve())); + assert!(!bool::from(G1Affine::from(a).is_identity())); + assert!(bool::from(G1Affine::from(b).is_on_curve())); + assert!(bool::from(G1Affine::from(b).is_identity())); + + let z = Fp::from_raw_unchecked([ + 0xba7a_fa1f_9a6f_e250, + 0xfa0f_5b59_5eaf_e731, + 0x3bdc_4776_94c3_06e7, + 0x2149_be4b_3949_fa24, + 0x64aa_6e06_49b2_078c, + 0x12b1_08ac_3364_3c3e, + ]); + + let c = G1Projective { + x: a.x * (z.square()), + y: a.y * (z.square() * z), + z, + }; + + assert_eq!(G1Affine::from(c), G1Affine::generator()); +} - assert!(a == c); - assert!(b != c); - assert!(c == a); - assert!(c != b); +#[test] +fn test_affine_to_projective() { + let a = G1Affine::generator(); + let b = G1Affine::identity(); - c.y = -c.y; - assert!(bool::from(c.is_on_curve())); + assert!(bool::from(G1Projective::from(a).is_on_curve())); + assert!(!bool::from(G1Projective::from(a).is_identity())); + assert!(bool::from(G1Projective::from(b).is_on_curve())); + assert!(bool::from(G1Projective::from(b).is_identity())); +} - assert!(a != c); - assert!(b != c); - assert!(c != a); - assert!(c != b); - - c.y = -c.y; - c.x = z; - assert!(!bool::from(c.is_on_curve())); - assert!(a != b); - assert!(a != c); - assert!(b != c); +#[test] +fn test_doubling() { + { + let tmp = G1Projective::identity().double(); + assert!(bool::from(tmp.is_identity())); + assert!(bool::from(tmp.is_on_curve())); } + { + let tmp = G1Projective::generator().double(); + assert!(!bool::from(tmp.is_identity())); + assert!(bool::from(tmp.is_on_curve())); - #[test] - fn test_conditionally_select_affine() { - let a = G1Affine::generator(); - let b = G1Affine::identity(); - - assert_eq!(G1Affine::conditional_select(&a, &b, Choice::from(0u8)), a); - assert_eq!(G1Affine::conditional_select(&a, &b, Choice::from(1u8)), b); + assert_eq!( + G1Affine::from(tmp), + G1Affine { + x: Fp::from_raw_unchecked([ + 0x53e9_78ce_58a9_ba3c, + 0x3ea0_583c_4f3d_65f9, + 0x4d20_bb47_f001_2960, + 0xa54c_664a_e5b2_b5d9, + 0x26b5_52a3_9d7e_b21f, + 0x0008_895d_26e6_8785, + ]), + y: Fp::from_raw_unchecked([ + 0x7011_0b32_9829_3940, + 0xda33_c539_3f1f_6afc, + 0xb86e_dfd1_6a5a_a785, + 0xaec6_d1c9_e7b1_c895, + 0x25cf_c2b5_22d1_1720, + 0x0636_1c83_f8d0_9b15, + ]), + infinity: 0u8.into() + } + ); } +} - #[test] - fn test_conditionally_select_projective() { - let a = G1Projective::generator(); +#[test] +fn test_projective_addition() { + { + let a = G1Projective::identity(); let b = G1Projective::identity(); + let c = a + b; + assert!(bool::from(c.is_identity())); + assert!(bool::from(c.is_on_curve())); + } + { + let a = G1Projective::identity(); + let mut b = G1Projective::generator(); + { + let z = Fp::from_raw_unchecked([ + 0xba7a_fa1f_9a6f_e250, + 0xfa0f_5b59_5eaf_e731, + 0x3bdc_4776_94c3_06e7, + 0x2149_be4b_3949_fa24, + 0x64aa_6e06_49b2_078c, + 0x12b1_08ac_3364_3c3e, + ]); - assert_eq!( - G1Projective::conditional_select(&a, &b, Choice::from(0u8)), - a - ); - assert_eq!( - G1Projective::conditional_select(&a, &b, Choice::from(1u8)), - b - ); + b = G1Projective { + x: b.x * (z.square()), + y: b.y * (z.square() * z), + z, + }; + } + let c = a + b; + assert!(!bool::from(c.is_identity())); + assert!(bool::from(c.is_on_curve())); + assert!(c == G1Projective::generator()); } + { + let a = G1Projective::identity(); + let mut b = G1Projective::generator(); + { + let z = Fp::from_raw_unchecked([ + 0xba7a_fa1f_9a6f_e250, + 0xfa0f_5b59_5eaf_e731, + 0x3bdc_4776_94c3_06e7, + 0x2149_be4b_3949_fa24, + 0x64aa_6e06_49b2_078c, + 0x12b1_08ac_3364_3c3e, + ]); - #[test] - fn test_projective_to_affine() { - let a = G1Projective::generator(); - let b = G1Projective::identity(); + b = G1Projective { + x: b.x * (z.square()), + y: b.y * (z.square() * z), + z, + }; + } + let c = b + a; + assert!(!bool::from(c.is_identity())); + assert!(bool::from(c.is_on_curve())); + assert!(c == G1Projective::generator()); + } + { + let a = G1Projective::generator().double().double(); // 4P + let b = G1Projective::generator().double(); // 2P + let c = a + b; - assert!(bool::from(G1Affine::from(a).is_on_curve())); - assert!(!bool::from(G1Affine::from(a).is_identity())); - assert!(bool::from(G1Affine::from(b).is_on_curve())); - assert!(bool::from(G1Affine::from(b).is_identity())); - - let z = Fp::from_raw_unchecked([ - 0xba7afa1f9a6fe250, - 0xfa0f5b595eafe731, - 0x3bdc477694c306e7, - 0x2149be4b3949fa24, - 0x64aa6e0649b2078c, - 0x12b108ac33643c3e, - ]); + let mut d = G1Projective::generator(); + for _ in 0..5 { + d = d + G1Projective::generator(); + } + assert!(!bool::from(c.is_identity())); + assert!(bool::from(c.is_on_curve())); + assert!(!bool::from(d.is_identity())); + assert!(bool::from(d.is_on_curve())); + assert_eq!(c, d); + } - let c = G1Projective { - x: a.x * (z.square()), - y: a.y * (z.square() * z), - z, + // Degenerate case + { + let beta = Fp::from_raw_unchecked([ + 0xcd03_c9e4_8671_f071, + 0x5dab_2246_1fcd_a5d2, + 0x5870_42af_d385_1b95, + 0x8eb6_0ebe_01ba_cb9e, + 0x03f9_7d6e_83d0_50d2, + 0x18f0_2065_5463_8741, + ]); + let beta = beta.square(); + let a = G1Projective::generator().double().double(); + let b = G1Projective { + x: a.x * beta, + y: -a.y, + z: a.z, }; + assert!(bool::from(a.is_on_curve())); + assert!(bool::from(b.is_on_curve())); - assert_eq!(G1Affine::from(c), G1Affine::generator()); + let c = a + b; + assert_eq!( + G1Affine::from(c), + G1Affine::from(G1Projective { + x: Fp::from_raw_unchecked([ + 0x29e1_e987_ef68_f2d0, + 0xc5f3_ec53_1db0_3233, + 0xacd6_c4b6_ca19_730f, + 0x18ad_9e82_7bc2_bab7, + 0x46e3_b2c5_785c_c7a9, + 0x07e5_71d4_2d22_ddd6, + ]), + y: Fp::from_raw_unchecked([ + 0x94d1_17a7_e5a5_39e7, + 0x8e17_ef67_3d4b_5d22, + 0x9d74_6aaf_508a_33ea, + 0x8c6d_883d_2516_c9a2, + 0x0bc3_b8d5_fb04_47f7, + 0x07bf_a4c7_210f_4f44, + ]), + z: Fp::one() + }) + ); + assert!(!bool::from(c.is_identity())); + assert!(bool::from(c.is_on_curve())); } +} - #[test] - fn test_affine_to_projective() { - let a = G1Affine::generator(); - let b = G1Affine::identity(); - - assert!(bool::from(G1Projective::from(a).is_on_curve())); - assert!(!bool::from(G1Projective::from(a).is_identity())); - assert!(bool::from(G1Projective::from(b).is_on_curve())); - assert!(bool::from(G1Projective::from(b).is_identity())); +#[test] +fn test_mixed_addition() { + { + let a = G1Affine::identity(); + let b = G1Projective::identity(); + let c = a + b; + assert!(bool::from(c.is_identity())); + assert!(bool::from(c.is_on_curve())); } - - #[test] - fn test_doubling() { + { + let a = G1Affine::identity(); + let mut b = G1Projective::generator(); { - let tmp = G1Projective::identity().double(); - assert!(bool::from(tmp.is_identity())); - assert!(bool::from(tmp.is_on_curve())); + let z = Fp::from_raw_unchecked([ + 0xba7a_fa1f_9a6f_e250, + 0xfa0f_5b59_5eaf_e731, + 0x3bdc_4776_94c3_06e7, + 0x2149_be4b_3949_fa24, + 0x64aa_6e06_49b2_078c, + 0x12b1_08ac_3364_3c3e, + ]); + + b = G1Projective { + x: b.x * (z.square()), + y: b.y * (z.square() * z), + z, + }; } + let c = a + b; + assert!(!bool::from(c.is_identity())); + assert!(bool::from(c.is_on_curve())); + assert!(c == G1Projective::generator()); + } + { + let a = G1Affine::identity(); + let mut b = G1Projective::generator(); { - let tmp = G1Projective::generator().double(); - assert!(!bool::from(tmp.is_identity())); - assert!(bool::from(tmp.is_on_curve())); - - assert_eq!( - G1Affine::from(tmp), - G1Affine { - x: Fp::from_raw_unchecked([ - 0x53e978ce58a9ba3c, - 0x3ea0583c4f3d65f9, - 0x4d20bb47f0012960, - 0xa54c664ae5b2b5d9, - 0x26b552a39d7eb21f, - 0x8895d26e68785 - ]), - y: Fp::from_raw_unchecked([ - 0x70110b3298293940, - 0xda33c5393f1f6afc, - 0xb86edfd16a5aa785, - 0xaec6d1c9e7b1c895, - 0x25cfc2b522d11720, - 0x6361c83f8d09b15 - ]), - infinity: 0u8.into() - } - ); + let z = Fp::from_raw_unchecked([ + 0xba7a_fa1f_9a6f_e250, + 0xfa0f_5b59_5eaf_e731, + 0x3bdc_4776_94c3_06e7, + 0x2149_be4b_3949_fa24, + 0x64aa_6e06_49b2_078c, + 0x12b1_08ac_3364_3c3e, + ]); + + b = G1Projective { + x: b.x * (z.square()), + y: b.y * (z.square() * z), + z, + }; } + let c = b + a; + assert!(!bool::from(c.is_identity())); + assert!(bool::from(c.is_on_curve())); + assert!(c == G1Projective::generator()); } + { + let a = G1Projective::generator().double().double(); // 4P + let b = G1Projective::generator().double(); // 2P + let c = a + b; - #[test] - fn test_projective_addition() { - { - let a = G1Projective::identity(); - let b = G1Projective::identity(); - let c = a + b; - assert!(bool::from(c.is_identity())); - assert!(bool::from(c.is_on_curve())); - } - { - let a = G1Projective::identity(); - let mut b = G1Projective::generator(); - { - let z = Fp::from_raw_unchecked([ - 0xba7afa1f9a6fe250, - 0xfa0f5b595eafe731, - 0x3bdc477694c306e7, - 0x2149be4b3949fa24, - 0x64aa6e0649b2078c, - 0x12b108ac33643c3e, - ]); - - b = G1Projective { - x: b.x * (z.square()), - y: b.y * (z.square() * z), - z, - }; - } - let c = a + b; - assert!(!bool::from(c.is_identity())); - assert!(bool::from(c.is_on_curve())); - assert!(c == G1Projective::generator()); - } - { - let a = G1Projective::identity(); - let mut b = G1Projective::generator(); - { - let z = Fp::from_raw_unchecked([ - 0xba7afa1f9a6fe250, - 0xfa0f5b595eafe731, - 0x3bdc477694c306e7, - 0x2149be4b3949fa24, - 0x64aa6e0649b2078c, - 0x12b108ac33643c3e, - ]); - - b = G1Projective { - x: b.x * (z.square()), - y: b.y * (z.square() * z), - z, - }; - } - let c = b + a; - assert!(!bool::from(c.is_identity())); - assert!(bool::from(c.is_on_curve())); - assert!(c == G1Projective::generator()); + let mut d = G1Projective::generator(); + for _ in 0..5 { + d = d + G1Affine::generator(); } - { - let a = G1Projective::generator().double().double(); // 4P - let b = G1Projective::generator().double(); // 2P - let c = a + b; + assert!(!bool::from(c.is_identity())); + assert!(bool::from(c.is_on_curve())); + assert!(!bool::from(d.is_identity())); + assert!(bool::from(d.is_on_curve())); + assert_eq!(c, d); + } - let mut d = G1Projective::generator(); - for _ in 0..5 { - d = d + G1Projective::generator(); - } - assert!(!bool::from(c.is_identity())); - assert!(bool::from(c.is_on_curve())); - assert!(!bool::from(d.is_identity())); - assert!(bool::from(d.is_on_curve())); - assert_eq!(c, d); - } + // Degenerate case + { + let beta = Fp::from_raw_unchecked([ + 0xcd03_c9e4_8671_f071, + 0x5dab_2246_1fcd_a5d2, + 0x5870_42af_d385_1b95, + 0x8eb6_0ebe_01ba_cb9e, + 0x03f9_7d6e_83d0_50d2, + 0x18f0_2065_5463_8741, + ]); + let beta = beta.square(); + let a = G1Projective::generator().double().double(); + let b = G1Projective { + x: a.x * beta, + y: -a.y, + z: a.z, + }; + let a = G1Affine::from(a); + assert!(bool::from(a.is_on_curve())); + assert!(bool::from(b.is_on_curve())); - // Degenerate case - { - let beta = Fp::from_raw_unchecked([ - 0xcd03c9e48671f071, - 0x5dab22461fcda5d2, - 0x587042afd3851b95, - 0x8eb60ebe01bacb9e, - 0x3f97d6e83d050d2, - 0x18f0206554638741, - ]); - let beta = beta.square(); - let a = G1Projective::generator().double().double(); - let b = G1Projective { - x: a.x * beta, - y: -a.y, - z: a.z, - }; - assert!(bool::from(a.is_on_curve())); - assert!(bool::from(b.is_on_curve())); - - let c = a + b; - assert_eq!( - G1Affine::from(c), - G1Affine::from(G1Projective { - x: Fp::from_raw_unchecked([ - 0x29e1e987ef68f2d0, - 0xc5f3ec531db03233, - 0xacd6c4b6ca19730f, - 0x18ad9e827bc2bab7, - 0x46e3b2c5785cc7a9, - 0x7e571d42d22ddd6 - ]), - y: Fp::from_raw_unchecked([ - 0x94d117a7e5a539e7, - 0x8e17ef673d4b5d22, - 0x9d746aaf508a33ea, - 0x8c6d883d2516c9a2, - 0xbc3b8d5fb0447f7, - 0x7bfa4c7210f4f44 - ]), - z: Fp::one() - }) - ); - assert!(!bool::from(c.is_identity())); - assert!(bool::from(c.is_on_curve())); - } + let c = a + b; + assert_eq!( + G1Affine::from(c), + G1Affine::from(G1Projective { + x: Fp::from_raw_unchecked([ + 0x29e1_e987_ef68_f2d0, + 0xc5f3_ec53_1db0_3233, + 0xacd6_c4b6_ca19_730f, + 0x18ad_9e82_7bc2_bab7, + 0x46e3_b2c5_785c_c7a9, + 0x07e5_71d4_2d22_ddd6, + ]), + y: Fp::from_raw_unchecked([ + 0x94d1_17a7_e5a5_39e7, + 0x8e17_ef67_3d4b_5d22, + 0x9d74_6aaf_508a_33ea, + 0x8c6d_883d_2516_c9a2, + 0x0bc3_b8d5_fb04_47f7, + 0x07bf_a4c7_210f_4f44, + ]), + z: Fp::one() + }) + ); + assert!(!bool::from(c.is_identity())); + assert!(bool::from(c.is_on_curve())); } +} - #[test] - fn test_mixed_addition() { - { - let a = G1Affine::identity(); - let b = G1Projective::identity(); - let c = a + b; - assert!(bool::from(c.is_identity())); - assert!(bool::from(c.is_on_curve())); - } - { - let a = G1Affine::identity(); - let mut b = G1Projective::generator(); - { - let z = Fp::from_raw_unchecked([ - 0xba7afa1f9a6fe250, - 0xfa0f5b595eafe731, - 0x3bdc477694c306e7, - 0x2149be4b3949fa24, - 0x64aa6e0649b2078c, - 0x12b108ac33643c3e, - ]); - - b = G1Projective { - x: b.x * (z.square()), - y: b.y * (z.square() * z), - z, - }; - } - let c = a + b; - assert!(!bool::from(c.is_identity())); - assert!(bool::from(c.is_on_curve())); - assert!(c == G1Projective::generator()); - } - { - let a = G1Affine::identity(); - let mut b = G1Projective::generator(); - { - let z = Fp::from_raw_unchecked([ - 0xba7afa1f9a6fe250, - 0xfa0f5b595eafe731, - 0x3bdc477694c306e7, - 0x2149be4b3949fa24, - 0x64aa6e0649b2078c, - 0x12b108ac33643c3e, - ]); - - b = G1Projective { - x: b.x * (z.square()), - y: b.y * (z.square() * z), - z, - }; +#[test] +fn test_projective_negation_and_subtraction() { + let a = G1Projective::generator().double(); + assert_eq!(a + (-a), G1Projective::identity()); + assert_eq!(a + (-a), a - a); +} + +#[test] +fn test_affine_negation_and_subtraction() { + let a = G1Affine::generator(); + assert_eq!(G1Projective::from(a) + (-a), G1Projective::identity()); + assert_eq!(G1Projective::from(a) + (-a), G1Projective::from(a) - a); +} + +#[test] +fn test_projective_scalar_multiplication() { + let g = G1Projective::generator(); + let a = BlsScalar::from_raw([ + 0x2b56_8297_a56d_a71c, + 0xd8c3_9ecb_0ef3_75d1, + 0x435c_38da_67bf_bf96, + 0x8088_a050_26b6_59b2, + ]); + let b = BlsScalar::from_raw([ + 0x785f_dd9b_26ef_8b85, + 0xc997_f258_3769_5c18, + 0x4c8d_bc39_e7b7_56c1, + 0x70d9_b6cc_6d87_df20, + ]); + let c = a * b; + + assert_eq!((g * a) * b, g * c); +} + +#[test] +fn test_affine_scalar_multiplication() { + let g = G1Affine::generator(); + let a = BlsScalar::from_raw([ + 0x2b56_8297_a56d_a71c, + 0xd8c3_9ecb_0ef3_75d1, + 0x435c_38da_67bf_bf96, + 0x8088_a050_26b6_59b2, + ]); + let b = BlsScalar::from_raw([ + 0x785f_dd9b_26ef_8b85, + 0xc997_f258_3769_5c18, + 0x4c8d_bc39_e7b7_56c1, + 0x70d9_b6cc_6d87_df20, + ]); + let c = a * b; + + assert_eq!(G1Affine::from(g * a) * b, g * c); +} + +#[test] +fn test_is_torsion_free() { + let a = G1Affine { + x: Fp::from_raw_unchecked([ + 0x0aba_f895_b97e_43c8, + 0xba4c_6432_eb9b_61b0, + 0x1250_6f52_adfe_307f, + 0x7502_8c34_3933_6b72, + 0x8474_4f05_b8e9_bd71, + 0x113d_554f_b095_54f7, + ]), + y: Fp::from_raw_unchecked([ + 0x73e9_0e88_f5cf_01c0, + 0x3700_7b65_dd31_97e2, + 0x5cf9_a199_2f0d_7c78, + 0x4f83_c10b_9eb3_330d, + 0xf6a6_3f6f_07f6_0961, + 0x0c53_b5b9_7e63_4df3, + ]), + infinity: 0u8.into(), + }; + assert!(!bool::from(a.is_torsion_free())); + + assert!(bool::from(G1Affine::identity().is_torsion_free())); + assert!(bool::from(G1Affine::generator().is_torsion_free())); +} + +#[test] +fn test_mul_by_x() { + // multiplying by `x` a point in G1 is the same as multiplying by + // the equivalent scalar. + let generator = G1Projective::generator(); + let x = if crate::BLS_X_IS_NEGATIVE { + -BlsScalar::from(crate::BLS_X) + } else { + BlsScalar::from(crate::BLS_X) + }; + assert_eq!(generator.mul_by_x(), generator * x); + + let point = G1Projective::generator() * BlsScalar::from(42); + assert_eq!(point.mul_by_x(), point * x); +} + +#[test] +fn test_clear_cofactor() { + // the generator (and the identity) are always on the curve, + // even after clearing the cofactor + let generator = G1Projective::generator(); + assert!(bool::from(generator.clear_cofactor().is_on_curve())); + let id = G1Projective::identity(); + assert!(bool::from(id.clear_cofactor().is_on_curve())); + + let point = G1Projective { + x: Fp::from_raw_unchecked([ + 0x48af5ff540c817f0, + 0xd73893acaf379d5a, + 0xe6c43584e18e023c, + 0x1eda39c30f188b3e, + 0xf618c6d3ccc0f8d8, + 0x0073542cd671e16c, + ]), + y: Fp::from_raw_unchecked([ + 0x57bf8be79461d0ba, + 0xfc61459cee3547c3, + 0x0d23567df1ef147b, + 0x0ee187bcce1d9b64, + 0xb0c8cfbe9dc8fdc1, + 0x1328661767ef368b, + ]), + z: Fp::from_raw_unchecked([ + 0x3d2d1c670671394e, + 0x0ee3a800a2f7c1ca, + 0x270f4f21da2e5050, + 0xe02840a53f1be768, + 0x55debeb597512690, + 0x08bd25353dc8f791, + ]), + }; + + assert!(bool::from(point.is_on_curve())); + assert!(!bool::from(G1Affine::from(point).is_torsion_free())); + let cleared_point = point.clear_cofactor(); + assert!(bool::from(cleared_point.is_on_curve())); + assert!(bool::from(G1Affine::from(cleared_point).is_torsion_free())); + + // in BLS12-381 the cofactor in G1 can be + // cleared multiplying by (1-x) + let h_eff = BlsScalar::from(1) + BlsScalar::from(crate::BLS_X); + assert_eq!(point.clear_cofactor(), point * h_eff); +} + +#[test] +fn test_batch_normalize() { + let a = G1Projective::generator().double(); + let b = a.double(); + let c = b.double(); + + for a_identity in (0..1).map(|n| n == 1) { + for b_identity in (0..1).map(|n| n == 1) { + for c_identity in (0..1).map(|n| n == 1) { + let mut v = [a, b, c]; + if a_identity { + v[0] = G1Projective::identity() + } + if b_identity { + v[1] = G1Projective::identity() + } + if c_identity { + v[2] = G1Projective::identity() + } + + let mut t = [ + G1Affine::identity(), + G1Affine::identity(), + G1Affine::identity(), + ]; + let expected = [ + G1Affine::from(v[0]), + G1Affine::from(v[1]), + G1Affine::from(v[2]), + ]; + + G1Projective::batch_normalize(&v[..], &mut t[..]); + + assert_eq!(&t[..], &expected[..]); } - let c = b + a; - assert!(!bool::from(c.is_identity())); - assert!(bool::from(c.is_on_curve())); - assert!(c == G1Projective::generator()); } - { - let a = G1Projective::generator().double().double(); // 4P - let b = G1Projective::generator().double(); // 2P - let c = a + b; + } +} - let mut d = G1Projective::generator(); - for _ in 0..5 { - d = d + G1Affine::generator(); - } - assert!(!bool::from(c.is_identity())); - assert!(bool::from(c.is_on_curve())); - assert!(!bool::from(d.is_identity())); - assert!(bool::from(d.is_on_curve())); - assert_eq!(c, d); +mod dusk { + use super::*; + + #[cfg(feature = "serde_req")] + use serde::{de::Visitor, Deserialize, Deserializer, Serialize, Serializer}; + + impl G1Affine { + /// Bytes size of the raw representation + pub const RAW_SIZE: usize = 97; + + /// Raw bytes representation + /// + /// The intended usage of this function is for trusted sets of data where performance is + /// critical. + /// + /// For secure serialization, check `to_bytes` + pub fn to_raw_bytes(&self) -> [u8; Self::RAW_SIZE] { + let mut bytes = [0u8; Self::RAW_SIZE]; + let chunks = bytes.chunks_mut(8); + + self.x + .internal_repr() + .iter() + .chain(self.y.internal_repr().iter()) + .zip(chunks) + .for_each(|(n, c)| c.copy_from_slice(&n.to_le_bytes())); + + bytes[Self::RAW_SIZE - 1] = self.infinity.into(); + + bytes } - // Degenerate case - { - let beta = Fp::from_raw_unchecked([ - 0xcd03c9e48671f071, - 0x5dab22461fcda5d2, - 0x587042afd3851b95, - 0x8eb60ebe01bacb9e, - 0x3f97d6e83d050d2, - 0x18f0206554638741, - ]); - let beta = beta.square(); - let a = G1Projective::generator().double().double(); - let b = G1Projective { - x: a.x * beta, - y: -a.y, - z: a.z, + /// Create a `G1Affine` from a set of bytes created by `G1Affine::to_raw_bytes`. + /// + /// No check is performed and no constant time is granted. The expected usage of this function + /// is for trusted bytes where performance is critical. + /// + /// For secure serialization, check `from_bytes` + /// + /// After generating the point, you can check `is_on_curve` and `is_torsion_free` to grant its + /// security + pub unsafe fn from_slice_unchecked(bytes: &[u8]) -> Self { + let mut x = [0u64; 6]; + let mut y = [0u64; 6]; + let mut z = [0u8; 8]; + + bytes + .chunks_exact(8) + .zip(x.iter_mut().chain(y.iter_mut())) + .for_each(|(c, n)| { + z.copy_from_slice(c); + *n = u64::from_le_bytes(z); + }); + + let x = Fp::from_raw_unchecked(x); + let y = Fp::from_raw_unchecked(y); + + let infinity = if bytes.len() >= Self::RAW_SIZE { + bytes[Self::RAW_SIZE - 1].into() + } else { + 0u8.into() }; - let a = G1Affine::from(a); - assert!(bool::from(a.is_on_curve())); - assert!(bool::from(b.is_on_curve())); - - let c = a + b; - assert_eq!( - G1Affine::from(c), - G1Affine::from(G1Projective { - x: Fp::from_raw_unchecked([ - 0x29e1e987ef68f2d0, - 0xc5f3ec531db03233, - 0xacd6c4b6ca19730f, - 0x18ad9e827bc2bab7, - 0x46e3b2c5785cc7a9, - 0x7e571d42d22ddd6 - ]), - y: Fp::from_raw_unchecked([ - 0x94d117a7e5a539e7, - 0x8e17ef673d4b5d22, - 0x9d746aaf508a33ea, - 0x8c6d883d2516c9a2, - 0xbc3b8d5fb0447f7, - 0x7bfa4c7210f4f44 - ]), - z: Fp::one() - }) - ); - assert!(!bool::from(c.is_identity())); - assert!(bool::from(c.is_on_curve())); + + Self { x, y, infinity } } } - #[test] - fn test_projective_negation_and_subtraction() { - let a = G1Projective::generator().double(); - assert_eq!(a + (-a), G1Projective::identity()); - assert_eq!(a + (-a), a - a); - } + impl Serializable<48> for G1Affine { + type Error = BytesError; - #[test] - fn test_affine_negation_and_subtraction() { - let a = G1Affine::generator(); - assert_eq!(G1Projective::from(a) + (-a), G1Projective::identity()); - assert_eq!(G1Projective::from(a) + (-a), G1Projective::from(a) - a); - } + /// Serializes this element into compressed form. See + /// [`notes::serialization`](crate::notes::serialization) + /// for details about how group elements are serialized. + fn to_bytes(&self) -> [u8; Self::SIZE] { + // Strictly speaking, self.x is zero already when self.infinity is true, but + // to guard against implementation mistakes we do not assume this. + let mut res = + Fp::conditional_select(&self.x, &Fp::zero(), self.infinity.into()).to_bytes(); - #[test] - fn test_projective_scalar_multiplication() { - let g = G1Projective::generator(); - let a = BlsScalar::from_raw([ - 0x2b568297a56da71c, - 0xd8c39ecb0ef375d1, - 0x435c38da67bfbf96, - 0x8088a05026b659b2, - ]); - let b = BlsScalar::from_raw([ - 0x785fdd9b26ef8b85, - 0xc997f25837695c18, - 0x4c8dbc39e7b756c1, - 0x70d9b6cc6d87df20, - ]); - let c = a * b; + // This point is in compressed form, so we set the most significant bit. + res[0] |= 1u8 << 7; - assert_eq!((g * a) * b, g * c); - } + // Is this point at infinity? If so, set the second-most significant bit. + res[0] |= u8::conditional_select(&0u8, &(1u8 << 6), self.infinity.into()); - #[test] - fn test_affine_scalar_multiplication() { - let g = G1Affine::generator(); - let a = BlsScalar::from_raw([ - 0x2b568297a56da71c, - 0xd8c39ecb0ef375d1, - 0x435c38da67bfbf96, - 0x8088a05026b659b2, - ]); - let b = BlsScalar::from_raw([ - 0x785fdd9b26ef8b85, - 0xc997f25837695c18, - 0x4c8dbc39e7b756c1, - 0x70d9b6cc6d87df20, - ]); - let c = a * b; + // Is the y-coordinate the lexicographically largest of the two associated with the + // x-coordinate? If so, set the third-most significant bit so long as this is not + // the point at infinity. + res[0] |= u8::conditional_select( + &0u8, + &(1u8 << 5), + (!Choice::from(self.infinity)) & self.y.lexicographically_largest(), + ); - assert_eq!(G1Affine::from(g * a) * b, g * c); - } + res + } - #[test] - fn test_is_torsion_free() { - let a = G1Affine { - x: Fp::from_raw_unchecked([ - 0xabaf895b97e43c8, - 0xba4c6432eb9b61b0, - 0x12506f52adfe307f, - 0x75028c3439336b72, - 0x84744f05b8e9bd71, - 0x113d554fb09554f7, - ]), - y: Fp::from_raw_unchecked([ - 0x73e90e88f5cf01c0, - 0x37007b65dd3197e2, - 0x5cf9a1992f0d7c78, - 0x4f83c10b9eb3330d, - 0xf6a63f6f07f60961, - 0xc53b5b97e634df3, - ]), - infinity: 0u8.into(), - }; - assert!(!bool::from(a.is_torsion_free())); + /// Attempts to deserialize a compressed element. See + /// [`notes::serialization`](crate::notes::serialization) + /// for details about how group elements are serialized. + fn from_bytes(buf: &[u8; Self::SIZE]) -> Result { + // We already know the point is on the curve because this is established + // by the y-coordinate recovery procedure in from_compressed_unchecked(). - assert!(bool::from(G1Affine::identity().is_torsion_free())); - assert!(bool::from(G1Affine::generator().is_torsion_free())); - } + let compression_flag_set = Choice::from((buf[0] >> 7) & 1); + let infinity_flag_set = Choice::from((buf[0] >> 6) & 1); + let sort_flag_set = Choice::from((buf[0] >> 5) & 1); - #[test] - fn test_mul_by_x() { - // multiplying by `x` a point in G1 is the same as multiplying by - // the equivalent scalar. - let generator = G1Projective::generator(); - let x = if crate::BLS_X_IS_NEGATIVE { - -BlsScalar::from(crate::BLS_X) - } else { - BlsScalar::from(crate::BLS_X) - }; - assert_eq!(generator.mul_by_x(), generator * x); + // Attempt to obtain the x-coordinate + let x = { + let mut tmp = [0; Self::SIZE]; + tmp.copy_from_slice(&buf[..Self::SIZE]); - let point = G1Projective::generator() * BlsScalar::from(42); - assert_eq!(point.mul_by_x(), point * x); - } + // Mask away the flag bits + tmp[0] &= 0b0001_1111; - #[test] - fn test_clear_cofactor() { - // the generator (and the identity) are always on the curve, - // even after clearing the cofactor - let generator = G1Projective::generator(); - assert!(bool::from(generator.clear_cofactor().is_on_curve())); - let id = G1Projective::identity(); - assert!(bool::from(id.clear_cofactor().is_on_curve())); - - let point = G1Projective { - x: Fp::from_raw_unchecked([ - 0x48af5ff540c817f0, - 0xd73893acaf379d5a, - 0xe6c43584e18e023c, - 0x1eda39c30f188b3e, - 0xf618c6d3ccc0f8d8, - 0x0073542cd671e16c, - ]), - y: Fp::from_raw_unchecked([ - 0x57bf8be79461d0ba, - 0xfc61459cee3547c3, - 0x0d23567df1ef147b, - 0x0ee187bcce1d9b64, - 0xb0c8cfbe9dc8fdc1, - 0x1328661767ef368b, - ]), - z: Fp::from_raw_unchecked([ - 0x3d2d1c670671394e, - 0x0ee3a800a2f7c1ca, - 0x270f4f21da2e5050, - 0xe02840a53f1be768, - 0x55debeb597512690, - 0x08bd25353dc8f791, - ]), - }; + Fp::from_bytes(&tmp) + }; - assert!(bool::from(point.is_on_curve())); - assert!(!bool::from(G1Affine::from(point).is_torsion_free())); - let cleared_point = point.clear_cofactor(); - assert!(bool::from(cleared_point.is_on_curve())); - assert!(bool::from(G1Affine::from(cleared_point).is_torsion_free())); + let x: Option = x + .and_then(|x| { + // If the infinity flag is set, return the value assuming + // the x-coordinate is zero and the sort bit is not set. + // + // Otherwise, return a recovered point (assuming the correct + // y-coordinate can be found) so long as the infinity flag + // was not set. + CtOption::new( + G1Affine::identity(), + infinity_flag_set & // Infinity flag should be set + compression_flag_set & // Compression flag should be set + (!sort_flag_set) & // Sort flag should not be set + x.is_zero(), // The x-coordinate should be zero + ) + .or_else(|| { + // Recover a y-coordinate given x by y = sqrt(x^3 + 4) + ((x.square() * x) + B).sqrt().and_then(|y| { + // Switch to the correct y-coordinate if necessary. + let y = Fp::conditional_select( + &y, + &-y, + y.lexicographically_largest() ^ sort_flag_set, + ); + + CtOption::new( + G1Affine { + x, + y, + infinity: infinity_flag_set.into(), + }, + (!infinity_flag_set) & // Infinity flag should not be set + compression_flag_set, // Compression flag should be set + ) + }) + }) + }) + .and_then(|p| CtOption::new(p, p.is_torsion_free())) + .into(); - // in BLS12-381 the cofactor in G1 can be - // cleared multiplying by (1-x) - let h_eff = BlsScalar::from(1) + BlsScalar::from(crate::BLS_X); - assert_eq!(point.clear_cofactor(), point * h_eff); + x.ok_or(BytesError::InvalidData) + } } - #[test] - fn test_batch_normalize() { - let a = G1Projective::generator().double(); - let b = a.double(); - let c = b.double(); - - for a_identity in (0..1).map(|n| n == 1) { - for b_identity in (0..1).map(|n| n == 1) { - for c_identity in (0..1).map(|n| n == 1) { - let mut v = [a, b, c]; - if a_identity { - v[0] = G1Projective::identity() - } - if b_identity { - v[1] = G1Projective::identity() - } - if c_identity { - v[2] = G1Projective::identity() - } + #[cfg(feature = "serde_req")] + impl Serialize for G1Affine { + fn serialize(&self, serializer: S) -> Result + where + S: Serializer, + { + use serde::ser::SerializeTuple; + let mut tup = serializer.serialize_tuple(G1Affine::SIZE)?; + for byte in self.to_bytes().iter() { + tup.serialize_element(byte)?; + } + tup.end() + } + } - let mut t = [ - G1Affine::identity(), - G1Affine::identity(), - G1Affine::identity(), - ]; - let expected = [ - G1Affine::from(v[0]), - G1Affine::from(v[1]), - G1Affine::from(v[2]), - ]; + #[cfg(feature = "serde_req")] + impl<'de> Deserialize<'de> for G1Affine { + fn deserialize(deserializer: D) -> Result + where + D: Deserializer<'de>, + { + struct G1AffineVisitor; + + impl<'de> Visitor<'de> for G1AffineVisitor { + type Value = G1Affine; + + fn expecting(&self, formatter: &mut ::core::fmt::Formatter) -> ::core::fmt::Result { + formatter + .write_str("a 48-byte cannonical compressed G1Affine point from Bls12_381") + } - G1Projective::batch_normalize(&v[..], &mut t[..]); + fn visit_seq(self, mut seq: A) -> Result + where + A: serde::de::SeqAccess<'de>, + { + let mut bytes = [0u8; G1Affine::SIZE]; + for i in 0..G1Affine::SIZE { + bytes[i] = seq + .next_element()? + .ok_or(serde::de::Error::invalid_length(i, &"expected 48 bytes"))?; + } - assert_eq!(&t[..], &expected[..]); + G1Affine::from_bytes(&bytes).map_err(|_| { + serde::de::Error::custom(&"compressed G1Affine was not canonically encoded") + }) } } + + deserializer.deserialize_tuple(G1Affine::SIZE, G1AffineVisitor) } } diff --git a/src/g2.rs b/src/g2.rs index 6d5563b3..e6cd549d 100644 --- a/src/g2.rs +++ b/src/g2.rs @@ -1,18 +1,16 @@ //! This module provides an implementation of the $\mathbb{G}_2$ group of BLS12-381. -use crate::choice; -use crate::fp::Fp; -use crate::fp2::Fp2; -use crate::BlsScalar; - use core::borrow::Borrow; use core::iter::Sum; use core::ops::{Add, AddAssign, Mul, MulAssign, Neg, Sub, SubAssign}; + use dusk_bytes::{Error as BytesError, HexDebug, Serializable}; use subtle::{Choice, ConditionallySelectable, ConstantTimeEq, CtOption}; -#[cfg(feature = "serde_req")] -use serde::{de::Visitor, Deserialize, Deserializer, Serialize, Serializer}; +use crate::choice; +use crate::fp::Fp; +use crate::fp2::Fp2; +use crate::BlsScalar; #[cfg(feature = "rkyv-impl")] use bytecheck::CheckBytes; @@ -96,170 +94,6 @@ impl PartialEq for G2Affine { } } -impl Serializable<96> for G2Affine { - type Error = BytesError; - - /// Serializes this element into compressed form. See [`notes::serialization`](crate::notes::serialization) - /// for details about how group elements are serialized. - fn to_bytes(&self) -> [u8; Self::SIZE] { - let infinity = self.infinity.into(); - - // Strictly speaking, self.x is zero already when self.infinity is true, but - // to guard against implementation mistakes we do not assume this. - let x = Fp2::conditional_select(&self.x, &Fp2::zero(), infinity); - - let mut res = [0; Self::SIZE]; - - (&mut res[0..48]).copy_from_slice(&x.c1.to_bytes()[..]); - (&mut res[48..96]).copy_from_slice(&x.c0.to_bytes()[..]); - - // This point is in compressed form, so we set the most significant bit. - res[0] |= 1u8 << 7; - - // Is this point at infinity? If so, set the second-most significant bit. - res[0] |= u8::conditional_select(&0u8, &(1u8 << 6), infinity); - - // Is the y-coordinate the lexicographically largest of the two associated with the - // x-coordinate? If so, set the third-most significant bit so long as this is not - // the point at infinity. - res[0] |= u8::conditional_select( - &0u8, - &(1u8 << 5), - (!infinity) & self.y.lexicographically_largest(), - ); - - res - } - - /// Attempts to deserialize a compressed element. See [`notes::serialization`](crate::notes::serialization) - /// for details about how group elements are serialized. - fn from_bytes(buf: &[u8; Self::SIZE]) -> Result { - // We already know the point is on the curve because this is established - // by the y-coordinate recovery procedure in from_compressed_unchecked(). - - // Obtain the three flags from the start of the byte sequence - let compression_flag_set = Choice::from((buf[0] >> 7) & 1); - let infinity_flag_set = Choice::from((buf[0] >> 6) & 1); - let sort_flag_set = Choice::from((buf[0] >> 5) & 1); - - // Attempt to obtain the x-coordinate - let xc1 = { - let mut tmp = [0; 48]; - tmp.copy_from_slice(&buf[0..48]); - - // Mask away the flag bits - tmp[0] &= 0b0001_1111; - - Fp::from_bytes(&tmp) - }; - let xc0 = { - let mut tmp = [0; 48]; - tmp.copy_from_slice(&buf[48..96]); - - Fp::from_bytes(&tmp) - }; - - let x: Option = xc1 - .and_then(|xc1| { - xc0.and_then(|xc0| { - let x = Fp2 { c0: xc0, c1: xc1 }; - - // If the infinity flag is set, return the value assuming - // the x-coordinate is zero and the sort bit is not set. - // - // Otherwise, return a recovered point (assuming the correct - // y-coordinate can be found) so long as the infinity flag - // was not set. - CtOption::new( - G2Affine::identity(), - infinity_flag_set & // Infinity flag should be set - compression_flag_set & // Compression flag should be set - (!sort_flag_set) & // Sort flag should not be set - x.is_zero(), // The x-coordinate should be zero - ) - .or_else(|| { - // Recover a y-coordinate given x by y = sqrt(x^3 + 4) - ((x.square() * x) + B).sqrt().and_then(|y| { - // Switch to the correct y-coordinate if necessary. - let y = Fp2::conditional_select( - &y, - &-y, - y.lexicographically_largest() ^ sort_flag_set, - ); - - CtOption::new( - G2Affine { - x, - y, - infinity: infinity_flag_set.into(), - }, - (!infinity_flag_set) & // Infinity flag should not be set - compression_flag_set, // Compression flag should be set - ) - }) - }) - }) - }) - .into(); - - match x { - Some(x) if x.is_torsion_free().unwrap_u8() == 1 => Ok(x), - _ => Err(BytesError::InvalidData), - } - } -} - -#[cfg(feature = "serde_req")] -impl Serialize for G2Affine { - fn serialize(&self, serializer: S) -> Result - where - S: Serializer, - { - use serde::ser::SerializeTuple; - let mut tup = serializer.serialize_tuple(Self::SIZE)?; - for byte in self.to_bytes().iter() { - tup.serialize_element(byte)?; - } - tup.end() - } -} - -#[cfg(feature = "serde_req")] -impl<'de> Deserialize<'de> for G2Affine { - fn deserialize(deserializer: D) -> Result - where - D: Deserializer<'de>, - { - struct G2AffineVisitor; - - impl<'de> Visitor<'de> for G2AffineVisitor { - type Value = G2Affine; - - fn expecting(&self, formatter: &mut ::core::fmt::Formatter) -> ::core::fmt::Result { - formatter.write_str("a 48-byte cannonical compressed G2Affine point from Bls12_381") - } - - fn visit_seq(self, mut seq: A) -> Result - where - A: serde::de::SeqAccess<'de>, - { - let mut bytes = [0u8; G2Affine::SIZE]; - for i in 0..G2Affine::SIZE { - bytes[i] = seq - .next_element()? - .ok_or(serde::de::Error::invalid_length(i, &"expected 48 bytes"))?; - } - - G2Affine::from_bytes(&bytes).map_err(|_| { - serde::de::Error::custom(&"compressed G2Affine was not canonically encoded") - }) - } - } - - deserializer.deserialize_tuple(Self::SIZE, G2AffineVisitor) - } -} - impl<'a> Neg for &'a G2Affine { type Output = G2Affine; @@ -335,20 +169,20 @@ impl_binops_additive_specify_output!(G2Affine, G2Projective, G2Projective); const B: Fp2 = Fp2 { c0: Fp::from_raw_unchecked([ - 0xaa270000000cfff3, - 0x53cc0032fc34000a, - 0x478fe97a6b0a807f, - 0xb1d37ebee6ba24d7, - 0x8ec9733bbf78ab2f, - 0x9d645513d83de7e, + 0xaa27_0000_000c_fff3, + 0x53cc_0032_fc34_000a, + 0x478f_e97a_6b0a_807f, + 0xb1d3_7ebe_e6ba_24d7, + 0x8ec9_733b_bf78_ab2f, + 0x09d6_4551_3d83_de7e, ]), c1: Fp::from_raw_unchecked([ - 0xaa270000000cfff3, - 0x53cc0032fc34000a, - 0x478fe97a6b0a807f, - 0xb1d37ebee6ba24d7, - 0x8ec9733bbf78ab2f, - 0x9d645513d83de7e, + 0xaa27_0000_000c_fff3, + 0x53cc_0032_fc34_000a, + 0x478f_e97a_6b0a_807f, + 0xb1d3_7ebe_e6ba_24d7, + 0x8ec9_733b_bf78_ab2f, + 0x09d6_4551_3d83_de7e, ]), }; @@ -371,112 +205,44 @@ impl G2Affine { G2Affine { x: Fp2 { c0: Fp::from_raw_unchecked([ - 0xf5f28fa202940a10, - 0xb3f5fb2687b4961a, - 0xa1a893b53e2ae580, - 0x9894999d1a3caee9, - 0x6f67b7631863366b, - 0x58191924350bcd7, + 0xf5f2_8fa2_0294_0a10, + 0xb3f5_fb26_87b4_961a, + 0xa1a8_93b5_3e2a_e580, + 0x9894_999d_1a3c_aee9, + 0x6f67_b763_1863_366b, + 0x0581_9192_4350_bcd7, ]), c1: Fp::from_raw_unchecked([ - 0xa5a9c0759e23f606, - 0xaaa0c59dbccd60c3, - 0x3bb17e18e2867806, - 0x1b1ab6cc8541b367, - 0xc2b6ed0ef2158547, - 0x11922a097360edf3, + 0xa5a9_c075_9e23_f606, + 0xaaa0_c59d_bccd_60c3, + 0x3bb1_7e18_e286_7806, + 0x1b1a_b6cc_8541_b367, + 0xc2b6_ed0e_f215_8547, + 0x1192_2a09_7360_edf3, ]), }, y: Fp2 { c0: Fp::from_raw_unchecked([ - 0x4c730af860494c4a, - 0x597cfa1f5e369c5a, - 0xe7e6856caa0a635a, - 0xbbefb5e96e0d495f, - 0x7d3a975f0ef25a2, - 0x83fd8e7e80dae5, + 0x4c73_0af8_6049_4c4a, + 0x597c_fa1f_5e36_9c5a, + 0xe7e6_856c_aa0a_635a, + 0xbbef_b5e9_6e0d_495f, + 0x07d3_a975_f0ef_25a2, + 0x0083_fd8e_7e80_dae5, ]), c1: Fp::from_raw_unchecked([ - 0xadc0fc92df64b05d, - 0x18aa270a2b1461dc, - 0x86adac6a3be4eba0, - 0x79495c4ec93da33a, - 0xe7175850a43ccaed, - 0xb2bc2a163de1bf2, + 0xadc0_fc92_df64_b05d, + 0x18aa_270a_2b14_61dc, + 0x86ad_ac6a_3be4_eba0, + 0x7949_5c4e_c93d_a33a, + 0xe717_5850_a43c_caed, + 0x0b2b_c2a1_63de_1bf2, ]), }, infinity: 0u8.into(), } } - /// Raw bytes representation - /// - /// The intended usage of this function is for trusted sets of data where performance is - /// critical. - /// - /// For secure serialization, check `to_bytes` - pub fn to_raw_bytes(&self) -> [u8; Self::RAW_SIZE] { - let mut bytes = [0u8; Self::RAW_SIZE]; - let chunks = bytes.chunks_mut(8); - - self.x - .c0 - .internal_repr() - .iter() - .chain(self.x.c1.internal_repr().iter()) - .chain(self.y.c0.internal_repr().iter()) - .chain(self.y.c1.internal_repr().iter()) - .zip(chunks) - .for_each(|(n, c)| c.copy_from_slice(&n.to_le_bytes())); - - bytes[Self::RAW_SIZE - 1] = self.infinity.into(); - - bytes - } - - /// Create a `G2Affine` from a set of bytes created by `G2Affine::to_raw_bytes`. - /// - /// No check is performed and no constant time is granted. The expected usage of this function - /// is for trusted bytes where performance is critical. - /// - /// For secure serialization, check `from_bytes` - /// - /// After generating the point, you can check `is_on_curve` and `is_torsion_free` to grant its - /// security - pub unsafe fn from_slice_unchecked(bytes: &[u8]) -> Self { - let mut xc0 = [0u64; 6]; - let mut xc1 = [0u64; 6]; - let mut yc0 = [0u64; 6]; - let mut yc1 = [0u64; 6]; - let mut z = [0u8; 8]; - - xc0.iter_mut() - .chain(xc1.iter_mut()) - .chain(yc0.iter_mut()) - .chain(yc1.iter_mut()) - .zip(bytes.chunks_exact(8)) - .for_each(|(n, c)| { - z.copy_from_slice(c); - *n = u64::from_le_bytes(z); - }); - - let c0 = Fp::from_raw_unchecked(xc0); - let c1 = Fp::from_raw_unchecked(xc1); - let x = Fp2 { c0, c1 }; - - let c0 = Fp::from_raw_unchecked(yc0); - let c1 = Fp::from_raw_unchecked(yc1); - let y = Fp2 { c0, c1 }; - - let infinity = if bytes.len() >= Self::RAW_SIZE { - bytes[Self::RAW_SIZE - 1].into() - } else { - 0u8.into() - }; - - Self { x, y, infinity } - } - /// Returns true if this element is the identity (the point at infinity). #[inline] pub fn is_identity(&self) -> Choice { @@ -646,38 +412,38 @@ impl G2Projective { G2Projective { x: Fp2 { c0: Fp::from_raw_unchecked([ - 0xf5f28fa202940a10, - 0xb3f5fb2687b4961a, - 0xa1a893b53e2ae580, - 0x9894999d1a3caee9, - 0x6f67b7631863366b, - 0x58191924350bcd7, + 0xf5f2_8fa2_0294_0a10, + 0xb3f5_fb26_87b4_961a, + 0xa1a8_93b5_3e2a_e580, + 0x9894_999d_1a3c_aee9, + 0x6f67_b763_1863_366b, + 0x0581_9192_4350_bcd7, ]), c1: Fp::from_raw_unchecked([ - 0xa5a9c0759e23f606, - 0xaaa0c59dbccd60c3, - 0x3bb17e18e2867806, - 0x1b1ab6cc8541b367, - 0xc2b6ed0ef2158547, - 0x11922a097360edf3, + 0xa5a9_c075_9e23_f606, + 0xaaa0_c59d_bccd_60c3, + 0x3bb1_7e18_e286_7806, + 0x1b1a_b6cc_8541_b367, + 0xc2b6_ed0e_f215_8547, + 0x1192_2a09_7360_edf3, ]), }, y: Fp2 { c0: Fp::from_raw_unchecked([ - 0x4c730af860494c4a, - 0x597cfa1f5e369c5a, - 0xe7e6856caa0a635a, - 0xbbefb5e96e0d495f, - 0x7d3a975f0ef25a2, - 0x83fd8e7e80dae5, + 0x4c73_0af8_6049_4c4a, + 0x597c_fa1f_5e36_9c5a, + 0xe7e6_856c_aa0a_635a, + 0xbbef_b5e9_6e0d_495f, + 0x07d3_a975_f0ef_25a2, + 0x0083_fd8e_7e80_dae5, ]), c1: Fp::from_raw_unchecked([ - 0xadc0fc92df64b05d, - 0x18aa270a2b1461dc, - 0x86adac6a3be4eba0, - 0x79495c4ec93da33a, - 0xe7175850a43ccaed, - 0xb2bc2a163de1bf2, + 0xadc0_fc92_df64_b05d, + 0x18aa_270a_2b14_61dc, + 0x86ad_ac6a_3be4_eba0, + 0x7949_5c4e_c93d_a33a, + 0xe717_5850_a43c_caed, + 0x0b2b_c2a1_63de_1bf2, ]), }, z: Fp2::one(), @@ -718,16 +484,18 @@ impl G2Projective { /// Adds this point to another point. pub fn add(&self, rhs: &G2Projective) -> G2Projective { - // This Jacobian point addition technique is based on the implementation in libsecp256k1, - // which assumes that rhs has z=1. Let's address the case of zero z-coordinates generally. + // This Jacobian point addition technique is based on the implementation + // in libsecp256k1, which assumes that rhs has z=1. Let's address the + // case of zero z-coordinates generally. - // If self is the identity, return rhs. Otherwise, return self. The other cases will be - // predicated on neither self nor rhs being the identity. + // If self is the identity, return rhs. Otherwise, return self. The + // other cases will be predicated on neither self nor rhs being the identity. let f1 = self.is_identity(); let res = G2Projective::conditional_select(self, rhs, f1); let f2 = rhs.is_identity(); - // If neither are the identity but x1 = x2 and y1 != y2, then return the identity + // If neither are the identity but x1 = x2 and y1 != y2, then return the + // identity let z = rhs.z.square(); let u1 = self.x * z; let z = z * rhs.z; @@ -747,8 +515,9 @@ impl G2Projective { let tt = u1 * m_alt; let rr = rr + tt; - // Correct for x1 != x2 but y1 = -y2, which can occur because p - 1 is divisible by 3. - // libsecp256k1 does this by substituting in an alternative (defined) expression for lambda. + // Correct for x1 != x2 but y1 = -y2, which can occur because p - 1 is + // divisible by 3. libsecp256k1 does this by substituting in an + // alternative (defined) expression for lambda. let degenerate = m.is_zero() & rr.is_zero(); let rr_alt = s1 + s1; let m_alt = m_alt + u1; @@ -761,7 +530,8 @@ impl G2Projective { let n = n.square(); let n = Fp2::conditional_select(&n, &m, degenerate); let t = rr_alt.square(); - let z3 = m_alt * self.z * rhs.z; // We allow rhs.z != 1, so we must account for this. + // We allow rhs.z != 1, so we must account for this. + let z3 = m_alt * self.z * rhs.z; let z3 = z3 + z3; let q = -q; let t = t + q; @@ -787,16 +557,19 @@ impl G2Projective { /// Adds this point to another point in the affine model. pub fn add_mixed(&self, rhs: &G2Affine) -> G2Projective { - // This Jacobian point addition technique is based on the implementation in libsecp256k1, - // which assumes that rhs has z=1. Let's address the case of zero z-coordinates generally. + // This Jacobian point addition technique is based on the implementation + // in libsecp256k1, which assumes that rhs has z=1. Let's address the + // case of zero z-coordinates generally. - // If self is the identity, return rhs. Otherwise, return self. The other cases will be - // predicated on neither self nor rhs being the identity. + // If self is the identity, return rhs. Otherwise, return self. The + // other cases will be predicated on neither self nor rhs being the + // identity. let f1 = self.is_identity(); let res = G2Projective::conditional_select(self, &G2Projective::from(rhs), f1); let f2 = rhs.is_identity(); - // If neither are the identity but x1 = x2 and y1 != y2, then return the identity + // If neither are the identity but x1 = x2 and y1 != y2, then return + // the identity let u1 = self.x; let s1 = self.y; let z = self.z.square(); @@ -814,8 +587,9 @@ impl G2Projective { let tt = u1 * m_alt; let rr = rr + tt; - // Correct for x1 != x2 but y1 = -y2, which can occur because p - 1 is divisible by 3. - // libsecp256k1 does this by substituting in an alternative (defined) expression for lambda. + // Correct for x1 != x2 but y1 = -y2, which can occur because p - 1 is + // divisible by 3. libsecp256k1 does this by substituting in an + // alternative (defined) expression for lambda. let degenerate = m.is_zero() & rr.is_zero(); let rr_alt = s1 + s1; let m_alt = m_alt + u1; @@ -1057,896 +831,1133 @@ impl G2Projective { } } -#[cfg(test)] -mod tests { - use super::*; - - #[test] - fn test_is_on_curve() { - assert!(bool::from(G2Affine::identity().is_on_curve())); - assert!(bool::from(G2Affine::generator().is_on_curve())); - assert!(bool::from(G2Projective::identity().is_on_curve())); - assert!(bool::from(G2Projective::generator().is_on_curve())); - - let z = Fp2 { - c0: Fp::from_raw_unchecked([ - 0xba7afa1f9a6fe250, - 0xfa0f5b595eafe731, - 0x3bdc477694c306e7, - 0x2149be4b3949fa24, - 0x64aa6e0649b2078c, - 0x12b108ac33643c3e, - ]), - c1: Fp::from_raw_unchecked([ - 0x125325df3d35b5a8, - 0xdc469ef5555d7fe3, - 0x2d716d2443106a9, - 0x5a1db59a6ff37d0, - 0x7cf7784e5300bb8f, - 0x16a88922c7a5e844, - ]), - }; - - let gen = G2Affine::generator(); - let mut test = G2Projective { - x: gen.x * (z.square()), - y: gen.y * (z.square() * z), - z, - }; - - assert!(bool::from(test.is_on_curve())); - - test.x = z; - assert!(!bool::from(test.is_on_curve())); - } +#[test] +fn test_is_on_curve() { + assert!(bool::from(G2Affine::identity().is_on_curve())); + assert!(bool::from(G2Affine::generator().is_on_curve())); + assert!(bool::from(G2Projective::identity().is_on_curve())); + assert!(bool::from(G2Projective::generator().is_on_curve())); + + let z = Fp2 { + c0: Fp::from_raw_unchecked([ + 0xba7a_fa1f_9a6f_e250, + 0xfa0f_5b59_5eaf_e731, + 0x3bdc_4776_94c3_06e7, + 0x2149_be4b_3949_fa24, + 0x64aa_6e06_49b2_078c, + 0x12b1_08ac_3364_3c3e, + ]), + c1: Fp::from_raw_unchecked([ + 0x1253_25df_3d35_b5a8, + 0xdc46_9ef5_555d_7fe3, + 0x02d7_16d2_4431_06a9, + 0x05a1_db59_a6ff_37d0, + 0x7cf7_784e_5300_bb8f, + 0x16a8_8922_c7a5_e844, + ]), + }; + + let gen = G2Affine::generator(); + let mut test = G2Projective { + x: gen.x * (z.square()), + y: gen.y * (z.square() * z), + z, + }; + + assert!(bool::from(test.is_on_curve())); + + test.x = z; + assert!(!bool::from(test.is_on_curve())); +} - #[test] - fn test_affine_point_equality() { - let a = G2Affine::generator(); - let b = G2Affine::identity(); - - assert!(a == a); - assert!(b == b); - assert!(a != b); - assert!(b != a); - } +#[test] +fn test_affine_point_equality() { + let a = G2Affine::generator(); + let b = G2Affine::identity(); - #[test] - fn test_projective_point_equality() { - let a = G2Projective::generator(); - let b = G2Projective::identity(); + assert!(a == a); + assert!(b == b); + assert!(a != b); + assert!(b != a); +} - assert!(a == a); - assert!(b == b); - assert!(a != b); - assert!(b != a); +#[test] +fn test_projective_point_equality() { + let a = G2Projective::generator(); + let b = G2Projective::identity(); + + assert!(a == a); + assert!(b == b); + assert!(a != b); + assert!(b != a); + + let z = Fp2 { + c0: Fp::from_raw_unchecked([ + 0xba7a_fa1f_9a6f_e250, + 0xfa0f_5b59_5eaf_e731, + 0x3bdc_4776_94c3_06e7, + 0x2149_be4b_3949_fa24, + 0x64aa_6e06_49b2_078c, + 0x12b1_08ac_3364_3c3e, + ]), + c1: Fp::from_raw_unchecked([ + 0x1253_25df_3d35_b5a8, + 0xdc46_9ef5_555d_7fe3, + 0x02d7_16d2_4431_06a9, + 0x05a1_db59_a6ff_37d0, + 0x7cf7_784e_5300_bb8f, + 0x16a8_8922_c7a5_e844, + ]), + }; + + let mut c = G2Projective { + x: a.x * (z.square()), + y: a.y * (z.square() * z), + z, + }; + assert!(bool::from(c.is_on_curve())); + + assert!(a == c); + assert!(b != c); + assert!(c == a); + assert!(c != b); + + c.y = -c.y; + assert!(bool::from(c.is_on_curve())); + + assert!(a != c); + assert!(b != c); + assert!(c != a); + assert!(c != b); + + c.y = -c.y; + c.x = z; + assert!(!bool::from(c.is_on_curve())); + assert!(a != b); + assert!(a != c); + assert!(b != c); +} - let z = Fp2 { - c0: Fp::from_raw_unchecked([ - 0xba7afa1f9a6fe250, - 0xfa0f5b595eafe731, - 0x3bdc477694c306e7, - 0x2149be4b3949fa24, - 0x64aa6e0649b2078c, - 0x12b108ac33643c3e, - ]), - c1: Fp::from_raw_unchecked([ - 0x125325df3d35b5a8, - 0xdc469ef5555d7fe3, - 0x2d716d2443106a9, - 0x5a1db59a6ff37d0, - 0x7cf7784e5300bb8f, - 0x16a88922c7a5e844, - ]), - }; +#[test] +fn test_conditionally_select_affine() { + let a = G2Affine::generator(); + let b = G2Affine::identity(); - let mut c = G2Projective { - x: a.x * (z.square()), - y: a.y * (z.square() * z), - z, - }; - assert!(bool::from(c.is_on_curve())); + assert_eq!(G2Affine::conditional_select(&a, &b, Choice::from(0u8)), a); + assert_eq!(G2Affine::conditional_select(&a, &b, Choice::from(1u8)), b); +} - assert!(a == c); - assert!(b != c); - assert!(c == a); - assert!(c != b); +#[test] +fn test_conditionally_select_projective() { + let a = G2Projective::generator(); + let b = G2Projective::identity(); + + assert_eq!( + G2Projective::conditional_select(&a, &b, Choice::from(0u8)), + a + ); + assert_eq!( + G2Projective::conditional_select(&a, &b, Choice::from(1u8)), + b + ); +} - c.y = -c.y; - assert!(bool::from(c.is_on_curve())); +#[test] +fn test_projective_to_affine() { + let a = G2Projective::generator(); + let b = G2Projective::identity(); + + assert!(bool::from(G2Affine::from(a).is_on_curve())); + assert!(!bool::from(G2Affine::from(a).is_identity())); + assert!(bool::from(G2Affine::from(b).is_on_curve())); + assert!(bool::from(G2Affine::from(b).is_identity())); + + let z = Fp2 { + c0: Fp::from_raw_unchecked([ + 0xba7a_fa1f_9a6f_e250, + 0xfa0f_5b59_5eaf_e731, + 0x3bdc_4776_94c3_06e7, + 0x2149_be4b_3949_fa24, + 0x64aa_6e06_49b2_078c, + 0x12b1_08ac_3364_3c3e, + ]), + c1: Fp::from_raw_unchecked([ + 0x1253_25df_3d35_b5a8, + 0xdc46_9ef5_555d_7fe3, + 0x02d7_16d2_4431_06a9, + 0x05a1_db59_a6ff_37d0, + 0x7cf7_784e_5300_bb8f, + 0x16a8_8922_c7a5_e844, + ]), + }; + + let c = G2Projective { + x: a.x * (z.square()), + y: a.y * (z.square() * z), + z, + }; + + assert_eq!(G2Affine::from(c), G2Affine::generator()); +} - assert!(a != c); - assert!(b != c); - assert!(c != a); - assert!(c != b); - - c.y = -c.y; - c.x = z; - assert!(!bool::from(c.is_on_curve())); - assert!(a != b); - assert!(a != c); - assert!(b != c); - } +#[test] +fn test_affine_to_projective() { + let a = G2Affine::generator(); + let b = G2Affine::identity(); - #[test] - fn test_conditionally_select_affine() { - let a = G2Affine::generator(); - let b = G2Affine::identity(); + assert!(bool::from(G2Projective::from(a).is_on_curve())); + assert!(!bool::from(G2Projective::from(a).is_identity())); + assert!(bool::from(G2Projective::from(b).is_on_curve())); + assert!(bool::from(G2Projective::from(b).is_identity())); +} - assert_eq!(G2Affine::conditional_select(&a, &b, Choice::from(0u8)), a); - assert_eq!(G2Affine::conditional_select(&a, &b, Choice::from(1u8)), b); +#[test] +fn test_doubling() { + { + let tmp = G2Projective::identity().double(); + assert!(bool::from(tmp.is_identity())); + assert!(bool::from(tmp.is_on_curve())); } - - #[test] - fn test_conditionally_select_projective() { - let a = G2Projective::generator(); - let b = G2Projective::identity(); + { + let tmp = G2Projective::generator().double(); + assert!(!bool::from(tmp.is_identity())); + assert!(bool::from(tmp.is_on_curve())); assert_eq!( - G2Projective::conditional_select(&a, &b, Choice::from(0u8)), - a - ); - assert_eq!( - G2Projective::conditional_select(&a, &b, Choice::from(1u8)), - b + G2Affine::from(tmp), + G2Affine { + x: Fp2 { + c0: Fp::from_raw_unchecked([ + 0xe9d9_e2da_9620_f98b, + 0x54f1_1993_46b9_7f36, + 0x3db3_b820_376b_ed27, + 0xcfdb_31c9_b0b6_4f4c, + 0x41d7_c127_8635_4493, + 0x0571_0794_c255_c064, + ]), + c1: Fp::from_raw_unchecked([ + 0xd6c1_d3ca_6ea0_d06e, + 0xda0c_bd90_5595_489f, + 0x4f53_52d4_3479_221d, + 0x8ade_5d73_6f8c_97e0, + 0x48cc_8433_925e_f70e, + 0x08d7_ea71_ea91_ef81, + ]), + }, + y: Fp2 { + c0: Fp::from_raw_unchecked([ + 0x15ba_26eb_4b0d_186f, + 0x0d08_6d64_b7e9_e01e, + 0xc8b8_48dd_652f_4c78, + 0xeecf_46a6_123b_ae4f, + 0x255e_8dd8_b6dc_812a, + 0x1641_42af_21dc_f93f, + ]), + c1: Fp::from_raw_unchecked([ + 0xf9b4_a1a8_9598_4db4, + 0xd417_b114_cccf_f748, + 0x6856_301f_c89f_086e, + 0x41c7_7787_8931_e3da, + 0x3556_b155_066a_2105, + 0x00ac_f7d3_25cb_89cf, + ]), + }, + infinity: 0u8.into() + } ); } +} - #[test] - fn test_projective_to_affine() { - let a = G2Projective::generator(); +#[test] +fn test_projective_addition() { + { + let a = G2Projective::identity(); let b = G2Projective::identity(); - - assert!(bool::from(G2Affine::from(a).is_on_curve())); - assert!(!bool::from(G2Affine::from(a).is_identity())); - assert!(bool::from(G2Affine::from(b).is_on_curve())); - assert!(bool::from(G2Affine::from(b).is_identity())); - - let z = Fp2 { - c0: Fp::from_raw_unchecked([ - 0xba7afa1f9a6fe250, - 0xfa0f5b595eafe731, - 0x3bdc477694c306e7, - 0x2149be4b3949fa24, - 0x64aa6e0649b2078c, - 0x12b108ac33643c3e, - ]), - c1: Fp::from_raw_unchecked([ - 0x125325df3d35b5a8, - 0xdc469ef5555d7fe3, - 0x2d716d2443106a9, - 0x5a1db59a6ff37d0, - 0x7cf7784e5300bb8f, - 0x16a88922c7a5e844, - ]), - }; - - let c = G2Projective { - x: a.x * (z.square()), - y: a.y * (z.square() * z), - z, - }; - - assert_eq!(G2Affine::from(c), G2Affine::generator()); - } - - #[test] - fn test_affine_to_projective() { - let a = G2Affine::generator(); - let b = G2Affine::identity(); - - assert!(bool::from(G2Projective::from(a).is_on_curve())); - assert!(!bool::from(G2Projective::from(a).is_identity())); - assert!(bool::from(G2Projective::from(b).is_on_curve())); - assert!(bool::from(G2Projective::from(b).is_identity())); + let c = a + b; + assert!(bool::from(c.is_identity())); + assert!(bool::from(c.is_on_curve())); } - - #[test] - fn test_doubling() { + { + let a = G2Projective::identity(); + let mut b = G2Projective::generator(); { - let tmp = G2Projective::identity().double(); - assert!(bool::from(tmp.is_identity())); - assert!(bool::from(tmp.is_on_curve())); + let z = Fp2 { + c0: Fp::from_raw_unchecked([ + 0xba7a_fa1f_9a6f_e250, + 0xfa0f_5b59_5eaf_e731, + 0x3bdc_4776_94c3_06e7, + 0x2149_be4b_3949_fa24, + 0x64aa_6e06_49b2_078c, + 0x12b1_08ac_3364_3c3e, + ]), + c1: Fp::from_raw_unchecked([ + 0x1253_25df_3d35_b5a8, + 0xdc46_9ef5_555d_7fe3, + 0x02d7_16d2_4431_06a9, + 0x05a1_db59_a6ff_37d0, + 0x7cf7_784e_5300_bb8f, + 0x16a8_8922_c7a5_e844, + ]), + }; + + b = G2Projective { + x: b.x * (z.square()), + y: b.y * (z.square() * z), + z, + }; } + let c = a + b; + assert!(!bool::from(c.is_identity())); + assert!(bool::from(c.is_on_curve())); + assert!(c == G2Projective::generator()); + } + { + let a = G2Projective::identity(); + let mut b = G2Projective::generator(); { - let tmp = G2Projective::generator().double(); - assert!(!bool::from(tmp.is_identity())); - assert!(bool::from(tmp.is_on_curve())); - - assert_eq!( - G2Affine::from(tmp), - G2Affine { - x: Fp2 { - c0: Fp::from_raw_unchecked([ - 0xe9d9e2da9620f98b, - 0x54f1199346b97f36, - 0x3db3b820376bed27, - 0xcfdb31c9b0b64f4c, - 0x41d7c12786354493, - 0x5710794c255c064 - ]), - c1: Fp::from_raw_unchecked([ - 0xd6c1d3ca6ea0d06e, - 0xda0cbd905595489f, - 0x4f5352d43479221d, - 0x8ade5d736f8c97e0, - 0x48cc8433925ef70e, - 0x8d7ea71ea91ef81 - ]), - }, - y: Fp2 { - c0: Fp::from_raw_unchecked([ - 0x15ba26eb4b0d186f, - 0xd086d64b7e9e01e, - 0xc8b848dd652f4c78, - 0xeecf46a6123bae4f, - 0x255e8dd8b6dc812a, - 0x164142af21dcf93f - ]), - c1: Fp::from_raw_unchecked([ - 0xf9b4a1a895984db4, - 0xd417b114cccff748, - 0x6856301fc89f086e, - 0x41c777878931e3da, - 0x3556b155066a2105, - 0xacf7d325cb89cf - ]), - }, - infinity: 0u8.into() - } - ); + let z = Fp2 { + c0: Fp::from_raw_unchecked([ + 0xba7a_fa1f_9a6f_e250, + 0xfa0f_5b59_5eaf_e731, + 0x3bdc_4776_94c3_06e7, + 0x2149_be4b_3949_fa24, + 0x64aa_6e06_49b2_078c, + 0x12b1_08ac_3364_3c3e, + ]), + c1: Fp::from_raw_unchecked([ + 0x1253_25df_3d35_b5a8, + 0xdc46_9ef5_555d_7fe3, + 0x02d7_16d2_4431_06a9, + 0x05a1_db59_a6ff_37d0, + 0x7cf7_784e_5300_bb8f, + 0x16a8_8922_c7a5_e844, + ]), + }; + + b = G2Projective { + x: b.x * (z.square()), + y: b.y * (z.square() * z), + z, + }; } + let c = b + a; + assert!(!bool::from(c.is_identity())); + assert!(bool::from(c.is_on_curve())); + assert!(c == G2Projective::generator()); } + { + let a = G2Projective::generator().double().double(); // 4P + let b = G2Projective::generator().double(); // 2P + let c = a + b; - #[test] - fn test_projective_addition() { - { - let a = G2Projective::identity(); - let b = G2Projective::identity(); - let c = a + b; - assert!(bool::from(c.is_identity())); - assert!(bool::from(c.is_on_curve())); + let mut d = G2Projective::generator(); + for _ in 0..5 { + d = d + G2Projective::generator(); } - { - let a = G2Projective::identity(); - let mut b = G2Projective::generator(); - { - let z = Fp2 { + assert!(!bool::from(c.is_identity())); + assert!(bool::from(c.is_on_curve())); + assert!(!bool::from(d.is_identity())); + assert!(bool::from(d.is_on_curve())); + assert_eq!(c, d); + } + + // Degenerate case + { + let beta = Fp2 { + c0: Fp::from_raw_unchecked([ + 0xcd03_c9e4_8671_f071, + 0x5dab_2246_1fcd_a5d2, + 0x5870_42af_d385_1b95, + 0x8eb6_0ebe_01ba_cb9e, + 0x03f9_7d6e_83d0_50d2, + 0x18f0_2065_5463_8741, + ]), + c1: Fp::zero(), + }; + let beta = beta.square(); + let a = G2Projective::generator().double().double(); + let b = G2Projective { + x: a.x * beta, + y: -a.y, + z: a.z, + }; + assert!(bool::from(a.is_on_curve())); + assert!(bool::from(b.is_on_curve())); + + let c = a + b; + assert_eq!( + G2Affine::from(c), + G2Affine::from(G2Projective { + x: Fp2 { c0: Fp::from_raw_unchecked([ - 0xba7afa1f9a6fe250, - 0xfa0f5b595eafe731, - 0x3bdc477694c306e7, - 0x2149be4b3949fa24, - 0x64aa6e0649b2078c, - 0x12b108ac33643c3e, + 0x705a_bc79_9ca7_73d3, + 0xfe13_2292_c1d4_bf08, + 0xf37e_ce3e_07b2_b466, + 0x887e_1c43_f447_e301, + 0x1e09_70d0_33bc_77e8, + 0x1985_c81e_20a6_93f2, ]), c1: Fp::from_raw_unchecked([ - 0x125325df3d35b5a8, - 0xdc469ef5555d7fe3, - 0x2d716d2443106a9, - 0x5a1db59a6ff37d0, - 0x7cf7784e5300bb8f, - 0x16a88922c7a5e844, - ]), - }; - - b = G2Projective { - x: b.x * (z.square()), - y: b.y * (z.square() * z), - z, - }; - } - let c = a + b; - assert!(!bool::from(c.is_identity())); - assert!(bool::from(c.is_on_curve())); - assert!(c == G2Projective::generator()); - } - { - let a = G2Projective::identity(); - let mut b = G2Projective::generator(); - { - let z = Fp2 { + 0x1d79_b25d_b36a_b924, + 0x2394_8e4d_5296_39d3, + 0x471b_a7fb_0d00_6297, + 0x2c36_d4b4_465d_c4c0, + 0x82bb_c3cf_ec67_f538, + 0x051d_2728_b67b_f952, + ]) + }, + y: Fp2 { c0: Fp::from_raw_unchecked([ - 0xba7afa1f9a6fe250, - 0xfa0f5b595eafe731, - 0x3bdc477694c306e7, - 0x2149be4b3949fa24, - 0x64aa6e0649b2078c, - 0x12b108ac33643c3e, + 0x41b1_bbf6_576c_0abf, + 0xb6cc_9371_3f7a_0f9a, + 0x6b65_b43e_48f3_f01f, + 0xfb7a_4cfc_af81_be4f, + 0x3e32_dadc_6ec2_2cb6, + 0x0bb0_fc49_d798_07e3, ]), c1: Fp::from_raw_unchecked([ - 0x125325df3d35b5a8, - 0xdc469ef5555d7fe3, - 0x2d716d2443106a9, - 0x5a1db59a6ff37d0, - 0x7cf7784e5300bb8f, - 0x16a88922c7a5e844, - ]), - }; + 0x7d13_9778_8f5f_2ddf, + 0xab29_0714_4ff0_d8e8, + 0x5b75_73e0_cdb9_1f92, + 0x4cb8_932d_d31d_af28, + 0x62bb_fac6_db05_2a54, + 0x11f9_5c16_d14c_3bbe, + ]) + }, + z: Fp2::one() + }) + ); + assert!(!bool::from(c.is_identity())); + assert!(bool::from(c.is_on_curve())); + } +} - b = G2Projective { - x: b.x * (z.square()), - y: b.y * (z.square() * z), - z, - }; - } - let c = b + a; - assert!(!bool::from(c.is_identity())); - assert!(bool::from(c.is_on_curve())); - assert!(c == G2Projective::generator()); - } +#[test] +fn test_mixed_addition() { + { + let a = G2Affine::identity(); + let b = G2Projective::identity(); + let c = a + b; + assert!(bool::from(c.is_identity())); + assert!(bool::from(c.is_on_curve())); + } + { + let a = G2Affine::identity(); + let mut b = G2Projective::generator(); { - let a = G2Projective::generator().double().double(); // 4P - let b = G2Projective::generator().double(); // 2P - let c = a + b; + let z = Fp2 { + c0: Fp::from_raw_unchecked([ + 0xba7a_fa1f_9a6f_e250, + 0xfa0f_5b59_5eaf_e731, + 0x3bdc_4776_94c3_06e7, + 0x2149_be4b_3949_fa24, + 0x64aa_6e06_49b2_078c, + 0x12b1_08ac_3364_3c3e, + ]), + c1: Fp::from_raw_unchecked([ + 0x1253_25df_3d35_b5a8, + 0xdc46_9ef5_555d_7fe3, + 0x02d7_16d2_4431_06a9, + 0x05a1_db59_a6ff_37d0, + 0x7cf7_784e_5300_bb8f, + 0x16a8_8922_c7a5_e844, + ]), + }; - let mut d = G2Projective::generator(); - for _ in 0..5 { - d = d + G2Projective::generator(); - } - assert!(!bool::from(c.is_identity())); - assert!(bool::from(c.is_on_curve())); - assert!(!bool::from(d.is_identity())); - assert!(bool::from(d.is_on_curve())); - assert_eq!(c, d); + b = G2Projective { + x: b.x * (z.square()), + y: b.y * (z.square() * z), + z, + }; } - - // Degenerate case + let c = a + b; + assert!(!bool::from(c.is_identity())); + assert!(bool::from(c.is_on_curve())); + assert!(c == G2Projective::generator()); + } + { + let a = G2Affine::identity(); + let mut b = G2Projective::generator(); { - let beta = Fp2 { + let z = Fp2 { c0: Fp::from_raw_unchecked([ - 0xcd03c9e48671f071, - 0x5dab22461fcda5d2, - 0x587042afd3851b95, - 0x8eb60ebe01bacb9e, - 0x3f97d6e83d050d2, - 0x18f0206554638741, + 0xba7a_fa1f_9a6f_e250, + 0xfa0f_5b59_5eaf_e731, + 0x3bdc_4776_94c3_06e7, + 0x2149_be4b_3949_fa24, + 0x64aa_6e06_49b2_078c, + 0x12b1_08ac_3364_3c3e, + ]), + c1: Fp::from_raw_unchecked([ + 0x1253_25df_3d35_b5a8, + 0xdc46_9ef5_555d_7fe3, + 0x02d7_16d2_4431_06a9, + 0x05a1_db59_a6ff_37d0, + 0x7cf7_784e_5300_bb8f, + 0x16a8_8922_c7a5_e844, ]), - c1: Fp::zero(), }; - let beta = beta.square(); - let a = G2Projective::generator().double().double(); - let b = G2Projective { - x: a.x * beta, - y: -a.y, - z: a.z, + + b = G2Projective { + x: b.x * (z.square()), + y: b.y * (z.square() * z), + z, }; - assert!(bool::from(a.is_on_curve())); - assert!(bool::from(b.is_on_curve())); - - let c = a + b; - assert_eq!( - G2Affine::from(c), - G2Affine::from(G2Projective { - x: Fp2 { - c0: Fp::from_raw_unchecked([ - 0x705abc799ca773d3, - 0xfe132292c1d4bf08, - 0xf37ece3e07b2b466, - 0x887e1c43f447e301, - 0x1e0970d033bc77e8, - 0x1985c81e20a693f2 - ]), - c1: Fp::from_raw_unchecked([ - 0x1d79b25db36ab924, - 0x23948e4d529639d3, - 0x471ba7fb0d006297, - 0x2c36d4b4465dc4c0, - 0x82bbc3cfec67f538, - 0x51d2728b67bf952 - ]) - }, - y: Fp2 { - c0: Fp::from_raw_unchecked([ - 0x41b1bbf6576c0abf, - 0xb6cc93713f7a0f9a, - 0x6b65b43e48f3f01f, - 0xfb7a4cfcaf81be4f, - 0x3e32dadc6ec22cb6, - 0xbb0fc49d79807e3 - ]), - c1: Fp::from_raw_unchecked([ - 0x7d1397788f5f2ddf, - 0xab2907144ff0d8e8, - 0x5b7573e0cdb91f92, - 0x4cb8932dd31daf28, - 0x62bbfac6db052a54, - 0x11f95c16d14c3bbe - ]) - }, - z: Fp2::one() - }) - ); - assert!(!bool::from(c.is_identity())); - assert!(bool::from(c.is_on_curve())); } + let c = b + a; + assert!(!bool::from(c.is_identity())); + assert!(bool::from(c.is_on_curve())); + assert!(c == G2Projective::generator()); } + { + let a = G2Projective::generator().double().double(); // 4P + let b = G2Projective::generator().double(); // 2P + let c = a + b; - #[test] - fn test_mixed_addition() { - { - let a = G2Affine::identity(); - let b = G2Projective::identity(); - let c = a + b; - assert!(bool::from(c.is_identity())); - assert!(bool::from(c.is_on_curve())); + let mut d = G2Projective::generator(); + for _ in 0..5 { + d = d + G2Affine::generator(); } - { - let a = G2Affine::identity(); - let mut b = G2Projective::generator(); - { - let z = Fp2 { + assert!(!bool::from(c.is_identity())); + assert!(bool::from(c.is_on_curve())); + assert!(!bool::from(d.is_identity())); + assert!(bool::from(d.is_on_curve())); + assert_eq!(c, d); + } + + // Degenerate case + { + let beta = Fp2 { + c0: Fp::from_raw_unchecked([ + 0xcd03_c9e4_8671_f071, + 0x5dab_2246_1fcd_a5d2, + 0x5870_42af_d385_1b95, + 0x8eb6_0ebe_01ba_cb9e, + 0x03f9_7d6e_83d0_50d2, + 0x18f0_2065_5463_8741, + ]), + c1: Fp::zero(), + }; + let beta = beta.square(); + let a = G2Projective::generator().double().double(); + let b = G2Projective { + x: a.x * beta, + y: -a.y, + z: a.z, + }; + let a = G2Affine::from(a); + assert!(bool::from(a.is_on_curve())); + assert!(bool::from(b.is_on_curve())); + + let c = a + b; + assert_eq!( + G2Affine::from(c), + G2Affine::from(G2Projective { + x: Fp2 { c0: Fp::from_raw_unchecked([ - 0xba7afa1f9a6fe250, - 0xfa0f5b595eafe731, - 0x3bdc477694c306e7, - 0x2149be4b3949fa24, - 0x64aa6e0649b2078c, - 0x12b108ac33643c3e, + 0x705a_bc79_9ca7_73d3, + 0xfe13_2292_c1d4_bf08, + 0xf37e_ce3e_07b2_b466, + 0x887e_1c43_f447_e301, + 0x1e09_70d0_33bc_77e8, + 0x1985_c81e_20a6_93f2, ]), c1: Fp::from_raw_unchecked([ - 0x125325df3d35b5a8, - 0xdc469ef5555d7fe3, - 0x2d716d2443106a9, - 0x5a1db59a6ff37d0, - 0x7cf7784e5300bb8f, - 0x16a88922c7a5e844, - ]), - }; - - b = G2Projective { - x: b.x * (z.square()), - y: b.y * (z.square() * z), - z, - }; - } - let c = a + b; - assert!(!bool::from(c.is_identity())); - assert!(bool::from(c.is_on_curve())); - assert!(c == G2Projective::generator()); - } - { - let a = G2Affine::identity(); - let mut b = G2Projective::generator(); - { - let z = Fp2 { + 0x1d79_b25d_b36a_b924, + 0x2394_8e4d_5296_39d3, + 0x471b_a7fb_0d00_6297, + 0x2c36_d4b4_465d_c4c0, + 0x82bb_c3cf_ec67_f538, + 0x051d_2728_b67b_f952, + ]) + }, + y: Fp2 { c0: Fp::from_raw_unchecked([ - 0xba7afa1f9a6fe250, - 0xfa0f5b595eafe731, - 0x3bdc477694c306e7, - 0x2149be4b3949fa24, - 0x64aa6e0649b2078c, - 0x12b108ac33643c3e, + 0x41b1_bbf6_576c_0abf, + 0xb6cc_9371_3f7a_0f9a, + 0x6b65_b43e_48f3_f01f, + 0xfb7a_4cfc_af81_be4f, + 0x3e32_dadc_6ec2_2cb6, + 0x0bb0_fc49_d798_07e3, ]), c1: Fp::from_raw_unchecked([ - 0x125325df3d35b5a8, - 0xdc469ef5555d7fe3, - 0x2d716d2443106a9, - 0x5a1db59a6ff37d0, - 0x7cf7784e5300bb8f, - 0x16a88922c7a5e844, - ]), - }; + 0x7d13_9778_8f5f_2ddf, + 0xab29_0714_4ff0_d8e8, + 0x5b75_73e0_cdb9_1f92, + 0x4cb8_932d_d31d_af28, + 0x62bb_fac6_db05_2a54, + 0x11f9_5c16_d14c_3bbe, + ]) + }, + z: Fp2::one() + }) + ); + assert!(!bool::from(c.is_identity())); + assert!(bool::from(c.is_on_curve())); + } +} + +#[test] +fn test_projective_negation_and_subtraction() { + let a = G2Projective::generator().double(); + assert_eq!(a + (-a), G2Projective::identity()); + assert_eq!(a + (-a), a - a); +} + +#[test] +fn test_affine_negation_and_subtraction() { + let a = G2Affine::generator(); + assert_eq!(G2Projective::from(a) + (-a), G2Projective::identity()); + assert_eq!(G2Projective::from(a) + (-a), G2Projective::from(a) - a); +} + +#[test] +fn test_projective_scalar_multiplication() { + let g = G2Projective::generator(); + let a = BlsScalar::from_raw([ + 0x2b56_8297_a56d_a71c, + 0xd8c3_9ecb_0ef3_75d1, + 0x435c_38da_67bf_bf96, + 0x8088_a050_26b6_59b2, + ]); + let b = BlsScalar::from_raw([ + 0x785f_dd9b_26ef_8b85, + 0xc997_f258_3769_5c18, + 0x4c8d_bc39_e7b7_56c1, + 0x70d9_b6cc_6d87_df20, + ]); + let c = a * b; + + assert_eq!((g * a) * b, g * c); +} + +#[test] +fn test_affine_scalar_multiplication() { + let g = G2Affine::generator(); + let a = BlsScalar::from_raw([ + 0x2b56_8297_a56d_a71c, + 0xd8c3_9ecb_0ef3_75d1, + 0x435c_38da_67bf_bf96, + 0x8088_a050_26b6_59b2, + ]); + let b = BlsScalar::from_raw([ + 0x785f_dd9b_26ef_8b85, + 0xc997_f258_3769_5c18, + 0x4c8d_bc39_e7b7_56c1, + 0x70d9_b6cc_6d87_df20, + ]); + let c = a * b; + + assert_eq!(G2Affine::from(g * a) * b, g * c); +} - b = G2Projective { - x: b.x * (z.square()), - y: b.y * (z.square() * z), - z, - }; +#[test] +fn test_is_torsion_free() { + let a = G2Affine { + x: Fp2 { + c0: Fp::from_raw_unchecked([ + 0x89f5_50c8_13db_6431, + 0xa50b_e8c4_56cd_8a1a, + 0xa45b_3741_14ca_e851, + 0xbb61_90f5_bf7f_ff63, + 0x970c_a02c_3ba8_0bc7, + 0x02b8_5d24_e840_fbac, + ]), + c1: Fp::from_raw_unchecked([ + 0x6888_bc53_d707_16dc, + 0x3dea_6b41_1768_2d70, + 0xd8f5_f930_500c_a354, + 0x6b5e_cb65_56f5_c155, + 0xc96b_ef04_3477_8ab0, + 0x0508_1505_5150_06ad, + ]), + }, + y: Fp2 { + c0: Fp::from_raw_unchecked([ + 0x3cf1_ea0d_434b_0f40, + 0x1a0d_c610_e603_e333, + 0x7f89_9561_60c7_2fa0, + 0x25ee_03de_cf64_31c5, + 0xeee8_e206_ec0f_e137, + 0x0975_92b2_26df_ef28, + ]), + c1: Fp::from_raw_unchecked([ + 0x71e8_bb5f_2924_7367, + 0xa5fe_049e_2118_31ce, + 0x0ce6_b354_502a_3896, + 0x93b0_1200_0997_314e, + 0x6759_f3b6_aa5b_42ac, + 0x1569_44c4_dfe9_2bbb, + ]), + }, + infinity: 0u8.into(), + }; + assert!(!bool::from(a.is_torsion_free())); + + assert!(bool::from(G2Affine::identity().is_torsion_free())); + assert!(bool::from(G2Affine::generator().is_torsion_free())); +} + +#[cfg(feature = "endo")] +#[test] +fn test_mul_by_x() { + // multiplying by `x` a point in G2 is the same as multiplying by + // the equivalent scalar. + let generator = G2Projective::generator(); + let x = if crate::BLS_X_IS_NEGATIVE { + -BlsScalar::from(crate::BLS_X) + } else { + BlsScalar::from(crate::BLS_X) + }; + assert_eq!(generator.mul_by_x(), generator * x); + + let point = G2Projective::generator() * BlsScalar::from(42); + assert_eq!(point.mul_by_x(), point * x); +} + +#[cfg(feature = "endo")] +#[test] +fn test_psi() { + let generator = G2Projective::generator(); + + // `point` is a random point in the curve + let point = G2Projective { + x: Fp2 { + c0: Fp::from_raw_unchecked([ + 0xee4c8cb7c047eaf2, + 0x44ca22eee036b604, + 0x33b3affb2aefe101, + 0x15d3e45bbafaeb02, + 0x7bfc2154cd7419a4, + 0x0a2d0c2b756e5edc, + ]), + c1: Fp::from_raw_unchecked([ + 0xfc224361029a8777, + 0x4cbf2baab8740924, + 0xc5008c6ec6592c89, + 0xecc2c57b472a9c2d, + 0x8613eafd9d81ffb1, + 0x10fe54daa2d3d495, + ]), + }, + y: Fp2 { + c0: Fp::from_raw_unchecked([ + 0x7de7edc43953b75c, + 0x58be1d2de35e87dc, + 0x5731d30b0e337b40, + 0xbe93b60cfeaae4c9, + 0x8b22c203764bedca, + 0x01616c8d1033b771, + ]), + c1: Fp::from_raw_unchecked([ + 0xea126fe476b5733b, + 0x85cee68b5dae1652, + 0x98247779f7272b04, + 0xa649c8b468c6e808, + 0xb5b9a62dff0c4e45, + 0x1555b67fc7bbe73d, + ]), + }, + z: Fp2 { + c0: Fp::from_raw_unchecked([ + 0x0ef2ddffab187c0a, + 0x2424522b7d5ecbfc, + 0xc6f341a3398054f4, + 0x5523ddf409502df0, + 0xd55c0b5a88e0dd97, + 0x066428d704923e52, + ]), + c1: Fp::from_raw_unchecked([ + 0x538bbe0c95b4878d, + 0xad04a50379522881, + 0x6d5c05bf5c12fb64, + 0x4ce4a069a2d34787, + 0x59ea6c8d0dffaeaf, + 0x0d42a083a75bd6f3, + ]), + }, + }; + assert!(bool::from(point.is_on_curve())); + + // psi2(P) = psi(psi(P)) + assert_eq!(generator.psi2(), generator.psi().psi()); + assert_eq!(point.psi2(), point.psi().psi()); + // psi(P) is a morphism + assert_eq!(generator.double().psi(), generator.psi().double()); + assert_eq!(point.psi() + generator.psi(), (point + generator).psi()); + // psi(P) behaves in the same way on the same projective point + let mut normalized_point = [G2Affine::identity()]; + G2Projective::batch_normalize(&[point], &mut normalized_point); + let normalized_point = G2Projective::from(normalized_point[0]); + assert_eq!(point.psi(), normalized_point.psi()); + assert_eq!(point.psi2(), normalized_point.psi2()); +} + +#[test] +fn test_clear_cofactor() { + // `point` is a random point in the curve + let point = G2Projective { + x: Fp2 { + c0: Fp::from_raw_unchecked([ + 0xee4c8cb7c047eaf2, + 0x44ca22eee036b604, + 0x33b3affb2aefe101, + 0x15d3e45bbafaeb02, + 0x7bfc2154cd7419a4, + 0x0a2d0c2b756e5edc, + ]), + c1: Fp::from_raw_unchecked([ + 0xfc224361029a8777, + 0x4cbf2baab8740924, + 0xc5008c6ec6592c89, + 0xecc2c57b472a9c2d, + 0x8613eafd9d81ffb1, + 0x10fe54daa2d3d495, + ]), + }, + y: Fp2 { + c0: Fp::from_raw_unchecked([ + 0x7de7edc43953b75c, + 0x58be1d2de35e87dc, + 0x5731d30b0e337b40, + 0xbe93b60cfeaae4c9, + 0x8b22c203764bedca, + 0x01616c8d1033b771, + ]), + c1: Fp::from_raw_unchecked([ + 0xea126fe476b5733b, + 0x85cee68b5dae1652, + 0x98247779f7272b04, + 0xa649c8b468c6e808, + 0xb5b9a62dff0c4e45, + 0x1555b67fc7bbe73d, + ]), + }, + z: Fp2 { + c0: Fp::from_raw_unchecked([ + 0x0ef2ddffab187c0a, + 0x2424522b7d5ecbfc, + 0xc6f341a3398054f4, + 0x5523ddf409502df0, + 0xd55c0b5a88e0dd97, + 0x066428d704923e52, + ]), + c1: Fp::from_raw_unchecked([ + 0x538bbe0c95b4878d, + 0xad04a50379522881, + 0x6d5c05bf5c12fb64, + 0x4ce4a069a2d34787, + 0x59ea6c8d0dffaeaf, + 0x0d42a083a75bd6f3, + ]), + }, + }; + + assert!(bool::from(point.is_on_curve())); + assert!(!bool::from(G2Affine::from(point).is_torsion_free())); + let cleared_point = point.clear_cofactor(); + + assert!(bool::from(cleared_point.is_on_curve())); + assert!(bool::from(G2Affine::from(cleared_point).is_torsion_free())); + + // the generator (and the identity) are always on the curve, + // even after clearing the cofactor + let generator = G2Projective::generator(); + assert!(bool::from(generator.clear_cofactor().is_on_curve())); + let id = G2Projective::identity(); + assert!(bool::from(id.clear_cofactor().is_on_curve())); + + // test the effect on q-torsion points multiplying by h_eff modulo |BlsScalar| + // h_eff % q = 0x2b116900400069009a40200040001ffff + let h_eff_modq: [u8; 32] = [ + 0xff, 0xff, 0x01, 0x00, 0x04, 0x00, 0x02, 0xa4, 0x09, 0x90, 0x06, 0x00, 0x04, 0x90, 0x16, + 0xb1, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, + ]; + assert_eq!(generator.clear_cofactor(), generator.multiply(&h_eff_modq)); + assert_eq!( + cleared_point.clear_cofactor(), + cleared_point.multiply(&h_eff_modq) + ); +} + +#[test] +fn test_batch_normalize() { + let a = G2Projective::generator().double(); + let b = a.double(); + let c = b.double(); + + for a_identity in (0..1).map(|n| n == 1) { + for b_identity in (0..1).map(|n| n == 1) { + for c_identity in (0..1).map(|n| n == 1) { + let mut v = [a, b, c]; + if a_identity { + v[0] = G2Projective::identity() + } + if b_identity { + v[1] = G2Projective::identity() + } + if c_identity { + v[2] = G2Projective::identity() + } + + let mut t = [ + G2Affine::identity(), + G2Affine::identity(), + G2Affine::identity(), + ]; + let expected = [ + G2Affine::from(v[0]), + G2Affine::from(v[1]), + G2Affine::from(v[2]), + ]; + + G2Projective::batch_normalize(&v[..], &mut t[..]); + + assert_eq!(&t[..], &expected[..]); } - let c = b + a; - assert!(!bool::from(c.is_identity())); - assert!(bool::from(c.is_on_curve())); - assert!(c == G2Projective::generator()); } - { - let a = G2Projective::generator().double().double(); // 4P - let b = G2Projective::generator().double(); // 2P - let c = a + b; + } +} - let mut d = G2Projective::generator(); - for _ in 0..5 { - d = d + G2Affine::generator(); - } - assert!(!bool::from(c.is_identity())); - assert!(bool::from(c.is_on_curve())); - assert!(!bool::from(d.is_identity())); - assert!(bool::from(d.is_on_curve())); - assert_eq!(c, d); +mod dusk { + use super::*; + + #[cfg(feature = "serde_req")] + use serde::{de::Visitor, Deserialize, Deserializer, Serialize, Serializer}; + + impl G2Affine { + /// Raw bytes representation + /// + /// The intended usage of this function is for trusted sets of data where performance is + /// critical. + /// + /// For secure serialization, check `to_bytes` + pub fn to_raw_bytes(&self) -> [u8; Self::RAW_SIZE] { + let mut bytes = [0u8; Self::RAW_SIZE]; + let chunks = bytes.chunks_mut(8); + + self.x + .c0 + .internal_repr() + .iter() + .chain(self.x.c1.internal_repr().iter()) + .chain(self.y.c0.internal_repr().iter()) + .chain(self.y.c1.internal_repr().iter()) + .zip(chunks) + .for_each(|(n, c)| c.copy_from_slice(&n.to_le_bytes())); + + bytes[Self::RAW_SIZE - 1] = self.infinity.into(); + + bytes } - // Degenerate case - { - let beta = Fp2 { - c0: Fp::from_raw_unchecked([ - 0xcd03c9e48671f071, - 0x5dab22461fcda5d2, - 0x587042afd3851b95, - 0x8eb60ebe01bacb9e, - 0x3f97d6e83d050d2, - 0x18f0206554638741, - ]), - c1: Fp::zero(), - }; - let beta = beta.square(); - let a = G2Projective::generator().double().double(); - let b = G2Projective { - x: a.x * beta, - y: -a.y, - z: a.z, + /// Create a `G2Affine` from a set of bytes created by `G2Affine::to_raw_bytes`. + /// + /// No check is performed and no constant time is granted. The expected usage of this function + /// is for trusted bytes where performance is critical. + /// + /// For secure serialization, check `from_bytes` + /// + /// After generating the point, you can check `is_on_curve` and `is_torsion_free` to grant its + /// security + pub unsafe fn from_slice_unchecked(bytes: &[u8]) -> Self { + let mut xc0 = [0u64; 6]; + let mut xc1 = [0u64; 6]; + let mut yc0 = [0u64; 6]; + let mut yc1 = [0u64; 6]; + let mut z = [0u8; 8]; + + xc0.iter_mut() + .chain(xc1.iter_mut()) + .chain(yc0.iter_mut()) + .chain(yc1.iter_mut()) + .zip(bytes.chunks_exact(8)) + .for_each(|(n, c)| { + z.copy_from_slice(c); + *n = u64::from_le_bytes(z); + }); + + let c0 = Fp::from_raw_unchecked(xc0); + let c1 = Fp::from_raw_unchecked(xc1); + let x = Fp2 { c0, c1 }; + + let c0 = Fp::from_raw_unchecked(yc0); + let c1 = Fp::from_raw_unchecked(yc1); + let y = Fp2 { c0, c1 }; + + let infinity = if bytes.len() >= Self::RAW_SIZE { + bytes[Self::RAW_SIZE - 1].into() + } else { + 0u8.into() }; - let a = G2Affine::from(a); - assert!(bool::from(a.is_on_curve())); - assert!(bool::from(b.is_on_curve())); - - let c = a + b; - assert_eq!( - G2Affine::from(c), - G2Affine::from(G2Projective { - x: Fp2 { - c0: Fp::from_raw_unchecked([ - 0x705abc799ca773d3, - 0xfe132292c1d4bf08, - 0xf37ece3e07b2b466, - 0x887e1c43f447e301, - 0x1e0970d033bc77e8, - 0x1985c81e20a693f2 - ]), - c1: Fp::from_raw_unchecked([ - 0x1d79b25db36ab924, - 0x23948e4d529639d3, - 0x471ba7fb0d006297, - 0x2c36d4b4465dc4c0, - 0x82bbc3cfec67f538, - 0x51d2728b67bf952 - ]) - }, - y: Fp2 { - c0: Fp::from_raw_unchecked([ - 0x41b1bbf6576c0abf, - 0xb6cc93713f7a0f9a, - 0x6b65b43e48f3f01f, - 0xfb7a4cfcaf81be4f, - 0x3e32dadc6ec22cb6, - 0xbb0fc49d79807e3 - ]), - c1: Fp::from_raw_unchecked([ - 0x7d1397788f5f2ddf, - 0xab2907144ff0d8e8, - 0x5b7573e0cdb91f92, - 0x4cb8932dd31daf28, - 0x62bbfac6db052a54, - 0x11f95c16d14c3bbe - ]) - }, - z: Fp2::one() - }) - ); - assert!(!bool::from(c.is_identity())); - assert!(bool::from(c.is_on_curve())); + + Self { x, y, infinity } } } - #[test] - fn test_projective_negation_and_subtraction() { - let a = G2Projective::generator().double(); - assert_eq!(a + (-a), G2Projective::identity()); - assert_eq!(a + (-a), a - a); - } + impl Serializable<96> for G2Affine { + type Error = BytesError; - #[test] - fn test_affine_negation_and_subtraction() { - let a = G2Affine::generator(); - assert_eq!(G2Projective::from(a) + (-a), G2Projective::identity()); - assert_eq!(G2Projective::from(a) + (-a), G2Projective::from(a) - a); - } + /// Serializes this element into compressed form. See [`notes::serialization`](crate::notes::serialization) + /// for details about how group elements are serialized. + fn to_bytes(&self) -> [u8; Self::SIZE] { + let infinity = self.infinity.into(); - #[test] - fn test_projective_scalar_multiplication() { - let g = G2Projective::generator(); - let a = BlsScalar::from_raw([ - 0x2b568297a56da71c, - 0xd8c39ecb0ef375d1, - 0x435c38da67bfbf96, - 0x8088a05026b659b2, - ]); - let b = BlsScalar::from_raw([ - 0x785fdd9b26ef8b85, - 0xc997f25837695c18, - 0x4c8dbc39e7b756c1, - 0x70d9b6cc6d87df20, - ]); - let c = a * b; - - assert_eq!((g * a) * b, g * c); - } + // Strictly speaking, self.x is zero already when self.infinity is true, but + // to guard against implementation mistakes we do not assume this. + let x = Fp2::conditional_select(&self.x, &Fp2::zero(), infinity); - #[test] - fn test_affine_scalar_multiplication() { - let g = G2Affine::generator(); - let a = BlsScalar::from_raw([ - 0x2b568297a56da71c, - 0xd8c39ecb0ef375d1, - 0x435c38da67bfbf96, - 0x8088a05026b659b2, - ]); - let b = BlsScalar::from_raw([ - 0x785fdd9b26ef8b85, - 0xc997f25837695c18, - 0x4c8dbc39e7b756c1, - 0x70d9b6cc6d87df20, - ]); - let c = a * b; - - assert_eq!(G2Affine::from(g * a) * b, g * c); - } + let mut res = [0; Self::SIZE]; - #[test] - fn test_is_torsion_free() { - let a = G2Affine { - x: Fp2 { - c0: Fp::from_raw_unchecked([ - 0x89f550c813db6431, - 0xa50be8c456cd8a1a, - 0xa45b374114cae851, - 0xbb6190f5bf7fff63, - 0x970ca02c3ba80bc7, - 0x2b85d24e840fbac, - ]), - c1: Fp::from_raw_unchecked([ - 0x6888bc53d70716dc, - 0x3dea6b4117682d70, - 0xd8f5f930500ca354, - 0x6b5ecb6556f5c155, - 0xc96bef0434778ab0, - 0x5081505515006ad, - ]), - }, - y: Fp2 { - c0: Fp::from_raw_unchecked([ - 0x3cf1ea0d434b0f40, - 0x1a0dc610e603e333, - 0x7f89956160c72fa0, - 0x25ee03decf6431c5, - 0xeee8e206ec0fe137, - 0x97592b226dfef28, - ]), - c1: Fp::from_raw_unchecked([ - 0x71e8bb5f29247367, - 0xa5fe049e211831ce, - 0xce6b354502a3896, - 0x93b012000997314e, - 0x6759f3b6aa5b42ac, - 0x156944c4dfe92bbb, - ]), - }, - infinity: 0u8.into(), - }; - assert!(!bool::from(a.is_torsion_free())); + (&mut res[0..48]).copy_from_slice(&x.c1.to_bytes()[..]); + (&mut res[48..96]).copy_from_slice(&x.c0.to_bytes()[..]); - assert!(bool::from(G2Affine::identity().is_torsion_free())); - assert!(bool::from(G2Affine::generator().is_torsion_free())); - } + // This point is in compressed form, so we set the most significant bit. + res[0] |= 1u8 << 7; - #[cfg(feature = "endo")] - #[test] - fn test_mul_by_x() { - // multiplying by `x` a point in G2 is the same as multiplying by - // the equivalent scalar. - let generator = G2Projective::generator(); - let x = if crate::BLS_X_IS_NEGATIVE { - -BlsScalar::from(crate::BLS_X) - } else { - BlsScalar::from(crate::BLS_X) - }; - assert_eq!(generator.mul_by_x(), generator * x); + // Is this point at infinity? If so, set the second-most significant bit. + res[0] |= u8::conditional_select(&0u8, &(1u8 << 6), infinity); - let point = G2Projective::generator() * BlsScalar::from(42); - assert_eq!(point.mul_by_x(), point * x); - } + // Is the y-coordinate the lexicographically largest of the two associated with the + // x-coordinate? If so, set the third-most significant bit so long as this is not + // the point at infinity. + res[0] |= u8::conditional_select( + &0u8, + &(1u8 << 5), + (!infinity) & self.y.lexicographically_largest(), + ); - #[cfg(feature = "endo")] - #[test] - fn test_psi() { - let generator = G2Projective::generator(); + res + } - // `point` is a random point in the curve - let point = G2Projective { - x: Fp2 { - c0: Fp::from_raw_unchecked([ - 0xee4c8cb7c047eaf2, - 0x44ca22eee036b604, - 0x33b3affb2aefe101, - 0x15d3e45bbafaeb02, - 0x7bfc2154cd7419a4, - 0x0a2d0c2b756e5edc, - ]), - c1: Fp::from_raw_unchecked([ - 0xfc224361029a8777, - 0x4cbf2baab8740924, - 0xc5008c6ec6592c89, - 0xecc2c57b472a9c2d, - 0x8613eafd9d81ffb1, - 0x10fe54daa2d3d495, - ]), - }, - y: Fp2 { - c0: Fp::from_raw_unchecked([ - 0x7de7edc43953b75c, - 0x58be1d2de35e87dc, - 0x5731d30b0e337b40, - 0xbe93b60cfeaae4c9, - 0x8b22c203764bedca, - 0x01616c8d1033b771, - ]), - c1: Fp::from_raw_unchecked([ - 0xea126fe476b5733b, - 0x85cee68b5dae1652, - 0x98247779f7272b04, - 0xa649c8b468c6e808, - 0xb5b9a62dff0c4e45, - 0x1555b67fc7bbe73d, - ]), - }, - z: Fp2 { - c0: Fp::from_raw_unchecked([ - 0x0ef2ddffab187c0a, - 0x2424522b7d5ecbfc, - 0xc6f341a3398054f4, - 0x5523ddf409502df0, - 0xd55c0b5a88e0dd97, - 0x066428d704923e52, - ]), - c1: Fp::from_raw_unchecked([ - 0x538bbe0c95b4878d, - 0xad04a50379522881, - 0x6d5c05bf5c12fb64, - 0x4ce4a069a2d34787, - 0x59ea6c8d0dffaeaf, - 0x0d42a083a75bd6f3, - ]), - }, - }; - assert!(bool::from(point.is_on_curve())); - - // psi2(P) = psi(psi(P)) - assert_eq!(generator.psi2(), generator.psi().psi()); - assert_eq!(point.psi2(), point.psi().psi()); - // psi(P) is a morphism - assert_eq!(generator.double().psi(), generator.psi().double()); - assert_eq!(point.psi() + generator.psi(), (point + generator).psi()); - // psi(P) behaves in the same way on the same projective point - let mut normalized_point = [G2Affine::identity()]; - G2Projective::batch_normalize(&[point], &mut normalized_point); - let normalized_point = G2Projective::from(normalized_point[0]); - assert_eq!(point.psi(), normalized_point.psi()); - assert_eq!(point.psi2(), normalized_point.psi2()); - } + /// Attempts to deserialize a compressed element. See [`notes::serialization`](crate::notes::serialization) + /// for details about how group elements are serialized. + fn from_bytes(buf: &[u8; Self::SIZE]) -> Result { + // We already know the point is on the curve because this is established + // by the y-coordinate recovery procedure in from_compressed_unchecked(). - #[test] - fn test_clear_cofactor() { - // `point` is a random point in the curve - let point = G2Projective { - x: Fp2 { - c0: Fp::from_raw_unchecked([ - 0xee4c8cb7c047eaf2, - 0x44ca22eee036b604, - 0x33b3affb2aefe101, - 0x15d3e45bbafaeb02, - 0x7bfc2154cd7419a4, - 0x0a2d0c2b756e5edc, - ]), - c1: Fp::from_raw_unchecked([ - 0xfc224361029a8777, - 0x4cbf2baab8740924, - 0xc5008c6ec6592c89, - 0xecc2c57b472a9c2d, - 0x8613eafd9d81ffb1, - 0x10fe54daa2d3d495, - ]), - }, - y: Fp2 { - c0: Fp::from_raw_unchecked([ - 0x7de7edc43953b75c, - 0x58be1d2de35e87dc, - 0x5731d30b0e337b40, - 0xbe93b60cfeaae4c9, - 0x8b22c203764bedca, - 0x01616c8d1033b771, - ]), - c1: Fp::from_raw_unchecked([ - 0xea126fe476b5733b, - 0x85cee68b5dae1652, - 0x98247779f7272b04, - 0xa649c8b468c6e808, - 0xb5b9a62dff0c4e45, - 0x1555b67fc7bbe73d, - ]), - }, - z: Fp2 { - c0: Fp::from_raw_unchecked([ - 0x0ef2ddffab187c0a, - 0x2424522b7d5ecbfc, - 0xc6f341a3398054f4, - 0x5523ddf409502df0, - 0xd55c0b5a88e0dd97, - 0x066428d704923e52, - ]), - c1: Fp::from_raw_unchecked([ - 0x538bbe0c95b4878d, - 0xad04a50379522881, - 0x6d5c05bf5c12fb64, - 0x4ce4a069a2d34787, - 0x59ea6c8d0dffaeaf, - 0x0d42a083a75bd6f3, - ]), - }, - }; + // Obtain the three flags from the start of the byte sequence + let compression_flag_set = Choice::from((buf[0] >> 7) & 1); + let infinity_flag_set = Choice::from((buf[0] >> 6) & 1); + let sort_flag_set = Choice::from((buf[0] >> 5) & 1); - assert!(bool::from(point.is_on_curve())); - assert!(!bool::from(G2Affine::from(point).is_torsion_free())); - let cleared_point = point.clear_cofactor(); - - assert!(bool::from(cleared_point.is_on_curve())); - assert!(bool::from(G2Affine::from(cleared_point).is_torsion_free())); - - // the generator (and the identity) are always on the curve, - // even after clearing the cofactor - let generator = G2Projective::generator(); - assert!(bool::from(generator.clear_cofactor().is_on_curve())); - let id = G2Projective::identity(); - assert!(bool::from(id.clear_cofactor().is_on_curve())); - - // test the effect on q-torsion points multiplying by h_eff modulo |BlsScalar| - // h_eff % q = 0x2b116900400069009a40200040001ffff - let h_eff_modq: [u8; 32] = [ - 0xff, 0xff, 0x01, 0x00, 0x04, 0x00, 0x02, 0xa4, 0x09, 0x90, 0x06, 0x00, 0x04, 0x90, - 0x16, 0xb1, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, - ]; - assert_eq!(generator.clear_cofactor(), generator.multiply(&h_eff_modq)); - assert_eq!( - cleared_point.clear_cofactor(), - cleared_point.multiply(&h_eff_modq) - ); + // Attempt to obtain the x-coordinate + let xc1 = { + let mut tmp = [0; 48]; + tmp.copy_from_slice(&buf[0..48]); + + // Mask away the flag bits + tmp[0] &= 0b0001_1111; + + Fp::from_bytes(&tmp) + }; + let xc0 = { + let mut tmp = [0; 48]; + tmp.copy_from_slice(&buf[48..96]); + + Fp::from_bytes(&tmp) + }; + + let x: Option = xc1 + .and_then(|xc1| { + xc0.and_then(|xc0| { + let x = Fp2 { c0: xc0, c1: xc1 }; + + // If the infinity flag is set, return the value assuming + // the x-coordinate is zero and the sort bit is not set. + // + // Otherwise, return a recovered point (assuming the correct + // y-coordinate can be found) so long as the infinity flag + // was not set. + CtOption::new( + G2Affine::identity(), + infinity_flag_set & // Infinity flag should be set + compression_flag_set & // Compression flag should be set + (!sort_flag_set) & // Sort flag should not be set + x.is_zero(), // The x-coordinate should be zero + ) + .or_else(|| { + // Recover a y-coordinate given x by y = sqrt(x^3 + 4) + ((x.square() * x) + B).sqrt().and_then(|y| { + // Switch to the correct y-coordinate if necessary. + let y = Fp2::conditional_select( + &y, + &-y, + y.lexicographically_largest() ^ sort_flag_set, + ); + + CtOption::new( + G2Affine { + x, + y, + infinity: infinity_flag_set.into(), + }, + (!infinity_flag_set) & // Infinity flag should not be set + compression_flag_set, // Compression flag should be set + ) + }) + }) + }) + }) + .into(); + + match x { + Some(x) if x.is_torsion_free().unwrap_u8() == 1 => Ok(x), + _ => Err(BytesError::InvalidData), + } + } } - #[test] - fn test_batch_normalize() { - let a = G2Projective::generator().double(); - let b = a.double(); - let c = b.double(); - - for a_identity in (0..1).map(|n| n == 1) { - for b_identity in (0..1).map(|n| n == 1) { - for c_identity in (0..1).map(|n| n == 1) { - let mut v = [a, b, c]; - if a_identity { - v[0] = G2Projective::identity() - } - if b_identity { - v[1] = G2Projective::identity() - } - if c_identity { - v[2] = G2Projective::identity() - } + #[cfg(feature = "serde_req")] + impl Serialize for G2Affine { + fn serialize(&self, serializer: S) -> Result + where + S: Serializer, + { + use serde::ser::SerializeTuple; + let mut tup = serializer.serialize_tuple(Self::SIZE)?; + for byte in self.to_bytes().iter() { + tup.serialize_element(byte)?; + } + tup.end() + } + } - let mut t = [ - G2Affine::identity(), - G2Affine::identity(), - G2Affine::identity(), - ]; - let expected = [ - G2Affine::from(v[0]), - G2Affine::from(v[1]), - G2Affine::from(v[2]), - ]; + #[cfg(feature = "serde_req")] + impl<'de> Deserialize<'de> for G2Affine { + fn deserialize(deserializer: D) -> Result + where + D: Deserializer<'de>, + { + struct G2AffineVisitor; - G2Projective::batch_normalize(&v[..], &mut t[..]); + impl<'de> Visitor<'de> for G2AffineVisitor { + type Value = G2Affine; - assert_eq!(&t[..], &expected[..]); + fn expecting(&self, formatter: &mut ::core::fmt::Formatter) -> ::core::fmt::Result { + formatter + .write_str("a 48-byte cannonical compressed G2Affine point from Bls12_381") + } + + fn visit_seq(self, mut seq: A) -> Result + where + A: serde::de::SeqAccess<'de>, + { + let mut bytes = [0u8; G2Affine::SIZE]; + for i in 0..G2Affine::SIZE { + bytes[i] = seq + .next_element()? + .ok_or(serde::de::Error::invalid_length(i, &"expected 48 bytes"))?; + } + + G2Affine::from_bytes(&bytes).map_err(|_| { + serde::de::Error::custom(&"compressed G2Affine was not canonically encoded") + }) } } + + deserializer.deserialize_tuple(Self::SIZE, G2AffineVisitor) } } diff --git a/src/lib.rs b/src/lib.rs index c0c53553..c30e8c34 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -43,14 +43,17 @@ pub mod notes { pub mod serialization; } -mod choice; +mod dusk; +use dusk::choice; +#[cfg(all(feature = "groups", feature = "alloc"))] +pub use dusk::multiscalar_mul; + mod scalar; pub use scalar::Scalar as BlsScalar; -pub use scalar::{ROOT_OF_UNITY, TWO_ADACITY}; - #[cfg(feature = "rkyv-impl")] pub use scalar::{ArchivedScalar as ArchivedBlsScalar, ScalarResolver as BlsScalarResolver}; +pub use scalar::{ROOT_OF_UNITY, TWO_ADACITY}; #[cfg(feature = "groups")] mod fp; @@ -61,17 +64,14 @@ mod g1; #[cfg(feature = "groups")] mod g2; -#[cfg(feature = "groups")] -pub use g1::{G1Affine, G1Projective}; - #[cfg(all(feature = "groups", feature = "rkyv-impl"))] pub use g1::{ArchivedG1Affine, G1AffineResolver}; - #[cfg(feature = "groups")] -pub use g2::{G2Affine, G2Projective}; - +pub use g1::{G1Affine, G1Projective}; #[cfg(all(feature = "groups", feature = "rkyv-impl"))] pub use g2::{ArchivedG2Affine, G2AffineResolver}; +#[cfg(feature = "groups")] +pub use g2::{G2Affine, G2Projective}; #[cfg(feature = "groups")] mod fp12; @@ -80,7 +80,7 @@ mod fp6; // The BLS parameter x for BLS12-381 is -0xd201000000010000 #[cfg(feature = "groups")] -const BLS_X: u64 = 0xd201000000010000; +const BLS_X: u64 = 0xd201_0000_0001_0000; #[cfg(feature = "groups")] const BLS_X_IS_NEGATIVE: bool = true; @@ -90,14 +90,11 @@ mod pairings; #[cfg(feature = "pairings")] pub use pairings::{pairing, Gt, MillerLoopResult}; -#[cfg(all(feature = "pairings", feature = "rkyv-impl"))] -pub use pairings::{ArchivedGt, ArchivedMillerLoopResult, GtResolver, MillerLoopResultResolver}; - #[cfg(all(feature = "pairings", feature = "alloc"))] pub use pairings::{multi_miller_loop, G2Prepared}; #[cfg(all(feature = "pairings", feature = "rkyv-impl"))] -pub use pairings::{ArchivedG2Prepared, G2PreparedResolver}; - -#[cfg(all(feature = "groups", feature = "alloc"))] -pub mod multiscalar_mul; +pub use pairings::{ + ArchivedG2Prepared, ArchivedGt, ArchivedMillerLoopResult, G2PreparedResolver, GtResolver, + MillerLoopResultResolver, +}; diff --git a/src/notes/design.rs b/src/notes/design.rs index 69b772d7..11d52261 100644 --- a/src/notes/design.rs +++ b/src/notes/design.rs @@ -18,14 +18,14 @@ //! //! This can be derived using the following sage script: //! -//! ```python +//! ```text //! param = -0xd201000000010000 //! def r(x): //! return (x**4) - (x**2) + 1 //! def q(x): //! return (((x - 1) ** 2) * ((x**4) - (x**2) + 1) // 3) + x //! def g1_h(x): -//! return ((x-1)**2) // 3 +//! return ((x-1)**2) // 3 //! def g2_h(x): //! return ((x**8) - (4 * (x**7)) + (5 * (x**6)) - (4 * (x**4)) + (6 * (x**3)) - (4 * (x**2)) - (4*x) + 13) // 9 //! q = q(param) @@ -33,30 +33,30 @@ //! Fq = GF(q) //! ec = EllipticCurve(Fq, [0, 4]) //! def psqrt(v): -//! assert(not v.is_zero()) -//! a = sqrt(v) -//! b = -a -//! if a < b: -//! return a -//! else: -//! return b +//! assert(not v.is_zero()) +//! a = sqrt(v) +//! b = -a +//! if a < b: +//! return a +//! else: +//! return b //! for x in range(0,100): -//! rhs = Fq(x)^3 + 4 -//! if rhs.is_square(): -//! y = psqrt(rhs) -//! p = ec(x, y) * g1_h(param) -//! if (not p.is_zero()) and (p * r).is_zero(): -//! print("g1 generator: {}".format(p)) -//! break +//! rhs = Fq(x)^3 + 4 +//! if rhs.is_square(): +//! y = psqrt(rhs) +//! p = ec(x, y) * g1_h(param) +//! if (not p.is_zero()) and (p * r).is_zero(): +//! print("g1 generator: {}".format(p)) +//! break //! Fq2. = GF(q^2, modulus=[1, 0, 1]) //! ec2 = EllipticCurve(Fq2, [0, (4 * (1 + i))]) //! assert(ec2.order() == (r * g2_h(param))) //! for x in range(0,100): -//! rhs = (Fq2(x))^3 + (4 * (1 + i)) -//! if rhs.is_square(): -//! y = psqrt(rhs) -//! p = ec2(Fq2(x), y) * g2_h(param) -//! if not p.is_zero() and (p * r).is_zero(): -//! print("g2 generator: {}".format(p)) -//! break +//! rhs = (Fq2(x))^3 + (4 * (1 + i)) +//! if rhs.is_square(): +//! y = psqrt(rhs) +//! p = ec2(Fq2(x), y) * g2_h(param) +//! if not p.is_zero() and (p * r).is_zero(): +//! print("g2 generator: {}".format(p)) +//! break //! ``` diff --git a/src/pairings.rs b/src/pairings.rs index ac6a5a8b..7e1fcb1e 100644 --- a/src/pairings.rs +++ b/src/pairings.rs @@ -5,25 +5,20 @@ use crate::fp2::Fp2; use crate::fp6::Fp6; use crate::{BlsScalar, G1Affine, G2Affine, G2Projective, BLS_X, BLS_X_IS_NEGATIVE}; -use dusk_bytes::Serializable; - use core::ops::{Add, AddAssign, Mul, MulAssign, Neg, Sub, SubAssign}; use subtle::{Choice, ConditionallySelectable, ConstantTimeEq}; -#[cfg(feature = "serde_req")] -use serde::{ - self, de::Visitor, ser::SerializeStruct, Deserialize, Deserializer, Serialize, Serializer, -}; +#[cfg(feature = "alloc")] +use alloc::vec::Vec; + +use dusk_bytes::Serializable; #[cfg(feature = "rkyv-impl")] use bytecheck::CheckBytes; #[cfg(feature = "rkyv-impl")] use rkyv::{Archive, Deserialize as RkyvDeserialize, Serialize as RkyvSerialize}; -#[cfg(feature = "alloc")] -use alloc::vec::Vec; - /// Represents results of a Miller loop, one of the most expensive portions /// of the pairing function. `MillerLoopResult`s cannot be compared with each /// other until `.final_exponentiation()` is called, which is also expensive. @@ -62,12 +57,12 @@ impl MillerLoopResult { // https://eprint.iacr.org/2009/565.pdf #[must_use] fn cyclotomic_square(f: Fp12) -> Fp12 { - let mut z0 = f.c0.c0.clone(); - let mut z4 = f.c0.c1.clone(); - let mut z3 = f.c0.c2.clone(); - let mut z2 = f.c1.c0.clone(); - let mut z1 = f.c1.c1.clone(); - let mut z5 = f.c1.c2.clone(); + let mut z0 = f.c0.c0; + let mut z4 = f.c0.c1; + let mut z3 = f.c0.c2; + let mut z2 = f.c1.c0; + let mut z1 = f.c1.c1; + let mut z5 = f.c1.c2; let (t0, t1) = fp4_square(z0, z1); @@ -311,177 +306,6 @@ pub struct G2Prepared { coeffs: Vec<(Fp2, Fp2, Fp2)>, } -#[cfg(feature = "alloc")] -impl G2Prepared { - /// Raw bytes representation - /// - /// The intended usage of this function is for trusted sets of data where performance is - /// critical. This way, the `infinity` internal attribute will not be stored and the - /// coefficients will be stored without any check. - pub fn to_raw_bytes(&self) -> Vec { - let mut bytes = alloc::vec![0u8; 288 * self.coeffs.len()]; - let mut chunks = bytes.chunks_exact_mut(8); - - self.coeffs.iter().for_each(|(a, b, c)| { - a.c0.internal_repr() - .iter() - .chain(a.c1.internal_repr().iter()) - .chain(b.c0.internal_repr().iter()) - .chain(b.c1.internal_repr().iter()) - .chain(c.c0.internal_repr().iter()) - .chain(c.c1.internal_repr().iter()) - .for_each(|n| { - if let Some(c) = chunks.next() { - c.copy_from_slice(&n.to_le_bytes()) - } - }) - }); - - bytes - } - - /// Create a `G2Prepared` from a set of bytes created by `G2Prepared::to_raw_bytes`. - /// - /// No check is performed and no constant time is granted. The `infinity` attribute is also - /// lost. The expected usage of this function is for trusted bytes where performance is - /// critical. - pub unsafe fn from_slice_unchecked(bytes: &[u8]) -> Self { - let coeffs = bytes - .chunks_exact(288) - .map(|c| { - let mut ac0 = [0u64; 6]; - let mut ac1 = [0u64; 6]; - let mut bc0 = [0u64; 6]; - let mut bc1 = [0u64; 6]; - let mut cc0 = [0u64; 6]; - let mut cc1 = [0u64; 6]; - let mut z = [0u8; 8]; - - ac0.iter_mut() - .chain(ac1.iter_mut()) - .chain(bc0.iter_mut()) - .chain(bc1.iter_mut()) - .chain(cc0.iter_mut()) - .chain(cc1.iter_mut()) - .zip(c.chunks_exact(8)) - .for_each(|(n, c)| { - z.copy_from_slice(c); - *n = u64::from_le_bytes(z); - }); - - let c0 = Fp::from_raw_unchecked(ac0); - let c1 = Fp::from_raw_unchecked(ac1); - let a = Fp2 { c0, c1 }; - - let c0 = Fp::from_raw_unchecked(bc0); - let c1 = Fp::from_raw_unchecked(bc1); - let b = Fp2 { c0, c1 }; - - let c0 = Fp::from_raw_unchecked(cc0); - let c1 = Fp::from_raw_unchecked(cc1); - let c = Fp2 { c0, c1 }; - - (a, b, c) - }) - .collect(); - let infinity = 0u8.into(); - - Self { coeffs, infinity } - } -} - -#[cfg(feature = "serde_req")] -impl Serialize for G2Prepared { - fn serialize(&self, serializer: S) -> Result - where - S: Serializer, - { - let mut g2_prepared = serializer.serialize_struct("struct G2Prepared", 2)?; - // We encode the choice as an u8 field. - g2_prepared.serialize_field("choice", &self.infinity.unwrap_u8())?; - // Since we have serde support for `Fp2` we can treat the `Vec` as a - // regular field. - g2_prepared.serialize_field("coeffs", &self.coeffs)?; - g2_prepared.end() - } -} - -#[cfg(feature = "serde_req")] -impl<'de> Deserialize<'de> for G2Prepared { - fn deserialize(deserializer: D) -> Result - where - D: Deserializer<'de>, - { - enum Field { - Choice, - Coeffs, - } - - impl<'de> Deserialize<'de> for Field { - fn deserialize(deserializer: D) -> Result - where - D: Deserializer<'de>, - { - struct FieldVisitor; - - impl<'de> Visitor<'de> for FieldVisitor { - type Value = Field; - - fn expecting( - &self, - formatter: &mut ::core::fmt::Formatter, - ) -> ::core::fmt::Result { - formatter.write_str("struct G2Prepared") - } - - fn visit_str(self, value: &str) -> Result - where - E: serde::de::Error, - { - match value { - "choice" => Ok(Field::Choice), - "coeffs" => Ok(Field::Coeffs), - _ => Err(serde::de::Error::unknown_field(value, FIELDS)), - } - } - } - - deserializer.deserialize_identifier(FieldVisitor) - } - } - - struct G2PreparedVisitor; - - impl<'de> Visitor<'de> for G2PreparedVisitor { - type Value = G2Prepared; - - fn expecting(&self, formatter: &mut ::core::fmt::Formatter) -> ::core::fmt::Result { - formatter.write_str("struct G2Prepared") - } - - fn visit_seq(self, mut seq: V) -> Result - where - V: serde::de::SeqAccess<'de>, - { - let choice_as_u8: u8 = seq - .next_element()? - .ok_or_else(|| serde::de::Error::invalid_length(0, &self))?; - let coeffs = seq - .next_element()? - .ok_or_else(|| serde::de::Error::invalid_length(0, &self))?; - let choice: Choice = Choice::from(choice_as_u8); - Ok(G2Prepared { - infinity: choice.into(), - coeffs, - }) - } - } - - const FIELDS: &[&str] = &["choice", "coeffs"]; - deserializer.deserialize_struct("G2Prepared", FIELDS, G2PreparedVisitor) - } -} - #[cfg(feature = "alloc")] impl From for G2Prepared { fn from(q: G2Affine) -> G2Prepared { @@ -755,104 +579,282 @@ fn addition_step(r: &mut G2Projective, q: &G2Affine) -> (Fp2, Fp2, Fp2) { (t10, t1, t9) } -#[cfg(test)] -mod tests { +#[test] +fn test_bilinearity() { + use crate::BlsScalar; + + let a = BlsScalar::from_raw([1, 2, 3, 4]).invert().unwrap().square(); + let b = BlsScalar::from_raw([5, 6, 7, 8]).invert().unwrap().square(); + let c = a * b; + + let g = G1Affine::from(G1Affine::generator() * a); + let h = G2Affine::from(G2Affine::generator() * b); + let p = pairing(&g, &h); + + assert!(p != Gt::identity()); + + let expected = G1Affine::from(G1Affine::generator() * c); + + assert_eq!(p, pairing(&expected, &G2Affine::generator())); + assert_eq!( + p, + pairing(&G1Affine::generator(), &G2Affine::generator()) * c + ); +} + +#[test] +fn test_unitary() { + let g = G1Affine::generator(); + let h = G2Affine::generator(); + let p = -pairing(&g, &h); + let q = pairing(&g, &-h); + let r = pairing(&-g, &h); + + assert_eq!(p, q); + assert_eq!(q, r); +} + +#[cfg(feature = "alloc")] +#[test] +fn test_multi_miller_loop() { + let a1 = G1Affine::generator(); + let b1 = G2Affine::generator(); + + let a2 = G1Affine::from( + G1Affine::generator() * BlsScalar::from_raw([1, 2, 3, 4]).invert().unwrap().square(), + ); + let b2 = G2Affine::from( + G2Affine::generator() * BlsScalar::from_raw([4, 2, 2, 4]).invert().unwrap().square(), + ); + + let a3 = G1Affine::identity(); + let b3 = G2Affine::from( + G2Affine::generator() * BlsScalar::from_raw([9, 2, 2, 4]).invert().unwrap().square(), + ); + + let a4 = G1Affine::from( + G1Affine::generator() * BlsScalar::from_raw([5, 5, 5, 5]).invert().unwrap().square(), + ); + let b4 = G2Affine::identity(); + + let a5 = G1Affine::from( + G1Affine::generator() + * BlsScalar::from_raw([323, 32, 3, 1]) + .invert() + .unwrap() + .square(), + ); + let b5 = G2Affine::from( + G2Affine::generator() + * BlsScalar::from_raw([4, 2, 2, 9099]) + .invert() + .unwrap() + .square(), + ); + + let b1_prepared = G2Prepared::from(b1); + let b2_prepared = G2Prepared::from(b2); + let b3_prepared = G2Prepared::from(b3); + let b4_prepared = G2Prepared::from(b4); + let b5_prepared = G2Prepared::from(b5); + + let expected = pairing(&a1, &b1) + + pairing(&a2, &b2) + + pairing(&a3, &b3) + + pairing(&a4, &b4) + + pairing(&a5, &b5); + + let test = multi_miller_loop(&[ + (&a1, &b1_prepared), + (&a2, &b2_prepared), + (&a3, &b3_prepared), + (&a4, &b4_prepared), + (&a5, &b5_prepared), + ]) + .final_exponentiation(); + + assert_eq!(expected, test); +} + +mod dusk { use super::*; - #[test] - fn test_bilinearity() { - use crate::BlsScalar; - let a = BlsScalar::from_raw([1, 2, 3, 4]).invert().unwrap().square(); - let b = BlsScalar::from_raw([5, 6, 7, 8]).invert().unwrap().square(); - let c = a * b; + #[cfg(feature = "serde_req")] + use serde::{ + self, de::Visitor, ser::SerializeStruct, Deserialize, Deserializer, Serialize, Serializer, + }; - let g = G1Affine::from(G1Affine::generator() * a); - let h = G2Affine::from(G2Affine::generator() * b); - let p = pairing(&g, &h); + #[cfg(feature = "alloc")] + impl G2Prepared { + /// Raw bytes representation + /// + /// The intended usage of this function is for trusted sets of data + /// where performance is critical. This way, the `infinity` internal + /// attribute will not be stored and the coefficients will be stored + /// without any check. + pub fn to_raw_bytes(&self) -> Vec { + let mut bytes = alloc::vec![0u8; 288 * self.coeffs.len()]; + let mut chunks = bytes.chunks_exact_mut(8); + + self.coeffs.iter().for_each(|(a, b, c)| { + a.c0.internal_repr() + .iter() + .chain(a.c1.internal_repr().iter()) + .chain(b.c0.internal_repr().iter()) + .chain(b.c1.internal_repr().iter()) + .chain(c.c0.internal_repr().iter()) + .chain(c.c1.internal_repr().iter()) + .for_each(|n| { + if let Some(c) = chunks.next() { + c.copy_from_slice(&n.to_le_bytes()) + } + }) + }); - assert!(p != Gt::identity()); + bytes + } - let expected = G1Affine::from(G1Affine::generator() * c); + /// Create a `G2Prepared` from a set of bytes created by + /// `G2Prepared::to_raw_bytes`. + /// + /// No check is performed and no constant time is granted. The + /// `infinity` attribute is also lost. The expected usage of this + /// function is for trusted bytes where performance is critical. + pub unsafe fn from_slice_unchecked(bytes: &[u8]) -> Self { + let coeffs = bytes + .chunks_exact(288) + .map(|c| { + let mut ac0 = [0u64; 6]; + let mut ac1 = [0u64; 6]; + let mut bc0 = [0u64; 6]; + let mut bc1 = [0u64; 6]; + let mut cc0 = [0u64; 6]; + let mut cc1 = [0u64; 6]; + let mut z = [0u8; 8]; + + ac0.iter_mut() + .chain(ac1.iter_mut()) + .chain(bc0.iter_mut()) + .chain(bc1.iter_mut()) + .chain(cc0.iter_mut()) + .chain(cc1.iter_mut()) + .zip(c.chunks_exact(8)) + .for_each(|(n, c)| { + z.copy_from_slice(c); + *n = u64::from_le_bytes(z); + }); + + let c0 = Fp::from_raw_unchecked(ac0); + let c1 = Fp::from_raw_unchecked(ac1); + let a = Fp2 { c0, c1 }; + + let c0 = Fp::from_raw_unchecked(bc0); + let c1 = Fp::from_raw_unchecked(bc1); + let b = Fp2 { c0, c1 }; + + let c0 = Fp::from_raw_unchecked(cc0); + let c1 = Fp::from_raw_unchecked(cc1); + let c = Fp2 { c0, c1 }; + + (a, b, c) + }) + .collect(); + let infinity = 0u8.into(); - assert_eq!(p, pairing(&expected, &G2Affine::generator())); - assert_eq!( - p, - pairing(&G1Affine::generator(), &G2Affine::generator()) * c - ); + Self { coeffs, infinity } + } } - #[test] - fn test_unitary() { - let g = G1Affine::generator(); - let h = G2Affine::generator(); - let p = -pairing(&g, &h); - let q = pairing(&g, &-h); - let r = pairing(&-g, &h); - - assert_eq!(p, q); - assert_eq!(q, r); + #[cfg(feature = "serde_req")] + impl Serialize for G2Prepared { + fn serialize(&self, serializer: S) -> Result + where + S: Serializer, + { + let mut g2_prepared = serializer.serialize_struct("struct G2Prepared", 2)?; + // We encode the choice as an u8 field. + g2_prepared.serialize_field("choice", &self.infinity.unwrap_u8())?; + // Since we have serde support for `Fp2` we can treat the `Vec` as a + // regular field. + g2_prepared.serialize_field("coeffs", &self.coeffs)?; + g2_prepared.end() + } } - #[cfg(feature = "alloc")] - #[test] - fn test_multi_miller_loop() { - let a1 = G1Affine::generator(); - let b1 = G2Affine::generator(); - - let a2 = G1Affine::from( - G1Affine::generator() * BlsScalar::from_raw([1, 2, 3, 4]).invert().unwrap().square(), - ); - let b2 = G2Affine::from( - G2Affine::generator() * BlsScalar::from_raw([4, 2, 2, 4]).invert().unwrap().square(), - ); - - let a3 = G1Affine::identity(); - let b3 = G2Affine::from( - G2Affine::generator() * BlsScalar::from_raw([9, 2, 2, 4]).invert().unwrap().square(), - ); - - let a4 = G1Affine::from( - G1Affine::generator() * BlsScalar::from_raw([5, 5, 5, 5]).invert().unwrap().square(), - ); - let b4 = G2Affine::identity(); - - let a5 = G1Affine::from( - G1Affine::generator() - * BlsScalar::from_raw([323, 32, 3, 1]) - .invert() - .unwrap() - .square(), - ); - let b5 = G2Affine::from( - G2Affine::generator() - * BlsScalar::from_raw([4, 2, 2, 9099]) - .invert() - .unwrap() - .square(), - ); - - let b1_prepared = G2Prepared::from(b1); - let b2_prepared = G2Prepared::from(b2); - let b3_prepared = G2Prepared::from(b3); - let b4_prepared = G2Prepared::from(b4); - let b5_prepared = G2Prepared::from(b5); - - let expected = pairing(&a1, &b1) - + pairing(&a2, &b2) - + pairing(&a3, &b3) - + pairing(&a4, &b4) - + pairing(&a5, &b5); - - let test = multi_miller_loop(&[ - (&a1, &b1_prepared), - (&a2, &b2_prepared), - (&a3, &b3_prepared), - (&a4, &b4_prepared), - (&a5, &b5_prepared), - ]) - .final_exponentiation(); - - assert_eq!(expected, test); + #[cfg(feature = "serde_req")] + impl<'de> Deserialize<'de> for G2Prepared { + fn deserialize(deserializer: D) -> Result + where + D: Deserializer<'de>, + { + enum Field { + Choice, + Coeffs, + } + + impl<'de> Deserialize<'de> for Field { + fn deserialize(deserializer: D) -> Result + where + D: Deserializer<'de>, + { + struct FieldVisitor; + + impl<'de> Visitor<'de> for FieldVisitor { + type Value = Field; + + fn expecting( + &self, + formatter: &mut ::core::fmt::Formatter, + ) -> ::core::fmt::Result { + formatter.write_str("struct G2Prepared") + } + + fn visit_str(self, value: &str) -> Result + where + E: serde::de::Error, + { + match value { + "choice" => Ok(Field::Choice), + "coeffs" => Ok(Field::Coeffs), + _ => Err(serde::de::Error::unknown_field(value, FIELDS)), + } + } + } + + deserializer.deserialize_identifier(FieldVisitor) + } + } + + struct G2PreparedVisitor; + + impl<'de> Visitor<'de> for G2PreparedVisitor { + type Value = G2Prepared; + + fn expecting(&self, formatter: &mut ::core::fmt::Formatter) -> ::core::fmt::Result { + formatter.write_str("struct G2Prepared") + } + + fn visit_seq(self, mut seq: V) -> Result + where + V: serde::de::SeqAccess<'de>, + { + let choice_as_u8: u8 = seq + .next_element()? + .ok_or_else(|| serde::de::Error::invalid_length(0, &self))?; + let coeffs = seq + .next_element()? + .ok_or_else(|| serde::de::Error::invalid_length(0, &self))?; + let choice: Choice = Choice::from(choice_as_u8); + Ok(G2Prepared { + infinity: choice.into(), + coeffs, + }) + } + } + + const FIELDS: &[&str] = &["choice", "coeffs"]; + deserializer.deserialize_struct("G2Prepared", FIELDS, G2PreparedVisitor) + } } #[test] diff --git a/src/scalar.rs b/src/scalar.rs index 3cfd35d3..f0d6de12 100644 --- a/src/scalar.rs +++ b/src/scalar.rs @@ -1,18 +1,17 @@ //! This module provides an implementation of the BLS12-381 scalar field $\mathbb{F}_q$ //! where `q = 0x73eda753299d7d483339d80809a1d80553bda402fffe5bfeffffffff00000001` -use crate::util::{adc, mac, sbb}; use core::borrow::Borrow; use core::cmp::{Ord, Ordering, PartialOrd}; use core::convert::TryFrom; use core::iter::{Product, Sum}; use core::ops::{Add, AddAssign, BitAnd, BitXor, Mul, MulAssign, Neg, Sub, SubAssign}; -use dusk_bytes::{Error as BytesError, HexDebug, Serializable}; use rand_core::{CryptoRng, RngCore}; + +use dusk_bytes::{Error as BytesError, HexDebug, Serializable}; use subtle::{Choice, ConditionallySelectable, ConstantTimeEq, CtOption}; -#[cfg(feature = "serde_req")] -use serde::{de::Visitor, Deserialize, Deserializer, Serialize, Serializer}; +use crate::util::{adc, mac, sbb}; #[cfg(feature = "rkyv-impl")] use bytecheck::CheckBytes; @@ -51,80 +50,6 @@ impl PartialEq for Scalar { } } -impl PartialOrd for Scalar { - fn partial_cmp(&self, other: &Scalar) -> Option { - Some(self.cmp(&other)) - } -} - -impl Ord for Scalar { - fn cmp(&self, other: &Self) -> Ordering { - for i in (0..4).rev() { - if self.0[i] > other.0[i] { - return Ordering::Greater; - } else if self.0[i] < other.0[i] { - return Ordering::Less; - } - } - Ordering::Equal - } -} - -impl Serializable<32> for Scalar { - type Error = BytesError; - - /// Converts an element of `Scalar` into a byte representation in - /// little-endian byte order. - fn to_bytes(&self) -> [u8; Self::SIZE] { - // Turn into canonical form by computing - // (a.R) / R = a - let tmp = Scalar::montgomery_reduce(self.0[0], self.0[1], self.0[2], self.0[3], 0, 0, 0, 0); - - let mut res = [0; Self::SIZE]; - res[0..8].copy_from_slice(&tmp.0[0].to_le_bytes()); - res[8..16].copy_from_slice(&tmp.0[1].to_le_bytes()); - res[16..24].copy_from_slice(&tmp.0[2].to_le_bytes()); - res[24..32].copy_from_slice(&tmp.0[3].to_le_bytes()); - - res - } - - /// Attempts to convert a little-endian byte representation of - /// a scalar into a `Scalar`, failing if the input is not canonical. - fn from_bytes(buf: &[u8; Self::SIZE]) -> Result { - let mut s = [0u64; 4]; - - s.iter_mut() - .zip(buf.chunks_exact(8)) - .try_for_each(|(s, b)| { - <[u8; 8]>::try_from(b) - .map(|b| *s = u64::from_le_bytes(b)) - .map_err(|_| BytesError::InvalidData) - })?; - - // Try to subtract the modulus - let (_, borrow) = sbb(s[0], MODULUS.0[0], 0); - let (_, borrow) = sbb(s[1], MODULUS.0[1], borrow); - let (_, borrow) = sbb(s[2], MODULUS.0[2], borrow); - let (_, borrow) = sbb(s[3], MODULUS.0[3], borrow); - - // If the element is smaller than MODULUS then the - // subtraction will underflow, producing a borrow value - // of 0xffff...ffff. Otherwise, it'll be zero. - if (borrow as u8) & 1 != 1 { - return Err(BytesError::InvalidData); - } - - let mut s = Scalar(s); - - // Convert to Montgomery form by computing - // (a.R^0 * R^2) / R = a.R - s *= &R2; - - Ok(s) - } -} - impl ConditionallySelectable for Scalar { fn conditional_select(a: &Self, b: &Self, choice: Choice) -> Self { Scalar([ @@ -136,73 +61,6 @@ impl ConditionallySelectable for Scalar { } } -#[cfg(feature = "serde_req")] -impl Serialize for Scalar { - fn serialize(&self, serializer: S) -> Result - where - S: Serializer, - { - use serde::ser::SerializeTuple; - let mut tup = serializer.serialize_tuple(Self::SIZE)?; - for byte in self.to_bytes().iter() { - tup.serialize_element(byte)?; - } - tup.end() - } -} - -#[cfg(feature = "serde_req")] -impl<'de> Deserialize<'de> for Scalar { - fn deserialize(deserializer: D) -> Result - where - D: Deserializer<'de>, - { - struct ScalarVisitor; - - impl<'de> Visitor<'de> for ScalarVisitor { - type Value = Scalar; - - fn expecting(&self, formatter: &mut ::core::fmt::Formatter) -> ::core::fmt::Result { - formatter.write_str("a 32-byte cannonical Scalar from Bls12_381") - } - - fn visit_seq(self, mut seq: A) -> Result - where - A: serde::de::SeqAccess<'de>, - { - let mut bytes = [0u8; Scalar::SIZE]; - - for i in 0..Scalar::SIZE { - bytes[i] = seq - .next_element()? - .ok_or(serde::de::Error::invalid_length(i, &"expected 32 bytes"))?; - } - - Scalar::from_bytes(&bytes) - .map_err(|_| serde::de::Error::custom(&"scalar was not canonically encoded")) - } - } - - deserializer.deserialize_tuple(Self::SIZE, ScalarVisitor) - } -} - -#[allow(dead_code)] -pub const GEN_X: Scalar = Scalar([ - 0x1539098E9CBCC1D5, - 0x0CCC77B0E1804E8D, - 0x6EEF947A6FD0FB2C, - 0xA3D063F54E10DDE9, -]); - -#[allow(dead_code)] -pub const GEN_Y: Scalar = Scalar([ - 0x6540D21E7007DC60, - 0x3B0D848E832A862F, - 0xB53BB87E05DA8257, - 0xCD482CC3FD6FF4D, -]); - /// Constant representing the modulus /// q = 0x73eda753299d7d483339d80809a1d80553bda402fffe5bfeffffffff00000001 pub const MODULUS: Scalar = Scalar([ @@ -257,83 +115,38 @@ impl<'a, 'b> Mul<&'b Scalar> for &'a Scalar { } } -impl<'a, 'b> BitXor<&'b Scalar> for &'a Scalar { - type Output = Scalar; - - fn bitxor(self, rhs: &'b Scalar) -> Scalar { - let a_red = self.reduce(); - let b_red = rhs.reduce(); - Scalar::from_raw([ - a_red.0[0] ^ b_red.0[0], - a_red.0[1] ^ b_red.0[1], - a_red.0[2] ^ b_red.0[2], - a_red.0[3] ^ b_red.0[3], - ]) - } -} - -impl BitXor for Scalar { - type Output = Scalar; - - fn bitxor(self, rhs: Scalar) -> Scalar { - &self ^ &rhs - } -} - -impl<'a, 'b> BitAnd<&'b Scalar> for &'a Scalar { - type Output = Scalar; - - fn bitand(self, rhs: &'b Scalar) -> Scalar { - let a_red = self.reduce(); - let b_red = rhs.reduce(); - Scalar::from_raw([ - a_red.0[0] & b_red.0[0], - a_red.0[1] & b_red.0[1], - a_red.0[2] & b_red.0[2], - a_red.0[3] & b_red.0[3], - ]) - } -} - -impl BitAnd for Scalar { - type Output = Scalar; - - fn bitand(self, rhs: Scalar) -> Scalar { - &self & &rhs - } -} - impl_binops_additive!(Scalar, Scalar); impl_binops_multiplicative!(Scalar, Scalar); /// INV = -(q^{-1} mod 2^64) mod 2^64 -const INV: u64 = 0xfffffffeffffffff; +const INV: u64 = 0xffff_fffe_ffff_ffff; /// R = 2^256 mod q const R: Scalar = Scalar([ - 0x00000001fffffffe, - 0x5884b7fa00034802, - 0x998c4fefecbc4ff5, - 0x1824b159acc5056f, + 0x0000_0001_ffff_fffe, + 0x5884_b7fa_0003_4802, + 0x998c_4fef_ecbc_4ff5, + 0x1824_b159_acc5_056f, ]); /// R^2 = 2^512 mod q const R2: Scalar = Scalar([ - 0xc999e990f3f29c6d, - 0x2b6cedcb87925c23, - 0x05d314967254398f, - 0x0748d9d99f59ff11, + 0xc999_e990_f3f2_9c6d, + 0x2b6c_edcb_8792_5c23, + 0x05d3_1496_7254_398f, + 0x0748_d9d9_9f59_ff11, ]); /// R^3 = 2^768 mod q const R3: Scalar = Scalar([ - 0xc62c1807439b73af, - 0x1b3e0d188cf06990, - 0x73d13c71c7b5f418, - 0x6e2a5bb9c8db33e9, + 0xc62c_1807_439b_73af, + 0x1b3e_0d18_8cf0_6990, + 0x73d1_3c71_c7b5_f418, + 0x6e2a_5bb9_c8db_33e9, ]); /// Two adacity +/// 2^TWO_ADACITY * t = MODULUS - 1 with t odd pub const TWO_ADACITY: u32 = 32; /// GENERATOR^t where t * 2^s + 1 = q @@ -344,36 +157,12 @@ pub const TWO_ADACITY: u32 = 32; /// of the q - 1 order multiplicative /// subgroup. pub const ROOT_OF_UNITY: Scalar = Scalar([ - 0xb9b58d8c5f0e466a, - 0x5b1b4c801819d7ec, - 0x0af53ae352a31e64, - 0x5bf3adda19e9b27b, + 0xb9b5_8d8c_5f0e_466a, + 0x5b1b_4c80_1819_d7ec, + 0x0af5_3ae3_52a3_1e64, + 0x5bf3_adda_19e9_b27b, ]); -impl Product for Scalar -where - T: Borrow, -{ - fn product(iter: I) -> Self - where - I: Iterator, - { - iter.fold(Scalar::one(), |acc, item| acc * item.borrow()) - } -} - -impl Sum for Scalar -where - T: Borrow, -{ - fn sum(iter: I) -> Self - where - I: Iterator, - { - iter.fold(Scalar::zero(), |acc, item| acc + item.borrow()) - } -} - impl Default for Scalar { #[inline] fn default() -> Self { @@ -394,21 +183,6 @@ impl Scalar { R } - /// Checks in ct_time whether a Scalar is equal to zero. - pub fn is_zero(&self) -> Choice { - self.ct_eq(&Scalar::zero()) - } - - /// Checks in ct_time whether a Scalar is equal to one. - pub fn is_one(&self) -> Choice { - self.ct_eq(&Scalar::one()) - } - - /// Returns the internal representation of the Scalar. - pub const fn internal_repr(&self) -> &[u64; 4] { - &self.0 - } - /// Doubles this field element. #[inline] pub const fn double(&self) -> Scalar { @@ -416,19 +190,6 @@ impl Scalar { self.add(self) } - /// Returns the bit representation of the given `Scalar` as - /// an array of 256 bits represented as `u8`. - pub fn to_bits(&self) -> [u8; 256] { - let mut res = [0u8; 256]; - let bytes = self.to_bytes(); - for (byte, bits) in bytes.iter().zip(res.chunks_mut(8)) { - bits.iter_mut() - .enumerate() - .for_each(|(i, bit)| *bit = (byte >> i) & 1) - } - res - } - /// Converts a 512-bit little endian integer into /// a `Scalar` by reducing by the modulus. pub fn from_bytes_wide(bytes: &[u8; 64]) -> Scalar { @@ -470,31 +231,6 @@ impl Scalar { (&Scalar(val)).mul(&R2) } - /// Generate a valid Scalar choosen uniformly using user- - /// provided rng. - /// - /// By `rng` we mean any Rng that implements: `Rng` + `CryptoRng`. - pub fn random(rand: &mut T) -> Scalar - where - T: RngCore + CryptoRng, - { - let mut bytes = [0u8; Self::SIZE]; - rand.fill_bytes(&mut bytes); - - // Ensure that the value is lower than `MODULUS`. - // Since modulus has 254-bits or less, we cut our bytes - // to get cannonical Scalars. - bytes[31] &= 0b0011_1111; - - Scalar::from_bytes(&bytes).unwrap_or_default() - } - - /// Reduces the scalar and returns it multiplied by the montgomery - /// radix. - pub fn reduce(&self) -> Scalar { - Scalar::montgomery_reduce(self.0[0], self.0[1], self.0[2], self.0[3], 0, 0, 0, 0) - } - /// Squares this element. #[inline] pub const fn square(&self) -> Scalar { @@ -527,55 +263,6 @@ impl Scalar { Scalar::montgomery_reduce(r0, r1, r2, r3, r4, r5, r6, r7) } - /// Computes the square root of this element, if it exists. - pub fn sqrt(&self) -> CtOption { - // Tonelli-Shank's algorithm for q mod 16 = 1 - // https://eprint.iacr.org/2012/685.pdf (page 12, algorithm 5) - - // w = self^((t - 1) // 2) - // = self^6104339283789297388802252303364915521546564123189034618274734669823 - let w = self.pow_vartime(&[ - 0x7fff2dff7fffffff, - 0x04d0ec02a9ded201, - 0x94cebea4199cec04, - 0x0000000039f6d3a9, - ]); - - let mut v = TWO_ADACITY; - let mut x = self * w; - let mut b = x * w; - - // Initialize z as the 2^S root of unity. - let mut z = ROOT_OF_UNITY; - - for max_v in (1..=TWO_ADACITY).rev() { - let mut k = 1; - let mut tmp = b.square(); - let mut j_less_than_v: Choice = 1.into(); - - for j in 2..max_v { - let tmp_is_one = tmp.ct_eq(&Scalar::one()); - let squared = Scalar::conditional_select(&tmp, &z, tmp_is_one).square(); - tmp = Scalar::conditional_select(&squared, &tmp, tmp_is_one); - let new_z = Scalar::conditional_select(&z, &squared, tmp_is_one); - j_less_than_v &= !j.ct_eq(&v); - k = u32::conditional_select(&j, &k, tmp_is_one); - z = Scalar::conditional_select(&z, &new_z, j_less_than_v); - } - - let result = x * z; - x = Scalar::conditional_select(&result, &x, b.ct_eq(&Scalar::one())); - z = z.square(); - b *= z; - v = k; - } - - CtOption::new( - x, - (x * x).ct_eq(self), // Only return Some if it's the square root. - ) - } - /// Exponentiates `self` by `by`, where `by` is a /// little-endian order integer exponent. pub fn pow(&self, by: &[u64; 4]) -> Self { @@ -611,20 +298,6 @@ impl Scalar { res } - /// Computes `2^X` where X is a `u64` without the need to generate - /// an array in the stack as `pow` & `pow_vartime` require. - pub fn pow_of_2(by: u64) -> Self { - let two = Scalar::from(2u64); - let mut res = Self::one(); - for i in (0..64).rev() { - res = res.square(); - let mut tmp = res; - tmp *= two; - res.conditional_assign(&tmp, (((by >> i) & 0x1) as u8).into()); - } - res - } - /// Computes the multiplicative inverse of this element, failing if the /// element is zero. pub fn invert(&self) -> Option { @@ -639,7 +312,7 @@ impl Scalar { /// Computes the multiplicative inverse of this element in constant time, /// failing if the element is zero. - fn invert_ct(&self) -> CtOption { + pub fn invert_ct(&self) -> CtOption { #[inline(always)] fn square_assign_multi(n: &mut Scalar, num_times: usize) { for _ in 0..num_times { @@ -859,33 +532,6 @@ impl Scalar { Scalar([d0 & mask, d1 & mask, d2 & mask, d3 & mask]) } - - /// SHR impl - #[inline] - pub fn divn(&mut self, mut n: u32) { - if n >= 256 { - *self = Self::from(0); - return; - } - - while n >= 64 { - let mut t = 0; - for i in self.0.iter_mut().rev() { - core::mem::swap(&mut t, i); - } - n -= 64; - } - - if n > 0 { - let mut t = 0; - for i in self.0.iter_mut().rev() { - let t2 = *i << (64 - n); - *i >>= n; - *i |= t; - t = t2; - } - } - } } impl<'a> From<&'a Scalar> for [u8; Scalar::SIZE] { @@ -894,449 +540,807 @@ impl<'a> From<&'a Scalar> for [u8; Scalar::SIZE] { } } -#[cfg(test)] -mod tests { - use super::*; - - #[test] - fn test_inv() { - // Compute -(q^{-1} mod 2^64) mod 2^64 by exponentiating - // by totient(2**64) - 1 - - let mut inv = 1u64; - for _ in 0..63 { - inv = inv.wrapping_mul(inv); - inv = inv.wrapping_mul(MODULUS.0[0]); - } - inv = inv.wrapping_neg(); - - assert_eq!(inv, INV); +impl Sum for Scalar +where + T: Borrow, +{ + fn sum(iter: I) -> Self + where + I: Iterator, + { + iter.fold(Scalar::zero(), |acc, item| acc + item.borrow()) } +} - #[cfg(feature = "std")] - #[test] - fn test_debug() { - assert_eq!( - format!("{:?}", Scalar::zero()), - "0000000000000000000000000000000000000000000000000000000000000000" - ); - assert_eq!( - format!("{:?}", Scalar::one()), - "0100000000000000000000000000000000000000000000000000000000000000" - ); - assert_eq!( - format!("{:?}", R2), - "feffffff0100000002480300fab78458f54fbcecef4f8c996f05c5ac59b12418" - ); - assert_eq!( - format!("{:#x}", R2), - "0xfeffffff0100000002480300fab78458f54fbcecef4f8c996f05c5ac59b12418" - ); +impl Product for Scalar +where + T: Borrow, +{ + fn product(iter: I) -> Self + where + I: Iterator, + { + iter.fold(Scalar::one(), |acc, item| acc * item.borrow()) } +} - #[test] - fn test_equality() { - assert_eq!(Scalar::zero(), Scalar::zero()); - assert_eq!(Scalar::one(), Scalar::one()); - assert_eq!(R2, R2); +#[test] +fn test_inv() { + // Compute -(q^{-1} mod 2^64) mod 2^64 by exponentiating + // by totient(2**64) - 1 - assert!(Scalar::zero() != Scalar::one()); - assert!(Scalar::one() != R2); + let mut inv = 1u64; + for _ in 0..63 { + inv = inv.wrapping_mul(inv); + inv = inv.wrapping_mul(MODULUS.0[0]); } + inv = inv.wrapping_neg(); - #[test] - fn test_to_bytes() { - assert_eq!( - Scalar::zero().to_bytes(), - [ - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0 - ] - ); - - assert_eq!( - Scalar::one().to_bytes(), - [ - 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0 - ] - ); - - assert_eq!( - R2.to_bytes(), - [ - 254, 255, 255, 255, 1, 0, 0, 0, 2, 72, 3, 0, 250, 183, 132, 88, 245, 79, 188, 236, - 239, 79, 140, 153, 111, 5, 197, 172, 89, 177, 36, 24 - ] - ); - - assert_eq!( - (-&Scalar::one()).to_bytes(), - [ - 0, 0, 0, 0, 255, 255, 255, 255, 254, 91, 254, 255, 2, 164, 189, 83, 5, 216, 161, 9, - 8, 216, 57, 51, 72, 125, 157, 41, 83, 167, 237, 115 - ] - ); - } + assert_eq!(inv, INV); +} - #[test] - fn test_from_bytes() { - assert_eq!( - Scalar::from_bytes(&[ - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0 - ]) - .unwrap(), - Scalar::zero() - ); +#[cfg(feature = "std")] +#[test] +fn test_debug() { + assert_eq!( + format!("{:?}", Scalar::zero()), + "0000000000000000000000000000000000000000000000000000000000000000" + ); + assert_eq!( + format!("{:?}", Scalar::one()), + "0100000000000000000000000000000000000000000000000000000000000000" + ); + assert_eq!( + format!("{:?}", R2), + "feffffff0100000002480300fab78458f54fbcecef4f8c996f05c5ac59b12418" + ); + assert_eq!( + format!("{:#x}", R2), + "0xfeffffff0100000002480300fab78458f54fbcecef4f8c996f05c5ac59b12418" + ); +} - assert_eq!( - Scalar::from_bytes(&[ - 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0 - ]) - .unwrap(), - Scalar::one() - ); +#[test] +fn test_equality() { + assert_eq!(Scalar::zero(), Scalar::zero()); + assert_eq!(Scalar::one(), Scalar::one()); + assert_eq!(R2, R2); - assert_eq!( - Scalar::from_bytes(&[ - 254, 255, 255, 255, 1, 0, 0, 0, 2, 72, 3, 0, 250, 183, 132, 88, 245, 79, 188, 236, - 239, 79, 140, 153, 111, 5, 197, 172, 89, 177, 36, 24 - ]) - .unwrap(), - R2 - ); + assert!(Scalar::zero() != Scalar::one()); + assert!(Scalar::one() != R2); +} - // -1 should work - assert!(Scalar::from_bytes(&[ +#[test] +fn test_to_bytes() { + assert_eq!( + Scalar::zero().to_bytes(), + [ + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0 + ] + ); + + assert_eq!( + Scalar::one().to_bytes(), + [ + 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0 + ] + ); + + assert_eq!( + R2.to_bytes(), + [ + 254, 255, 255, 255, 1, 0, 0, 0, 2, 72, 3, 0, 250, 183, 132, 88, 245, 79, 188, 236, 239, + 79, 140, 153, 111, 5, 197, 172, 89, 177, 36, 24 + ] + ); + + assert_eq!( + (-&Scalar::one()).to_bytes(), + [ 0, 0, 0, 0, 255, 255, 255, 255, 254, 91, 254, 255, 2, 164, 189, 83, 5, 216, 161, 9, 8, 216, 57, 51, 72, 125, 157, 41, 83, 167, 237, 115 + ] + ); +} + +#[test] +fn test_from_bytes() { + assert_eq!( + Scalar::from_bytes(&[ + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0 + ]) + .unwrap(), + Scalar::zero() + ); + + assert_eq!( + Scalar::from_bytes(&[ + 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0 ]) - .is_ok()); + .unwrap(), + Scalar::one() + ); + + assert_eq!( + Scalar::from_bytes(&[ + 254, 255, 255, 255, 1, 0, 0, 0, 2, 72, 3, 0, 250, 183, 132, 88, 245, 79, 188, 236, 239, + 79, 140, 153, 111, 5, 197, 172, 89, 177, 36, 24 + ]) + .unwrap(), + R2 + ); + + // -1 should work + assert!(Scalar::from_bytes(&[ + 0, 0, 0, 0, 255, 255, 255, 255, 254, 91, 254, 255, 2, 164, 189, 83, 5, 216, 161, 9, 8, 216, + 57, 51, 72, 125, 157, 41, 83, 167, 237, 115 + ]) + .is_ok()); + + // modulus is invalid + assert!(Scalar::from_bytes(&[ + 1, 0, 0, 0, 255, 255, 255, 255, 254, 91, 254, 255, 2, 164, 189, 83, 5, 216, 161, 9, 8, 216, + 57, 51, 72, 125, 157, 41, 83, 167, 237, 115 + ]) + .is_err()); + + // Anything larger than the modulus is invalid + assert!(Scalar::from_bytes(&[ + 2, 0, 0, 0, 255, 255, 255, 255, 254, 91, 254, 255, 2, 164, 189, 83, 5, 216, 161, 9, 8, 216, + 57, 51, 72, 125, 157, 41, 83, 167, 237, 115 + ]) + .is_err()); + assert!(Scalar::from_bytes(&[ + 1, 0, 0, 0, 255, 255, 255, 255, 254, 91, 254, 255, 2, 164, 189, 83, 5, 216, 161, 9, 8, 216, + 58, 51, 72, 125, 157, 41, 83, 167, 237, 115 + ]) + .is_err()); + assert!(Scalar::from_bytes(&[ + 1, 0, 0, 0, 255, 255, 255, 255, 254, 91, 254, 255, 2, 164, 189, 83, 5, 216, 161, 9, 8, 216, + 57, 51, 72, 125, 157, 41, 83, 167, 237, 116 + ]) + .is_err()); +} - // modulus is invalid - assert!(Scalar::from_bytes(&[ - 1, 0, 0, 0, 255, 255, 255, 255, 254, 91, 254, 255, 2, 164, 189, 83, 5, 216, 161, 9, 8, - 216, 57, 51, 72, 125, 157, 41, 83, 167, 237, 115 +#[test] +fn test_from_u512_zero() { + assert_eq!( + Scalar::zero(), + Scalar::from_u512([ + MODULUS.0[0], + MODULUS.0[1], + MODULUS.0[2], + MODULUS.0[3], + 0, + 0, + 0, + 0 ]) - .is_err()); + ); +} - // Anything larger than the modulus is invalid - assert!(Scalar::from_bytes(&[ - 2, 0, 0, 0, 255, 255, 255, 255, 254, 91, 254, 255, 2, 164, 189, 83, 5, 216, 161, 9, 8, - 216, 57, 51, 72, 125, 157, 41, 83, 167, 237, 115 +#[test] +fn test_from_u512_r() { + assert_eq!(R, Scalar::from_u512([1, 0, 0, 0, 0, 0, 0, 0])); +} + +#[test] +fn test_from_u512_r2() { + assert_eq!(R2, Scalar::from_u512([0, 0, 0, 0, 1, 0, 0, 0])); +} + +#[test] +fn test_from_u512_max() { + let max_u64 = 0xffffffffffffffff; + assert_eq!( + R3 - R, + Scalar::from_u512([max_u64, max_u64, max_u64, max_u64, max_u64, max_u64, max_u64, max_u64]) + ); +} + +#[test] +fn test_from_bytes_wide_r2() { + assert_eq!( + R2, + Scalar::from_bytes_wide(&[ + 254, 255, 255, 255, 1, 0, 0, 0, 2, 72, 3, 0, 250, 183, 132, 88, 245, 79, 188, 236, 239, + 79, 140, 153, 111, 5, 197, 172, 89, 177, 36, 24, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, ]) - .is_err()); - assert!(Scalar::from_bytes(&[ - 1, 0, 0, 0, 255, 255, 255, 255, 254, 91, 254, 255, 2, 164, 189, 83, 5, 216, 161, 9, 8, - 216, 58, 51, 72, 125, 157, 41, 83, 167, 237, 115 + ); +} + +#[test] +fn test_from_bytes_wide_negative_one() { + assert_eq!( + -&Scalar::one(), + Scalar::from_bytes_wide(&[ + 0, 0, 0, 0, 255, 255, 255, 255, 254, 91, 254, 255, 2, 164, 189, 83, 5, 216, 161, 9, 8, + 216, 57, 51, 72, 125, 157, 41, 83, 167, 237, 115, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, ]) - .is_err()); - assert!(Scalar::from_bytes(&[ - 1, 0, 0, 0, 255, 255, 255, 255, 254, 91, 254, 255, 2, 164, 189, 83, 5, 216, 161, 9, 8, - 216, 57, 51, 72, 125, 157, 41, 83, 167, 237, 116 + ); +} + +#[test] +fn test_from_bytes_wide_maximum() { + assert_eq!( + Scalar([ + 0xc62c1805439b73b1, + 0xc2b9551e8ced218e, + 0xda44ec81daf9a422, + 0x5605aa601c162e79 + ]), + Scalar::from_bytes_wide(&[0xff; 64]) + ); +} + +#[test] +fn test_zero() { + assert_eq!(Scalar::zero(), -&Scalar::zero()); + assert_eq!(Scalar::zero(), Scalar::zero() + Scalar::zero()); + assert_eq!(Scalar::zero(), Scalar::zero() - Scalar::zero()); + assert_eq!(Scalar::zero(), Scalar::zero() * Scalar::zero()); +} + +#[cfg(test)] +const LARGEST: Scalar = Scalar([ + 0xffff_ffff_0000_0000, + 0x53bd_a402_fffe_5bfe, + 0x3339_d808_09a1_d805, + 0x73ed_a753_299d_7d48, +]); + +#[test] +fn test_addition() { + let mut tmp = LARGEST; + tmp += &LARGEST; + + assert_eq!( + tmp, + Scalar([ + 0xffff_fffe_ffff_ffff, + 0x53bd_a402_fffe_5bfe, + 0x3339_d808_09a1_d805, + 0x73ed_a753_299d_7d48, ]) - .is_err()); - } + ); - #[test] - fn test_from_u512_zero() { - assert_eq!( - Scalar::zero(), - Scalar::from_u512([ - MODULUS.0[0], - MODULUS.0[1], - MODULUS.0[2], - MODULUS.0[3], - 0, - 0, - 0, - 0 - ]) - ); - } + let mut tmp = LARGEST; + tmp += &Scalar([1, 0, 0, 0]); - #[test] - fn test_from_u512_r() { - assert_eq!(R, Scalar::from_u512([1, 0, 0, 0, 0, 0, 0, 0])); - } + assert_eq!(tmp, Scalar::zero()); +} - #[test] - fn test_from_u512_r2() { - assert_eq!(R2, Scalar::from_u512([0, 0, 0, 0, 1, 0, 0, 0])); - } +#[test] +fn test_negation() { + let tmp = -&LARGEST; - #[test] - fn test_from_u512_max() { - let max_u64 = 0xffffffffffffffff; - assert_eq!( - R3 - R, - Scalar::from_u512([ - max_u64, max_u64, max_u64, max_u64, max_u64, max_u64, max_u64, max_u64 - ]) - ); - } + assert_eq!(tmp, Scalar([1, 0, 0, 0])); - #[test] - fn test_from_bytes_wide_r2() { - assert_eq!( - R2, - Scalar::from_bytes_wide(&[ - 254, 255, 255, 255, 1, 0, 0, 0, 2, 72, 3, 0, 250, 183, 132, 88, 245, 79, 188, 236, - 239, 79, 140, 153, 111, 5, 197, 172, 89, 177, 36, 24, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - ]) - ); - } + let tmp = -&Scalar::zero(); + assert_eq!(tmp, Scalar::zero()); + let tmp = -&Scalar([1, 0, 0, 0]); + assert_eq!(tmp, LARGEST); +} - #[test] - fn test_from_bytes_wide_negative_one() { - assert_eq!( - -&Scalar::one(), - Scalar::from_bytes_wide(&[ - 0, 0, 0, 0, 255, 255, 255, 255, 254, 91, 254, 255, 2, 164, 189, 83, 5, 216, 161, 9, - 8, 216, 57, 51, 72, 125, 157, 41, 83, 167, 237, 115, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - ]) - ); +#[test] +fn test_subtraction() { + let mut tmp = LARGEST; + tmp -= &LARGEST; + + assert_eq!(tmp, Scalar::zero()); + + let mut tmp = Scalar::zero(); + tmp -= &LARGEST; + + let mut tmp2 = MODULUS; + tmp2 -= &LARGEST; + + assert_eq!(tmp, tmp2); +} + +#[test] +fn test_multiplication() { + let mut cur = LARGEST; + + for _ in 0..100 { + let mut tmp = cur; + tmp *= &cur; + + let mut tmp2 = Scalar::zero(); + for b in cur + .to_bytes() + .iter() + .rev() + .flat_map(|byte| (0..8).rev().map(move |i| ((byte >> i) & 1u8) == 1u8)) + { + let tmp3 = tmp2; + tmp2.add_assign(&tmp3); + + if b { + tmp2.add_assign(&cur); + } + } + + assert_eq!(tmp, tmp2); + + cur.add_assign(&LARGEST); } +} - #[test] - fn test_from_bytes_wide_maximum() { - assert_eq!( - Scalar([ - 0xc62c1805439b73b1, - 0xc2b9551e8ced218e, - 0xda44ec81daf9a422, - 0x5605aa601c162e79 - ]), - Scalar::from_bytes_wide(&[0xff; 64]) - ); +#[test] +fn test_squaring() { + let mut cur = LARGEST; + + for _ in 0..100 { + let mut tmp = cur; + tmp = tmp.square(); + + let mut tmp2 = Scalar::zero(); + for b in cur + .to_bytes() + .iter() + .rev() + .flat_map(|byte| (0..8).rev().map(move |i| ((byte >> i) & 1u8) == 1u8)) + { + let tmp3 = tmp2; + tmp2.add_assign(&tmp3); + + if b { + tmp2.add_assign(&cur); + } + } + + assert_eq!(tmp, tmp2); + + cur.add_assign(&LARGEST); } +} - #[test] - fn test_zero() { - assert_eq!(Scalar::zero(), -&Scalar::zero()); - assert_eq!(Scalar::zero(), Scalar::zero() + Scalar::zero()); - assert_eq!(Scalar::zero(), Scalar::zero() - Scalar::zero()); - assert_eq!(Scalar::zero(), Scalar::zero() * Scalar::zero()); +#[test] +fn test_inversion() { + assert_eq!(Scalar::zero().invert(), None); + assert_eq!(Scalar::one().invert().unwrap(), Scalar::one()); + assert_eq!((-&Scalar::one()).invert().unwrap(), -&Scalar::one()); + + let mut tmp = R2; + + for _ in 0..100 { + let mut tmp2 = tmp.invert().unwrap(); + tmp2.mul_assign(&tmp); + + assert_eq!(tmp2, Scalar::one()); + + tmp.add_assign(&R2); } +} - #[cfg(test)] - const LARGEST: Scalar = Scalar([ - 0xffffffff00000000, +#[test] +fn test_invert_is_pow() { + let q_minus_2 = [ + 0xfffffffeffffffff, 0x53bda402fffe5bfe, 0x3339d80809a1d805, 0x73eda753299d7d48, - ]); + ]; - #[test] - fn test_addition() { - let mut tmp = LARGEST; - tmp += &LARGEST; + let mut r1 = R; + let mut r2 = R; + let mut r3 = R; - assert_eq!( - tmp, - Scalar([ - 0xfffffffeffffffff, - 0x53bda402fffe5bfe, - 0x3339d80809a1d805, - 0x73eda753299d7d48 - ]) - ); + for _ in 0..100 { + r1 = r1.invert().unwrap(); + r2 = r2.pow_vartime(&q_minus_2); + r3 = r3.pow(&q_minus_2); - let mut tmp = LARGEST; - tmp += &Scalar([1, 0, 0, 0]); + assert_eq!(r1, r2); + assert_eq!(r2, r3); + // Add R so we check something different next time around + r1.add_assign(&R); + r2 = r1; + r3 = r1; + } +} - assert_eq!(tmp, Scalar::zero()); +#[test] +fn test_sqrt() { + { + assert_eq!(Scalar::zero().sqrt().unwrap(), Scalar::zero()); } - #[test] - fn test_negation() { - let tmp = -&LARGEST; + let mut square = Scalar([ + 0x46cd85a5f273077e, + 0x1d30c47dd68fc735, + 0x77f656f60beca0eb, + 0x494aa01bdf32468d, + ]); - assert_eq!(tmp, Scalar([1, 0, 0, 0])); + let mut none_count = 0; - let tmp = -&Scalar::zero(); - assert_eq!(tmp, Scalar::zero()); - let tmp = -&Scalar([1, 0, 0, 0]); - assert_eq!(tmp, LARGEST); + for _ in 0..100 { + let square_root = square.sqrt(); + if square_root.is_none().unwrap_u8() == 1 { + none_count += 1; + } else { + assert_eq!(square_root.unwrap() * square_root.unwrap(), square); + } + square -= Scalar::one(); } - #[test] - fn test_subtraction() { - let mut tmp = LARGEST; - tmp -= &LARGEST; + assert_eq!(49, none_count); +} + +#[test] +fn test_from_raw() { + assert_eq!( + Scalar::from_raw([ + 0x1fffffffd, + 0x5884b7fa00034802, + 0x998c4fefecbc4ff5, + 0x1824b159acc5056f + ]), + Scalar::from_raw([0xffffffffffffffff; 4]) + ); - assert_eq!(tmp, Scalar::zero()); + assert_eq!(Scalar::from_raw(MODULUS.0), Scalar::zero()); - let mut tmp = Scalar::zero(); - tmp -= &LARGEST; + assert_eq!(Scalar::from_raw([1, 0, 0, 0]), R); +} + +#[test] +fn test_double() { + let a = Scalar::from_raw([ + 0x1fff3231233ffffd, + 0x4884b7fa00034802, + 0x998c4fefecbc4ff3, + 0x1824b159acc50562, + ]); + assert_eq!(a.double(), a + a); +} - let mut tmp2 = MODULUS; - tmp2 -= &LARGEST; +mod dusk { + use super::*; - assert_eq!(tmp, tmp2); + #[cfg(feature = "serde_req")] + use serde::{de::Visitor, Deserialize, Deserializer, Serialize, Serializer}; + + impl PartialOrd for Scalar { + fn partial_cmp(&self, other: &Scalar) -> Option { + Some(self.cmp(&other)) + } } - #[test] - fn test_multiplication() { - let mut cur = LARGEST; - - for _ in 0..100 { - let mut tmp = cur; - tmp *= &cur; - - let mut tmp2 = Scalar::zero(); - for b in cur - .to_bytes() - .iter() - .rev() - .flat_map(|byte| (0..8).rev().map(move |i| ((byte >> i) & 1u8) == 1u8)) - { - let tmp3 = tmp2; - tmp2.add_assign(&tmp3); - - if b { - tmp2.add_assign(&cur); + impl Ord for Scalar { + fn cmp(&self, other: &Self) -> Ordering { + for i in (0..4).rev() { + if self.0[i] > other.0[i] { + return Ordering::Greater; + } else if self.0[i] < other.0[i] { + return Ordering::Less; } } + Ordering::Equal + } + } + + impl Serializable<32> for Scalar { + type Error = BytesError; + + /// Converts an element of `Scalar` into a byte representation in + /// little-endian byte order. + fn to_bytes(&self) -> [u8; Self::SIZE] { + // Turn into canonical form by computing + // (a.R) / R = a + let tmp = + Scalar::montgomery_reduce(self.0[0], self.0[1], self.0[2], self.0[3], 0, 0, 0, 0); + + let mut res = [0; Self::SIZE]; + res[0..8].copy_from_slice(&tmp.0[0].to_le_bytes()); + res[8..16].copy_from_slice(&tmp.0[1].to_le_bytes()); + res[16..24].copy_from_slice(&tmp.0[2].to_le_bytes()); + res[24..32].copy_from_slice(&tmp.0[3].to_le_bytes()); + + res + } + + /// Attempts to convert a little-endian byte representation of + /// a scalar into a `Scalar`, failing if the input is not canonical. + fn from_bytes(buf: &[u8; Self::SIZE]) -> Result { + let mut s = [0u64; 4]; + + s.iter_mut() + .zip(buf.chunks_exact(8)) + .try_for_each(|(s, b)| { + <[u8; 8]>::try_from(b) + .map(|b| *s = u64::from_le_bytes(b)) + .map_err(|_| BytesError::InvalidData) + })?; + + // Try to subtract the modulus + let (_, borrow) = sbb(s[0], MODULUS.0[0], 0); + let (_, borrow) = sbb(s[1], MODULUS.0[1], borrow); + let (_, borrow) = sbb(s[2], MODULUS.0[2], borrow); + let (_, borrow) = sbb(s[3], MODULUS.0[3], borrow); + + // If the element is smaller than MODULUS then the + // subtraction will underflow, producing a borrow value + // of 0xffff...ffff. Otherwise, it'll be zero. + if (borrow as u8) & 1 != 1 { + return Err(BytesError::InvalidData); + } + + let mut s = Scalar(s); - assert_eq!(tmp, tmp2); + // Convert to Montgomery form by computing + // (a.R^0 * R^2) / R = a.R + s *= &R2; - cur.add_assign(&LARGEST); + Ok(s) } } - #[test] - fn test_squaring() { - let mut cur = LARGEST; - - for _ in 0..100 { - let mut tmp = cur; - tmp = tmp.square(); - - let mut tmp2 = Scalar::zero(); - for b in cur - .to_bytes() - .iter() - .rev() - .flat_map(|byte| (0..8).rev().map(move |i| ((byte >> i) & 1u8) == 1u8)) - { - let tmp3 = tmp2; - tmp2.add_assign(&tmp3); - - if b { - tmp2.add_assign(&cur); - } + #[cfg(feature = "serde_req")] + impl Serialize for Scalar { + fn serialize(&self, serializer: S) -> Result + where + S: Serializer, + { + use serde::ser::SerializeTuple; + let mut tup = serializer.serialize_tuple(Self::SIZE)?; + for byte in self.to_bytes().iter() { + tup.serialize_element(byte)?; } + tup.end() + } + } + + #[cfg(feature = "serde_req")] + impl<'de> Deserialize<'de> for Scalar { + fn deserialize(deserializer: D) -> Result + where + D: Deserializer<'de>, + { + struct ScalarVisitor; + + impl<'de> Visitor<'de> for ScalarVisitor { + type Value = Scalar; - assert_eq!(tmp, tmp2); + fn expecting(&self, formatter: &mut ::core::fmt::Formatter) -> ::core::fmt::Result { + formatter.write_str("a 32-byte cannonical Scalar from Bls12_381") + } + + fn visit_seq(self, mut seq: A) -> Result + where + A: serde::de::SeqAccess<'de>, + { + let mut bytes = [0u8; Scalar::SIZE]; + + for i in 0..Scalar::SIZE { + bytes[i] = seq + .next_element()? + .ok_or(serde::de::Error::invalid_length(i, &"expected 32 bytes"))?; + } + + Scalar::from_bytes(&bytes).map_err(|_| { + serde::de::Error::custom(&"scalar was not canonically encoded") + }) + } + } - cur.add_assign(&LARGEST); + deserializer.deserialize_tuple(Self::SIZE, ScalarVisitor) } } - #[test] - fn test_inversion() { - assert_eq!(Scalar::zero().invert(), None); - assert_eq!(Scalar::one().invert().unwrap(), Scalar::one()); - assert_eq!((-&Scalar::one()).invert().unwrap(), -&Scalar::one()); + #[allow(dead_code)] + pub const GEN_X: Scalar = Scalar([ + 0x1539098E9CBCC1D5, + 0x0CCC77B0E1804E8D, + 0x6EEF947A6FD0FB2C, + 0xA3D063F54E10DDE9, + ]); - let mut tmp = R2; + #[allow(dead_code)] + pub const GEN_Y: Scalar = Scalar([ + 0x6540D21E7007DC60, + 0x3B0D848E832A862F, + 0xB53BB87E05DA8257, + 0xCD482CC3FD6FF4D, + ]); + + impl<'a, 'b> BitXor<&'b Scalar> for &'a Scalar { + type Output = Scalar; - for _ in 0..100 { - let mut tmp2 = tmp.invert().unwrap(); - tmp2.mul_assign(&tmp); + fn bitxor(self, rhs: &'b Scalar) -> Scalar { + let a_red = self.reduce(); + let b_red = rhs.reduce(); + Scalar::from_raw([ + a_red.0[0] ^ b_red.0[0], + a_red.0[1] ^ b_red.0[1], + a_red.0[2] ^ b_red.0[2], + a_red.0[3] ^ b_red.0[3], + ]) + } + } - assert_eq!(tmp2, Scalar::one()); + impl BitXor for Scalar { + type Output = Scalar; - tmp.add_assign(&R2); + fn bitxor(self, rhs: Scalar) -> Scalar { + &self ^ &rhs } } - #[test] - fn test_invert_is_pow() { - let q_minus_2 = [ - 0xfffffffeffffffff, - 0x53bda402fffe5bfe, - 0x3339d80809a1d805, - 0x73eda753299d7d48, - ]; + impl BitAnd for Scalar { + type Output = Scalar; - let mut r1 = R; - let mut r2 = R; - let mut r3 = R; - - for _ in 0..100 { - r1 = r1.invert().unwrap(); - r2 = r2.pow_vartime(&q_minus_2); - r3 = r3.pow(&q_minus_2); - - assert_eq!(r1, r2); - assert_eq!(r2, r3); - // Add R so we check something different next time around - r1.add_assign(&R); - r2 = r1; - r3 = r1; + fn bitand(self, rhs: Scalar) -> Scalar { + &self & &rhs } } - #[test] - fn test_sqrt() { + impl<'a, 'b> BitAnd<&'b Scalar> for &'a Scalar { + type Output = Scalar; + + fn bitand(self, rhs: &'b Scalar) -> Scalar { + let a_red = self.reduce(); + let b_red = rhs.reduce(); + Scalar::from_raw([ + a_red.0[0] & b_red.0[0], + a_red.0[1] & b_red.0[1], + a_red.0[2] & b_red.0[2], + a_red.0[3] & b_red.0[3], + ]) + } + } + + impl Scalar { + /// Checks in ct_time whether a Scalar is equal to zero. + pub fn is_zero(&self) -> Choice { + self.ct_eq(&Scalar::zero()) + } + + /// Checks in ct_time whether a Scalar is equal to one. + pub fn is_one(&self) -> Choice { + self.ct_eq(&Scalar::one()) + } + + /// Returns the internal representation of the Scalar. + pub const fn internal_repr(&self) -> &[u64; 4] { + &self.0 + } + + /// Returns the bit representation of the given `Scalar` as + /// an array of 256 bits represented as `u8`. + pub fn to_bits(&self) -> [u8; 256] { + let mut res = [0u8; 256]; + let bytes = self.to_bytes(); + for (byte, bits) in bytes.iter().zip(res.chunks_mut(8)) { + bits.iter_mut() + .enumerate() + .for_each(|(i, bit)| *bit = (byte >> i) & 1) + } + res + } + + /// Generate a valid Scalar choosen uniformly using user- + /// provided rng. + /// + /// By `rng` we mean any Rng that implements: `Rng` + `CryptoRng`. + pub fn random(rand: &mut T) -> Scalar + where + T: RngCore + CryptoRng, { - assert_eq!(Scalar::zero().sqrt().unwrap(), Scalar::zero()); + let mut bytes = [0u8; Self::SIZE]; + rand.fill_bytes(&mut bytes); + + // Ensure that the value is lower than `MODULUS`. + // Since modulus has 254-bits or less, we cut our bytes + // to get cannonical Scalars. + bytes[31] &= 0b0011_1111; + + Scalar::from_bytes(&bytes).unwrap_or_default() } - let mut square = Scalar([ - 0x46cd85a5f273077e, - 0x1d30c47dd68fc735, - 0x77f656f60beca0eb, - 0x494aa01bdf32468d, - ]); - - let mut none_count = 0; - - for _ in 0..100 { - let square_root = square.sqrt(); - if square_root.is_none().unwrap_u8() == 1 { - none_count += 1; - } else { - assert_eq!(square_root.unwrap() * square_root.unwrap(), square); + /// Computes the square root of this element, if it exists. + pub fn sqrt(&self) -> CtOption { + // Tonelli-Shank's algorithm for q mod 16 = 1 + // https://eprint.iacr.org/2012/685.pdf (page 12, algorithm 5) + + // w = self^((t - 1) // 2) + // = self^6104339283789297388802252303364915521546564123189034618274734669823 + let w = self.pow_vartime(&[ + 0x7fff2dff7fffffff, + 0x04d0ec02a9ded201, + 0x94cebea4199cec04, + 0x0000000039f6d3a9, + ]); + + let mut v = TWO_ADACITY; + let mut x = self * w; + let mut b = x * w; + + // Initialize z as the 2^S root of unity. + let mut z = ROOT_OF_UNITY; + + for max_v in (1..=TWO_ADACITY).rev() { + let mut k = 1; + let mut tmp = b.square(); + let mut j_less_than_v: Choice = 1.into(); + + for j in 2..max_v { + let tmp_is_one = tmp.ct_eq(&Scalar::one()); + let squared = Scalar::conditional_select(&tmp, &z, tmp_is_one).square(); + tmp = Scalar::conditional_select(&squared, &tmp, tmp_is_one); + let new_z = Scalar::conditional_select(&z, &squared, tmp_is_one); + j_less_than_v &= !j.ct_eq(&v); + k = u32::conditional_select(&j, &k, tmp_is_one); + z = Scalar::conditional_select(&z, &new_z, j_less_than_v); + } + + let result = x * z; + x = Scalar::conditional_select(&result, &x, b.ct_eq(&Scalar::one())); + z = z.square(); + b *= z; + v = k; } - square -= Scalar::one(); + + CtOption::new( + x, + (x * x).ct_eq(self), // Only return Some if it's the square root. + ) } - assert_eq!(49, none_count); - } + /// Reduces the scalar and returns it multiplied by the montgomery + /// radix. + pub fn reduce(&self) -> Scalar { + Scalar::montgomery_reduce(self.0[0], self.0[1], self.0[2], self.0[3], 0, 0, 0, 0) + } - #[test] - fn test_from_raw() { - assert_eq!( - Scalar::from_raw([ - 0x1fffffffd, - 0x5884b7fa00034802, - 0x998c4fefecbc4ff5, - 0x1824b159acc5056f - ]), - Scalar::from_raw([0xffffffffffffffff; 4]) - ); + /// Computes `2^X` where X is a `u64` without the need to generate + /// an array in the stack as `pow` & `pow_vartime` require. + pub fn pow_of_2(by: u64) -> Self { + let two = Scalar::from(2u64); + let mut res = Self::one(); + for i in (0..64).rev() { + res = res.square(); + let mut tmp = res; + tmp *= two; + res.conditional_assign(&tmp, (((by >> i) & 0x1) as u8).into()); + } + res + } - assert_eq!(Scalar::from_raw(MODULUS.0), Scalar::zero()); + /// SHR impl + #[inline] + pub fn divn(&mut self, mut n: u32) { + if n >= 256 { + *self = Self::from(0); + return; + } - assert_eq!(Scalar::from_raw([1, 0, 0, 0]), R); - } + while n >= 64 { + let mut t = 0; + for i in self.0.iter_mut().rev() { + core::mem::swap(&mut t, i); + } + n -= 64; + } - #[test] - fn test_double() { - let a = Scalar::from_raw([ - 0x1fff3231233ffffd, - 0x4884b7fa00034802, - 0x998c4fefecbc4ff3, - 0x1824b159acc50562, - ]); - assert_eq!(a.double(), a + a); + if n > 0 { + let mut t = 0; + for i in self.0.iter_mut().rev() { + let t2 = *i << (64 - n); + *i >>= n; + *i |= t; + t = t2; + } + } + } } #[test] diff --git a/src/tests/mod.rs b/src/tests/mod.rs index 8e2c7156..39d108b1 100644 --- a/src/tests/mod.rs +++ b/src/tests/mod.rs @@ -28,173 +28,168 @@ macro_rules! test_vectors { }; } -#[cfg(test)] -mod tests { - use super::*; - - #[test] - fn g1_compressed_valid_test_vectors() { - let bytes: &'static [u8] = include_bytes!("g1_compressed_valid_test_vectors.dat"); - test_vectors!(G1Projective, G1Affine, to_bytes, from_bytes, bytes); - } +#[test] +fn g1_compressed_valid_test_vectors() { + let bytes: &'static [u8] = include_bytes!("g1_compressed_valid_test_vectors.dat"); + test_vectors!(G1Projective, G1Affine, to_bytes, from_bytes, bytes); +} - #[test] - fn g2_compressed_valid_test_vectors() { - let bytes: &'static [u8] = include_bytes!("g2_compressed_valid_test_vectors.dat"); - test_vectors!(G2Projective, G2Affine, to_bytes, from_bytes, bytes); - } +#[test] +fn g2_compressed_valid_test_vectors() { + let bytes: &'static [u8] = include_bytes!("g2_compressed_valid_test_vectors.dat"); + test_vectors!(G2Projective, G2Affine, to_bytes, from_bytes, bytes); +} - #[test] - fn test_pairing_result_against_relic() { - /* - Sent to me from Diego Aranha (author of RELIC library): - 1250EBD871FC0A92 A7B2D83168D0D727 272D441BEFA15C50 3DD8E90CE98DB3E7 B6D194F60839C508 A84305AACA1789B6 - 089A1C5B46E5110B 86750EC6A5323488 68A84045483C92B7 AF5AF689452EAFAB F1A8943E50439F1D 59882A98EAA0170F - 1368BB445C7C2D20 9703F239689CE34C 0378A68E72A6B3B2 16DA0E22A5031B54 DDFF57309396B38C 881C4C849EC23E87 - 193502B86EDB8857 C273FA075A505129 37E0794E1E65A761 7C90D8BD66065B1F FFE51D7A579973B1 315021EC3C19934F - 01B2F522473D1713 91125BA84DC4007C FBF2F8DA752F7C74 185203FCCA589AC7 19C34DFFBBAAD843 1DAD1C1FB597AAA5 - 018107154F25A764 BD3C79937A45B845 46DA634B8F6BE14A 8061E55CCEBA478B 23F7DACAA35C8CA7 8BEAE9624045B4B6 - 19F26337D205FB46 9CD6BD15C3D5A04D C88784FBB3D0B2DB DEA54D43B2B73F2C BB12D58386A8703E 0F948226E47EE89D - 06FBA23EB7C5AF0D 9F80940CA771B6FF D5857BAAF222EB95 A7D2809D61BFE02E 1BFD1B68FF02F0B8 102AE1C2D5D5AB1A - 11B8B424CD48BF38 FCEF68083B0B0EC5 C81A93B330EE1A67 7D0D15FF7B984E89 78EF48881E32FAC9 1B93B47333E2BA57 - 03350F55A7AEFCD3 C31B4FCB6CE5771C C6A0E9786AB59733 20C806AD36082910 7BA810C5A09FFDD9 BE2291A0C25A99A2 - 04C581234D086A99 02249B64728FFD21 A189E87935A95405 1C7CDBA7B3872629 A4FAFC05066245CB 9108F0242D0FE3EF - 0F41E58663BF08CF 068672CBD01A7EC7 3BACA4D72CA93544 DEFF686BFD6DF543 D48EAA24AFE47E1E FDE449383B676631 - */ +#[test] +fn test_pairing_result_against_relic() { + /* + Sent to me from Diego Aranha (author of RELIC library): + 1250EBD871FC0A92 A7B2D83168D0D727 272D441BEFA15C50 3DD8E90CE98DB3E7 B6D194F60839C508 A84305AACA1789B6 + 089A1C5B46E5110B 86750EC6A5323488 68A84045483C92B7 AF5AF689452EAFAB F1A8943E50439F1D 59882A98EAA0170F + 1368BB445C7C2D20 9703F239689CE34C 0378A68E72A6B3B2 16DA0E22A5031B54 DDFF57309396B38C 881C4C849EC23E87 + 193502B86EDB8857 C273FA075A505129 37E0794E1E65A761 7C90D8BD66065B1F FFE51D7A579973B1 315021EC3C19934F + 01B2F522473D1713 91125BA84DC4007C FBF2F8DA752F7C74 185203FCCA589AC7 19C34DFFBBAAD843 1DAD1C1FB597AAA5 + 018107154F25A764 BD3C79937A45B845 46DA634B8F6BE14A 8061E55CCEBA478B 23F7DACAA35C8CA7 8BEAE9624045B4B6 + 19F26337D205FB46 9CD6BD15C3D5A04D C88784FBB3D0B2DB DEA54D43B2B73F2C BB12D58386A8703E 0F948226E47EE89D + 06FBA23EB7C5AF0D 9F80940CA771B6FF D5857BAAF222EB95 A7D2809D61BFE02E 1BFD1B68FF02F0B8 102AE1C2D5D5AB1A + 11B8B424CD48BF38 FCEF68083B0B0EC5 C81A93B330EE1A67 7D0D15FF7B984E89 78EF48881E32FAC9 1B93B47333E2BA57 + 03350F55A7AEFCD3 C31B4FCB6CE5771C C6A0E9786AB59733 20C806AD36082910 7BA810C5A09FFDD9 BE2291A0C25A99A2 + 04C581234D086A99 02249B64728FFD21 A189E87935A95405 1C7CDBA7B3872629 A4FAFC05066245CB 9108F0242D0FE3EF + 0F41E58663BF08CF 068672CBD01A7EC7 3BACA4D72CA93544 DEFF686BFD6DF543 D48EAA24AFE47E1E FDE449383B676631 + */ - let a = G1Affine::generator(); - let b = G2Affine::generator(); + let a = G1Affine::generator(); + let b = G2Affine::generator(); - use super::fp::Fp; - use super::fp12::Fp12; - use super::fp2::Fp2; - use super::fp6::Fp6; + use super::fp::Fp; + use super::fp12::Fp12; + use super::fp2::Fp2; + use super::fp6::Fp6; - let res = pairing(&a, &b); + let res = pairing(&a, &b); - let prep = G2Prepared::from(b); + let prep = G2Prepared::from(b); - assert_eq!( - res, - multi_miller_loop(&[(&a, &prep)]).final_exponentiation() - ); + assert_eq!( + res, + multi_miller_loop(&[(&a, &prep)]).final_exponentiation() + ); - assert_eq!( - res.0, - Fp12 { - c0: Fp6 { - c0: Fp2 { - c0: Fp::from_raw_unchecked([ - 0x1972e433a01f85c5, - 0x97d32b76fd772538, - 0xc8ce546fc96bcdf9, - 0xcef63e7366d40614, - 0xa611342781843780, - 0x13f3448a3fc6d825 - ]), - c1: Fp::from_raw_unchecked([ - 0xd26331b02e9d6995, - 0x9d68a482f7797e7d, - 0x9c9b29248d39ea92, - 0xf4801ca2e13107aa, - 0xa16c0732bdbcb066, - 0x83ca4afba360478 - ]) - }, - c1: Fp2 { - c0: Fp::from_raw_unchecked([ - 0x59e261db0916b641, - 0x2716b6f4b23e960d, - 0xc8e55b10a0bd9c45, - 0xbdb0bd99c4deda8, - 0x8cf89ebf57fdaac5, - 0x12d6b7929e777a5e - ]), - c1: Fp::from_raw_unchecked([ - 0x5fc85188b0e15f35, - 0x34a06e3a8f096365, - 0xdb3126a6e02ad62c, - 0xfc6f5aa97d9a990b, - 0xa12f55f5eb89c210, - 0x1723703a926f8889 - ]) - }, - c2: Fp2 { - c0: Fp::from_raw_unchecked([ - 0x93588f2971828778, - 0x43f65b8611ab7585, - 0x3183aaf5ec279fdf, - 0xfa73d7e18ac99df6, - 0x64e176a6a64c99b0, - 0x179fa78c58388f1f - ]), - c1: Fp::from_raw_unchecked([ - 0x672a0a11ca2aef12, - 0xd11b9b52aa3f16b, - 0xa44412d0699d056e, - 0xc01d0177221a5ba5, - 0x66e0cede6c735529, - 0x5f5a71e9fddc339 - ]) - } + assert_eq!( + res.0, + Fp12 { + c0: Fp6 { + c0: Fp2 { + c0: Fp::from_raw_unchecked([ + 0x1972_e433_a01f_85c5, + 0x97d3_2b76_fd77_2538, + 0xc8ce_546f_c96b_cdf9, + 0xcef6_3e73_66d4_0614, + 0xa611_3427_8184_3780, + 0x13f3_448a_3fc6_d825, + ]), + c1: Fp::from_raw_unchecked([ + 0xd263_31b0_2e9d_6995, + 0x9d68_a482_f779_7e7d, + 0x9c9b_2924_8d39_ea92, + 0xf480_1ca2_e131_07aa, + 0xa16c_0732_bdbc_b066, + 0x083c_a4af_ba36_0478, + ]) + }, + c1: Fp2 { + c0: Fp::from_raw_unchecked([ + 0x59e2_61db_0916_b641, + 0x2716_b6f4_b23e_960d, + 0xc8e5_5b10_a0bd_9c45, + 0x0bdb_0bd9_9c4d_eda8, + 0x8cf8_9ebf_57fd_aac5, + 0x12d6_b792_9e77_7a5e, + ]), + c1: Fp::from_raw_unchecked([ + 0x5fc8_5188_b0e1_5f35, + 0x34a0_6e3a_8f09_6365, + 0xdb31_26a6_e02a_d62c, + 0xfc6f_5aa9_7d9a_990b, + 0xa12f_55f5_eb89_c210, + 0x1723_703a_926f_8889, + ]) }, - c1: Fp6 { - c0: Fp2 { - c0: Fp::from_raw_unchecked([ - 0xd30a88a1b062c679, - 0x5ac56a5d35fc8304, - 0xd0c834a6a81f290d, - 0xcd5430c2da3707c7, - 0xf0c27ff780500af0, - 0x9245da6e2d72eae - ]), - c1: Fp::from_raw_unchecked([ - 0x9f2e0676791b5156, - 0xe2d1c8234918fe13, - 0x4c9e459f3c561bf4, - 0xa3e85e53b9d3e3c1, - 0x820a121e21a70020, - 0x15af618341c59acc - ]) - }, - c1: Fp2 { - c0: Fp::from_raw_unchecked([ - 0x7c95658c24993ab1, - 0x73eb38721ca886b9, - 0x5256d749477434bc, - 0x8ba41902ea504a8b, - 0x4a3d3f80c86ce6d, - 0x18a64a87fb686eaa - ]), - c1: Fp::from_raw_unchecked([ - 0xbb83e71bb920cf26, - 0x2a5277ac92a73945, - 0xfc0ee59f94f046a0, - 0x7158cdf3786058f7, - 0x7cc1061b82f945f6, - 0x3f847aa9fdbe567 - ]) - }, - c2: Fp2 { - c0: Fp::from_raw_unchecked([ - 0x8078dba56134e657, - 0x1cd7ec9a43998a6e, - 0xb1aa599a1a993766, - 0xc9a0f62f0842ee44, - 0x8e159be3b605dffa, - 0xc86ba0d4af13fc2 - ]), - c1: Fp::from_raw_unchecked([ - 0xe80ff2a06a52ffb1, - 0x7694ca48721a906c, - 0x7583183e03b08514, - 0xf567afdd40cee4e2, - 0x9a6d96d2e526a5fc, - 0x197e9f49861f2242 - ]) - } + c2: Fp2 { + c0: Fp::from_raw_unchecked([ + 0x9358_8f29_7182_8778, + 0x43f6_5b86_11ab_7585, + 0x3183_aaf5_ec27_9fdf, + 0xfa73_d7e1_8ac9_9df6, + 0x64e1_76a6_a64c_99b0, + 0x179f_a78c_5838_8f1f, + ]), + c1: Fp::from_raw_unchecked([ + 0x672a_0a11_ca2a_ef12, + 0x0d11_b9b5_2aa3_f16b, + 0xa444_12d0_699d_056e, + 0xc01d_0177_221a_5ba5, + 0x66e0_cede_6c73_5529, + 0x05f5_a71e_9fdd_c339, + ]) + } + }, + c1: Fp6 { + c0: Fp2 { + c0: Fp::from_raw_unchecked([ + 0xd30a_88a1_b062_c679, + 0x5ac5_6a5d_35fc_8304, + 0xd0c8_34a6_a81f_290d, + 0xcd54_30c2_da37_07c7, + 0xf0c2_7ff7_8050_0af0, + 0x0924_5da6_e2d7_2eae, + ]), + c1: Fp::from_raw_unchecked([ + 0x9f2e_0676_791b_5156, + 0xe2d1_c823_4918_fe13, + 0x4c9e_459f_3c56_1bf4, + 0xa3e8_5e53_b9d3_e3c1, + 0x820a_121e_21a7_0020, + 0x15af_6183_41c5_9acc, + ]) + }, + c1: Fp2 { + c0: Fp::from_raw_unchecked([ + 0x7c95_658c_2499_3ab1, + 0x73eb_3872_1ca8_86b9, + 0x5256_d749_4774_34bc, + 0x8ba4_1902_ea50_4a8b, + 0x04a3_d3f8_0c86_ce6d, + 0x18a6_4a87_fb68_6eaa, + ]), + c1: Fp::from_raw_unchecked([ + 0xbb83_e71b_b920_cf26, + 0x2a52_77ac_92a7_3945, + 0xfc0e_e59f_94f0_46a0, + 0x7158_cdf3_7860_58f7, + 0x7cc1_061b_82f9_45f6, + 0x03f8_47aa_9fdb_e567, + ]) + }, + c2: Fp2 { + c0: Fp::from_raw_unchecked([ + 0x8078_dba5_6134_e657, + 0x1cd7_ec9a_4399_8a6e, + 0xb1aa_599a_1a99_3766, + 0xc9a0_f62f_0842_ee44, + 0x8e15_9be3_b605_dffa, + 0x0c86_ba0d_4af1_3fc2, + ]), + c1: Fp::from_raw_unchecked([ + 0xe80f_f2a0_6a52_ffb1, + 0x7694_ca48_721a_906c, + 0x7583_183e_03b0_8514, + 0xf567_afdd_40ce_e4e2, + 0x9a6d_96d2_e526_a5fc, + 0x197e_9f49_861f_2242, + ]) } } - ); - } + } + ); }