diff --git a/Cargo.lock b/Cargo.lock index 5004588f..e45f1302 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -174,7 +174,7 @@ dependencies = [ [[package]] name = "bn" version = "0.4.4" -source = "git+https://github.com/paritytech/bn?branch=upgrade#e9f64f518d0ae64cb4f9a8c7c544e3ff0ef82584" +source = "git+https://github.com/paritytech/bn?branch=upgrade#76c1296cfe62875524affb295bc067e42a202b0d" dependencies = [ "byteorder 1.2.7 (registry+https://github.com/rust-lang/crates.io-index)", "crunchy 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)", diff --git a/crypto/src/pghr13.rs b/crypto/src/pghr13.rs index f7890167..2ad73882 100644 --- a/crypto/src/pghr13.rs +++ b/crypto/src/pghr13.rs @@ -1,4 +1,4 @@ -pub use bn::{Fr, Fq, G1, G2, Group, arith::U256, AffineG1}; +pub use bn::{Fr, Fq, Fq2, G1, G2, Group, arith::U256, AffineG1}; use bn::pairing; use std::ops::Neg; @@ -53,6 +53,12 @@ lazy_static! { Fq::from_u256(3.into()).expect("3 is a valid field element and static; qed").neg() * Fq::from_u256(4.into()).expect("4 is a valid field element and static; qed").inverse() .expect("4 has inverse in Fq and is static; qed"); + + pub static ref FQ_MINUS1_DIV2: Fq = + Fq::from_u256(1.into()).expect("1 is a valid field element and static; qed").neg() * + Fq::from_u256(2.into()).expect("2 is a valid field element and static; qed").inverse() + .expect("2 has inverse in Fq and is static; qed"); + } // Shanks’s algorithm for q ≡ 3 (mod 4) @@ -72,6 +78,25 @@ fn fq_sqrt(a: Fq) -> Option { } } +fn fq2_sqrt(a: Fq2) -> Option { + let a1 = a.pow(FQ_MINUS3_DIV4.into_u256()); + let a1a = a1 * a; + let alpha = a1 * a1a; + let a0 = alpha.pow(*FQ) * alpha; + + if a0 == Fq2::one().neg() { + return None; + } + + if alpha == Fq2::one().neg() { + Some(Fq2::i() * a1a) + } else { + let b = (alpha + Fq2::one()).pow(FQ_MINUS1_DIV2.into_u256()); + Some(b * a1a) + } +} + + fn g1_from_compressed(data: &[u8]) -> Result { if data.len() != 33 { return Err(Error::InvalidRawInput); } @@ -122,13 +147,28 @@ mod tests { } #[test] - fn sqrt() { + fn sqrt_fq() { let fq1 = Fq::from_str("5204065062716160319596273903996315000119019512886596366359652578430118331601").unwrap(); let fq2 = Fq::from_str("348579348568").unwrap(); assert_eq!(fq1, fq_sqrt(fq2).expect("348579348568 is quadratic residue")); } + #[test] + fn sqrt_fq2() { + let x1 = Fq2::new( + Fq::from_str("12844195307879678418043983815760255909500142247603239203345049921980497041944").unwrap(), + Fq::from_str("7476417578426924565731404322659619974551724117137577781074613937423560117731").unwrap(), + ); + + let x2 = Fq2::new( + Fq::from_str("3345897230485723946872934576923485762803457692345760237495682347502347589474").unwrap(), + Fq::from_str("1234912378405347958234756902345768290345762348957605678245967234857634857676").unwrap(), + ); + + assert_eq!(fq2_sqrt(x2).unwrap(), x1); + } + #[test] fn g1_deserialize() { let g1 = g1_from_compressed(&hex("0230644e72e131a029b85045b68181585d97816a916871ca8d3c208c16d87cfd46")).expect("Invalid g1 decompress result");