diff --git a/Cargo.toml b/Cargo.toml index 721e59da..fe9a0613 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -18,6 +18,7 @@ repository = "https://github.com/ebfull/pairing" rand = "0.4" byteorder = "1" ff = { version = "0.4", features = ["derive"] } +blake2b_simd = "0.4.1" [features] unstable-features = ["expose-arith"] diff --git a/src/bls12_381/README.md b/src/bls12_381/README.md index d3811a14..a722907a 100644 --- a/src/bls12_381/README.md +++ b/src/bls12_381/README.md @@ -69,3 +69,17 @@ The most-significant three bits of a G1 or G2 encoding should be masked away bef * The second-most significant bit indicates that the point is at infinity. If this bit is set, the remaining bits of the group element's encoding should be set to zero. * The third-most significant bit is set if (and only if) this point is in compressed form _and_ it is not the point at infinity _and_ its y-coordinate is the lexicographically largest of the two associated with the encoded x-coordinate. +### Hashing to the Curve + +* Elements of Fq are encoded by taking the output of a BLAKE2b digest and interpreting it as a big-endian integer. The integer is reduced (mod q), making it uniform in the field with negligible bias. +* Elements of Fq2 are encoded by appending "_c0" to the supplied BLAKE2b preimage, to compute the encoding to Fq for c0, and by appending "_c1" to the same BLAKE2b preimage, to compute the encoding to Fq for c1. +* Elements of E and E' are encoded from an element *t* by taking the first valid abscissa (for the b in each respective curve): + * x_1 = (-1 + sqrt(-3))/2 - (sqrt(-3) * t^2)/(1 + b + t^2) + * x_2 = (-1 - sqrt(-3))/2 + (sqrt(-3) * t^2)/(1 + b + t^2) + * x_3 = 1 - (1 + b + t^2)^2 / (3 * t^2) +* In this encoding, we always map t=0 to the point at infinity. For the encoding to E, We also map: + * `t = 0x019cfaba0c258165d092f6bca9a081871e62a126c499340dc71c0e9527f923f3b299592a7a9503066cc5362484d96dd7` to the fixed generator. (See "Generators" above.) + * `t = 0x186417302d5a65347a88b0f999ab2b504614aa5e2eebdeb1a014c40bceb7d2306c12a6d436befcf94d39c9db7b263cd4` to the negative of the fixed generator. (See "Generators" above.) +* In this encoding, the y-coordinate of the resulting point is chosen to be the lexicographically largest if (and only if) the input `t` is also lexicographically larger than its negative. +* Hashing to G1, given a message `msg`, involves hashing to Fq by supplying the BLAKE2b preimage `msg | "G1_0"` to create `t0`, and hashing to Fq by supplying the BLAKE2b preimage `msg | "G1_1"` to create `t1`. `t0` and `t1` are used to encode into E via the above encoding, the resulting points are added together, and that result is multiplied by the cofactor. +* Hashing to G2, given a message `msg`, involves hashing to Fq2 by supplying the BLAKE2b preimage `msg | "G2_0"` to create `t0`, and hashing to Fq2 by supplying the BLAKE2b preimage `msg | "G2_1"` to create `t1`. `t0` and `t1` are used to encode into E' via the above encoding, the resulting points are added together, and that result is multiplied by the cofactor. diff --git a/src/bls12_381/ec.rs b/src/bls12_381/ec.rs index 37fcbba1..d3dea377 100644 --- a/src/bls12_381/ec.rs +++ b/src/bls12_381/ec.rs @@ -14,11 +14,10 @@ macro_rules! curve_impl { pub struct $affine { pub(crate) x: $basefield, pub(crate) y: $basefield, - pub(crate) infinity: bool + pub(crate) infinity: bool, } - impl ::std::fmt::Display for $affine - { + impl ::std::fmt::Display for $affine { fn fmt(&self, f: &mut ::std::fmt::Formatter) -> ::std::fmt::Result { if self.infinity { write!(f, "{}(Infinity)", $name) @@ -28,15 +27,22 @@ macro_rules! curve_impl { } } + fn y2_from_x(x: $basefield) -> $basefield { + let mut y2 = x.clone(); + y2.square(); + y2.mul_assign(&x); + y2.add_assign(&$affine::get_coeff_b()); + y2 + } + #[derive(Copy, Clone, Debug, Eq)] pub struct $projective { - pub(crate) x: $basefield, - pub(crate) y: $basefield, - pub(crate) z: $basefield + pub(crate) x: $basefield, + pub(crate) y: $basefield, + pub(crate) z: $basefield, } - impl ::std::fmt::Display for $projective - { + impl ::std::fmt::Display for $projective { fn fmt(&self, f: &mut ::std::fmt::Formatter) -> ::std::fmt::Result { write!(f, "{}", self.into_affine()) } @@ -47,11 +53,16 @@ macro_rules! curve_impl { if self.is_zero() { return other.is_zero(); } - if other.is_zero() { return false; } + if self.is_normalized() { + if other.is_normalized() { + return self.into_affine() == other.into_affine() + } + } + // The points (X, Y, Z) and (X', Y', Z') // are equal when (X * Z^2) = (X' * Z'^2) // and (Y * Z^3) = (Y' * Z'^3). @@ -89,7 +100,9 @@ macro_rules! curve_impl { let mut res = $projective::zero(); for i in bits { res.double(); - if i { res.add_assign_mixed(self) } + if i { + res.add_assign_mixed(self) + } } res } @@ -112,12 +125,8 @@ macro_rules! curve_impl { $affine { x: x, - y: if (y < negy) ^ greatest { - y - } else { - negy - }, - infinity: false + y: if (y < negy) ^ greatest { y } else { negy }, + infinity: false, } }) } @@ -130,18 +139,103 @@ macro_rules! curve_impl { let mut y2 = self.y; y2.square(); - let mut x3b = self.x; - x3b.square(); - x3b.mul_assign(&self.x); - x3b.add_assign(&Self::get_coeff_b()); - - y2 == x3b + y2 == y2_from_x(self.x) } } fn is_in_correct_subgroup_assuming_on_curve(&self) -> bool { self.mul($scalarfield::char()).is_zero() } + + /// Implements the Shallue–van de Woestijne encoding described in + /// Section 3, "Indifferentiable Hashing to Barreto–Naehrig Curves" + /// from Foque-Tibouchi: . + /// + /// The encoding is adapted for BLS12-381. + /// + /// This encoding produces a point in E/E'. It does not reach every + /// point. The resulting point may not be in the prime order subgroup, + /// but it will be on the curve. It could be the point at infinity. + /// + /// ## Description + /// + /// Lemma 3 gives us three points: + /// + /// x_1 = (-1 + sqrt(-3))/2 - (sqrt(-3) * t^2)/(1 + b + t^2) + /// x_2 = (-1 - sqrt(-3))/2 + (sqrt(-3) * t^2)/(1 + b + t^2) + /// x_3 = 1 - (1 + b + t^2)^2/(3 * t^2) + /// + /// Given t != 0 and t != 1 + b + t^2 != 0, at least one of + /// these three points (x1, x2, x3) is valid on the curve. + /// + /// In the paper, 1 + b + t^2 != 0 has no solutions, but for + /// E(Fq) in our construction, it does have two solutions. + /// We follow the convention of the paper by mapping these + /// to some arbitrary points; in our case, the positive/negative + /// fixed generator (with the parity of the y-coordinate + /// corresponding to the t value). + /// + /// Unlike the paper, which maps t = 0 to an arbitrary point, + /// we map it to the point at infinity. This arrangement allows + /// us to preserve sw_encode(t) = sw_encode(-t) for all t. + /// + /// We choose the smallest i such that x_i is on the curve. + /// We choose the corresponding y-coordinate with the same + /// parity, defined as the point being lexicographically larger + /// than its negative. + fn sw_encode(t: $basefield) -> Self { + // Handle the case t == 0 + if t.is_zero() { + return Self::zero(); + } + + // We choose the corresponding y-coordinate with the same parity as t. + let parity = t.parity(); + + // w = (t^2 + b + 1)^(-1) * sqrt(-3) * t + let mut w = t; + w.square(); + w.add_assign(&$affine::get_coeff_b()); + w.add_assign(&$basefield::one()); + + // Handle the case t^2 + b + 1 == 0 + if w.is_zero() { + let mut ret = Self::one(); + if parity { + ret.negate() + } + return ret; + } + + w = w.inverse().unwrap(); + w.mul_assign(&$basefield::get_swenc_sqrt_neg_three()); + w.mul_assign(&t); + + // x1 = - wt + (sqrt(-3) - 1) / 2 + let mut x1 = w; + x1.mul_assign(&t); + x1.negate(); + x1.add_assign(&$basefield::get_swenc_sqrt_neg_three_minus_one_div_two()); + if let Some(p) = Self::get_point_from_x(x1, parity) { + return p; + } + + // x2 = -1 - x1 + let mut x2 = x1; + x2.negate(); + x2.sub_assign(&$basefield::one()); + if let Some(p) = Self::get_point_from_x(x2, parity) { + return p; + } + + // x3 = 1/w^2 + 1 + let mut x3 = w; + x3.square(); + x3 = x3.inverse().unwrap(); + x3.add_assign(&$basefield::one()); + Self::get_point_from_x(x3, parity) + .expect("this point must be valid if the other two are not") + } } impl CurveAffine for $affine { @@ -159,7 +253,7 @@ macro_rules! curve_impl { $affine { x: $basefield::zero(), y: $basefield::one(), - infinity: true + infinity: true, } } @@ -193,7 +287,6 @@ macro_rules! curve_impl { fn into_projective(&self) -> $projective { (*self).into() } - } impl Rand for $projective { @@ -225,7 +318,7 @@ macro_rules! curve_impl { $projective { x: $basefield::zero(), y: $basefield::one(), - z: $basefield::zero() + z: $basefield::zero(), } } @@ -243,7 +336,7 @@ macro_rules! curve_impl { self.is_zero() || self.z == $basefield::one() } - fn batch_normalization(v: &mut [Self]) + fn batch_normalization>(v: &mut [S]) { // Montgomery’s Trick and Fast Implementation of Masked AES // Genelle, Prouff and Quisquater @@ -253,8 +346,9 @@ macro_rules! curve_impl { let mut prod = Vec::with_capacity(v.len()); let mut tmp = $basefield::one(); for g in v.iter_mut() - // Ignore normalized elements - .filter(|g| !g.is_normalized()) + .map(|g| g.borrow_mut()) + // Ignore normalized elements + .filter(|g| !g.is_normalized()) { tmp.mul_assign(&g.z); prod.push(tmp); @@ -265,12 +359,16 @@ macro_rules! curve_impl { // Second pass: iterate backwards to compute inverses for (g, s) in v.iter_mut() - // Backwards - .rev() - // Ignore normalized elements - .filter(|g| !g.is_normalized()) - // Backwards, skip last element, fill in one for last term. - .zip(prod.into_iter().rev().skip(1).chain(Some($basefield::one()))) + .map(|g| g.borrow_mut()) + // Backwards + .rev() + // Ignore normalized elements + .filter(|g| !g.is_normalized()) + // Backwards, skip last element, fill in one for last term. + .zip( + prod.into_iter().rev().skip(1) + .chain(Some($basefield::one())), + ) { // tmp := tmp * g.z; g.z := tmp * s = 1/z let mut newtmp = tmp; @@ -282,7 +380,8 @@ macro_rules! curve_impl { // Perform affine transformations for g in v.iter_mut() - .filter(|g| !g.is_normalized()) + .map(|g| g.borrow_mut()) + .filter(|g| !g.is_normalized()) { let mut z = g.z; // 1/z z.square(); // 1/z^2 @@ -536,8 +635,7 @@ macro_rules! curve_impl { let mut found_one = false; - for i in BitIterator::new(other.into()) - { + for i in BitIterator::new(other.into()) { if found_one { res.double(); } else { @@ -563,6 +661,33 @@ macro_rules! curve_impl { fn recommended_wnaf_for_num_scalars(num_scalars: usize) -> usize { Self::empirical_recommended_wnaf_for_num_scalars(num_scalars) } + + /// Implements "Indifferentiable Hashing to Barreto–Naehrig Curves" from Foque-Tibouchi. + /// + fn hash(msg: &[u8]) -> Self { + // The construction of Foque et al. requires us to construct two + // "random oracles" in the field, encode their image with `sw_encode`, + // and finally add them. + // We construct them appending to the message the string + // $name_$oracle + // For instance, the first oracle in group G1 appends: "G1_0". + let mut hasher_0 = blake2b_simd::State::new(); + hasher_0.update(msg); + hasher_0.update($name.as_bytes()); + let mut hasher_1 = hasher_0.clone(); + + hasher_0.update(b"_0"); + let t0 = Self::Base::hash(hasher_0); + let t0 = Self::Affine::sw_encode(t0); + + hasher_1.update(b"_1"); + let t1 = Self::Base::hash(hasher_1); + let t1 = Self::Affine::sw_encode(t1); + + let mut res = t0.into_projective(); + res.add_assign_mixed(&t1); + res.into_affine().scale_by_cofactor() + } } // The affine point X, Y is represented in the jacobian @@ -575,7 +700,7 @@ macro_rules! curve_impl { $projective { x: p.x, y: p.y, - z: $basefield::one() + z: $basefield::one(), } } } @@ -592,7 +717,7 @@ macro_rules! curve_impl { $affine { x: p.x, y: p.y, - infinity: false + infinity: false, } } else { // Z is nonzero, so it must have an inverse in a field. @@ -612,14 +737,85 @@ macro_rules! curve_impl { $affine { x: x, y: y, - infinity: false + infinity: false, } } } } - } + #[cfg(test)] + use rand::{SeedableRng, XorShiftRng}; + + #[test] + fn test_hash() { + let mut rng = XorShiftRng::from_seed([0x5dbe6259, 0x8d313d76, 0x3237db17, 0xe5bc0654]); + + for _ in 0..100 { + let seed: [u8; 32] = rng.gen(); + let p = $projective::hash(&seed).into_affine(); + assert!(!p.is_zero()); + assert!(p.is_on_curve()); + assert!(p.is_in_correct_subgroup_assuming_on_curve()); + } + } + + #[test] + fn test_sw_encode() { + let mut rng = XorShiftRng::from_seed([0x5dbe6259, 0x8d313d76, 0x3237db17, 0xe5bc0654]); + + for _ in 0..100 { + let mut t = $basefield::rand(&mut rng); + let p = $affine::sw_encode(t); + assert!(p.is_on_curve()); + assert!(!p.is_zero()); + + t.negate(); + let mut minus_p = $affine::sw_encode(t).into_projective(); + minus_p.add_assign_mixed(&p); + assert!(minus_p.is_zero()); + } + } + }; } +macro_rules! encoded_point_delegations { + ($t:ident) => { + + impl AsRef<[u8]> for $t { + fn as_ref(&self) -> &[u8] { + &self.0 + } + } + impl AsMut<[u8]> for $t { + fn as_mut(&mut self) -> &mut [u8] { + &mut self.0 + } + } + + impl PartialEq for $t { + fn eq(&self, other: &$t) -> bool { + PartialEq::eq(&self.0[..], &other.0[..]) + } + } + impl Eq for $t { } + impl PartialOrd for $t { + fn partial_cmp(&self, other: &$t) -> Option<::std::cmp::Ordering> { + PartialOrd::partial_cmp(&self.0[..], &other.0[..]) + } + } + impl Ord for $t { + fn cmp(&self, other: &Self) -> ::std::cmp::Ordering { + Ord::cmp(&self.0[..], &other.0[..]) + } + } + + impl ::std::hash::Hash for $t { + fn hash(&self, state: &mut H) { + self.0[..].hash(state); + } + } + } +} // encoded_point_delegations + pub mod g1 { use super::super::{Bls12, Fq, Fq12, FqRepr, Fr, FrRepr}; use super::g2::G2Affine; @@ -643,17 +839,7 @@ pub mod g1 { #[derive(Copy, Clone)] pub struct G1Uncompressed([u8; 96]); - impl AsRef<[u8]> for G1Uncompressed { - fn as_ref(&self) -> &[u8] { - &self.0 - } - } - - impl AsMut<[u8]> for G1Uncompressed { - fn as_mut(&mut self) -> &mut [u8] { - &mut self.0 - } - } + encoded_point_delegations!(G1Uncompressed); impl fmt::Debug for G1Uncompressed { fn fmt(&self, formatter: &mut fmt::Formatter) -> Result<(), fmt::Error> { @@ -753,17 +939,7 @@ pub mod g1 { #[derive(Copy, Clone)] pub struct G1Compressed([u8; 48]); - impl AsRef<[u8]> for G1Compressed { - fn as_ref(&self) -> &[u8] { - &self.0 - } - } - - impl AsMut<[u8]> for G1Compressed { - fn as_mut(&mut self) -> &mut [u8] { - &mut self.0 - } - } + encoded_point_delegations!(G1Compressed); impl fmt::Debug for G1Compressed { fn fmt(&self, formatter: &mut fmt::Formatter) -> Result<(), fmt::Error> { @@ -987,7 +1163,8 @@ pub mod g1 { 0x9fe83b1b4a5d648d, 0xf583cc5a508f6a40, 0xc3ad2aefde0bb13, - ])).unwrap(), + ])) + .unwrap(), y: Fq::from_repr(FqRepr([ 0x60aa6f9552f03aae, 0xecd01d5181300d35, @@ -995,7 +1172,8 @@ pub mod g1 { 0xe760f57922998c9d, 0x953703f5795a39e5, 0xfe3ae0922df702c, - ])).unwrap(), + ])) + .unwrap(), infinity: false, }; assert!(!p.is_on_curve()); @@ -1012,7 +1190,8 @@ pub mod g1 { 0xea034ee2928b30a8, 0xbd8833dc7c79a7f7, 0xe45c9f0c0438675, - ])).unwrap(), + ])) + .unwrap(), y: Fq::from_repr(FqRepr([ 0x3b450eb1ab7b5dad, 0xa65cb81e975e8675, @@ -1020,7 +1199,8 @@ pub mod g1 { 0x753ddf21a2601d20, 0x532d0b640bd3ff8b, 0x118d2c543f031102, - ])).unwrap(), + ])) + .unwrap(), infinity: false, }; assert!(!p.is_on_curve()); @@ -1038,7 +1218,8 @@ pub mod g1 { 0xf35de9ce0d6b4e84, 0x265bddd23d1dec54, 0x12a8778088458308, - ])).unwrap(), + ])) + .unwrap(), y: Fq::from_repr(FqRepr([ 0x8a22defa0d526256, 0xc57ca55456fcb9ae, @@ -1046,7 +1227,8 @@ pub mod g1 { 0x921beef89d4f29df, 0x5b6fda44ad85fa78, 0xed74ab9f302cbe0, - ])).unwrap(), + ])) + .unwrap(), infinity: false, }; assert!(p.is_on_curve()); @@ -1064,7 +1246,8 @@ pub mod g1 { 0x485e77d50a5df10d, 0x4c6fcac4b55fd479, 0x86ed4d9906fb064, - ])).unwrap(), + ])) + .unwrap(), y: Fq::from_repr(FqRepr([ 0xd25ee6461538c65, 0x9f3bbb2ecd3719b9, @@ -1072,7 +1255,8 @@ pub mod g1 { 0xcefca68333c35288, 0x570c8005f8573fa6, 0x152ca696fe034442, - ])).unwrap(), + ])) + .unwrap(), z: Fq::one(), }; @@ -1084,7 +1268,8 @@ pub mod g1 { 0x5f44314ec5e3fb03, 0x24e8538737c6e675, 0x8abd623a594fba8, - ])).unwrap(), + ])) + .unwrap(), y: Fq::from_repr(FqRepr([ 0x6b0528f088bb7044, 0x2fdeb5c82917ff9e, @@ -1092,7 +1277,8 @@ pub mod g1 { 0xd65104c6f95a872a, 0x1f2998a5a9c61253, 0xe74846154a9e44, - ])).unwrap(), + ])) + .unwrap(), z: Fq::one(), }); @@ -1108,7 +1294,8 @@ pub mod g1 { 0xc4f9a52a428e23bb, 0xd178b28dd4f407ef, 0x17fb8905e9183c69 - ])).unwrap(), + ])) + .unwrap(), y: Fq::from_repr(FqRepr([ 0xd0de9d65292b7710, 0xf6a05f2bcf1d9ca7, @@ -1116,7 +1303,8 @@ pub mod g1 { 0xeec8d1a5b7466c58, 0x4bc362649dce6376, 0x430cbdc5455b00a - ])).unwrap(), + ])) + .unwrap(), infinity: false, } ); @@ -1132,7 +1320,8 @@ pub mod g1 { 0x485e77d50a5df10d, 0x4c6fcac4b55fd479, 0x86ed4d9906fb064, - ])).unwrap(), + ])) + .unwrap(), y: Fq::from_repr(FqRepr([ 0xd25ee6461538c65, 0x9f3bbb2ecd3719b9, @@ -1140,7 +1329,8 @@ pub mod g1 { 0xcefca68333c35288, 0x570c8005f8573fa6, 0x152ca696fe034442, - ])).unwrap(), + ])) + .unwrap(), z: Fq::one(), }; @@ -1158,7 +1348,8 @@ pub mod g1 { 0x4b914c16687dcde0, 0x66c8baf177d20533, 0xaf960cff3d83833 - ])).unwrap(), + ])) + .unwrap(), y: Fq::from_repr(FqRepr([ 0x3f0675695f5177a8, 0x2b6d82ae178a1ba0, @@ -1166,7 +1357,8 @@ pub mod g1 { 0x1771a65b60572f4e, 0x8b547c1313b27555, 0x135075589a687b1e - ])).unwrap(), + ])) + .unwrap(), infinity: false, } ); @@ -1189,7 +1381,8 @@ pub mod g1 { 0x71ffa8021531705, 0x7418d484386d267, 0xd5108d8ff1fbd6, - ])).unwrap(), + ])) + .unwrap(), y: Fq::from_repr(FqRepr([ 0xa776ccbfe9981766, 0x255632964ff40f4a, @@ -1197,7 +1390,8 @@ pub mod g1 { 0x520f74773e74c8c3, 0x484c8fc982008f0, 0xee2c3d922008cc6, - ])).unwrap(), + ])) + .unwrap(), infinity: false, }; @@ -1209,7 +1403,8 @@ pub mod g1 { 0xc6e05201e5f83991, 0xf7c75910816f207c, 0x18d4043e78103106, - ])).unwrap(), + ])) + .unwrap(), y: Fq::from_repr(FqRepr([ 0xa776ccbfe9981766, 0x255632964ff40f4a, @@ -1217,7 +1412,8 @@ pub mod g1 { 0x520f74773e74c8c3, 0x484c8fc982008f0, 0xee2c3d922008cc6, - ])).unwrap(), + ])) + .unwrap(), infinity: false, }; @@ -1232,7 +1428,8 @@ pub mod g1 { 0x9676ff02ec39c227, 0x4c12c15d7e55b9f3, 0x57fd1e317db9bd, - ])).unwrap(), + ])) + .unwrap(), y: Fq::from_repr(FqRepr([ 0x1288334016679345, 0xf955cd68615ff0b5, @@ -1240,7 +1437,8 @@ pub mod g1 { 0x1267d70db51049fb, 0x4696deb9ab2ba3e7, 0xb1e4e11177f59d4, - ])).unwrap(), + ])) + .unwrap(), infinity: false, }; @@ -1259,6 +1457,56 @@ pub mod g1 { assert_eq!(tmp2, c.into_projective()); } + #[test] + fn test_g1_sw_encode_degenerate() { + // test the degenerate case t = 0 + let p = G1Affine::sw_encode(Fq::zero()); + assert!(p.is_on_curve()); + assert!(p.is_zero()); + + // test the degenerate case t^2 = - b - 1 + let mut t = Fq::one(); + t.add_assign(&G1Affine::get_coeff_b()); + t.negate(); + let mut t = t.sqrt().unwrap(); + t.negate(); // If sqrt impl changes, this test will be affected + let p = G1Affine::sw_encode(t); + assert!(p.is_on_curve()); + assert!(!p.is_zero()); + assert_eq!(p.y.parity(), t.parity()); + assert_eq!(p, G1Affine::one()); + t.negate(); + let p = G1Affine::sw_encode(t); + assert!(p.is_on_curve()); + assert!(!p.is_zero()); + assert_eq!(p.y.parity(), t.parity()); + { + let mut negone = G1Affine::one(); + negone.negate(); + assert_eq!(p, negone); + } + + // test that the encoding function is odd for the above t + t.negate(); + let mut minus_p = G1Affine::sw_encode(t).into_projective(); + minus_p.add_assign_mixed(&p); + assert!(minus_p.is_zero()); + } + + #[test] + fn g1_hash_test_vectors() { + // Obtained via python/sage + + let p = G1::hash(&[]); + let q = G1 { + x: Fq::from_str("315124130825307604287835216317628428134609737854237653839182597515996444073032649481416725367158979153513345579672").unwrap(), + y: Fq::from_str("3093537746211397858160667262592024570071165158580434464756577567510401504168962073691924150397172185836012224315174").unwrap(), + z: Fq::one() + }; + + assert_eq!(p, q); + } + #[test] fn g1_curve_tests() { ::tests::curve::curve_tests::(); @@ -1288,17 +1536,7 @@ pub mod g2 { #[derive(Copy, Clone)] pub struct G2Uncompressed([u8; 192]); - impl AsRef<[u8]> for G2Uncompressed { - fn as_ref(&self) -> &[u8] { - &self.0 - } - } - - impl AsMut<[u8]> for G2Uncompressed { - fn as_mut(&mut self) -> &mut [u8] { - &mut self.0 - } - } + encoded_point_delegations!(G2Uncompressed); impl fmt::Debug for G2Uncompressed { fn fmt(&self, formatter: &mut fmt::Formatter) -> Result<(), fmt::Error> { @@ -1414,17 +1652,7 @@ pub mod g2 { #[derive(Copy, Clone)] pub struct G2Compressed([u8; 96]); - impl AsRef<[u8]> for G2Compressed { - fn as_ref(&self) -> &[u8] { - &self.0 - } - } - - impl AsMut<[u8]> for G2Compressed { - fn as_mut(&mut self) -> &mut [u8] { - &mut self.0 - } - } + encoded_point_delegations!(G2Compressed); impl fmt::Debug for G2Compressed { fn fmt(&self, formatter: &mut fmt::Formatter) -> Result<(), fmt::Error> { @@ -1668,7 +1896,8 @@ pub mod g2 { 0x7a17a004747e3dbe, 0xcc65406a7c2e5a73, 0x10b8c03d64db4d0c, - ])).unwrap(), + ])) + .unwrap(), c1: Fq::from_repr(FqRepr([ 0xd30e70fe2f029778, 0xda30772df0f5212e, @@ -1676,7 +1905,8 @@ pub mod g2 { 0xfb777e5b9b568608, 0x789bac1fec71a2b9, 0x1342f02e2da54405, - ])).unwrap(), + ])) + .unwrap(), }, y: Fq2 { c0: Fq::from_repr(FqRepr([ @@ -1686,7 +1916,8 @@ pub mod g2 { 0x663015d9410eb608, 0x78e82a79d829a544, 0x40a00545bb3c1e, - ])).unwrap(), + ])) + .unwrap(), c1: Fq::from_repr(FqRepr([ 0x4709802348e79377, 0xb5ac4dc9204bcfbd, @@ -1694,7 +1925,8 @@ pub mod g2 { 0x15008b1dc399e8df, 0x68128fd0548a3829, 0x16a613db5c873aaa, - ])).unwrap(), + ])) + .unwrap(), }, infinity: false, }; @@ -1713,7 +1945,8 @@ pub mod g2 { 0x41abba710d6c692c, 0xffcc4b2b62ce8484, 0x6993ec01b8934ed, - ])).unwrap(), + ])) + .unwrap(), c1: Fq::from_repr(FqRepr([ 0xb94e92d5f874e26, 0x44516408bc115d95, @@ -1721,7 +1954,8 @@ pub mod g2 { 0xa5a0c2b7131f3555, 0x83800965822367e7, 0x10cf1d3ad8d90bfa, - ])).unwrap(), + ])) + .unwrap(), }, y: Fq2 { c0: Fq::from_repr(FqRepr([ @@ -1731,7 +1965,8 @@ pub mod g2 { 0x5a9171720e73eb51, 0x38eb4fd8d658adb7, 0xb649051bbc1164d, - ])).unwrap(), + ])) + .unwrap(), c1: Fq::from_repr(FqRepr([ 0x9225814253d7df75, 0xc196c2513477f887, @@ -1739,7 +1974,8 @@ pub mod g2 { 0x55f2b8efad953e04, 0x7379345eda55265e, 0x377f2e6208fd4cb, - ])).unwrap(), + ])) + .unwrap(), }, infinity: false, }; @@ -1759,7 +1995,8 @@ pub mod g2 { 0x2199bc19c48c393d, 0x4a151b732a6075bf, 0x17762a3b9108c4a7, - ])).unwrap(), + ])) + .unwrap(), c1: Fq::from_repr(FqRepr([ 0x26f461e944bbd3d1, 0x298f3189a9cf6ed6, @@ -1767,7 +2004,8 @@ pub mod g2 { 0x7e147f3f9e6e241, 0x72a9b63583963fff, 0x158b0083c000462, - ])).unwrap(), + ])) + .unwrap(), }, y: Fq2 { c0: Fq::from_repr(FqRepr([ @@ -1777,7 +2015,8 @@ pub mod g2 { 0x68cad19430706b4d, 0x3ccfb97b924dcea8, 0x1660f93434588f8d, - ])).unwrap(), + ])) + .unwrap(), c1: Fq::from_repr(FqRepr([ 0xaaed3985b6dcb9c7, 0xc1e985d6d898d9f4, @@ -1785,7 +2024,8 @@ pub mod g2 { 0x3940a2dbb914b529, 0xbeb88137cf34f3e7, 0x1699ee577c61b694, - ])).unwrap(), + ])) + .unwrap(), }, infinity: false, }; @@ -1805,7 +2045,8 @@ pub mod g2 { 0x72556c999f3707ac, 0x4617f2e6774e9711, 0x100b2fe5bffe030b, - ])).unwrap(), + ])) + .unwrap(), c1: Fq::from_repr(FqRepr([ 0x7a33555977ec608, 0xe23039d1fe9c0881, @@ -1813,7 +2054,8 @@ pub mod g2 { 0x4637c4f417667e2e, 0x93ebe7c3e41f6acc, 0xde884f89a9a371b, - ])).unwrap(), + ])) + .unwrap(), }, y: Fq2 { c0: Fq::from_repr(FqRepr([ @@ -1823,7 +2065,8 @@ pub mod g2 { 0x25fd427b4122f231, 0xd83112aace35cae, 0x191b2432407cbb7f, - ])).unwrap(), + ])) + .unwrap(), c1: Fq::from_repr(FqRepr([ 0xf68ae82fe97662f5, 0xe986057068b50b7d, @@ -1831,7 +2074,8 @@ pub mod g2 { 0x9eaa6d19de569196, 0xf6a03d31e2ec2183, 0x3bdafaf7ca9b39b, - ])).unwrap(), + ])) + .unwrap(), }, z: Fq2::one(), }; @@ -1845,7 +2089,8 @@ pub mod g2 { 0x8e73a96b329ad190, 0x27c546f75ee1f3ab, 0xa33d27add5e7e82, - ])).unwrap(), + ])) + .unwrap(), c1: Fq::from_repr(FqRepr([ 0x93b1ebcd54870dfe, 0xf1578300e1342e11, @@ -1853,7 +2098,8 @@ pub mod g2 { 0x2089faf462438296, 0x828e5848cd48ea66, 0x141ecbac1deb038b, - ])).unwrap(), + ])) + .unwrap(), }, y: Fq2 { c0: Fq::from_repr(FqRepr([ @@ -1863,7 +2109,8 @@ pub mod g2 { 0x2767032fc37cc31d, 0xd5ee2aba84fd10fe, 0x16576ccd3dd0a4e8, - ])).unwrap(), + ])) + .unwrap(), c1: Fq::from_repr(FqRepr([ 0x4da9b6f6a96d1dd2, 0x9657f7da77f1650e, @@ -1871,7 +2118,8 @@ pub mod g2 { 0x31898db63f87363a, 0xabab040ddbd097cc, 0x11ad236b9ba02990, - ])).unwrap(), + ])) + .unwrap(), }, z: Fq2::one(), }); @@ -1889,7 +2137,8 @@ pub mod g2 { 0xf1273e6406eef9cc, 0xababd760ff05cb92, 0xd7c20456617e89 - ])).unwrap(), + ])) + .unwrap(), c1: Fq::from_repr(FqRepr([ 0xd1a50b8572cbd2b8, 0x238f0ac6119d07df, @@ -1897,7 +2146,8 @@ pub mod g2 { 0x8b203284c51edf6b, 0xc8a0b730bbb21f5e, 0x1a3b59d29a31274 - ])).unwrap(), + ])) + .unwrap(), }, y: Fq2 { c0: Fq::from_repr(FqRepr([ @@ -1907,7 +2157,8 @@ pub mod g2 { 0x64528ab3863633dc, 0x159384333d7cba97, 0x4cb84741f3cafe8 - ])).unwrap(), + ])) + .unwrap(), c1: Fq::from_repr(FqRepr([ 0x242af0dc3640e1a4, 0xe90a73ad65c66919, @@ -1915,7 +2166,8 @@ pub mod g2 { 0x38528f92b689644d, 0xb6884deec59fb21f, 0x3c075d3ec52ba90 - ])).unwrap(), + ])) + .unwrap(), }, infinity: false, } @@ -1933,7 +2185,8 @@ pub mod g2 { 0x72556c999f3707ac, 0x4617f2e6774e9711, 0x100b2fe5bffe030b, - ])).unwrap(), + ])) + .unwrap(), c1: Fq::from_repr(FqRepr([ 0x7a33555977ec608, 0xe23039d1fe9c0881, @@ -1941,7 +2194,8 @@ pub mod g2 { 0x4637c4f417667e2e, 0x93ebe7c3e41f6acc, 0xde884f89a9a371b, - ])).unwrap(), + ])) + .unwrap(), }, y: Fq2 { c0: Fq::from_repr(FqRepr([ @@ -1951,7 +2205,8 @@ pub mod g2 { 0x25fd427b4122f231, 0xd83112aace35cae, 0x191b2432407cbb7f, - ])).unwrap(), + ])) + .unwrap(), c1: Fq::from_repr(FqRepr([ 0xf68ae82fe97662f5, 0xe986057068b50b7d, @@ -1959,7 +2214,8 @@ pub mod g2 { 0x9eaa6d19de569196, 0xf6a03d31e2ec2183, 0x3bdafaf7ca9b39b, - ])).unwrap(), + ])) + .unwrap(), }, z: Fq2::one(), }; @@ -1979,7 +2235,8 @@ pub mod g2 { 0xbcedcfce1e52d986, 0x9755d4a3926e9862, 0x18bab73760fd8024 - ])).unwrap(), + ])) + .unwrap(), c1: Fq::from_repr(FqRepr([ 0x4e7c5e0a2ae5b99e, 0x96e582a27f028961, @@ -1987,7 +2244,8 @@ pub mod g2 { 0xeb0cf5e610ef4fe7, 0x7b4c2bae8db6e70b, 0xf136e43909fca0 - ])).unwrap(), + ])) + .unwrap(), }, y: Fq2 { c0: Fq::from_repr(FqRepr([ @@ -1997,7 +2255,8 @@ pub mod g2 { 0xa5a2a51f7fde787b, 0x8b92866bc6384188, 0x81a53fe531d64ef - ])).unwrap(), + ])) + .unwrap(), c1: Fq::from_repr(FqRepr([ 0x4c5d607666239b34, 0xeddb5f48304d14b3, @@ -2005,13 +2264,47 @@ pub mod g2 { 0xb271f52f12ead742, 0x244e6c2015c83348, 0x19e2deae6eb9b441 - ])).unwrap(), + ])) + .unwrap(), }, infinity: false, } ); } + #[test] + fn test_g2_sw_encode_degenerate() { + // test the degenerate cases t = 0 and t^2 = - b - 1 + let p = G2Affine::sw_encode(Fq2::zero()); + assert!(p.is_on_curve()); + assert!(p.is_zero()); + + let mut t = Fq2::one(); + t.add_assign(&G2Affine::get_coeff_b()); + t.negate(); + assert_eq!(t.sqrt(), None); + } + + #[test] + fn g2_hash_test_vectors() { + // Obtained via python/sage + + let p = G2::hash(&[]); + let q = G2 { + x: Fq2 { + c0: Fq::from_str("1703269368484048424021410903959703695180015303406562561298910892586704964724393392000690938204229678426081532099421").unwrap(), + c1: Fq::from_str("1899273078921065702469032215023284089292737398509481436818508674759333584516218669155175722702009534138251936259418").unwrap(), + }, + y: Fq2 { + c0: Fq::from_str("1983733072556618192444995460520049530986901623449598282145749270559646083332830971089171683246431283765594628842386").unwrap(), + c1: Fq::from_str("915456324395362816875268588526293724551529076411493014293832389675785871078275824878933205442411635336958461433442").unwrap(), + }, + z: Fq2::one() + }; + + assert_eq!(p, q); + } + #[test] fn g2_curve_tests() { ::tests::curve::curve_tests::(); diff --git a/src/bls12_381/fq.rs b/src/bls12_381/fq.rs index fd0d416d..2d84c4bc 100644 --- a/src/bls12_381/fq.rs +++ b/src/bls12_381/fq.rs @@ -1,5 +1,8 @@ use super::fq2::Fq2; -use ff::{Field, PrimeField, PrimeFieldDecodingError, PrimeFieldRepr}; +use ff::{BitIterator, Field, PrimeField, PrimeFieldDecodingError, PrimeFieldRepr}; + +use blake2b_simd::State as Blake2b; +use byteorder::{BigEndian, ByteOrder}; // B coefficient of BLS12-381 curve, 4. pub const B_COEFF: Fq = Fq(FqRepr([ @@ -11,6 +14,30 @@ pub const B_COEFF: Fq = Fq(FqRepr([ 0x9d645513d83de7e, ])); +// SWENC_SQRT_NEG_THREE = sqrt(-3) mod q = +// 1586958781458431025242759403266842894121773480562120986020912974854563298150952611241517463240701 +// used to help find a Fq-rational point in the conic described by the Shallue–van de Woestijne encoding. +const SWENC_SQRT_NEG_THREE: Fq = Fq(FqRepr([ + 0x1dec6c36f3181f22, + 0xb4b9bb641054b457, + 0x25695a2be9415286, + 0x982b6cbf66c749bc, + 0x7d58e1ae1feb7873, + 0x62c96300937c0b9, +])); + +// SWENC_SQRT_NEG_THREE_MINUS_ONE_DIV_TWO = (sqrt(-3) - 1) / 2 mod q = +// 793479390729215512621379701633421447060886740281060493010456487427281649075476305620758731620350 +// used to speed up the computation of the abscissa x_1(t). +const SWENC_SQRT_NEG_THREE_MINUS_ONE_DIV_TWO: Fq = Fq(FqRepr([ + 0x30f1361b798a64e8, + 0xf3b8ddab7ece5a2a, + 0x16a8ca3ac61577f7, + 0xc26a2ff874fd029b, + 0x3636b76660701c6e, + 0x51ba4ab241b6160, +])); + // The generators of G1/G2 are computed by finding the lexicographically smallest valid x coordinate, // and its lexicographically smallest y coordinate and multiplying it by the cofactor such that the // result is nonzero. @@ -448,11 +475,103 @@ pub const NEGATIVE_ONE: Fq = Fq(FqRepr([ #[PrimeFieldGenerator = "2"] pub struct Fq(FqRepr); +impl Fq { + pub(crate) fn parity(&self) -> bool { + let mut neg = *self; + neg.negate(); + *self > neg + } + + pub(crate) fn get_swenc_sqrt_neg_three() -> Fq { + SWENC_SQRT_NEG_THREE + } + + pub(crate) fn get_swenc_sqrt_neg_three_minus_one_div_two() -> Fq { + SWENC_SQRT_NEG_THREE_MINUS_ONE_DIV_TWO + } + + fn mul_bits>(&self, bits: BitIterator) -> Self { + let mut res = Self::zero(); + for bit in bits { + res.double(); + + if bit { + res.add_assign(self) + } + } + res + } + + /// Hash into the field. This takes a `Blake2b` instance, + /// computes the hash, and interprets the hash as a big endian + /// number. The number is reduced mod q. The caller is + /// responsible for ensuring the Blake2b instance was + /// initialized with a 64 byte digest result. + pub(crate) fn hash(mut hasher: Blake2b) -> Self { + let mut repr: [u64; 8] = [0; 8]; + BigEndian::read_u64_into(hasher.finalize().as_bytes(), &mut repr); + repr.reverse(); + Self::one().mul_bits(BitIterator::new(repr)) + } +} + +#[cfg(test)] +use ff::SqrtField; +#[cfg(test)] +use rand::{Rand, Rng, SeedableRng, XorShiftRng}; + +#[test] +fn test_hash() { + // check that an arbitrary image of the hash is in the field. + let mut hasher = Blake2b::new(); + hasher.update(&[0x42; 32]); + assert!(Fq::hash(hasher).is_valid()); + + let mut rng = XorShiftRng::from_seed([0x5dbe6259, 0x8d313d76, 0x3237db17, 0xe5bc0654]); + let mut lsb_ones: i32 = 0; + for _ in 0..1000 { + let seed = rng.gen::<[u8; 32]>(); + let mut hasher = Blake2b::new(); + hasher.update(&seed); + let e = Fq::hash(hasher); + // check that the hash image is in the field + assert!(e.is_valid()); + // count how many less-significant bits are set in each limb + lsb_ones += e + .into_repr() + .as_ref() + .iter() + .map(|x| if x % 2 == 0 { 0i32 } else { 1i32 }) + .sum::(); + } + // lsb_ones should a uniformly random variable 100*X + // where X is a coin flip + let mean = 1000 * 6 / 2; + // sqrt(1000 * 6 * .25) = 38.72983346207417 + let variance = 40 * 4; + assert!((lsb_ones - mean).abs() < variance); +} + #[test] fn test_b_coeff() { assert_eq!(Fq::from_repr(FqRepr::from(4)).unwrap(), B_COEFF); } +#[test] +fn test_swenc_consts() { + // c0 = sqrt(-3) + let mut c0 = Fq::from_repr(FqRepr::from(3)).unwrap(); + c0.negate(); + let c0 = c0.sqrt().unwrap(); + assert_eq!(c0, Fq::get_swenc_sqrt_neg_three()); + + // c2 = (sqrt(-3) - 1) / 2 + let mut expected = Fq::get_swenc_sqrt_neg_three_minus_one_div_two(); + expected.add_assign(&Fq::get_swenc_sqrt_neg_three_minus_one_div_two()); + expected.add_assign(&Fq::one()); + assert_eq!(c0, expected); +} + #[test] fn test_frob_coeffs() { let mut nqr = Fq::one(); @@ -1172,9 +1291,6 @@ fn test_neg_one() { assert_eq!(NEGATIVE_ONE, o); } -#[cfg(test)] -use rand::{Rand, SeedableRng, XorShiftRng}; - #[test] fn test_fq_repr_ordering() { use std::cmp::Ordering; @@ -1574,26 +1690,24 @@ fn test_fq_is_valid() { a.0.sub_noborrow(&FqRepr::from(1)); assert!(a.is_valid()); assert!(Fq(FqRepr::from(0)).is_valid()); - assert!( - Fq(FqRepr([ - 0xdf4671abd14dab3e, - 0xe2dc0c9f534fbd33, - 0x31ca6c880cc444a6, - 0x257a67e70ef33359, - 0xf9b29e493f899b36, - 0x17c8be1800b9f059 - ])).is_valid() - ); - assert!( - !Fq(FqRepr([ - 0xffffffffffffffff, - 0xffffffffffffffff, - 0xffffffffffffffff, - 0xffffffffffffffff, - 0xffffffffffffffff, - 0xffffffffffffffff - ])).is_valid() - ); + assert!(Fq(FqRepr([ + 0xdf4671abd14dab3e, + 0xe2dc0c9f534fbd33, + 0x31ca6c880cc444a6, + 0x257a67e70ef33359, + 0xf9b29e493f899b36, + 0x17c8be1800b9f059 + ])) + .is_valid()); + assert!(!Fq(FqRepr([ + 0xffffffffffffffff, + 0xffffffffffffffff, + 0xffffffffffffffff, + 0xffffffffffffffff, + 0xffffffffffffffff, + 0xffffffffffffffff + ])) + .is_valid()); let mut rng = XorShiftRng::from_seed([0x5dbe6259, 0x8d313d76, 0x3237db17, 0xe5bc0654]); @@ -1929,7 +2043,8 @@ fn test_fq_squaring() { 0xdc05c659b4e15b27, 0x79361e5a802c6a23, 0x24bcbe5d51b9a6f - ])).unwrap() + ])) + .unwrap() ); let mut rng = XorShiftRng::from_seed([0x5dbe6259, 0x8d313d76, 0x3237db17, 0xe5bc0654]); @@ -2061,16 +2176,15 @@ fn test_fq_sqrt() { #[test] fn test_fq_from_into_repr() { // q + 1 should not be in the field - assert!( - Fq::from_repr(FqRepr([ - 0xb9feffffffffaaac, - 0x1eabfffeb153ffff, - 0x6730d2a0f6b0f624, - 0x64774b84f38512bf, - 0x4b1ba7b6434bacd7, - 0x1a0111ea397fe69a - ])).is_err() - ); + assert!(Fq::from_repr(FqRepr([ + 0xb9feffffffffaaac, + 0x1eabfffeb153ffff, + 0x6730d2a0f6b0f624, + 0x64774b84f38512bf, + 0x4b1ba7b6434bacd7, + 0x1a0111ea397fe69a + ])) + .is_err()); // q should not be in the field assert!(Fq::from_repr(Fq::char()).is_err()); @@ -2244,3 +2358,13 @@ fn test_fq_legendre() { ]); assert_eq!(QuadraticResidue, Fq::from_repr(e).unwrap().legendre()); } + +#[test] +fn test_fq_hash() { + let h = Blake2b::new(); + + assert_eq!( + Fq::hash(h), + Fq::from_str("2969971670216977749765615497980645380676770840200853026844185893034591434908642692717019379762235872058512380843355").unwrap() + ); +} diff --git a/src/bls12_381/fq2.rs b/src/bls12_381/fq2.rs index 363439a6..1b8b54b1 100644 --- a/src/bls12_381/fq2.rs +++ b/src/bls12_381/fq2.rs @@ -1,4 +1,5 @@ -use super::fq::{FROBENIUS_COEFF_FQ2_C1, Fq, NEGATIVE_ONE}; +use super::fq::{Fq, FROBENIUS_COEFF_FQ2_C1, NEGATIVE_ONE}; +use blake2b_simd::State as Blake2b; use ff::{Field, SqrtField}; use rand::{Rand, Rng}; @@ -54,6 +55,37 @@ impl Fq2 { t1 } + + pub(crate) fn parity(&self) -> bool { + let mut neg = *self; + neg.negate(); + *self > neg + } + + pub(crate) fn get_swenc_sqrt_neg_three() -> Fq2 { + Fq2 { + c0: Fq::get_swenc_sqrt_neg_three(), + c1: Fq::zero(), + } + } + + pub(crate) fn get_swenc_sqrt_neg_three_minus_one_div_two() -> Fq2 { + Fq2 { + c0: Fq::get_swenc_sqrt_neg_three_minus_one_div_two(), + c1: Fq::zero(), + } + } + + pub(crate) fn hash(mut hasher_c0: Blake2b) -> Self { + let mut hasher_c1 = hasher_c0.clone(); + hasher_c0.update(b"_c0"); + hasher_c1.update(b"_c1"); + + Fq2 { + c0: Fq::hash(hasher_c0), + c1: Fq::hash(hasher_c1), + } + } } impl Rand for Fq2 { @@ -263,12 +295,11 @@ fn test_fq2_basics() { ); assert!(Fq2::zero().is_zero()); assert!(!Fq2::one().is_zero()); - assert!( - !Fq2 { - c0: Fq::zero(), - c1: Fq::one(), - }.is_zero() - ); + assert!(!Fq2 { + c0: Fq::zero(), + c1: Fq::one(), + } + .is_zero()); } #[test] @@ -311,7 +342,8 @@ fn test_fq2_squaring() { 0xf7f295a94e58ae7c, 0x41b76dcc1c3fbe5e, 0x7080c5fa1d8e042, - ])).unwrap(), + ])) + .unwrap(), c1: Fq::from_repr(FqRepr([ 0x38f473b3c870a4ab, 0x6ad3291177c8c7e5, @@ -319,7 +351,8 @@ fn test_fq2_squaring() { 0xbfb99020604137a0, 0xfc58a7b7be815407, 0x10d1615e75250a21, - ])).unwrap(), + ])) + .unwrap(), }; a.square(); assert_eq!( @@ -332,7 +365,8 @@ fn test_fq2_squaring() { 0xcb674157618da176, 0x4cf17b5893c3d327, 0x7eac81369c43361 - ])).unwrap(), + ])) + .unwrap(), c1: Fq::from_repr(FqRepr([ 0xc1579cf58e980cf8, 0xa23eb7e12dd54d98, @@ -340,7 +374,8 @@ fn test_fq2_squaring() { 0x38d0d7275a9689e1, 0x739c983042779a65, 0x1542a61c8a8db994 - ])).unwrap(), + ])) + .unwrap(), } ); } @@ -358,7 +393,8 @@ fn test_fq2_mul() { 0x9ee53e7e84d7532e, 0x1c202d8ed97afb45, 0x51d3f9253e2516f, - ])).unwrap(), + ])) + .unwrap(), c1: Fq::from_repr(FqRepr([ 0xa7348a8b511aedcf, 0x143c215d8176b319, @@ -366,7 +402,8 @@ fn test_fq2_mul() { 0x9533e4a9a5158be, 0x7a5e1ecb676d65f9, 0x180c3ee46656b008, - ])).unwrap(), + ])) + .unwrap(), }; a.mul_assign(&Fq2 { c0: Fq::from_repr(FqRepr([ @@ -376,7 +413,8 @@ fn test_fq2_mul() { 0xcd460f9f0c23e430, 0x6c9110292bfa409, 0x2c93a72eb8af83e, - ])).unwrap(), + ])) + .unwrap(), c1: Fq::from_repr(FqRepr([ 0x4b1c3f936d8992d4, 0x1d2a72916dba4c8a, @@ -384,7 +422,8 @@ fn test_fq2_mul() { 0x57a06d3135a752ae, 0x634cd3c6c565096d, 0x19e17334d4e93558, - ])).unwrap(), + ])) + .unwrap(), }); assert_eq!( a, @@ -396,7 +435,8 @@ fn test_fq2_mul() { 0x5511fe4d84ee5f78, 0x5310a202d92f9963, 0x1751afbe166e5399 - ])).unwrap(), + ])) + .unwrap(), c1: Fq::from_repr(FqRepr([ 0x84af0e1bd630117a, 0x6c63cd4da2c2aa7, @@ -404,7 +444,8 @@ fn test_fq2_mul() { 0xc975106579c275ee, 0x33a9ac82ce4c5083, 0x1ef1a36c201589d - ])).unwrap(), + ])) + .unwrap(), } ); } @@ -424,7 +465,8 @@ fn test_fq2_inverse() { 0x9ee53e7e84d7532e, 0x1c202d8ed97afb45, 0x51d3f9253e2516f, - ])).unwrap(), + ])) + .unwrap(), c1: Fq::from_repr(FqRepr([ 0xa7348a8b511aedcf, 0x143c215d8176b319, @@ -432,7 +474,8 @@ fn test_fq2_inverse() { 0x9533e4a9a5158be, 0x7a5e1ecb676d65f9, 0x180c3ee46656b008, - ])).unwrap(), + ])) + .unwrap(), }; let a = a.inverse().unwrap(); assert_eq!( @@ -445,7 +488,8 @@ fn test_fq2_inverse() { 0xdfba703293941c30, 0xa6c3d8f9586f2636, 0x1351ef01941b70c4 - ])).unwrap(), + ])) + .unwrap(), c1: Fq::from_repr(FqRepr([ 0x8c39fd76a8312cb4, 0x15d7b6b95defbff0, @@ -453,7 +497,8 @@ fn test_fq2_inverse() { 0xcbf651a0f367afb2, 0xdf4e54f0d3ef15a6, 0x103bdf241afb0019 - ])).unwrap(), + ])) + .unwrap(), } ); } @@ -471,7 +516,8 @@ fn test_fq2_addition() { 0xb966ce3bc2108b13, 0xccc649c4b9532bf3, 0xf8d295b2ded9dc, - ])).unwrap(), + ])) + .unwrap(), c1: Fq::from_repr(FqRepr([ 0x977df6efcdaee0db, 0x946ae52d684fa7ed, @@ -479,7 +525,8 @@ fn test_fq2_addition() { 0xb3f8afc0ee248cad, 0x4e464dea5bcfd41e, 0x12d1137b8a6a837, - ])).unwrap(), + ])) + .unwrap(), }; a.add_assign(&Fq2 { c0: Fq::from_repr(FqRepr([ @@ -489,7 +536,8 @@ fn test_fq2_addition() { 0x3b88899a42a6318f, 0x986a4a62fa82a49d, 0x13ce433fa26027f5, - ])).unwrap(), + ])) + .unwrap(), c1: Fq::from_repr(FqRepr([ 0x66323bf80b58b9b9, 0xa1379b6facf6e596, @@ -497,7 +545,8 @@ fn test_fq2_addition() { 0x2236f55246d0d44d, 0x4c8c1800eb104566, 0x11d6e20e986c2085, - ])).unwrap(), + ])) + .unwrap(), }); assert_eq!( a, @@ -509,7 +558,8 @@ fn test_fq2_addition() { 0xf4ef57d604b6bca2, 0x65309427b3d5d090, 0x14c715d5553f01d2 - ])).unwrap(), + ])) + .unwrap(), c1: Fq::from_repr(FqRepr([ 0xfdb032e7d9079a94, 0x35a2809d15468d83, @@ -517,7 +567,8 @@ fn test_fq2_addition() { 0xd62fa51334f560fa, 0x9ad265eb46e01984, 0x1303f3465112c8bc - ])).unwrap(), + ])) + .unwrap(), } ); } @@ -535,7 +586,8 @@ fn test_fq2_subtraction() { 0xb966ce3bc2108b13, 0xccc649c4b9532bf3, 0xf8d295b2ded9dc, - ])).unwrap(), + ])) + .unwrap(), c1: Fq::from_repr(FqRepr([ 0x977df6efcdaee0db, 0x946ae52d684fa7ed, @@ -543,7 +595,8 @@ fn test_fq2_subtraction() { 0xb3f8afc0ee248cad, 0x4e464dea5bcfd41e, 0x12d1137b8a6a837, - ])).unwrap(), + ])) + .unwrap(), }; a.sub_assign(&Fq2 { c0: Fq::from_repr(FqRepr([ @@ -553,7 +606,8 @@ fn test_fq2_subtraction() { 0x3b88899a42a6318f, 0x986a4a62fa82a49d, 0x13ce433fa26027f5, - ])).unwrap(), + ])) + .unwrap(), c1: Fq::from_repr(FqRepr([ 0x66323bf80b58b9b9, 0xa1379b6facf6e596, @@ -561,7 +615,8 @@ fn test_fq2_subtraction() { 0x2236f55246d0d44d, 0x4c8c1800eb104566, 0x11d6e20e986c2085, - ])).unwrap(), + ])) + .unwrap(), }); assert_eq!( a, @@ -573,7 +628,8 @@ fn test_fq2_subtraction() { 0xe255902672ef6c43, 0x7f77a718021c342d, 0x72ba14049fe9881 - ])).unwrap(), + ])) + .unwrap(), c1: Fq::from_repr(FqRepr([ 0xeb4abaf7c255d1cd, 0x11df49bc6cacc256, @@ -581,7 +637,8 @@ fn test_fq2_subtraction() { 0xf63905f39ad8cb1f, 0x4cd5dd9fb40b3b8f, 0x957411359ba6e4c - ])).unwrap(), + ])) + .unwrap(), } ); } @@ -599,7 +656,8 @@ fn test_fq2_negation() { 0xb966ce3bc2108b13, 0xccc649c4b9532bf3, 0xf8d295b2ded9dc, - ])).unwrap(), + ])) + .unwrap(), c1: Fq::from_repr(FqRepr([ 0x977df6efcdaee0db, 0x946ae52d684fa7ed, @@ -607,7 +665,8 @@ fn test_fq2_negation() { 0xb3f8afc0ee248cad, 0x4e464dea5bcfd41e, 0x12d1137b8a6a837, - ])).unwrap(), + ])) + .unwrap(), }; a.negate(); assert_eq!( @@ -620,7 +679,8 @@ fn test_fq2_negation() { 0xab107d49317487ab, 0x7e555df189f880e3, 0x19083f5486a10cbd - ])).unwrap(), + ])) + .unwrap(), c1: Fq::from_repr(FqRepr([ 0x228109103250c9d0, 0x8a411ad149045812, @@ -628,7 +688,8 @@ fn test_fq2_negation() { 0xb07e9bc405608611, 0xfcd559cbe77bd8b8, 0x18d400b280d93e62 - ])).unwrap(), + ])) + .unwrap(), } ); } @@ -646,7 +707,8 @@ fn test_fq2_doubling() { 0xb966ce3bc2108b13, 0xccc649c4b9532bf3, 0xf8d295b2ded9dc, - ])).unwrap(), + ])) + .unwrap(), c1: Fq::from_repr(FqRepr([ 0x977df6efcdaee0db, 0x946ae52d684fa7ed, @@ -654,7 +716,8 @@ fn test_fq2_doubling() { 0xb3f8afc0ee248cad, 0x4e464dea5bcfd41e, 0x12d1137b8a6a837, - ])).unwrap(), + ])) + .unwrap(), }; a.double(); assert_eq!( @@ -667,7 +730,8 @@ fn test_fq2_doubling() { 0x72cd9c7784211627, 0x998c938972a657e7, 0x1f1a52b65bdb3b9 - ])).unwrap(), + ])) + .unwrap(), c1: Fq::from_repr(FqRepr([ 0x2efbeddf9b5dc1b6, 0x28d5ca5ad09f4fdb, @@ -675,7 +739,8 @@ fn test_fq2_doubling() { 0x67f15f81dc49195b, 0x9c8c9bd4b79fa83d, 0x25a226f714d506e - ])).unwrap(), + ])) + .unwrap(), } ); } @@ -693,7 +758,8 @@ fn test_fq2_frobenius_map() { 0xb966ce3bc2108b13, 0xccc649c4b9532bf3, 0xf8d295b2ded9dc, - ])).unwrap(), + ])) + .unwrap(), c1: Fq::from_repr(FqRepr([ 0x977df6efcdaee0db, 0x946ae52d684fa7ed, @@ -701,7 +767,8 @@ fn test_fq2_frobenius_map() { 0xb3f8afc0ee248cad, 0x4e464dea5bcfd41e, 0x12d1137b8a6a837, - ])).unwrap(), + ])) + .unwrap(), }; a.frobenius_map(0); assert_eq!( @@ -714,7 +781,8 @@ fn test_fq2_frobenius_map() { 0xb966ce3bc2108b13, 0xccc649c4b9532bf3, 0xf8d295b2ded9dc - ])).unwrap(), + ])) + .unwrap(), c1: Fq::from_repr(FqRepr([ 0x977df6efcdaee0db, 0x946ae52d684fa7ed, @@ -722,7 +790,8 @@ fn test_fq2_frobenius_map() { 0xb3f8afc0ee248cad, 0x4e464dea5bcfd41e, 0x12d1137b8a6a837 - ])).unwrap(), + ])) + .unwrap(), } ); a.frobenius_map(1); @@ -736,7 +805,8 @@ fn test_fq2_frobenius_map() { 0xb966ce3bc2108b13, 0xccc649c4b9532bf3, 0xf8d295b2ded9dc - ])).unwrap(), + ])) + .unwrap(), c1: Fq::from_repr(FqRepr([ 0x228109103250c9d0, 0x8a411ad149045812, @@ -744,7 +814,8 @@ fn test_fq2_frobenius_map() { 0xb07e9bc405608611, 0xfcd559cbe77bd8b8, 0x18d400b280d93e62 - ])).unwrap(), + ])) + .unwrap(), } ); a.frobenius_map(1); @@ -758,7 +829,8 @@ fn test_fq2_frobenius_map() { 0xb966ce3bc2108b13, 0xccc649c4b9532bf3, 0xf8d295b2ded9dc - ])).unwrap(), + ])) + .unwrap(), c1: Fq::from_repr(FqRepr([ 0x977df6efcdaee0db, 0x946ae52d684fa7ed, @@ -766,7 +838,8 @@ fn test_fq2_frobenius_map() { 0xb3f8afc0ee248cad, 0x4e464dea5bcfd41e, 0x12d1137b8a6a837 - ])).unwrap(), + ])) + .unwrap(), } ); a.frobenius_map(2); @@ -780,7 +853,8 @@ fn test_fq2_frobenius_map() { 0xb966ce3bc2108b13, 0xccc649c4b9532bf3, 0xf8d295b2ded9dc - ])).unwrap(), + ])) + .unwrap(), c1: Fq::from_repr(FqRepr([ 0x977df6efcdaee0db, 0x946ae52d684fa7ed, @@ -788,7 +862,8 @@ fn test_fq2_frobenius_map() { 0xb3f8afc0ee248cad, 0x4e464dea5bcfd41e, 0x12d1137b8a6a837 - ])).unwrap(), + ])) + .unwrap(), } ); } @@ -807,7 +882,8 @@ fn test_fq2_sqrt() { 0xdb4a116b5bf74aa1, 0x1e58b2159dfe10e2, 0x7ca7da1f13606ac - ])).unwrap(), + ])) + .unwrap(), c1: Fq::from_repr(FqRepr([ 0xfa8de88b7516d2c3, 0x371a75ed14f41629, @@ -815,9 +891,11 @@ fn test_fq2_sqrt() { 0x212611bca4e99121, 0x8ee5394d77afb3d, 0xec92336650e49d5 - ])).unwrap(), - }.sqrt() + ])) .unwrap(), + } + .sqrt() + .unwrap(), Fq2 { c0: Fq::from_repr(FqRepr([ 0x40b299b2704258c5, @@ -826,7 +904,8 @@ fn test_fq2_sqrt() { 0x8d7f1f723d02c1d3, 0x881b3e01b611c070, 0x10f6963bbad2ebc5 - ])).unwrap(), + ])) + .unwrap(), c1: Fq::from_repr(FqRepr([ 0xc099534fc209e752, 0x7670594665676447, @@ -834,7 +913,8 @@ fn test_fq2_sqrt() { 0x6b852aeaf2afcb1b, 0xa4c93b08105d71a9, 0x8d7cfff94216330 - ])).unwrap(), + ])) + .unwrap(), } ); @@ -847,10 +927,12 @@ fn test_fq2_sqrt() { 0x64774b84f38512bf, 0x4b1ba7b6434bacd7, 0x1a0111ea397fe69a - ])).unwrap(), - c1: Fq::zero(), - }.sqrt() + ])) .unwrap(), + c1: Fq::zero(), + } + .sqrt() + .unwrap(), Fq2 { c0: Fq::zero(), c1: Fq::from_repr(FqRepr([ @@ -860,7 +942,8 @@ fn test_fq2_sqrt() { 0x64774b84f38512bf, 0x4b1ba7b6434bacd7, 0x1a0111ea397fe69a - ])).unwrap(), + ])) + .unwrap(), } ); } @@ -908,3 +991,25 @@ fn fq2_field_tests() { ::tests::field::random_sqrt_tests::(); ::tests::field::random_frobenius_tests::(super::fq::Fq::char(), 13); } + +#[test] +fn test_swenc_consts() { + use super::FqRepr; + use PrimeField; + + // c0 = sqrt(-3) + let mut c0 = Fq2 { + c0: Fq::from_repr(FqRepr::from(3)).unwrap(), + c1: Fq::zero(), + }; + c0.negate(); + let mut c0 = c0.sqrt().unwrap(); + c0.negate(); // Fq2 sqrt impl produces the negative sqrt + assert_eq!(c0, Fq2::get_swenc_sqrt_neg_three()); + + // c2 = (sqrt(-3) - 1) / 2 + let mut expected = Fq2::get_swenc_sqrt_neg_three_minus_one_div_two(); + expected.add_assign(&Fq2::get_swenc_sqrt_neg_three_minus_one_div_two()); + expected.add_assign(&Fq2::one()); + assert_eq!(c0, expected); +} diff --git a/src/bls12_381/tests/g1_hashtopoint.dat b/src/bls12_381/tests/g1_hashtopoint.dat new file mode 100644 index 00000000..e8977d9c Binary files /dev/null and b/src/bls12_381/tests/g1_hashtopoint.dat differ diff --git a/src/bls12_381/tests/g2_hashtopoint.dat b/src/bls12_381/tests/g2_hashtopoint.dat new file mode 100644 index 00000000..352eb0c2 Binary files /dev/null and b/src/bls12_381/tests/g2_hashtopoint.dat differ diff --git a/src/bls12_381/tests/mod.rs b/src/bls12_381/tests/mod.rs index bf6c5959..344529ce 100644 --- a/src/bls12_381/tests/mod.rs +++ b/src/bls12_381/tests/mod.rs @@ -52,6 +52,36 @@ fn test_pairing_result_against_relic() { }); } +#[test] +fn test_g1_hash_vectors() { + let mut expected = &include_bytes!("g1_hashtopoint.dat")[..]; + + for i in 0..1000 { + let p = G1::hash(format!("{}", i).as_bytes()) + .into_affine() + .into_uncompressed(); + + assert_eq!(p.as_ref(), &expected[0..96]); + expected = &expected[96..]; + } + assert_eq!(expected.len(), 0); +} + +#[test] +fn test_g2_hash_vectors() { + let mut expected = &include_bytes!("g2_hashtopoint.dat")[..]; + + for i in 0..1000 { + let p = G2::hash(format!("{}", i).as_bytes()) + .into_affine() + .into_uncompressed(); + + assert_eq!(p.as_ref(), &expected[0..(96 * 2)]); + expected = &expected[(96 * 2)..]; + } + assert_eq!(expected.len(), 0); +} + fn test_vectors>(expected: &[u8]) { let mut e = G::zero(); diff --git a/src/lib.rs b/src/lib.rs index bbced76f..c08e71eb 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -8,9 +8,11 @@ #![cfg_attr(feature = "cargo-clippy", allow(many_single_char_names))] #![cfg_attr(feature = "cargo-clippy", allow(new_without_default_derive))] #![cfg_attr(feature = "cargo-clippy", allow(write_literal))] +#![cfg_attr(feature = "clippy", allow(string_lit_as_bytes))] // Force public structures to implement Debug #![deny(missing_debug_implementations)] +extern crate blake2b_simd; extern crate byteorder; #[macro_use] extern crate ff; @@ -38,8 +40,7 @@ pub trait Engine: ScalarEngine { Base = Self::Fq, Scalar = Self::Fr, Affine = Self::G1Affine, - > - + From; + > + From; /// The affine representation of an element in G1. type G1Affine: CurveAffine< @@ -49,8 +50,7 @@ pub trait Engine: ScalarEngine { Projective = Self::G1, Pair = Self::G2Affine, PairingResult = Self::Fqk, - > - + From; + > + From; /// The projective representation of an element in G2. type G2: CurveProjective< @@ -58,8 +58,7 @@ pub trait Engine: ScalarEngine { Base = Self::Fqe, Scalar = Self::Fr, Affine = Self::G2Affine, - > - + From; + > + From; /// The affine representation of an element in G2. type G2Affine: CurveAffine< @@ -69,8 +68,7 @@ pub trait Engine: ScalarEngine { Projective = Self::G2, Pair = Self::G1Affine, PairingResult = Self::Fqk, - > - + From; + > + From; /// The base field that hosts G1. type Fq: PrimeField + SqrtField; @@ -102,7 +100,8 @@ pub trait Engine: ScalarEngine { { Self::final_exponentiation(&Self::miller_loop( [(&(p.into().prepare()), &(q.into().prepare()))].into_iter(), - )).unwrap() + )) + .unwrap() } } @@ -137,7 +136,7 @@ pub trait CurveProjective: /// Normalizes a slice of projective elements so that /// conversion to affine is cheap. - fn batch_normalization(v: &mut [Self]); + fn batch_normalization>(v: &mut [S]); /// Checks if the point is already "normalized" so that /// cheap affine conversion is possible. @@ -175,6 +174,9 @@ pub trait CurveProjective: /// Recommends a wNAF window size given the number of scalars you intend to multiply /// a base by. Always returns a number between 2 and 22, inclusive. fn recommended_wnaf_for_num_scalars(num_scalars: usize) -> usize; + + /// Given a message, hash into a random element of the prime-order subgroup. + fn hash(&[u8]) -> Self; } /// Affine representation of an elliptic curve point guaranteed to be @@ -232,7 +234,8 @@ pub trait CurveAffine: /// An encoded elliptic curve point, which should essentially wrap a `[u8; N]`. pub trait EncodedPoint: - Sized + Send + Sync + AsRef<[u8]> + AsMut<[u8]> + Clone + Copy + 'static + Sized + Send + Sync + AsRef<[u8]> + AsMut<[u8]> + Clone + Copy + + PartialOrd + Ord + PartialEq + Eq + ::std::hash::Hash + 'static { type Affine: CurveAffine;