From 3d6703bd252497565e93983f958ac6430073a88f Mon Sep 17 00:00:00 2001 From: Wouter Geraedts Date: Tue, 20 Aug 2019 10:32:33 +0200 Subject: [PATCH 01/12] Implement serialization and deserialization for Fp2,6,12 and Gt --- src/fp12.rs | 63 +++++++++++++++++++++++++++++++++++++++++++++++++ src/fp2.rs | 27 +++++++++++++++++++++ src/fp6.rs | 30 +++++++++++++++++++++++ src/pairings.rs | 14 ++++++++++- src/scalar.rs | 10 +++++++- 5 files changed, 142 insertions(+), 2 deletions(-) diff --git a/src/fp12.rs b/src/fp12.rs index de9b5407..9c27201b 100644 --- a/src/fp12.rs +++ b/src/fp12.rs @@ -1,6 +1,7 @@ use crate::fp::*; use crate::fp2::*; use crate::fp6::*; +use crate::scalar::MODULUS; use core::fmt; use core::ops::{Add, AddAssign, Mul, MulAssign, Neg, Sub, SubAssign}; @@ -178,6 +179,68 @@ impl Fp12 { c1: self.c1 * -t, }) } + + /// Although this is labeled "vartime", it is only + /// variable time with respect to the exponent. It + /// is also not exposed in the public API. + pub fn pow_vartime(&self, by: &[u64]) -> Self { + let mut res = Self::one(); + for e in by.iter().rev() { + for i in (0..64).rev() { + res = res.square(); + + if ((*e >> i) & 1) == 1 { + res *= self; + } + } + } + res + } + + /// Attempts to convert a little-endian byte representation of + /// a scalar into an `Fp12`. + /// + /// Only fails when the underlying Fp elements are not canonical, + /// but not when `Fp12` is not part of the subgroup. + pub fn from_bytes_unchecked(bytes: &[u8; 576]) -> CtOption { + let mut buf = [0u8; 288]; + + buf.copy_from_slice(&bytes[0..288]); + let c0 = Fp6::from_bytes_unchecked(&buf); + buf.copy_from_slice(&bytes[288..576]); + let c1 = Fp6::from_bytes_unchecked(&buf); + + c0.and_then(|c0| c1.map(|c1| Fp12 { c0, c1 })) + } + + /// Attempts to convert a little-endian byte representation of + /// a scalar into an `Fp12`, failing if the input is not canonical. + pub fn from_bytes(bytes: &[u8; 576]) -> CtOption { + Fp12::from_bytes_unchecked(bytes).and_then(|res| { + // The exponent is a constant, + // thus this operation is constant time as well. + let modulus_pow = res.pow_vartime(&<[u64; 4]>::from(&MODULUS)); + + // Any field of characteristic p has at most one subgroup + // of order q so it suffices to check that raising the + // element to the power q aka scalar::MODULUS gives the + // identity. + let is_some = modulus_pow.ct_eq(&Fp12::one()); + + CtOption::new(res, is_some) + }) + } + + /// Converts an element of `Fp12` into a byte representation in + /// big-endian byte order. + pub fn to_bytes(&self) -> [u8; 576] { + let mut res = [0; 576]; + + res[0..288].copy_from_slice(&self.c0.to_bytes()); + res[288..576].copy_from_slice(&self.c1.to_bytes()); + + res + } } impl<'a, 'b> Mul<&'b Fp12> for &'a Fp12 { diff --git a/src/fp2.rs b/src/fp2.rs index d91662d8..21456438 100644 --- a/src/fp2.rs +++ b/src/fp2.rs @@ -334,6 +334,33 @@ impl Fp2 { } res } + + /// Attempts to convert a little-endian byte representation of + /// a scalar into an `Fp2`. + /// + /// Only fails when the underlying Fp elements are not canonical, + /// but not when `Fp2` is not part of the subgroup. + pub fn from_bytes_unchecked(bytes: &[u8; 96]) -> CtOption { + let mut buf = [0u8; 48]; + + buf.copy_from_slice(&bytes[0..48]); + let c0 = Fp::from_bytes(&buf); + buf.copy_from_slice(&bytes[48..96]); + let c1 = Fp::from_bytes(&buf); + + c0.and_then(|c0| c1.map(|c1| Fp2 { c0, c1 })) + } + + /// Converts an element of `Fp2` into a byte representation in + /// big-endian byte order. + pub fn to_bytes(&self) -> [u8; 96] { + let mut res = [0; 96]; + + res[0..48].copy_from_slice(&self.c0.to_bytes()); + res[48..96].copy_from_slice(&self.c1.to_bytes()); + + res + } } #[test] diff --git a/src/fp6.rs b/src/fp6.rs index 50ed2ebf..6c5c37cf 100644 --- a/src/fp6.rs +++ b/src/fp6.rs @@ -222,6 +222,36 @@ impl Fp6 { c2: t * c2, }) } + + /// Attempts to convert a little-endian byte representation of + /// a scalar into an `Fp6`. + /// + /// Only fails when the underlying Fp elements are not canonical, + /// but not when `Fp6` is not part of the subgroup. + pub fn from_bytes_unchecked(bytes: &[u8; 288]) -> CtOption { + let mut buf = [0u8; 96]; + + buf.copy_from_slice(&bytes[0..96]); + let c0 = Fp2::from_bytes_unchecked(&buf); + buf.copy_from_slice(&bytes[96..192]); + let c1 = Fp2::from_bytes_unchecked(&buf); + buf.copy_from_slice(&bytes[192..288]); + let c2 = Fp2::from_bytes_unchecked(&buf); + + c0.and_then(|c0| c1.and_then(|c1| c2.map(|c2| Fp6 { c0, c1, c2 }))) + } + + /// Converts an element of `Fp6` into a byte representation in + /// big-endian byte order. + pub fn to_bytes(&self) -> [u8; 288] { + let mut res = [0; 288]; + + res[0..96].copy_from_slice(&self.c0.to_bytes()); + res[96..192].copy_from_slice(&self.c1.to_bytes()); + res[192..288].copy_from_slice(&self.c2.to_bytes()); + + res + } } impl<'a, 'b> Mul<&'b Fp6> for &'a Fp6 { diff --git a/src/pairings.rs b/src/pairings.rs index 8ef69321..147ee4ef 100644 --- a/src/pairings.rs +++ b/src/pairings.rs @@ -4,7 +4,7 @@ use crate::{G1Affine, G2Affine, G2Projective, Scalar, BLS_X, BLS_X_IS_NEGATIVE}; use core::ops::{Add, AddAssign, Mul, MulAssign, Neg, Sub, SubAssign}; -use subtle::{Choice, ConditionallySelectable, ConstantTimeEq}; +use subtle::{Choice, ConditionallySelectable, ConstantTimeEq, CtOption}; /// Represents results of a Miller loop, one of the most expensive portions /// of the pairing function. `MillerLoopResult`s cannot be compared with each @@ -147,6 +147,18 @@ impl Gt { pub fn double(&self) -> Gt { Gt(self.0.square()) } + + /// Serializes this element into uncompressed form. See [`notes::serialization`](crate::notes::serialization) + /// for details about how group elements are serialized. + pub fn to_uncompressed(&self) -> [u8; 576] { + self.0.to_bytes() + } + + /// Attempts to deserialize an uncompressed element. See [`notes::serialization`](crate::notes::serialization) + /// for details about how group elements are serialized. + pub fn from_uncompressed(bytes: &[u8; 576]) -> CtOption { + Fp12::from_bytes(bytes).map(|x| Gt(x)) + } } impl<'a> Neg for &'a Gt { diff --git a/src/scalar.rs b/src/scalar.rs index 387b95ea..d6d91fb7 100644 --- a/src/scalar.rs +++ b/src/scalar.rs @@ -63,7 +63,7 @@ impl ConditionallySelectable for Scalar { /// Constant representing the modulus /// q = 0x73eda753299d7d483339d80809a1d80553bda402fffe5bfeffffffff00000001 -const MODULUS: Scalar = Scalar([ +pub(crate) const MODULUS: Scalar = Scalar([ 0xffffffff00000001, 0x53bda402fffe5bfe, 0x3339d80809a1d805, @@ -619,6 +619,14 @@ impl<'a> From<&'a Scalar> for [u8; 32] { } } +impl<'a> From<&'a Scalar> for [u64; 4] { + fn from(value: &'a Scalar) -> [u64; 4] { + let res = + Scalar::montgomery_reduce(value.0[0], value.0[1], value.0[2], value.0[3], 0, 0, 0, 0); + res.0 + } +} + #[test] fn test_inv() { // Compute -(q^{-1} mod 2^64) mod 2^64 by exponentiating From fd12b57a8a117a58179dacf594a5f38b3e6271e2 Mon Sep 17 00:00:00 2001 From: Wouter Geraedts Date: Tue, 20 Aug 2019 10:48:19 +0200 Subject: [PATCH 02/12] Added Default for Gt --- src/pairings.rs | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/src/pairings.rs b/src/pairings.rs index 147ee4ef..cd3a93cc 100644 --- a/src/pairings.rs +++ b/src/pairings.rs @@ -129,6 +129,12 @@ impl ConditionallySelectable for Gt { } } +impl Default for Gt { + fn default() -> Gt { + Gt::identity() + } +} + impl Eq for Gt {} impl PartialEq for Gt { #[inline] From 12f82497f5d9aad78ef24006e8dc0794ba4eb8ef Mon Sep 17 00:00:00 2001 From: Wouter Geraedts Date: Tue, 3 Sep 2019 15:00:56 +0200 Subject: [PATCH 03/12] Added scalar benchmarks and gt scalar multiplication benchmark --- benches/groups.rs | 27 +++++++++++++++++++++++++++ 1 file changed, 27 insertions(+) diff --git a/benches/groups.rs b/benches/groups.rs index 5d2b22b3..7031e898 100644 --- a/benches/groups.rs +++ b/benches/groups.rs @@ -7,14 +7,41 @@ use bls12_381::*; use criterion::{black_box, Criterion}; fn criterion_benchmark(c: &mut Criterion) { + // Scalar + { + let name = "Scalar"; + let x = Scalar::from_raw([1, 2, 3, 4]); + let y = Scalar::from_raw([1, 2, 3, 4]); + let bytes = [0u8; 64]; + c.bench_function(&format!("{} addition", name), move |b| { + b.iter(|| black_box(&x) + black_box(&y)) + }); + c.bench_function(&format!("{} multiplication", name), move |b| { + b.iter(|| black_box(&x) * black_box(&y)) + }); + c.bench_function(&format!("{} exponentiation", name), move |b| { + b.iter(|| black_box(&x).pow(&black_box((&y).into()))) + }); + c.bench_function(&format!("{} from bytes wide", name), move |b| { + b.iter(|| Scalar::from_bytes_wide(black_box(&bytes))) + }); + } + // Pairings { + let name = "Gt"; let g = G1Affine::generator(); let h = G2Affine::generator(); + let a = pairing(&g, &h); + let s = Scalar::from_raw([1, 2, 3, 4]); c.bench_function("full pairing", move |b| { b.iter(|| pairing(black_box(&g), black_box(&h))) }); + c.bench_function(&format!("{} scalar multiplication", name), move |b| { + b.iter(|| black_box(a) * black_box(s)) + }); } + // G1Affine { let name = "G1Affine"; From 28689c7a9064a7d0fb81618d4791c75d9cfa21a6 Mon Sep 17 00:00:00 2001 From: Wouter Geraedts Date: Tue, 3 Sep 2019 17:38:29 +0200 Subject: [PATCH 04/12] Implemented compressed serialisation/deserialisation for Gt --- src/fp12.rs | 24 +-- src/fp6.rs | 445 ++++++++++++++++++++++++++++++++++++++++++++++++ src/pairings.rs | 100 ++++++++++- 3 files changed, 558 insertions(+), 11 deletions(-) diff --git a/src/fp12.rs b/src/fp12.rs index 9c27201b..d4b1f7fa 100644 --- a/src/fp12.rs +++ b/src/fp12.rs @@ -217,16 +217,7 @@ impl Fp12 { /// a scalar into an `Fp12`, failing if the input is not canonical. pub fn from_bytes(bytes: &[u8; 576]) -> CtOption { Fp12::from_bytes_unchecked(bytes).and_then(|res| { - // The exponent is a constant, - // thus this operation is constant time as well. - let modulus_pow = res.pow_vartime(&<[u64; 4]>::from(&MODULUS)); - - // Any field of characteristic p has at most one subgroup - // of order q so it suffices to check that raising the - // element to the power q aka scalar::MODULUS gives the - // identity. - let is_some = modulus_pow.ct_eq(&Fp12::one()); - + let is_some = res.is_element(); CtOption::new(res, is_some) }) } @@ -241,6 +232,19 @@ impl Fp12 { res } + + /// Returns true if this element belongs to the Fp12 group. + pub fn is_element(&self) -> Choice { + // The exponent is a constant, + // thus this operation is constant time as well. + let modulus_pow = self.pow_vartime(&<[u64; 4]>::from(&MODULUS)); + + // Any field of characteristic p has at most one subgroup + // of order q so it suffices to check that raising the + // element to the power q aka scalar::MODULUS gives the + // identity. + modulus_pow.ct_eq(&Fp12::one()) + } } impl<'a, 'b> Mul<&'b Fp12> for &'a Fp12 { diff --git a/src/fp6.rs b/src/fp6.rs index 6c5c37cf..dccbc741 100644 --- a/src/fp6.rs +++ b/src/fp6.rs @@ -142,6 +142,20 @@ impl Fp6 { } } + /// Returns whether or not this element is strictly lexicographically + /// larger than its negation. + #[inline] + pub fn lexicographically_largest(&self) -> Choice { + // If this element's c1 coefficient is lexicographically largest + // then it is lexicographically largest. Otherwise, in the event + // the c1 coefficient is zero and the c0 coefficient is + // lexicographically largest, then this element is lexicographically + // largest. + + self.c1.lexicographically_largest() + | (self.c1.is_zero() & self.c0.lexicographically_largest()) + } + /// Raises this element to p. #[inline(always)] pub fn frobenius_map(&self) -> Self { @@ -202,6 +216,124 @@ impl Fp6 { } } + /// Square root + /// + /// As described by: + /// "On the Computation of Square Roots in Finite Fields", proposition 2.1 + /// By Siguna Müller, 2004 + /// + /// Uses the fact that p^6 = 9 mod 16. + pub fn sqrt(&self) -> CtOption { + // Q_1_4 = (modulus^6 - 1) / 4 + const Q_1_4: [u64; 36] = [ + 0xb1b26118f01c175a, + 0xf8a2683cfd2fcb7, + 0xf5ecead6e31ae561, + 0x788892a3ae5aaa66, + 0x6f1b989afdd74c6c, + 0x44b0febfb9ca2f19, + 0xaa44afead22b2a8c, + 0x44412b069787405b, + 0x1d4f314ef085b227, + 0xa3438bfd9d5dc836, + 0x3aca6af3d8e4c9cd, + 0x9233ff8daf86758b, + 0xf183aa79f6a23e1e, + 0x9285a7b5ef849914, + 0x3392479651e7cbc1, + 0xba3bd9bd93f0e78e, + 0x1681362d6278bb82, + 0xbf9fb30183701059, + 0x8e8e4f1c7eea8aa5, + 0xe0eba5f5b90a8877, + 0x82c196b55e440708, + 0x476387890d02af5e, + 0x733d7734aebdd85b, + 0x233beef2d2cc2a7b, + 0xfe1257e301d152ee, + 0x977cd3e02d91b8c0, + 0x1a7e36349a50bf5b, + 0xf734044b05c5b0a7, + 0x6455a14c3662f861, + 0xedea13251b5203df, + 0x714e2975915a9a71, + 0x817b0e2e3d10781d, + 0x52fd761dc052d57d, + 0x3c8b51fa3d322987, + 0x687d273175e44744, + 0x49b8ea73982, + ]; + + // Note: 2^((p^6-1)/4) = 1 in Fp6, so s^2 = self^((p^6-1)/2), + // so Legendre-symbol, hence self is a quadratic residue iff s = 1 or s = -1. + // TODO: use addition chains. + let s = (self + self).pow_vartime(&Q_1_4); + + let v = Fp6 { + c0: Fp2::zero(), + c1: Fp2::one(), + c2: Fp2::zero(), + }; + + let is_one = s.ct_eq(&Fp6::one()); + let is_neg_one = s.ct_eq(&-Fp6::one()); + + let d = Fp6::conditional_select(&-Fp6::one(), &v, is_one); + + // Q_9_16 = (modulus^6 - 9) / 16 + const Q_9_16: [u64; 36] = [ + 0xec6c98463c0705d6, + 0x43e289a0f3f4bf2d, + 0xbd7b3ab5b8c6b958, + 0x1e2224a8eb96aa99, + 0x5bc6e626bf75d31b, + 0x112c3fafee728bc6, + 0xea912bfab48acaa3, + 0xd1104ac1a5e1d016, + 0x8753cc53bc216c89, + 0x68d0e2ff6757720d, + 0xceb29abcf6393273, + 0xa48cffe36be19d62, + 0x3c60ea9e7da88f87, + 0x64a169ed7be12645, + 0x8ce491e59479f2f0, + 0xae8ef66f64fc39e3, + 0x45a04d8b589e2ee0, + 0x6fe7ecc060dc0416, + 0xe3a393c71fbaa2a9, + 0x383ae97d6e42a21d, + 0xa0b065ad579101c2, + 0xd1d8e1e24340abd7, + 0xdccf5dcd2baf7616, + 0x88cefbbcb4b30a9e, + 0x3f8495f8c07454bb, + 0xe5df34f80b646e30, + 0xc69f8d8d26942fd6, + 0x7dcd0112c1716c29, + 0xd91568530d98be18, + 0x7b7a84c946d480f7, + 0x5c538a5d6456a69c, + 0x605ec38b8f441e07, + 0xd4bf5d877014b55f, + 0xf22d47e8f4c8a61, + 0x9a1f49cc5d7911d1, + 0x126e3a9ce60, + ]; + + let dd = d * d; + let ddx = dd * self; + + // TODO: use addition chains. + let z = (ddx + ddx).pow_vartime(&Q_9_16); + + let hi = ddx * z * z; + let i = hi + hi; + + let a = z * d * self * (i - Fp6::one()); + + CtOption::new(a, is_one | is_neg_one) + } + #[inline] pub fn invert(&self) -> CtOption { let c0 = (self.c1 * self.c2).mul_by_nonresidue(); @@ -223,6 +355,23 @@ impl Fp6 { }) } + /// Although this is labeled "vartime", it is only + /// variable time with respect to the exponent. It + /// is also not exposed in the public API. + pub fn pow_vartime(&self, by: &[u64]) -> Self { + let mut res = Self::one(); + for e in by.iter().rev() { + for i in (0..64).rev() { + res = res.square(); + + if ((*e >> i) & 1) == 1 { + res *= self; + } + } + } + res + } + /// Attempts to convert a little-endian byte representation of /// a scalar into an `Fp6`. /// @@ -535,3 +684,299 @@ fn test_arithmetic() { ); assert_eq!(&a.invert().unwrap() * &a, Fp6::one()); } + +#[test] +fn test_sqrt() { + let a = Fp6 { + c0: Fp2 { + c0: Fp::from_raw_unchecked([ + 0x615eaaf7e0049a1b, + 0x7db3249009df9588, + 0x5d9254c0f7ae87f1, + 0x14fee19cbfc1faca, + 0x3017e7271c83b32b, + 0xbdc34aaf515eb44, + ]), + c1: Fp::from_raw_unchecked([ + 0x27e6b317a77e12d0, + 0x341b70fc95934deb, + 0x26bd37e4251442ab, + 0x8c7bf72e39756512, + 0x1d2a1377ffc35dd4, + 0x735f5a52f945f95, + ]), + }, + c1: Fp2 { + c0: Fp::from_raw_unchecked([ + 0x2b5775a7a21ba5ba, + 0x8b5c1025c7098c9f, + 0x4d29b1556a548261, + 0x7a045cbceb12c9f0, + 0x2324654df63d1675, + 0x1113123138f58432, + ]), + c1: Fp::from_raw_unchecked([ + 0x3f4d0c00005dc31b, + 0xed1d44e80072a5b, + 0xfdeda4845c7115ed, + 0x6b8d8cd2f54986dd, + 0xa3de763c81254081, + 0x1030efee1d581ee4, + ]), + }, + c2: Fp2 { + c0: Fp::from_raw_unchecked([ + 0xf376d245bed59044, + 0x335afd18409563ee, + 0xd1ee1e7d2cfba1b4, + 0x17086c56016a6b2b, + 0x30c195f0664865a9, + 0x5bc0c3bef4e9565, + ]), + c1: Fp::from_raw_unchecked([ + 0x29241b89771406dd, + 0x3b269017c337a140, + 0xcf0c50cfdf0fb818, + 0xf1a56e35e67614bd, + 0x373427c6e475ec5e, + 0x10ab1bd5fbed215d, + ]), + }, + }; + + assert!(bool::from(a.sqrt().is_none())); + + let b = Fp6 { + c0: Fp2 { + c0: Fp::from_raw_unchecked([ + 0x760900000002fffd, + 0xebf4000bc40c0002, + 0x5f48985753c758ba, + 0x77ce585370525745, + 0x5c071a97a256ec6d, + 0x15f65ec3fa80e493, + ]), + c1: Fp::from_raw_unchecked([ + 0x321300000006554f, + 0xb93c0018d6c40005, + 0x57605e0db0ddbb51, + 0x8b256521ed1f9bcb, + 0x6cf28d7901622c03, + 0x11ebab9dbb81e28c, + ]), + }, + c1: Fp2 { + c0: Fp::from_raw_unchecked([ + 0xee1d00000009aaa1, + 0x86840025e97c0007, + 0x4f7823c40df41de8, + 0x9e7c71f069ece051, + 0x7dde005a606d6b99, + 0xde0f8777c82e085, + ]), + c1: Fp::from_raw_unchecked([ + 0xaa270000000cfff3, + 0x53cc0032fc34000a, + 0x478fe97a6b0a807f, + 0xb1d37ebee6ba24d7, + 0x8ec9733bbf78ab2f, + 0x9d645513d83de7e, + ]), + }, + c2: Fp2 { + c0: Fp::from_raw_unchecked([ + 0x6631000000105545, + 0x211400400eec000d, + 0x3fa7af30c820e316, + 0xc52a8b8d6387695d, + 0x9fb4e61d1e83eac5, + 0x5cb922afe84dc77, + ]), + c1: Fp::from_raw_unchecked([ + 0x223b00000013aa97, + 0xee5c004d21a40010, + 0x37bf74e7253745ac, + 0xd881985be054ade3, + 0xb0a058fe7d8f2a5b, + 0x1c0df04bf85da70, + ]), + }, + }; + let b_sqrt = Fp6 { + c0: Fp2 { + c0: Fp::from_raw_unchecked([ + 0xdacab8ec196d0e90, + 0x87e85ab6ea88b979, + 0x3dfe939a4a365ef1, + 0x78d2523061125499, + 0x6fc4397c4dc7b39, + 0x178d99f425a98078, + ]), + c1: Fp::from_raw_unchecked([ + 0x5f61615b4b6b9955, + 0xfa5b876c8ea831b5, + 0x3fd6d7cd22e2fb76, + 0x2d55c9a9feef3d0a, + 0x7adfaf601698839c, + 0xd2971c3c245dbdb, + ]), + }, + c1: Fp2 { + c0: Fp::from_raw_unchecked([ + 0xd1857aba9d3a5ad2, + 0xaa0fcc118b33fd83, + 0xdddf06c2cd76474b, + 0xf2ba6fae3c211902, + 0x81b879d941bf01e8, + 0x16efa6ec5c6ebf43, + ]), + c1: Fp::from_raw_unchecked([ + 0x6b7a79f9320e4b80, + 0xf0d55c31e63117d6, + 0x9f0c4f9fbb78699e, + 0xffc9af394b9b8049, + 0xb76d97ef754a5ad, + 0xb5172e8b69f5596, + ]), + }, + c2: Fp2 { + c0: Fp::from_raw_unchecked([ + 0xf140b9d2f1e99c5e, + 0xc78982e4ca301b97, + 0x98f3a4b656f50198, + 0xaa310cb32c652865, + 0xcbee9785769731bb, + 0x16f81c9ea55bde91, + ]), + c1: Fp::from_raw_unchecked([ + 0x83304d5cf6ddb3d0, + 0x3bc1eac936b91f3f, + 0x26009dc8b2afd880, + 0x3d88fa5fd4a3a1a7, + 0x524af7c39e6b675d, + 0x1460fef116f3d046, + ]), + }, + }; + + assert_eq!(b_sqrt * b_sqrt, b); + assert_eq!(b.sqrt().unwrap().square(), b); + assert_eq!(b.sqrt().unwrap(), b_sqrt); + + let c = Fp6 { + c0: Fp2 { + c0: Fp::from_raw_unchecked([ + 0xaa270000000cfff3, + 0x53cc0032fc34000a, + 0x478fe97a6b0a807f, + 0xb1d37ebee6ba24d7, + 0x8ec9733bbf78ab2f, + 0x9d645513d83de7e, + ]), + c1: Fp::from_raw_unchecked([ + 0x321300000006554f, + 0xb93c0018d6c40005, + 0x57605e0db0ddbb51, + 0x8b256521ed1f9bcb, + 0x6cf28d7901622c03, + 0x11ebab9dbb81e28c, + ]), + }, + c1: Fp2 { + c0: Fp::from_raw_unchecked([ + 0xee1d00000009aaa1, + 0x86840025e97c0007, + 0x4f7823c40df41de8, + 0x9e7c71f069ece051, + 0x7dde005a606d6b99, + 0xde0f8777c82e085, + ]), + c1: Fp::from_raw_unchecked([ + 0xaa270000000cfff3, + 0x53cc0032fc34000a, + 0x478fe97a6b0a807f, + 0xb1d37ebee6ba24d7, + 0x8ec9733bbf78ab2f, + 0x9d645513d83de7e, + ]), + }, + c2: Fp2 { + c0: Fp::from_raw_unchecked([ + 0x6631000000105545, + 0x211400400eec000d, + 0x3fa7af30c820e316, + 0xc52a8b8d6387695d, + 0x9fb4e61d1e83eac5, + 0x5cb922afe84dc77, + ]), + c1: Fp::from_raw_unchecked([ + 0x223b00000013aa97, + 0xee5c004d21a40010, + 0x37bf74e7253745ac, + 0xd881985be054ade3, + 0xb0a058fe7d8f2a5b, + 0x1c0df04bf85da70, + ]), + }, + }; + let c_sqrt = Fp6 { + c0: Fp2 { + c0: Fp::from_raw_unchecked([ + 0xbc5c83c79ee17378, + 0x6234c76e1e43427d, + 0xa967a76ded98934, + 0x60530cb49f3aa701, + 0xf1e78d8b238ce13b, + 0xcae66f9d906cc2, + ]), + c1: Fp::from_raw_unchecked([ + 0x8e0b93ad5a9e2ad8, + 0x9f651961fde14bf2, + 0x4c1dbb672da9e549, + 0x6a9dd580ee524230, + 0x37f847eccc026, + 0x8759709a578b0d, + ]), + }, + c1: Fp2 { + c0: Fp::from_raw_unchecked([ + 0x1df7771f87b25d2d, + 0xce9d90f1fb56fe78, + 0xea74bda2cc72e5ea, + 0xf240542d5067f34e, + 0x5c127ed5f9d549c6, + 0x4b40109ac4a835a, + ]), + c1: Fp::from_raw_unchecked([ + 0x280644f936de9b22, + 0xc66d88e8b24bcc50, + 0x59c13da5b138eb11, + 0x58eb4797886a4ad5, + 0x906577dcb6d18661, + 0x12b4501b3e3c9f3a, + ]), + }, + c2: Fp2 { + c0: Fp::from_raw_unchecked([ + 0xccbcf4677c99dfcb, + 0x8001c4f4626cc646, + 0x47d3f89c286446a9, + 0x1c85adb35001a959, + 0x933daef463a2592c, + 0x2763061b8787ca0, + ]), + c1: Fp::from_raw_unchecked([ + 0xdcb4c1ccf25dcf8e, + 0xf1a4f384c2a0a4ae, + 0x3e20636334c0d7d1, + 0xcb6d42fd5a06e476, + 0x3eff57d6357d7d40, + 0x1528dc22578f54dd, + ]), + }, + }; + + assert_eq!(c_sqrt * c_sqrt, c); + assert_eq!(c.sqrt().unwrap().square(), c); + assert_eq!(c.sqrt().unwrap(), c_sqrt); +} diff --git a/src/pairings.rs b/src/pairings.rs index cd3a93cc..4071c722 100644 --- a/src/pairings.rs +++ b/src/pairings.rs @@ -1,5 +1,6 @@ use crate::fp12::Fp12; use crate::fp2::Fp2; +use crate::fp6::Fp6; use crate::{G1Affine, G2Affine, G2Projective, Scalar, BLS_X, BLS_X_IS_NEGATIVE}; use core::ops::{Add, AddAssign, Mul, MulAssign, Neg, Sub, SubAssign}; @@ -163,7 +164,62 @@ impl Gt { /// Attempts to deserialize an uncompressed element. See [`notes::serialization`](crate::notes::serialization) /// for details about how group elements are serialized. pub fn from_uncompressed(bytes: &[u8; 576]) -> CtOption { - Fp12::from_bytes(bytes).map(|x| Gt(x)) + Fp12::from_bytes(bytes).map(Gt) + } + + /// Serializes this element into compressed form. See [`notes::serialization`](crate::notes::serialization) + /// for details about how group elements are serialized. + pub fn to_compressed(&self) -> [u8; 288] { + let mut res = self.0.c1.to_bytes(); + + // This point is in compressed form, so we set the most significant bit. + res[0] |= 1u8 << 7; + + // Is the y-coordinate the lexicographically largest of the two associated with the + // x-coordinate? If so, set the second-most significant bit so long as this is not + // the point at infinity. + res[0] |= u8::conditional_select(&0u8, &(1u8 << 6), self.0.c0.lexicographically_largest()); + + res + } + + /// Attempts to deserialize a compressed element. See [`notes::serialization`](crate::notes::serialization) + /// for details about how group elements are serialized. + pub fn from_compressed(bytes: &[u8; 288]) -> CtOption { + Self::from_compressed_unchecked(bytes).and_then(|p| CtOption::new(p, p.0.is_element())) + } + + /// Attempts to deserialize an compressed element, not checking if the + /// element is in the correct pairing group. + /// **This is dangerous to call unless you trust the bytes you are reading; otherwise, + /// API invariants may be broken.** Please consider using `from_uncompressed()` instead. + pub fn from_compressed_unchecked(bytes: &[u8; 288]) -> CtOption { + let compression_flag_set = Choice::from((bytes[0] >> 7) & 1); + let sort_flag_set = Choice::from((bytes[0] >> 6) & 1); + + let xc1 = { + let mut tmp = [0; 288]; + tmp.copy_from_slice(&bytes[0..288]); + + // Mask away the flag bits + tmp[0] &= 0b0011_1111; + + Fp6::from_bytes_unchecked(&tmp) + }; + + xc1.and_then(|c1| { + // c_0^2 = 1 + v * c_1^2 + let xc0 = (Fp6::one() + c1.square().mul_by_nonresidue()).sqrt(); + + xc0.and_then(|c0| { + let p = Fp12 { + c0: Fp6::conditional_select(&c0, &-c0, sort_flag_set), + c1, + }; + + CtOption::new(Gt(p), compression_flag_set) + }) + }) } } @@ -432,3 +488,45 @@ fn test_unitary() { assert_eq!(p, q); assert_eq!(q, r); } + +#[test] +fn test_uncompressed() { + let gt = + pairing(&G1Affine::generator(), &G2Affine::generator()) * Scalar::from_raw([1, 2, 3, 4]); + let buf = gt.to_uncompressed(); + let gt2 = Gt::from_uncompressed(&buf).unwrap(); + + assert_eq!(gt, gt2); + + let gt = + pairing(&G1Affine::generator(), &G2Affine::generator()) * Scalar::from_raw([1, 2, 3, 5]); + let buf = gt.to_uncompressed(); + let gt2 = Gt::from_uncompressed(&buf).unwrap(); + + assert_eq!(gt, gt2); +} + +#[test] +fn test_compressed() { + let gt = + pairing(&G1Affine::generator(), &G2Affine::generator()) * Scalar::from_raw([1, 2, 3, 4]); + let buf = gt.to_compressed(); + + assert_eq!(buf[0] >> 7 & 1, 1); + assert_eq!(buf[0] >> 6 & 1, 1); + + let gt2 = Gt::from_compressed_unchecked(&buf).unwrap(); + + assert_eq!(gt, gt2); + + let gt = + pairing(&G1Affine::generator(), &G2Affine::generator()) * Scalar::from_raw([1, 2, 3, 5]); + let buf = gt.to_compressed(); + + assert_eq!(buf[0] >> 7 & 1, 1); + assert_eq!(buf[0] >> 6 & 1, 0); + + let gt2 = Gt::from_compressed_unchecked(&buf).unwrap(); + + assert_eq!(gt, gt2); +} From b9d15f272d647990526cb374f30fb7c6bb1387c4 Mon Sep 17 00:00:00 2001 From: Wouter Geraedts Date: Thu, 5 Sep 2019 18:02:06 +0200 Subject: [PATCH 05/12] Fix c0.lexicographically_largest test in Gt::from_compressed --- src/pairings.rs | 16 +++++++++++----- 1 file changed, 11 insertions(+), 5 deletions(-) diff --git a/src/pairings.rs b/src/pairings.rs index 4071c722..293cce5c 100644 --- a/src/pairings.rs +++ b/src/pairings.rs @@ -213,7 +213,11 @@ impl Gt { xc0.and_then(|c0| { let p = Fp12 { - c0: Fp6::conditional_select(&c0, &-c0, sort_flag_set), + c0: Fp6::conditional_select( + &c0, + &-c0, + c0.lexicographically_largest() ^ sort_flag_set, + ), c1, }; @@ -510,23 +514,25 @@ fn test_uncompressed() { fn test_compressed() { let gt = pairing(&G1Affine::generator(), &G2Affine::generator()) * Scalar::from_raw([1, 2, 3, 4]); + let buf = gt.to_compressed(); assert_eq!(buf[0] >> 7 & 1, 1); assert_eq!(buf[0] >> 6 & 1, 1); - let gt2 = Gt::from_compressed_unchecked(&buf).unwrap(); + let gt2 = Gt::from_compressed(&buf).unwrap(); assert_eq!(gt, gt2); - let gt = - pairing(&G1Affine::generator(), &G2Affine::generator()) * Scalar::from_raw([1, 2, 3, 5]); + let gt = pairing(&G1Affine::generator(), &G2Affine::generator()) + * Scalar::from_raw([500001, 2, 3, 4]); + let buf = gt.to_compressed(); assert_eq!(buf[0] >> 7 & 1, 1); assert_eq!(buf[0] >> 6 & 1, 0); - let gt2 = Gt::from_compressed_unchecked(&buf).unwrap(); + let gt2 = Gt::from_compressed(&buf).unwrap(); assert_eq!(gt, gt2); } From b454f0f3a05fc2bcdf4614066fcfd0b3623f501d Mon Sep 17 00:00:00 2001 From: Wouter Geraedts Date: Thu, 5 Sep 2019 18:10:09 +0200 Subject: [PATCH 06/12] Added deserialisation benchmarks for Gt --- benches/groups.rs | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/benches/groups.rs b/benches/groups.rs index 7031e898..04f3320b 100644 --- a/benches/groups.rs +++ b/benches/groups.rs @@ -34,12 +34,22 @@ fn criterion_benchmark(c: &mut Criterion) { let h = G2Affine::generator(); let a = pairing(&g, &h); let s = Scalar::from_raw([1, 2, 3, 4]); + let compressed = [0u8; 288]; + let uncompressed = [0u8; 576]; c.bench_function("full pairing", move |b| { b.iter(|| pairing(black_box(&g), black_box(&h))) }); c.bench_function(&format!("{} scalar multiplication", name), move |b| { b.iter(|| black_box(a) * black_box(s)) }); + c.bench_function( + &format!("{} deserialize compressed element", name), + move |b| b.iter(|| Gt::from_compressed(black_box(&compressed))), + ); + c.bench_function( + &format!("{} deserialize uncompressed element", name), + move |b| b.iter(|| Gt::from_uncompressed(black_box(&uncompressed))), + ); } // G1Affine From 41e39748d0c89727842b511f99c1cb7af5d57344 Mon Sep 17 00:00:00 2001 From: Bas Westerbaan Date: Thu, 5 Sep 2019 19:16:07 +0200 Subject: [PATCH 07/12] Fp6::sqrt, eliminate an exponentiation This makes Fp6::sqrt twice as fast. --- src/fp6.rs | 103 +++++++++++++++++++---------------------------------- 1 file changed, 37 insertions(+), 66 deletions(-) diff --git a/src/fp6.rs b/src/fp6.rs index dccbc741..09d64e92 100644 --- a/src/fp6.rs +++ b/src/fp6.rs @@ -218,69 +218,36 @@ impl Fp6 { /// Square root /// - /// As described by: - /// "On the Computation of Square Roots in Finite Fields", proposition 2.1 - /// By Siguna Müller, 2004 + /// Based on the generalized Atkin-algorithm due to Siguna Müller described + /// in proposition 2.1 of the 2014 "On the Computation of Square Roots + /// in Finite Fields". In his proposal Müller uses two exponentiations, + /// of which one can be eliminated. /// /// Uses the fact that p^6 = 9 mod 16. pub fn sqrt(&self) -> CtOption { - // Q_1_4 = (modulus^6 - 1) / 4 - const Q_1_4: [u64; 36] = [ - 0xb1b26118f01c175a, - 0xf8a2683cfd2fcb7, - 0xf5ecead6e31ae561, - 0x788892a3ae5aaa66, - 0x6f1b989afdd74c6c, - 0x44b0febfb9ca2f19, - 0xaa44afead22b2a8c, - 0x44412b069787405b, - 0x1d4f314ef085b227, - 0xa3438bfd9d5dc836, - 0x3aca6af3d8e4c9cd, - 0x9233ff8daf86758b, - 0xf183aa79f6a23e1e, - 0x9285a7b5ef849914, - 0x3392479651e7cbc1, - 0xba3bd9bd93f0e78e, - 0x1681362d6278bb82, - 0xbf9fb30183701059, - 0x8e8e4f1c7eea8aa5, - 0xe0eba5f5b90a8877, - 0x82c196b55e440708, - 0x476387890d02af5e, - 0x733d7734aebdd85b, - 0x233beef2d2cc2a7b, - 0xfe1257e301d152ee, - 0x977cd3e02d91b8c0, - 0x1a7e36349a50bf5b, - 0xf734044b05c5b0a7, - 0x6455a14c3662f861, - 0xedea13251b5203df, - 0x714e2975915a9a71, - 0x817b0e2e3d10781d, - 0x52fd761dc052d57d, - 0x3c8b51fa3d322987, - 0x687d273175e44744, - 0x49b8ea73982, - ]; - - // Note: 2^((p^6-1)/4) = 1 in Fp6, so s^2 = self^((p^6-1)/2), - // so Legendre-symbol, hence self is a quadratic residue iff s = 1 or s = -1. - // TODO: use addition chains. - let s = (self + self).pow_vartime(&Q_1_4); - let v = Fp6 { + // In Müller's proposal one first computes s := (2x)^((p^6-1)/4). + // If s is 1 or -1, then the x is a quadratic residue (ie. the square + // exists.) Depending on the value of s, one choses a random d which + // is either a quadratic residue or not. Instead of computing s, we + // simply proceed with two fixed choices of d of which one is + // a quadratic residue and the other isn't. At the end we check which + // candidate is an actual root and return it (or return nothing + // if both aren't roots.) + + let d1 = -Fp6::one(); // -1, a quadratic residue + let d2 = Fp6 { c0: Fp2::zero(), c1: Fp2::one(), c2: Fp2::zero(), - }; + }; // v, a quadratic non-residue - let is_one = s.ct_eq(&Fp6::one()); - let is_neg_one = s.ct_eq(&-Fp6::one()); + // (2d1^2)^((p^6-9)/16) + let d1p = Fp6 { c0: Fp2 { c0: Fp::from_raw_unchecked([0x3e2f585da55c9ad1, 0x4294213d86c18183, 0x382844c88b623732, 0x92ad2afd19103e18, 0x1d794e4fac7cf0b9, 0xbd592fc7d825ec8]), c1: Fp::from_raw_unchecked([0,0,0,0,0,0])}, c1: Fp2 { c0: Fp::from_raw_unchecked([0,0,0,0,0,0]), c1: Fp::from_raw_unchecked([0,0,0,0,0,0])}, c2: Fp2 { c0: Fp::from_raw_unchecked([0,0,0,0,0,0]), c1: Fp::from_raw_unchecked([0,0,0,0,0,0])}}; + // (2d2^2)^((p^6-9)/16) + let d2p = Fp6 { c0: Fp2 { c0: Fp::from_raw_unchecked([0,0,0,0,0,0]), c1: Fp::from_raw_unchecked([0,0,0,0,0,0])}, c1: Fp2 { c0: Fp::from_raw_unchecked([0,0,0,0,0,0]), c1: Fp::from_raw_unchecked([0,0,0,0,0,0])}, c2: Fp2 { c0: Fp::from_raw_unchecked([0,0,0,0,0,0]), c1: Fp::from_raw_unchecked([0xa1fafffffffe5557, 0x995bfff976a3fffe, 0x3f41d24d174ceb4, 0xf6547998c1995dbd, 0x778a468f507a6034, 0x20559931f7f8103])}}; - let d = Fp6::conditional_select(&-Fp6::one(), &v, is_one); - - // Q_9_16 = (modulus^6 - 9) / 16 + // Q_9_16 = (p^6 - 9) / 16 const Q_9_16: [u64; 36] = [ 0xec6c98463c0705d6, 0x43e289a0f3f4bf2d, @@ -320,18 +287,22 @@ impl Fp6 { 0x126e3a9ce60, ]; - let dd = d * d; - let ddx = dd * self; - - // TODO: use addition chains. - let z = (ddx + ddx).pow_vartime(&Q_9_16); - - let hi = ddx * z * z; - let i = hi + hi; - - let a = z * d * self * (i - Fp6::one()); - - CtOption::new(a, is_one | is_neg_one) + let xp = self.pow_vartime(&Q_9_16); // x^((p^6-9)/16) + let z1 = xp * d1p; + let z2 = xp * d2p; + let z1d1 = z1*d1; + let z2d2 = z2*d2; + let hi1 = z1d1*z1d1*self; + let hi2 = z2d2*z2d2*self; + let i1 = hi1 + hi1; + let i2 = hi2 + hi2; + let a1 = z1d1* self *(i1 - Fp6::one()); + let a2 = z2d2* self *(i2 - Fp6::one()); + let c1 = self.ct_eq(&(a1*a1)); + let c2 = self.ct_eq(&(a2*a2)); + + let a = Fp6::conditional_select(&a1, &a2, c2); + CtOption::new(a, c1 | c2) } #[inline] From 70b2d367ff06cc7ee91e3e23d9c011343c29df09 Mon Sep 17 00:00:00 2001 From: Bas Westerbaan Date: Fri, 6 Sep 2019 17:59:03 +0200 Subject: [PATCH 08/12] fp6::pow_vartime, use an 8-bit window Improves performance of Gt-compressed-lement deserialization by 30%. --- src/fp6.rs | 44 ++++++++++++++++++++++++++++++++++++++------ 1 file changed, 38 insertions(+), 6 deletions(-) diff --git a/src/fp6.rs b/src/fp6.rs index 09d64e92..e47c317f 100644 --- a/src/fp6.rs +++ b/src/fp6.rs @@ -330,15 +330,47 @@ impl Fp6 { /// variable time with respect to the exponent. It /// is also not exposed in the public API. pub fn pow_vartime(&self, by: &[u64]) -> Self { - let mut res = Self::one(); - for e in by.iter().rev() { - for i in (0..64).rev() { - res = res.square(); + // We use a 8-bit window. A 7-bit window would use the least + // (weighed) number of squares and multiplications, but the code + // would be a bit trickier. A smaller window (5- or 6-bit) might + // be even faster, as the lookup-table would fit in L1 cache. + + // Precompute lut[i] = x^i for i in {0, ..., 255} + let mut lut : [Fp6; 256] = [Default::default(); 256]; + lut[0] = Fp6::one(); + lut[1] = *self; + for i in 1..128 { + lut[2*i] = lut[i].square(); + lut[2*i + 1] = lut[2*i] * self; + } - if ((*e >> i) & 1) == 1 { - res *= self; + let mut res = Fp6::one(); + let mut first = true; + for j in (0..by.len()).rev() { + let e = by[j]; + if first { + first = false; + } else { + for _ in 0..8 { + res = res.square(); } } + + res *= lut[((e >> (7 * 8)) & 255u64) as usize]; + for _ in 0..8 { res = res.square(); } + res *= lut[((e >> (6 * 8)) & 255u64) as usize]; + for _ in 0..8 { res = res.square(); } + res *= lut[((e >> (5 * 8)) & 255u64) as usize]; + for _ in 0..8 { res = res.square(); } + res *= lut[((e >> (4 * 8)) & 255u64) as usize]; + for _ in 0..8 { res = res.square(); } + res *= lut[((e >> (3 * 8)) & 255u64) as usize]; + for _ in 0..8 { res = res.square(); } + res *= lut[((e >> (2 * 8)) & 255u64) as usize]; + for _ in 0..8 { res = res.square(); } + res *= lut[((e >> (1 * 8)) & 255u64) as usize]; + for _ in 0..8 { res = res.square(); } + res *= lut[(e & 255u64) as usize]; } res } From a9ae82874ff3f15e9937742c474e5283fb794cfb Mon Sep 17 00:00:00 2001 From: Wouter Geraedts Date: Tue, 10 Sep 2019 10:05:14 +0200 Subject: [PATCH 09/12] Ran formatting over fp6 --- src/fp6.rs | 61 ++++++++++++++++++++++++++++++++++++++++++++---------- 1 file changed, 50 insertions(+), 11 deletions(-) diff --git a/src/fp6.rs b/src/fp6.rs index e47c317f..0b4779c2 100644 --- a/src/fp6.rs +++ b/src/fp6.rs @@ -225,7 +225,6 @@ impl Fp6 { /// /// Uses the fact that p^6 = 9 mod 16. pub fn sqrt(&self) -> CtOption { - // In Müller's proposal one first computes s := (2x)^((p^6-1)/4). // If s is 1 or -1, then the x is a quadratic residue (ie. the square // exists.) Depending on the value of s, one choses a random d which @@ -243,9 +242,49 @@ impl Fp6 { }; // v, a quadratic non-residue // (2d1^2)^((p^6-9)/16) - let d1p = Fp6 { c0: Fp2 { c0: Fp::from_raw_unchecked([0x3e2f585da55c9ad1, 0x4294213d86c18183, 0x382844c88b623732, 0x92ad2afd19103e18, 0x1d794e4fac7cf0b9, 0xbd592fc7d825ec8]), c1: Fp::from_raw_unchecked([0,0,0,0,0,0])}, c1: Fp2 { c0: Fp::from_raw_unchecked([0,0,0,0,0,0]), c1: Fp::from_raw_unchecked([0,0,0,0,0,0])}, c2: Fp2 { c0: Fp::from_raw_unchecked([0,0,0,0,0,0]), c1: Fp::from_raw_unchecked([0,0,0,0,0,0])}}; + let d1p = Fp6 { + c0: Fp2 { + c0: Fp::from_raw_unchecked([ + 0x3e2f585da55c9ad1, + 0x4294213d86c18183, + 0x382844c88b623732, + 0x92ad2afd19103e18, + 0x1d794e4fac7cf0b9, + 0xbd592fc7d825ec8, + ]), + c1: Fp::from_raw_unchecked([0, 0, 0, 0, 0, 0]), + }, + c1: Fp2 { + c0: Fp::from_raw_unchecked([0, 0, 0, 0, 0, 0]), + c1: Fp::from_raw_unchecked([0, 0, 0, 0, 0, 0]), + }, + c2: Fp2 { + c0: Fp::from_raw_unchecked([0, 0, 0, 0, 0, 0]), + c1: Fp::from_raw_unchecked([0, 0, 0, 0, 0, 0]), + }, + }; // (2d2^2)^((p^6-9)/16) - let d2p = Fp6 { c0: Fp2 { c0: Fp::from_raw_unchecked([0,0,0,0,0,0]), c1: Fp::from_raw_unchecked([0,0,0,0,0,0])}, c1: Fp2 { c0: Fp::from_raw_unchecked([0,0,0,0,0,0]), c1: Fp::from_raw_unchecked([0,0,0,0,0,0])}, c2: Fp2 { c0: Fp::from_raw_unchecked([0,0,0,0,0,0]), c1: Fp::from_raw_unchecked([0xa1fafffffffe5557, 0x995bfff976a3fffe, 0x3f41d24d174ceb4, 0xf6547998c1995dbd, 0x778a468f507a6034, 0x20559931f7f8103])}}; + let d2p = Fp6 { + c0: Fp2 { + c0: Fp::from_raw_unchecked([0, 0, 0, 0, 0, 0]), + c1: Fp::from_raw_unchecked([0, 0, 0, 0, 0, 0]), + }, + c1: Fp2 { + c0: Fp::from_raw_unchecked([0, 0, 0, 0, 0, 0]), + c1: Fp::from_raw_unchecked([0, 0, 0, 0, 0, 0]), + }, + c2: Fp2 { + c0: Fp::from_raw_unchecked([0, 0, 0, 0, 0, 0]), + c1: Fp::from_raw_unchecked([ + 0xa1fafffffffe5557, + 0x995bfff976a3fffe, + 0x3f41d24d174ceb4, + 0xf6547998c1995dbd, + 0x778a468f507a6034, + 0x20559931f7f8103, + ]), + }, + }; // Q_9_16 = (p^6 - 9) / 16 const Q_9_16: [u64; 36] = [ @@ -290,16 +329,16 @@ impl Fp6 { let xp = self.pow_vartime(&Q_9_16); // x^((p^6-9)/16) let z1 = xp * d1p; let z2 = xp * d2p; - let z1d1 = z1*d1; - let z2d2 = z2*d2; - let hi1 = z1d1*z1d1*self; - let hi2 = z2d2*z2d2*self; + let z1d1 = z1 * d1; + let z2d2 = z2 * d2; + let hi1 = z1d1 * z1d1 * self; + let hi2 = z2d2 * z2d2 * self; let i1 = hi1 + hi1; let i2 = hi2 + hi2; - let a1 = z1d1* self *(i1 - Fp6::one()); - let a2 = z2d2* self *(i2 - Fp6::one()); - let c1 = self.ct_eq(&(a1*a1)); - let c2 = self.ct_eq(&(a2*a2)); + let a1 = z1d1 * self * (i1 - Fp6::one()); + let a2 = z2d2 * self * (i2 - Fp6::one()); + let c1 = self.ct_eq(&(a1 * a1)); + let c2 = self.ct_eq(&(a2 * a2)); let a = Fp6::conditional_select(&a1, &a2, c2); CtOption::new(a, c1 | c2) From 076405ef7c428d03592328d761b2c5869e58ac5b Mon Sep 17 00:00:00 2001 From: Wouter Geraedts Date: Thu, 14 Nov 2019 12:27:35 +0100 Subject: [PATCH 10/12] Fixed typo in comments --- src/pairings.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/pairings.rs b/src/pairings.rs index 293cce5c..2817e186 100644 --- a/src/pairings.rs +++ b/src/pairings.rs @@ -189,7 +189,7 @@ impl Gt { Self::from_compressed_unchecked(bytes).and_then(|p| CtOption::new(p, p.0.is_element())) } - /// Attempts to deserialize an compressed element, not checking if the + /// Attempts to deserialize a compressed element, not checking if the /// element is in the correct pairing group. /// **This is dangerous to call unless you trust the bytes you are reading; otherwise, /// API invariants may be broken.** Please consider using `from_uncompressed()` instead. From a3498f793072d798f4b66766863519c5b812649d Mon Sep 17 00:00:00 2001 From: Wouter Geraedts Date: Thu, 14 Nov 2019 12:27:42 +0100 Subject: [PATCH 11/12] Fixed lexicographically_largest --- src/fp6.rs | 11 +++-------- 1 file changed, 3 insertions(+), 8 deletions(-) diff --git a/src/fp6.rs b/src/fp6.rs index 0b4779c2..75b7db7c 100644 --- a/src/fp6.rs +++ b/src/fp6.rs @@ -146,14 +146,9 @@ impl Fp6 { /// larger than its negation. #[inline] pub fn lexicographically_largest(&self) -> Choice { - // If this element's c1 coefficient is lexicographically largest - // then it is lexicographically largest. Otherwise, in the event - // the c1 coefficient is zero and the c0 coefficient is - // lexicographically largest, then this element is lexicographically - // largest. - - self.c1.lexicographically_largest() - | (self.c1.is_zero() & self.c0.lexicographically_largest()) + self.c2.lexicographically_largest() + | (self.c2.is_zero() & self.c1.lexicographically_largest()) + | (self.c2.is_zero() & self.c1.is_zero() & self.c0.lexicographically_largest()) } /// Raises this element to p. From 76f26809413a067a7da36b6a53daa3a20d8b2817 Mon Sep 17 00:00:00 2001 From: Wouter Geraedts Date: Thu, 14 Nov 2019 13:00:54 +0100 Subject: [PATCH 12/12] Fixed from_bytes comments --- src/fp12.rs | 3 +-- src/fp2.rs | 3 +-- src/fp6.rs | 3 +-- 3 files changed, 3 insertions(+), 6 deletions(-) diff --git a/src/fp12.rs b/src/fp12.rs index d4b1f7fa..3a0a3f4e 100644 --- a/src/fp12.rs +++ b/src/fp12.rs @@ -197,8 +197,7 @@ impl Fp12 { res } - /// Attempts to convert a little-endian byte representation of - /// a scalar into an `Fp12`. + /// Attempts to convert a big-endian byte representation into an `Fp12`. /// /// Only fails when the underlying Fp elements are not canonical, /// but not when `Fp12` is not part of the subgroup. diff --git a/src/fp2.rs b/src/fp2.rs index 21456438..2020752e 100644 --- a/src/fp2.rs +++ b/src/fp2.rs @@ -335,8 +335,7 @@ impl Fp2 { res } - /// Attempts to convert a little-endian byte representation of - /// a scalar into an `Fp2`. + /// Attempts to convert a big-endian byte representation into an `Fp2`. /// /// Only fails when the underlying Fp elements are not canonical, /// but not when `Fp2` is not part of the subgroup. diff --git a/src/fp6.rs b/src/fp6.rs index 75b7db7c..561c2db9 100644 --- a/src/fp6.rs +++ b/src/fp6.rs @@ -409,8 +409,7 @@ impl Fp6 { res } - /// Attempts to convert a little-endian byte representation of - /// a scalar into an `Fp6`. + /// Attempts to convert a big-endian byte representation into an `Fp6`. /// /// Only fails when the underlying Fp elements are not canonical, /// but not when `Fp6` is not part of the subgroup.