diff --git a/.gitignore b/.gitignore index 4308d82..750c824 100644 --- a/.gitignore +++ b/.gitignore @@ -1,3 +1,4 @@ target/ **/*.rs.bk Cargo.lock +.vscode \ No newline at end of file diff --git a/Cargo.toml b/Cargo.toml index 5f16018..8d3e885 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -2,17 +2,18 @@ name = "pairing" # Remember to change version string in README.md. -version = "0.14.2" +version = "0.15.0" authors = [ "Sean Bowe ", "Jack Grigg ", + "Alex Vlasov " ] license = "MIT/Apache-2.0" description = "Pairing-friendly elliptic curve library" documentation = "https://docs.rs/pairing/" -homepage = "https://github.com/ebfull/pairing" -repository = "https://github.com/ebfull/pairing" +homepage = "https://github.com/matterinc/pairing" +repository = "https://github.com/matterinc/pairing" [dependencies] rand = "0.4" diff --git a/README.md b/README.md index bf386de..3031b61 100644 --- a/README.md +++ b/README.md @@ -1,6 +1,8 @@ -# pairing [![Crates.io](https://img.shields.io/crates/v/pairing.svg)](https://crates.io/crates/pairing) # +# pairing "community edition" -This is a Rust crate for using pairing-friendly elliptic curves. Currently, only the [BLS12-381](https://z.cash/blog/new-snark-curve.html) construction is implemented. +Originally developed by ZCash, with extensions from us to make it a little more pleasant. + +This is a Rust crate for using pairing-friendly elliptic curves. Currently, only the [BLS12-381](https://z.cash/blog/new-snark-curve.html) and BN256 curves are implemented. ## [Documentation](https://docs.rs/pairing/) diff --git a/benches/bn256/ec.rs b/benches/bn256/ec.rs new file mode 100644 index 0000000..b188f4c --- /dev/null +++ b/benches/bn256/ec.rs @@ -0,0 +1,127 @@ +mod g1 { + use rand::{Rand, SeedableRng, XorShiftRng}; + + use pairing::bn256::*; + use pairing::CurveProjective; + + #[bench] + fn bench_g1_mul_assign(b: &mut ::test::Bencher) { + const SAMPLES: usize = 1000; + + let mut rng = XorShiftRng::from_seed([0x5dbe6259, 0x8d313d76, 0x3237db17, 0xe5bc0654]); + + let v: Vec<(G1, Fr)> = (0..SAMPLES) + .map(|_| (G1::rand(&mut rng), Fr::rand(&mut rng))) + .collect(); + + let mut count = 0; + b.iter(|| { + let mut tmp = v[count].0; + tmp.mul_assign(v[count].1); + count = (count + 1) % SAMPLES; + tmp + }); + } + + #[bench] + fn bench_g1_add_assign(b: &mut ::test::Bencher) { + const SAMPLES: usize = 1000; + + let mut rng = XorShiftRng::from_seed([0x5dbe6259, 0x8d313d76, 0x3237db17, 0xe5bc0654]); + + let v: Vec<(G1, G1)> = (0..SAMPLES) + .map(|_| (G1::rand(&mut rng), G1::rand(&mut rng))) + .collect(); + + let mut count = 0; + b.iter(|| { + let mut tmp = v[count].0; + tmp.add_assign(&v[count].1); + count = (count + 1) % SAMPLES; + tmp + }); + } + + #[bench] + fn bench_g1_add_assign_mixed(b: &mut ::test::Bencher) { + const SAMPLES: usize = 1000; + + let mut rng = XorShiftRng::from_seed([0x5dbe6259, 0x8d313d76, 0x3237db17, 0xe5bc0654]); + + let v: Vec<(G1, G1Affine)> = (0..SAMPLES) + .map(|_| (G1::rand(&mut rng), G1::rand(&mut rng).into())) + .collect(); + + let mut count = 0; + b.iter(|| { + let mut tmp = v[count].0; + tmp.add_assign_mixed(&v[count].1); + count = (count + 1) % SAMPLES; + tmp + }); + } +} + +mod g2 { + use rand::{Rand, SeedableRng, XorShiftRng}; + + use pairing::bls12_381::*; + use pairing::CurveProjective; + + #[bench] + fn bench_g2_mul_assign(b: &mut ::test::Bencher) { + const SAMPLES: usize = 1000; + + let mut rng = XorShiftRng::from_seed([0x5dbe6259, 0x8d313d76, 0x3237db17, 0xe5bc0654]); + + let v: Vec<(G2, Fr)> = (0..SAMPLES) + .map(|_| (G2::rand(&mut rng), Fr::rand(&mut rng))) + .collect(); + + let mut count = 0; + b.iter(|| { + let mut tmp = v[count].0; + tmp.mul_assign(v[count].1); + count = (count + 1) % SAMPLES; + tmp + }); + } + + #[bench] + fn bench_g2_add_assign(b: &mut ::test::Bencher) { + const SAMPLES: usize = 1000; + + let mut rng = XorShiftRng::from_seed([0x5dbe6259, 0x8d313d76, 0x3237db17, 0xe5bc0654]); + + let v: Vec<(G2, G2)> = (0..SAMPLES) + .map(|_| (G2::rand(&mut rng), G2::rand(&mut rng))) + .collect(); + + let mut count = 0; + b.iter(|| { + let mut tmp = v[count].0; + tmp.add_assign(&v[count].1); + count = (count + 1) % SAMPLES; + tmp + }); + } + + #[bench] + fn bench_g2_add_assign_mixed(b: &mut ::test::Bencher) { + const SAMPLES: usize = 1000; + + let mut rng = XorShiftRng::from_seed([0x5dbe6259, 0x8d313d76, 0x3237db17, 0xe5bc0654]); + + let v: Vec<(G2, G2Affine)> = (0..SAMPLES) + .map(|_| (G2::rand(&mut rng), G2::rand(&mut rng).into())) + .collect(); + + let mut count = 0; + b.iter(|| { + let mut tmp = v[count].0; + tmp.add_assign_mixed(&v[count].1); + count = (count + 1) % SAMPLES; + tmp + }); + } +} diff --git a/benches/bn256/fq.rs b/benches/bn256/fq.rs new file mode 100644 index 0000000..85345b3 --- /dev/null +++ b/benches/bn256/fq.rs @@ -0,0 +1,268 @@ +use rand::{Rand, SeedableRng, XorShiftRng}; + +use ff::{Field, PrimeField, PrimeFieldRepr, SqrtField}; +use pairing::bn256::*; + +#[bench] +fn bench_fq_repr_add_nocarry(b: &mut ::test::Bencher) { + const SAMPLES: usize = 1000; + + let mut rng = XorShiftRng::from_seed([0x5dbe6259, 0x8d313d76, 0x3237db17, 0xe5bc0654]); + + let v: Vec<(FqRepr, FqRepr)> = (0..SAMPLES) + .map(|_| { + let mut tmp1 = FqRepr::rand(&mut rng); + let mut tmp2 = FqRepr::rand(&mut rng); + // Shave a few bits off to avoid overflow. + for _ in 0..3 { + tmp1.div2(); + tmp2.div2(); + } + (tmp1, tmp2) + }) + .collect(); + + let mut count = 0; + b.iter(|| { + let mut tmp = v[count].0; + tmp.add_nocarry(&v[count].1); + count = (count + 1) % SAMPLES; + tmp + }); +} + +#[bench] +fn bench_fq_repr_sub_noborrow(b: &mut ::test::Bencher) { + const SAMPLES: usize = 1000; + + let mut rng = XorShiftRng::from_seed([0x5dbe6259, 0x8d313d76, 0x3237db17, 0xe5bc0654]); + + let v: Vec<(FqRepr, FqRepr)> = (0..SAMPLES) + .map(|_| { + let tmp1 = FqRepr::rand(&mut rng); + let mut tmp2 = tmp1; + // Ensure tmp2 is smaller than tmp1. + for _ in 0..10 { + tmp2.div2(); + } + (tmp1, tmp2) + }) + .collect(); + + let mut count = 0; + b.iter(|| { + let mut tmp = v[count].0; + tmp.sub_noborrow(&v[count].1); + count = (count + 1) % SAMPLES; + tmp + }); +} + +#[bench] +fn bench_fq_repr_num_bits(b: &mut ::test::Bencher) { + const SAMPLES: usize = 1000; + + let mut rng = XorShiftRng::from_seed([0x5dbe6259, 0x8d313d76, 0x3237db17, 0xe5bc0654]); + + let v: Vec = (0..SAMPLES).map(|_| FqRepr::rand(&mut rng)).collect(); + + let mut count = 0; + b.iter(|| { + let tmp = v[count].num_bits(); + count = (count + 1) % SAMPLES; + tmp + }); +} + +#[bench] +fn bench_fq_repr_mul2(b: &mut ::test::Bencher) { + const SAMPLES: usize = 1000; + + let mut rng = XorShiftRng::from_seed([0x5dbe6259, 0x8d313d76, 0x3237db17, 0xe5bc0654]); + + let v: Vec = (0..SAMPLES).map(|_| FqRepr::rand(&mut rng)).collect(); + + let mut count = 0; + b.iter(|| { + let mut tmp = v[count]; + tmp.mul2(); + count = (count + 1) % SAMPLES; + tmp + }); +} + +#[bench] +fn bench_fq_repr_div2(b: &mut ::test::Bencher) { + const SAMPLES: usize = 1000; + + let mut rng = XorShiftRng::from_seed([0x5dbe6259, 0x8d313d76, 0x3237db17, 0xe5bc0654]); + + let v: Vec = (0..SAMPLES).map(|_| FqRepr::rand(&mut rng)).collect(); + + let mut count = 0; + b.iter(|| { + let mut tmp = v[count]; + tmp.div2(); + count = (count + 1) % SAMPLES; + tmp + }); +} + +#[bench] +fn bench_fq_add_assign(b: &mut ::test::Bencher) { + const SAMPLES: usize = 1000; + + let mut rng = XorShiftRng::from_seed([0x5dbe6259, 0x8d313d76, 0x3237db17, 0xe5bc0654]); + + let v: Vec<(Fq, Fq)> = (0..SAMPLES) + .map(|_| (Fq::rand(&mut rng), Fq::rand(&mut rng))) + .collect(); + + let mut count = 0; + b.iter(|| { + let mut tmp = v[count].0; + tmp.add_assign(&v[count].1); + count = (count + 1) % SAMPLES; + tmp + }); +} + +#[bench] +fn bench_fq_sub_assign(b: &mut ::test::Bencher) { + const SAMPLES: usize = 1000; + + let mut rng = XorShiftRng::from_seed([0x5dbe6259, 0x8d313d76, 0x3237db17, 0xe5bc0654]); + + let v: Vec<(Fq, Fq)> = (0..SAMPLES) + .map(|_| (Fq::rand(&mut rng), Fq::rand(&mut rng))) + .collect(); + + let mut count = 0; + b.iter(|| { + let mut tmp = v[count].0; + tmp.sub_assign(&v[count].1); + count = (count + 1) % SAMPLES; + tmp + }); +} + +#[bench] +fn bench_fq_mul_assign(b: &mut ::test::Bencher) { + const SAMPLES: usize = 1000; + + let mut rng = XorShiftRng::from_seed([0x5dbe6259, 0x8d313d76, 0x3237db17, 0xe5bc0654]); + + let v: Vec<(Fq, Fq)> = (0..SAMPLES) + .map(|_| (Fq::rand(&mut rng), Fq::rand(&mut rng))) + .collect(); + + let mut count = 0; + b.iter(|| { + let mut tmp = v[count].0; + tmp.mul_assign(&v[count].1); + count = (count + 1) % SAMPLES; + tmp + }); +} + +#[bench] +fn bench_fq_square(b: &mut ::test::Bencher) { + const SAMPLES: usize = 1000; + + let mut rng = XorShiftRng::from_seed([0x5dbe6259, 0x8d313d76, 0x3237db17, 0xe5bc0654]); + + let v: Vec = (0..SAMPLES).map(|_| Fq::rand(&mut rng)).collect(); + + let mut count = 0; + b.iter(|| { + let mut tmp = v[count]; + tmp.square(); + count = (count + 1) % SAMPLES; + tmp + }); +} + +#[bench] +fn bench_fq_inverse(b: &mut ::test::Bencher) { + const SAMPLES: usize = 1000; + + let mut rng = XorShiftRng::from_seed([0x5dbe6259, 0x8d313d76, 0x3237db17, 0xe5bc0654]); + + let v: Vec = (0..SAMPLES).map(|_| Fq::rand(&mut rng)).collect(); + + let mut count = 0; + b.iter(|| { + count = (count + 1) % SAMPLES; + v[count].inverse() + }); +} + +#[bench] +fn bench_fq_negate(b: &mut ::test::Bencher) { + const SAMPLES: usize = 1000; + + let mut rng = XorShiftRng::from_seed([0x5dbe6259, 0x8d313d76, 0x3237db17, 0xe5bc0654]); + + let v: Vec = (0..SAMPLES).map(|_| Fq::rand(&mut rng)).collect(); + + let mut count = 0; + b.iter(|| { + let mut tmp = v[count]; + tmp.negate(); + count = (count + 1) % SAMPLES; + tmp + }); +} + +#[bench] +fn bench_fq_sqrt(b: &mut ::test::Bencher) { + const SAMPLES: usize = 1000; + + let mut rng = XorShiftRng::from_seed([0x5dbe6259, 0x8d313d76, 0x3237db17, 0xe5bc0654]); + + let v: Vec = (0..SAMPLES) + .map(|_| { + let mut tmp = Fq::rand(&mut rng); + tmp.square(); + tmp + }) + .collect(); + + let mut count = 0; + b.iter(|| { + count = (count + 1) % SAMPLES; + v[count].sqrt() + }); +} + +#[bench] +fn bench_fq_into_repr(b: &mut ::test::Bencher) { + const SAMPLES: usize = 1000; + + let mut rng = XorShiftRng::from_seed([0x5dbe6259, 0x8d313d76, 0x3237db17, 0xe5bc0654]); + + let v: Vec = (0..SAMPLES).map(|_| Fq::rand(&mut rng)).collect(); + + let mut count = 0; + b.iter(|| { + count = (count + 1) % SAMPLES; + v[count].into_repr() + }); +} + +#[bench] +fn bench_fq_from_repr(b: &mut ::test::Bencher) { + const SAMPLES: usize = 1000; + + let mut rng = XorShiftRng::from_seed([0x5dbe6259, 0x8d313d76, 0x3237db17, 0xe5bc0654]); + + let v: Vec = (0..SAMPLES) + .map(|_| Fq::rand(&mut rng).into_repr()) + .collect(); + + let mut count = 0; + b.iter(|| { + count = (count + 1) % SAMPLES; + Fq::from_repr(v[count]) + }); +} diff --git a/benches/bn256/fq12.rs b/benches/bn256/fq12.rs new file mode 100644 index 0000000..42fca9d --- /dev/null +++ b/benches/bn256/fq12.rs @@ -0,0 +1,94 @@ +use rand::{Rand, SeedableRng, XorShiftRng}; + +use ff::Field; +use pairing::bn256::*; + +#[bench] +fn bench_fq12_add_assign(b: &mut ::test::Bencher) { + const SAMPLES: usize = 1000; + + let mut rng = XorShiftRng::from_seed([0x5dbe6259, 0x8d313d76, 0x3237db17, 0xe5bc0654]); + + let v: Vec<(Fq12, Fq12)> = (0..SAMPLES) + .map(|_| (Fq12::rand(&mut rng), Fq12::rand(&mut rng))) + .collect(); + + let mut count = 0; + b.iter(|| { + let mut tmp = v[count].0; + tmp.add_assign(&v[count].1); + count = (count + 1) % SAMPLES; + tmp + }); +} + +#[bench] +fn bench_fq12_sub_assign(b: &mut ::test::Bencher) { + const SAMPLES: usize = 1000; + + let mut rng = XorShiftRng::from_seed([0x5dbe6259, 0x8d313d76, 0x3237db17, 0xe5bc0654]); + + let v: Vec<(Fq12, Fq12)> = (0..SAMPLES) + .map(|_| (Fq12::rand(&mut rng), Fq12::rand(&mut rng))) + .collect(); + + let mut count = 0; + b.iter(|| { + let mut tmp = v[count].0; + tmp.sub_assign(&v[count].1); + count = (count + 1) % SAMPLES; + tmp + }); +} + +#[bench] +fn bench_fq12_mul_assign(b: &mut ::test::Bencher) { + const SAMPLES: usize = 1000; + + let mut rng = XorShiftRng::from_seed([0x5dbe6259, 0x8d313d76, 0x3237db17, 0xe5bc0654]); + + let v: Vec<(Fq12, Fq12)> = (0..SAMPLES) + .map(|_| (Fq12::rand(&mut rng), Fq12::rand(&mut rng))) + .collect(); + + let mut count = 0; + b.iter(|| { + let mut tmp = v[count].0; + tmp.mul_assign(&v[count].1); + count = (count + 1) % SAMPLES; + tmp + }); +} + +#[bench] +fn bench_fq12_squaring(b: &mut ::test::Bencher) { + const SAMPLES: usize = 1000; + + let mut rng = XorShiftRng::from_seed([0x5dbe6259, 0x8d313d76, 0x3237db17, 0xe5bc0654]); + + let v: Vec = (0..SAMPLES).map(|_| Fq12::rand(&mut rng)).collect(); + + let mut count = 0; + b.iter(|| { + let mut tmp = v[count]; + tmp.square(); + count = (count + 1) % SAMPLES; + tmp + }); +} + +#[bench] +fn bench_fq12_inverse(b: &mut ::test::Bencher) { + const SAMPLES: usize = 1000; + + let mut rng = XorShiftRng::from_seed([0x5dbe6259, 0x8d313d76, 0x3237db17, 0xe5bc0654]); + + let v: Vec = (0..SAMPLES).map(|_| Fq12::rand(&mut rng)).collect(); + + let mut count = 0; + b.iter(|| { + let tmp = v[count].inverse(); + count = (count + 1) % SAMPLES; + tmp + }); +} diff --git a/benches/bn256/fq2.rs b/benches/bn256/fq2.rs new file mode 100644 index 0000000..ee592ca --- /dev/null +++ b/benches/bn256/fq2.rs @@ -0,0 +1,110 @@ +use rand::{Rand, SeedableRng, XorShiftRng}; + +use ff::{Field, SqrtField}; +use pairing::bn256::*; + +#[bench] +fn bench_fq2_add_assign(b: &mut ::test::Bencher) { + const SAMPLES: usize = 1000; + + let mut rng = XorShiftRng::from_seed([0x5dbe6259, 0x8d313d76, 0x3237db17, 0xe5bc0654]); + + let v: Vec<(Fq2, Fq2)> = (0..SAMPLES) + .map(|_| (Fq2::rand(&mut rng), Fq2::rand(&mut rng))) + .collect(); + + let mut count = 0; + b.iter(|| { + let mut tmp = v[count].0; + tmp.add_assign(&v[count].1); + count = (count + 1) % SAMPLES; + tmp + }); +} + +#[bench] +fn bench_fq2_sub_assign(b: &mut ::test::Bencher) { + const SAMPLES: usize = 1000; + + let mut rng = XorShiftRng::from_seed([0x5dbe6259, 0x8d313d76, 0x3237db17, 0xe5bc0654]); + + let v: Vec<(Fq2, Fq2)> = (0..SAMPLES) + .map(|_| (Fq2::rand(&mut rng), Fq2::rand(&mut rng))) + .collect(); + + let mut count = 0; + b.iter(|| { + let mut tmp = v[count].0; + tmp.sub_assign(&v[count].1); + count = (count + 1) % SAMPLES; + tmp + }); +} + +#[bench] +fn bench_fq2_mul_assign(b: &mut ::test::Bencher) { + const SAMPLES: usize = 1000; + + let mut rng = XorShiftRng::from_seed([0x5dbe6259, 0x8d313d76, 0x3237db17, 0xe5bc0654]); + + let v: Vec<(Fq2, Fq2)> = (0..SAMPLES) + .map(|_| (Fq2::rand(&mut rng), Fq2::rand(&mut rng))) + .collect(); + + let mut count = 0; + b.iter(|| { + let mut tmp = v[count].0; + tmp.mul_assign(&v[count].1); + count = (count + 1) % SAMPLES; + tmp + }); +} + +#[bench] +fn bench_fq2_squaring(b: &mut ::test::Bencher) { + const SAMPLES: usize = 1000; + + let mut rng = XorShiftRng::from_seed([0x5dbe6259, 0x8d313d76, 0x3237db17, 0xe5bc0654]); + + let v: Vec = (0..SAMPLES).map(|_| Fq2::rand(&mut rng)).collect(); + + let mut count = 0; + b.iter(|| { + let mut tmp = v[count]; + tmp.square(); + count = (count + 1) % SAMPLES; + tmp + }); +} + +#[bench] +fn bench_fq2_inverse(b: &mut ::test::Bencher) { + const SAMPLES: usize = 1000; + + let mut rng = XorShiftRng::from_seed([0x5dbe6259, 0x8d313d76, 0x3237db17, 0xe5bc0654]); + + let v: Vec = (0..SAMPLES).map(|_| Fq2::rand(&mut rng)).collect(); + + let mut count = 0; + b.iter(|| { + let tmp = v[count].inverse(); + count = (count + 1) % SAMPLES; + tmp + }); +} + +#[bench] +fn bench_fq2_sqrt(b: &mut ::test::Bencher) { + const SAMPLES: usize = 1000; + + let mut rng = XorShiftRng::from_seed([0x5dbe6259, 0x8d313d76, 0x3237db17, 0xe5bc0654]); + + let v: Vec = (0..SAMPLES).map(|_| Fq2::rand(&mut rng)).collect(); + + let mut count = 0; + b.iter(|| { + let tmp = v[count].sqrt(); + count = (count + 1) % SAMPLES; + tmp + }); +} diff --git a/benches/bn256/fr.rs b/benches/bn256/fr.rs new file mode 100644 index 0000000..62f0fe7 --- /dev/null +++ b/benches/bn256/fr.rs @@ -0,0 +1,268 @@ +use rand::{Rand, SeedableRng, XorShiftRng}; + +use ff::{Field, PrimeField, PrimeFieldRepr, SqrtField}; +use pairing::bn256::*; + +#[bench] +fn bench_fr_repr_add_nocarry(b: &mut ::test::Bencher) { + const SAMPLES: usize = 1000; + + let mut rng = XorShiftRng::from_seed([0x5dbe6259, 0x8d313d76, 0x3237db17, 0xe5bc0654]); + + let v: Vec<(FrRepr, FrRepr)> = (0..SAMPLES) + .map(|_| { + let mut tmp1 = FrRepr::rand(&mut rng); + let mut tmp2 = FrRepr::rand(&mut rng); + // Shave a few bits off to avoid overflow. + for _ in 0..3 { + tmp1.div2(); + tmp2.div2(); + } + (tmp1, tmp2) + }) + .collect(); + + let mut count = 0; + b.iter(|| { + let mut tmp = v[count].0; + tmp.add_nocarry(&v[count].1); + count = (count + 1) % SAMPLES; + tmp + }); +} + +#[bench] +fn bench_fr_repr_sub_noborrow(b: &mut ::test::Bencher) { + const SAMPLES: usize = 1000; + + let mut rng = XorShiftRng::from_seed([0x5dbe6259, 0x8d313d76, 0x3237db17, 0xe5bc0654]); + + let v: Vec<(FrRepr, FrRepr)> = (0..SAMPLES) + .map(|_| { + let tmp1 = FrRepr::rand(&mut rng); + let mut tmp2 = tmp1; + // Ensure tmp2 is smaller than tmp1. + for _ in 0..10 { + tmp2.div2(); + } + (tmp1, tmp2) + }) + .collect(); + + let mut count = 0; + b.iter(|| { + let mut tmp = v[count].0; + tmp.sub_noborrow(&v[count].1); + count = (count + 1) % SAMPLES; + tmp + }); +} + +#[bench] +fn bench_fr_repr_num_bits(b: &mut ::test::Bencher) { + const SAMPLES: usize = 1000; + + let mut rng = XorShiftRng::from_seed([0x5dbe6259, 0x8d313d76, 0x3237db17, 0xe5bc0654]); + + let v: Vec = (0..SAMPLES).map(|_| FrRepr::rand(&mut rng)).collect(); + + let mut count = 0; + b.iter(|| { + let tmp = v[count].num_bits(); + count = (count + 1) % SAMPLES; + tmp + }); +} + +#[bench] +fn bench_fr_repr_mul2(b: &mut ::test::Bencher) { + const SAMPLES: usize = 1000; + + let mut rng = XorShiftRng::from_seed([0x5dbe6259, 0x8d313d76, 0x3237db17, 0xe5bc0654]); + + let v: Vec = (0..SAMPLES).map(|_| FrRepr::rand(&mut rng)).collect(); + + let mut count = 0; + b.iter(|| { + let mut tmp = v[count]; + tmp.mul2(); + count = (count + 1) % SAMPLES; + tmp + }); +} + +#[bench] +fn bench_fr_repr_div2(b: &mut ::test::Bencher) { + const SAMPLES: usize = 1000; + + let mut rng = XorShiftRng::from_seed([0x5dbe6259, 0x8d313d76, 0x3237db17, 0xe5bc0654]); + + let v: Vec = (0..SAMPLES).map(|_| FrRepr::rand(&mut rng)).collect(); + + let mut count = 0; + b.iter(|| { + let mut tmp = v[count]; + tmp.div2(); + count = (count + 1) % SAMPLES; + tmp + }); +} + +#[bench] +fn bench_fr_add_assign(b: &mut ::test::Bencher) { + const SAMPLES: usize = 1000; + + let mut rng = XorShiftRng::from_seed([0x5dbe6259, 0x8d313d76, 0x3237db17, 0xe5bc0654]); + + let v: Vec<(Fr, Fr)> = (0..SAMPLES) + .map(|_| (Fr::rand(&mut rng), Fr::rand(&mut rng))) + .collect(); + + let mut count = 0; + b.iter(|| { + let mut tmp = v[count].0; + tmp.add_assign(&v[count].1); + count = (count + 1) % SAMPLES; + tmp + }); +} + +#[bench] +fn bench_fr_sub_assign(b: &mut ::test::Bencher) { + const SAMPLES: usize = 1000; + + let mut rng = XorShiftRng::from_seed([0x5dbe6259, 0x8d313d76, 0x3237db17, 0xe5bc0654]); + + let v: Vec<(Fr, Fr)> = (0..SAMPLES) + .map(|_| (Fr::rand(&mut rng), Fr::rand(&mut rng))) + .collect(); + + let mut count = 0; + b.iter(|| { + let mut tmp = v[count].0; + tmp.sub_assign(&v[count].1); + count = (count + 1) % SAMPLES; + tmp + }); +} + +#[bench] +fn bench_fr_mul_assign(b: &mut ::test::Bencher) { + const SAMPLES: usize = 1000; + + let mut rng = XorShiftRng::from_seed([0x5dbe6259, 0x8d313d76, 0x3237db17, 0xe5bc0654]); + + let v: Vec<(Fr, Fr)> = (0..SAMPLES) + .map(|_| (Fr::rand(&mut rng), Fr::rand(&mut rng))) + .collect(); + + let mut count = 0; + b.iter(|| { + let mut tmp = v[count].0; + tmp.mul_assign(&v[count].1); + count = (count + 1) % SAMPLES; + tmp + }); +} + +#[bench] +fn bench_fr_square(b: &mut ::test::Bencher) { + const SAMPLES: usize = 1000; + + let mut rng = XorShiftRng::from_seed([0x5dbe6259, 0x8d313d76, 0x3237db17, 0xe5bc0654]); + + let v: Vec = (0..SAMPLES).map(|_| Fr::rand(&mut rng)).collect(); + + let mut count = 0; + b.iter(|| { + let mut tmp = v[count]; + tmp.square(); + count = (count + 1) % SAMPLES; + tmp + }); +} + +#[bench] +fn bench_fr_inverse(b: &mut ::test::Bencher) { + const SAMPLES: usize = 1000; + + let mut rng = XorShiftRng::from_seed([0x5dbe6259, 0x8d313d76, 0x3237db17, 0xe5bc0654]); + + let v: Vec = (0..SAMPLES).map(|_| Fr::rand(&mut rng)).collect(); + + let mut count = 0; + b.iter(|| { + count = (count + 1) % SAMPLES; + v[count].inverse() + }); +} + +#[bench] +fn bench_fr_negate(b: &mut ::test::Bencher) { + const SAMPLES: usize = 1000; + + let mut rng = XorShiftRng::from_seed([0x5dbe6259, 0x8d313d76, 0x3237db17, 0xe5bc0654]); + + let v: Vec = (0..SAMPLES).map(|_| Fr::rand(&mut rng)).collect(); + + let mut count = 0; + b.iter(|| { + let mut tmp = v[count]; + tmp.negate(); + count = (count + 1) % SAMPLES; + tmp + }); +} + +#[bench] +fn bench_fr_sqrt(b: &mut ::test::Bencher) { + const SAMPLES: usize = 1000; + + let mut rng = XorShiftRng::from_seed([0x5dbe6259, 0x8d313d76, 0x3237db17, 0xe5bc0654]); + + let v: Vec = (0..SAMPLES) + .map(|_| { + let mut tmp = Fr::rand(&mut rng); + tmp.square(); + tmp + }) + .collect(); + + let mut count = 0; + b.iter(|| { + count = (count + 1) % SAMPLES; + v[count].sqrt() + }); +} + +#[bench] +fn bench_fr_into_repr(b: &mut ::test::Bencher) { + const SAMPLES: usize = 1000; + + let mut rng = XorShiftRng::from_seed([0x5dbe6259, 0x8d313d76, 0x3237db17, 0xe5bc0654]); + + let v: Vec = (0..SAMPLES).map(|_| Fr::rand(&mut rng)).collect(); + + let mut count = 0; + b.iter(|| { + count = (count + 1) % SAMPLES; + v[count].into_repr() + }); +} + +#[bench] +fn bench_fr_from_repr(b: &mut ::test::Bencher) { + const SAMPLES: usize = 1000; + + let mut rng = XorShiftRng::from_seed([0x5dbe6259, 0x8d313d76, 0x3237db17, 0xe5bc0654]); + + let v: Vec = (0..SAMPLES) + .map(|_| Fr::rand(&mut rng).into_repr()) + .collect(); + + let mut count = 0; + b.iter(|| { + count = (count + 1) % SAMPLES; + Fr::from_repr(v[count]) + }); +} diff --git a/benches/bn256/mod.rs b/benches/bn256/mod.rs new file mode 100644 index 0000000..3559cca --- /dev/null +++ b/benches/bn256/mod.rs @@ -0,0 +1,107 @@ +mod ec; +mod fq; +mod fq12; +mod fq2; +mod fr; + +use rand::{Rand, SeedableRng, XorShiftRng}; + +use pairing::bn256::*; +use pairing::{CurveAffine, Engine}; + +#[bench] +fn bench_pairing_g1_preparation(b: &mut ::test::Bencher) { + const SAMPLES: usize = 1000; + + let mut rng = XorShiftRng::from_seed([0x5dbe6259, 0x8d313d76, 0x3237db17, 0xe5bc0654]); + + let v: Vec = (0..SAMPLES).map(|_| G1::rand(&mut rng)).collect(); + + let mut count = 0; + b.iter(|| { + let tmp = G1Affine::from(v[count]).prepare(); + count = (count + 1) % SAMPLES; + tmp + }); +} + +#[bench] +fn bench_pairing_g2_preparation(b: &mut ::test::Bencher) { + const SAMPLES: usize = 1000; + + let mut rng = XorShiftRng::from_seed([0x5dbe6259, 0x8d313d76, 0x3237db17, 0xe5bc0654]); + + let v: Vec = (0..SAMPLES).map(|_| G2::rand(&mut rng)).collect(); + + let mut count = 0; + b.iter(|| { + let tmp = G2Affine::from(v[count]).prepare(); + count = (count + 1) % SAMPLES; + tmp + }); +} + +#[bench] +fn bench_pairing_miller_loop(b: &mut ::test::Bencher) { + const SAMPLES: usize = 1000; + + let mut rng = XorShiftRng::from_seed([0x5dbe6259, 0x8d313d76, 0x3237db17, 0xe5bc0654]); + + let v: Vec<(G1Prepared, G2Prepared)> = (0..SAMPLES) + .map(|_| { + ( + G1Affine::from(G1::rand(&mut rng)).prepare(), + G2Affine::from(G2::rand(&mut rng)).prepare(), + ) + }) + .collect(); + + let mut count = 0; + b.iter(|| { + let tmp = Bn256::miller_loop(&[(&v[count].0, &v[count].1)]); + count = (count + 1) % SAMPLES; + tmp + }); +} + +#[bench] +fn bench_pairing_final_exponentiation(b: &mut ::test::Bencher) { + const SAMPLES: usize = 1000; + + let mut rng = XorShiftRng::from_seed([0x5dbe6259, 0x8d313d76, 0x3237db17, 0xe5bc0654]); + + let v: Vec = (0..SAMPLES) + .map(|_| { + ( + G1Affine::from(G1::rand(&mut rng)).prepare(), + G2Affine::from(G2::rand(&mut rng)).prepare(), + ) + }) + .map(|(ref p, ref q)| Bn256::miller_loop(&[(p, q)])) + .collect(); + + let mut count = 0; + b.iter(|| { + let tmp = Bn256::final_exponentiation(&v[count]); + count = (count + 1) % SAMPLES; + tmp + }); +} + +#[bench] +fn bench_pairing_full(b: &mut ::test::Bencher) { + const SAMPLES: usize = 1000; + + let mut rng = XorShiftRng::from_seed([0x5dbe6259, 0x8d313d76, 0x3237db17, 0xe5bc0654]); + + let v: Vec<(G1, G2)> = (0..SAMPLES) + .map(|_| (G1::rand(&mut rng), G2::rand(&mut rng))) + .collect(); + + let mut count = 0; + b.iter(|| { + let tmp = Bn256::pairing(v[count].0, v[count].1); + count = (count + 1) % SAMPLES; + tmp + }); +} diff --git a/benches/pairing_benches.rs b/benches/pairing_benches.rs index af32a8a..ddfd04d 100644 --- a/benches/pairing_benches.rs +++ b/benches/pairing_benches.rs @@ -6,3 +6,4 @@ extern crate rand; extern crate test; mod bls12_381; +mod bn256; diff --git a/src/bls12_381/ec.rs b/src/bls12_381/ec.rs index 37fcbba..5c0545f 100644 --- a/src/bls12_381/ec.rs +++ b/src/bls12_381/ec.rs @@ -1262,6 +1262,7 @@ pub mod g1 { #[test] fn g1_curve_tests() { ::tests::curve::curve_tests::(); + ::tests::curve::random_transformation_tests_with_cofactor::(); } } @@ -2015,6 +2016,7 @@ pub mod g2 { #[test] fn g2_curve_tests() { ::tests::curve::curve_tests::(); + ::tests::curve::random_transformation_tests_with_cofactor::(); } } diff --git a/src/bn256/README.md b/src/bn256/README.md new file mode 100644 index 0000000..8d6b211 --- /dev/null +++ b/src/bn256/README.md @@ -0,0 +1,14 @@ +# BN256 + +This is an implementation of the BN256 pairing-friendly elliptic curve construction. + +## BN256 Parameterization + +Follows go-ethereum parametrization. + +## Notes + +- I couldn't find an easy wat of getting random G2 for BN256 curve (also have no idea why just scaling by cofactor works for BLS12), so don't use it. Make random sccalar and multiply by generator. +- For this reason tests had to be copied and modified for some cases. + + diff --git a/src/bn256/ec.rs b/src/bn256/ec.rs new file mode 100644 index 0000000..13342d5 --- /dev/null +++ b/src/bn256/ec.rs @@ -0,0 +1,1598 @@ +macro_rules! curve_impl { + ( + $name:expr, + $projective:ident, + $affine:ident, + $prepared:ident, + $basefield:ident, + $scalarfield:ident, + $uncompressed:ident, + $compressed:ident, + $pairing:ident + ) => { + #[derive(Copy, Clone, PartialEq, Eq, Debug)] + pub struct $affine { + pub(crate) x: $basefield, + pub(crate) y: $basefield, + pub(crate) infinity: bool + } + + impl ::std::fmt::Display for $affine + { + fn fmt(&self, f: &mut ::std::fmt::Formatter) -> ::std::fmt::Result { + if self.infinity { + write!(f, "{}(Infinity)", $name) + } else { + write!(f, "{}(x={}, y={})", $name, self.x, self.y) + } + } + } + + #[derive(Copy, Clone, Debug, Eq)] + pub struct $projective { + pub(crate) x: $basefield, + pub(crate) y: $basefield, + pub(crate) z: $basefield + } + + impl ::std::fmt::Display for $projective + { + fn fmt(&self, f: &mut ::std::fmt::Formatter) -> ::std::fmt::Result { + write!(f, "{}", self.into_affine()) + } + } + + impl PartialEq for $projective { + fn eq(&self, other: &$projective) -> bool { + if self.is_zero() { + return other.is_zero(); + } + + if other.is_zero() { + return false; + } + + // 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). + + let mut z1 = self.z; + z1.square(); + let mut z2 = other.z; + z2.square(); + + let mut tmp1 = self.x; + tmp1.mul_assign(&z2); + + let mut tmp2 = other.x; + tmp2.mul_assign(&z1); + + if tmp1 != tmp2 { + return false; + } + + z1.mul_assign(&self.z); + z2.mul_assign(&other.z); + z2.mul_assign(&self.y); + z1.mul_assign(&other.y); + + if z1 != z2 { + return false; + } + + true + } + } + + impl $affine { + fn mul_bits>(&self, bits: BitIterator) -> $projective { + let mut res = $projective::zero(); + for i in bits { + res.double(); + if i { res.add_assign_mixed(self) } + } + res + } + + /// Attempts to construct an affine point given an x-coordinate. The + /// point is not guaranteed to be in the prime order subgroup. + /// + /// If and only if `greatest` is set will the lexicographically + /// largest y-coordinate be selected. + fn get_point_from_x(x: $basefield, greatest: bool) -> Option<$affine> { + // Compute x^3 + b + let mut x3b = x; + x3b.square(); + x3b.mul_assign(&x); + x3b.add_assign(&$affine::get_coeff_b()); + + x3b.sqrt().map(|y| { + let mut negy = y; + negy.negate(); + + $affine { + x: x, + y: if (y < negy) ^ greatest { + y + } else { + negy + }, + infinity: false + } + }) + } + + fn is_on_curve(&self) -> bool { + if self.is_zero() { + true + } else { + // Check that the point is on the curve + 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 + } + } + + } + + impl CurveAffine for $affine { + type Engine = Bn256; + type Scalar = $scalarfield; + type Base = $basefield; + type Prepared = $prepared; + type Projective = $projective; + type Uncompressed = $uncompressed; + type Compressed = $compressed; + type Pair = $pairing; + type PairingResult = Fq12; + + fn zero() -> Self { + $affine { + x: $basefield::zero(), + y: $basefield::one(), + infinity: true + } + } + + fn one() -> Self { + Self::get_generator() + } + + fn is_zero(&self) -> bool { + self.infinity + } + + fn mul::Repr>>(&self, by: S) -> $projective { + let bits = BitIterator::new(by.into()); + self.mul_bits(bits) + } + + fn negate(&mut self) { + if !self.is_zero() { + self.y.negate(); + } + } + + fn prepare(&self) -> Self::Prepared { + $prepared::from_affine(*self) + } + + fn pairing_with(&self, other: &Self::Pair) -> Self::PairingResult { + self.perform_pairing(other) + } + + fn into_projective(&self) -> $projective { + (*self).into() + } + + } + + // impl Rand for $projective { + // fn rand(rng: &mut R) -> Self { + // loop { + // let x = rng.gen(); + // let greatest = rng.gen(); + + // if let Some(p) = $affine::get_point_from_x(x, greatest) { + // if !p.is_zero() { + // // let mut q = p.into_projective(); + // // q.mul_assign($scalarfield::char()); && q.is_zero() + // if p.is_on_curve() { + // return p.into_projective(); + // } + // } + // } + // } + // } + // } + + impl CurveProjective for $projective { + type Engine = Bn256; + type Scalar = $scalarfield; + type Base = $basefield; + type Affine = $affine; + + // The point at infinity is always represented by + // Z = 0. + fn zero() -> Self { + $projective { + x: $basefield::zero(), + y: $basefield::one(), + z: $basefield::zero() + } + } + + fn one() -> Self { + $affine::one().into() + } + + // The point at infinity is always represented by + // Z = 0. + fn is_zero(&self) -> bool { + self.z.is_zero() + } + + fn is_normalized(&self) -> bool { + self.is_zero() || self.z == $basefield::one() + } + + fn batch_normalization(v: &mut [Self]) + { + // Montgomery’s Trick and Fast Implementation of Masked AES + // Genelle, Prouff and Quisquater + // Section 3.2 + + // First pass: compute [a, ab, abc, ...] + 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()) + { + tmp.mul_assign(&g.z); + prod.push(tmp); + } + + // Invert `tmp`. + tmp = tmp.inverse().unwrap(); // Guaranteed to be nonzero. + + // 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()))) + { + // tmp := tmp * g.z; g.z := tmp * s = 1/z + let mut newtmp = tmp; + newtmp.mul_assign(&g.z); + g.z = tmp; + g.z.mul_assign(&s); + tmp = newtmp; + } + + // Perform affine transformations + for g in v.iter_mut() + .filter(|g| !g.is_normalized()) + { + let mut z = g.z; // 1/z + z.square(); // 1/z^2 + g.x.mul_assign(&z); // x/z^2 + z.mul_assign(&g.z); // 1/z^3 + g.y.mul_assign(&z); // y/z^3 + g.z = $basefield::one(); // z = 1 + } + } + + fn double(&mut self) { + if self.is_zero() { + return; + } + + // Other than the point at infinity, no points on E or E' + // can double to equal the point at infinity, as y=0 is + // never true for points on the curve. + + // http://www.hyperelliptic.org/EFD/g1p/auto-shortw-jacobian-0.html#doubling-dbl-2009-l + + // A = X1^2 + let mut a = self.x; + a.square(); + + // B = Y1^2 + let mut b = self.y; + b.square(); + + // C = B^2 + let mut c = b; + c.square(); + + // D = 2*((X1+B)2-A-C) + let mut d = self.x; + d.add_assign(&b); + d.square(); + d.sub_assign(&a); + d.sub_assign(&c); + d.double(); + + // E = 3*A + let mut e = a; + e.double(); + e.add_assign(&a); + + // F = E^2 + let mut f = e; + f.square(); + + // Z3 = 2*Y1*Z1 + self.z.mul_assign(&self.y); + self.z.double(); + + // X3 = F-2*D + self.x = f; + self.x.sub_assign(&d); + self.x.sub_assign(&d); + + // Y3 = E*(D-X3)-8*C + self.y = d; + self.y.sub_assign(&self.x); + self.y.mul_assign(&e); + c.double(); + c.double(); + c.double(); + self.y.sub_assign(&c); + } + + fn add_assign(&mut self, other: &Self) { + if self.is_zero() { + *self = *other; + return; + } + + if other.is_zero() { + return; + } + + // http://www.hyperelliptic.org/EFD/g1p/auto-shortw-jacobian-0.html#addition-add-2007-bl + + // Z1Z1 = Z1^2 + let mut z1z1 = self.z; + z1z1.square(); + + // Z2Z2 = Z2^2 + let mut z2z2 = other.z; + z2z2.square(); + + // U1 = X1*Z2Z2 + let mut u1 = self.x; + u1.mul_assign(&z2z2); + + // U2 = X2*Z1Z1 + let mut u2 = other.x; + u2.mul_assign(&z1z1); + + // S1 = Y1*Z2*Z2Z2 + let mut s1 = self.y; + s1.mul_assign(&other.z); + s1.mul_assign(&z2z2); + + // S2 = Y2*Z1*Z1Z1 + let mut s2 = other.y; + s2.mul_assign(&self.z); + s2.mul_assign(&z1z1); + + if u1 == u2 && s1 == s2 { + // The two points are equal, so we double. + self.double(); + } else { + // If we're adding -a and a together, self.z becomes zero as H becomes zero. + + if u1 == u2 { + // The two points are equal, so we double. + (*self) = Self::zero(); + return; + } + + // H = U2-U1 + let mut h = u2; + h.sub_assign(&u1); + + // I = (2*H)^2 + let mut i = h; + i.double(); + i.square(); + + // J = H*I + let mut j = h; + j.mul_assign(&i); + + // r = 2*(S2-S1) + let mut r = s2; + r.sub_assign(&s1); + r.double(); + + // V = U1*I + let mut v = u1; + v.mul_assign(&i); + + // X3 = r^2 - J - 2*V + self.x = r; + self.x.square(); + self.x.sub_assign(&j); + self.x.sub_assign(&v); + self.x.sub_assign(&v); + + // Y3 = r*(V - X3) - 2*S1*J + self.y = v; + self.y.sub_assign(&self.x); + self.y.mul_assign(&r); + s1.mul_assign(&j); // S1 = S1 * J * 2 + s1.double(); + self.y.sub_assign(&s1); + + // Z3 = ((Z1+Z2)^2 - Z1Z1 - Z2Z2)*H + self.z.add_assign(&other.z); + self.z.square(); + self.z.sub_assign(&z1z1); + self.z.sub_assign(&z2z2); + self.z.mul_assign(&h); + } + } + + fn add_assign_mixed(&mut self, other: &Self::Affine) { + if other.is_zero() { + return; + } + + if self.is_zero() { + self.x = other.x; + self.y = other.y; + self.z = $basefield::one(); + return; + } + + // http://www.hyperelliptic.org/EFD/g1p/auto-shortw-jacobian-0.html#addition-madd-2007-bl + + // Z1Z1 = Z1^2 + let mut z1z1 = self.z; + z1z1.square(); + + // U2 = X2*Z1Z1 + let mut u2 = other.x; + u2.mul_assign(&z1z1); + + // S2 = Y2*Z1*Z1Z1 + let mut s2 = other.y; + s2.mul_assign(&self.z); + s2.mul_assign(&z1z1); + + if self.x == u2 && self.y == s2 { + // The two points are equal, so we double. + self.double(); + } else { + // If we're adding -a and a together, self.z becomes zero as H becomes zero. + + // H = U2-X1 + let mut h = u2; + h.sub_assign(&self.x); + + // HH = H^2 + let mut hh = h; + hh.square(); + + // I = 4*HH + let mut i = hh; + i.double(); + i.double(); + + // J = H*I + let mut j = h; + j.mul_assign(&i); + + // r = 2*(S2-Y1) + let mut r = s2; + r.sub_assign(&self.y); + r.double(); + + // V = X1*I + let mut v = self.x; + v.mul_assign(&i); + + // X3 = r^2 - J - 2*V + self.x = r; + self.x.square(); + self.x.sub_assign(&j); + self.x.sub_assign(&v); + self.x.sub_assign(&v); + + // Y3 = r*(V-X3)-2*Y1*J + j.mul_assign(&self.y); // J = 2*Y1*J + j.double(); + self.y = v; + self.y.sub_assign(&self.x); + self.y.mul_assign(&r); + self.y.sub_assign(&j); + + // Z3 = (Z1+H)^2-Z1Z1-HH + self.z.add_assign(&h); + self.z.square(); + self.z.sub_assign(&z1z1); + self.z.sub_assign(&hh); + } + } + + fn negate(&mut self) { + if !self.is_zero() { + self.y.negate() + } + } + + fn mul_assign::Repr>>(&mut self, other: S) { + let mut res = Self::zero(); + + let mut found_one = false; + + for i in BitIterator::new(other.into()) + { + if found_one { + res.double(); + } else { + found_one = i; + } + + if i { + res.add_assign(self); + } + } + + *self = res; + } + + fn into_affine(&self) -> $affine { + (*self).into() + } + + fn recommended_wnaf_for_scalar(scalar: ::Repr) -> usize { + Self::empirical_recommended_wnaf_for_scalar(scalar) + } + + fn recommended_wnaf_for_num_scalars(num_scalars: usize) -> usize { + Self::empirical_recommended_wnaf_for_num_scalars(num_scalars) + } + } + + // The affine point X, Y is represented in the jacobian + // coordinates with Z = 1. + impl From<$affine> for $projective { + fn from(p: $affine) -> $projective { + if p.is_zero() { + $projective::zero() + } else { + $projective { + x: p.x, + y: p.y, + z: $basefield::one() + } + } + } + } + + // The projective point X, Y, Z is represented in the affine + // coordinates as X/Z^2, Y/Z^3. + impl From<$projective> for $affine { + fn from(p: $projective) -> $affine { + if p.is_zero() { + $affine::zero() + } else if p.z == $basefield::one() { + // If Z is one, the point is already normalized. + $affine { + x: p.x, + y: p.y, + infinity: false + } + } else { + // Z is nonzero, so it must have an inverse in a field. + let zinv = p.z.inverse().unwrap(); + let mut zinv_powered = zinv; + zinv_powered.square(); + + // X/Z^2 + let mut x = p.x; + x.mul_assign(&zinv_powered); + + // Y/Z^3 + let mut y = p.y; + zinv_powered.mul_assign(&zinv); + y.mul_assign(&zinv_powered); + + $affine { + x: x, + y: y, + infinity: false + } + } + } + } + } +} + +pub mod g1 { + use super::super::{Bn256, Fq, Fq12, FqRepr, Fr, FrRepr}; + use super::g2::G2Affine; + use ff::{BitIterator, Field, PrimeField, PrimeFieldRepr, SqrtField}; + use rand::{Rand, Rng}; + use std::fmt; + use {CurveAffine, CurveProjective, EncodedPoint, Engine, GroupDecodingError}; + + curve_impl!( + "G1", + G1, + G1Affine, + G1Prepared, + Fq, + Fr, + G1Uncompressed, + G1Compressed, + G2Affine + ); + + #[derive(Copy, Clone)] + pub struct G1Uncompressed([u8; 64]); + + impl Rand for G1 { + fn rand(rng: &mut R) -> Self { + loop { + let x = rng.gen(); + let greatest = rng.gen(); + + if let Some(p) = G1Affine::get_point_from_x(x, greatest) { + if !p.is_zero() { + if p.is_on_curve() { + return p.into_projective(); + } + } + } + } + } + } + + impl Rand for G1Affine { + fn rand(rng: &mut R) -> Self { + loop { + let x = rng.gen(); + let greatest = rng.gen(); + + if let Some(p) = G1Affine::get_point_from_x(x, greatest) { + if !p.is_zero() { + if p.is_on_curve() { + return p; + } + } + } + } + } + } + + 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 + } + } + + impl fmt::Debug for G1Uncompressed { + fn fmt(&self, formatter: &mut fmt::Formatter) -> Result<(), fmt::Error> { + self.0[..].fmt(formatter) + } + } + + impl EncodedPoint for G1Uncompressed { + type Affine = G1Affine; + + fn empty() -> Self { + G1Uncompressed([0; 64]) + } + fn size() -> usize { + 64 + } + fn into_affine(&self) -> Result { + let affine = self.into_affine_unchecked()?; + + if !affine.is_on_curve() { + Err(GroupDecodingError::NotOnCurve) + } else { + Ok(affine) + } + } + fn into_affine_unchecked(&self) -> Result { + // Create a copy of this representation. + let mut copy = self.0; + + if copy[0] & (1 << 6) != 0 { + // This is the point at infinity, which means that if we mask away + // the first two bits, the entire representation should consist + // of zeroes. + copy[0] &= 0x3f; + + if copy.iter().all(|b| *b == 0) { + Ok(G1Affine::zero()) + } else { + Err(GroupDecodingError::UnexpectedInformation) + } + } else { + if copy[0] & (1 << 7) != 0 { + // The bit indicating the y-coordinate should be lexicographically + // largest is set, but this is an uncompressed element. + return Err(GroupDecodingError::UnexpectedInformation); + } + + // Unset the two most significant bits. + copy[0] &= 0x3f; + + let mut x = FqRepr([0; 4]); + let mut y = FqRepr([0; 4]); + + { + let mut reader = ©[..]; + + x.read_be(&mut reader).unwrap(); + y.read_be(&mut reader).unwrap(); + } + + Ok(G1Affine { + x: Fq::from_repr(x).map_err(|e| { + GroupDecodingError::CoordinateDecodingError("x coordinate", e) + })?, + y: Fq::from_repr(y).map_err(|e| { + GroupDecodingError::CoordinateDecodingError("y coordinate", e) + })?, + infinity: false, + }) + } + } + fn from_affine(affine: G1Affine) -> Self { + let mut res = Self::empty(); + + if affine.is_zero() { + // Set the second-most significant bit to indicate this point + // is at infinity. + res.0[0] |= 1 << 6; + } else { + let mut writer = &mut res.0[..]; + + affine.x.into_repr().write_be(&mut writer).unwrap(); + affine.y.into_repr().write_be(&mut writer).unwrap(); + } + + res + } + } + + #[derive(Copy, Clone)] + pub struct G1Compressed([u8; 32]); + + 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 + } + } + + impl fmt::Debug for G1Compressed { + fn fmt(&self, formatter: &mut fmt::Formatter) -> Result<(), fmt::Error> { + self.0[..].fmt(formatter) + } + } + + impl EncodedPoint for G1Compressed { + type Affine = G1Affine; + + fn empty() -> Self { + G1Compressed([0; 32]) + } + fn size() -> usize { + 32 + } + fn into_affine(&self) -> Result { + let affine = self.into_affine_unchecked()?; + + // NB: Decompression guarantees that it is on the curve already. + + Ok(affine) + } + fn into_affine_unchecked(&self) -> Result { + // Create a copy of this representation. + let mut copy = self.0; + + if copy[0] & (1 << 6) != 0 { + // This is the point at infinity, which means that if we mask away + // the first two bits, the entire representation should consist + // of zeroes. + copy[0] &= 0x3f; + + if copy.iter().all(|b| *b == 0) { + Ok(G1Affine::zero()) + } else { + Err(GroupDecodingError::UnexpectedInformation) + } + } else { + // Determine if the intended y coordinate must be greater + // lexicographically. + let greatest = copy[0] & (1 << 7) != 0; + + // Unset the two most significant bits. + copy[0] &= 0x3f; + + let mut x = FqRepr([0; 4]); + + { + let mut reader = ©[..]; + + x.read_be(&mut reader).unwrap(); + } + + // Interpret as Fq element. + let x = Fq::from_repr(x) + .map_err(|e| GroupDecodingError::CoordinateDecodingError("x coordinate", e))?; + + G1Affine::get_point_from_x(x, greatest).ok_or(GroupDecodingError::NotOnCurve) + } + } + fn from_affine(affine: G1Affine) -> Self { + let mut res = Self::empty(); + + if affine.is_zero() { + // Set the second-most significant bit to indicate this point + // is at infinity. + res.0[0] |= 1 << 6; + } else { + { + let mut writer = &mut res.0[..]; + + affine.x.into_repr().write_be(&mut writer).unwrap(); + } + + let mut negy = affine.y; + negy.negate(); + + // Set the third most significant bit if the correct y-coordinate + // is lexicographically largest. + if affine.y > negy { + res.0[0] |= 1 << 7; + } + } + + res + } + } + + impl G1Affine { + fn get_generator() -> Self { + G1Affine { + x: super::super::fq::G1_GENERATOR_X, + y: super::super::fq::G1_GENERATOR_Y, + infinity: false, + } + } + + fn get_coeff_b() -> Fq { + super::super::fq::B_COEFF + } + + fn perform_pairing(&self, other: &G2Affine) -> Fq12 { + super::super::Bn256::pairing(*self, *other) + } + } + + impl G1 { + fn empirical_recommended_wnaf_for_scalar(scalar: FrRepr) -> usize { + let num_bits = scalar.num_bits() as usize; + + if num_bits >= 130 { + 4 + } else if num_bits >= 34 { + 3 + } else { + 2 + } + } + + fn empirical_recommended_wnaf_for_num_scalars(num_scalars: usize) -> usize { + const RECOMMENDATIONS: [usize; 12] = + [1, 3, 7, 20, 43, 120, 273, 563, 1630, 3128, 7933, 62569]; + + let mut ret = 4; + for r in &RECOMMENDATIONS { + if num_scalars > *r { + ret += 1; + } else { + break; + } + } + + ret + } + } + + #[derive(Clone, Debug)] + pub struct G1Prepared(pub(crate) G1Affine); + + impl G1Prepared { + pub fn is_zero(&self) -> bool { + self.0.is_zero() + } + + pub fn from_affine(p: G1Affine) -> Self { + G1Prepared(p) + } + } + + #[test] + fn g1_generator() { + use SqrtField; + + let mut x = Fq::zero(); + let mut i = 0; + loop { + // y^2 = x^3 + b + let mut rhs = x; + rhs.square(); + rhs.mul_assign(&x); + rhs.add_assign(&G1Affine::get_coeff_b()); + + if let Some(y) = rhs.sqrt() { + let yrepr = y.into_repr(); + let mut negy = y; + negy.negate(); + let negyrepr = negy.into_repr(); + + let p = G1Affine { + x: x, + y: if yrepr < negyrepr { y } else { negy }, + infinity: false, + }; + + let g1 = p.into_projective(); + if !g1.is_zero() { + assert_eq!(i, 1); + let g1 = G1Affine::from(g1); + + assert_eq!(g1, G1Affine::one()); + break; + } + } + + i += 1; + x.add_assign(&Fq::one()); + } + } + + #[test] + + fn test_base_point_addition_and_doubling() { + let mut a = G1::one(); + print!("{}\n\n", a); + + a.add_assign(&G1::one()); + + print!("{}\n\n", a); + } + + #[test] + fn g1_curve_tests() { + ::tests::curve::curve_tests::(); + ::tests::curve::random_transformation_tests::(); + } +} + +pub mod g2 { + use super::super::{Bn256, Fq, Fq12, Fq2, FqRepr, Fr, FrRepr}; + use super::g1::G1Affine; + use ff::{BitIterator, Field, PrimeField, PrimeFieldRepr, SqrtField}; + use rand::{Rand, Rng}; + use std::fmt; + use {CurveAffine, CurveProjective, EncodedPoint, Engine, GroupDecodingError}; + + curve_impl!( + "G2", + G2, + G2Affine, + G2Prepared, + Fq2, + Fr, + G2Uncompressed, + G2Compressed, + G1Affine + ); + + impl Rand for G2 { + fn rand(rng: &mut R) -> Self { + let mut r = G2::one(); + let k = Fr::rand(rng); + r.mul_assign(k); + return r; + } + } + + impl Rand for G2Affine { + fn rand(rng: &mut R) -> Self { + let mut r = G2::one(); + let k = Fr::rand(rng); + r.mul_assign(k); + return r.into_affine(); + } + } + + #[derive(Copy, Clone)] + pub struct G2Uncompressed([u8; 128]); + + 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 + } + } + + impl fmt::Debug for G2Uncompressed { + fn fmt(&self, formatter: &mut fmt::Formatter) -> Result<(), fmt::Error> { + self.0[..].fmt(formatter) + } + } + + impl EncodedPoint for G2Uncompressed { + type Affine = G2Affine; + + fn empty() -> Self { + G2Uncompressed([0; 128]) + } + fn size() -> usize { + 128 + } + fn into_affine(&self) -> Result { + let affine = self.into_affine_unchecked()?; + + if !affine.is_on_curve() { + Err(GroupDecodingError::NotOnCurve) + } else { + Ok(affine) + } + } + fn into_affine_unchecked(&self) -> Result { + // Create a copy of this representation. + let mut copy = self.0; + + if copy[0] & (1 << 7) != 0 { + // Distinguisher bit is set, but this should be uncompressed! + return Err(GroupDecodingError::UnexpectedCompressionMode); + } + + if copy[0] & (1 << 6) != 0 { + // This is the point at infinity, which means that if we mask away + // the first two bits, the entire representation should consist + // of zeroes. + copy[0] &= 0x3f; + + if copy.iter().all(|b| *b == 0) { + Ok(G2Affine::zero()) + } else { + Err(GroupDecodingError::UnexpectedInformation) + } + } else { + + // Unset the two most significant bits. + copy[0] &= 0x3f; + + let mut x_c0 = FqRepr([0; 4]); + let mut x_c1 = FqRepr([0; 4]); + let mut y_c0 = FqRepr([0; 4]); + let mut y_c1 = FqRepr([0; 4]); + + { + let mut reader = ©[..]; + + x_c1.read_be(&mut reader).unwrap(); + x_c0.read_be(&mut reader).unwrap(); + y_c1.read_be(&mut reader).unwrap(); + y_c0.read_be(&mut reader).unwrap(); + } + + Ok(G2Affine { + x: Fq2 { + c0: Fq::from_repr(x_c0).map_err(|e| { + GroupDecodingError::CoordinateDecodingError("x coordinate (c0)", e) + })?, + c1: Fq::from_repr(x_c1).map_err(|e| { + GroupDecodingError::CoordinateDecodingError("x coordinate (c1)", e) + })?, + }, + y: Fq2 { + c0: Fq::from_repr(y_c0).map_err(|e| { + GroupDecodingError::CoordinateDecodingError("y coordinate (c0)", e) + })?, + c1: Fq::from_repr(y_c1).map_err(|e| { + GroupDecodingError::CoordinateDecodingError("y coordinate (c1)", e) + })?, + }, + infinity: false, + }) + } + } + fn from_affine(affine: G2Affine) -> Self { + let mut res = Self::empty(); + + if affine.is_zero() { + // Set the second-most significant bit to indicate this point + // is at infinity. + res.0[0] |= 1 << 6; + } else { + let mut writer = &mut res.0[..]; + + affine.x.c1.into_repr().write_be(&mut writer).unwrap(); + affine.x.c0.into_repr().write_be(&mut writer).unwrap(); + affine.y.c1.into_repr().write_be(&mut writer).unwrap(); + affine.y.c0.into_repr().write_be(&mut writer).unwrap(); + } + + res + } + } + + #[derive(Copy, Clone)] + pub struct G2Compressed([u8; 64]); + + 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 + } + } + + impl fmt::Debug for G2Compressed { + fn fmt(&self, formatter: &mut fmt::Formatter) -> Result<(), fmt::Error> { + self.0[..].fmt(formatter) + } + } + + impl EncodedPoint for G2Compressed { + type Affine = G2Affine; + + fn empty() -> Self { + G2Compressed([0; 64]) + } + fn size() -> usize { + 64 + } + fn into_affine(&self) -> Result { + let affine = self.into_affine_unchecked()?; + + // NB: Decompression guarantees that it is on the curve already. + + Ok(affine) + } + fn into_affine_unchecked(&self) -> Result { + // Create a copy of this representation. + let mut copy = self.0; + + if copy[0] & (1 << 6) != 0 { + // This is the point at infinity, which means that if we mask away + // the first two bits, the entire representation should consist + // of zeroes. + copy[0] &= 0x3f; + + if copy.iter().all(|b| *b == 0) { + Ok(G2Affine::zero()) + } else { + Err(GroupDecodingError::UnexpectedInformation) + } + } else { + // Determine if the intended y coordinate must be greater + // lexicographically. + let greatest = copy[0] & (1 << 7) != 0; + + // Unset the two most significant bits. + copy[0] &= 0x3f; + + let mut x_c1 = FqRepr([0; 4]); + let mut x_c0 = FqRepr([0; 4]); + + { + let mut reader = ©[..]; + + x_c1.read_be(&mut reader).unwrap(); + x_c0.read_be(&mut reader).unwrap(); + } + + // Interpret as Fq element. + let x = Fq2 { + c0: Fq::from_repr(x_c0).map_err(|e| { + GroupDecodingError::CoordinateDecodingError("x coordinate (c0)", e) + })?, + c1: Fq::from_repr(x_c1).map_err(|e| { + GroupDecodingError::CoordinateDecodingError("x coordinate (c1)", e) + })?, + }; + + G2Affine::get_point_from_x(x, greatest).ok_or(GroupDecodingError::NotOnCurve) + } + } + fn from_affine(affine: G2Affine) -> Self { + let mut res = Self::empty(); + + if affine.is_zero() { + // Set the second-most significant bit to indicate this point + // is at infinity. + res.0[0] |= 1 << 6; + } else { + { + let mut writer = &mut res.0[..]; + + affine.x.c1.into_repr().write_be(&mut writer).unwrap(); + affine.x.c0.into_repr().write_be(&mut writer).unwrap(); + } + + let mut negy = affine.y; + negy.negate(); + + // Set the third most significant bit if the correct y-coordinate + // is lexicographically largest. + if affine.y > negy { + res.0[0] |= 1 << 7; + } + } + + res + } + } + + impl G2Affine { + fn get_generator() -> Self { + G2Affine { + x: Fq2 { + c0: super::super::fq::G2_GENERATOR_X_C0, + c1: super::super::fq::G2_GENERATOR_X_C1, + }, + y: Fq2 { + c0: super::super::fq::G2_GENERATOR_Y_C0, + c1: super::super::fq::G2_GENERATOR_Y_C1, + }, + infinity: false, + } + } + + fn get_coeff_b() -> Fq2 { + super::super::fq::B_COEFF_FQ2 + // Fq2 { + // c0: super::super::fq::B_COEFF, + // c1: super::super::fq::B_COEFF, + // } + } + + fn perform_pairing(&self, other: &G1Affine) -> Fq12 { + super::super::Bn256::pairing(*other, *self) + } + } + + impl G2 { + fn empirical_recommended_wnaf_for_scalar(scalar: FrRepr) -> usize { + let num_bits = scalar.num_bits() as usize; + + if num_bits >= 103 { + 4 + } else if num_bits >= 37 { + 3 + } else { + 2 + } + } + + fn empirical_recommended_wnaf_for_num_scalars(num_scalars: usize) -> usize { + const RECOMMENDATIONS: [usize; 11] = + [1, 3, 8, 20, 47, 126, 260, 826, 1501, 4555, 84071]; + + let mut ret = 4; + for r in &RECOMMENDATIONS { + if num_scalars > *r { + ret += 1; + } else { + break; + } + } + + ret + } + } + + #[derive(Clone, Debug)] + pub struct G2Prepared { + pub(crate) coeffs: Vec<(Fq2, Fq2, Fq2)>, + pub(crate) infinity: bool, + } + + // This generator does not take a random element in Fp2 + // and tries to increment it to be on a curve, but + // generates a random scalar and multiplies predefined generator by it + + #[test] + fn g2_generator() { + use SqrtField; + + let mut x = Fq2::zero(); + let mut i = 0; + loop { + // y^2 = x^3 + b + let mut rhs = x; + rhs.square(); + rhs.mul_assign(&x); + rhs.add_assign(&G2Affine::get_coeff_b()); + + if let Some(y) = rhs.sqrt() { + let mut negy = y; + negy.negate(); + + let p = G2Affine { + x: x, + y: if y < negy { y } else { negy }, + infinity: false, + }; + + + let g2 = p.into_projective(); + if !g2.is_zero() { + assert_eq!(i, 0); + let g2 = G2Affine::from(g2); + + assert_eq!(g2, G2Affine::one()); + break; + } + } + + i += 1; + x.add_assign(&Fq2::one()); + } + } + + #[cfg(test)] + use rand::{SeedableRng, XorShiftRng}; + + #[test] + fn g2_generator_on_curve() { + use SqrtField; + + let gen = G2Affine::get_generator(); + let x = gen.x; + // y^2 = x^3 + 3/xi + let mut rhs = x; + rhs.square(); + rhs.mul_assign(&x); + rhs.add_assign(&G2Affine::get_coeff_b()); + + if let Some(y) = rhs.sqrt() { + let mut negy = y; + negy.negate(); + + let p = G2Affine { + x: x, + y: if y < negy { y } else { negy }, + infinity: false, + }; + + assert_eq!(p.y, gen.y); + assert_eq!(p, G2Affine::one()); + return; + } + panic!(); + } + + #[test] + fn g2_curve_tests() { + ::tests::curve::curve_tests::(); + ::tests::curve::random_transformation_tests::(); + } + + #[test] + + fn test_b_coeff() { + let b2 = G2Affine::get_coeff_b(); + print!("{}\n\n", b2); + } + + #[test] + + fn test_base_point_addition_and_doubling() { + let mut two = G2::one(); + two.add_assign(&G2::one()); + + let one = G2::one(); + + let mut three21 = two; + three21.add_assign(&one); + + let mut three12 = one; + three12.add_assign(&two); + + assert_eq!(three12, three21); + } + + #[test] + fn test_addition_and_doubling() { + + let mut rng = XorShiftRng::from_seed([0x5dbe6259, 0x8d313d76, 0x3237db17, 0xe5bc0654]); + + for _ in 0..1000 { + let a = G2::rand(&mut rng); + assert!(a.into_affine().is_on_curve()); + let b = G2::rand(&mut rng); + let c = G2::rand(&mut rng); + let a_affine = a.into_affine(); + let b_affine = b.into_affine(); + let c_affine = c.into_affine(); + + // a + a should equal the doubling + { + let mut aplusa = a; + aplusa.add_assign(&a); + + let mut aplusamixed = a; + aplusamixed.add_assign_mixed(&a.into_affine()); + + let mut adouble = a; + adouble.double(); + + assert_eq!(aplusa, adouble); + assert_eq!(aplusamixed, adouble); + } + + let mut ab = a; + ab.add_assign(&b); + + let mut ba = b; + ba.add_assign(&a); + + assert_eq!(ab, ba, "Addition should not depend on order"); + + let mut tmp = vec![G2::zero(); 6]; + + // (a + b) + c + tmp[0] = a; + tmp[0].add_assign(&b); + tmp[0].add_assign(&c); + + // a + (b + c) + tmp[1] = b; + tmp[1].add_assign(&c); + tmp[1].add_assign(&a); + + // (a + c) + b + tmp[2] = a; + tmp[2].add_assign(&c); + tmp[2].add_assign(&b); + + // Mixed addition + + // (a + b) + c + tmp[3] = a_affine.into_projective(); + tmp[3].add_assign_mixed(&b_affine); + tmp[3].add_assign_mixed(&c_affine); + + // a + (b + c) + tmp[4] = b_affine.into_projective(); + tmp[4].add_assign_mixed(&c_affine); + tmp[4].add_assign_mixed(&a_affine); + + // (a + c) + b + tmp[5] = a_affine.into_projective(); + tmp[5].add_assign_mixed(&c_affine); + tmp[5].add_assign_mixed(&b_affine); + + // Comparisons + for i in 0..6 { + for j in 0..6 { + assert_eq!(tmp[i], tmp[j]); + assert_eq!(tmp[i].into_affine(), tmp[j].into_affine()); + } + + assert!(tmp[i] != a); + assert!(tmp[i] != b); + assert!(tmp[i] != c); + + assert!(a != tmp[i]); + assert!(b != tmp[i]); + assert!(c != tmp[i]); + } + + } + } + + #[test] + fn random_negation_tests() { + let mut rng = XorShiftRng::from_seed([0x5dbe6259, 0x8d313d76, 0x3237db17, 0xe5bc0654]); + + for _ in 0..1000 { + // let r = G2::rand(&mut rng); + // assert!(r.into_affine().is_on_curve()); + + let mut r = G2::one(); + let k = Fr::rand(&mut rng); + r.mul_assign(k); + + let s = Fr::rand(&mut rng); + let mut sneg = s; + sneg.negate(); + + let mut t1 = r; + t1.mul_assign(s); + + let mut t2 = r; + t2.mul_assign(sneg); + + let mut t3 = t1; + t3.add_assign(&t2); + assert!(t3.is_zero()); + + let mut t4 = t1; + t4.add_assign_mixed(&t2.into_affine()); + assert!(t4.is_zero()); + + t1.negate(); + assert_eq!(t1, t2); + } + } + + #[test] + fn mul_by_order_tests() { + let mut rng = XorShiftRng::from_seed([0x5dbe6259, 0x8d313d76, 0x3237db17, 0xe5bc0654]); + + for _ in 0..1000 { + // let r = G2::rand(&mut rng); + + let mut r = G2::one(); + let k = Fr::rand(&mut rng); + r.mul_assign(k); + + let order = Fr::char(); + + let mut q = G2::one(); + q.mul_assign(order); + assert!(q.is_zero()); + + r.mul_assign(order); + assert!(r.is_zero()); + + // let mut t = G2::rand(&mut rng); + // t.mul_assign(order); + // assert!(t.is_zero()); + } + } + +} + +pub use self::g1::*; +pub use self::g2::*; diff --git a/src/bn256/fq.rs b/src/bn256/fq.rs new file mode 100644 index 0000000..19e55d3 --- /dev/null +++ b/src/bn256/fq.rs @@ -0,0 +1,579 @@ +use super::fq2::Fq2; +use ff::{Field, PrimeField, PrimeFieldDecodingError, PrimeFieldRepr}; + +#[derive(PrimeField)] +#[PrimeFieldModulus = "21888242871839275222246405745257275088696311157297823662689037894645226208583"] +#[PrimeFieldGenerator = "2"] +pub struct Fq(FqRepr); + +// B coefficient of BN256 curve, B = 3 +// In Montgommery form with R = 2^256 +pub const B_COEFF: Fq = Fq(FqRepr([ + 0x7a17caa950ad28d7, + 0x1f6ac17ae15521b9, + 0x334bea4e696bd284, + 0x2a1f6744ce179d8e, +])); + +pub const B_COEFF_FQ2: Fq2 = Fq2 { + c0: Fq(FqRepr([ + 0x3bf938e377b802a8, + 0x020b1b273633535d, + 0x26b7edf049755260, + 0x2514c6324384a86d, + ])), + c1: Fq(FqRepr([ + 0x38e7ecccd1dcff67, + 0x65f0b37d93ce0d3e, + 0xd749d0dd22ac00aa, + 0x0141b9ce4a688d4d, + ])), +}; + + +// The generators of G1/G2 + +// Generator of G1 +// x = 1 +// y = 2 +pub const G1_GENERATOR_X: Fq = Fq(FqRepr([ + 0xd35d438dc58f0d9d, + 0x0a78eb28f5c70b3d, + 0x666ea36f7879462c, + 0x0e0a77c19a07df2f, +])); +pub const G1_GENERATOR_Y: Fq = Fq(FqRepr([ + 0xa6ba871b8b1e1b3a, + 0x14f1d651eb8e167b, + 0xccdd46def0f28c58, + 0x1c14ef83340fbe5e, +])); + +// Generator of G2 +// +// x = 11559732032986387107991004021392285783925812861821192530917403151452391805634*u +// + 10857046999023057135944570762232829481370756359578518086990519993285655852781 +// +// y = 4082367875863433681332203403145435568316851327593401208105741076214120093531*u +// + 8495653923123431417604973247489272438418190587263600148770280649306958101930 + +pub const G2_GENERATOR_X_C0: Fq = Fq(FqRepr([ + 0x8e83b5d102bc2026, + 0xdceb1935497b0172, + 0xfbb8264797811adf, + 0x19573841af96503b, +])); +pub const G2_GENERATOR_X_C1: Fq = Fq(FqRepr([ + 0xafb4737da84c6140, + 0x6043dd5a5802d8c4, + 0x09e950fc52a02f86, + 0x14fef0833aea7b6b, +])); +pub const G2_GENERATOR_Y_C0: Fq = Fq(FqRepr([ + 0x619dfa9d886be9f6, + 0xfe7fd297f59e9b78, + 0xff9e1a62231b7dfe, + 0x28fd7eebae9e4206, +])); +pub const G2_GENERATOR_Y_C1: Fq = Fq(FqRepr([ + 0x64095b56c71856ee, + 0xdc57f922327d3cbb, + 0x55f935be33351076, + 0x0da4a0e693fd6482, +])); + + +// Coefficients for the Frobenius automorphism. +pub const FROBENIUS_COEFF_FQ2_C1: [Fq; 2] = [ + // Fq(-1)**(((q^0) - 1) / 2) + // it's 1 in Montgommery form + Fq(FqRepr([ + 0xd35d438dc58f0d9d, + 0x0a78eb28f5c70b3d, + 0x666ea36f7879462c, + 0x0e0a77c19a07df2f, + ])), + // Fq(-1)**(((q^1) - 1) / 2) + Fq(FqRepr([ + 0x68c3488912edefaa, + 0x8d087f6872aabf4f, + 0x51e1a24709081231, + 0x2259d6b14729c0fa, + ])), +]; + + // Fq2(u + 9)**(((q^1) - 1) / 2) +pub const XI_TO_Q_MINUS_1_OVER_2: Fq2 = Fq2 { + c0: Fq(FqRepr([ + 0xe4bbdd0c2936b629, + 0xbb30f162e133bacb, + 0x31a9d1b6f9645366, + 0x253570bea500f8dd, + ])), + c1: Fq(FqRepr([ + 0xa1d77ce45ffe77c7, + 0x07affd117826d1db, + 0x6d16bd27bb7edc6b, + 0x2c87200285defecc, + ])), +}; + +pub const FROBENIUS_COEFF_FQ6_C1: [Fq2; 6] = [ + // Fq2(u + 9)**(((q^0) - 1) / 3) + Fq2 { + c0: Fq(FqRepr([ + 0xd35d438dc58f0d9d, + 0x0a78eb28f5c70b3d, + 0x666ea36f7879462c, + 0x0e0a77c19a07df2f, + ])), + c1: Fq(FqRepr([0x0, 0x0, 0x0, 0x0])), + }, + // Fq2(u + 9)**(((q^1) - 1) / 3) + // taken from go-ethereum and also re-calculated manually + Fq2 { + c0: Fq(FqRepr([ + 0xb5773b104563ab30, + 0x347f91c8a9aa6454, + 0x7a007127242e0991, + 0x1956bcd8118214ec, + ])), + c1: Fq(FqRepr([ + 0x6e849f1ea0aa4757, + 0xaa1c7b6d89f89141, + 0xb6e713cdfae0ca3a, + 0x26694fbb4e82ebc3, + ])), + }, + // Fq2(u + 9)**(((q^2) - 1) / 3) + // this one and other below are recalculated manually + Fq2 { + c0: Fq(FqRepr([ + 0x3350c88e13e80b9c, + 0x7dce557cdb5e56b9, + 0x6001b4b8b615564a, + 0x2682e617020217e0, + ])), + c1: Fq(FqRepr([0x0, 0x0, 0x0, 0x0])), + }, + // Fq2(u + 9)**(((q^3) - 1) / 3) + Fq2 { + c0: Fq(FqRepr([ + 0xc9af22f716ad6bad, + 0xb311782a4aa662b2, + 0x19eeaf64e248c7f4, + 0x20273e77e3439f82, + ])), + c1: Fq(FqRepr([ + 0xacc02860f7ce93ac, + 0x3933d5817ba76b4c, + 0x69e6188b446c8467, + 0x0a46036d4417cc55, + ])), + }, + // Fq2(u + 9)**(((q^4) - 1) / 3) + Fq2 { + c0: Fq(FqRepr([ + 0x71930c11d782e155, + 0xa6bb947cffbe3323, + 0xaa303344d4741444, + 0x2c3b3f0d26594943, + ])), + c1: Fq(FqRepr([0x0, 0x0, 0x0, 0x0,])), + }, + // Fq2(u + 9)**(((q^5) - 1) / 3) + Fq2 { + c0: Fq(FqRepr([ + 0xf91aba2654e8e3b1, + 0x4771cb2fdc92ce12, + 0xdcb16ae0fc8bdf35, + 0x274aa195cd9d8be4, + ])), + c1: Fq(FqRepr([ + 0x5cfc50ae18811f8b, + 0x4bb28433cb43988c, + 0x4fd35f13c3b56219, + 0x301949bd2fc8883a, + ])), + }, +]; + +pub const FROBENIUS_COEFF_FQ6_C2: [Fq2; 6] = [ + // Fq2(u + 1)**(((2q^0) - 2) / 3) + Fq2 { + c0: Fq(FqRepr([ + 0xd35d438dc58f0d9d, + 0x0a78eb28f5c70b3d, + 0x666ea36f7879462c, + 0x0e0a77c19a07df2f, + ])), + c1: Fq(FqRepr([0x0, 0x0, 0x0, 0x0])), + }, + // Fq2(u + 1)**(((2q^1) - 2) / 3) + Fq2 { + c0: Fq(FqRepr([ + 0x7361d77f843abe92, + 0xa5bb2bd3273411fb, + 0x9c941f314b3e2399, + 0x15df9cddbb9fd3ec, + ])), + c1: Fq(FqRepr([ + 0x5dddfd154bd8c949, + 0x62cb29a5a4445b60, + 0x37bc870a0c7dd2b9, + 0x24830a9d3171f0fd, + ])), + }, + // Fq2(u + 1)**(((2q^2) - 2) / 3) + Fq2 { + c0: Fq(FqRepr([ + 0x71930c11d782e155, + 0xa6bb947cffbe3323, + 0xaa303344d4741444, + 0x2c3b3f0d26594943, + ])), + c1: Fq(FqRepr([0x0, 0x0, 0x0, 0x0,])), + }, + // Fq2(u + 1)**(((2q^3) - 2) / 3) + Fq2 { + c0: Fq(FqRepr([ + 0x448a93a57b6762df, + 0xbfd62df528fdeadf, + 0xd858f5d00e9bd47a, + 0x06b03d4d3476ec58, + ])), + c1: Fq(FqRepr([ + 0x2b19daf4bcc936d1, + 0xa1a54e7a56f4299f, + 0xb533eee05adeaef1, + 0x170c812b84dda0b2, + ])), + }, + // Fq2(u + 1)**(((2q^4) - 2) / 3) + Fq2 { + c0: Fq(FqRepr([ + 0x3350c88e13e80b9c, + 0x7dce557cdb5e56b9, + 0x6001b4b8b615564a, + 0x2682e617020217e0, + ])), + c1: Fq(FqRepr([0x0, 0x0, 0x0, 0x0,])), + }, + // Fq2(u + 1)**(((2q^5) - 2) / 3) + Fq2 { + c0: Fq(FqRepr([ + 0x843420f1d8dadbd6, + 0x31f010c9183fcdb2, + 0x436330b527a76049, + 0x13d47447f11adfe4, + ])), + c1: Fq(FqRepr([ + 0xef494023a857fa74, + 0x2a925d02d5ab101a, + 0x83b015829ba62f10, + 0x2539111d0c13aea3, + ])), + }, +]; + +// non_residue^((modulus^i-1)/6) for i=0,...,11 +pub const FROBENIUS_COEFF_FQ12_C1: [Fq2; 12] = [ + // Fq2(u + 1)**(((q^0) - 1) / 6) + Fq2 { + c0: Fq(FqRepr([ + 0xd35d438dc58f0d9d, + 0x0a78eb28f5c70b3d, + 0x666ea36f7879462c, + 0x0e0a77c19a07df2f, + ])), + c1: Fq(FqRepr([0x0, 0x0, 0x0, 0x0])), + }, + // Fq2(u + 1)**(((q^1) - 1) / 6) + Fq2 { + c0: Fq(FqRepr([ + 0xaf9ba69633144907, + 0xca6b1d7387afb78a, + 0x11bded5ef08a2087, + 0x02f34d751a1f3a7c, + ])), + c1: Fq(FqRepr([ + 0xa222ae234c492d72, + 0xd00f02a4565de15b, + 0xdc2ff3a253dfc926, + 0x10a75716b3899551, + ])), + }, + // Fq2(u + 1)**(((q^2) - 1) / 6) + Fq2 { + c0: Fq(FqRepr([ + 0xca8d800500fa1bf2, + 0xf0c5d61468b39769, + 0x0e201271ad0d4418, + 0x04290f65bad856e6, + ])), + c1: Fq(FqRepr([0x0, 0x0, 0x0, 0x0])), + }, + // Fq2(u + 1)**(((q^3) - 1) / 6) + Fq2 { + c0: Fq(FqRepr([ + 0x365316184e46d97d, + 0x0af7129ed4c96d9f, + 0x659da72fca1009b5, + 0x08116d8983a20d23, + ])), + c1: Fq(FqRepr([ + 0xb1df4af7c39c1939, + 0x3d9f02878a73bf7f, + 0x9b2220928caf0ae0, + 0x26684515eff054a6, + ])), + }, + // Fq2(u + 1)**(((q^4) - 1) / 6) + Fq2 { + c0: Fq(FqRepr([ + 0x3350c88e13e80b9c, + 0x7dce557cdb5e56b9, + 0x6001b4b8b615564a, + 0x2682e617020217e0, + ])), + c1: Fq(FqRepr([0x0, 0x0, 0x0, 0x0,])), + }, + // Fq2(u + 1)**(((q^5) - 1) / 6) + Fq2 { + c0: Fq(FqRepr([ + 0x86b76f821b329076, + 0x408bf52b4d19b614, + 0x53dfb9d0d985e92d, + 0x051e20146982d2a7, + ])), + c1: Fq(FqRepr([ + 0x0fbc9cd47752ebc7, + 0x6d8fffe33415de24, + 0xbef22cf038cf41b9, + 0x15c0edff3c66bf54, + ])), + }, + // Fq2(u + 1)**(((q^6) - 1) / 6) + Fq2 { + c0: Fq(FqRepr([ + 0x68c3488912edefaa, + 0x8d087f6872aabf4f, + 0x51e1a24709081231, + 0x2259d6b14729c0fa, + ])), + c1: Fq(FqRepr([0x0, 0x0, 0x0, 0x0,])), + }, + // Fq2(u + 1)**(((q^7) - 1) / 6) + Fq2 { + c0: Fq(FqRepr([ + 0x8c84e580a568b440, + 0xcd164d1de0c21302, + 0xa692585790f737d5, + 0x2d7100fdc71265ad, + ])), + c1: Fq(FqRepr([ + 0x99fdddf38c33cfd5, + 0xc77267ed1213e931, + 0xdc2052142da18f36, + 0x1fbcf75c2da80ad7, + ])), + }, + // Fq2(u + 1)**(((q^8) - 1) / 6) + Fq2 { + c0: Fq(FqRepr([ + 0x71930c11d782e155, + 0xa6bb947cffbe3323, + 0xaa303344d4741444, + 0x2c3b3f0d26594943, + ])), + c1: Fq(FqRepr([0x0, 0x0, 0x0, 0x0,])), + }, + // Fq2(u + 1)**(((q^9) - 1) / 6) + Fq2 { + c0: Fq(FqRepr([ + 0x05cd75fe8a3623ca, + 0x8c8a57f293a85cee, + 0x52b29e86b7714ea8, + 0x2852e0e95d8f9306, + ])), + c1: Fq(FqRepr([ + 0x8a41411f14e0e40e, + 0x59e26809ddfe0b0d, + 0x1d2e2523f4d24d7d, + 0x09fc095cf1414b83, + ])), + }, + // Fq2(u + 1)**(((q^10) - 1) / 6) + Fq2 { + c0: Fq(FqRepr([ + 0x08cfc388c494f1ab, + 0x19b315148d1373d4, + 0x584e90fdcb6c0213, + 0x09e1685bdf2f8849, + ])), + c1: Fq(FqRepr([0x0, 0x0, 0x0, 0x0,])), + }, + // Fq2(u + 1)**(((q^11) - 1) / 6) + Fq2 { + c0: Fq(FqRepr([ + 0xb5691c94bd4a6cd1, + 0x56f575661b581478, + 0x64708be5a7fb6f30, + 0x2b462e5e77aecd82, + ])), + c1: Fq(FqRepr([ + 0x2c63ef42612a1180, + 0x29f16aae345bec69, + 0xf95e18c648b216a4, + 0x1aa36073a4cae0d4, + ])), + }, +]; + +// -((2**256) mod q) mod q +pub const NEGATIVE_ONE: Fq = Fq(FqRepr([ + 0x974bc177a0000006, + 0xf13771b2da58a367, + 0x51e1a2470908122e, + 0x2259d6b14729c0fa, +])); + +#[cfg(test)] +use rand::{Rand, SeedableRng, XorShiftRng}; + +#[test] +fn test_fq_repr_from() { + assert_eq!(FqRepr::from(100), FqRepr([100, 0, 0, 0])); + assert_eq!(FqRepr::from(3), FqRepr([3, 0, 0, 0])); +} + +#[test] +fn test_fq_repr_is_odd() { + assert!(!FqRepr::from(0).is_odd()); + assert!(FqRepr::from(0).is_even()); + assert!(FqRepr::from(1).is_odd()); + assert!(!FqRepr::from(1).is_even()); + assert!(!FqRepr::from(324834872).is_odd()); + assert!(FqRepr::from(324834872).is_even()); + assert!(FqRepr::from(324834873).is_odd()); + assert!(!FqRepr::from(324834873).is_even()); +} + +#[test] +fn test_fq_repr_num_bits() { + let mut a = FqRepr::from(0); + assert_eq!(0, a.num_bits()); + a = FqRepr::from(1); + for i in 1..257 { + assert_eq!(i, a.num_bits()); + a.mul2(); + } + assert_eq!(0, a.num_bits()); +} + +#[test] +fn test_fq_is_valid() { + print!("modulus = {}\n", MODULUS); + print!("R = {}\n", R); + let mut a = Fq(MODULUS); + assert!(!a.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 + ])).is_valid() + ); + assert!( + !Fq(FqRepr([ + 0xffffffffffffffff, + 0xffffffffffffffff, + 0xffffffffffffffff, + 0xffffffffffffffff, + ])).is_valid() + ); + + let mut rng = XorShiftRng::from_seed([0x5dbe6259, 0x8d313d76, 0x3237db17, 0xe5bc0654]); + + for _ in 0..1000 { + let a = Fq::rand(&mut rng); + assert!(a.is_valid()); + } +} + +#[test] +fn test_fq_repr_display() { + assert_eq!( + format!("{}", Fq::into_repr(&Fq::one())), + "0x0000000000000000000000000000000000000000000000000000000000000001".to_string() + ); + assert_eq!( + format!("{}", FqRepr([0, 0, 0, 0])), + "0x0000000000000000000000000000000000000000000000000000000000000000".to_string() + ); +} + +#[test] +fn test_fq_num_bits() { + assert_eq!(Fq::NUM_BITS, 254); + assert_eq!(Fq::CAPACITY, 253); +} + +#[test] +fn test_fq_sqrt() { + use ff::SqrtField; + + let mut rng = XorShiftRng::from_seed([0x5dbe6259, 0x8d313d76, 0x3237db17, 0xe5bc0654]); + + assert_eq!(Fq::zero().sqrt().unwrap(), Fq::zero()); + + for _ in 0..1000 { + // Ensure sqrt(a^2) = a or -a + let a = Fq::rand(&mut rng); + let mut nega = a; + nega.negate(); + let mut b = a; + b.square(); + + let b = b.sqrt().unwrap(); + + assert!(a == b || nega == b); + } + + for _ in 0..1000 { + // Ensure sqrt(a)^2 = a for random a + let a = Fq::rand(&mut rng); + + if let Some(mut tmp) = a.sqrt() { + tmp.square(); + + assert_eq!(a, tmp); + } + } +} + +#[test] +fn test_fq_sqrt_2() { + use ff::SqrtField; + + let x = Fq::from_str("4").unwrap(); + print!("x = {}\n", x); + if let Some(y) = x.sqrt() { + print!("y = {}\n", y); + let mut y_other = y; + y_other.negate(); + print!("y' = {}\n", y_other); + } +} + +#[test] +fn fq_field_tests() { + ::tests::field::random_field_tests::(); + ::tests::field::random_sqrt_tests::(); + ::tests::field::random_frobenius_tests::(Fq::char(), 13); + ::tests::field::from_str_tests::(); +} \ No newline at end of file diff --git a/src/bn256/fq12.rs b/src/bn256/fq12.rs new file mode 100644 index 0000000..67fe6cb --- /dev/null +++ b/src/bn256/fq12.rs @@ -0,0 +1,221 @@ +use super::fq::FROBENIUS_COEFF_FQ12_C1; +use super::fq2::Fq2; +use super::fq6::Fq6; +use ff::Field; +use rand::{Rand, Rng}; + +/// An element of Fq12, represented by c0 + c1 * w. +#[derive(Copy, Clone, Debug, Eq, PartialEq)] +pub struct Fq12 { + pub c0: Fq6, + pub c1: Fq6, +} + +impl ::std::fmt::Display for Fq12 { + fn fmt(&self, f: &mut ::std::fmt::Formatter) -> ::std::fmt::Result { + write!(f, "Fq12({} + {} * w)", self.c0, self.c1) + } +} + +impl Rand for Fq12 { + fn rand(rng: &mut R) -> Self { + Fq12 { + c0: rng.gen(), + c1: rng.gen(), + } + } +} + +// BN256 and BLS12 implementations should be the same +// Defined over w^2 - v = 0 + +impl Fq12 { + pub fn conjugate(&mut self) { + self.c1.negate(); + } + + pub fn mul_by_014(&mut self, c0: &Fq2, c1: &Fq2, c4: &Fq2) { + let mut aa = self.c0; + aa.mul_by_01(c0, c1); + let mut bb = self.c1; + bb.mul_by_1(c4); + let mut o = *c1; + o.add_assign(c4); + self.c1.add_assign(&self.c0); + self.c1.mul_by_01(c0, &o); + self.c1.sub_assign(&aa); + self.c1.sub_assign(&bb); + self.c0 = bb; + self.c0.mul_by_nonresidue(); + self.c0.add_assign(&aa); + } + // TODO make it hand optimized + // // multiply by (c0, c1, c2) + (c3, c4, c5)*w where only c0, c3 and c4 are non-zero + pub fn mul_by_034(&mut self, c0: &Fq2, c3: &Fq2, c4: &Fq2) { + self.mul_assign(&Fq12 { + c0: Fq6 { + c0: *c0, + c1: Fq2::zero(), + c2: Fq2::zero(), + }, + c1: Fq6 { + c0: *c3, + c1: *c4, + c2: Fq2::zero(), + }, + }); + } +} + +impl Field for Fq12 { + fn zero() -> Self { + Fq12 { + c0: Fq6::zero(), + c1: Fq6::zero(), + } + } + + fn one() -> Self { + Fq12 { + c0: Fq6::one(), + c1: Fq6::zero(), + } + } + + fn is_zero(&self) -> bool { + self.c0.is_zero() && self.c1.is_zero() + } + + fn double(&mut self) { + self.c0.double(); + self.c1.double(); + } + + fn negate(&mut self) { + self.c0.negate(); + self.c1.negate(); + } + + fn add_assign(&mut self, other: &Self) { + self.c0.add_assign(&other.c0); + self.c1.add_assign(&other.c1); + } + + fn sub_assign(&mut self, other: &Self) { + self.c0.sub_assign(&other.c0); + self.c1.sub_assign(&other.c1); + } + + fn frobenius_map(&mut self, power: usize) { + self.c0.frobenius_map(power); + self.c1.frobenius_map(power); + + self.c1.c0.mul_assign(&FROBENIUS_COEFF_FQ12_C1[power % 12]); + self.c1.c1.mul_assign(&FROBENIUS_COEFF_FQ12_C1[power % 12]); + self.c1.c2.mul_assign(&FROBENIUS_COEFF_FQ12_C1[power % 12]); + } + + fn square(&mut self) { + let mut ab = self.c0; + ab.mul_assign(&self.c1); + let mut c0c1 = self.c0; + c0c1.add_assign(&self.c1); + let mut c0 = self.c1; + c0.mul_by_nonresidue(); + c0.add_assign(&self.c0); + c0.mul_assign(&c0c1); + c0.sub_assign(&ab); + self.c1 = ab; + self.c1.add_assign(&ab); + ab.mul_by_nonresidue(); + c0.sub_assign(&ab); + self.c0 = c0; + } + + fn mul_assign(&mut self, other: &Self) { + let mut aa = self.c0; + aa.mul_assign(&other.c0); + let mut bb = self.c1; + bb.mul_assign(&other.c1); + let mut o = other.c0; + o.add_assign(&other.c1); + self.c1.add_assign(&self.c0); + self.c1.mul_assign(&o); + self.c1.sub_assign(&aa); + self.c1.sub_assign(&bb); + self.c0 = bb; + self.c0.mul_by_nonresidue(); + self.c0.add_assign(&aa); + } + + fn inverse(&self) -> Option { + let mut c0s = self.c0; + c0s.square(); + let mut c1s = self.c1; + c1s.square(); + c1s.mul_by_nonresidue(); + c0s.sub_assign(&c1s); + + c0s.inverse().map(|t| { + let mut tmp = Fq12 { c0: t, c1: t }; + tmp.c0.mul_assign(&self.c0); + tmp.c1.mul_assign(&self.c1); + tmp.c1.negate(); + + tmp + }) + } +} + +#[cfg(test)] +use rand::{SeedableRng, XorShiftRng}; + +#[test] +fn test_fq12_mul_by_014() { + let mut rng = XorShiftRng::from_seed([0x5dbe6259, 0x8d313d76, 0x3237db17, 0xe5bc0654]); + + for _ in 0..1000 { + let c0 = Fq2::rand(&mut rng); + let c1 = Fq2::rand(&mut rng); + let c5 = Fq2::rand(&mut rng); + let mut a = Fq12::rand(&mut rng); + let mut b = a; + + a.mul_by_014(&c0, &c1, &c5); + b.mul_assign(&Fq12 { + c0: Fq6 { + c0: c0, + c1: c1, + c2: Fq2::zero(), + }, + c1: Fq6 { + c0: Fq2::zero(), + c1: c5, + c2: Fq2::zero(), + }, + }); + + assert_eq!(a, b); + } +} + +#[test] +fn test_squaring() { + let mut rng = XorShiftRng::from_seed([0x5dbe6259, 0x8d313d76, 0x3237db17, 0xe5bc0654]); + + for _ in 0..1000 { + let mut a = Fq12::rand(&mut rng); + let mut b = a; + b.mul_assign(&a); + a.square(); + assert_eq!(a, b); + } +} + +#[test] +fn fq12_field_tests() { + use ff::PrimeField; + + ::tests::field::random_field_tests::(); + ::tests::field::random_frobenius_tests::(super::fq::Fq::char(), 13); +} diff --git a/src/bn256/fq2.rs b/src/bn256/fq2.rs new file mode 100644 index 0000000..98939f3 --- /dev/null +++ b/src/bn256/fq2.rs @@ -0,0 +1,966 @@ +use super::fq::{FROBENIUS_COEFF_FQ2_C1, Fq, NEGATIVE_ONE}; +use ff::{Field, SqrtField}; +use rand::{Rand, Rng}; + +use std::cmp::Ordering; + +/// An element of Fq2, represented by c0 + c1 * u. +#[derive(Copy, Clone, Debug, Eq, PartialEq)] +pub struct Fq2 { + pub c0: Fq, + pub c1: Fq, +} + +impl ::std::fmt::Display for Fq2 { + fn fmt(&self, f: &mut ::std::fmt::Formatter) -> ::std::fmt::Result { + write!(f, "Fq2({} + {} * u)", self.c0, self.c1) + } +} + +/// `Fq2` elements are ordered lexicographically. +impl Ord for Fq2 { + #[inline(always)] + fn cmp(&self, other: &Fq2) -> Ordering { + match self.c1.cmp(&other.c1) { + Ordering::Greater => Ordering::Greater, + Ordering::Less => Ordering::Less, + Ordering::Equal => self.c0.cmp(&other.c0), + } + } +} + +impl PartialOrd for Fq2 { + #[inline(always)] + fn partial_cmp(&self, other: &Fq2) -> Option { + Some(self.cmp(other)) + } +} + +impl Fq2 { + // This is very confusing part, cause depends not of Fq2 itself, but form Fq6 construction + + /// Multiply this element by quadratic nonresidue 9 + u. + pub fn mul_by_nonresidue(&mut self) { + // (xi+y)(i+9) = (9x+y)i+(9y-x) + let t0 = self.c0; + let t1 = self.c1; + + // 8*x*i + 8*y + self.double(); + self.double(); + self.double(); + + // 9*y + self.c0.add_assign(&t0); + // (9*y - x) + self.c0.sub_assign(&t1); + + // (9*x)i + self.c1.add_assign(&t1); + // (9*x + y) + self.c1.add_assign(&t0); + } + + // Multiply this element by ξ where ξ=i+9 + pub fn mul_by_xi(&mut self) { + // (xi+y)(i+9) = (9x+y)i+(9y-x) + let t0 = self.c0; + let t1 = self.c1; + + // 8*x*i + 8*y + self.double(); + self.double(); + self.double(); + + // 9*y + self.c0.add_assign(&t0); + // (9*y - x) + self.c0.sub_assign(&t1); + + // (9*x)i + self.c1.add_assign(&t1); + // (9*x + y) + self.c1.add_assign(&t0); + } + + /// Norm of Fq2 as extension field in i over Fq + pub fn norm(&self) -> Fq { + let mut t0 = self.c0; + let mut t1 = self.c1; + t0.square(); + t1.square(); + t1.add_assign(&t0); + + t1 + } + + // conjucate by negating c1 + pub fn conjugate(&mut self) { + self.c1.negate(); + } +} + +impl Rand for Fq2 { + fn rand(rng: &mut R) -> Self { + Fq2 { + c0: rng.gen(), + c1: rng.gen(), + } + } +} + +impl Field for Fq2 { + fn zero() -> Self { + Fq2 { + c0: Fq::zero(), + c1: Fq::zero(), + } + } + + fn one() -> Self { + Fq2 { + c0: Fq::one(), + c1: Fq::zero(), + } + } + + fn is_zero(&self) -> bool { + self.c0.is_zero() && self.c1.is_zero() + } + + fn square(&mut self) { + let mut ab = self.c0; + ab.mul_assign(&self.c1); + let mut c0c1 = self.c0; + c0c1.add_assign(&self.c1); + let mut c0 = self.c1; + c0.negate(); + c0.add_assign(&self.c0); + c0.mul_assign(&c0c1); + c0.sub_assign(&ab); + self.c1 = ab; + self.c1.add_assign(&ab); + c0.add_assign(&ab); + self.c0 = c0; + } + + fn double(&mut self) { + self.c0.double(); + self.c1.double(); + } + + fn negate(&mut self) { + self.c0.negate(); + self.c1.negate(); + } + + fn add_assign(&mut self, other: &Self) { + self.c0.add_assign(&other.c0); + self.c1.add_assign(&other.c1); + } + + fn sub_assign(&mut self, other: &Self) { + self.c0.sub_assign(&other.c0); + self.c1.sub_assign(&other.c1); + } + + fn mul_assign(&mut self, other: &Self) { + let mut aa = self.c0; + aa.mul_assign(&other.c0); + let mut bb = self.c1; + bb.mul_assign(&other.c1); + let mut o = other.c0; + o.add_assign(&other.c1); + self.c1.add_assign(&self.c0); + self.c1.mul_assign(&o); + self.c1.sub_assign(&aa); + self.c1.sub_assign(&bb); + self.c0 = aa; + self.c0.sub_assign(&bb); + } + + fn inverse(&self) -> Option { + let mut t1 = self.c1; + t1.square(); + let mut t0 = self.c0; + t0.square(); + t0.add_assign(&t1); + t0.inverse().map(|t| { + let mut tmp = Fq2 { + c0: self.c0, + c1: self.c1, + }; + tmp.c0.mul_assign(&t); + tmp.c1.mul_assign(&t); + tmp.c1.negate(); + + tmp + }) + } + + fn frobenius_map(&mut self, power: usize) { + self.c1.mul_assign(&FROBENIUS_COEFF_FQ2_C1[power % 2]); + } +} + +impl SqrtField for Fq2 { + fn legendre(&self) -> ::ff::LegendreSymbol { + self.norm().legendre() + } + + fn sqrt(&self) -> Option { + // Algorithm 9, https://eprint.iacr.org/2012/685.pdf + + if self.is_zero() { + Some(Self::zero()) + } else { + // a1 = self^((q - 3) / 4) + let mut a1 = self.pow([ + 0x4f082305b61f3f51, + 0x65e05aa45a1c72a3, + 0x6e14116da0605617, + 0x0c19139cb84c680a, + ]); + let mut alpha = a1; + alpha.square(); + alpha.mul_assign(self); + let mut a0 = alpha; + a0.frobenius_map(1); + a0.mul_assign(&alpha); + + let neg1 = Fq2 { + c0: NEGATIVE_ONE, + c1: Fq::zero(), + }; + + if a0 == neg1 { + None + } else { + a1.mul_assign(self); + + if alpha == neg1 { + a1.mul_assign(&Fq2 { + c0: Fq::zero(), + c1: Fq::one(), + }); + } else { + alpha.add_assign(&Fq2::one()); + // alpha = alpha^((q - 1) / 2) + alpha = alpha.pow([ + 0x9e10460b6c3e7ea3, + 0xcbc0b548b438e546, + 0xdc2822db40c0ac2e, + 0x183227397098d014, + ]); + a1.mul_assign(&alpha); + } + + Some(a1) + } + } + } +} + +#[test] +fn test_fq2_get_b() { + use ff::Field; + + let mut a = Fq2::one(); + a.mul_by_nonresidue(); + let mut b = a.inverse().unwrap(); + let c = b; + b.double(); + b.add_assign(&c); + + print!("B coeff in Fq2 = {}\n", b); +} + +#[test] +fn test_fq2_frobc1() { + use ff::Field; + + let mut a = Fq2::one(); + a.mul_by_nonresidue(); + + let res1 = a.pow([ + 0x69602eb24829a9c2, + 0xdd2b2385cd7b4384, + 0xe81ac1e7808072c9, + 0x10216f7ba065e00d, + ]); + print!("Frob1 = {}\n", res1); + + let res2 = a.pow([ + 0x691c1d8b62747890, + 0x8cab57b9adf8eb00, + 0x18c55d8979dcee49, + 0x56cd8a31d35b6b98, + 0xb7a4a8c966ece684, + 0xe5592c705cbd1cac, + 0x1dde2529566d9b5e, + 0x030c96e827699534, + ]); + print!("Frob2 = {}\n", res2); + + let res3 = a.pow([ + 0x3de6332b975d69b2, + 0x587b5b2bd890e101, + 0x16677d4bec77bbeb, + 0x3fdfdba3309dd645, + 0xdfd4137cd943954b, + 0xcfb035047f38c226, + 0x01b5daf7ac73104c, + 0x4cce8699d63e4f06, + 0x40c0b41264a4b9f4, + 0x7806da9ba1f6d7fb, + 0x110a40708107d53a, + 0x00938e25ae57c88f, + ]); + print!("Frob3 = {}\n", res3); + + let res4 = a.pow([ + 0xabb30419f6bee420, + 0x6ce183e5f2f8d3b9, + 0x9db42a441998ac99, + 0xf74b04aa96e3852f, + 0x64de4542a9807c06, + 0x41f83258fd90abd1, + 0x5ecb5383626aeca3, + 0xb60804ce8f24ca82, + 0xd4b3aadc1344e8bb, + 0x436b70833cb2615b, + 0x1a87eeb627861611, + 0x4e155ea3e5090666, + 0xacfcff9291a10112, + 0x1cba0005b295d5bc, + 0x319c8e7f94b31729, + 0x001be477ceef2455, + ]); + print!("Frob4 = {}\n", res4); + + let res5 = a.pow([ + 0x7501aa71de0e8ea2, + 0x97516fd7ca83b8fe, + 0x7da14ac0c03d4182, + 0xaf5d35dc7f80498d, + 0xb257f7f84fb899e0, + 0x372cb1bd547dbe69, + 0xb6696efbf52d5146, + 0x03b6707d4a42574c, + 0xeae6c62cf1670269, + 0xfe70626cbbb760e9, + 0xfa9d12d01fb42086, + 0xc85218d5a7af23b7, + 0x0a70a73464ed35fb, + 0x878713d44d9a2aca, + 0xc81d8fc5cdfe15ee, + 0xa3ebe919611e544d, + 0xfe46bd734126775c, + 0x06f8a7579371f67f, + 0xa94a371ceb68884c, + 0x000545c441ba73d6, + ]); + print!("Frob5 = {}\n", res5); + + let res6 = a.pow([ + 0xfc4dae0d07a152b0, + 0x3f383f79f9859a0a, + 0x261f0da312f72ab2, + 0x9cc6b2e6efb101d8, + 0xf45a236f76e806da, + 0x7158062cd79d6743, + 0x8adabccc870f23db, + 0x24428ff02b7988c1, + 0x8f55fa0a7ecfa21d, + 0xd5574a8dc73fdcc2, + 0xb86f06772524e5ca, + 0xb4b11653b762bd0f, + 0xb84ec7291c154c58, + 0x2a095f1259f99fb5, + 0x6ccb38fbc9f54a74, + 0x3a3f77faca5c2ea0, + 0x21a469bdd36b9656, + 0x0fa2e41314b53258, + 0xf8ca5207cb9f028e, + 0x489fbf415ec8104e, + 0x711aafe44a1ab611, + 0xfb508020969bab31, + 0xb8b71e4e258cf968, + 0x0000ff25aa9c2350, + ]); + print!("Frob6 = {}\n", res6); +} + +#[test] +fn test_fq2_frobc2() { + use ff::Field; + + let mut a = Fq2::one(); + a.mul_by_nonresidue(); + + let res1 = a.pow([ + 0xd2c05d6490535384, + 0xba56470b9af68708, + 0xd03583cf0100e593, + 0x2042def740cbc01b, + ]); + print!("Frob1 = {}\n", res1); + + let res2 = a.pow([ + 0xd2383b16c4e8f120, + 0x1956af735bf1d600, + 0x318abb12f3b9dc93, + 0xad9b1463a6b6d730, + 0x6f495192cdd9cd08, + 0xcab258e0b97a3959, + 0x3bbc4a52acdb36bd, + 0x06192dd04ed32a68, + ]); + print!("Frob2 = {}\n", res2); + + let res3 = a.pow([ + 0x7bcc66572ebad364, + 0xb0f6b657b121c202, + 0x2ccefa97d8ef77d6, + 0x7fbfb746613bac8a, + 0xbfa826f9b2872a96, + 0x9f606a08fe71844d, + 0x036bb5ef58e62099, + 0x999d0d33ac7c9e0c, + 0x81816824c94973e8, + 0xf00db53743edaff6, + 0x221480e1020faa74, + 0x01271c4b5caf911e, + ]); + print!("Frob3 = {}\n", res3); + + let res4 = a.pow([ + 0x57660833ed7dc840, + 0xd9c307cbe5f1a773, + 0x3b68548833315932, + 0xee9609552dc70a5f, + 0xc9bc8a855300f80d, + 0x83f064b1fb2157a2, + 0xbd96a706c4d5d946, + 0x6c10099d1e499504, + 0xa96755b82689d177, + 0x86d6e1067964c2b7, + 0x350fdd6c4f0c2c22, + 0x9c2abd47ca120ccc, + 0x59f9ff2523420224, + 0x3974000b652bab79, + 0x63391cff29662e52, + 0x0037c8ef9dde48aa, + ]); + print!("Frob4 = {}\n", res4); + + let res5 = a.pow([ + 0xea0354e3bc1d1d44, + 0x2ea2dfaf950771fc, + 0xfb429581807a8305, + 0x5eba6bb8ff00931a, + 0x64afeff09f7133c1, + 0x6e59637aa8fb7cd3, + 0x6cd2ddf7ea5aa28c, + 0x076ce0fa9484ae99, + 0xd5cd8c59e2ce04d2, + 0xfce0c4d9776ec1d3, + 0xf53a25a03f68410d, + 0x90a431ab4f5e476f, + 0x14e14e68c9da6bf7, + 0x0f0e27a89b345594, + 0x903b1f8b9bfc2bdd, + 0x47d7d232c23ca89b, + 0xfc8d7ae6824ceeb9, + 0x0df14eaf26e3ecff, + 0x52946e39d6d11098, + 0x000a8b888374e7ad, + ]); + print!("Frob5 = {}\n", res5); + + let res6 = a.pow([ + 0xf89b5c1a0f42a560, + 0x7e707ef3f30b3415, + 0x4c3e1b4625ee5564, + 0x398d65cddf6203b0, + 0xe8b446deedd00db5, + 0xe2b00c59af3ace87, + 0x15b579990e1e47b6, + 0x48851fe056f31183, + 0x1eabf414fd9f443a, + 0xaaae951b8e7fb985, + 0x70de0cee4a49cb95, + 0x69622ca76ec57a1f, + 0x709d8e52382a98b1, + 0x5412be24b3f33f6b, + 0xd99671f793ea94e8, + 0x747eeff594b85d40, + 0x4348d37ba6d72cac, + 0x1f45c826296a64b0, + 0xf194a40f973e051c, + 0x913f7e82bd90209d, + 0xe2355fc894356c22, + 0xf6a100412d375662, + 0x716e3c9c4b19f2d1, + 0x0001fe4b553846a1, + ]); + print!("Frob6 = {}\n", res6); +} + +#[test] +fn test_fq2_frob12() { + use ff::Field; + + let mut a = Fq2::one(); + a.mul_by_nonresidue(); + + let res1 = a.pow([ + 0x34b017592414d4e1, + 0xee9591c2e6bda1c2, + 0xf40d60f3c0403964, + 0x0810b7bdd032f006, + ]); + print!("Frob1 = {}\n", res1); + + let res2 = a.pow([ + 0x348e0ec5b13a3c48, + 0xc655abdcd6fc7580, + 0x0c62aec4bcee7724, + 0x2b66c518e9adb5cc, + 0x5bd25464b3767342, + 0x72ac96382e5e8e56, + 0x0eef1294ab36cdaf, + 0x01864b7413b4ca9a, + ]); + print!("Frob2 = {}\n", res2); + + let res3 = a.pow([ + 0x9ef31995cbaeb4d9, + 0xac3dad95ec487080, + 0x8b33bea5f63bddf5, + 0x9fefedd1984eeb22, + 0x6fea09be6ca1caa5, + 0x67d81a823f9c6113, + 0x00daed7bd6398826, + 0x2667434ceb1f2783, + 0xa0605a0932525cfa, + 0x3c036d4dd0fb6bfd, + 0x888520384083ea9d, + 0x0049c712d72be447, + ]); + print!("Frob3 = {}\n", res3); + + let res4 = a.pow([ + 0xd5d9820cfb5f7210, + 0xb670c1f2f97c69dc, + 0xceda15220ccc564c, + 0x7ba582554b71c297, + 0xb26f22a154c03e03, + 0xa0fc192c7ec855e8, + 0x2f65a9c1b1357651, + 0xdb04026747926541, + 0xea59d56e09a2745d, + 0xa1b5b8419e5930ad, + 0x0d43f75b13c30b08, + 0x270aaf51f2848333, + 0x567e7fc948d08089, + 0x8e5d0002d94aeade, + 0x98ce473fca598b94, + 0x000df23be777922a, + ]); + print!("Frob4 = {}\n", res4); + + let res5 = a.pow([ + 0x3a80d538ef074751, + 0x4ba8b7ebe541dc7f, + 0xbed0a560601ea0c1, + 0x57ae9aee3fc024c6, + 0xd92bfbfc27dc4cf0, + 0x1b9658deaa3edf34, + 0x5b34b77dfa96a8a3, + 0x81db383ea5212ba6, + 0xf573631678b38134, + 0x7f3831365ddbb074, + 0xfd4e89680fda1043, + 0xe4290c6ad3d791db, + 0x0538539a32769afd, + 0x43c389ea26cd1565, + 0xe40ec7e2e6ff0af7, + 0x51f5f48cb08f2a26, + 0xff235eb9a0933bae, + 0x037c53abc9b8fb3f, + 0x54a51b8e75b44426, + 0x0002a2e220dd39eb, + ]); + print!("Frob5 = {}\n", res5); + + let res6 = a.pow([ + 0x7e26d70683d0a958, + 0x1f9c1fbcfcc2cd05, + 0x130f86d1897b9559, + 0x4e63597377d880ec, + 0xfa2d11b7bb74036d, + 0xb8ac03166bceb3a1, + 0xc56d5e66438791ed, + 0x922147f815bcc460, + 0x47aafd053f67d10e, + 0x6aaba546e39fee61, + 0xdc37833b929272e5, + 0x5a588b29dbb15e87, + 0xdc2763948e0aa62c, + 0x1504af892cfccfda, + 0x36659c7de4faa53a, + 0x1d1fbbfd652e1750, + 0x10d234dee9b5cb2b, + 0x07d172098a5a992c, + 0x7c652903e5cf8147, + 0xa44fdfa0af640827, + 0xb88d57f2250d5b08, + 0x7da840104b4dd598, + 0x5c5b8f2712c67cb4, + 0x00007f92d54e11a8, + ]); + print!("Frob6 = {}\n", res6); + + let res7 = a.pow([ + 0x75e1bff130efc449, + 0xfb4f505fb284ee15, + 0x57b30efd96c492f5, + 0xfdcb862e4e948b59, + 0x3def467dae8887e2, + 0xa47e3f76b755ca8c, + 0xa63f6ea3debc563a, + 0x115d1111a4fc4be2, + 0xc3b2ece674d74549, + 0xb2099a8141cb8830, + 0x120dc0b8ac63867a, + 0xae0245267985fe96, + 0xed38ce9a40128f1d, + 0xeba67d5d8ffa4939, + 0xbff55f706b0de0f5, + 0xb4f3f86e6f982aed, + 0x062675f8a89bd61d, + 0xa1098fb006a9726c, + 0xe974fc0c7b0e5d9c, + 0x0f10af0bdc56fe9e, + 0x628ca855d5d4ac87, + 0x7bd59e7101d9d82d, + 0xed98625bf5dc71aa, + 0x7ab9b78fdc8558f4, + 0x489b4c8564d6f8d2, + 0xd055177a2fbfcd94, + 0x059f68dd1e0cb392, + 0x0000181d8471f268, + ]); + print!("Frob7 = {}\n", res7); + + let res8 = a.pow([ + 0x742e7ca156ec6a20, + 0x3fee59e5c3e8de2e, + 0xfdef69cd295152ef, + 0xe4ad8aece2ce3640, + 0x05308778897ea5eb, + 0x0a4fc046ae1c2e50, + 0xb16faf17473bba4a, + 0xd106751c900aadfa, + 0x115301a6c43ba345, + 0x19f012d49d8a716c, + 0x6b9d91b1c2a56cc5, + 0xb77230690204b675, + 0xf6d68e7229980805, + 0xf4263d3b11784a87, + 0x24bb64e5adeaa33d, + 0x684c4ff325fa1c4d, + 0x79a8c6430472e684, + 0x823af8186da5609c, + 0x2087966741a30941, + 0x1876205eaf407912, + 0xa614d3f14990435e, + 0xd405328435bcc8df, + 0x5afac38bad541421, + 0x0706fb9d17dec3d8, + 0xecc747832c3f5f69, + 0xe231b0ffd6651ed5, + 0x45fa8e7ff2a80f15, + 0xdce48166a2ee0170, + 0x305fc72544895a12, + 0x516ac4b20d800019, + 0x826e9ab28689a4d3, + 0x0000048efbc0eaac, + ]); + print!("Frob8 = {}\n", res8); + + let res9 = a.pow(&[ + 0x64984dce4c07e3c1, + 0x2e2096f441339496, + 0xd50c9bd49d279670, + 0xd52ead3ce3a93422, + 0x426dad5fc6a6779a, + 0x3f9dd6b6f19bc638, + 0x6be503d3981b0db5, + 0x0b222e7512412d2c, + 0x484bd275e77ff0bf, + 0xb357542fb851205b, + 0xd8c995246bf492ff, + 0xc6b92fc3bf2887bc, + 0xcd27cfd0d4499277, + 0x967aa0012f40dcf9, + 0x312baab0f5bc64e3, + 0xe465b3c98a822e05, + 0x3133d12c8828f7b8, + 0x357a20a6a8a244ca, + 0xd40b61719905e5b9, + 0xcc4f1d5e2aed7a75, + 0x7895032e16409563, + 0x536db2a17eb54630, + 0xdd66ae0d2d5ac57e, + 0xe150b5a7f229f541, + 0xd882dbabee789616, + 0x1f380eb8775416ca, + 0x73eca6c1c0abcd02, + 0x8bd4f78c2fe1861e, + 0xc53f421003b18ea2, + 0xcae3f7b5d0591ecb, + 0xbebe6ab21737113e, + 0x838f0df2a5f7f26d, + 0xbc2aa2593b06d88f, + 0x0cb02b95a74a8a0a, + 0x74bd9a7b50725838, + 0x000000dc98741fbf, + ][..]); + print!("Frob9 = {}\n", res9); + + let res10 = a.pow(&[ + 0xed4127472fd6bc68, + 0x72748872e11c4b47, + 0x9c84e64776edc3f8, + 0x008119b96d78b386, + 0xfb0fbff1c5556968, + 0x5009c51f998020be, + 0xd6e688613527a368, + 0xbe4f27942823152c, + 0xd0f09d15c45fe09e, + 0x7eb531158d2bbea5, + 0x51bbe8e71be2cfd1, + 0xbab37561b8c0c7c4, + 0xd9173b5ab551b267, + 0x05fafd9be4c78781, + 0x61883bc8a78540ee, + 0x7fe7aee3dcb694fb, + 0x0e4e85b12b4ac8a8, + 0x9a0aa13a9ab47a86, + 0xd5a3bd591ae12d4b, + 0x5865cbfabbe53b4d, + 0xf98188a9b0cd490f, + 0x3985ef4af715da43, + 0x573661cd006ced38, + 0x95853a6aaa77d5c1, + 0x165d538f0628b55e, + 0x583e75f890f32cac, + 0x5becf43a08a490b9, + 0x63ed4071c1a8087a, + 0x151d41c7701faa25, + 0x1c661c8e4900b051, + 0x581aa0f552590875, + 0x31bf39ff43375aca, + 0xe27c0f3d11310329, + 0x04071459ef3a42c2, + 0x59a2b029be2d6a1f, + 0x30ef71f271cdbf61, + 0xf3774b177f326e78, + 0x976d79b23e8501c5, + 0x9ed0e138633123c9, + 0x00000029b304ecc1, + ][..]); + print!("Frob10 = {}\n", res10); + + let res11 = a.pow(&[ + 0xf4e8c249a335ddb9, + 0x965085c9440aef70, + 0xc16d84a741174aef, + 0xbe1a366b81fe0680, + 0x1c65508409269d2f, + 0x185861e9cd07fb21, + 0x26b682d951220b7a, + 0x09f189f5a7b75876, + 0x0f7133ab3ecff7f0, + 0xbf7d1ada5df0b2fd, + 0x4b0df5207414a4b6, + 0xbf6a6941b58966d3, + 0x6a15cc7b6bb0483a, + 0xc338843b8a236597, + 0xc8d724986bc0856f, + 0x1dcb8b084e928e52, + 0x3645ba97c4af9161, + 0x7d257d1abed180d3, + 0x0a66e85068416bdb, + 0x8b745a2aeb2bd27e, + 0xe34f87ec4949ec06, + 0x6ba47fa06f902fd6, + 0x225cd33864121ed2, + 0xea5d91e41a3b068b, + 0x35d2fbc8b7a05f5c, + 0xe5b1e22f3dcbc837, + 0xa9f7bdbee44d8301, + 0xbb7a57512450e143, + 0x2e2ca4188fd4eb5b, + 0x9d512b5d1e158636, + 0xdd18753b03f38ee8, + 0xbbe44db3214b380e, + 0x4534f7b060cca3d2, + 0xcbb0309736f9df06, + 0xfcb01aba828f0678, + 0xe2e4d5dac5cc7917, + 0x6631e85c4224e136, + 0xb6c334bbd109d480, + 0x2608e9c50edc2cdf, + 0x959dba8288258d16, + 0x00d895fc73e207c8, + 0x6b5ce08dc4a7bf13, + 0xb02a4f252d6a301f, + 0x00000007e1e7a192, + ][..]); + print!("Frob11 = {}\n", res11); +} + +#[test] +fn test_calculate_frob_1() { + let mut a = Fq2::one(); + a.mul_by_nonresidue(); + + // Fq2(u + 9)**(((q^1) - 1) / 3) + + print!("(i + 9) = {}\n", a); +} + +#[test] +fn test_fq2_ordering() { + let mut a = Fq2 { + c0: Fq::zero(), + c1: Fq::zero(), + }; + + let mut b = a.clone(); + + assert!(a.cmp(&b) == Ordering::Equal); + b.c0.add_assign(&Fq::one()); + assert!(a.cmp(&b) == Ordering::Less); + a.c0.add_assign(&Fq::one()); + assert!(a.cmp(&b) == Ordering::Equal); + b.c1.add_assign(&Fq::one()); + assert!(a.cmp(&b) == Ordering::Less); + a.c0.add_assign(&Fq::one()); + assert!(a.cmp(&b) == Ordering::Less); + a.c1.add_assign(&Fq::one()); + assert!(a.cmp(&b) == Ordering::Greater); + b.c0.add_assign(&Fq::one()); + assert!(a.cmp(&b) == Ordering::Equal); +} + +#[test] +fn test_fq2_basics() { + assert_eq!( + Fq2 { + c0: Fq::zero(), + c1: Fq::zero(), + }, + Fq2::zero() + ); + assert_eq!( + Fq2 { + c0: Fq::one(), + c1: Fq::zero(), + }, + Fq2::one() + ); + assert!(Fq2::zero().is_zero()); + assert!(!Fq2::one().is_zero()); + assert!( + !Fq2 { + c0: Fq::zero(), + c1: Fq::one(), + }.is_zero() + ); +} + +#[test] +fn test_fq2_squaring() { + use super::fq::FqRepr; + use ff::PrimeField; + + let mut a = Fq2 { + c0: Fq::one(), + c1: Fq::one(), + }; // u + 1 + a.square(); + assert_eq!( + a, + Fq2 { + c0: Fq::zero(), + c1: Fq::from_repr(FqRepr::from(2)).unwrap(), + } + ); // 2u + + let mut a = Fq2 { + c0: Fq::zero(), + c1: Fq::one(), + }; // u + a.square(); + assert_eq!(a, { + let mut neg1 = Fq::one(); + neg1.negate(); + Fq2 { + c0: neg1, + c1: Fq::zero(), + } + }); // -1 + +} + +#[test] +fn test_fq2_legendre() { + use ff::LegendreSymbol::*; + + assert_eq!(Zero, Fq2::zero().legendre()); + // i^2 = -1 + let mut m1 = Fq2::one(); + m1.negate(); + assert_eq!(QuadraticResidue, m1.legendre()); + m1.mul_by_nonresidue(); + assert_eq!(QuadraticNonResidue, m1.legendre()); +} + +#[cfg(test)] +use rand::{SeedableRng, XorShiftRng}; + +#[test] +fn test_fq2_mul_nonresidue() { + let mut rng = XorShiftRng::from_seed([0x5dbe6259, 0x8d313d76, 0x3237db17, 0xe5bc0654]); + let mut nine = Fq::one(); + nine.double(); + nine.double(); + nine.double(); + nine.add_assign(&Fq::one()); + let nqr = Fq2 { + c0: nine, + c1: Fq::one(), + }; + + for _ in 0..1000 { + let mut a = Fq2::rand(&mut rng); + let mut b = a; + a.mul_by_nonresidue(); + b.mul_assign(&nqr); + + assert_eq!(a, b); + } +} + +#[test] +fn fq2_field_tests() { + use ff::PrimeField; + + ::tests::field::random_field_tests::(); + ::tests::field::random_sqrt_tests::(); + ::tests::field::random_frobenius_tests::(super::fq::Fq::char(), 13); +} diff --git a/src/bn256/fq6.rs b/src/bn256/fq6.rs new file mode 100644 index 0000000..412e4ab --- /dev/null +++ b/src/bn256/fq6.rs @@ -0,0 +1,400 @@ +use super::fq::{FROBENIUS_COEFF_FQ6_C1, FROBENIUS_COEFF_FQ6_C2}; +use super::fq2::Fq2; +use ff::Field; +use rand::{Rand, Rng}; + +/// An element of Fq6, represented by c0 + c1 * v + c2 * v^(2). +#[derive(Copy, Clone, Debug, Eq, PartialEq)] +pub struct Fq6 { + pub c0: Fq2, + pub c1: Fq2, + pub c2: Fq2, +} + +impl ::std::fmt::Display for Fq6 { + fn fmt(&self, f: &mut ::std::fmt::Formatter) -> ::std::fmt::Result { + write!(f, "Fq6({} + {} * v, {} * v^2)", self.c0, self.c1, self.c2) + } +} + +impl Rand for Fq6 { + fn rand(rng: &mut R) -> Self { + Fq6 { + c0: rng.gen(), + c1: rng.gen(), + c2: rng.gen(), + } + } +} + +// Here it's getting tough, because extension tower diverges with BLS12 + +// BLS12 (v^3 - ξ) where ξ = u + 1 +// BN256 (v^3 - ξ) where ξ = u + 9 + +impl Fq6 { + + /// Multiply by cubic nonresidue v. + pub fn mul_by_nonresidue(&mut self) { + use std::mem::swap; + swap(&mut self.c0, &mut self.c1); + swap(&mut self.c0, &mut self.c2); + // c0, c1, c2 -> c2, c0, c1 + self.c0.mul_by_nonresidue(); + } + + /// Multiply by cubic nonresidue v. + pub fn mul_by_v(&mut self) { + use std::mem::swap; + swap(&mut self.c0, &mut self.c1); + swap(&mut self.c0, &mut self.c2); + + self.c0.mul_by_xi(); + } + + pub fn mul_by_1(&mut self, c1: &Fq2) { + let mut b_b = self.c1; + b_b.mul_assign(c1); + + let mut t1 = *c1; + { + let mut tmp = self.c1; + tmp.add_assign(&self.c2); + + t1.mul_assign(&tmp); + t1.sub_assign(&b_b); + t1.mul_by_nonresidue(); + } + + let mut t2 = *c1; + { + let mut tmp = self.c0; + tmp.add_assign(&self.c1); + + t2.mul_assign(&tmp); + t2.sub_assign(&b_b); + } + + self.c0 = t1; + self.c1 = t2; + self.c2 = b_b; + } + + pub fn mul_by_01(&mut self, c0: &Fq2, c1: &Fq2) { + let mut a_a = self.c0; + let mut b_b = self.c1; + a_a.mul_assign(c0); + b_b.mul_assign(c1); + + let mut t1 = *c1; + { + let mut tmp = self.c1; + tmp.add_assign(&self.c2); + + t1.mul_assign(&tmp); + t1.sub_assign(&b_b); + t1.mul_by_nonresidue(); + t1.add_assign(&a_a); + } + + let mut t3 = *c0; + { + let mut tmp = self.c0; + tmp.add_assign(&self.c2); + + t3.mul_assign(&tmp); + t3.sub_assign(&a_a); + t3.add_assign(&b_b); + } + + let mut t2 = *c0; + t2.add_assign(c1); + { + let mut tmp = self.c0; + tmp.add_assign(&self.c1); + + t2.mul_assign(&tmp); + t2.sub_assign(&a_a); + t2.sub_assign(&b_b); + } + + self.c0 = t1; + self.c1 = t2; + self.c2 = t3; + } +} + +impl Field for Fq6 { + fn zero() -> Self { + Fq6 { + c0: Fq2::zero(), + c1: Fq2::zero(), + c2: Fq2::zero(), + } + } + + fn one() -> Self { + Fq6 { + c0: Fq2::one(), + c1: Fq2::zero(), + c2: Fq2::zero(), + } + } + + fn is_zero(&self) -> bool { + self.c0.is_zero() && self.c1.is_zero() && self.c2.is_zero() + } + + fn double(&mut self) { + self.c0.double(); + self.c1.double(); + self.c2.double(); + } + + fn negate(&mut self) { + self.c0.negate(); + self.c1.negate(); + self.c2.negate(); + } + + fn add_assign(&mut self, other: &Self) { + self.c0.add_assign(&other.c0); + self.c1.add_assign(&other.c1); + self.c2.add_assign(&other.c2); + } + + fn sub_assign(&mut self, other: &Self) { + self.c0.sub_assign(&other.c0); + self.c1.sub_assign(&other.c1); + self.c2.sub_assign(&other.c2); + } + + fn frobenius_map(&mut self, power: usize) { + self.c0.frobenius_map(power); + self.c1.frobenius_map(power); + self.c2.frobenius_map(power); + + self.c1.mul_assign(&FROBENIUS_COEFF_FQ6_C1[power % 6]); + self.c2.mul_assign(&FROBENIUS_COEFF_FQ6_C2[power % 6]); + } + + fn square(&mut self) { + // s0 = a^2 + let mut s0 = self.c0; + s0.square(); + // s1 = 2ab + let mut ab = self.c0; + ab.mul_assign(&self.c1); + let mut s1 = ab; + s1.double(); + // s2 = (a - b + c)^2 + let mut s2 = self.c0; + s2.sub_assign(&self.c1); + s2.add_assign(&self.c2); + s2.square(); + // bc + let mut bc = self.c1; + bc.mul_assign(&self.c2); + // s3 = 2bc + let mut s3 = bc; + s3.double(); + // s4 = c^2 + let mut s4 = self.c2; + s4.square(); + + // new c0 = 2bc.mul_by_xi + a^2 + self.c0 = s3; + self.c0.mul_by_nonresidue(); + // self.c0.mul_by_xi(); + self.c0.add_assign(&s0); + + // new c1 = (c^2).mul_by_xi + 2ab + self.c1 = s4; + self.c1.mul_by_nonresidue(); + // self.c1.mul_by_xi(); + self.c1.add_assign(&s1); + + // new c2 = 2ab + (a - b + c)^2 + 2bc - a^2 - c^2 = b^2 + 2ac + self.c2 = s1; + self.c2.add_assign(&s2); + self.c2.add_assign(&s3); + self.c2.sub_assign(&s0); + self.c2.sub_assign(&s4); + } + + fn mul_assign(&mut self, other: &Self) { + let mut a_a = self.c0; + let mut b_b = self.c1; + let mut c_c = self.c2; + a_a.mul_assign(&other.c0); + b_b.mul_assign(&other.c1); + c_c.mul_assign(&other.c2); + + let mut t1 = other.c1; + t1.add_assign(&other.c2); + { + let mut tmp = self.c1; + tmp.add_assign(&self.c2); + + t1.mul_assign(&tmp); + t1.sub_assign(&b_b); + t1.sub_assign(&c_c); + t1.mul_by_nonresidue(); + t1.add_assign(&a_a); + } + + let mut t3 = other.c0; + t3.add_assign(&other.c2); + { + let mut tmp = self.c0; + tmp.add_assign(&self.c2); + + t3.mul_assign(&tmp); + t3.sub_assign(&a_a); + t3.add_assign(&b_b); + t3.sub_assign(&c_c); + } + + let mut t2 = other.c0; + t2.add_assign(&other.c1); + { + let mut tmp = self.c0; + tmp.add_assign(&self.c1); + + t2.mul_assign(&tmp); + t2.sub_assign(&a_a); + t2.sub_assign(&b_b); + c_c.mul_by_nonresidue(); + t2.add_assign(&c_c); + } + + self.c0 = t1; + self.c1 = t2; + self.c2 = t3; + } + + fn inverse(&self) -> Option { + let mut c0 = self.c2; + c0.mul_by_nonresidue(); + c0.mul_assign(&self.c1); + c0.negate(); + { + let mut c0s = self.c0; + c0s.square(); + c0.add_assign(&c0s); + } + let mut c1 = self.c2; + c1.square(); + c1.mul_by_nonresidue(); + { + let mut c01 = self.c0; + c01.mul_assign(&self.c1); + c1.sub_assign(&c01); + } + let mut c2 = self.c1; + c2.square(); + { + let mut c02 = self.c0; + c02.mul_assign(&self.c2); + c2.sub_assign(&c02); + } + + let mut tmp1 = self.c2; + tmp1.mul_assign(&c1); + let mut tmp2 = self.c1; + tmp2.mul_assign(&c2); + tmp1.add_assign(&tmp2); + tmp1.mul_by_nonresidue(); + tmp2 = self.c0; + tmp2.mul_assign(&c0); + tmp1.add_assign(&tmp2); + + match tmp1.inverse() { + Some(t) => { + let mut tmp = Fq6 { + c0: t, + c1: t, + c2: t, + }; + tmp.c0.mul_assign(&c0); + tmp.c1.mul_assign(&c1); + tmp.c2.mul_assign(&c2); + + Some(tmp) + } + None => None, + } + } +} + +#[cfg(test)] +use rand::{SeedableRng, XorShiftRng}; + +#[test] +fn test_fq6_mul_nonresidue() { + let mut rng = XorShiftRng::from_seed([0x5dbe6259, 0x8d313d76, 0x3237db17, 0xe5bc0654]); + + let nqr = Fq6 { + c0: Fq2::zero(), + c1: Fq2::one(), + c2: Fq2::zero(), + }; + + for _ in 0..1000 { + let mut a = Fq6::rand(&mut rng); + let mut b = a; + a.mul_by_nonresidue(); + b.mul_assign(&nqr); + + assert_eq!(a, b); + } +} + +#[test] +fn test_fq6_mul_by_1() { + let mut rng = XorShiftRng::from_seed([0x5dbe6259, 0x8d313d76, 0x3237db17, 0xe5bc0654]); + + for _ in 0..1000 { + let c1 = Fq2::rand(&mut rng); + let mut a = Fq6::rand(&mut rng); + let mut b = a; + + a.mul_by_1(&c1); + b.mul_assign(&Fq6 { + c0: Fq2::zero(), + c1: c1, + c2: Fq2::zero(), + }); + + assert_eq!(a, b); + } +} + +#[test] +fn test_fq6_mul_by_01() { + let mut rng = XorShiftRng::from_seed([0x5dbe6259, 0x8d313d76, 0x3237db17, 0xe5bc0654]); + + for _ in 0..1000 { + let c0 = Fq2::rand(&mut rng); + let c1 = Fq2::rand(&mut rng); + let mut a = Fq6::rand(&mut rng); + let mut b = a; + + a.mul_by_01(&c0, &c1); + b.mul_assign(&Fq6 { + c0: c0, + c1: c1, + c2: Fq2::zero(), + }); + + assert_eq!(a, b); + } +} + +#[test] +fn fq6_field_tests() { + use ff::PrimeField; + + ::tests::field::random_field_tests::(); + ::tests::field::random_frobenius_tests::(super::fq::Fq::char(), 13); +} diff --git a/src/bn256/fr.rs b/src/bn256/fr.rs new file mode 100644 index 0000000..e713ac6 --- /dev/null +++ b/src/bn256/fr.rs @@ -0,0 +1,11 @@ +use ff::{Field, PrimeField, PrimeFieldDecodingError, PrimeFieldRepr}; + +#[derive(PrimeField)] +#[PrimeFieldModulus = "21888242871839275222246405745257275088548364400416034343698204186575808495617"] +#[PrimeFieldGenerator = "7"] +pub struct Fr(FrRepr); + +#[test] +fn test_roots_of_unity() { + assert_eq!(Fr::S, 28); +} \ No newline at end of file diff --git a/src/bn256/mod.rs b/src/bn256/mod.rs new file mode 100644 index 0000000..b5688ab --- /dev/null +++ b/src/bn256/mod.rs @@ -0,0 +1,599 @@ +mod ec; +mod fq; +mod fq12; +mod fq2; +mod fq6; +mod fr; + +// #[cfg(test)] +// mod tests; + +pub use self::ec::{ + G1, G1Affine, G1Compressed, G1Prepared, G1Uncompressed, + G2, G2Affine, G2Compressed, G2Prepared, G2Uncompressed, +}; +pub use self::fq::{Fq, FqRepr, FROBENIUS_COEFF_FQ6_C1, XI_TO_Q_MINUS_1_OVER_2}; +pub use self::fq12::Fq12; +pub use self::fq2::Fq2; +pub use self::fq6::Fq6; +pub use self::fr::{Fr, FrRepr}; + +use super::{CurveAffine, Engine}; + +use ff::{Field, ScalarEngine}; + +#[derive(Clone, Debug)] +pub struct Bn256; + +// U value that originates this particular curve +pub const BN_U: u64 = 4965661367192848881; + +// // 6U+2 for in NAF form +pub const SIX_U_PLUS_2_NAF : [i8; 65] = [0, 0, 0, 1, 0, 1, 0, -1, 0, 0, 1, -1, 0, 0, 1, 0, + 0, 1, 1, 0, -1, 0, 0, 1, 0, -1, 0, 0, 0, 0, 1, 1, + 1, 0, 0, -1, 0, 0, 1, 0, 0, 0, 0, 0, -1, 0, 0, 1, + 1, 0, 0, -1, 0, 0, 0, 1, 1, 0, -1, 0, 0, 1, 0, 1, 1]; + + +impl ScalarEngine for Bn256 { + type Fr = Fr; +} + +impl Engine for Bn256 { + type G1 = G1; + type G1Affine = G1Affine; + type G2 = G2; + type G2Affine = G2Affine; + type Fq = Fq; + type Fqe = Fq2; + type Fqk = Fq12; + + fn miller_loop<'a, I>(i: I) -> Self::Fqk + where + I: IntoIterator< + Item = &'a ( + &'a ::Prepared, + &'a ::Prepared, + ), + >, + { + let mut pairs = vec![]; + for &(p, q) in i { + if !p.is_zero() && !q.is_zero() { + pairs.push((p, q.coeffs.iter())); + } + } + + // Final steps of the line function on prepared coefficients + fn ell(f: &mut Fq12, coeffs: &(Fq2, Fq2, Fq2), p: &G1Affine) { + let mut c0 = coeffs.0; + let mut c1 = coeffs.1; + + c0.c0.mul_assign(&p.y); + c0.c1.mul_assign(&p.y); + + c1.c0.mul_assign(&p.x); + c1.c1.mul_assign(&p.x); + + // Sparse multiplication in Fq12 + + f.mul_by_034(&c0, &c1, &coeffs.2); + } + + let mut f = Fq12::one(); + + for i in (1..SIX_U_PLUS_2_NAF.len()).rev() { + if i != SIX_U_PLUS_2_NAF.len() - 1 { + f.square(); + } + for &mut (p, ref mut coeffs) in &mut pairs { + ell(&mut f, coeffs.next().unwrap(), &p.0); + } + let x = SIX_U_PLUS_2_NAF[i-1]; + match x { + 1 => { + for &mut (p, ref mut coeffs) in &mut pairs { + ell(&mut f, coeffs.next().unwrap(), &p.0); + } + } + -1 => { + for &mut (p, ref mut coeffs) in &mut pairs { + ell(&mut f, coeffs.next().unwrap(), &p.0); + } + } + _ => { + continue + } + } + } + + // two additional steps: for q1 and minus q2 + + for &mut (p, ref mut coeffs) in &mut pairs { + ell(&mut f, coeffs.next().unwrap(), &p.0); + } + + for &mut (p, ref mut coeffs) in &mut pairs { + ell(&mut f, coeffs.next().unwrap(), &p.0); + } + + for &mut (_p, ref mut coeffs) in &mut pairs { + assert_eq!(coeffs.next(), None); + } + + f + } + + fn final_exponentiation(r: &Fq12) -> Option { + let mut f1 = *r; + f1.conjugate(); + + match r.inverse() { + Some(mut f2) => { + let mut r = f1; + r.mul_assign(&f2); + f2 = r; + r.frobenius_map(2); + r.mul_assign(&f2); + + fn exp_by_x(f: &mut Fq12, x: u64) { + *f = f.pow(&[x]); + } + + let x = BN_U; + + let mut fp = r; + fp.frobenius_map(1); + + let mut fp2 = r; + fp2.frobenius_map(2); + let mut fp3 = fp2; + fp3.frobenius_map(1); + + let mut fu = r; + exp_by_x(&mut fu, x); + + let mut fu2 = fu; + exp_by_x(&mut fu2, x); + + let mut fu3 = fu2; + exp_by_x(&mut fu3, x); + + let mut y3 = fu; + y3.frobenius_map(1); + + let mut fu2p = fu2; + fu2p.frobenius_map(1); + + let mut fu3p = fu3; + fu3p.frobenius_map(1); + + let mut y2 = fu2; + y2.frobenius_map(2); + + let mut y0 = fp; + y0.mul_assign(&fp2); + y0.mul_assign(&fp3); + + let mut y1 = r; + y1.conjugate(); + + let mut y5 = fu2; + y5.conjugate(); + + y3.conjugate(); + + let mut y4 = fu; + y4.mul_assign(&fu2p); + y4.conjugate(); + + let mut y6 = fu3; + y6.mul_assign(&fu3p); + y6.conjugate(); + + + y6.square(); + y6.mul_assign(&y4); + y6.mul_assign(&y5); + + let mut t1 = y3; + t1.mul_assign(&y5); + t1.mul_assign(&y6); + + y6.mul_assign(&y2); + + t1.square(); + t1.mul_assign(&y6); + t1.square(); + + let mut t0 = t1; + t0.mul_assign(&y1); + + t1.mul_assign(&y0); + + t0.square(); + t0.mul_assign(&t1); + + Some(t0) + } + None => None, + } + } + +} + +impl G2Prepared { + pub fn is_zero(&self) -> bool { + self.infinity + } + + pub fn from_affine(q: G2Affine) -> Self { + if q.is_zero() { + return G2Prepared { + coeffs: vec![], + infinity: true, + }; + } + + fn doubling_step(r: &mut G2) -> (Fq2, Fq2, Fq2) { + // Adaptation of Algorithm 26, https://eprint.iacr.org/2010/354.pdf + let mut tmp0 = r.x; + tmp0.square(); + + let mut tmp1 = r.y; + tmp1.square(); + + let mut tmp2 = tmp1; + tmp2.square(); + + let mut tmp3 = tmp1; + tmp3.add_assign(&r.x); + tmp3.square(); + tmp3.sub_assign(&tmp0); + tmp3.sub_assign(&tmp2); + tmp3.double(); + + let mut tmp4 = tmp0; + tmp4.double(); + tmp4.add_assign(&tmp0); + + let mut tmp6 = r.x; + tmp6.add_assign(&tmp4); + + let mut tmp5 = tmp4; + tmp5.square(); + + let mut zsquared = r.z; + zsquared.square(); + + r.x = tmp5; + r.x.sub_assign(&tmp3); + r.x.sub_assign(&tmp3); + + r.z.add_assign(&r.y); + r.z.square(); + r.z.sub_assign(&tmp1); + r.z.sub_assign(&zsquared); + + r.y = tmp3; + r.y.sub_assign(&r.x); + r.y.mul_assign(&tmp4); + + tmp2.double(); + tmp2.double(); + tmp2.double(); + + r.y.sub_assign(&tmp2); + + // up to here everything was by algorith, line 11 + // use R instead of new T + + // tmp3 is the first part of line 12 + tmp3 = tmp4; + tmp3.mul_assign(&zsquared); + tmp3.double(); + tmp3.negate(); + + // tmp6 is from line 14 + tmp6.square(); + tmp6.sub_assign(&tmp0); + tmp6.sub_assign(&tmp5); + + tmp1.double(); + tmp1.double(); + + tmp6.sub_assign(&tmp1); + + // tmp0 is the first part of line 16 + tmp0 = r.z; + tmp0.mul_assign(&zsquared); + tmp0.double(); + + (tmp0, tmp3, tmp6) + } + + fn addition_step(r: &mut G2, q: &G2Affine) -> (Fq2, Fq2, Fq2) { + // Adaptation of Algorithm 27, https://eprint.iacr.org/2010/354.pdf + let mut zsquared = r.z; + zsquared.square(); + + let mut ysquared = q.y; + ysquared.square(); + + // t0 corresponds to line 1 + let mut t0 = zsquared; + t0.mul_assign(&q.x); + + // t1 corresponds to lines 2 and 3 + let mut t1 = q.y; + t1.add_assign(&r.z); + t1.square(); + t1.sub_assign(&ysquared); + t1.sub_assign(&zsquared); + t1.mul_assign(&zsquared); + + // t2 corresponds to line 4 + let mut t2 = t0; + t2.sub_assign(&r.x); + + // t3 corresponds to line 5 + let mut t3 = t2; + t3.square(); + + // t4 corresponds to line 6 + let mut t4 = t3; + t4.double(); + t4.double(); + + // t5 corresponds to line 7 + let mut t5 = t4; + t5.mul_assign(&t2); + + // t6 corresponds to line 8 + let mut t6 = t1; + t6.sub_assign(&r.y); + t6.sub_assign(&r.y); + + // t9 corresponds to line 9 + let mut t9 = t6; + t9.mul_assign(&q.x); + + // corresponds to line 10 + let mut t7 = t4; + t7.mul_assign(&r.x); + + // corresponds to line 11, but assigns to r.x instead of T.x + r.x = t6; + r.x.square(); + r.x.sub_assign(&t5); + r.x.sub_assign(&t7); + r.x.sub_assign(&t7); + + // corresponds to line 12, but assigns to r.z instead of T.z + r.z.add_assign(&t2); + r.z.square(); + r.z.sub_assign(&zsquared); + r.z.sub_assign(&t3); + + // corresponds to line 13 + let mut t10 = q.y; + t10.add_assign(&r.z); + + // corresponds to line 14 + let mut t8 = t7; + t8.sub_assign(&r.x); + t8.mul_assign(&t6); + + // corresponds to line 15 + t0 = r.y; + t0.mul_assign(&t5); + t0.double(); + + // corresponds to line 12, but assigns to r.y instead of T.y + r.y = t8; + r.y.sub_assign(&t0); + + // corresponds to line 17 + t10.square(); + t10.sub_assign(&ysquared); + + let mut ztsquared = r.z; + ztsquared.square(); + + t10.sub_assign(&ztsquared); + + // corresponds to line 18 + t9.double(); + t9.sub_assign(&t10); + + // t10 = 2*Zt from Algo 27, line 19 + t10 = r.z; + t10.double(); + + // t1 = first multiplicator of line 21 + t6.negate(); + + t1 = t6; + t1.double(); + + // t9 corresponds to t9 from Algo 27 + (t10, t1, t9) + } + + let mut coeffs = vec![]; + let mut r: G2 = q.into(); + + let mut negq = q; + negq.negate(); + + for i in (1..SIX_U_PLUS_2_NAF.len()).rev() { + coeffs.push(doubling_step(& mut r)); + let x = SIX_U_PLUS_2_NAF[i-1]; + match x { + 1 => { + coeffs.push(addition_step(&mut r, &q)); + } + -1 => { + coeffs.push(addition_step(&mut r, &negq)); + } + _ => continue, + } + } + + let mut q1 = q; + + q1.x.c1.negate(); + q1.x.mul_assign(&FROBENIUS_COEFF_FQ6_C1[1]); + + q1.y.c1.negate(); + q1.y.mul_assign(&XI_TO_Q_MINUS_1_OVER_2); + + coeffs.push(addition_step(&mut r, &q1)); + + let mut minusq2 = q; + minusq2.x.mul_assign(&FROBENIUS_COEFF_FQ6_C1[2]); + + coeffs.push(addition_step(&mut r, &minusq2)); + + G2Prepared { + coeffs, + infinity: false, + } + } +} + + +#[cfg(test)] +use rand::{Rand, SeedableRng, XorShiftRng}; + +#[test] +fn test_pairing() { + use {CurveProjective}; + let mut g1 = G1::one(); + + let mut g2 = G2::one(); + g2.double(); + + let pair12 = Bn256::pairing(g1, g2); + + g1 = G1::one(); + g1.double(); + + g2 = G2::one(); + + let pair21 = Bn256::pairing(g1, g2); + + assert_eq!(pair12, pair21); + + // print!("GT = {}\n", pair12); + + g1 = G1::one(); + g1.double(); + g1.double(); + + let pair41 = Bn256::pairing(g1, g2); + + g1 = G1::one(); + g1.double(); + + g2.double(); + + let pair22 = Bn256::pairing(g1, g2); + + assert_eq!(pair41, pair22); + + g1 = G1::one(); + g1.double(); + g1.add_assign(&G1::one()); + + g2 = G2::one(); + g2.double(); + + let pair32 = Bn256::pairing(g1, g2); + + g2 = G2::one(); + g2.double(); + g2.add_assign(&G2::one()); + + g1 = G1::one(); + g1.double(); + + let pair23 = Bn256::pairing(g1, g2); + + assert_eq!(pair23, pair32); + + let mut rng = XorShiftRng::from_seed([0x5dbe6259, 0x8d313d76, 0x3237db17, 0xe5bc0654]); + + for _ in 0..1000 { + let a = Fr::rand(&mut rng); + let b = Fr::rand(&mut rng); + + let mut g1 = G1::one(); + g1.mul_assign(a); + + let mut g2 = G2::one(); + g1.mul_assign(b); + + let pair_ab = Bn256::pairing(g1, g2); + + g1 = G1::one(); + g1.mul_assign(b); + + g2 = G2::one(); + g1.mul_assign(a); + + let pair_ba = Bn256::pairing(g1, g2); + + assert_eq!(pair_ab, pair_ba); + + } + +} + +#[test] +fn random_bilinearity_tests() { + use {CurveProjective}; + use ff::PrimeField; + + let mut rng = XorShiftRng::from_seed([0x5dbe6259, 0x8d313d76, 0x3237db17, 0xe5bc0654]); + + for _ in 0..1000 { + let mut a = G1::one(); + let ka = Fr::rand(&mut rng); + a.mul_assign(ka); + let mut b = G2::one(); + let kb = Fr::rand(&mut rng); + b.mul_assign(kb); + + let c = Fr::rand(&mut rng); + let d = Fr::rand(&mut rng); + + let mut ac = a; + ac.mul_assign(c); + + let mut ad = a; + ad.mul_assign(d); + + let mut bc = b; + bc.mul_assign(c); + + let mut bd = b; + bd.mul_assign(d); + + let acbd = Bn256::pairing(ac, bd); + let adbc = Bn256::pairing(ad, bc); + + let mut cd = c; + cd.mul_assign(&d); + + let abcd = Bn256::pairing(a, b).pow(cd.into_repr()); + + assert_eq!(acbd, adbc); + assert_eq!(acbd, abcd); + } +} + +#[test] +fn bn256_engine_tests() { + ::tests::engine::engine_tests::(); +} diff --git a/src/lib.rs b/src/lib.rs index bbced76..4d2051f 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -2,17 +2,16 @@ // common mistakes or strange code patterns. If the `cargo-clippy` feature // is provided, all compiler warnings are prohibited. #![cfg_attr(feature = "cargo-clippy", deny(warnings))] -#![cfg_attr(feature = "cargo-clippy", allow(inline_always))] -#![cfg_attr(feature = "cargo-clippy", allow(too_many_arguments))] -#![cfg_attr(feature = "cargo-clippy", allow(unreadable_literal))] -#![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 = "cargo-clippy", allow(inline_always))] +// #![cfg_attr(feature = "cargo-clippy", allow(too_many_arguments))] +// #![cfg_attr(feature = "cargo-clippy", allow(unreadable_literal))] +// #![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))] // Force public structures to implement Debug #![deny(missing_debug_implementations)] extern crate byteorder; -#[macro_use] extern crate ff; extern crate rand; @@ -20,6 +19,7 @@ extern crate rand; pub mod tests; pub mod bls12_381; +pub mod bn256; mod wnaf; pub use self::wnaf::Wnaf; diff --git a/src/tests/curve.rs b/src/tests/curve.rs index bb0406c..a398a73 100644 --- a/src/tests/curve.rs +++ b/src/tests/curve.rs @@ -61,7 +61,6 @@ pub fn curve_tests() { random_multiplication_tests::(); random_doubling_tests::(); random_negation_tests::(); - random_transformation_tests::(); random_wnaf_tests::(); random_encoding_tests::(); } @@ -345,7 +344,46 @@ fn random_addition_tests() { } } -fn random_transformation_tests() { +pub fn random_transformation_tests() { + let mut rng = XorShiftRng::from_seed([0x5dbe6259, 0x8d313d76, 0x3237db17, 0xe5bc0654]); + + for _ in 0..1000 { + let g = G::rand(&mut rng); + let g_affine = g.into_affine(); + let g_projective = g_affine.into_projective(); + assert_eq!(g, g_projective); + } + + // Batch normalization + for _ in 0..10 { + let mut v = (0..1000).map(|_| G::rand(&mut rng)).collect::>(); + + use rand::distributions::{IndependentSample, Range}; + let between = Range::new(0, 1000); + // Sprinkle in some normalized points + for _ in 0..5 { + v[between.ind_sample(&mut rng)] = G::zero(); + } + for _ in 0..5 { + let s = between.ind_sample(&mut rng); + v[s] = v[s].into_affine().into_projective(); + } + + let expected_v = v + .iter() + .map(|v| v.into_affine().into_projective()) + .collect::>(); + G::batch_normalization(&mut v); + + for i in &v { + assert!(i.is_normalized()); + } + + assert_eq!(v, expected_v); + } +} + +pub fn random_transformation_tests_with_cofactor() { let mut rng = XorShiftRng::from_seed([0x5dbe6259, 0x8d313d76, 0x3237db17, 0xe5bc0654]); for _ in 0..1000 { @@ -360,6 +398,7 @@ fn random_transformation_tests() { let mut v = (0..1000).map(|_| G::rand(&mut rng)).collect::>(); for i in &v { + assert!(!i.is_normalized()); }