diff --git a/pairing/.gitignore b/pairing/.gitignore new file mode 100644 index 0000000..750c824 --- /dev/null +++ b/pairing/.gitignore @@ -0,0 +1,4 @@ +target/ +**/*.rs.bk +Cargo.lock +.vscode \ No newline at end of file diff --git a/pairing/COPYRIGHT b/pairing/COPYRIGHT new file mode 100644 index 0000000..c3876a4 --- /dev/null +++ b/pairing/COPYRIGHT @@ -0,0 +1,14 @@ +Copyrights in the "pairing" library are retained by their contributors. No +copyright assignment is required to contribute to the "pairing" library. + +The "pairing" library is licensed under either of + + * Apache License, Version 2.0, (see ./LICENSE-APACHE or http://www.apache.org/licenses/LICENSE-2.0) + * MIT license (see ./LICENSE-MIT or http://opensource.org/licenses/MIT) + +at your option. + +Unless you explicitly state otherwise, any contribution intentionally +submitted for inclusion in the work by you, as defined in the Apache-2.0 +license, shall be dual licensed as above, without any additional terms or +conditions. diff --git a/pairing/Cargo.toml b/pairing/Cargo.toml new file mode 100644 index 0000000..8eaac3c --- /dev/null +++ b/pairing/Cargo.toml @@ -0,0 +1,28 @@ +[package] +name = "pairing_ce" + +# Remember to change version string in README.md. +version = "0.18.0" +authors = [ + "Sean Bowe ", + "Jack Grigg ", + "Alex Vlasov ", + "Alex Gluchowski " +] +license = "MIT/Apache-2.0" + +description = "Pairing-friendly elliptic curve library" +documentation = "https://docs.rs/pairing/" +homepage = "https://github.com/matter-labs/pairing" +repository = "https://github.com/matter-labs/pairing" +edition = "2018" + +[dependencies] +rand = "0.4" +byteorder = "1" +ff = {package = "ff_ce", version = "0.7", features = ["derive"]} +#ff = { path = '../ff', package = "ff_ce", features = ["derive"]} + +[features] +expose-arith = [] +default = [] diff --git a/pairing/LICENSE-APACHE b/pairing/LICENSE-APACHE new file mode 100644 index 0000000..16fe87b --- /dev/null +++ b/pairing/LICENSE-APACHE @@ -0,0 +1,201 @@ + Apache License + Version 2.0, January 2004 + http://www.apache.org/licenses/ + +TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + +1. Definitions. + + "License" shall mean the terms and conditions for use, reproduction, + and distribution as defined by Sections 1 through 9 of this document. + + "Licensor" shall mean the copyright owner or entity authorized by + the copyright owner that is granting the License. + + "Legal Entity" shall mean the union of the acting entity and all + other entities that control, are controlled by, or are under common + control with that entity. For the purposes of this definition, + "control" means (i) the power, direct or indirect, to cause the + direction or management of such entity, whether by contract or + otherwise, or (ii) ownership of fifty percent (50%) or more of the + outstanding shares, or (iii) beneficial ownership of such entity. + + "You" (or "Your") shall mean an individual or Legal Entity + exercising permissions granted by this License. + + "Source" form shall mean the preferred form for making modifications, + including but not limited to software source code, documentation + source, and configuration files. + + "Object" form shall mean any form resulting from mechanical + transformation or translation of a Source form, including but + not limited to compiled object code, generated documentation, + and conversions to other media types. + + "Work" shall mean the work of authorship, whether in Source or + Object form, made available under the License, as indicated by a + copyright notice that is included in or attached to the work + (an example is provided in the Appendix below). + + "Derivative Works" shall mean any work, whether in Source or Object + form, that is based on (or derived from) the Work and for which the + editorial revisions, annotations, elaborations, or other modifications + represent, as a whole, an original work of authorship. For the purposes + of this License, Derivative Works shall not include works that remain + separable from, or merely link (or bind by name) to the interfaces of, + the Work and Derivative Works thereof. + + "Contribution" shall mean any work of authorship, including + the original version of the Work and any modifications or additions + to that Work or Derivative Works thereof, that is intentionally + submitted to Licensor for inclusion in the Work by the copyright owner + or by an individual or Legal Entity authorized to submit on behalf of + the copyright owner. For the purposes of this definition, "submitted" + means any form of electronic, verbal, or written communication sent + to the Licensor or its representatives, including but not limited to + communication on electronic mailing lists, source code control systems, + and issue tracking systems that are managed by, or on behalf of, the + Licensor for the purpose of discussing and improving the Work, but + excluding communication that is conspicuously marked or otherwise + designated in writing by the copyright owner as "Not a Contribution." + + "Contributor" shall mean Licensor and any individual or Legal Entity + on behalf of whom a Contribution has been received by Licensor and + subsequently incorporated within the Work. + +2. Grant of Copyright License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + copyright license to reproduce, prepare Derivative Works of, + publicly display, publicly perform, sublicense, and distribute the + Work and such Derivative Works in Source or Object form. + +3. Grant of Patent License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + (except as stated in this section) patent license to make, have made, + use, offer to sell, sell, import, and otherwise transfer the Work, + where such license applies only to those patent claims licensable + by such Contributor that are necessarily infringed by their + Contribution(s) alone or by combination of their Contribution(s) + with the Work to which such Contribution(s) was submitted. If You + institute patent litigation against any entity (including a + cross-claim or counterclaim in a lawsuit) alleging that the Work + or a Contribution incorporated within the Work constitutes direct + or contributory patent infringement, then any patent licenses + granted to You under this License for that Work shall terminate + as of the date such litigation is filed. + +4. Redistribution. You may reproduce and distribute copies of the + Work or Derivative Works thereof in any medium, with or without + modifications, and in Source or Object form, provided that You + meet the following conditions: + + (a) You must give any other recipients of the Work or + Derivative Works a copy of this License; and + + (b) You must cause any modified files to carry prominent notices + stating that You changed the files; and + + (c) You must retain, in the Source form of any Derivative Works + that You distribute, all copyright, patent, trademark, and + attribution notices from the Source form of the Work, + excluding those notices that do not pertain to any part of + the Derivative Works; and + + (d) If the Work includes a "NOTICE" text file as part of its + distribution, then any Derivative Works that You distribute must + include a readable copy of the attribution notices contained + within such NOTICE file, excluding those notices that do not + pertain to any part of the Derivative Works, in at least one + of the following places: within a NOTICE text file distributed + as part of the Derivative Works; within the Source form or + documentation, if provided along with the Derivative Works; or, + within a display generated by the Derivative Works, if and + wherever such third-party notices normally appear. The contents + of the NOTICE file are for informational purposes only and + do not modify the License. You may add Your own attribution + notices within Derivative Works that You distribute, alongside + or as an addendum to the NOTICE text from the Work, provided + that such additional attribution notices cannot be construed + as modifying the License. + + You may add Your own copyright statement to Your modifications and + may provide additional or different license terms and conditions + for use, reproduction, or distribution of Your modifications, or + for any such Derivative Works as a whole, provided Your use, + reproduction, and distribution of the Work otherwise complies with + the conditions stated in this License. + +5. Submission of Contributions. Unless You explicitly state otherwise, + any Contribution intentionally submitted for inclusion in the Work + by You to the Licensor shall be under the terms and conditions of + this License, without any additional terms or conditions. + Notwithstanding the above, nothing herein shall supersede or modify + the terms of any separate license agreement you may have executed + with Licensor regarding such Contributions. + +6. Trademarks. This License does not grant permission to use the trade + names, trademarks, service marks, or product names of the Licensor, + except as required for reasonable and customary use in describing the + origin of the Work and reproducing the content of the NOTICE file. + +7. Disclaimer of Warranty. Unless required by applicable law or + agreed to in writing, Licensor provides the Work (and each + Contributor provides its Contributions) on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied, including, without limitation, any warranties or conditions + of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A + PARTICULAR PURPOSE. You are solely responsible for determining the + appropriateness of using or redistributing the Work and assume any + risks associated with Your exercise of permissions under this License. + +8. Limitation of Liability. In no event and under no legal theory, + whether in tort (including negligence), contract, or otherwise, + unless required by applicable law (such as deliberate and grossly + negligent acts) or agreed to in writing, shall any Contributor be + liable to You for damages, including any direct, indirect, special, + incidental, or consequential damages of any character arising as a + result of this License or out of the use or inability to use the + Work (including but not limited to damages for loss of goodwill, + work stoppage, computer failure or malfunction, or any and all + other commercial damages or losses), even if such Contributor + has been advised of the possibility of such damages. + +9. Accepting Warranty or Additional Liability. While redistributing + the Work or Derivative Works thereof, You may choose to offer, + and charge a fee for, acceptance of support, warranty, indemnity, + or other liability obligations and/or rights consistent with this + License. However, in accepting such obligations, You may act only + on Your own behalf and on Your sole responsibility, not on behalf + of any other Contributor, and only if You agree to indemnify, + defend, and hold each Contributor harmless for any liability + incurred by, or claims asserted against, such Contributor by reason + of your accepting any such warranty or additional liability. + +END OF TERMS AND CONDITIONS + +APPENDIX: How to apply the Apache License to your work. + + To apply the Apache License to your work, attach the following + boilerplate notice, with the fields enclosed by brackets "[]" + replaced with your own identifying information. (Don't include + the brackets!) The text should be enclosed in the appropriate + comment syntax for the file format. We also recommend that a + file or class name and description of purpose be included on the + same "printed page" as the copyright notice for easier + identification within third-party archives. + +Copyright [yyyy] [name of copyright owner] + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. diff --git a/pairing/LICENSE-MIT b/pairing/LICENSE-MIT new file mode 100644 index 0000000..31aa793 --- /dev/null +++ b/pairing/LICENSE-MIT @@ -0,0 +1,23 @@ +Permission is hereby granted, free of charge, to any +person obtaining a copy of this software and associated +documentation files (the "Software"), to deal in the +Software without restriction, including without +limitation the rights to use, copy, modify, merge, +publish, distribute, sublicense, and/or sell copies of +the Software, and to permit persons to whom the Software +is furnished to do so, subject to the following +conditions: + +The above copyright notice and this permission notice +shall be included in all copies or substantial portions +of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF +ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED +TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A +PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT +SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY +CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION +OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR +IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER +DEALINGS IN THE SOFTWARE. diff --git a/pairing/README.md b/pairing/README.md new file mode 100644 index 0000000..c4347c7 --- /dev/null +++ b/pairing/README.md @@ -0,0 +1,31 @@ +# pairing "community edition" + +Now published as `pairing_ce` to `crates.io` for users convenience. + +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/) + +Bring the `pairing` crate into your project just as you normally would. + +## Security Warnings + +This library does not make any guarantees about constant-time operations, memory access patterns, or resistance to side-channel attacks. + +## License + +Licensed under either of + + * Apache License, Version 2.0, ([LICENSE-APACHE](LICENSE-APACHE) or http://www.apache.org/licenses/LICENSE-2.0) + * MIT license ([LICENSE-MIT](LICENSE-MIT) or http://opensource.org/licenses/MIT) + +at your option. + +### Contribution + +Unless you explicitly state otherwise, any contribution intentionally +submitted for inclusion in the work by you, as defined in the Apache-2.0 +license, shall be dual licensed as above, without any additional terms or +conditions. diff --git a/pairing/benches/bls12_381/ec.rs b/pairing/benches/bls12_381/ec.rs new file mode 100644 index 0000000..cbd0590 --- /dev/null +++ b/pairing/benches/bls12_381/ec.rs @@ -0,0 +1,127 @@ +mod g1 { + use rand::{Rand, SeedableRng, XorShiftRng}; + + use pairing::bls12_381::*; + 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/pairing/benches/bls12_381/fq.rs b/pairing/benches/bls12_381/fq.rs new file mode 100644 index 0000000..053a10c --- /dev/null +++ b/pairing/benches/bls12_381/fq.rs @@ -0,0 +1,268 @@ +use rand::{Rand, SeedableRng, XorShiftRng}; + +use ff::{Field, PrimeField, PrimeFieldRepr, SqrtField}; +use pairing::bls12_381::*; + +#[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/pairing/benches/bls12_381/fq12.rs b/pairing/benches/bls12_381/fq12.rs new file mode 100644 index 0000000..84daca2 --- /dev/null +++ b/pairing/benches/bls12_381/fq12.rs @@ -0,0 +1,94 @@ +use rand::{Rand, SeedableRng, XorShiftRng}; + +use ff::Field; +use pairing::bls12_381::*; + +#[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/pairing/benches/bls12_381/fq2.rs b/pairing/benches/bls12_381/fq2.rs new file mode 100644 index 0000000..521b6ab --- /dev/null +++ b/pairing/benches/bls12_381/fq2.rs @@ -0,0 +1,110 @@ +use rand::{Rand, SeedableRng, XorShiftRng}; + +use ff::{Field, SqrtField}; +use pairing::bls12_381::*; + +#[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/pairing/benches/bls12_381/fr.rs b/pairing/benches/bls12_381/fr.rs new file mode 100644 index 0000000..13b0d0e --- /dev/null +++ b/pairing/benches/bls12_381/fr.rs @@ -0,0 +1,268 @@ +use rand::{Rand, SeedableRng, XorShiftRng}; + +use ff::{Field, PrimeField, PrimeFieldRepr, SqrtField}; +use pairing::bls12_381::*; + +#[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/pairing/benches/bls12_381/mod.rs b/pairing/benches/bls12_381/mod.rs new file mode 100644 index 0000000..9b46c85 --- /dev/null +++ b/pairing/benches/bls12_381/mod.rs @@ -0,0 +1,107 @@ +mod ec; +mod fq; +mod fq12; +mod fq2; +mod fr; + +use rand::{Rand, SeedableRng, XorShiftRng}; + +use pairing::bls12_381::*; +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 = Bls12::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)| Bls12::miller_loop(&[(p, q)])) + .collect(); + + let mut count = 0; + b.iter(|| { + let tmp = Bls12::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 = Bls12::pairing(v[count].0, v[count].1); + count = (count + 1) % SAMPLES; + tmp + }); +} diff --git a/pairing/benches/bn256/ec.rs b/pairing/benches/bn256/ec.rs new file mode 100644 index 0000000..b188f4c --- /dev/null +++ b/pairing/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/pairing/benches/bn256/fq.rs b/pairing/benches/bn256/fq.rs new file mode 100644 index 0000000..85345b3 --- /dev/null +++ b/pairing/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/pairing/benches/bn256/fq12.rs b/pairing/benches/bn256/fq12.rs new file mode 100644 index 0000000..42fca9d --- /dev/null +++ b/pairing/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/pairing/benches/bn256/fq2.rs b/pairing/benches/bn256/fq2.rs new file mode 100644 index 0000000..ee592ca --- /dev/null +++ b/pairing/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/pairing/benches/bn256/fr.rs b/pairing/benches/bn256/fr.rs new file mode 100644 index 0000000..62f0fe7 --- /dev/null +++ b/pairing/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/pairing/benches/bn256/mod.rs b/pairing/benches/bn256/mod.rs new file mode 100644 index 0000000..3559cca --- /dev/null +++ b/pairing/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/pairing/benches/pairing_benches.rs b/pairing/benches/pairing_benches.rs new file mode 100644 index 0000000..757865a --- /dev/null +++ b/pairing/benches/pairing_benches.rs @@ -0,0 +1,9 @@ +#![feature(test)] + +extern crate ff; +extern crate pairing_ce; +extern crate rand; +extern crate test; + +mod bls12_381; +mod bn256; diff --git a/pairing/src/bls12_381/README.md b/pairing/src/bls12_381/README.md new file mode 100644 index 0000000..d3811a1 --- /dev/null +++ b/pairing/src/bls12_381/README.md @@ -0,0 +1,71 @@ +# BLS12-381 + +This is an implementation of the BLS12-381 pairing-friendly elliptic curve construction. + +## BLS12 Parameterization + +BLS12 curves are parameterized by a value *x* such that the base field modulus *q* and subgroup *r* can be computed by: + +* q = (x - 1)2 ((x4 - x2 + 1) / 3) + x +* r = (x4 - x2 + 1) + +Given primes *q* and *r* parameterized as above, we can easily construct an elliptic curve over the prime field F*q* which contains a subgroup of order *r* such that *r* | (*q*12 - 1), giving it an embedding degree of 12. Instantiating its sextic twist over an extension field Fq2 gives rise to an efficient bilinear pairing function between elements of the order *r* subgroups of either curves, into an order *r* multiplicative subgroup of Fq12. + +In zk-SNARK schemes, we require Fr with large 2n roots of unity for performing efficient fast-fourier transforms. As such, guaranteeing that large 2n | (r - 1), or equivalently that *x* has a large 2n factor, gives rise to BLS12 curves suitable for zk-SNARKs. + +Due to recent research, it is estimated by many that *q* should be approximately 384 bits to target 128-bit security. Conveniently, *r* is approximately 256 bits when *q* is approximately 384 bits, making BLS12 curves ideal for 128-bit security. It also makes them ideal for many zk-SNARK applications, as the scalar field can be used for keying material such as embedded curve constructions. + +Many curves match our descriptions, but we require some extra properties for efficiency purposes: + +* *q* should be smaller than 2383, and *r* should be smaller than 2255, so that the most significant bit is unset when using 64-bit or 32-bit limbs. This allows for cheap reductions. +* Fq12 is typically constructed using towers of extension fields. As a byproduct of [research](https://eprint.iacr.org/2011/465.pdf) for BLS curves of embedding degree 24, we can identify subfamilies of BLS12 curves (for our purposes, where x mod 72 = {16, 64}) that produce efficient extension field towers and twisting isomorphisms. +* We desire *x* of small Hamming weight, to increase the performance of the pairing function. + +## BLS12-381 Instantiation + +The BLS12-381 construction is instantiated by `x = -0xd201000000010000`, which produces the largest `q` and smallest Hamming weight of `x` that meets the above requirements. This produces: + +* q = `0x1a0111ea397fe69a4b1ba7b6434bacd764774b84f38512bf6730d2a0f6b0f6241eabfffeb153ffffb9feffffffffaaab` (381 bits) +* r = `0x73eda753299d7d483339d80809a1d80553bda402fffe5bfeffffffff00000001` (255 bits) + +Our extension field tower is constructed as follows: + +1. Fq2 is constructed as Fq(u) / (u2 - β) where β = -1. +2. Fq6 is constructed as Fq2(v) / (v3 - ξ) where ξ = u + 1 +3. Fq12 is constructed as Fq6(w) / (w2 - γ) where γ = v + +Now, we instantiate the elliptic curve E(Fq) : y2 = x3 + 4, and the elliptic curve E'(Fq2) : y2 = x3 + 4(u + 1). + +The group G1 is the *r* order subgroup of E, which has cofactor (x - 1)2 / 3. The group G2 is the *r* order subgroup of E', which has cofactor (x8 - 4x7 + 5x6 - 4x4 + 6x3 - 4x2 - 4x + 13) / 9. + +### Generators + +The generators of G1 and G2 are computed by finding the lexicographically smallest valid `x`-coordinate, and its lexicographically smallest `y`-coordinate and scaling it by the cofactor such that the result is not the point at infinity. + +#### G1 + +``` +x = 3685416753713387016781088315183077757961620795782546409894578378688607592378376318836054947676345821548104185464507 +y = 1339506544944476473020471379941921221584933875938349620426543736416511423956333506472724655353366534992391756441569 +``` + +#### G2 + +``` +x = 3059144344244213709971259814753781636986470325476647558659373206291635324768958432433509563104347017837885763365758*u + 352701069587466618187139116011060144890029952792775240219908644239793785735715026873347600343865175952761926303160 +y = 927553665492332455747201965776037880757740193453592970025027978793976877002675564980949289727957565575433344219582*u + 1985150602287291935568054521177171638300868978215655730859378665066344726373823718423869104263333984641494340347905 +``` + +### Serialization + +* Fq elements are encoded in big-endian form. They occupy 48 bytes in this form. +* Fq2 elements are encoded in big-endian form, meaning that the Fq element c0 + c1 * u is represented by the Fq element c1 followed by the Fq element c0. This means Fq2 elements occupy 96 bytes in this form. +* The group G1 uses Fq elements for coordinates. The group G2 uses Fq2 elements for coordinates. +* G1 and G2 elements can be encoded in uncompressed form (the x-coordinate followed by the y-coordinate) or in compressed form (just the x-coordinate). G1 elements occupy 96 bytes in uncompressed form, and 48 bytes in compressed form. G2 elements occupy 192 bytes in uncompressed form, and 96 bytes in compressed form. + +The most-significant three bits of a G1 or G2 encoding should be masked away before the coordinate(s) are interpreted. These bits are used to unambiguously represent the underlying element: + +* The most significant bit, when set, indicates that the point is in compressed form. Otherwise, the point is in uncompressed form. +* The second-most significant bit indicates that the point is at infinity. If this bit is set, the remaining bits of the group element's encoding should be set to zero. +* The third-most significant bit is set if (and only if) this point is in compressed form _and_ it is not the point at infinity _and_ its y-coordinate is the lexicographically largest of the two associated with the encoded x-coordinate. + diff --git a/pairing/src/bls12_381/ec.rs b/pairing/src/bls12_381/ec.rs new file mode 100644 index 0000000..d4be8cf --- /dev/null +++ b/pairing/src/bls12_381/ec.rs @@ -0,0 +1,2077 @@ +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 + } + } + + fn is_in_correct_subgroup_assuming_on_curve(&self) -> bool { + self.mul($scalarfield::char()).is_zero() + } + } + + impl CurveAffine for $affine { + type Engine = Bls12; + 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) { + let p = p.scale_by_cofactor(); + + if !p.is_zero() { + return p; + } + } + } + } + } + + impl CurveProjective for $projective { + type Engine = Bls12; + 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. (-4 and -4u-4 + // are not cubic residue in their respective fields.) + + // 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. + + // 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::{Bls12, Fq, Fq12, FqRepr, Fr, FrRepr}; + use super::g2::G2Affine; + use ff::{BitIterator, Field, PrimeField, PrimeFieldRepr, SqrtField}; + use rand::{Rand, Rng}; + use std::fmt; + use crate::{RawEncodable, CurveAffine, CurveProjective, EncodedPoint, Engine, GroupDecodingError}; + + curve_impl!( + "G1", + G1, + G1Affine, + G1Prepared, + Fq, + Fr, + G1Uncompressed, + G1Compressed, + G2Affine + ); + + #[derive(Copy, Clone)] + pub struct G1Uncompressed([u8; 96]); + + impl AsRef<[u8]> for G1Uncompressed { + fn as_ref(&self) -> &[u8] { + &self.0 + } + } + + impl AsMut<[u8]> for G1Uncompressed { + fn as_mut(&mut self) -> &mut [u8] { + &mut self.0 + } + } + + 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; 96]) + } + fn size() -> usize { + 96 + } + fn into_affine(&self) -> Result { + let affine = self.into_affine_unchecked()?; + + if !affine.is_on_curve() { + Err(GroupDecodingError::NotOnCurve) + } else if !affine.is_in_correct_subgroup_assuming_on_curve() { + Err(GroupDecodingError::NotInSubgroup) + } 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(G1Affine::zero()) + } else { + Err(GroupDecodingError::UnexpectedInformation) + } + } else { + if copy[0] & (1 << 5) != 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 three most significant bits. + copy[0] &= 0x1f; + + let mut x = FqRepr([0; 6]); + let mut y = FqRepr([0; 6]); + + { + 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 + } + } + + impl RawEncodable for G1Affine { + fn into_raw_uncompressed_le(&self) -> Self::Uncompressed { + let mut res = Self::Uncompressed::empty(); + { + let mut writer = &mut res.0[..]; + + self.x.into_raw_repr().write_le(&mut writer).unwrap(); + self.y.into_raw_repr().write_le(&mut writer).unwrap(); + } + + res + } + + fn from_raw_uncompressed_le_unchecked( + encoded: &Self::Uncompressed, + _infinity: bool + ) -> Result { + let copy = encoded.0; + if copy.iter().all(|b| *b == 0) { + return Ok(Self::zero()); + } + + let mut x = FqRepr([0; 6]); + let mut y = FqRepr([0; 6]); + + { + let mut reader = ©[..]; + x.read_le(&mut reader).unwrap(); + y.read_le(&mut reader).unwrap(); + } + + Ok(G1Affine { + x: Fq::from_raw_repr(x).map_err(|e| { + GroupDecodingError::CoordinateDecodingError("x coordinate", e) + })?, + y: Fq::from_raw_repr(y).map_err(|e| { + GroupDecodingError::CoordinateDecodingError("y coordinate", e) + })?, + infinity: false, + }) + } + + fn from_raw_uncompressed_le(encoded: &Self::Uncompressed, _infinity: bool) -> Result { + let affine = Self::from_raw_uncompressed_le_unchecked(&encoded, _infinity)?; + + if !affine.is_on_curve() { + Err(GroupDecodingError::NotOnCurve) + } else { + Ok(affine) + } + } + } + + #[derive(Copy, Clone)] + pub struct G1Compressed([u8; 48]); + + impl AsRef<[u8]> for G1Compressed { + fn as_ref(&self) -> &[u8] { + &self.0 + } + } + + impl AsMut<[u8]> for G1Compressed { + fn as_mut(&mut self) -> &mut [u8] { + &mut self.0 + } + } + + 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; 48]) + } + fn size() -> usize { + 48 + } + fn into_affine(&self) -> Result { + let affine = self.into_affine_unchecked()?; + + // NB: Decompression guarantees that it is on the curve already. + + if !affine.is_in_correct_subgroup_assuming_on_curve() { + Err(GroupDecodingError::NotInSubgroup) + } 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 isn't set. + 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(G1Affine::zero()) + } else { + Err(GroupDecodingError::UnexpectedInformation) + } + } else { + // Determine if the intended y coordinate must be greater + // lexicographically. + let greatest = copy[0] & (1 << 5) != 0; + + // Unset the three most significant bits. + copy[0] &= 0x1f; + + let mut x = FqRepr([0; 6]); + + { + 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 << 5; + } + } + + // Set highest bit to distinguish this as a compressed element. + res.0[0] |= 1 << 7; + + res + } + } + + impl G1Affine { + fn scale_by_cofactor(&self) -> G1 { + // G1 cofactor = (x - 1)^2 / 3 = 76329603384216526031706109802092473003 + let cofactor = BitIterator::new([0x8c00aaab0000aaab, 0x396c8c005555e156]); + self.mul_bits(cofactor) + } + + 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::Bls12::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, + }; + assert!(!p.is_in_correct_subgroup_assuming_on_curve()); + + let g1 = p.scale_by_cofactor(); + if !g1.is_zero() { + assert_eq!(i, 4); + let g1 = G1Affine::from(g1); + + assert!(g1.is_in_correct_subgroup_assuming_on_curve()); + + assert_eq!(g1, G1Affine::one()); + break; + } + } + + i += 1; + x.add_assign(&Fq::one()); + } + } + + #[test] + fn g1_test_is_valid() { + // Reject point on isomorphic twist (b = 24) + { + let p = G1Affine { + x: Fq::from_repr(FqRepr([ + 0xc58d887b66c035dc, + 0x10cbfd301d553822, + 0xaf23e064f1131ee5, + 0x9fe83b1b4a5d648d, + 0xf583cc5a508f6a40, + 0xc3ad2aefde0bb13, + ])).unwrap(), + y: Fq::from_repr(FqRepr([ + 0x60aa6f9552f03aae, + 0xecd01d5181300d35, + 0x8af1cdb8aa8ce167, + 0xe760f57922998c9d, + 0x953703f5795a39e5, + 0xfe3ae0922df702c, + ])).unwrap(), + infinity: false, + }; + assert!(!p.is_on_curve()); + assert!(p.is_in_correct_subgroup_assuming_on_curve()); + } + + // Reject point on a twist (b = 3) + { + let p = G1Affine { + x: Fq::from_repr(FqRepr([ + 0xee6adf83511e15f5, + 0x92ddd328f27a4ba6, + 0xe305bd1ac65adba7, + 0xea034ee2928b30a8, + 0xbd8833dc7c79a7f7, + 0xe45c9f0c0438675, + ])).unwrap(), + y: Fq::from_repr(FqRepr([ + 0x3b450eb1ab7b5dad, + 0xa65cb81e975e8675, + 0xaa548682b21726e5, + 0x753ddf21a2601d20, + 0x532d0b640bd3ff8b, + 0x118d2c543f031102, + ])).unwrap(), + infinity: false, + }; + assert!(!p.is_on_curve()); + assert!(!p.is_in_correct_subgroup_assuming_on_curve()); + } + + // Reject point in an invalid subgroup + // There is only one r-order subgroup, as r does not divide the cofactor. + { + let p = G1Affine { + x: Fq::from_repr(FqRepr([ + 0x76e1c971c6db8fe8, + 0xe37e1a610eff2f79, + 0x88ae9c499f46f0c0, + 0xf35de9ce0d6b4e84, + 0x265bddd23d1dec54, + 0x12a8778088458308, + ])).unwrap(), + y: Fq::from_repr(FqRepr([ + 0x8a22defa0d526256, + 0xc57ca55456fcb9ae, + 0x1ba194e89bab2610, + 0x921beef89d4f29df, + 0x5b6fda44ad85fa78, + 0xed74ab9f302cbe0, + ])).unwrap(), + infinity: false, + }; + assert!(p.is_on_curve()); + assert!(!p.is_in_correct_subgroup_assuming_on_curve()); + } + } + + #[test] + fn test_g1_addition_correctness() { + let mut p = G1 { + x: Fq::from_repr(FqRepr([ + 0x47fd1f891d6e8bbf, + 0x79a3b0448f31a2aa, + 0x81f3339e5f9968f, + 0x485e77d50a5df10d, + 0x4c6fcac4b55fd479, + 0x86ed4d9906fb064, + ])).unwrap(), + y: Fq::from_repr(FqRepr([ + 0xd25ee6461538c65, + 0x9f3bbb2ecd3719b9, + 0xa06fd3f1e540910d, + 0xcefca68333c35288, + 0x570c8005f8573fa6, + 0x152ca696fe034442, + ])).unwrap(), + z: Fq::one(), + }; + + p.add_assign(&G1 { + x: Fq::from_repr(FqRepr([ + 0xeec78f3096213cbf, + 0xa12beb1fea1056e6, + 0xc286c0211c40dd54, + 0x5f44314ec5e3fb03, + 0x24e8538737c6e675, + 0x8abd623a594fba8, + ])).unwrap(), + y: Fq::from_repr(FqRepr([ + 0x6b0528f088bb7044, + 0x2fdeb5c82917ff9e, + 0x9a5181f2fac226ad, + 0xd65104c6f95a872a, + 0x1f2998a5a9c61253, + 0xe74846154a9e44, + ])).unwrap(), + z: Fq::one(), + }); + + let p = G1Affine::from(p); + + assert_eq!( + p, + G1Affine { + x: Fq::from_repr(FqRepr([ + 0x6dd3098f22235df, + 0xe865d221c8090260, + 0xeb96bb99fa50779f, + 0xc4f9a52a428e23bb, + 0xd178b28dd4f407ef, + 0x17fb8905e9183c69 + ])).unwrap(), + y: Fq::from_repr(FqRepr([ + 0xd0de9d65292b7710, + 0xf6a05f2bcf1d9ca7, + 0x1040e27012f20b64, + 0xeec8d1a5b7466c58, + 0x4bc362649dce6376, + 0x430cbdc5455b00a + ])).unwrap(), + infinity: false, + } + ); + } + + #[test] + fn test_g1_doubling_correctness() { + let mut p = G1 { + x: Fq::from_repr(FqRepr([ + 0x47fd1f891d6e8bbf, + 0x79a3b0448f31a2aa, + 0x81f3339e5f9968f, + 0x485e77d50a5df10d, + 0x4c6fcac4b55fd479, + 0x86ed4d9906fb064, + ])).unwrap(), + y: Fq::from_repr(FqRepr([ + 0xd25ee6461538c65, + 0x9f3bbb2ecd3719b9, + 0xa06fd3f1e540910d, + 0xcefca68333c35288, + 0x570c8005f8573fa6, + 0x152ca696fe034442, + ])).unwrap(), + z: Fq::one(), + }; + + p.double(); + + let p = G1Affine::from(p); + + assert_eq!( + p, + G1Affine { + x: Fq::from_repr(FqRepr([ + 0xf939ddfe0ead7018, + 0x3b03942e732aecb, + 0xce0e9c38fdb11851, + 0x4b914c16687dcde0, + 0x66c8baf177d20533, + 0xaf960cff3d83833 + ])).unwrap(), + y: Fq::from_repr(FqRepr([ + 0x3f0675695f5177a8, + 0x2b6d82ae178a1ba0, + 0x9096380dd8e51b11, + 0x1771a65b60572f4e, + 0x8b547c1313b27555, + 0x135075589a687b1e + ])).unwrap(), + infinity: false, + } + ); + } + + #[test] + fn test_g1_same_y() { + // Test the addition of two points with different x coordinates + // but the same y coordinate. + + // x1 = 128100205326445210408953809171070606737678357140298133325128175840781723996595026100005714405541449960643523234125 + // x2 = 3821408151224848222394078037104966877485040835569514006839342061575586899845797797516352881516922679872117658572470 + // y = 2291134451313223670499022936083127939567618746216464377735567679979105510603740918204953301371880765657042046687078 + + let a = G1Affine { + x: Fq::from_repr(FqRepr([ + 0xea431f2cc38fc94d, + 0x3ad2354a07f5472b, + 0xfe669f133f16c26a, + 0x71ffa8021531705, + 0x7418d484386d267, + 0xd5108d8ff1fbd6, + ])).unwrap(), + y: Fq::from_repr(FqRepr([ + 0xa776ccbfe9981766, + 0x255632964ff40f4a, + 0xc09744e650b00499, + 0x520f74773e74c8c3, + 0x484c8fc982008f0, + 0xee2c3d922008cc6, + ])).unwrap(), + infinity: false, + }; + + let b = G1Affine { + x: Fq::from_repr(FqRepr([ + 0xe06cdb156b6356b6, + 0xd9040b2d75448ad9, + 0xe702f14bb0e2aca5, + 0xc6e05201e5f83991, + 0xf7c75910816f207c, + 0x18d4043e78103106, + ])).unwrap(), + y: Fq::from_repr(FqRepr([ + 0xa776ccbfe9981766, + 0x255632964ff40f4a, + 0xc09744e650b00499, + 0x520f74773e74c8c3, + 0x484c8fc982008f0, + 0xee2c3d922008cc6, + ])).unwrap(), + infinity: false, + }; + + // Expected + // x = 52901198670373960614757979459866672334163627229195745167587898707663026648445040826329033206551534205133090753192 + // y = 1711275103908443722918766889652776216989264073722543507596490456144926139887096946237734327757134898380852225872709 + let c = G1Affine { + x: Fq::from_repr(FqRepr([ + 0xef4f05bdd10c8aa8, + 0xad5bf87341a2df9, + 0x81c7424206b78714, + 0x9676ff02ec39c227, + 0x4c12c15d7e55b9f3, + 0x57fd1e317db9bd, + ])).unwrap(), + y: Fq::from_repr(FqRepr([ + 0x1288334016679345, + 0xf955cd68615ff0b5, + 0xa6998dbaa600f18a, + 0x1267d70db51049fb, + 0x4696deb9ab2ba3e7, + 0xb1e4e11177f59d4, + ])).unwrap(), + infinity: false, + }; + + assert!(a.is_on_curve() && a.is_in_correct_subgroup_assuming_on_curve()); + assert!(b.is_on_curve() && b.is_in_correct_subgroup_assuming_on_curve()); + assert!(c.is_on_curve() && c.is_in_correct_subgroup_assuming_on_curve()); + + let mut tmp1 = a.into_projective(); + tmp1.add_assign(&b.into_projective()); + assert_eq!(tmp1.into_affine(), c); + assert_eq!(tmp1, c.into_projective()); + + let mut tmp2 = a.into_projective(); + tmp2.add_assign_mixed(&b); + assert_eq!(tmp2.into_affine(), c); + assert_eq!(tmp2, c.into_projective()); + } + + #[test] + fn g1_curve_tests() { + crate::tests::curve::curve_tests::(); + crate::tests::curve::random_transformation_tests_with_cofactor::(); + } +} + +pub mod g2 { + use super::super::{Bls12, 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 crate::{CurveAffine, CurveProjective, EncodedPoint, Engine, GroupDecodingError}; + + curve_impl!( + "G2", + G2, + G2Affine, + G2Prepared, + Fq2, + Fr, + G2Uncompressed, + G2Compressed, + G1Affine + ); + + #[derive(Copy, Clone)] + pub struct G2Uncompressed([u8; 192]); + + impl AsRef<[u8]> for G2Uncompressed { + fn as_ref(&self) -> &[u8] { + &self.0 + } + } + + impl AsMut<[u8]> for G2Uncompressed { + fn as_mut(&mut self) -> &mut [u8] { + &mut self.0 + } + } + + 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; 192]) + } + fn size() -> usize { + 192 + } + fn into_affine(&self) -> Result { + let affine = self.into_affine_unchecked()?; + + if !affine.is_on_curve() { + Err(GroupDecodingError::NotOnCurve) + } else if !affine.is_in_correct_subgroup_assuming_on_curve() { + Err(GroupDecodingError::NotInSubgroup) + } 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 { + if copy[0] & (1 << 5) != 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 three most significant bits. + copy[0] &= 0x1f; + + let mut x_c0 = FqRepr([0; 6]); + let mut x_c1 = FqRepr([0; 6]); + let mut y_c0 = FqRepr([0; 6]); + let mut y_c1 = FqRepr([0; 6]); + + { + 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; 96]); + + impl AsRef<[u8]> for G2Compressed { + fn as_ref(&self) -> &[u8] { + &self.0 + } + } + + impl AsMut<[u8]> for G2Compressed { + fn as_mut(&mut self) -> &mut [u8] { + &mut self.0 + } + } + + 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; 96]) + } + fn size() -> usize { + 96 + } + fn into_affine(&self) -> Result { + let affine = self.into_affine_unchecked()?; + + // NB: Decompression guarantees that it is on the curve already. + + if !affine.is_in_correct_subgroup_assuming_on_curve() { + Err(GroupDecodingError::NotInSubgroup) + } 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 isn't set. + 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 { + // Determine if the intended y coordinate must be greater + // lexicographically. + let greatest = copy[0] & (1 << 5) != 0; + + // Unset the three most significant bits. + copy[0] &= 0x1f; + + let mut x_c1 = FqRepr([0; 6]); + let mut x_c0 = FqRepr([0; 6]); + + { + 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 << 5; + } + } + + // Set highest bit to distinguish this as a compressed element. + 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 { + Fq2 { + c0: super::super::fq::B_COEFF, + c1: super::super::fq::B_COEFF, + } + } + + fn scale_by_cofactor(&self) -> G2 { + // G2 cofactor = (x^8 - 4 x^7 + 5 x^6) - (4 x^4 + 6 x^3 - 4 x^2 - 4 x + 13) // 9 + // 0x5d543a95414e7f1091d50792876a202cd91de4547085abaa68a205b2e5a7ddfa628f1cb4d9e82ef21537e293a6691ae1616ec6e786f0c70cf1c38e31c7238e5 + let cofactor = BitIterator::new([ + 0xcf1c38e31c7238e5, + 0x1616ec6e786f0c70, + 0x21537e293a6691ae, + 0xa628f1cb4d9e82ef, + 0xa68a205b2e5a7ddf, + 0xcd91de4547085aba, + 0x91d50792876a202, + 0x5d543a95414e7f1, + ]); + self.mul_bits(cofactor) + } + + fn perform_pairing(&self, other: &G1Affine) -> Fq12 { + super::super::Bls12::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, + } + + #[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, + }; + + assert!(!p.is_in_correct_subgroup_assuming_on_curve()); + + let g2 = p.scale_by_cofactor(); + if !g2.is_zero() { + assert_eq!(i, 2); + let g2 = G2Affine::from(g2); + + assert!(g2.is_in_correct_subgroup_assuming_on_curve()); + assert_eq!(g2, G2Affine::one()); + break; + } + } + + i += 1; + x.add_assign(&Fq2::one()); + } + } + + #[test] + fn g2_test_is_valid() { + // Reject point on isomorphic twist (b = 3 * (u + 1)) + { + let p = G2Affine { + x: Fq2 { + c0: Fq::from_repr(FqRepr([ + 0xa757072d9fa35ba9, + 0xae3fb2fb418f6e8a, + 0xc1598ec46faa0c7c, + 0x7a17a004747e3dbe, + 0xcc65406a7c2e5a73, + 0x10b8c03d64db4d0c, + ])).unwrap(), + c1: Fq::from_repr(FqRepr([ + 0xd30e70fe2f029778, + 0xda30772df0f5212e, + 0x5b47a9ff9a233a50, + 0xfb777e5b9b568608, + 0x789bac1fec71a2b9, + 0x1342f02e2da54405, + ])).unwrap(), + }, + y: Fq2 { + c0: Fq::from_repr(FqRepr([ + 0xfe0812043de54dca, + 0xe455171a3d47a646, + 0xa493f36bc20be98a, + 0x663015d9410eb608, + 0x78e82a79d829a544, + 0x40a00545bb3c1e, + ])).unwrap(), + c1: Fq::from_repr(FqRepr([ + 0x4709802348e79377, + 0xb5ac4dc9204bcfbd, + 0xda361c97d02f42b2, + 0x15008b1dc399e8df, + 0x68128fd0548a3829, + 0x16a613db5c873aaa, + ])).unwrap(), + }, + infinity: false, + }; + assert!(!p.is_on_curve()); + assert!(p.is_in_correct_subgroup_assuming_on_curve()); + } + + // Reject point on a twist (b = 2 * (u + 1)) + { + let p = G2Affine { + x: Fq2 { + c0: Fq::from_repr(FqRepr([ + 0xf4fdfe95a705f917, + 0xc2914df688233238, + 0x37c6b12cca35a34b, + 0x41abba710d6c692c, + 0xffcc4b2b62ce8484, + 0x6993ec01b8934ed, + ])).unwrap(), + c1: Fq::from_repr(FqRepr([ + 0xb94e92d5f874e26, + 0x44516408bc115d95, + 0xe93946b290caa591, + 0xa5a0c2b7131f3555, + 0x83800965822367e7, + 0x10cf1d3ad8d90bfa, + ])).unwrap(), + }, + y: Fq2 { + c0: Fq::from_repr(FqRepr([ + 0xbf00334c79701d97, + 0x4fe714f9ff204f9a, + 0xab70b28002f3d825, + 0x5a9171720e73eb51, + 0x38eb4fd8d658adb7, + 0xb649051bbc1164d, + ])).unwrap(), + c1: Fq::from_repr(FqRepr([ + 0x9225814253d7df75, + 0xc196c2513477f887, + 0xe05e2fbd15a804e0, + 0x55f2b8efad953e04, + 0x7379345eda55265e, + 0x377f2e6208fd4cb, + ])).unwrap(), + }, + infinity: false, + }; + assert!(!p.is_on_curve()); + assert!(!p.is_in_correct_subgroup_assuming_on_curve()); + } + + // Reject point in an invalid subgroup + // There is only one r-order subgroup, as r does not divide the cofactor. + { + let p = G2Affine { + x: Fq2 { + c0: Fq::from_repr(FqRepr([ + 0x262cea73ea1906c, + 0x2f08540770fabd6, + 0x4ceb92d0a76057be, + 0x2199bc19c48c393d, + 0x4a151b732a6075bf, + 0x17762a3b9108c4a7, + ])).unwrap(), + c1: Fq::from_repr(FqRepr([ + 0x26f461e944bbd3d1, + 0x298f3189a9cf6ed6, + 0x74328ad8bc2aa150, + 0x7e147f3f9e6e241, + 0x72a9b63583963fff, + 0x158b0083c000462, + ])).unwrap(), + }, + y: Fq2 { + c0: Fq::from_repr(FqRepr([ + 0x91fb0b225ecf103b, + 0x55d42edc1dc46ba0, + 0x43939b11997b1943, + 0x68cad19430706b4d, + 0x3ccfb97b924dcea8, + 0x1660f93434588f8d, + ])).unwrap(), + c1: Fq::from_repr(FqRepr([ + 0xaaed3985b6dcb9c7, + 0xc1e985d6d898d9f4, + 0x618bd2ac3271ac42, + 0x3940a2dbb914b529, + 0xbeb88137cf34f3e7, + 0x1699ee577c61b694, + ])).unwrap(), + }, + infinity: false, + }; + assert!(p.is_on_curve()); + assert!(!p.is_in_correct_subgroup_assuming_on_curve()); + } + } + + #[test] + fn test_g2_addition_correctness() { + let mut p = G2 { + x: Fq2 { + c0: Fq::from_repr(FqRepr([ + 0x6c994cc1e303094e, + 0xf034642d2c9e85bd, + 0x275094f1352123a9, + 0x72556c999f3707ac, + 0x4617f2e6774e9711, + 0x100b2fe5bffe030b, + ])).unwrap(), + c1: Fq::from_repr(FqRepr([ + 0x7a33555977ec608, + 0xe23039d1fe9c0881, + 0x19ce4678aed4fcb5, + 0x4637c4f417667e2e, + 0x93ebe7c3e41f6acc, + 0xde884f89a9a371b, + ])).unwrap(), + }, + y: Fq2 { + c0: Fq::from_repr(FqRepr([ + 0xe073119472e1eb62, + 0x44fb3391fe3c9c30, + 0xaa9b066d74694006, + 0x25fd427b4122f231, + 0xd83112aace35cae, + 0x191b2432407cbb7f, + ])).unwrap(), + c1: Fq::from_repr(FqRepr([ + 0xf68ae82fe97662f5, + 0xe986057068b50b7d, + 0x96c30f0411590b48, + 0x9eaa6d19de569196, + 0xf6a03d31e2ec2183, + 0x3bdafaf7ca9b39b, + ])).unwrap(), + }, + z: Fq2::one(), + }; + + p.add_assign(&G2 { + x: Fq2 { + c0: Fq::from_repr(FqRepr([ + 0xa8c763d25910bdd3, + 0x408777b30ca3add4, + 0x6115fcc12e2769e, + 0x8e73a96b329ad190, + 0x27c546f75ee1f3ab, + 0xa33d27add5e7e82, + ])).unwrap(), + c1: Fq::from_repr(FqRepr([ + 0x93b1ebcd54870dfe, + 0xf1578300e1342e11, + 0x8270dca3a912407b, + 0x2089faf462438296, + 0x828e5848cd48ea66, + 0x141ecbac1deb038b, + ])).unwrap(), + }, + y: Fq2 { + c0: Fq::from_repr(FqRepr([ + 0xf5d2c28857229c3f, + 0x8c1574228757ca23, + 0xe8d8102175f5dc19, + 0x2767032fc37cc31d, + 0xd5ee2aba84fd10fe, + 0x16576ccd3dd0a4e8, + ])).unwrap(), + c1: Fq::from_repr(FqRepr([ + 0x4da9b6f6a96d1dd2, + 0x9657f7da77f1650e, + 0xbc150712f9ffe6da, + 0x31898db63f87363a, + 0xabab040ddbd097cc, + 0x11ad236b9ba02990, + ])).unwrap(), + }, + z: Fq2::one(), + }); + + let p = G2Affine::from(p); + + assert_eq!( + p, + G2Affine { + x: Fq2 { + c0: Fq::from_repr(FqRepr([ + 0xcde7ee8a3f2ac8af, + 0xfc642eb35975b069, + 0xa7de72b7dd0e64b7, + 0xf1273e6406eef9cc, + 0xababd760ff05cb92, + 0xd7c20456617e89 + ])).unwrap(), + c1: Fq::from_repr(FqRepr([ + 0xd1a50b8572cbd2b8, + 0x238f0ac6119d07df, + 0x4dbe924fe5fd6ac2, + 0x8b203284c51edf6b, + 0xc8a0b730bbb21f5e, + 0x1a3b59d29a31274 + ])).unwrap(), + }, + y: Fq2 { + c0: Fq::from_repr(FqRepr([ + 0x9e709e78a8eaa4c9, + 0xd30921c93ec342f4, + 0x6d1ef332486f5e34, + 0x64528ab3863633dc, + 0x159384333d7cba97, + 0x4cb84741f3cafe8 + ])).unwrap(), + c1: Fq::from_repr(FqRepr([ + 0x242af0dc3640e1a4, + 0xe90a73ad65c66919, + 0x2bd7ca7f4346f9ec, + 0x38528f92b689644d, + 0xb6884deec59fb21f, + 0x3c075d3ec52ba90 + ])).unwrap(), + }, + infinity: false, + } + ); + } + + #[test] + fn test_g2_doubling_correctness() { + let mut p = G2 { + x: Fq2 { + c0: Fq::from_repr(FqRepr([ + 0x6c994cc1e303094e, + 0xf034642d2c9e85bd, + 0x275094f1352123a9, + 0x72556c999f3707ac, + 0x4617f2e6774e9711, + 0x100b2fe5bffe030b, + ])).unwrap(), + c1: Fq::from_repr(FqRepr([ + 0x7a33555977ec608, + 0xe23039d1fe9c0881, + 0x19ce4678aed4fcb5, + 0x4637c4f417667e2e, + 0x93ebe7c3e41f6acc, + 0xde884f89a9a371b, + ])).unwrap(), + }, + y: Fq2 { + c0: Fq::from_repr(FqRepr([ + 0xe073119472e1eb62, + 0x44fb3391fe3c9c30, + 0xaa9b066d74694006, + 0x25fd427b4122f231, + 0xd83112aace35cae, + 0x191b2432407cbb7f, + ])).unwrap(), + c1: Fq::from_repr(FqRepr([ + 0xf68ae82fe97662f5, + 0xe986057068b50b7d, + 0x96c30f0411590b48, + 0x9eaa6d19de569196, + 0xf6a03d31e2ec2183, + 0x3bdafaf7ca9b39b, + ])).unwrap(), + }, + z: Fq2::one(), + }; + + p.double(); + + let p = G2Affine::from(p); + + assert_eq!( + p, + G2Affine { + x: Fq2 { + c0: Fq::from_repr(FqRepr([ + 0x91ccb1292727c404, + 0x91a6cb182438fad7, + 0x116aee59434de902, + 0xbcedcfce1e52d986, + 0x9755d4a3926e9862, + 0x18bab73760fd8024 + ])).unwrap(), + c1: Fq::from_repr(FqRepr([ + 0x4e7c5e0a2ae5b99e, + 0x96e582a27f028961, + 0xc74d1cf4ef2d5926, + 0xeb0cf5e610ef4fe7, + 0x7b4c2bae8db6e70b, + 0xf136e43909fca0 + ])).unwrap(), + }, + y: Fq2 { + c0: Fq::from_repr(FqRepr([ + 0x954d4466ab13e58, + 0x3ee42eec614cf890, + 0x853bb1d28877577e, + 0xa5a2a51f7fde787b, + 0x8b92866bc6384188, + 0x81a53fe531d64ef + ])).unwrap(), + c1: Fq::from_repr(FqRepr([ + 0x4c5d607666239b34, + 0xeddb5f48304d14b3, + 0x337167ee6e8e3cb6, + 0xb271f52f12ead742, + 0x244e6c2015c83348, + 0x19e2deae6eb9b441 + ])).unwrap(), + }, + infinity: false, + } + ); + } + + #[test] + fn g2_curve_tests() { + crate::tests::curve::curve_tests::(); + crate::tests::curve::random_transformation_tests_with_cofactor::(); + } +} + +pub use self::g1::*; +pub use self::g2::*; diff --git a/pairing/src/bls12_381/fq.rs b/pairing/src/bls12_381/fq.rs new file mode 100644 index 0000000..601b503 --- /dev/null +++ b/pairing/src/bls12_381/fq.rs @@ -0,0 +1,2246 @@ +use super::fq2::Fq2; +use ff::{Field, PrimeField, PrimeFieldRepr}; + +// B coefficient of BLS12-381 curve, 4. +pub const B_COEFF: Fq = Fq(FqRepr([ + 0xaa270000000cfff3, + 0x53cc0032fc34000a, + 0x478fe97a6b0a807f, + 0xb1d37ebee6ba24d7, + 0x8ec9733bbf78ab2f, + 0x9d645513d83de7e, +])); + +// The generators of G1/G2 are computed by finding the lexicographically smallest valid x coordinate, +// and its lexicographically smallest y coordinate and multiplying it by the cofactor such that the +// result is nonzero. + +// Generator of G1 +// x = 3685416753713387016781088315183077757961620795782546409894578378688607592378376318836054947676345821548104185464507 +// y = 1339506544944476473020471379941921221584933875938349620426543736416511423956333506472724655353366534992391756441569 +pub const G1_GENERATOR_X: Fq = Fq(FqRepr([ + 0x5cb38790fd530c16, + 0x7817fc679976fff5, + 0x154f95c7143ba1c1, + 0xf0ae6acdf3d0e747, + 0xedce6ecc21dbf440, + 0x120177419e0bfb75, +])); +pub const G1_GENERATOR_Y: Fq = Fq(FqRepr([ + 0xbaac93d50ce72271, + 0x8c22631a7918fd8e, + 0xdd595f13570725ce, + 0x51ac582950405194, + 0xe1c8c3fad0059c0, + 0xbbc3efc5008a26a, +])); + +// Generator of G2 +// x = 3059144344244213709971259814753781636986470325476647558659373206291635324768958432433509563104347017837885763365758*u + 352701069587466618187139116011060144890029952792775240219908644239793785735715026873347600343865175952761926303160 +// y = 927553665492332455747201965776037880757740193453592970025027978793976877002675564980949289727957565575433344219582*u + 1985150602287291935568054521177171638300868978215655730859378665066344726373823718423869104263333984641494340347905 +pub const G2_GENERATOR_X_C0: Fq = Fq(FqRepr([ + 0xf5f28fa202940a10, + 0xb3f5fb2687b4961a, + 0xa1a893b53e2ae580, + 0x9894999d1a3caee9, + 0x6f67b7631863366b, + 0x58191924350bcd7, +])); +pub const G2_GENERATOR_X_C1: Fq = Fq(FqRepr([ + 0xa5a9c0759e23f606, + 0xaaa0c59dbccd60c3, + 0x3bb17e18e2867806, + 0x1b1ab6cc8541b367, + 0xc2b6ed0ef2158547, + 0x11922a097360edf3, +])); +pub const G2_GENERATOR_Y_C0: Fq = Fq(FqRepr([ + 0x4c730af860494c4a, + 0x597cfa1f5e369c5a, + 0xe7e6856caa0a635a, + 0xbbefb5e96e0d495f, + 0x7d3a975f0ef25a2, + 0x83fd8e7e80dae5, +])); +pub const G2_GENERATOR_Y_C1: Fq = Fq(FqRepr([ + 0xadc0fc92df64b05d, + 0x18aa270a2b1461dc, + 0x86adac6a3be4eba0, + 0x79495c4ec93da33a, + 0xe7175850a43ccaed, + 0xb2bc2a163de1bf2, +])); + +// Coefficients for the Frobenius automorphism. +pub const FROBENIUS_COEFF_FQ2_C1: [Fq; 2] = [ + // Fq(-1)**(((q^0) - 1) / 2) + Fq(FqRepr([ + 0x760900000002fffd, + 0xebf4000bc40c0002, + 0x5f48985753c758ba, + 0x77ce585370525745, + 0x5c071a97a256ec6d, + 0x15f65ec3fa80e493, + ])), + // Fq(-1)**(((q^1) - 1) / 2) + Fq(FqRepr([ + 0x43f5fffffffcaaae, + 0x32b7fff2ed47fffd, + 0x7e83a49a2e99d69, + 0xeca8f3318332bb7a, + 0xef148d1ea0f4c069, + 0x40ab3263eff0206, + ])), +]; + +pub const FROBENIUS_COEFF_FQ6_C1: [Fq2; 6] = [ + // Fq2(u + 1)**(((q^0) - 1) / 3) + Fq2 { + c0: Fq(FqRepr([ + 0x760900000002fffd, + 0xebf4000bc40c0002, + 0x5f48985753c758ba, + 0x77ce585370525745, + 0x5c071a97a256ec6d, + 0x15f65ec3fa80e493, + ])), + c1: Fq(FqRepr([0x0, 0x0, 0x0, 0x0, 0x0, 0x0])), + }, + // Fq2(u + 1)**(((q^1) - 1) / 3) + Fq2 { + c0: Fq(FqRepr([0x0, 0x0, 0x0, 0x0, 0x0, 0x0])), + c1: Fq(FqRepr([ + 0xcd03c9e48671f071, + 0x5dab22461fcda5d2, + 0x587042afd3851b95, + 0x8eb60ebe01bacb9e, + 0x3f97d6e83d050d2, + 0x18f0206554638741, + ])), + }, + // Fq2(u + 1)**(((q^2) - 1) / 3) + Fq2 { + c0: Fq(FqRepr([ + 0x30f1361b798a64e8, + 0xf3b8ddab7ece5a2a, + 0x16a8ca3ac61577f7, + 0xc26a2ff874fd029b, + 0x3636b76660701c6e, + 0x51ba4ab241b6160, + ])), + c1: Fq(FqRepr([0x0, 0x0, 0x0, 0x0, 0x0, 0x0])), + }, + // Fq2(u + 1)**(((q^3) - 1) / 3) + Fq2 { + c0: Fq(FqRepr([0x0, 0x0, 0x0, 0x0, 0x0, 0x0])), + c1: Fq(FqRepr([ + 0x760900000002fffd, + 0xebf4000bc40c0002, + 0x5f48985753c758ba, + 0x77ce585370525745, + 0x5c071a97a256ec6d, + 0x15f65ec3fa80e493, + ])), + }, + // Fq2(u + 1)**(((q^4) - 1) / 3) + Fq2 { + c0: Fq(FqRepr([ + 0xcd03c9e48671f071, + 0x5dab22461fcda5d2, + 0x587042afd3851b95, + 0x8eb60ebe01bacb9e, + 0x3f97d6e83d050d2, + 0x18f0206554638741, + ])), + c1: Fq(FqRepr([0x0, 0x0, 0x0, 0x0, 0x0, 0x0])), + }, + // Fq2(u + 1)**(((q^5) - 1) / 3) + Fq2 { + c0: Fq(FqRepr([0x0, 0x0, 0x0, 0x0, 0x0, 0x0])), + c1: Fq(FqRepr([ + 0x30f1361b798a64e8, + 0xf3b8ddab7ece5a2a, + 0x16a8ca3ac61577f7, + 0xc26a2ff874fd029b, + 0x3636b76660701c6e, + 0x51ba4ab241b6160, + ])), + }, +]; + +pub const FROBENIUS_COEFF_FQ6_C2: [Fq2; 6] = [ + // Fq2(u + 1)**(((2q^0) - 2) / 3) + Fq2 { + c0: Fq(FqRepr([ + 0x760900000002fffd, + 0xebf4000bc40c0002, + 0x5f48985753c758ba, + 0x77ce585370525745, + 0x5c071a97a256ec6d, + 0x15f65ec3fa80e493, + ])), + c1: Fq(FqRepr([0x0, 0x0, 0x0, 0x0, 0x0, 0x0])), + }, + // Fq2(u + 1)**(((2q^1) - 2) / 3) + Fq2 { + c0: Fq(FqRepr([ + 0x890dc9e4867545c3, + 0x2af322533285a5d5, + 0x50880866309b7e2c, + 0xa20d1b8c7e881024, + 0x14e4f04fe2db9068, + 0x14e56d3f1564853a, + ])), + c1: Fq(FqRepr([0x0, 0x0, 0x0, 0x0, 0x0, 0x0])), + }, + // Fq2(u + 1)**(((2q^2) - 2) / 3) + Fq2 { + c0: Fq(FqRepr([ + 0xcd03c9e48671f071, + 0x5dab22461fcda5d2, + 0x587042afd3851b95, + 0x8eb60ebe01bacb9e, + 0x3f97d6e83d050d2, + 0x18f0206554638741, + ])), + c1: Fq(FqRepr([0x0, 0x0, 0x0, 0x0, 0x0, 0x0])), + }, + // Fq2(u + 1)**(((2q^3) - 2) / 3) + Fq2 { + c0: Fq(FqRepr([ + 0x43f5fffffffcaaae, + 0x32b7fff2ed47fffd, + 0x7e83a49a2e99d69, + 0xeca8f3318332bb7a, + 0xef148d1ea0f4c069, + 0x40ab3263eff0206, + ])), + c1: Fq(FqRepr([0x0, 0x0, 0x0, 0x0, 0x0, 0x0])), + }, + // Fq2(u + 1)**(((2q^4) - 2) / 3) + Fq2 { + c0: Fq(FqRepr([ + 0x30f1361b798a64e8, + 0xf3b8ddab7ece5a2a, + 0x16a8ca3ac61577f7, + 0xc26a2ff874fd029b, + 0x3636b76660701c6e, + 0x51ba4ab241b6160, + ])), + c1: Fq(FqRepr([0x0, 0x0, 0x0, 0x0, 0x0, 0x0])), + }, + // Fq2(u + 1)**(((2q^5) - 2) / 3) + Fq2 { + c0: Fq(FqRepr([ + 0xecfb361b798dba3a, + 0xc100ddb891865a2c, + 0xec08ff1232bda8e, + 0xd5c13cc6f1ca4721, + 0x47222a47bf7b5c04, + 0x110f184e51c5f59, + ])), + c1: Fq(FqRepr([0x0, 0x0, 0x0, 0x0, 0x0, 0x0])), + }, +]; + +// 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([ + 0x760900000002fffd, + 0xebf4000bc40c0002, + 0x5f48985753c758ba, + 0x77ce585370525745, + 0x5c071a97a256ec6d, + 0x15f65ec3fa80e493, + ])), + c1: Fq(FqRepr([0x0, 0x0, 0x0, 0x0, 0x0, 0x0])), + }, + // Fq2(u + 1)**(((q^1) - 1) / 6) + Fq2 { + c0: Fq(FqRepr([ + 0x7089552b319d465, + 0xc6695f92b50a8313, + 0x97e83cccd117228f, + 0xa35baecab2dc29ee, + 0x1ce393ea5daace4d, + 0x8f2220fb0fb66eb, + ])), + c1: Fq(FqRepr([ + 0xb2f66aad4ce5d646, + 0x5842a06bfc497cec, + 0xcf4895d42599d394, + 0xc11b9cba40a8e8d0, + 0x2e3813cbe5a0de89, + 0x110eefda88847faf, + ])), + }, + // Fq2(u + 1)**(((q^2) - 1) / 6) + Fq2 { + c0: Fq(FqRepr([ + 0xecfb361b798dba3a, + 0xc100ddb891865a2c, + 0xec08ff1232bda8e, + 0xd5c13cc6f1ca4721, + 0x47222a47bf7b5c04, + 0x110f184e51c5f59, + ])), + c1: Fq(FqRepr([0x0, 0x0, 0x0, 0x0, 0x0, 0x0])), + }, + // Fq2(u + 1)**(((q^3) - 1) / 6) + Fq2 { + c0: Fq(FqRepr([ + 0x3e2f585da55c9ad1, + 0x4294213d86c18183, + 0x382844c88b623732, + 0x92ad2afd19103e18, + 0x1d794e4fac7cf0b9, + 0xbd592fc7d825ec8, + ])), + c1: Fq(FqRepr([ + 0x7bcfa7a25aa30fda, + 0xdc17dec12a927e7c, + 0x2f088dd86b4ebef1, + 0xd1ca2087da74d4a7, + 0x2da2596696cebc1d, + 0xe2b7eedbbfd87d2, + ])), + }, + // Fq2(u + 1)**(((q^4) - 1) / 6) + Fq2 { + c0: Fq(FqRepr([ + 0x30f1361b798a64e8, + 0xf3b8ddab7ece5a2a, + 0x16a8ca3ac61577f7, + 0xc26a2ff874fd029b, + 0x3636b76660701c6e, + 0x51ba4ab241b6160, + ])), + c1: Fq(FqRepr([0x0, 0x0, 0x0, 0x0, 0x0, 0x0])), + }, + // Fq2(u + 1)**(((q^5) - 1) / 6) + Fq2 { + c0: Fq(FqRepr([ + 0x3726c30af242c66c, + 0x7c2ac1aad1b6fe70, + 0xa04007fbba4b14a2, + 0xef517c3266341429, + 0x95ba654ed2226b, + 0x2e370eccc86f7dd, + ])), + c1: Fq(FqRepr([ + 0x82d83cf50dbce43f, + 0xa2813e53df9d018f, + 0xc6f0caa53c65e181, + 0x7525cf528d50fe95, + 0x4a85ed50f4798a6b, + 0x171da0fd6cf8eebd, + ])), + }, + // Fq2(u + 1)**(((q^6) - 1) / 6) + Fq2 { + c0: Fq(FqRepr([ + 0x43f5fffffffcaaae, + 0x32b7fff2ed47fffd, + 0x7e83a49a2e99d69, + 0xeca8f3318332bb7a, + 0xef148d1ea0f4c069, + 0x40ab3263eff0206, + ])), + c1: Fq(FqRepr([0x0, 0x0, 0x0, 0x0, 0x0, 0x0])), + }, + // Fq2(u + 1)**(((q^7) - 1) / 6) + Fq2 { + c0: Fq(FqRepr([ + 0xb2f66aad4ce5d646, + 0x5842a06bfc497cec, + 0xcf4895d42599d394, + 0xc11b9cba40a8e8d0, + 0x2e3813cbe5a0de89, + 0x110eefda88847faf, + ])), + c1: Fq(FqRepr([ + 0x7089552b319d465, + 0xc6695f92b50a8313, + 0x97e83cccd117228f, + 0xa35baecab2dc29ee, + 0x1ce393ea5daace4d, + 0x8f2220fb0fb66eb, + ])), + }, + // Fq2(u + 1)**(((q^8) - 1) / 6) + Fq2 { + c0: Fq(FqRepr([ + 0xcd03c9e48671f071, + 0x5dab22461fcda5d2, + 0x587042afd3851b95, + 0x8eb60ebe01bacb9e, + 0x3f97d6e83d050d2, + 0x18f0206554638741, + ])), + c1: Fq(FqRepr([0x0, 0x0, 0x0, 0x0, 0x0, 0x0])), + }, + // Fq2(u + 1)**(((q^9) - 1) / 6) + Fq2 { + c0: Fq(FqRepr([ + 0x7bcfa7a25aa30fda, + 0xdc17dec12a927e7c, + 0x2f088dd86b4ebef1, + 0xd1ca2087da74d4a7, + 0x2da2596696cebc1d, + 0xe2b7eedbbfd87d2, + ])), + c1: Fq(FqRepr([ + 0x3e2f585da55c9ad1, + 0x4294213d86c18183, + 0x382844c88b623732, + 0x92ad2afd19103e18, + 0x1d794e4fac7cf0b9, + 0xbd592fc7d825ec8, + ])), + }, + // Fq2(u + 1)**(((q^10) - 1) / 6) + Fq2 { + c0: Fq(FqRepr([ + 0x890dc9e4867545c3, + 0x2af322533285a5d5, + 0x50880866309b7e2c, + 0xa20d1b8c7e881024, + 0x14e4f04fe2db9068, + 0x14e56d3f1564853a, + ])), + c1: Fq(FqRepr([0x0, 0x0, 0x0, 0x0, 0x0, 0x0])), + }, + // Fq2(u + 1)**(((q^11) - 1) / 6) + Fq2 { + c0: Fq(FqRepr([ + 0x82d83cf50dbce43f, + 0xa2813e53df9d018f, + 0xc6f0caa53c65e181, + 0x7525cf528d50fe95, + 0x4a85ed50f4798a6b, + 0x171da0fd6cf8eebd, + ])), + c1: Fq(FqRepr([ + 0x3726c30af242c66c, + 0x7c2ac1aad1b6fe70, + 0xa04007fbba4b14a2, + 0xef517c3266341429, + 0x95ba654ed2226b, + 0x2e370eccc86f7dd, + ])), + }, +]; + +// -((2**384) mod q) mod q +pub const NEGATIVE_ONE: Fq = Fq(FqRepr([ + 0x43f5fffffffcaaae, + 0x32b7fff2ed47fffd, + 0x7e83a49a2e99d69, + 0xeca8f3318332bb7a, + 0xef148d1ea0f4c069, + 0x40ab3263eff0206, +])); + +#[derive(PrimeField)] +#[PrimeFieldModulus = "4002409555221667393417789825735904156556882819939007885332058136124031650490837864442687629129015664037894272559787"] +#[PrimeFieldGenerator = "2"] +pub struct Fq(FqRepr); + +#[test] +fn test_b_coeff() { + assert_eq!(Fq::from_repr(FqRepr::from(4)).unwrap(), B_COEFF); +} + +#[test] +fn test_frob_coeffs() { + let mut nqr = Fq::one(); + nqr.negate(); + + assert_eq!(FROBENIUS_COEFF_FQ2_C1[0], Fq::one()); + assert_eq!( + FROBENIUS_COEFF_FQ2_C1[1], + nqr.pow([ + 0xdcff7fffffffd555, + 0xf55ffff58a9ffff, + 0xb39869507b587b12, + 0xb23ba5c279c2895f, + 0x258dd3db21a5d66b, + 0xd0088f51cbff34d + ]) + ); + + let nqr = Fq2 { + c0: Fq::one(), + c1: Fq::one(), + }; + + assert_eq!(FROBENIUS_COEFF_FQ6_C1[0], Fq2::one()); + assert_eq!( + FROBENIUS_COEFF_FQ6_C1[1], + nqr.pow([ + 0x9354ffffffffe38e, + 0xa395554e5c6aaaa, + 0xcd104635a790520c, + 0xcc27c3d6fbd7063f, + 0x190937e76bc3e447, + 0x8ab05f8bdd54cde + ]) + ); + assert_eq!( + FROBENIUS_COEFF_FQ6_C1[2], + nqr.pow([ + 0xb78e0000097b2f68, + 0xd44f23b47cbd64e3, + 0x5cb9668120b069a9, + 0xccea85f9bf7b3d16, + 0xdba2c8d7adb356d, + 0x9cd75ded75d7429, + 0xfc65c31103284fab, + 0xc58cb9a9b249ee24, + 0xccf734c3118a2e9a, + 0xa0f4304c5a256ce6, + 0xc3f0d2f8e0ba61f8, + 0xe167e192ebca97 + ]) + ); + assert_eq!( + FROBENIUS_COEFF_FQ6_C1[3], + nqr.pow([ + 0xdbc6fcd6f35b9e06, + 0x997dead10becd6aa, + 0x9dbbd24c17206460, + 0x72b97acc6057c45e, + 0xf8e9a230bf0c628e, + 0x647ccb1885c63a7, + 0xce80264fc55bf6ee, + 0x94d8d716c3939fc4, + 0xad78f0eb77ee6ee1, + 0xd6fe49bfe57dc5f9, + 0x2656d6c15c63647, + 0xdf6282f111fa903, + 0x1bdba63e0632b4bb, + 0x6883597bcaa505eb, + 0xa56d4ec90c34a982, + 0x7e4c42823bbe90b2, + 0xf64728aa6dcb0f20, + 0x16e57e16ef152f + ]) + ); + assert_eq!( + FROBENIUS_COEFF_FQ6_C1[4], + nqr.pow([ + 0x4649add3c71c6d90, + 0x43caa6528972a865, + 0xcda8445bbaaa0fbb, + 0xc93dea665662aa66, + 0x2863bc891834481d, + 0x51a0c3f5d4ccbed8, + 0x9210e660f90ccae9, + 0xe2bd6836c546d65e, + 0xf223abbaa7cf778b, + 0xd4f10b222cf11680, + 0xd540f5eff4a1962e, + 0xa123a1f140b56526, + 0x31ace500636a59f6, + 0x3a82bc8c8dfa57a9, + 0x648c511e217fc1f8, + 0x36c17ffd53a4558f, + 0x881bef5fd684eefd, + 0x5d648dbdc5dbb522, + 0x8fd07bf06e5e59b8, + 0x8ddec8a9acaa4b51, + 0x4cc1f8688e2def26, + 0xa74e63cb492c03de, + 0x57c968173d1349bb, + 0x253674e02a866 + ]) + ); + assert_eq!( + FROBENIUS_COEFF_FQ6_C1[5], + nqr.pow([ + 0xf896f792732eb2be, + 0x49c86a6d1dc593a1, + 0xe5b31e94581f91c3, + 0xe3da5cc0a6b20d7f, + 0x822caef950e0bfed, + 0x317ed950b9ee67cd, + 0xffd664016ee3f6cd, + 0x77d991c88810b122, + 0x62e72e635e698264, + 0x905e1a1a2d22814a, + 0xf5b7ab3a3f33d981, + 0x175871b0bc0e25dd, + 0x1e2e9a63df5c3772, + 0xe888b1f7445b149d, + 0x9551c19e5e7e2c24, + 0xecf21939a3d2d6be, + 0xd830dbfdab72dbd4, + 0x7b34af8d622d40c0, + 0x3df6d20a45671242, + 0xaf86bee30e21d98, + 0x41064c1534e5df5d, + 0xf5f6cabd3164c609, + 0xa5d14bdf2b7ee65, + 0xa718c069defc9138, + 0xdb1447e770e3110e, + 0xc1b164a9e90af491, + 0x7180441f9d251602, + 0x1fd3a5e6a9a893e, + 0x1e17b779d54d5db, + 0x3c7afafe3174 + ]) + ); + + assert_eq!(FROBENIUS_COEFF_FQ6_C2[0], Fq2::one()); + assert_eq!( + FROBENIUS_COEFF_FQ6_C2[1], + nqr.pow([ + 0x26a9ffffffffc71c, + 0x1472aaa9cb8d5555, + 0x9a208c6b4f20a418, + 0x984f87adf7ae0c7f, + 0x32126fced787c88f, + 0x11560bf17baa99bc + ]) + ); + assert_eq!( + FROBENIUS_COEFF_FQ6_C2[2], + nqr.pow([ + 0x6f1c000012f65ed0, + 0xa89e4768f97ac9c7, + 0xb972cd024160d353, + 0x99d50bf37ef67a2c, + 0x1b74591af5b66adb, + 0x139aebbdaebae852, + 0xf8cb862206509f56, + 0x8b1973536493dc49, + 0x99ee698623145d35, + 0x41e86098b44ad9cd, + 0x87e1a5f1c174c3f1, + 0x1c2cfc325d7952f + ]) + ); + assert_eq!( + FROBENIUS_COEFF_FQ6_C2[3], + nqr.pow([ + 0xb78df9ade6b73c0c, + 0x32fbd5a217d9ad55, + 0x3b77a4982e40c8c1, + 0xe572f598c0af88bd, + 0xf1d344617e18c51c, + 0xc8f996310b8c74f, + 0x9d004c9f8ab7eddc, + 0x29b1ae2d87273f89, + 0x5af1e1d6efdcddc3, + 0xadfc937fcafb8bf3, + 0x4cadad82b8c6c8f, + 0x1bec505e223f5206, + 0x37b74c7c0c656976, + 0xd106b2f7954a0bd6, + 0x4ada9d9218695304, + 0xfc988504777d2165, + 0xec8e5154db961e40, + 0x2dcafc2dde2a5f + ]) + ); + assert_eq!( + FROBENIUS_COEFF_FQ6_C2[4], + nqr.pow([ + 0x8c935ba78e38db20, + 0x87954ca512e550ca, + 0x9b5088b775541f76, + 0x927bd4ccacc554cd, + 0x50c779123068903b, + 0xa34187eba9997db0, + 0x2421ccc1f21995d2, + 0xc57ad06d8a8dacbd, + 0xe44757754f9eef17, + 0xa9e2164459e22d01, + 0xaa81ebdfe9432c5d, + 0x424743e2816aca4d, + 0x6359ca00c6d4b3ed, + 0x750579191bf4af52, + 0xc918a23c42ff83f0, + 0x6d82fffaa748ab1e, + 0x1037debfad09ddfa, + 0xbac91b7b8bb76a45, + 0x1fa0f7e0dcbcb370, + 0x1bbd9153595496a3, + 0x9983f0d11c5bde4d, + 0x4e9cc796925807bc, + 0xaf92d02e7a269377, + 0x4a6ce9c0550cc + ]) + ); + assert_eq!( + FROBENIUS_COEFF_FQ6_C2[5], + nqr.pow([ + 0xf12def24e65d657c, + 0x9390d4da3b8b2743, + 0xcb663d28b03f2386, + 0xc7b4b9814d641aff, + 0x4595df2a1c17fdb, + 0x62fdb2a173dccf9b, + 0xffacc802ddc7ed9a, + 0xefb3239110216245, + 0xc5ce5cc6bcd304c8, + 0x20bc34345a450294, + 0xeb6f56747e67b303, + 0x2eb0e361781c4bbb, + 0x3c5d34c7beb86ee4, + 0xd11163ee88b6293a, + 0x2aa3833cbcfc5849, + 0xd9e4327347a5ad7d, + 0xb061b7fb56e5b7a9, + 0xf6695f1ac45a8181, + 0x7beda4148ace2484, + 0x15f0d7dc61c43b30, + 0x820c982a69cbbeba, + 0xebed957a62c98c12, + 0x14ba297be56fdccb, + 0x4e3180d3bdf92270, + 0xb6288fcee1c6221d, + 0x8362c953d215e923, + 0xe300883f3a4a2c05, + 0x3fa74bcd535127c, + 0x3c2f6ef3aa9abb6, + 0x78f5f5fc62e8 + ]) + ); + + assert_eq!(FROBENIUS_COEFF_FQ12_C1[0], Fq2::one()); + assert_eq!( + FROBENIUS_COEFF_FQ12_C1[1], + nqr.pow([ + 0x49aa7ffffffff1c7, + 0x51caaaa72e35555, + 0xe688231ad3c82906, + 0xe613e1eb7deb831f, + 0xc849bf3b5e1f223, + 0x45582fc5eeaa66f + ]) + ); + assert_eq!( + FROBENIUS_COEFF_FQ12_C1[2], + nqr.pow([ + 0xdbc7000004bd97b4, + 0xea2791da3e5eb271, + 0x2e5cb340905834d4, + 0xe67542fcdfbd9e8b, + 0x86dd1646bd6d9ab6, + 0x84e6baef6baeba14, + 0x7e32e188819427d5, + 0x62c65cd4d924f712, + 0x667b9a6188c5174d, + 0x507a18262d12b673, + 0xe1f8697c705d30fc, + 0x70b3f0c975e54b + ]) + ); + assert_eq!( + FROBENIUS_COEFF_FQ12_C1[3], + nqr.pow(vec![ + 0x6de37e6b79adcf03, + 0x4cbef56885f66b55, + 0x4edde9260b903230, + 0x395cbd66302be22f, + 0xfc74d1185f863147, + 0x323e658c42e31d3, + 0x67401327e2adfb77, + 0xca6c6b8b61c9cfe2, + 0xd6bc7875bbf73770, + 0xeb7f24dff2bee2fc, + 0x8132b6b60ae31b23, + 0x86fb1417888fd481, + 0x8dedd31f03195a5d, + 0x3441acbde55282f5, + 0x52b6a764861a54c1, + 0x3f2621411ddf4859, + 0xfb23945536e58790, + 0xb72bf0b778a97, + ]) + ); + assert_eq!( + FROBENIUS_COEFF_FQ12_C1[4], + nqr.pow(vec![ + 0xa324d6e9e38e36c8, + 0xa1e5532944b95432, + 0x66d4222ddd5507dd, + 0xe49ef5332b315533, + 0x1431de448c1a240e, + 0xa8d061faea665f6c, + 0x490873307c866574, + 0xf15eb41b62a36b2f, + 0x7911d5dd53e7bbc5, + 0x6a78859116788b40, + 0x6aa07af7fa50cb17, + 0x5091d0f8a05ab293, + 0x98d6728031b52cfb, + 0x1d415e4646fd2bd4, + 0xb246288f10bfe0fc, + 0x9b60bffea9d22ac7, + 0x440df7afeb42777e, + 0x2eb246dee2edda91, + 0xc7e83df8372f2cdc, + 0x46ef6454d65525a8, + 0x2660fc344716f793, + 0xd3a731e5a49601ef, + 0x2be4b40b9e89a4dd, + 0x129b3a7015433, + ]) + ); + assert_eq!( + FROBENIUS_COEFF_FQ12_C1[5], + nqr.pow(vec![ + 0xfc4b7bc93997595f, + 0xa4e435368ee2c9d0, + 0xf2d98f4a2c0fc8e1, + 0xf1ed2e60535906bf, + 0xc116577ca8705ff6, + 0x98bf6ca85cf733e6, + 0x7feb3200b771fb66, + 0x3becc8e444085891, + 0x31739731af34c132, + 0xc82f0d0d169140a5, + 0xfadbd59d1f99ecc0, + 0xbac38d85e0712ee, + 0x8f174d31efae1bb9, + 0x744458fba22d8a4e, + 0x4aa8e0cf2f3f1612, + 0x76790c9cd1e96b5f, + 0x6c186dfed5b96dea, + 0x3d9a57c6b116a060, + 0x1efb690522b38921, + 0x857c35f718710ecc, + 0xa083260a9a72efae, + 0xfafb655e98b26304, + 0x52e8a5ef95bf732, + 0x538c6034ef7e489c, + 0xed8a23f3b8718887, + 0x60d8b254f4857a48, + 0x38c0220fce928b01, + 0x80fe9d2f354d449f, + 0xf0bdbbceaa6aed, + 0x1e3d7d7f18ba, + ]) + ); + assert_eq!( + FROBENIUS_COEFF_FQ12_C1[6], + nqr.pow(vec![ + 0x21219610a012ba3c, + 0xa5c19ad35375325, + 0x4e9df1e497674396, + 0xfb05b717c991c6ef, + 0x4a1265bca93a32f2, + 0xd875ff2a7bdc1f66, + 0xc6d8754736c771b2, + 0x2d80c759ba5a2ae7, + 0x138a20df4b03cc1a, + 0xc22d07fe68e93024, + 0xd1dc474d3b433133, + 0xc22aa5e75044e5c, + 0xf657c6fbf9c17ebf, + 0xc591a794a58660d, + 0x2261850ee1453281, + 0xd17d3bd3b7f5efb4, + 0xf00cec8ec507d01, + 0x2a6a775657a00ae6, + 0x5f098a12ff470719, + 0x409d194e7b5c5afa, + 0x1d66478e982af5b, + 0xda425a5b5e01ca3f, + 0xf77e4f78747e903c, + 0x177d49f73732c6fc, + 0xa9618fecabe0e1f4, + 0xba5337eac90bd080, + 0x66fececdbc35d4e7, + 0xa4cd583203d9206f, + 0x98391632ceeca596, + 0x4946b76e1236ad3f, + 0xa0dec64e60e711a1, + 0xfcb41ed3605013, + 0x8ca8f9692ae1e3a9, + 0xd3078bfc28cc1baf, + 0xf0536f764e982f82, + 0x3125f1a2656, + ]) + ); + assert_eq!( + FROBENIUS_COEFF_FQ12_C1[7], + nqr.pow(vec![ + 0x742754a1f22fdb, + 0x2a1955c2dec3a702, + 0x9747b28c796d134e, + 0xc113a0411f59db79, + 0x3bb0fa929853bfc1, + 0x28c3c25f8f6fb487, + 0xbc2b6c99d3045b34, + 0x98fb67d6badde1fd, + 0x48841d76a24d2073, + 0xd49891145fe93ae6, + 0xc772b9c8e74d4099, + 0xccf4e7b9907755bb, + 0x9cf47b25d42fd908, + 0x5616a0c347fc445d, + 0xff93b7a7ad1b8a6d, + 0xac2099256b78a77a, + 0x7804a95b02892e1c, + 0x5cf59ca7bfd69776, + 0xa7023502acd3c866, + 0xc76f4982fcf8f37, + 0x51862a5a57ac986e, + 0x38b80ed72b1b1023, + 0x4a291812066a61e1, + 0xcd8a685eff45631, + 0x3f40f708764e4fa5, + 0x8aa0441891285092, + 0x9eff60d71cdf0a9, + 0x4fdd9d56517e2bfa, + 0x1f3c80d74a28bc85, + 0x24617417c064b648, + 0x7ddda1e4385d5088, + 0xf9e132b11dd32a16, + 0xcc957cb8ef66ab99, + 0xd4f206d37cb752c5, + 0x40de343f28ad616b, + 0x8d1f24379068f0e3, + 0x6f31d7947ea21137, + 0x27311f9c32184061, + 0x9eea0664cc78ce5f, + 0x7d4151f6fea9a0da, + 0x454096fa75bd571a, + 0x4fe0f20ecb, + ]) + ); + assert_eq!( + FROBENIUS_COEFF_FQ12_C1[8], + nqr.pow(vec![ + 0x802f5720d0b25710, + 0x6714f0a258b85c7c, + 0x31394c90afdf16e, + 0xe9d2b0c64f957b19, + 0xe67c0d9c5e7903ee, + 0x3156fdc5443ea8ef, + 0x7c4c50524d88c892, + 0xc99dc8990c0ad244, + 0xd37ababf3649a896, + 0x76fe4b838ff7a20c, + 0xcf69ee2cec728db3, + 0xb83535548e5f41, + 0x371147684ccb0c23, + 0x194f6f4fa500db52, + 0xc4571dc78a4c5374, + 0xe4d46d479999ca97, + 0x76b6785a615a151c, + 0xcceb8bcea7eaf8c1, + 0x80d87a6fbe5ae687, + 0x6a97ddddb85ce85, + 0xd783958f26034204, + 0x7144506f2e2e8590, + 0x948693d377aef166, + 0x8364621ed6f96056, + 0xf021777c4c09ee2d, + 0xc6cf5e746ecd50b, + 0xa2337b7aa22743df, + 0xae753f8bbacab39c, + 0xfc782a9e34d3c1cc, + 0x21b827324fe494d9, + 0x5692ce350ed03b38, + 0xf323a2b3cd0481b0, + 0xe859c97a4ccad2e3, + 0x48434b70381e4503, + 0x46042d62e4132ed8, + 0x48c4d6f56122e2f2, + 0xf87711ab9f5c1af7, + 0xb14b7a054759b469, + 0x8eb0a96993ffa9aa, + 0x9b21fb6fc58b760c, + 0xf3abdd115d2e7d25, + 0xf7beac3d4d12409c, + 0x40a5585cce69bf03, + 0x697881e1ba22d5a8, + 0x3d6c04e6ad373fd9, + 0x849871bf627be886, + 0x550f4b9b71b28ef9, + 0x81d2e0d78, + ]) + ); + assert_eq!( + FROBENIUS_COEFF_FQ12_C1[9], + nqr.pow(vec![ + 0x4af4accf7de0b977, + 0x742485e21805b4ee, + 0xee388fbc4ac36dec, + 0x1e199da57ad178a, + 0xc27c12b292c6726a, + 0x162e6ed84505b5e8, + 0xe191683f336e09df, + 0x17deb7e8d1e0fce6, + 0xd944f19ad06f5836, + 0x4c5f5e59f6276026, + 0xf1ba9c7c148a38a8, + 0xd205fe2dba72b326, + 0x9a2cf2a4c289824e, + 0x4f47ad512c39e24d, + 0xc5894d984000ea09, + 0x2974c03ff7cf01fa, + 0xfcd243b48cb99a22, + 0x2b5150c9313ac1e8, + 0x9089f37c7fc80eda, + 0x989540cc9a7aea56, + 0x1ab1d4e337e63018, + 0x42b546c30d357e43, + 0x1c6abc04f76233d9, + 0x78b3b8d88bf73e47, + 0x151c4e4c45dc68e6, + 0x519a79c4f54397ed, + 0x93f5b51535a127c5, + 0x5fc51b6f52fa153e, + 0x2e0504f2d4a965c3, + 0xc85bd3a3da52bffe, + 0x98c60957a46a89ef, + 0x48c03b5976b91cae, + 0xc6598040a0a61438, + 0xbf0b49dc255953af, + 0xb78dff905b628ab4, + 0x68140b797ba74ab8, + 0x116cf037991d1143, + 0x2f7fe82e58acb0b8, + 0xc20bf7a8f7be5d45, + 0x86c2905c338d5709, + 0xff13a3ae6c8ace3d, + 0xb6f95e2282d08337, + 0xd49f7b313e9cbf29, + 0xf794517193a1ce8c, + 0x39641fecb596a874, + 0x411c4c4edf462fb3, + 0x3f8cd55c10cf25b4, + 0x2bdd7ea165e860b6, + 0xacd7d2cef4caa193, + 0x6558a1d09a05f96, + 0x1f52b5f5b546fc20, + 0x4ee22a5a8c250c12, + 0xd3a63a54a205b6b3, + 0xd2ff5be8, + ]) + ); + assert_eq!( + FROBENIUS_COEFF_FQ12_C1[10], + nqr.pow(vec![ + 0xe5953a4f96cdda44, + 0x336b2d734cbc32bb, + 0x3f79bfe3cd7410e, + 0x267ae19aaa0f0332, + 0x85a9c4db78d5c749, + 0x90996b046b5dc7d8, + 0x8945eae9820afc6a, + 0x2644ddea2b036bd, + 0x39898e35ac2e3819, + 0x2574eab095659ab9, + 0x65953d51ac5ea798, + 0xc6b8c7afe6752466, + 0x40e9e993e9286544, + 0x7e0ad34ad9700ea0, + 0xac1015eba2c69222, + 0x24f057a19239b5d8, + 0x2043b48c8a3767eb, + 0x1117c124a75d7ff4, + 0x433cfd1a09fb3ce7, + 0x25b087ce4bcf7fb, + 0xbcee0dc53a3e5bdb, + 0xbffda040cf028735, + 0xf7cf103a25512acc, + 0x31d4ecda673130b9, + 0xea0906dab18461e6, + 0x5a40585a5ac3050d, + 0x803358fc14fd0eda, + 0x3678ca654eada770, + 0x7b91a1293a45e33e, + 0xcd5e5b8ea8530e43, + 0x21ae563ab34da266, + 0xecb00dad60df8894, + 0x77fe53e652facfef, + 0x9b7d1ad0b00244ec, + 0xe695df5ca73f801, + 0x23cdb21feeab0149, + 0x14de113e7ea810d9, + 0x52600cd958dac7e7, + 0xc83392c14667e488, + 0x9f808444bc1717fc, + 0x56facb4bcf7c788f, + 0x8bcad53245fc3ca0, + 0xdef661e83f27d81c, + 0x37d4ebcac9ad87e5, + 0x6fe8b24f5cdb9324, + 0xee08a26c1197654c, + 0xc98b22f65f237e9a, + 0xf54873a908ed3401, + 0x6e1cb951d41f3f3, + 0x290b2250a54e8df6, + 0x7f36d51eb1db669e, + 0xb08c7ed81a6ee43e, + 0x95e1c90fb092f680, + 0x429e4afd0e8b820, + 0x2c14a83ee87d715c, + 0xf37267575cfc8af5, + 0xb99e9afeda3c2c30, + 0x8f0f69da75792d5a, + 0x35074a85a533c73, + 0x156ed119, + ]) + ); + assert_eq!( + FROBENIUS_COEFF_FQ12_C1[11], + nqr.pow(vec![ + 0x107db680942de533, + 0x6262b24d2052393b, + 0x6136df824159ebc, + 0xedb052c9970c5deb, + 0xca813aea916c3777, + 0xf49dacb9d76c1788, + 0x624941bd372933bb, + 0xa5e60c2520638331, + 0xb38b661683411074, + 0x1d2c9af4c43d962b, + 0x17d807a0f14aa830, + 0x6e6581a51012c108, + 0x668a537e5b35e6f5, + 0x6c396cf3782dca5d, + 0x33b679d1bff536ed, + 0x736cce41805d90aa, + 0x8a562f369eb680bf, + 0x9f61aa208a11ded8, + 0x43dd89dd94d20f35, + 0xcf84c6610575c10a, + 0x9f318d49cf2fe8e6, + 0xbbc6e5f25a6e434e, + 0x6528c433d11d987b, + 0xffced71cc48c0e8a, + 0x4cbb1474f4cb2a26, + 0x66a035c0b28b7231, + 0xa6f2875faa1a82ae, + 0xdd1ea3deff818b02, + 0xe0cfdf0dcdecf701, + 0x9aefa231f2f6d23, + 0xfb251297efa06746, + 0x5a40d367df985538, + 0x1ea31d69ab506fed, + 0xc64ea8280e89a73f, + 0x969acf9f2d4496f4, + 0xe84c9181ee60c52c, + 0xc60f27fc19fc6ca4, + 0x760b33d850154048, + 0x84f69080f66c8457, + 0xc0192ba0fabf640e, + 0xd2c338765c23a3a8, + 0xa7838c20f02cec6c, + 0xb7cf01d020572877, + 0xd63ffaeba0be200a, + 0xf7492baeb5f041ac, + 0x8602c5212170d117, + 0xad9b2e83a5a42068, + 0x2461829b3ba1083e, + 0x7c34650da5295273, + 0xdc824ba800a8265a, + 0xd18d9b47836af7b2, + 0x3af78945c58cbf4d, + 0x7ed9575b8596906c, + 0x6d0c133895009a66, + 0x53bc1247ea349fe1, + 0x6b3063078d41aa7a, + 0x6184acd8cd880b33, + 0x76f4d15503fd1b96, + 0x7a9afd61eef25746, + 0xce974aadece60609, + 0x88ca59546a8ceafd, + 0x6d29391c41a0ac07, + 0x443843a60e0f46a6, + 0xa1590f62fd2602c7, + 0x536d5b15b514373f, + 0x22d582b, + ]) + ); +} + +#[test] +fn test_neg_one() { + let mut o = Fq::one(); + o.negate(); + + assert_eq!(NEGATIVE_ONE, o); +} + +#[cfg(test)] +use rand::{Rand, SeedableRng, XorShiftRng}; + +#[test] +fn test_fq_repr_ordering() { + use std::cmp::Ordering; + + fn assert_equality(a: FqRepr, b: FqRepr) { + assert_eq!(a, b); + assert!(a.cmp(&b) == Ordering::Equal); + } + + fn assert_lt(a: FqRepr, b: FqRepr) { + assert!(a < b); + assert!(b > a); + } + + assert_equality( + FqRepr([9999, 9999, 9999, 9999, 9999, 9999]), + FqRepr([9999, 9999, 9999, 9999, 9999, 9999]), + ); + assert_equality( + FqRepr([9999, 9998, 9999, 9999, 9999, 9999]), + FqRepr([9999, 9998, 9999, 9999, 9999, 9999]), + ); + assert_equality( + FqRepr([9999, 9999, 9999, 9997, 9999, 9999]), + FqRepr([9999, 9999, 9999, 9997, 9999, 9999]), + ); + assert_lt( + FqRepr([9999, 9999, 9999, 9997, 9999, 9998]), + FqRepr([9999, 9999, 9999, 9997, 9999, 9999]), + ); + assert_lt( + FqRepr([9999, 9999, 9999, 9997, 9998, 9999]), + FqRepr([9999, 9999, 9999, 9997, 9999, 9999]), + ); + assert_lt( + FqRepr([9, 9999, 9999, 9997, 9998, 9999]), + FqRepr([9999, 9999, 9999, 9997, 9999, 9999]), + ); +} + +#[test] +fn test_fq_repr_from() { + assert_eq!(FqRepr::from(100), FqRepr([100, 0, 0, 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_is_zero() { + assert!(FqRepr::from(0).is_zero()); + assert!(!FqRepr::from(1).is_zero()); + assert!(!FqRepr([0, 0, 0, 0, 1, 0]).is_zero()); +} + +#[test] +fn test_fq_repr_div2() { + let mut a = FqRepr([ + 0x8b0ad39f8dd7482a, + 0x147221c9a7178b69, + 0x54764cb08d8a6aa0, + 0x8519d708e1d83041, + 0x41f82777bd13fdb, + 0xf43944578f9b771b, + ]); + a.div2(); + assert_eq!( + a, + FqRepr([ + 0xc58569cfc6eba415, + 0xa3910e4d38bc5b4, + 0xaa3b265846c53550, + 0xc28ceb8470ec1820, + 0x820fc13bbde89fed, + 0x7a1ca22bc7cdbb8d + ]) + ); + for _ in 0..10 { + a.div2(); + } + assert_eq!( + a, + FqRepr([ + 0x6d31615a73f1bae9, + 0x54028e443934e2f1, + 0x82a8ec99611b14d, + 0xfb70a33ae11c3b06, + 0xe36083f04eef7a27, + 0x1e87288af1f36e + ]) + ); + for _ in 0..300 { + a.div2(); + } + assert_eq!(a, FqRepr([0x7288af1f36ee3608, 0x1e8, 0x0, 0x0, 0x0, 0x0])); + for _ in 0..50 { + a.div2(); + } + assert_eq!(a, FqRepr([0x7a1ca2, 0x0, 0x0, 0x0, 0x0, 0x0])); + for _ in 0..22 { + a.div2(); + } + assert_eq!(a, FqRepr([0x1, 0x0, 0x0, 0x0, 0x0, 0x0])); + a.div2(); + assert!(a.is_zero()); +} + +#[test] +fn test_fq_repr_shr() { + let mut a = FqRepr([ + 0xaa5cdd6172847ffd, + 0x43242c06aed55287, + 0x9ddd5b312f3dd104, + 0xc5541fd48046b7e7, + 0x16080cf4071e0b05, + 0x1225f2901aea514e, + ]); + a.shr(0); + assert_eq!( + a, + FqRepr([ + 0xaa5cdd6172847ffd, + 0x43242c06aed55287, + 0x9ddd5b312f3dd104, + 0xc5541fd48046b7e7, + 0x16080cf4071e0b05, + 0x1225f2901aea514e + ]) + ); + a.shr(1); + assert_eq!( + a, + FqRepr([ + 0xd52e6eb0b9423ffe, + 0x21921603576aa943, + 0xceeead98979ee882, + 0xe2aa0fea40235bf3, + 0xb04067a038f0582, + 0x912f9480d7528a7 + ]) + ); + a.shr(50); + assert_eq!( + a, + FqRepr([ + 0x8580d5daaa50f54b, + 0xab6625e7ba208864, + 0x83fa9008d6fcf3bb, + 0x19e80e3c160b8aa, + 0xbe52035d4a29c2c1, + 0x244 + ]) + ); + a.shr(130); + assert_eq!( + a, + FqRepr([ + 0xa0fea40235bf3cee, + 0x4067a038f0582e2a, + 0x2f9480d7528a70b0, + 0x91, + 0x0, + 0x0 + ]) + ); + a.shr(64); + assert_eq!( + a, + FqRepr([0x4067a038f0582e2a, 0x2f9480d7528a70b0, 0x91, 0x0, 0x0, 0x0]) + ); +} + +#[test] +fn test_fq_repr_mul2() { + let mut a = FqRepr::from(23712937547); + a.mul2(); + assert_eq!(a, FqRepr([0xb0acd6c96, 0x0, 0x0, 0x0, 0x0, 0x0])); + for _ in 0..60 { + a.mul2(); + } + assert_eq!( + a, + FqRepr([0x6000000000000000, 0xb0acd6c9, 0x0, 0x0, 0x0, 0x0]) + ); + for _ in 0..300 { + a.mul2(); + } + assert_eq!(a, FqRepr([0x0, 0x0, 0x0, 0x0, 0x0, 0xcd6c960000000000])); + for _ in 0..17 { + a.mul2(); + } + assert_eq!(a, FqRepr([0x0, 0x0, 0x0, 0x0, 0x0, 0x2c00000000000000])); + for _ in 0..6 { + a.mul2(); + } + assert!(a.is_zero()); +} + +#[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..385 { + assert_eq!(i, a.num_bits()); + a.mul2(); + } + assert_eq!(0, a.num_bits()); +} + +#[test] +fn test_fq_repr_sub_noborrow() { + let mut rng = XorShiftRng::from_seed([0x5dbe6259, 0x8d313d76, 0x3237db17, 0xe5bc0654]); + + let mut t = FqRepr([ + 0x827a4a08041ebd9, + 0x3c239f3dcc8f0d6b, + 0x9ab46a912d555364, + 0x196936b17b43910b, + 0xad0eb3948a5c34fd, + 0xd56f7b5ab8b5ce8, + ]); + t.sub_noborrow(&FqRepr([ + 0xc7867917187ca02b, + 0x5d75679d4911ffef, + 0x8c5b3e48b1a71c15, + 0x6a427ae846fd66aa, + 0x7a37e7265ee1eaf9, + 0x7c0577a26f59d5, + ])); + assert!( + t == FqRepr([ + 0x40a12b8967c54bae, + 0xdeae37a0837d0d7b, + 0xe592c487bae374e, + 0xaf26bbc934462a61, + 0x32d6cc6e2b7a4a03, + 0xcdaf23e091c0313 + ]) + ); + + for _ in 0..1000 { + let mut a = FqRepr::rand(&mut rng); + a.0[5] >>= 30; + let mut b = a; + for _ in 0..10 { + b.mul2(); + } + let mut c = b; + for _ in 0..10 { + c.mul2(); + } + + assert!(a < b); + assert!(b < c); + + let mut csub_ba = c; + csub_ba.sub_noborrow(&b); + csub_ba.sub_noborrow(&a); + + let mut csub_ab = c; + csub_ab.sub_noborrow(&a); + csub_ab.sub_noborrow(&b); + + assert_eq!(csub_ab, csub_ba); + } + + // Subtracting q+1 from q should produce -1 (mod 2**384) + let mut qplusone = FqRepr([ + 0xb9feffffffffaaab, + 0x1eabfffeb153ffff, + 0x6730d2a0f6b0f624, + 0x64774b84f38512bf, + 0x4b1ba7b6434bacd7, + 0x1a0111ea397fe69a, + ]); + qplusone.sub_noborrow(&FqRepr([ + 0xb9feffffffffaaac, + 0x1eabfffeb153ffff, + 0x6730d2a0f6b0f624, + 0x64774b84f38512bf, + 0x4b1ba7b6434bacd7, + 0x1a0111ea397fe69a, + ])); + assert_eq!( + qplusone, + FqRepr([ + 0xffffffffffffffff, + 0xffffffffffffffff, + 0xffffffffffffffff, + 0xffffffffffffffff, + 0xffffffffffffffff, + 0xffffffffffffffff + ]) + ); +} + +#[test] +fn test_fq_repr_add_nocarry() { + let mut rng = XorShiftRng::from_seed([0x5dbe6259, 0x8d313d76, 0x3237db17, 0xe5bc0654]); + + let mut t = FqRepr([ + 0x827a4a08041ebd9, + 0x3c239f3dcc8f0d6b, + 0x9ab46a912d555364, + 0x196936b17b43910b, + 0xad0eb3948a5c34fd, + 0xd56f7b5ab8b5ce8, + ]); + t.add_nocarry(&FqRepr([ + 0xc7867917187ca02b, + 0x5d75679d4911ffef, + 0x8c5b3e48b1a71c15, + 0x6a427ae846fd66aa, + 0x7a37e7265ee1eaf9, + 0x7c0577a26f59d5, + ])); + assert!( + t == FqRepr([ + 0xcfae1db798be8c04, + 0x999906db15a10d5a, + 0x270fa8d9defc6f79, + 0x83abb199c240f7b6, + 0x27469abae93e1ff6, + 0xdd2fd2d4dfab6be + ]) + ); + + // Test for the associativity of addition. + for _ in 0..1000 { + let mut a = FqRepr::rand(&mut rng); + let mut b = FqRepr::rand(&mut rng); + let mut c = FqRepr::rand(&mut rng); + + // Unset the first few bits, so that overflow won't occur. + a.0[5] >>= 3; + b.0[5] >>= 3; + c.0[5] >>= 3; + + let mut abc = a; + abc.add_nocarry(&b); + abc.add_nocarry(&c); + + let mut acb = a; + acb.add_nocarry(&c); + acb.add_nocarry(&b); + + let mut bac = b; + bac.add_nocarry(&a); + bac.add_nocarry(&c); + + let mut bca = b; + bca.add_nocarry(&c); + bca.add_nocarry(&a); + + let mut cab = c; + cab.add_nocarry(&a); + cab.add_nocarry(&b); + + let mut cba = c; + cba.add_nocarry(&b); + cba.add_nocarry(&a); + + assert_eq!(abc, acb); + assert_eq!(abc, bac); + assert_eq!(abc, bca); + assert_eq!(abc, cab); + assert_eq!(abc, cba); + } + + // Adding 1 to (2^384 - 1) should produce zero + let mut x = FqRepr([ + 0xffffffffffffffff, + 0xffffffffffffffff, + 0xffffffffffffffff, + 0xffffffffffffffff, + 0xffffffffffffffff, + 0xffffffffffffffff, + ]); + x.add_nocarry(&FqRepr::from(1)); + assert!(x.is_zero()); +} + +#[test] +fn test_fq_is_valid() { + 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, + 0xf9b29e493f899b36, + 0x17c8be1800b9f059 + ])).is_valid() + ); + assert!( + !Fq(FqRepr([ + 0xffffffffffffffff, + 0xffffffffffffffff, + 0xffffffffffffffff, + 0xffffffffffffffff, + 0xffffffffffffffff, + 0xffffffffffffffff + ])).is_valid() + ); + + let mut rng = XorShiftRng::from_seed([0x5dbe6259, 0x8d313d76, 0x3237db17, 0xe5bc0654]); + + for _ in 0..1000 { + let a = Fq::rand(&mut rng); + assert!(a.is_valid()); + } +} + +#[test] +fn test_fq_add_assign() { + { + // Random number + let mut tmp = Fq(FqRepr([ + 0x624434821df92b69, + 0x503260c04fd2e2ea, + 0xd9df726e0d16e8ce, + 0xfbcb39adfd5dfaeb, + 0x86b8a22b0c88b112, + 0x165a2ed809e4201b, + ])); + assert!(tmp.is_valid()); + // Test that adding zero has no effect. + tmp.add_assign(&Fq(FqRepr::from(0))); + assert_eq!( + tmp, + Fq(FqRepr([ + 0x624434821df92b69, + 0x503260c04fd2e2ea, + 0xd9df726e0d16e8ce, + 0xfbcb39adfd5dfaeb, + 0x86b8a22b0c88b112, + 0x165a2ed809e4201b + ])) + ); + // Add one and test for the result. + tmp.add_assign(&Fq(FqRepr::from(1))); + assert_eq!( + tmp, + Fq(FqRepr([ + 0x624434821df92b6a, + 0x503260c04fd2e2ea, + 0xd9df726e0d16e8ce, + 0xfbcb39adfd5dfaeb, + 0x86b8a22b0c88b112, + 0x165a2ed809e4201b + ])) + ); + // Add another random number that exercises the reduction. + tmp.add_assign(&Fq(FqRepr([ + 0x374d8f8ea7a648d8, + 0xe318bb0ebb8bfa9b, + 0x613d996f0a95b400, + 0x9fac233cb7e4fef1, + 0x67e47552d253c52, + 0x5c31b227edf25da, + ]))); + assert_eq!( + tmp, + Fq(FqRepr([ + 0xdf92c410c59fc997, + 0x149f1bd05a0add85, + 0xd3ec393c20fba6ab, + 0x37001165c1bde71d, + 0x421b41c9f662408e, + 0x21c38104f435f5b + ])) + ); + // Add one to (q - 1) and test for the result. + tmp = Fq(FqRepr([ + 0xb9feffffffffaaaa, + 0x1eabfffeb153ffff, + 0x6730d2a0f6b0f624, + 0x64774b84f38512bf, + 0x4b1ba7b6434bacd7, + 0x1a0111ea397fe69a, + ])); + tmp.add_assign(&Fq(FqRepr::from(1))); + assert!(tmp.0.is_zero()); + // Add a random number to another one such that the result is q - 1 + tmp = Fq(FqRepr([ + 0x531221a410efc95b, + 0x72819306027e9717, + 0x5ecefb937068b746, + 0x97de59cd6feaefd7, + 0xdc35c51158644588, + 0xb2d176c04f2100, + ])); + tmp.add_assign(&Fq(FqRepr([ + 0x66ecde5bef0fe14f, + 0xac2a6cf8aed568e8, + 0x861d70d86483edd, + 0xcc98f1b7839a22e8, + 0x6ee5e2a4eae7674e, + 0x194e40737930c599, + ]))); + assert_eq!( + tmp, + Fq(FqRepr([ + 0xb9feffffffffaaaa, + 0x1eabfffeb153ffff, + 0x6730d2a0f6b0f624, + 0x64774b84f38512bf, + 0x4b1ba7b6434bacd7, + 0x1a0111ea397fe69a + ])) + ); + // Add one to the result and test for it. + tmp.add_assign(&Fq(FqRepr::from(1))); + assert!(tmp.0.is_zero()); + } + + // Test associativity + + let mut rng = XorShiftRng::from_seed([0x5dbe6259, 0x8d313d76, 0x3237db17, 0xe5bc0654]); + + for _ in 0..1000 { + // Generate a, b, c and ensure (a + b) + c == a + (b + c). + let a = Fq::rand(&mut rng); + let b = Fq::rand(&mut rng); + let c = Fq::rand(&mut rng); + + let mut tmp1 = a; + tmp1.add_assign(&b); + tmp1.add_assign(&c); + + let mut tmp2 = b; + tmp2.add_assign(&c); + tmp2.add_assign(&a); + + assert!(tmp1.is_valid()); + assert!(tmp2.is_valid()); + assert_eq!(tmp1, tmp2); + } +} + +#[test] +fn test_fq_sub_assign() { + { + // Test arbitrary subtraction that tests reduction. + let mut tmp = Fq(FqRepr([ + 0x531221a410efc95b, + 0x72819306027e9717, + 0x5ecefb937068b746, + 0x97de59cd6feaefd7, + 0xdc35c51158644588, + 0xb2d176c04f2100, + ])); + tmp.sub_assign(&Fq(FqRepr([ + 0x98910d20877e4ada, + 0x940c983013f4b8ba, + 0xf677dc9b8345ba33, + 0xbef2ce6b7f577eba, + 0xe1ae288ac3222c44, + 0x5968bb602790806, + ]))); + assert_eq!( + tmp, + Fq(FqRepr([ + 0x748014838971292c, + 0xfd20fad49fddde5c, + 0xcf87f198e3d3f336, + 0x3d62d6e6e41883db, + 0x45a3443cd88dc61b, + 0x151d57aaf755ff94 + ])) + ); + + // Test the opposite subtraction which doesn't test reduction. + tmp = Fq(FqRepr([ + 0x98910d20877e4ada, + 0x940c983013f4b8ba, + 0xf677dc9b8345ba33, + 0xbef2ce6b7f577eba, + 0xe1ae288ac3222c44, + 0x5968bb602790806, + ])); + tmp.sub_assign(&Fq(FqRepr([ + 0x531221a410efc95b, + 0x72819306027e9717, + 0x5ecefb937068b746, + 0x97de59cd6feaefd7, + 0xdc35c51158644588, + 0xb2d176c04f2100, + ]))); + assert_eq!( + tmp, + Fq(FqRepr([ + 0x457eeb7c768e817f, + 0x218b052a117621a3, + 0x97a8e10812dd02ed, + 0x2714749e0f6c8ee3, + 0x57863796abde6bc, + 0x4e3ba3f4229e706 + ])) + ); + + // Test for sensible results with zero + tmp = Fq(FqRepr::from(0)); + tmp.sub_assign(&Fq(FqRepr::from(0))); + assert!(tmp.is_zero()); + + tmp = Fq(FqRepr([ + 0x98910d20877e4ada, + 0x940c983013f4b8ba, + 0xf677dc9b8345ba33, + 0xbef2ce6b7f577eba, + 0xe1ae288ac3222c44, + 0x5968bb602790806, + ])); + tmp.sub_assign(&Fq(FqRepr::from(0))); + assert_eq!( + tmp, + Fq(FqRepr([ + 0x98910d20877e4ada, + 0x940c983013f4b8ba, + 0xf677dc9b8345ba33, + 0xbef2ce6b7f577eba, + 0xe1ae288ac3222c44, + 0x5968bb602790806 + ])) + ); + } + + let mut rng = XorShiftRng::from_seed([0x5dbe6259, 0x8d313d76, 0x3237db17, 0xe5bc0654]); + + for _ in 0..1000 { + // Ensure that (a - b) + (b - a) = 0. + let a = Fq::rand(&mut rng); + let b = Fq::rand(&mut rng); + + let mut tmp1 = a; + tmp1.sub_assign(&b); + + let mut tmp2 = b; + tmp2.sub_assign(&a); + + tmp1.add_assign(&tmp2); + assert!(tmp1.is_zero()); + } +} + +#[test] +fn test_fq_mul_assign() { + let mut tmp = Fq(FqRepr([ + 0xcc6200000020aa8a, + 0x422800801dd8001a, + 0x7f4f5e619041c62c, + 0x8a55171ac70ed2ba, + 0x3f69cc3a3d07d58b, + 0xb972455fd09b8ef, + ])); + tmp.mul_assign(&Fq(FqRepr([ + 0x329300000030ffcf, + 0x633c00c02cc40028, + 0xbef70d925862a942, + 0x4f7fa2a82a963c17, + 0xdf1eb2575b8bc051, + 0x1162b680fb8e9566, + ]))); + assert!( + tmp == Fq(FqRepr([ + 0x9dc4000001ebfe14, + 0x2850078997b00193, + 0xa8197f1abb4d7bf, + 0xc0309573f4bfe871, + 0xf48d0923ffaf7620, + 0x11d4b58c7a926e66 + ])) + ); + + let mut rng = XorShiftRng::from_seed([0x5dbe6259, 0x8d313d76, 0x3237db17, 0xe5bc0654]); + + for _ in 0..1000000 { + // Ensure that (a * b) * c = a * (b * c) + let a = Fq::rand(&mut rng); + let b = Fq::rand(&mut rng); + let c = Fq::rand(&mut rng); + + let mut tmp1 = a; + tmp1.mul_assign(&b); + tmp1.mul_assign(&c); + + let mut tmp2 = b; + tmp2.mul_assign(&c); + tmp2.mul_assign(&a); + + assert_eq!(tmp1, tmp2); + } + + for _ in 0..1000000 { + // Ensure that r * (a + b + c) = r*a + r*b + r*c + + let r = Fq::rand(&mut rng); + let mut a = Fq::rand(&mut rng); + let mut b = Fq::rand(&mut rng); + let mut c = Fq::rand(&mut rng); + + let mut tmp1 = a; + tmp1.add_assign(&b); + tmp1.add_assign(&c); + tmp1.mul_assign(&r); + + a.mul_assign(&r); + b.mul_assign(&r); + c.mul_assign(&r); + + a.add_assign(&b); + a.add_assign(&c); + + assert_eq!(tmp1, a); + } +} + +#[test] +fn test_fq_squaring() { + let mut a = Fq(FqRepr([ + 0xffffffffffffffff, + 0xffffffffffffffff, + 0xffffffffffffffff, + 0xffffffffffffffff, + 0xffffffffffffffff, + 0x19ffffffffffffff, + ])); + assert!(a.is_valid()); + a.square(); + assert_eq!( + a, + Fq::from_repr(FqRepr([ + 0x1cfb28fe7dfbbb86, + 0x24cbe1731577a59, + 0xcce1d4edc120e66e, + 0xdc05c659b4e15b27, + 0x79361e5a802c6a23, + 0x24bcbe5d51b9a6f + ])).unwrap() + ); + + let mut rng = XorShiftRng::from_seed([0x5dbe6259, 0x8d313d76, 0x3237db17, 0xe5bc0654]); + + for _ in 0..1000000 { + // Ensure that (a * a) = a^2 + let a = Fq::rand(&mut rng); + + let mut tmp = a; + tmp.square(); + + let mut tmp2 = a; + tmp2.mul_assign(&a); + + assert_eq!(tmp, tmp2); + } +} + +#[test] +fn test_fq_inverse() { + assert!(Fq::zero().inverse().is_none()); + + let mut rng = XorShiftRng::from_seed([0x5dbe6259, 0x8d313d76, 0x3237db17, 0xe5bc0654]); + + let one = Fq::one(); + + for _ in 0..1000 { + // Ensure that a * a^-1 = 1 + let mut a = Fq::rand(&mut rng); + let ainv = a.inverse().unwrap(); + a.mul_assign(&ainv); + assert_eq!(a, one); + } +} + +#[test] +fn test_fq_double() { + let mut rng = XorShiftRng::from_seed([0x5dbe6259, 0x8d313d76, 0x3237db17, 0xe5bc0654]); + + for _ in 0..1000 { + // Ensure doubling a is equivalent to adding a to itself. + let mut a = Fq::rand(&mut rng); + let mut b = a; + b.add_assign(&a); + a.double(); + assert_eq!(a, b); + } +} + +#[test] +fn test_fq_negate() { + { + let mut a = Fq::zero(); + a.negate(); + + assert!(a.is_zero()); + } + + let mut rng = XorShiftRng::from_seed([0x5dbe6259, 0x8d313d76, 0x3237db17, 0xe5bc0654]); + + for _ in 0..1000 { + // Ensure (a - (-a)) = 0. + let mut a = Fq::rand(&mut rng); + let mut b = a; + b.negate(); + a.add_assign(&b); + + assert!(a.is_zero()); + } +} + +#[test] +fn test_fq_pow() { + let mut rng = XorShiftRng::from_seed([0x5dbe6259, 0x8d313d76, 0x3237db17, 0xe5bc0654]); + + for i in 0..1000 { + // Exponentiate by various small numbers and ensure it consists with repeated + // multiplication. + let a = Fq::rand(&mut rng); + let target = a.pow(&[i]); + let mut c = Fq::one(); + for _ in 0..i { + c.mul_assign(&a); + } + assert_eq!(c, target); + } + + for _ in 0..1000 { + // Exponentiating by the modulus should have no effect in a prime field. + let a = Fq::rand(&mut rng); + + assert_eq!(a, a.pow(Fq::char())); + } +} + +#[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_from_into_repr() { + // q + 1 should not be in the field + assert!( + Fq::from_repr(FqRepr([ + 0xb9feffffffffaaac, + 0x1eabfffeb153ffff, + 0x6730d2a0f6b0f624, + 0x64774b84f38512bf, + 0x4b1ba7b6434bacd7, + 0x1a0111ea397fe69a + ])).is_err() + ); + + // q should not be in the field + assert!(Fq::from_repr(Fq::char()).is_err()); + + // Multiply some arbitrary representations to see if the result is as expected. + let a = FqRepr([ + 0x4a49dad4ff6cde2d, + 0xac62a82a8f51cd50, + 0x2b1f41ab9f36d640, + 0x908a387f480735f1, + 0xae30740c08a875d7, + 0x6c80918a365ef78, + ]); + let mut a_fq = Fq::from_repr(a).unwrap(); + let b = FqRepr([ + 0xbba57917c32f0cf0, + 0xe7f878cf87f05e5d, + 0x9498b4292fd27459, + 0xd59fd94ee4572cfa, + 0x1f607186d5bb0059, + 0xb13955f5ac7f6a3, + ]); + let b_fq = Fq::from_repr(b).unwrap(); + let c = FqRepr([ + 0xf5f70713b717914c, + 0x355ea5ac64cbbab1, + 0xce60dd43417ec960, + 0xf16b9d77b0ad7d10, + 0xa44c204c1de7cdb7, + 0x1684487772bc9a5a, + ]); + a_fq.mul_assign(&b_fq); + assert_eq!(a_fq.into_repr(), c); + + // Zero should be in the field. + assert!(Fq::from_repr(FqRepr::from(0)).unwrap().is_zero()); + + let mut rng = XorShiftRng::from_seed([0x5dbe6259, 0x8d313d76, 0x3237db17, 0xe5bc0654]); + + for _ in 0..1000 { + // Try to turn Fq elements into representations and back again, and compare. + let a = Fq::rand(&mut rng); + let a_repr = a.into_repr(); + let b_repr = FqRepr::from(a); + assert_eq!(a_repr, b_repr); + let a_again = Fq::from_repr(a_repr).unwrap(); + + assert_eq!(a, a_again); + } +} + +#[test] +fn test_fq_repr_display() { + assert_eq!( + format!("{}", FqRepr([0xa956babf9301ea24, 0x39a8f184f3535c7b, 0xb38d35b3f6779585, 0x676cc4eef4c46f2c, 0xb1d4aad87651e694, 0x1947f0d5f4fe325a])), + "0x1947f0d5f4fe325ab1d4aad87651e694676cc4eef4c46f2cb38d35b3f677958539a8f184f3535c7ba956babf9301ea24".to_string() + ); + assert_eq!( + format!("{}", FqRepr([0xb4171485fd8622dd, 0x864229a6edec7ec5, 0xc57f7bdcf8dfb707, 0x6db7ff0ecea4584a, 0xf8d8578c4a57132d, 0x6eb66d42d9fcaaa])), + "0x06eb66d42d9fcaaaf8d8578c4a57132d6db7ff0ecea4584ac57f7bdcf8dfb707864229a6edec7ec5b4171485fd8622dd".to_string() + ); + assert_eq!( + format!("{}", FqRepr([0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff])), + "0xffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff".to_string() + ); + assert_eq!( + format!("{}", FqRepr([0, 0, 0, 0, 0, 0])), + "0x000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000".to_string() + ); +} + +#[test] +fn test_fq_display() { + assert_eq!( + format!("{}", Fq::from_repr(FqRepr([0xa956babf9301ea24, 0x39a8f184f3535c7b, 0xb38d35b3f6779585, 0x676cc4eef4c46f2c, 0xb1d4aad87651e694, 0x1947f0d5f4fe325a])).unwrap()), + "Fq(0x1947f0d5f4fe325ab1d4aad87651e694676cc4eef4c46f2cb38d35b3f677958539a8f184f3535c7ba956babf9301ea24)".to_string() + ); + assert_eq!( + format!("{}", Fq::from_repr(FqRepr([0xe28e79396ac2bbf8, 0x413f6f7f06ea87eb, 0xa4b62af4a792a689, 0xb7f89f88f59c1dc5, 0x9a551859b1e43a9a, 0x6c9f5a1060de974])).unwrap()), + "Fq(0x06c9f5a1060de9749a551859b1e43a9ab7f89f88f59c1dc5a4b62af4a792a689413f6f7f06ea87ebe28e79396ac2bbf8)".to_string() + ); +} + +#[test] +fn test_fq_num_bits() { + assert_eq!(Fq::NUM_BITS, 381); + assert_eq!(Fq::CAPACITY, 380); +} + +#[test] +fn test_fq_root_of_unity() { + use ff::SqrtField; + + assert_eq!(Fq::S, 1); + assert_eq!( + Fq::multiplicative_generator(), + Fq::from_repr(FqRepr::from(2)).unwrap() + ); + assert_eq!( + Fq::multiplicative_generator().pow([ + 0xdcff7fffffffd555, + 0xf55ffff58a9ffff, + 0xb39869507b587b12, + 0xb23ba5c279c2895f, + 0x258dd3db21a5d66b, + 0xd0088f51cbff34d + ]), + Fq::root_of_unity() + ); + assert_eq!(Fq::root_of_unity().pow([1 << Fq::S]), Fq::one()); + assert!(Fq::multiplicative_generator().sqrt().is_none()); +} + +#[test] +fn fq_field_tests() { + crate::tests::field::random_field_tests::(); + crate::tests::field::random_sqrt_tests::(); + crate::tests::field::random_frobenius_tests::(Fq::char(), 13); + crate::tests::field::from_str_tests::(); +} + +#[test] +fn test_fq_ordering() { + // FqRepr's ordering is well-tested, but we still need to make sure the Fq + // elements aren't being compared in Montgomery form. + for i in 0..100 { + assert!( + Fq::from_repr(FqRepr::from(i + 1)).unwrap() > Fq::from_repr(FqRepr::from(i)).unwrap() + ); + } +} + +#[test] +fn fq_repr_tests() { + crate::tests::repr::random_repr_tests::(); +} + +#[test] +fn test_fq_legendre() { + use ff::LegendreSymbol::*; + use ff::SqrtField; + + assert_eq!(QuadraticResidue, Fq::one().legendre()); + assert_eq!(Zero, Fq::zero().legendre()); + + assert_eq!( + QuadraticNonResidue, + Fq::from_repr(FqRepr::from(2)).unwrap().legendre() + ); + assert_eq!( + QuadraticResidue, + Fq::from_repr(FqRepr::from(4)).unwrap().legendre() + ); + + let e = FqRepr([ + 0x52a112f249778642, + 0xd0bedb989b7991f, + 0xdad3b6681aa63c05, + 0xf2efc0bb4721b283, + 0x6057a98f18c24733, + 0x1022c2fd122889e4, + ]); + assert_eq!(QuadraticNonResidue, Fq::from_repr(e).unwrap().legendre()); + let e = FqRepr([ + 0x6dae594e53a96c74, + 0x19b16ca9ba64b37b, + 0x5c764661a59bfc68, + 0xaa346e9b31c60a, + 0x346059f9d87a9fa9, + 0x1d61ac6bfd5c88b, + ]); + assert_eq!(QuadraticResidue, Fq::from_repr(e).unwrap().legendre()); +} diff --git a/pairing/src/bls12_381/fq12.rs b/pairing/src/bls12_381/fq12.rs new file mode 100644 index 0000000..b8744ca --- /dev/null +++ b/pairing/src/bls12_381/fq12.rs @@ -0,0 +1,189 @@ +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(), + } + } +} + +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); + } +} + +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 fq12_field_tests() { + use ff::PrimeField; + + crate::tests::field::random_field_tests::(); + crate::tests::field::random_frobenius_tests::(super::fq::Fq::char(), 13); +} diff --git a/pairing/src/bls12_381/fq2.rs b/pairing/src/bls12_381/fq2.rs new file mode 100644 index 0000000..9737adf --- /dev/null +++ b/pairing/src/bls12_381/fq2.rs @@ -0,0 +1,910 @@ +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 { + /// Multiply this element by the cubic and quadratic nonresidue 1 + u. + pub fn mul_by_nonresidue(&mut self) { + let t0 = self.c0; + self.c0.sub_assign(&self.c1); + 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 + } +} + +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([ + 0xee7fbfffffffeaaa, + 0x7aaffffac54ffff, + 0xd9cc34a83dac3d89, + 0xd91dd2e13ce144af, + 0x92c6e9ed90d2eb35, + 0x680447a8e5ff9a6, + ]); + 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([ + 0xdcff7fffffffd555, + 0xf55ffff58a9ffff, + 0xb39869507b587b12, + 0xb23ba5c279c2895f, + 0x258dd3db21a5d66b, + 0xd0088f51cbff34d, + ]); + a1.mul_assign(&alpha); + } + + Some(a1) + } + } + } +} + +#[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 + + let mut a = Fq2 { + c0: Fq::from_repr(FqRepr([ + 0x9c2c6309bbf8b598, + 0x4eef5c946536f602, + 0x90e34aab6fb6a6bd, + 0xf7f295a94e58ae7c, + 0x41b76dcc1c3fbe5e, + 0x7080c5fa1d8e042, + ])).unwrap(), + c1: Fq::from_repr(FqRepr([ + 0x38f473b3c870a4ab, + 0x6ad3291177c8c7e5, + 0xdac5a4c911a4353e, + 0xbfb99020604137a0, + 0xfc58a7b7be815407, + 0x10d1615e75250a21, + ])).unwrap(), + }; + a.square(); + assert_eq!( + a, + Fq2 { + c0: Fq::from_repr(FqRepr([ + 0xf262c28c538bcf68, + 0xb9f2a66eae1073ba, + 0xdc46ab8fad67ae0, + 0xcb674157618da176, + 0x4cf17b5893c3d327, + 0x7eac81369c43361 + ])).unwrap(), + c1: Fq::from_repr(FqRepr([ + 0xc1579cf58e980cf8, + 0xa23eb7e12dd54d98, + 0xe75138bce4cec7aa, + 0x38d0d7275a9689e1, + 0x739c983042779a65, + 0x1542a61c8a8db994 + ])).unwrap(), + } + ); +} + +#[test] +fn test_fq2_mul() { + use super::fq::FqRepr; + use ff::PrimeField; + + let mut a = Fq2 { + c0: Fq::from_repr(FqRepr([ + 0x85c9f989e1461f03, + 0xa2e33c333449a1d6, + 0x41e461154a7354a3, + 0x9ee53e7e84d7532e, + 0x1c202d8ed97afb45, + 0x51d3f9253e2516f, + ])).unwrap(), + c1: Fq::from_repr(FqRepr([ + 0xa7348a8b511aedcf, + 0x143c215d8176b319, + 0x4cc48081c09b8903, + 0x9533e4a9a5158be, + 0x7a5e1ecb676d65f9, + 0x180c3ee46656b008, + ])).unwrap(), + }; + a.mul_assign(&Fq2 { + c0: Fq::from_repr(FqRepr([ + 0xe21f9169805f537e, + 0xfc87e62e179c285d, + 0x27ece175be07a531, + 0xcd460f9f0c23e430, + 0x6c9110292bfa409, + 0x2c93a72eb8af83e, + ])).unwrap(), + c1: Fq::from_repr(FqRepr([ + 0x4b1c3f936d8992d4, + 0x1d2a72916dba4c8a, + 0x8871c508658d1e5f, + 0x57a06d3135a752ae, + 0x634cd3c6c565096d, + 0x19e17334d4e93558, + ])).unwrap(), + }); + assert_eq!( + a, + Fq2 { + c0: Fq::from_repr(FqRepr([ + 0x95b5127e6360c7e4, + 0xde29c31a19a6937e, + 0xf61a96dacf5a39bc, + 0x5511fe4d84ee5f78, + 0x5310a202d92f9963, + 0x1751afbe166e5399 + ])).unwrap(), + c1: Fq::from_repr(FqRepr([ + 0x84af0e1bd630117a, + 0x6c63cd4da2c2aa7, + 0x5ba6e5430e883d40, + 0xc975106579c275ee, + 0x33a9ac82ce4c5083, + 0x1ef1a36c201589d + ])).unwrap(), + } + ); +} + +#[test] +fn test_fq2_inverse() { + use super::fq::FqRepr; + use ff::PrimeField; + + assert!(Fq2::zero().inverse().is_none()); + + let a = Fq2 { + c0: Fq::from_repr(FqRepr([ + 0x85c9f989e1461f03, + 0xa2e33c333449a1d6, + 0x41e461154a7354a3, + 0x9ee53e7e84d7532e, + 0x1c202d8ed97afb45, + 0x51d3f9253e2516f, + ])).unwrap(), + c1: Fq::from_repr(FqRepr([ + 0xa7348a8b511aedcf, + 0x143c215d8176b319, + 0x4cc48081c09b8903, + 0x9533e4a9a5158be, + 0x7a5e1ecb676d65f9, + 0x180c3ee46656b008, + ])).unwrap(), + }; + let a = a.inverse().unwrap(); + assert_eq!( + a, + Fq2 { + c0: Fq::from_repr(FqRepr([ + 0x70300f9bcb9e594, + 0xe5ecda5fdafddbb2, + 0x64bef617d2915a8f, + 0xdfba703293941c30, + 0xa6c3d8f9586f2636, + 0x1351ef01941b70c4 + ])).unwrap(), + c1: Fq::from_repr(FqRepr([ + 0x8c39fd76a8312cb4, + 0x15d7b6b95defbff0, + 0x947143f89faedee9, + 0xcbf651a0f367afb2, + 0xdf4e54f0d3ef15a6, + 0x103bdf241afb0019 + ])).unwrap(), + } + ); +} + +#[test] +fn test_fq2_addition() { + use super::fq::FqRepr; + use ff::PrimeField; + + let mut a = Fq2 { + c0: Fq::from_repr(FqRepr([ + 0x2d0078036923ffc7, + 0x11e59ea221a3b6d2, + 0x8b1a52e0a90f59ed, + 0xb966ce3bc2108b13, + 0xccc649c4b9532bf3, + 0xf8d295b2ded9dc, + ])).unwrap(), + c1: Fq::from_repr(FqRepr([ + 0x977df6efcdaee0db, + 0x946ae52d684fa7ed, + 0xbe203411c66fb3a5, + 0xb3f8afc0ee248cad, + 0x4e464dea5bcfd41e, + 0x12d1137b8a6a837, + ])).unwrap(), + }; + a.add_assign(&Fq2 { + c0: Fq::from_repr(FqRepr([ + 0x619a02d78dc70ef2, + 0xb93adfc9119e33e8, + 0x4bf0b99a9f0dca12, + 0x3b88899a42a6318f, + 0x986a4a62fa82a49d, + 0x13ce433fa26027f5, + ])).unwrap(), + c1: Fq::from_repr(FqRepr([ + 0x66323bf80b58b9b9, + 0xa1379b6facf6e596, + 0x402aef1fb797e32f, + 0x2236f55246d0d44d, + 0x4c8c1800eb104566, + 0x11d6e20e986c2085, + ])).unwrap(), + }); + assert_eq!( + a, + Fq2 { + c0: Fq::from_repr(FqRepr([ + 0x8e9a7adaf6eb0eb9, + 0xcb207e6b3341eaba, + 0xd70b0c7b481d23ff, + 0xf4ef57d604b6bca2, + 0x65309427b3d5d090, + 0x14c715d5553f01d2 + ])).unwrap(), + c1: Fq::from_repr(FqRepr([ + 0xfdb032e7d9079a94, + 0x35a2809d15468d83, + 0xfe4b23317e0796d5, + 0xd62fa51334f560fa, + 0x9ad265eb46e01984, + 0x1303f3465112c8bc + ])).unwrap(), + } + ); +} + +#[test] +fn test_fq2_subtraction() { + use super::fq::FqRepr; + use ff::PrimeField; + + let mut a = Fq2 { + c0: Fq::from_repr(FqRepr([ + 0x2d0078036923ffc7, + 0x11e59ea221a3b6d2, + 0x8b1a52e0a90f59ed, + 0xb966ce3bc2108b13, + 0xccc649c4b9532bf3, + 0xf8d295b2ded9dc, + ])).unwrap(), + c1: Fq::from_repr(FqRepr([ + 0x977df6efcdaee0db, + 0x946ae52d684fa7ed, + 0xbe203411c66fb3a5, + 0xb3f8afc0ee248cad, + 0x4e464dea5bcfd41e, + 0x12d1137b8a6a837, + ])).unwrap(), + }; + a.sub_assign(&Fq2 { + c0: Fq::from_repr(FqRepr([ + 0x619a02d78dc70ef2, + 0xb93adfc9119e33e8, + 0x4bf0b99a9f0dca12, + 0x3b88899a42a6318f, + 0x986a4a62fa82a49d, + 0x13ce433fa26027f5, + ])).unwrap(), + c1: Fq::from_repr(FqRepr([ + 0x66323bf80b58b9b9, + 0xa1379b6facf6e596, + 0x402aef1fb797e32f, + 0x2236f55246d0d44d, + 0x4c8c1800eb104566, + 0x11d6e20e986c2085, + ])).unwrap(), + }); + assert_eq!( + a, + Fq2 { + c0: Fq::from_repr(FqRepr([ + 0x8565752bdb5c9b80, + 0x7756bed7c15982e9, + 0xa65a6be700b285fe, + 0xe255902672ef6c43, + 0x7f77a718021c342d, + 0x72ba14049fe9881 + ])).unwrap(), + c1: Fq::from_repr(FqRepr([ + 0xeb4abaf7c255d1cd, + 0x11df49bc6cacc256, + 0xe52617930588c69a, + 0xf63905f39ad8cb1f, + 0x4cd5dd9fb40b3b8f, + 0x957411359ba6e4c + ])).unwrap(), + } + ); +} + +#[test] +fn test_fq2_negation() { + use super::fq::FqRepr; + use ff::PrimeField; + + let mut a = Fq2 { + c0: Fq::from_repr(FqRepr([ + 0x2d0078036923ffc7, + 0x11e59ea221a3b6d2, + 0x8b1a52e0a90f59ed, + 0xb966ce3bc2108b13, + 0xccc649c4b9532bf3, + 0xf8d295b2ded9dc, + ])).unwrap(), + c1: Fq::from_repr(FqRepr([ + 0x977df6efcdaee0db, + 0x946ae52d684fa7ed, + 0xbe203411c66fb3a5, + 0xb3f8afc0ee248cad, + 0x4e464dea5bcfd41e, + 0x12d1137b8a6a837, + ])).unwrap(), + }; + a.negate(); + assert_eq!( + a, + Fq2 { + c0: Fq::from_repr(FqRepr([ + 0x8cfe87fc96dbaae4, + 0xcc6615c8fb0492d, + 0xdc167fc04da19c37, + 0xab107d49317487ab, + 0x7e555df189f880e3, + 0x19083f5486a10cbd + ])).unwrap(), + c1: Fq::from_repr(FqRepr([ + 0x228109103250c9d0, + 0x8a411ad149045812, + 0xa9109e8f3041427e, + 0xb07e9bc405608611, + 0xfcd559cbe77bd8b8, + 0x18d400b280d93e62 + ])).unwrap(), + } + ); +} + +#[test] +fn test_fq2_doubling() { + use super::fq::FqRepr; + use ff::PrimeField; + + let mut a = Fq2 { + c0: Fq::from_repr(FqRepr([ + 0x2d0078036923ffc7, + 0x11e59ea221a3b6d2, + 0x8b1a52e0a90f59ed, + 0xb966ce3bc2108b13, + 0xccc649c4b9532bf3, + 0xf8d295b2ded9dc, + ])).unwrap(), + c1: Fq::from_repr(FqRepr([ + 0x977df6efcdaee0db, + 0x946ae52d684fa7ed, + 0xbe203411c66fb3a5, + 0xb3f8afc0ee248cad, + 0x4e464dea5bcfd41e, + 0x12d1137b8a6a837, + ])).unwrap(), + }; + a.double(); + assert_eq!( + a, + Fq2 { + c0: Fq::from_repr(FqRepr([ + 0x5a00f006d247ff8e, + 0x23cb3d4443476da4, + 0x1634a5c1521eb3da, + 0x72cd9c7784211627, + 0x998c938972a657e7, + 0x1f1a52b65bdb3b9 + ])).unwrap(), + c1: Fq::from_repr(FqRepr([ + 0x2efbeddf9b5dc1b6, + 0x28d5ca5ad09f4fdb, + 0x7c4068238cdf674b, + 0x67f15f81dc49195b, + 0x9c8c9bd4b79fa83d, + 0x25a226f714d506e + ])).unwrap(), + } + ); +} + +#[test] +fn test_fq2_frobenius_map() { + use super::fq::FqRepr; + use ff::PrimeField; + + let mut a = Fq2 { + c0: Fq::from_repr(FqRepr([ + 0x2d0078036923ffc7, + 0x11e59ea221a3b6d2, + 0x8b1a52e0a90f59ed, + 0xb966ce3bc2108b13, + 0xccc649c4b9532bf3, + 0xf8d295b2ded9dc, + ])).unwrap(), + c1: Fq::from_repr(FqRepr([ + 0x977df6efcdaee0db, + 0x946ae52d684fa7ed, + 0xbe203411c66fb3a5, + 0xb3f8afc0ee248cad, + 0x4e464dea5bcfd41e, + 0x12d1137b8a6a837, + ])).unwrap(), + }; + a.frobenius_map(0); + assert_eq!( + a, + Fq2 { + c0: Fq::from_repr(FqRepr([ + 0x2d0078036923ffc7, + 0x11e59ea221a3b6d2, + 0x8b1a52e0a90f59ed, + 0xb966ce3bc2108b13, + 0xccc649c4b9532bf3, + 0xf8d295b2ded9dc + ])).unwrap(), + c1: Fq::from_repr(FqRepr([ + 0x977df6efcdaee0db, + 0x946ae52d684fa7ed, + 0xbe203411c66fb3a5, + 0xb3f8afc0ee248cad, + 0x4e464dea5bcfd41e, + 0x12d1137b8a6a837 + ])).unwrap(), + } + ); + a.frobenius_map(1); + assert_eq!( + a, + Fq2 { + c0: Fq::from_repr(FqRepr([ + 0x2d0078036923ffc7, + 0x11e59ea221a3b6d2, + 0x8b1a52e0a90f59ed, + 0xb966ce3bc2108b13, + 0xccc649c4b9532bf3, + 0xf8d295b2ded9dc + ])).unwrap(), + c1: Fq::from_repr(FqRepr([ + 0x228109103250c9d0, + 0x8a411ad149045812, + 0xa9109e8f3041427e, + 0xb07e9bc405608611, + 0xfcd559cbe77bd8b8, + 0x18d400b280d93e62 + ])).unwrap(), + } + ); + a.frobenius_map(1); + assert_eq!( + a, + Fq2 { + c0: Fq::from_repr(FqRepr([ + 0x2d0078036923ffc7, + 0x11e59ea221a3b6d2, + 0x8b1a52e0a90f59ed, + 0xb966ce3bc2108b13, + 0xccc649c4b9532bf3, + 0xf8d295b2ded9dc + ])).unwrap(), + c1: Fq::from_repr(FqRepr([ + 0x977df6efcdaee0db, + 0x946ae52d684fa7ed, + 0xbe203411c66fb3a5, + 0xb3f8afc0ee248cad, + 0x4e464dea5bcfd41e, + 0x12d1137b8a6a837 + ])).unwrap(), + } + ); + a.frobenius_map(2); + assert_eq!( + a, + Fq2 { + c0: Fq::from_repr(FqRepr([ + 0x2d0078036923ffc7, + 0x11e59ea221a3b6d2, + 0x8b1a52e0a90f59ed, + 0xb966ce3bc2108b13, + 0xccc649c4b9532bf3, + 0xf8d295b2ded9dc + ])).unwrap(), + c1: Fq::from_repr(FqRepr([ + 0x977df6efcdaee0db, + 0x946ae52d684fa7ed, + 0xbe203411c66fb3a5, + 0xb3f8afc0ee248cad, + 0x4e464dea5bcfd41e, + 0x12d1137b8a6a837 + ])).unwrap(), + } + ); +} + +#[test] +fn test_fq2_sqrt() { + use super::fq::FqRepr; + use ff::PrimeField; + + assert_eq!( + Fq2 { + c0: Fq::from_repr(FqRepr([ + 0x476b4c309720e227, + 0x34c2d04faffdab6, + 0xa57e6fc1bab51fd9, + 0xdb4a116b5bf74aa1, + 0x1e58b2159dfe10e2, + 0x7ca7da1f13606ac + ])).unwrap(), + c1: Fq::from_repr(FqRepr([ + 0xfa8de88b7516d2c3, + 0x371a75ed14f41629, + 0x4cec2dca577a3eb6, + 0x212611bca4e99121, + 0x8ee5394d77afb3d, + 0xec92336650e49d5 + ])).unwrap(), + }.sqrt() + .unwrap(), + Fq2 { + c0: Fq::from_repr(FqRepr([ + 0x40b299b2704258c5, + 0x6ef7de92e8c68b63, + 0x6d2ddbe552203e82, + 0x8d7f1f723d02c1d3, + 0x881b3e01b611c070, + 0x10f6963bbad2ebc5 + ])).unwrap(), + c1: Fq::from_repr(FqRepr([ + 0xc099534fc209e752, + 0x7670594665676447, + 0x28a20faed211efe7, + 0x6b852aeaf2afcb1b, + 0xa4c93b08105d71a9, + 0x8d7cfff94216330 + ])).unwrap(), + } + ); + + assert_eq!( + Fq2 { + c0: Fq::from_repr(FqRepr([ + 0xb9f78429d1517a6b, + 0x1eabfffeb153ffff, + 0x6730d2a0f6b0f624, + 0x64774b84f38512bf, + 0x4b1ba7b6434bacd7, + 0x1a0111ea397fe69a + ])).unwrap(), + c1: Fq::zero(), + }.sqrt() + .unwrap(), + Fq2 { + c0: Fq::zero(), + c1: Fq::from_repr(FqRepr([ + 0xb9fefffffd4357a3, + 0x1eabfffeb153ffff, + 0x6730d2a0f6b0f624, + 0x64774b84f38512bf, + 0x4b1ba7b6434bacd7, + 0x1a0111ea397fe69a + ])).unwrap(), + } + ); +} + +#[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 nqr = Fq2 { + c0: Fq::one(), + 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; + + crate::tests::field::random_field_tests::(); + crate::tests::field::random_sqrt_tests::(); + crate::tests::field::random_frobenius_tests::(super::fq::Fq::char(), 13); +} diff --git a/pairing/src/bls12_381/fq6.rs b/pairing/src/bls12_381/fq6.rs new file mode 100644 index 0000000..19b8316 --- /dev/null +++ b/pairing/src/bls12_381/fq6.rs @@ -0,0 +1,374 @@ +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(), + } + } +} + +impl Fq6 { + /// Multiply by quadratic 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); + + self.c0.mul_by_nonresidue(); + } + + 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) { + let mut s0 = self.c0; + s0.square(); + let mut ab = self.c0; + ab.mul_assign(&self.c1); + let mut s1 = ab; + s1.double(); + let mut s2 = self.c0; + s2.sub_assign(&self.c1); + s2.add_assign(&self.c2); + s2.square(); + let mut bc = self.c1; + bc.mul_assign(&self.c2); + let mut s3 = bc; + s3.double(); + let mut s4 = self.c2; + s4.square(); + + self.c0 = s3; + self.c0.mul_by_nonresidue(); + self.c0.add_assign(&s0); + + self.c1 = s4; + self.c1.mul_by_nonresidue(); + self.c1.add_assign(&s1); + + 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; + + crate::tests::field::random_field_tests::(); + crate::tests::field::random_frobenius_tests::(super::fq::Fq::char(), 13); +} diff --git a/pairing/src/bls12_381/fr.rs b/pairing/src/bls12_381/fr.rs new file mode 100644 index 0000000..bcecbaa --- /dev/null +++ b/pairing/src/bls12_381/fr.rs @@ -0,0 +1,986 @@ +use ff::{Field, PrimeField, PrimeFieldRepr}; + +#[derive(PrimeField)] +#[PrimeFieldModulus = "52435875175126190479447740508185965837690552500527637822603658699938581184513"] +#[PrimeFieldGenerator = "7"] +pub struct Fr(FrRepr); + +#[cfg(test)] +use rand::{Rand, SeedableRng, XorShiftRng}; + +#[test] +fn test_fr_repr_ordering() { + fn assert_equality(a: FrRepr, b: FrRepr) { + assert_eq!(a, b); + assert!(a.cmp(&b) == ::std::cmp::Ordering::Equal); + } + + fn assert_lt(a: FrRepr, b: FrRepr) { + assert!(a < b); + assert!(b > a); + } + + assert_equality( + FrRepr([9999, 9999, 9999, 9999]), + FrRepr([9999, 9999, 9999, 9999]), + ); + assert_equality( + FrRepr([9999, 9998, 9999, 9999]), + FrRepr([9999, 9998, 9999, 9999]), + ); + assert_equality( + FrRepr([9999, 9999, 9999, 9997]), + FrRepr([9999, 9999, 9999, 9997]), + ); + assert_lt( + FrRepr([9999, 9997, 9999, 9998]), + FrRepr([9999, 9997, 9999, 9999]), + ); + assert_lt( + FrRepr([9999, 9997, 9998, 9999]), + FrRepr([9999, 9997, 9999, 9999]), + ); + assert_lt( + FrRepr([9, 9999, 9999, 9997]), + FrRepr([9999, 9999, 9999, 9997]), + ); +} + +#[test] +fn test_fr_repr_from() { + assert_eq!(FrRepr::from(100), FrRepr([100, 0, 0, 0])); +} + +#[test] +fn test_fr_repr_is_odd() { + assert!(!FrRepr::from(0).is_odd()); + assert!(FrRepr::from(0).is_even()); + assert!(FrRepr::from(1).is_odd()); + assert!(!FrRepr::from(1).is_even()); + assert!(!FrRepr::from(324834872).is_odd()); + assert!(FrRepr::from(324834872).is_even()); + assert!(FrRepr::from(324834873).is_odd()); + assert!(!FrRepr::from(324834873).is_even()); +} + +#[test] +fn test_fr_repr_is_zero() { + assert!(FrRepr::from(0).is_zero()); + assert!(!FrRepr::from(1).is_zero()); + assert!(!FrRepr([0, 0, 1, 0]).is_zero()); +} + +#[test] +fn test_fr_repr_div2() { + let mut a = FrRepr([ + 0xbd2920b19c972321, + 0x174ed0466a3be37e, + 0xd468d5e3b551f0b5, + 0xcb67c072733beefc, + ]); + a.div2(); + assert_eq!( + a, + FrRepr([ + 0x5e949058ce4b9190, + 0x8ba76823351df1bf, + 0x6a346af1daa8f85a, + 0x65b3e039399df77e + ]) + ); + for _ in 0..10 { + a.div2(); + } + assert_eq!( + a, + FrRepr([ + 0x6fd7a524163392e4, + 0x16a2e9da08cd477c, + 0xdf9a8d1abc76aa3e, + 0x196cf80e4e677d + ]) + ); + for _ in 0..200 { + a.div2(); + } + assert_eq!(a, FrRepr([0x196cf80e4e67, 0x0, 0x0, 0x0])); + for _ in 0..40 { + a.div2(); + } + assert_eq!(a, FrRepr([0x19, 0x0, 0x0, 0x0])); + for _ in 0..4 { + a.div2(); + } + assert_eq!(a, FrRepr([0x1, 0x0, 0x0, 0x0])); + a.div2(); + assert!(a.is_zero()); +} + +#[test] +fn test_fr_repr_shr() { + let mut a = FrRepr([ + 0xb33fbaec482a283f, + 0x997de0d3a88cb3df, + 0x9af62d2a9a0e5525, + 0x36003ab08de70da1, + ]); + a.shr(0); + assert_eq!( + a, + FrRepr([ + 0xb33fbaec482a283f, + 0x997de0d3a88cb3df, + 0x9af62d2a9a0e5525, + 0x36003ab08de70da1 + ]) + ); + a.shr(1); + assert_eq!( + a, + FrRepr([ + 0xd99fdd762415141f, + 0xccbef069d44659ef, + 0xcd7b16954d072a92, + 0x1b001d5846f386d0 + ]) + ); + a.shr(50); + assert_eq!( + a, + FrRepr([ + 0xbc1a7511967bf667, + 0xc5a55341caa4b32f, + 0x75611bce1b4335e, + 0x6c0 + ]) + ); + a.shr(130); + assert_eq!(a, FrRepr([0x1d5846f386d0cd7, 0x1b0, 0x0, 0x0])); + a.shr(64); + assert_eq!(a, FrRepr([0x1b0, 0x0, 0x0, 0x0])); +} + +#[test] +fn test_fr_repr_mul2() { + let mut a = FrRepr::from(23712937547); + a.mul2(); + assert_eq!(a, FrRepr([0xb0acd6c96, 0x0, 0x0, 0x0])); + for _ in 0..60 { + a.mul2(); + } + assert_eq!(a, FrRepr([0x6000000000000000, 0xb0acd6c9, 0x0, 0x0])); + for _ in 0..128 { + a.mul2(); + } + assert_eq!(a, FrRepr([0x0, 0x0, 0x6000000000000000, 0xb0acd6c9])); + for _ in 0..60 { + a.mul2(); + } + assert_eq!(a, FrRepr([0x0, 0x0, 0x0, 0x9600000000000000])); + for _ in 0..7 { + a.mul2(); + } + assert!(a.is_zero()); +} + +#[test] +fn test_fr_repr_num_bits() { + let mut a = FrRepr::from(0); + assert_eq!(0, a.num_bits()); + a = FrRepr::from(1); + for i in 1..257 { + assert_eq!(i, a.num_bits()); + a.mul2(); + } + assert_eq!(0, a.num_bits()); +} + +#[test] +fn test_fr_repr_sub_noborrow() { + let mut rng = XorShiftRng::from_seed([0x5dbe6259, 0x8d313d76, 0x3237db17, 0xe5bc0654]); + + let mut t = FrRepr([ + 0x8e62a7e85264e2c3, + 0xb23d34c1941d3ca, + 0x5976930b7502dd15, + 0x600f3fb517bf5495, + ]); + t.sub_noborrow(&FrRepr([ + 0xd64f669809cbc6a4, + 0xfa76cb9d90cf7637, + 0xfefb0df9038d43b3, + 0x298a30c744b31acf, + ])); + assert!( + t == FrRepr([ + 0xb813415048991c1f, + 0x10ad07ae88725d92, + 0x5a7b851271759961, + 0x36850eedd30c39c5 + ]) + ); + + for _ in 0..1000 { + let mut a = FrRepr::rand(&mut rng); + a.0[3] >>= 30; + let mut b = a; + for _ in 0..10 { + b.mul2(); + } + let mut c = b; + for _ in 0..10 { + c.mul2(); + } + + assert!(a < b); + assert!(b < c); + + let mut csub_ba = c; + csub_ba.sub_noborrow(&b); + csub_ba.sub_noborrow(&a); + + let mut csub_ab = c; + csub_ab.sub_noborrow(&a); + csub_ab.sub_noborrow(&b); + + assert_eq!(csub_ab, csub_ba); + } + + // Subtracting r+1 from r should produce -1 (mod 2**256) + let mut qplusone = FrRepr([ + 0xffffffff00000001, + 0x53bda402fffe5bfe, + 0x3339d80809a1d805, + 0x73eda753299d7d48, + ]); + qplusone.sub_noborrow(&FrRepr([ + 0xffffffff00000002, + 0x53bda402fffe5bfe, + 0x3339d80809a1d805, + 0x73eda753299d7d48, + ])); + assert_eq!( + qplusone, + FrRepr([ + 0xffffffffffffffff, + 0xffffffffffffffff, + 0xffffffffffffffff, + 0xffffffffffffffff + ]) + ); +} + +#[test] +fn test_fr_legendre() { + use ff::LegendreSymbol::*; + use ff::SqrtField; + + assert_eq!(QuadraticResidue, Fr::one().legendre()); + assert_eq!(Zero, Fr::zero().legendre()); + + let e = FrRepr([ + 0x0dbc5349cd5664da, + 0x8ac5b6296e3ae29d, + 0x127cb819feceaa3b, + 0x3a6b21fb03867191, + ]); + assert_eq!(QuadraticResidue, Fr::from_repr(e).unwrap().legendre()); + let e = FrRepr([ + 0x96341aefd047c045, + 0x9b5f4254500a4d65, + 0x1ee08223b68ac240, + 0x31d9cd545c0ec7c6, + ]); + assert_eq!(QuadraticNonResidue, Fr::from_repr(e).unwrap().legendre()); +} + +#[test] +fn test_fr_repr_add_nocarry() { + let mut rng = XorShiftRng::from_seed([0x5dbe6259, 0x8d313d76, 0x3237db17, 0xe5bc0654]); + + let mut t = FrRepr([ + 0xd64f669809cbc6a4, + 0xfa76cb9d90cf7637, + 0xfefb0df9038d43b3, + 0x298a30c744b31acf, + ]); + t.add_nocarry(&FrRepr([ + 0x8e62a7e85264e2c3, + 0xb23d34c1941d3ca, + 0x5976930b7502dd15, + 0x600f3fb517bf5495, + ])); + assert_eq!( + t, + FrRepr([ + 0x64b20e805c30a967, + 0x59a9ee9aa114a02, + 0x5871a104789020c9, + 0x8999707c5c726f65 + ]) + ); + + // Test for the associativity of addition. + for _ in 0..1000 { + let mut a = FrRepr::rand(&mut rng); + let mut b = FrRepr::rand(&mut rng); + let mut c = FrRepr::rand(&mut rng); + + // Unset the first few bits, so that overflow won't occur. + a.0[3] >>= 3; + b.0[3] >>= 3; + c.0[3] >>= 3; + + let mut abc = a; + abc.add_nocarry(&b); + abc.add_nocarry(&c); + + let mut acb = a; + acb.add_nocarry(&c); + acb.add_nocarry(&b); + + let mut bac = b; + bac.add_nocarry(&a); + bac.add_nocarry(&c); + + let mut bca = b; + bca.add_nocarry(&c); + bca.add_nocarry(&a); + + let mut cab = c; + cab.add_nocarry(&a); + cab.add_nocarry(&b); + + let mut cba = c; + cba.add_nocarry(&b); + cba.add_nocarry(&a); + + assert_eq!(abc, acb); + assert_eq!(abc, bac); + assert_eq!(abc, bca); + assert_eq!(abc, cab); + assert_eq!(abc, cba); + } + + // Adding 1 to (2^256 - 1) should produce zero + let mut x = FrRepr([ + 0xffffffffffffffff, + 0xffffffffffffffff, + 0xffffffffffffffff, + 0xffffffffffffffff, + ]); + x.add_nocarry(&FrRepr::from(1)); + assert!(x.is_zero()); +} + +#[test] +fn test_fr_is_valid() { + let mut a = Fr(MODULUS); + assert!(!a.is_valid()); + a.0.sub_noborrow(&FrRepr::from(1)); + assert!(a.is_valid()); + assert!(Fr(FrRepr::from(0)).is_valid()); + assert!( + Fr(FrRepr([ + 0xffffffff00000000, + 0x53bda402fffe5bfe, + 0x3339d80809a1d805, + 0x73eda753299d7d48 + ])).is_valid() + ); + assert!( + !Fr(FrRepr([ + 0xffffffffffffffff, + 0xffffffffffffffff, + 0xffffffffffffffff, + 0xffffffffffffffff + ])).is_valid() + ); + + let mut rng = XorShiftRng::from_seed([0x5dbe6259, 0x8d313d76, 0x3237db17, 0xe5bc0654]); + + for _ in 0..1000 { + let a = Fr::rand(&mut rng); + assert!(a.is_valid()); + } +} + +#[test] +fn test_fr_add_assign() { + { + // Random number + let mut tmp = Fr(FrRepr([ + 0x437ce7616d580765, + 0xd42d1ccb29d1235b, + 0xed8f753821bd1423, + 0x4eede1c9c89528ca, + ])); + assert!(tmp.is_valid()); + // Test that adding zero has no effect. + tmp.add_assign(&Fr(FrRepr::from(0))); + assert_eq!( + tmp, + Fr(FrRepr([ + 0x437ce7616d580765, + 0xd42d1ccb29d1235b, + 0xed8f753821bd1423, + 0x4eede1c9c89528ca + ])) + ); + // Add one and test for the result. + tmp.add_assign(&Fr(FrRepr::from(1))); + assert_eq!( + tmp, + Fr(FrRepr([ + 0x437ce7616d580766, + 0xd42d1ccb29d1235b, + 0xed8f753821bd1423, + 0x4eede1c9c89528ca + ])) + ); + // Add another random number that exercises the reduction. + tmp.add_assign(&Fr(FrRepr([ + 0x946f435944f7dc79, + 0xb55e7ee6533a9b9b, + 0x1e43b84c2f6194ca, + 0x58717ab525463496, + ]))); + assert_eq!( + tmp, + Fr(FrRepr([ + 0xd7ec2abbb24fe3de, + 0x35cdf7ae7d0d62f7, + 0xd899557c477cd0e9, + 0x3371b52bc43de018 + ])) + ); + // Add one to (r - 1) and test for the result. + tmp = Fr(FrRepr([ + 0xffffffff00000000, + 0x53bda402fffe5bfe, + 0x3339d80809a1d805, + 0x73eda753299d7d48, + ])); + tmp.add_assign(&Fr(FrRepr::from(1))); + assert!(tmp.0.is_zero()); + // Add a random number to another one such that the result is r - 1 + tmp = Fr(FrRepr([ + 0xade5adacdccb6190, + 0xaa21ee0f27db3ccd, + 0x2550f4704ae39086, + 0x591d1902e7c5ba27, + ])); + tmp.add_assign(&Fr(FrRepr([ + 0x521a525223349e70, + 0xa99bb5f3d8231f31, + 0xde8e397bebe477e, + 0x1ad08e5041d7c321, + ]))); + assert_eq!( + tmp, + Fr(FrRepr([ + 0xffffffff00000000, + 0x53bda402fffe5bfe, + 0x3339d80809a1d805, + 0x73eda753299d7d48 + ])) + ); + // Add one to the result and test for it. + tmp.add_assign(&Fr(FrRepr::from(1))); + assert!(tmp.0.is_zero()); + } + + // Test associativity + + let mut rng = XorShiftRng::from_seed([0x5dbe6259, 0x8d313d76, 0x3237db17, 0xe5bc0654]); + + for _ in 0..1000 { + // Generate a, b, c and ensure (a + b) + c == a + (b + c). + let a = Fr::rand(&mut rng); + let b = Fr::rand(&mut rng); + let c = Fr::rand(&mut rng); + + let mut tmp1 = a; + tmp1.add_assign(&b); + tmp1.add_assign(&c); + + let mut tmp2 = b; + tmp2.add_assign(&c); + tmp2.add_assign(&a); + + assert!(tmp1.is_valid()); + assert!(tmp2.is_valid()); + assert_eq!(tmp1, tmp2); + } +} + +#[test] +fn test_fr_sub_assign() { + { + // Test arbitrary subtraction that tests reduction. + let mut tmp = Fr(FrRepr([ + 0x6a68c64b6f735a2b, + 0xd5f4d143fe0a1972, + 0x37c17f3829267c62, + 0xa2f37391f30915c, + ])); + tmp.sub_assign(&Fr(FrRepr([ + 0xade5adacdccb6190, + 0xaa21ee0f27db3ccd, + 0x2550f4704ae39086, + 0x591d1902e7c5ba27, + ]))); + assert_eq!( + tmp, + Fr(FrRepr([ + 0xbc83189d92a7f89c, + 0x7f908737d62d38a3, + 0x45aa62cfe7e4c3e1, + 0x24ffc5896108547d + ])) + ); + + // Test the opposite subtraction which doesn't test reduction. + tmp = Fr(FrRepr([ + 0xade5adacdccb6190, + 0xaa21ee0f27db3ccd, + 0x2550f4704ae39086, + 0x591d1902e7c5ba27, + ])); + tmp.sub_assign(&Fr(FrRepr([ + 0x6a68c64b6f735a2b, + 0xd5f4d143fe0a1972, + 0x37c17f3829267c62, + 0xa2f37391f30915c, + ]))); + assert_eq!( + tmp, + Fr(FrRepr([ + 0x437ce7616d580765, + 0xd42d1ccb29d1235b, + 0xed8f753821bd1423, + 0x4eede1c9c89528ca + ])) + ); + + // Test for sensible results with zero + tmp = Fr(FrRepr::from(0)); + tmp.sub_assign(&Fr(FrRepr::from(0))); + assert!(tmp.is_zero()); + + tmp = Fr(FrRepr([ + 0x437ce7616d580765, + 0xd42d1ccb29d1235b, + 0xed8f753821bd1423, + 0x4eede1c9c89528ca, + ])); + tmp.sub_assign(&Fr(FrRepr::from(0))); + assert_eq!( + tmp, + Fr(FrRepr([ + 0x437ce7616d580765, + 0xd42d1ccb29d1235b, + 0xed8f753821bd1423, + 0x4eede1c9c89528ca + ])) + ); + } + + let mut rng = XorShiftRng::from_seed([0x5dbe6259, 0x8d313d76, 0x3237db17, 0xe5bc0654]); + + for _ in 0..1000 { + // Ensure that (a - b) + (b - a) = 0. + let a = Fr::rand(&mut rng); + let b = Fr::rand(&mut rng); + + let mut tmp1 = a; + tmp1.sub_assign(&b); + + let mut tmp2 = b; + tmp2.sub_assign(&a); + + tmp1.add_assign(&tmp2); + assert!(tmp1.is_zero()); + } +} + +#[test] +fn test_fr_mul_assign() { + let mut tmp = Fr(FrRepr([ + 0x6b7e9b8faeefc81a, + 0xe30a8463f348ba42, + 0xeff3cb67a8279c9c, + 0x3d303651bd7c774d, + ])); + tmp.mul_assign(&Fr(FrRepr([ + 0x13ae28e3bc35ebeb, + 0xa10f4488075cae2c, + 0x8160e95a853c3b5d, + 0x5ae3f03b561a841d, + ]))); + assert!( + tmp == Fr(FrRepr([ + 0x23717213ce710f71, + 0xdbee1fe53a16e1af, + 0xf565d3e1c2a48000, + 0x4426507ee75df9d7 + ])) + ); + + let mut rng = XorShiftRng::from_seed([0x5dbe6259, 0x8d313d76, 0x3237db17, 0xe5bc0654]); + + for _ in 0..1000000 { + // Ensure that (a * b) * c = a * (b * c) + let a = Fr::rand(&mut rng); + let b = Fr::rand(&mut rng); + let c = Fr::rand(&mut rng); + + let mut tmp1 = a; + tmp1.mul_assign(&b); + tmp1.mul_assign(&c); + + let mut tmp2 = b; + tmp2.mul_assign(&c); + tmp2.mul_assign(&a); + + assert_eq!(tmp1, tmp2); + } + + for _ in 0..1000000 { + // Ensure that r * (a + b + c) = r*a + r*b + r*c + + let r = Fr::rand(&mut rng); + let mut a = Fr::rand(&mut rng); + let mut b = Fr::rand(&mut rng); + let mut c = Fr::rand(&mut rng); + + let mut tmp1 = a; + tmp1.add_assign(&b); + tmp1.add_assign(&c); + tmp1.mul_assign(&r); + + a.mul_assign(&r); + b.mul_assign(&r); + c.mul_assign(&r); + + a.add_assign(&b); + a.add_assign(&c); + + assert_eq!(tmp1, a); + } +} + +#[test] +fn test_fr_squaring() { + let mut a = Fr(FrRepr([ + 0xffffffffffffffff, + 0xffffffffffffffff, + 0xffffffffffffffff, + 0x73eda753299d7d47, + ])); + assert!(a.is_valid()); + a.square(); + assert_eq!( + a, + Fr::from_repr(FrRepr([ + 0xc0d698e7bde077b8, + 0xb79a310579e76ec2, + 0xac1da8d0a9af4e5f, + 0x13f629c49bf23e97 + ])).unwrap() + ); + + let mut rng = XorShiftRng::from_seed([0x5dbe6259, 0x8d313d76, 0x3237db17, 0xe5bc0654]); + + for _ in 0..1000000 { + // Ensure that (a * a) = a^2 + let a = Fr::rand(&mut rng); + + let mut tmp = a; + tmp.square(); + + let mut tmp2 = a; + tmp2.mul_assign(&a); + + assert_eq!(tmp, tmp2); + } +} + +#[test] +fn test_fr_inverse() { + assert!(Fr::zero().inverse().is_none()); + + let mut rng = XorShiftRng::from_seed([0x5dbe6259, 0x8d313d76, 0x3237db17, 0xe5bc0654]); + + let one = Fr::one(); + + for _ in 0..1000 { + // Ensure that a * a^-1 = 1 + let mut a = Fr::rand(&mut rng); + let ainv = a.inverse().unwrap(); + a.mul_assign(&ainv); + assert_eq!(a, one); + } +} + +#[test] +fn test_fr_double() { + let mut rng = XorShiftRng::from_seed([0x5dbe6259, 0x8d313d76, 0x3237db17, 0xe5bc0654]); + + for _ in 0..1000 { + // Ensure doubling a is equivalent to adding a to itself. + let mut a = Fr::rand(&mut rng); + let mut b = a; + b.add_assign(&a); + a.double(); + assert_eq!(a, b); + } +} + +#[test] +fn test_fr_negate() { + { + let mut a = Fr::zero(); + a.negate(); + + assert!(a.is_zero()); + } + + let mut rng = XorShiftRng::from_seed([0x5dbe6259, 0x8d313d76, 0x3237db17, 0xe5bc0654]); + + for _ in 0..1000 { + // Ensure (a - (-a)) = 0. + let mut a = Fr::rand(&mut rng); + let mut b = a; + b.negate(); + a.add_assign(&b); + + assert!(a.is_zero()); + } +} + +#[test] +fn test_fr_pow() { + let mut rng = XorShiftRng::from_seed([0x5dbe6259, 0x8d313d76, 0x3237db17, 0xe5bc0654]); + + for i in 0..1000 { + // Exponentiate by various small numbers and ensure it consists with repeated + // multiplication. + let a = Fr::rand(&mut rng); + let target = a.pow(&[i]); + let mut c = Fr::one(); + for _ in 0..i { + c.mul_assign(&a); + } + assert_eq!(c, target); + } + + for _ in 0..1000 { + // Exponentiating by the modulus should have no effect in a prime field. + let a = Fr::rand(&mut rng); + + assert_eq!(a, a.pow(Fr::char())); + } +} + +#[test] +fn test_fr_sqrt() { + use ff::SqrtField; + + let mut rng = XorShiftRng::from_seed([0x5dbe6259, 0x8d313d76, 0x3237db17, 0xe5bc0654]); + + assert_eq!(Fr::zero().sqrt().unwrap(), Fr::zero()); + + for _ in 0..1000 { + // Ensure sqrt(a^2) = a or -a + let a = Fr::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 = Fr::rand(&mut rng); + + if let Some(mut tmp) = a.sqrt() { + tmp.square(); + + assert_eq!(a, tmp); + } + } +} + +#[test] +fn test_fr_from_into_repr() { + // r + 1 should not be in the field + assert!( + Fr::from_repr(FrRepr([ + 0xffffffff00000002, + 0x53bda402fffe5bfe, + 0x3339d80809a1d805, + 0x73eda753299d7d48 + ])).is_err() + ); + + // r should not be in the field + assert!(Fr::from_repr(Fr::char()).is_err()); + + // Multiply some arbitrary representations to see if the result is as expected. + let a = FrRepr([ + 0x25ebe3a3ad3c0c6a, + 0x6990e39d092e817c, + 0x941f900d42f5658e, + 0x44f8a103b38a71e0, + ]); + let mut a_fr = Fr::from_repr(a).unwrap(); + let b = FrRepr([ + 0x264e9454885e2475, + 0x46f7746bb0308370, + 0x4683ef5347411f9, + 0x58838d7f208d4492, + ]); + let b_fr = Fr::from_repr(b).unwrap(); + let c = FrRepr([ + 0x48a09ab93cfc740d, + 0x3a6600fbfc7a671, + 0x838567017501d767, + 0x7161d6da77745512, + ]); + a_fr.mul_assign(&b_fr); + assert_eq!(a_fr.into_repr(), c); + + // Zero should be in the field. + assert!(Fr::from_repr(FrRepr::from(0)).unwrap().is_zero()); + + let mut rng = XorShiftRng::from_seed([0x5dbe6259, 0x8d313d76, 0x3237db17, 0xe5bc0654]); + + for _ in 0..1000 { + // Try to turn Fr elements into representations and back again, and compare. + let a = Fr::rand(&mut rng); + let a_repr = a.into_repr(); + let b_repr = FrRepr::from(a); + assert_eq!(a_repr, b_repr); + let a_again = Fr::from_repr(a_repr).unwrap(); + + assert_eq!(a, a_again); + } +} + +#[test] +fn test_fr_repr_display() { + assert_eq!( + format!( + "{}", + FrRepr([ + 0x2829c242fa826143, + 0x1f32cf4dd4330917, + 0x932e4e479d168cd9, + 0x513c77587f563f64 + ]) + ), + "0x513c77587f563f64932e4e479d168cd91f32cf4dd43309172829c242fa826143".to_string() + ); + assert_eq!( + format!( + "{}", + FrRepr([ + 0x25ebe3a3ad3c0c6a, + 0x6990e39d092e817c, + 0x941f900d42f5658e, + 0x44f8a103b38a71e0 + ]) + ), + "0x44f8a103b38a71e0941f900d42f5658e6990e39d092e817c25ebe3a3ad3c0c6a".to_string() + ); + assert_eq!( + format!( + "{}", + FrRepr([ + 0xffffffffffffffff, + 0xffffffffffffffff, + 0xffffffffffffffff, + 0xffffffffffffffff + ]) + ), + "0xffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff".to_string() + ); + assert_eq!( + format!("{}", FrRepr([0, 0, 0, 0])), + "0x0000000000000000000000000000000000000000000000000000000000000000".to_string() + ); +} + +#[test] +fn test_fr_display() { + assert_eq!( + format!( + "{}", + Fr::from_repr(FrRepr([ + 0xc3cae746a3b5ecc7, + 0x185ec8eb3f5b5aee, + 0x684499ffe4b9dd99, + 0x7c9bba7afb68faa + ])).unwrap() + ), + "Fr(0x07c9bba7afb68faa684499ffe4b9dd99185ec8eb3f5b5aeec3cae746a3b5ecc7)".to_string() + ); + assert_eq!( + format!( + "{}", + Fr::from_repr(FrRepr([ + 0x44c71298ff198106, + 0xb0ad10817df79b6a, + 0xd034a80a2b74132b, + 0x41cf9a1336f50719 + ])).unwrap() + ), + "Fr(0x41cf9a1336f50719d034a80a2b74132bb0ad10817df79b6a44c71298ff198106)".to_string() + ); +} + +#[test] +fn test_fr_num_bits() { + assert_eq!(Fr::NUM_BITS, 255); + assert_eq!(Fr::CAPACITY, 254); +} + +#[test] +fn test_fr_root_of_unity() { + use ff::SqrtField; + + assert_eq!(Fr::S, 32); + assert_eq!( + Fr::multiplicative_generator(), + Fr::from_repr(FrRepr::from(7)).unwrap() + ); + assert_eq!( + Fr::multiplicative_generator().pow([ + 0xfffe5bfeffffffff, + 0x9a1d80553bda402, + 0x299d7d483339d808, + 0x73eda753 + ]), + Fr::root_of_unity() + ); + assert_eq!(Fr::root_of_unity().pow([1 << Fr::S]), Fr::one()); + assert!(Fr::multiplicative_generator().sqrt().is_none()); +} + +#[test] +fn fr_field_tests() { + crate::tests::field::random_field_tests::(); + crate::tests::field::random_sqrt_tests::(); + crate::tests::field::random_frobenius_tests::(Fr::char(), 13); + crate::tests::field::from_str_tests::(); +} + +#[test] +fn fr_repr_tests() { + crate::tests::repr::random_repr_tests::(); +} diff --git a/pairing/src/bls12_381/mod.rs b/pairing/src/bls12_381/mod.rs new file mode 100644 index 0000000..005a772 --- /dev/null +++ b/pairing/src/bls12_381/mod.rs @@ -0,0 +1,369 @@ +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}; +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::{BitIterator, Field, ScalarEngine}; + +// The BLS parameter x for BLS12-381 is -0xd201000000010000 +const BLS_X: u64 = 0xd201000000010000; +const BLS_X_IS_NEGATIVE: bool = true; + +#[derive(Clone, Debug)] +pub struct Bls12; + +impl ScalarEngine for Bls12 { + type Fr = Fr; +} + +impl Engine for Bls12 { + 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())); + } + } + + // Twisting isomorphism from E to E' + 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_014(&coeffs.2, &c1, &c0); + } + + let mut f = Fq12::one(); + + let mut found_one = false; + for i in BitIterator::new(&[BLS_X >> 1]) { + if !found_one { + found_one = i; + continue; + } + + for &mut (p, ref mut coeffs) in &mut pairs { + ell(&mut f, coeffs.next().unwrap(), &p.0); + } + + if i { + for &mut (p, ref mut coeffs) in &mut pairs { + ell(&mut f, coeffs.next().unwrap(), &p.0); + } + } + + f.square(); + } + + for &mut (p, ref mut coeffs) in &mut pairs { + ell(&mut f, coeffs.next().unwrap(), &p.0); + } + + if BLS_X_IS_NEGATIVE { + f.conjugate(); + } + + 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]); + if BLS_X_IS_NEGATIVE { + f.conjugate(); + } + } + + let mut x = BLS_X; + let mut y0 = r; + y0.square(); + let mut y1 = y0; + exp_by_x(&mut y1, x); + x >>= 1; + let mut y2 = y1; + exp_by_x(&mut y2, x); + x <<= 1; + let mut y3 = r; + y3.conjugate(); + y1.mul_assign(&y3); + y1.conjugate(); + y1.mul_assign(&y2); + y2 = y1; + exp_by_x(&mut y2, x); + y3 = y2; + exp_by_x(&mut y3, x); + y1.conjugate(); + y3.mul_assign(&y1); + y1.conjugate(); + y1.frobenius_map(3); + y2.frobenius_map(2); + y1.mul_assign(&y2); + y2 = y3; + exp_by_x(&mut y2, x); + y2.mul_assign(&y0); + y2.mul_assign(&r); + y1.mul_assign(&y2); + y2 = y3; + y2.frobenius_map(1); + y1.mul_assign(&y2); + + Some(y1) + } + 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); + + tmp3 = tmp4; + tmp3.mul_assign(&zsquared); + tmp3.double(); + tmp3.negate(); + + tmp6.square(); + tmp6.sub_assign(&tmp0); + tmp6.sub_assign(&tmp5); + + tmp1.double(); + tmp1.double(); + + tmp6.sub_assign(&tmp1); + + 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(); + + let mut t0 = zsquared; + t0.mul_assign(&q.x); + + let mut t1 = q.y; + t1.add_assign(&r.z); + t1.square(); + t1.sub_assign(&ysquared); + t1.sub_assign(&zsquared); + t1.mul_assign(&zsquared); + + let mut t2 = t0; + t2.sub_assign(&r.x); + + let mut t3 = t2; + t3.square(); + + let mut t4 = t3; + t4.double(); + t4.double(); + + let mut t5 = t4; + t5.mul_assign(&t2); + + let mut t6 = t1; + t6.sub_assign(&r.y); + t6.sub_assign(&r.y); + + let mut t9 = t6; + t9.mul_assign(&q.x); + + let mut t7 = t4; + t7.mul_assign(&r.x); + + r.x = t6; + r.x.square(); + r.x.sub_assign(&t5); + r.x.sub_assign(&t7); + r.x.sub_assign(&t7); + + r.z.add_assign(&t2); + r.z.square(); + r.z.sub_assign(&zsquared); + r.z.sub_assign(&t3); + + let mut t10 = q.y; + t10.add_assign(&r.z); + + let mut t8 = t7; + t8.sub_assign(&r.x); + t8.mul_assign(&t6); + + t0 = r.y; + t0.mul_assign(&t5); + t0.double(); + + r.y = t8; + r.y.sub_assign(&t0); + + t10.square(); + t10.sub_assign(&ysquared); + + let mut ztsquared = r.z; + ztsquared.square(); + + t10.sub_assign(&ztsquared); + + t9.double(); + t9.sub_assign(&t10); + + t10 = r.z; + t10.double(); + + t6.negate(); + + t1 = t6; + t1.double(); + + (t10, t1, t9) + } + + let mut coeffs = vec![]; + let mut r: G2 = q.into(); + + let mut found_one = false; + for i in BitIterator::new([BLS_X >> 1]) { + if !found_one { + found_one = i; + continue; + } + + coeffs.push(doubling_step(&mut r)); + + if i { + coeffs.push(addition_step(&mut r, &q)); + } + } + + coeffs.push(doubling_step(&mut r)); + + G2Prepared { + coeffs, + infinity: false, + } + } +} + +#[test] +fn bls12_engine_tests() { + crate::tests::engine::engine_tests::(); +} diff --git a/pairing/src/bls12_381/tests/g1_compressed_valid_test_vectors.dat b/pairing/src/bls12_381/tests/g1_compressed_valid_test_vectors.dat new file mode 100644 index 0000000..ea8cd67 Binary files /dev/null and b/pairing/src/bls12_381/tests/g1_compressed_valid_test_vectors.dat differ diff --git a/pairing/src/bls12_381/tests/g1_uncompressed_invalid_test_vectors.dat b/pairing/src/bls12_381/tests/g1_uncompressed_invalid_test_vectors.dat new file mode 100644 index 0000000..e69de29 diff --git a/pairing/src/bls12_381/tests/g1_uncompressed_valid_test_vectors.dat b/pairing/src/bls12_381/tests/g1_uncompressed_valid_test_vectors.dat new file mode 100644 index 0000000..86abfba Binary files /dev/null and b/pairing/src/bls12_381/tests/g1_uncompressed_valid_test_vectors.dat differ diff --git a/pairing/src/bls12_381/tests/g2_compressed_valid_test_vectors.dat b/pairing/src/bls12_381/tests/g2_compressed_valid_test_vectors.dat new file mode 100644 index 0000000..a40bbe2 Binary files /dev/null and b/pairing/src/bls12_381/tests/g2_compressed_valid_test_vectors.dat differ diff --git a/pairing/src/bls12_381/tests/g2_uncompressed_valid_test_vectors.dat b/pairing/src/bls12_381/tests/g2_uncompressed_valid_test_vectors.dat new file mode 100644 index 0000000..92e4bc5 Binary files /dev/null and b/pairing/src/bls12_381/tests/g2_uncompressed_valid_test_vectors.dat differ diff --git a/pairing/src/bls12_381/tests/mod.rs b/pairing/src/bls12_381/tests/mod.rs new file mode 100644 index 0000000..4f2e5b1 --- /dev/null +++ b/pairing/src/bls12_381/tests/mod.rs @@ -0,0 +1,611 @@ +use super::*; +use crate::*; + +#[test] +fn test_pairing_result_against_relic() { + /* + Sent to me from Diego Aranha (author of RELIC library): + + 1250EBD871FC0A92 A7B2D83168D0D727 272D441BEFA15C50 3DD8E90CE98DB3E7 B6D194F60839C508 A84305AACA1789B6 + 089A1C5B46E5110B 86750EC6A5323488 68A84045483C92B7 AF5AF689452EAFAB F1A8943E50439F1D 59882A98EAA0170F + 1368BB445C7C2D20 9703F239689CE34C 0378A68E72A6B3B2 16DA0E22A5031B54 DDFF57309396B38C 881C4C849EC23E87 + 193502B86EDB8857 C273FA075A505129 37E0794E1E65A761 7C90D8BD66065B1F FFE51D7A579973B1 315021EC3C19934F + 01B2F522473D1713 91125BA84DC4007C FBF2F8DA752F7C74 185203FCCA589AC7 19C34DFFBBAAD843 1DAD1C1FB597AAA5 + 018107154F25A764 BD3C79937A45B845 46DA634B8F6BE14A 8061E55CCEBA478B 23F7DACAA35C8CA7 8BEAE9624045B4B6 + 19F26337D205FB46 9CD6BD15C3D5A04D C88784FBB3D0B2DB DEA54D43B2B73F2C BB12D58386A8703E 0F948226E47EE89D + 06FBA23EB7C5AF0D 9F80940CA771B6FF D5857BAAF222EB95 A7D2809D61BFE02E 1BFD1B68FF02F0B8 102AE1C2D5D5AB1A + 11B8B424CD48BF38 FCEF68083B0B0EC5 C81A93B330EE1A67 7D0D15FF7B984E89 78EF48881E32FAC9 1B93B47333E2BA57 + 03350F55A7AEFCD3 C31B4FCB6CE5771C C6A0E9786AB59733 20C806AD36082910 7BA810C5A09FFDD9 BE2291A0C25A99A2 + 04C581234D086A99 02249B64728FFD21 A189E87935A95405 1C7CDBA7B3872629 A4FAFC05066245CB 9108F0242D0FE3EF + 0F41E58663BF08CF 068672CBD01A7EC7 3BACA4D72CA93544 DEFF686BFD6DF543 D48EAA24AFE47E1E FDE449383B676631 + */ + + assert_eq!(Bls12::pairing(G1::one(), G2::one()), Fq12 { + c0: Fq6 { + c0: Fq2 { + c0: Fq::from_str("2819105605953691245277803056322684086884703000473961065716485506033588504203831029066448642358042597501014294104502").unwrap(), + c1: Fq::from_str("1323968232986996742571315206151405965104242542339680722164220900812303524334628370163366153839984196298685227734799").unwrap() + }, + c1: Fq2 { + c0: Fq::from_str("2987335049721312504428602988447616328830341722376962214011674875969052835043875658579425548512925634040144704192135").unwrap(), + c1: Fq::from_str("3879723582452552452538684314479081967502111497413076598816163759028842927668327542875108457755966417881797966271311").unwrap() + }, + c2: Fq2 { + c0: Fq::from_str("261508182517997003171385743374653339186059518494239543139839025878870012614975302676296704930880982238308326681253").unwrap(), + c1: Fq::from_str("231488992246460459663813598342448669854473942105054381511346786719005883340876032043606739070883099647773793170614").unwrap() + } + }, + c1: Fq6 { + c0: Fq2 { + c0: Fq::from_str("3993582095516422658773669068931361134188738159766715576187490305611759126554796569868053818105850661142222948198557").unwrap(), + c1: Fq::from_str("1074773511698422344502264006159859710502164045911412750831641680783012525555872467108249271286757399121183508900634").unwrap() + }, + c1: Fq2 { + c0: Fq::from_str("2727588299083545686739024317998512740561167011046940249988557419323068809019137624943703910267790601287073339193943").unwrap(), + c1: Fq::from_str("493643299814437640914745677854369670041080344349607504656543355799077485536288866009245028091988146107059514546594").unwrap() + }, + c2: Fq2 { + c0: Fq::from_str("734401332196641441839439105942623141234148957972407782257355060229193854324927417865401895596108124443575283868655").unwrap(), + c1: Fq::from_str("2348330098288556420918672502923664952620152483128593484301759394583320358354186482723629999370241674973832318248497").unwrap() + } + } + }); +} + +fn test_vectors>(expected: &[u8]) { + let mut e = G::zero(); + + let mut v = vec![]; + { + let mut expected = expected; + for _ in 0..1000 { + let e_affine = e.into_affine(); + let encoded = E::from_affine(e_affine); + v.extend_from_slice(encoded.as_ref()); + + let mut decoded = E::empty(); + decoded.as_mut().copy_from_slice(&expected[0..E::size()]); + expected = &expected[E::size()..]; + let decoded = decoded.into_affine().unwrap(); + assert_eq!(e_affine, decoded); + + e.add_assign(&G::one()); + } + } + + assert_eq!(&v[..], expected); +} + +#[test] +fn test_g1_uncompressed_valid_vectors() { + test_vectors::(include_bytes!("g1_uncompressed_valid_test_vectors.dat")); +} + +#[test] +fn test_g1_compressed_valid_vectors() { + test_vectors::(include_bytes!("g1_compressed_valid_test_vectors.dat")); +} + +#[test] +fn test_g2_uncompressed_valid_vectors() { + test_vectors::(include_bytes!("g2_uncompressed_valid_test_vectors.dat")); +} + +#[test] +fn test_g2_compressed_valid_vectors() { + test_vectors::(include_bytes!("g2_compressed_valid_test_vectors.dat")); +} + +#[test] +fn test_g1_uncompressed_invalid_vectors() { + { + let z = G1Affine::zero().into_uncompressed(); + + { + let mut z = z; + z.as_mut()[0] |= 0b1000_0000; + if let Err(GroupDecodingError::UnexpectedCompressionMode) = z.into_affine() { + // :) + } else { + panic!("should have rejected the point because we expected an uncompressed point"); + } + } + + { + let mut z = z; + z.as_mut()[0] |= 0b0010_0000; + if let Err(GroupDecodingError::UnexpectedInformation) = z.into_affine() { + // :) + } else { + panic!("should have rejected the point because the parity bit should not be set if the point is at infinity"); + } + } + + for i in 0..G1Uncompressed::size() { + let mut z = z; + z.as_mut()[i] |= 0b0000_0001; + if let Err(GroupDecodingError::UnexpectedInformation) = z.into_affine() { + // :) + } else { + panic!("should have rejected the point because the coordinates should be zeroes at the point at infinity"); + } + } + } + + let o = G1Affine::one().into_uncompressed(); + + { + let mut o = o; + o.as_mut()[0] |= 0b1000_0000; + if let Err(GroupDecodingError::UnexpectedCompressionMode) = o.into_affine() { + // :) + } else { + panic!("should have rejected the point because we expected an uncompressed point"); + } + } + + let m = Fq::char(); + + { + let mut o = o; + m.write_be(&mut o.as_mut()[0..]).unwrap(); + + if let Err(GroupDecodingError::CoordinateDecodingError(coordinate, _)) = o.into_affine() { + assert_eq!(coordinate, "x coordinate"); + } else { + panic!("should have rejected the point") + } + } + + { + let mut o = o; + m.write_be(&mut o.as_mut()[48..]).unwrap(); + + if let Err(GroupDecodingError::CoordinateDecodingError(coordinate, _)) = o.into_affine() { + assert_eq!(coordinate, "y coordinate"); + } else { + panic!("should have rejected the point") + } + } + + { + let m = Fq::zero().into_repr(); + + let mut o = o; + m.write_be(&mut o.as_mut()[0..]).unwrap(); + + if let Err(GroupDecodingError::NotOnCurve) = o.into_affine() { + // :) + } else { + panic!("should have rejected the point because it isn't on the curve") + } + } + + { + let mut o = o; + let mut x = Fq::one(); + + loop { + let mut x3b = x; + x3b.square(); + x3b.mul_assign(&x); + x3b.add_assign(&Fq::from_repr(FqRepr::from(4)).unwrap()); // TODO: perhaps expose coeff_b through API? + + if let Some(y) = x3b.sqrt() { + // We know this is on the curve, but it's likely not going to be in the correct subgroup. + x.into_repr().write_be(&mut o.as_mut()[0..]).unwrap(); + y.into_repr().write_be(&mut o.as_mut()[48..]).unwrap(); + + if let Err(GroupDecodingError::NotInSubgroup) = o.into_affine() { + break; + } else { + panic!( + "should have rejected the point because it isn't in the correct subgroup" + ) + } + } else { + x.add_assign(&Fq::one()); + } + } + } +} + +#[test] +fn test_g2_uncompressed_invalid_vectors() { + { + let z = G2Affine::zero().into_uncompressed(); + + { + let mut z = z; + z.as_mut()[0] |= 0b1000_0000; + if let Err(GroupDecodingError::UnexpectedCompressionMode) = z.into_affine() { + // :) + } else { + panic!("should have rejected the point because we expected an uncompressed point"); + } + } + + { + let mut z = z; + z.as_mut()[0] |= 0b0010_0000; + if let Err(GroupDecodingError::UnexpectedInformation) = z.into_affine() { + // :) + } else { + panic!("should have rejected the point because the parity bit should not be set if the point is at infinity"); + } + } + + for i in 0..G2Uncompressed::size() { + let mut z = z; + z.as_mut()[i] |= 0b0000_0001; + if let Err(GroupDecodingError::UnexpectedInformation) = z.into_affine() { + // :) + } else { + panic!("should have rejected the point because the coordinates should be zeroes at the point at infinity"); + } + } + } + + let o = G2Affine::one().into_uncompressed(); + + { + let mut o = o; + o.as_mut()[0] |= 0b1000_0000; + if let Err(GroupDecodingError::UnexpectedCompressionMode) = o.into_affine() { + // :) + } else { + panic!("should have rejected the point because we expected an uncompressed point"); + } + } + + let m = Fq::char(); + + { + let mut o = o; + m.write_be(&mut o.as_mut()[0..]).unwrap(); + + if let Err(GroupDecodingError::CoordinateDecodingError(coordinate, _)) = o.into_affine() { + assert_eq!(coordinate, "x coordinate (c1)"); + } else { + panic!("should have rejected the point") + } + } + + { + let mut o = o; + m.write_be(&mut o.as_mut()[48..]).unwrap(); + + if let Err(GroupDecodingError::CoordinateDecodingError(coordinate, _)) = o.into_affine() { + assert_eq!(coordinate, "x coordinate (c0)"); + } else { + panic!("should have rejected the point") + } + } + + { + let mut o = o; + m.write_be(&mut o.as_mut()[96..]).unwrap(); + + if let Err(GroupDecodingError::CoordinateDecodingError(coordinate, _)) = o.into_affine() { + assert_eq!(coordinate, "y coordinate (c1)"); + } else { + panic!("should have rejected the point") + } + } + + { + let mut o = o; + m.write_be(&mut o.as_mut()[144..]).unwrap(); + + if let Err(GroupDecodingError::CoordinateDecodingError(coordinate, _)) = o.into_affine() { + assert_eq!(coordinate, "y coordinate (c0)"); + } else { + panic!("should have rejected the point") + } + } + + { + let m = Fq::zero().into_repr(); + + let mut o = o; + m.write_be(&mut o.as_mut()[0..]).unwrap(); + m.write_be(&mut o.as_mut()[48..]).unwrap(); + + if let Err(GroupDecodingError::NotOnCurve) = o.into_affine() { + // :) + } else { + panic!("should have rejected the point because it isn't on the curve") + } + } + + { + let mut o = o; + let mut x = Fq2::one(); + + loop { + let mut x3b = x; + x3b.square(); + x3b.mul_assign(&x); + x3b.add_assign(&Fq2 { + c0: Fq::from_repr(FqRepr::from(4)).unwrap(), + c1: Fq::from_repr(FqRepr::from(4)).unwrap(), + }); // TODO: perhaps expose coeff_b through API? + + if let Some(y) = x3b.sqrt() { + // We know this is on the curve, but it's likely not going to be in the correct subgroup. + x.c1.into_repr().write_be(&mut o.as_mut()[0..]).unwrap(); + x.c0.into_repr().write_be(&mut o.as_mut()[48..]).unwrap(); + y.c1.into_repr().write_be(&mut o.as_mut()[96..]).unwrap(); + y.c0.into_repr().write_be(&mut o.as_mut()[144..]).unwrap(); + + if let Err(GroupDecodingError::NotInSubgroup) = o.into_affine() { + break; + } else { + panic!( + "should have rejected the point because it isn't in the correct subgroup" + ) + } + } else { + x.add_assign(&Fq2::one()); + } + } + } +} + +#[test] +fn test_g1_compressed_invalid_vectors() { + { + let z = G1Affine::zero().into_compressed(); + + { + let mut z = z; + z.as_mut()[0] &= 0b0111_1111; + if let Err(GroupDecodingError::UnexpectedCompressionMode) = z.into_affine() { + // :) + } else { + panic!("should have rejected the point because we expected a compressed point"); + } + } + + { + let mut z = z; + z.as_mut()[0] |= 0b0010_0000; + if let Err(GroupDecodingError::UnexpectedInformation) = z.into_affine() { + // :) + } else { + panic!("should have rejected the point because the parity bit should not be set if the point is at infinity"); + } + } + + for i in 0..G1Compressed::size() { + let mut z = z; + z.as_mut()[i] |= 0b0000_0001; + if let Err(GroupDecodingError::UnexpectedInformation) = z.into_affine() { + // :) + } else { + panic!("should have rejected the point because the coordinates should be zeroes at the point at infinity"); + } + } + } + + let o = G1Affine::one().into_compressed(); + + { + let mut o = o; + o.as_mut()[0] &= 0b0111_1111; + if let Err(GroupDecodingError::UnexpectedCompressionMode) = o.into_affine() { + // :) + } else { + panic!("should have rejected the point because we expected a compressed point"); + } + } + + let m = Fq::char(); + + { + let mut o = o; + m.write_be(&mut o.as_mut()[0..]).unwrap(); + o.as_mut()[0] |= 0b1000_0000; + + if let Err(GroupDecodingError::CoordinateDecodingError(coordinate, _)) = o.into_affine() { + assert_eq!(coordinate, "x coordinate"); + } else { + panic!("should have rejected the point") + } + } + + { + let mut o = o; + let mut x = Fq::one(); + + loop { + let mut x3b = x; + x3b.square(); + x3b.mul_assign(&x); + x3b.add_assign(&Fq::from_repr(FqRepr::from(4)).unwrap()); // TODO: perhaps expose coeff_b through API? + + if let Some(_) = x3b.sqrt() { + x.add_assign(&Fq::one()); + } else { + x.into_repr().write_be(&mut o.as_mut()[0..]).unwrap(); + o.as_mut()[0] |= 0b1000_0000; + + if let Err(GroupDecodingError::NotOnCurve) = o.into_affine() { + break; + } else { + panic!("should have rejected the point because it isn't on the curve") + } + } + } + } + + { + let mut o = o; + let mut x = Fq::one(); + + loop { + let mut x3b = x; + x3b.square(); + x3b.mul_assign(&x); + x3b.add_assign(&Fq::from_repr(FqRepr::from(4)).unwrap()); // TODO: perhaps expose coeff_b through API? + + if let Some(_) = x3b.sqrt() { + // We know this is on the curve, but it's likely not going to be in the correct subgroup. + x.into_repr().write_be(&mut o.as_mut()[0..]).unwrap(); + o.as_mut()[0] |= 0b1000_0000; + + if let Err(GroupDecodingError::NotInSubgroup) = o.into_affine() { + break; + } else { + panic!( + "should have rejected the point because it isn't in the correct subgroup" + ) + } + } else { + x.add_assign(&Fq::one()); + } + } + } +} + +#[test] +fn test_g2_compressed_invalid_vectors() { + { + let z = G2Affine::zero().into_compressed(); + + { + let mut z = z; + z.as_mut()[0] &= 0b0111_1111; + if let Err(GroupDecodingError::UnexpectedCompressionMode) = z.into_affine() { + // :) + } else { + panic!("should have rejected the point because we expected a compressed point"); + } + } + + { + let mut z = z; + z.as_mut()[0] |= 0b0010_0000; + if let Err(GroupDecodingError::UnexpectedInformation) = z.into_affine() { + // :) + } else { + panic!("should have rejected the point because the parity bit should not be set if the point is at infinity"); + } + } + + for i in 0..G2Compressed::size() { + let mut z = z; + z.as_mut()[i] |= 0b0000_0001; + if let Err(GroupDecodingError::UnexpectedInformation) = z.into_affine() { + // :) + } else { + panic!("should have rejected the point because the coordinates should be zeroes at the point at infinity"); + } + } + } + + let o = G2Affine::one().into_compressed(); + + { + let mut o = o; + o.as_mut()[0] &= 0b0111_1111; + if let Err(GroupDecodingError::UnexpectedCompressionMode) = o.into_affine() { + // :) + } else { + panic!("should have rejected the point because we expected a compressed point"); + } + } + + let m = Fq::char(); + + { + let mut o = o; + m.write_be(&mut o.as_mut()[0..]).unwrap(); + o.as_mut()[0] |= 0b1000_0000; + + if let Err(GroupDecodingError::CoordinateDecodingError(coordinate, _)) = o.into_affine() { + assert_eq!(coordinate, "x coordinate (c1)"); + } else { + panic!("should have rejected the point") + } + } + + { + let mut o = o; + m.write_be(&mut o.as_mut()[48..]).unwrap(); + o.as_mut()[0] |= 0b1000_0000; + + if let Err(GroupDecodingError::CoordinateDecodingError(coordinate, _)) = o.into_affine() { + assert_eq!(coordinate, "x coordinate (c0)"); + } else { + panic!("should have rejected the point") + } + } + + { + let mut o = o; + let mut x = Fq2 { + c0: Fq::one(), + c1: Fq::one(), + }; + + loop { + let mut x3b = x; + x3b.square(); + x3b.mul_assign(&x); + x3b.add_assign(&Fq2 { + c0: Fq::from_repr(FqRepr::from(4)).unwrap(), + c1: Fq::from_repr(FqRepr::from(4)).unwrap(), + }); // TODO: perhaps expose coeff_b through API? + + if let Some(_) = x3b.sqrt() { + x.add_assign(&Fq2::one()); + } else { + x.c1.into_repr().write_be(&mut o.as_mut()[0..]).unwrap(); + x.c0.into_repr().write_be(&mut o.as_mut()[48..]).unwrap(); + o.as_mut()[0] |= 0b1000_0000; + + if let Err(GroupDecodingError::NotOnCurve) = o.into_affine() { + break; + } else { + panic!("should have rejected the point because it isn't on the curve") + } + } + } + } + + { + let mut o = o; + let mut x = Fq2 { + c0: Fq::one(), + c1: Fq::one(), + }; + + loop { + let mut x3b = x; + x3b.square(); + x3b.mul_assign(&x); + x3b.add_assign(&Fq2 { + c0: Fq::from_repr(FqRepr::from(4)).unwrap(), + c1: Fq::from_repr(FqRepr::from(4)).unwrap(), + }); // TODO: perhaps expose coeff_b through API? + + if let Some(_) = x3b.sqrt() { + // We know this is on the curve, but it's likely not going to be in the correct subgroup. + x.c1.into_repr().write_be(&mut o.as_mut()[0..]).unwrap(); + x.c0.into_repr().write_be(&mut o.as_mut()[48..]).unwrap(); + o.as_mut()[0] |= 0b1000_0000; + + if let Err(GroupDecodingError::NotInSubgroup) = o.into_affine() { + break; + } else { + panic!( + "should have rejected the point because it isn't in the correct subgroup" + ) + } + } else { + x.add_assign(&Fq2::one()); + } + } + } +} diff --git a/pairing/src/bn256/README.md b/pairing/src/bn256/README.md new file mode 100644 index 0000000..8d6b211 --- /dev/null +++ b/pairing/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/pairing/src/bn256/ec.rs b/pairing/src/bn256/ec.rs new file mode 100644 index 0000000..6413d6b --- /dev/null +++ b/pairing/src/bn256/ec.rs @@ -0,0 +1,1717 @@ +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 crate::{RawEncodable, CurveAffine, CurveProjective, EncodedPoint, Engine, GroupDecodingError}; + + curve_impl!( + "G1", + G1, + G1Affine, + G1Prepared, + Fq, + Fr, + G1Uncompressed, + G1Compressed, + G2Affine + ); + + impl RawEncodable for G1Affine { + fn into_raw_uncompressed_le(&self) -> Self::Uncompressed { + let mut res = Self::Uncompressed::empty(); + { + let mut writer = &mut res.0[..]; + + self.x.into_raw_repr().write_le(&mut writer).unwrap(); + self.y.into_raw_repr().write_le(&mut writer).unwrap(); + } + + res + } + + /// Creates a point from raw encoded coordinates without checking on curve + fn from_raw_uncompressed_le_unchecked( + encoded: &Self::Uncompressed, + _infinity: bool + ) -> Result { + let copy = encoded.0; + + if copy.iter().all(|b| *b == 0) { + return Ok(Self::zero()); + } + + let mut x = FqRepr([0; 4]); + let mut y = FqRepr([0; 4]); + + { + let mut reader = ©[..]; + x.read_le(&mut reader).unwrap(); + y.read_le(&mut reader).unwrap(); + } + + Ok(G1Affine { + x: Fq::from_raw_repr(x).map_err(|e| { + GroupDecodingError::CoordinateDecodingError("x coordinate", e) + })?, + y: Fq::from_raw_repr(y).map_err(|e| { + GroupDecodingError::CoordinateDecodingError("y coordinate", e) + })?, + infinity: false, + }) + } + + fn from_raw_uncompressed_le(encoded: &Self::Uncompressed, _infinity: bool) -> Result { + let affine = Self::from_raw_uncompressed_le_unchecked(&encoded, _infinity)?; + + if !affine.is_on_curve() { + Err(GroupDecodingError::NotOnCurve) + } else { + Ok(affine) + } + } + } + + #[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 scale_by_cofactor(&self) -> G1 { + // self.into_projective() + // } + + 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() { + crate::tests::curve::curve_tests::(); + crate::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 crate::{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 { + loop { + let x = rng.gen(); + let greatest = rng.gen(); + + if let Some(p) = G2Affine::get_point_from_x(x, greatest) { + if !p.is_zero() { + if p.is_on_curve() { + return p.scale_by_cofactor(); + } + } + } + } + } + } + + impl Rand for G2Affine { + fn rand(rng: &mut R) -> Self { + let r = G2::rand(rng); + 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 scale_by_cofactor(&self) -> G2 { + // G2 cofactor = 2p - n = 2q - r + // 0x30644e72e131a029b85045b68181585e06ceecda572a2489345f2299c0f9fa8d + let cofactor = BitIterator::new([ + 0x345f2299c0f9fa8d, + 0x06ceecda572a2489, + 0xb85045b68181585e, + 0x30644e72e131a029, + ]); + self.mul_bits(cofactor) + } + + 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()); + } + } + + #[test] + fn test_generate_g2_in_subgroup() { + use SqrtField; + + let mut x = Fq2::zero(); + 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(); + let mut minus_one = Fr::one(); + minus_one.negate(); + + let mut expected_zero = p.mul(minus_one); + expected_zero.add_assign(&g2); + + if !expected_zero.is_zero() { + let p = expected_zero.into_affine(); + let scaled_by_cofactor = p.scale_by_cofactor(); + if scaled_by_cofactor.is_zero() { + let g2 = G2Affine::from(expected_zero); + println!("Invalid subgroup point = {}", g2); + return; + } + } + } + + 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() { + crate::tests::curve::curve_tests::(); + crate::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/pairing/src/bn256/fq.rs b/pairing/src/bn256/fq.rs new file mode 100644 index 0000000..9d40d3d --- /dev/null +++ b/pairing/src/bn256/fq.rs @@ -0,0 +1,579 @@ +use super::fq2::Fq2; +use ff::{Field, PrimeField, 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() { + crate::tests::field::random_field_tests::(); + crate::tests::field::random_sqrt_tests::(); + crate::tests::field::random_frobenius_tests::(Fq::char(), 13); + crate::tests::field::from_str_tests::(); +} \ No newline at end of file diff --git a/pairing/src/bn256/fq12.rs b/pairing/src/bn256/fq12.rs new file mode 100644 index 0000000..79a68e7 --- /dev/null +++ b/pairing/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; + + crate::tests::field::random_field_tests::(); + crate::tests::field::random_frobenius_tests::(super::fq::Fq::char(), 13); +} diff --git a/pairing/src/bn256/fq2.rs b/pairing/src/bn256/fq2.rs new file mode 100644 index 0000000..4a1f9b9 --- /dev/null +++ b/pairing/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; + + crate::tests::field::random_field_tests::(); + crate::tests::field::random_sqrt_tests::(); + crate::tests::field::random_frobenius_tests::(super::fq::Fq::char(), 13); +} diff --git a/pairing/src/bn256/fq6.rs b/pairing/src/bn256/fq6.rs new file mode 100644 index 0000000..d9e481e --- /dev/null +++ b/pairing/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; + + crate::tests::field::random_field_tests::(); + crate::tests::field::random_frobenius_tests::(super::fq::Fq::char(), 13); +} diff --git a/pairing/src/bn256/fr.rs b/pairing/src/bn256/fr.rs new file mode 100644 index 0000000..7f4d103 --- /dev/null +++ b/pairing/src/bn256/fr.rs @@ -0,0 +1,39 @@ +use ff::{Field, PrimeField, PrimeFieldRepr}; + +#[derive(PrimeField)] +#[PrimeFieldModulus = "21888242871839275222246405745257275088548364400416034343698204186575808495617"] +#[PrimeFieldGenerator = "7"] +pub struct Fr(FrRepr); + +#[test] +fn test_to_hex() { + assert_eq!(Fr::one().to_hex(), "0000000000000000000000000000000000000000000000000000000000000001"); +} + +#[test] +fn test_fr_from_hex() { + let fr = Fr::from_hex("0000000000000000000000000000000000000000000000000000000000000001").unwrap(); + assert_eq!(fr, Fr::one()); + + let fr = Fr::from_hex("0x0000000000000000000000000000000000000000000000000000000000000001").unwrap(); + assert_eq!(fr, Fr::one()); + + let fr = Fr::from_hex("0x01").unwrap(); + assert_eq!(fr, Fr::one()); + + let fr = Fr::from_hex("0x00").unwrap(); + assert_eq!(fr, Fr::zero()); + + let fr = Fr::from_hex("00").unwrap(); + assert_eq!(fr, Fr::zero()); +} + +#[test] +fn test_roots_of_unity() { + assert_eq!(Fr::S, 28); +} + +#[test] +fn test_default() { + assert_eq!(Fr::default(), Fr::zero()); +} \ No newline at end of file diff --git a/pairing/src/bn256/mod.rs b/pairing/src/bn256/mod.rs new file mode 100644 index 0000000..54087e1 --- /dev/null +++ b/pairing/src/bn256/mod.rs @@ -0,0 +1,604 @@ +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 crate::{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 crate::{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() { + crate::tests::engine::engine_tests::(); +} diff --git a/pairing/src/lib.rs b/pairing/src/lib.rs new file mode 100644 index 0000000..8c1a255 --- /dev/null +++ b/pairing/src/lib.rs @@ -0,0 +1,316 @@ +// `clippy` is a code linting tool for improving code quality by catching +// 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))] +// Force public structures to implement Debug +#![deny(missing_debug_implementations)] + +extern crate byteorder; +extern crate rand; + +#[cfg(test)] +pub mod tests; + +pub extern crate ff; + +pub use ff::*; + +pub mod bls12_381; +pub mod bn256; + +mod wnaf; +pub use self::wnaf::Wnaf; + +use ff::{Field, PrimeField, PrimeFieldDecodingError, PrimeFieldRepr, ScalarEngine, SqrtField}; +use std::error::Error; +use std::fmt; + +/// An "engine" is a collection of types (fields, elliptic curve groups, etc.) +/// with well-defined relationships. In particular, the G1/G2 curve groups are +/// of prime order `r`, and are equipped with a bilinear pairing function. +pub trait Engine: ScalarEngine { + /// The projective representation of an element in G1. + type G1: CurveProjective< + Engine = Self, + Base = Self::Fq, + Scalar = Self::Fr, + Affine = Self::G1Affine, + > + + From; + + /// The affine representation of an element in G1. + type G1Affine: CurveAffine< + Engine = Self, + Base = Self::Fq, + Scalar = Self::Fr, + Projective = Self::G1, + Pair = Self::G2Affine, + PairingResult = Self::Fqk, + > + + From + RawEncodable; + + /// The projective representation of an element in G2. + type G2: CurveProjective< + Engine = Self, + Base = Self::Fqe, + Scalar = Self::Fr, + Affine = Self::G2Affine, + > + + From; + + /// The affine representation of an element in G2. + type G2Affine: CurveAffine< + Engine = Self, + Base = Self::Fqe, + Scalar = Self::Fr, + Projective = Self::G2, + Pair = Self::G1Affine, + PairingResult = Self::Fqk, + > + + From; + + /// The base field that hosts G1. + type Fq: PrimeField + SqrtField; + + /// The extension field that hosts G2. + type Fqe: SqrtField; + + /// The extension field that hosts the target group of the pairing. + type Fqk: Field; + + /// Perform a miller loop with some number of (G1, G2) pairs. + fn miller_loop<'a, I>(i: I) -> Self::Fqk + where + I: IntoIterator< + Item = &'a ( + &'a ::Prepared, + &'a ::Prepared, + ), + >; + + /// Perform final exponentiation of the result of a miller loop. + fn final_exponentiation(r: &Self::Fqk) -> Option; + + /// Performs a complete pairing operation `(p, q)`. + fn pairing(p: G1, q: G2) -> Self::Fqk + where + G1: Into, + G2: Into, + { + Self::final_exponentiation(&Self::miller_loop( + [(&(p.into().prepare()), &(q.into().prepare()))].into_iter(), + )).unwrap() + } +} + +/// Projective representation of an elliptic curve point guaranteed to be +/// in the correct prime order subgroup. +pub trait CurveProjective: + PartialEq + + Eq + + Sized + + Copy + + Clone + + Send + + Sync + + fmt::Debug + + fmt::Display + + rand::Rand + + 'static +{ + type Engine: Engine; + type Scalar: PrimeField + SqrtField; + type Base: SqrtField; + type Affine: CurveAffine; + + /// Returns the additive identity. + fn zero() -> Self; + + /// Returns a fixed generator of unknown exponent. + fn one() -> Self; + + /// Determines if this point is the point at infinity. + fn is_zero(&self) -> bool; + + /// Normalizes a slice of projective elements so that + /// conversion to affine is cheap. + fn batch_normalization(v: &mut [Self]); + + /// Checks if the point is already "normalized" so that + /// cheap affine conversion is possible. + fn is_normalized(&self) -> bool; + + /// Doubles this element. + fn double(&mut self); + + /// Adds another element to this element. + fn add_assign(&mut self, other: &Self); + + /// Subtracts another element from this element. + fn sub_assign(&mut self, other: &Self) { + let mut tmp = *other; + tmp.negate(); + self.add_assign(&tmp); + } + + /// Adds an affine element to this element. + fn add_assign_mixed(&mut self, other: &Self::Affine); + + /// Negates this element. + fn negate(&mut self); + + /// Performs scalar multiplication of this element. + fn mul_assign::Repr>>(&mut self, other: S); + + /// Converts this element into its affine representation. + fn into_affine(&self) -> Self::Affine; + + /// Recommends a wNAF window table size given a scalar. Always returns a number + /// between 2 and 22, inclusive. + fn recommended_wnaf_for_scalar(scalar: ::Repr) -> usize; + + /// Recommends a wNAF window size given the number of scalars you intend to multiply + /// a base by. Always returns a number between 2 and 22, inclusive. + fn recommended_wnaf_for_num_scalars(num_scalars: usize) -> usize; +} + +/// Affine representation of an elliptic curve point guaranteed to be +/// in the correct prime order subgroup. +pub trait CurveAffine: + Copy + Clone + Sized + Send + Sync + fmt::Debug + fmt::Display + PartialEq + Eq + 'static +{ + type Engine: Engine; + type Scalar: PrimeField + SqrtField; + type Base: SqrtField; + type Projective: CurveProjective; + type Prepared: Clone + Send + Sync + 'static; + type Uncompressed: EncodedPoint; + type Compressed: EncodedPoint; + type Pair: CurveAffine; + type PairingResult: Field; + + /// Returns the additive identity. + fn zero() -> Self; + + /// Returns a fixed generator of unknown exponent. + fn one() -> Self; + + /// Determines if this point represents the point at infinity; the + /// additive identity. + fn is_zero(&self) -> bool; + + /// Negates this element. + fn negate(&mut self); + + /// Performs scalar multiplication of this element with mixed addition. + fn mul::Repr>>(&self, other: S) -> Self::Projective; + + /// Prepares this element for pairing purposes. + fn prepare(&self) -> Self::Prepared; + + /// Perform a pairing + fn pairing_with(&self, other: &Self::Pair) -> Self::PairingResult; + + /// Converts this element into its affine representation. + fn into_projective(&self) -> Self::Projective; + + /// Converts this element into its compressed encoding, so long as it's not + /// the point at infinity. + fn into_compressed(&self) -> Self::Compressed { + ::from_affine(*self) + } + + /// Converts this element into its uncompressed encoding, so long as it's not + /// the point at infinity. + fn into_uncompressed(&self) -> Self::Uncompressed { + ::from_affine(*self) + } +} + +pub trait RawEncodable: CurveAffine { + /// Converts this element into its uncompressed encoding, so long as it's not + /// the point at infinity. Leaves coordinates in Montgommery form + fn into_raw_uncompressed_le(&self) -> Self::Uncompressed; + + /// Creates a point from raw encoded coordinates without checking on curve + fn from_raw_uncompressed_le_unchecked(encoded: &Self::Uncompressed, infinity: bool) -> Result; + + /// Creates a point from raw encoded coordinates + fn from_raw_uncompressed_le(encoded: &Self::Uncompressed, infinity: bool) -> Result; +} + +/// An encoded elliptic curve point, which should essentially wrap a `[u8; N]`. +pub trait EncodedPoint: + Sized + Send + Sync + AsRef<[u8]> + AsMut<[u8]> + Clone + Copy + 'static +{ + type Affine: CurveAffine; + + /// Creates an empty representation. + fn empty() -> Self; + + /// Returns the number of bytes consumed by this representation. + fn size() -> usize; + + /// Converts an `EncodedPoint` into a `CurveAffine` element, + /// if the encoding represents a valid element. + fn into_affine(&self) -> Result; + + /// Converts an `EncodedPoint` into a `CurveAffine` element, + /// without guaranteeing that the encoding represents a valid + /// element. This is useful when the caller knows the encoding is + /// valid already. + /// + /// If the encoding is invalid, this can break API invariants, + /// so caution is strongly encouraged. + fn into_affine_unchecked(&self) -> Result; + + /// Creates an `EncodedPoint` from an affine point, as long as the + /// point is not the point at infinity. + fn from_affine(affine: Self::Affine) -> Self; +} + +/// An error that may occur when trying to decode an `EncodedPoint`. +#[derive(Debug)] +pub enum GroupDecodingError { + /// The coordinate(s) do not lie on the curve. + NotOnCurve, + /// The element is not part of the r-order subgroup. + NotInSubgroup, + /// One of the coordinates could not be decoded + CoordinateDecodingError(&'static str, PrimeFieldDecodingError), + /// The compression mode of the encoded element was not as expected + UnexpectedCompressionMode, + /// The encoding contained bits that should not have been set + UnexpectedInformation, +} + +impl Error for GroupDecodingError { + fn description(&self) -> &str { + match *self { + GroupDecodingError::NotOnCurve => "coordinate(s) do not lie on the curve", + GroupDecodingError::NotInSubgroup => "the element is not part of an r-order subgroup", + GroupDecodingError::CoordinateDecodingError(..) => "coordinate(s) could not be decoded", + GroupDecodingError::UnexpectedCompressionMode => { + "encoding has unexpected compression mode" + } + GroupDecodingError::UnexpectedInformation => "encoding has unexpected information", + } + } +} + +impl fmt::Display for GroupDecodingError { + fn fmt(&self, f: &mut fmt::Formatter) -> Result<(), fmt::Error> { + match *self { + GroupDecodingError::CoordinateDecodingError(description, ref err) => { + write!(f, "{} decoding error: {}", description, err) + } + _ => write!(f, "{}", self.description()), + } + } +} diff --git a/pairing/src/tests/curve.rs b/pairing/src/tests/curve.rs new file mode 100644 index 0000000..4663a42 --- /dev/null +++ b/pairing/src/tests/curve.rs @@ -0,0 +1,460 @@ +use ff::Field; +use rand::{Rand, Rng, SeedableRng, XorShiftRng}; + +use crate::{CurveAffine, CurveProjective, EncodedPoint}; + +pub fn curve_tests() { + let mut rng = XorShiftRng::from_seed([0x5dbe6259, 0x8d313d76, 0x3237db17, 0xe5bc0654]); + + // Negation edge case with zero. + { + let mut z = G::zero(); + z.negate(); + assert!(z.is_zero()); + } + + // Doubling edge case with zero. + { + let mut z = G::zero(); + z.double(); + assert!(z.is_zero()); + } + + // Addition edge cases with zero + { + let mut r = G::rand(&mut rng); + let rcopy = r; + r.add_assign(&G::zero()); + assert_eq!(r, rcopy); + r.add_assign_mixed(&G::Affine::zero()); + assert_eq!(r, rcopy); + + let mut z = G::zero(); + z.add_assign(&G::zero()); + assert!(z.is_zero()); + z.add_assign_mixed(&G::Affine::zero()); + assert!(z.is_zero()); + + let mut z2 = z; + z2.add_assign(&r); + + z.add_assign_mixed(&r.into_affine()); + + assert_eq!(z, z2); + assert_eq!(z, r); + } + + // Transformations + { + let a = G::rand(&mut rng); + let b = a.into_affine().into_projective(); + let c = a + .into_affine() + .into_projective() + .into_affine() + .into_projective(); + assert_eq!(a, b); + assert_eq!(b, c); + } + + random_addition_tests::(); + random_multiplication_tests::(); + random_doubling_tests::(); + random_negation_tests::(); + random_wnaf_tests::(); + random_encoding_tests::(); +} + +fn random_wnaf_tests() { + use ff::PrimeField; + use crate::wnaf::*; + + let mut rng = XorShiftRng::from_seed([0x5dbe6259, 0x8d313d76, 0x3237db17, 0xe5bc0654]); + + { + let mut table = vec![]; + let mut wnaf = vec![]; + + for w in 2..14 { + for _ in 0..100 { + let g = G::rand(&mut rng); + let s = G::Scalar::rand(&mut rng).into_repr(); + let mut g1 = g; + g1.mul_assign(s); + + wnaf_table(&mut table, g, w); + wnaf_form(&mut wnaf, s, w); + let g2 = wnaf_exp(&table, &wnaf); + + assert_eq!(g1, g2); + } + } + } + + { + fn only_compiles_if_send(_: &S) {} + + for _ in 0..100 { + let g = G::rand(&mut rng); + let s = G::Scalar::rand(&mut rng).into_repr(); + let mut g1 = g; + g1.mul_assign(s); + + let g2 = { + let mut wnaf = Wnaf::new(); + wnaf.base(g, 1).scalar(s) + }; + let g3 = { + let mut wnaf = Wnaf::new(); + wnaf.scalar(s).base(g) + }; + let g4 = { + let mut wnaf = Wnaf::new(); + let mut shared = wnaf.base(g, 1).shared(); + + only_compiles_if_send(&shared); + + shared.scalar(s) + }; + let g5 = { + let mut wnaf = Wnaf::new(); + let mut shared = wnaf.scalar(s).shared(); + + only_compiles_if_send(&shared); + + shared.base(g) + }; + + let g6 = { + let mut wnaf = Wnaf::new(); + { + // Populate the vectors. + wnaf.base(rng.gen(), 1).scalar(rng.gen()); + } + wnaf.base(g, 1).scalar(s) + }; + let g7 = { + let mut wnaf = Wnaf::new(); + { + // Populate the vectors. + wnaf.base(rng.gen(), 1).scalar(rng.gen()); + } + wnaf.scalar(s).base(g) + }; + let g8 = { + let mut wnaf = Wnaf::new(); + { + // Populate the vectors. + wnaf.base(rng.gen(), 1).scalar(rng.gen()); + } + let mut shared = wnaf.base(g, 1).shared(); + + only_compiles_if_send(&shared); + + shared.scalar(s) + }; + let g9 = { + let mut wnaf = Wnaf::new(); + { + // Populate the vectors. + wnaf.base(rng.gen(), 1).scalar(rng.gen()); + } + let mut shared = wnaf.scalar(s).shared(); + + only_compiles_if_send(&shared); + + shared.base(g) + }; + + assert_eq!(g1, g2); + assert_eq!(g1, g3); + assert_eq!(g1, g4); + assert_eq!(g1, g5); + assert_eq!(g1, g6); + assert_eq!(g1, g7); + assert_eq!(g1, g8); + assert_eq!(g1, g9); + } + } +} + +fn random_negation_tests() { + let mut rng = XorShiftRng::from_seed([0x5dbe6259, 0x8d313d76, 0x3237db17, 0xe5bc0654]); + + for _ in 0..1000 { + let r = G::rand(&mut rng); + + let s = G::Scalar::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); + } +} + +fn random_doubling_tests() { + let mut rng = XorShiftRng::from_seed([0x5dbe6259, 0x8d313d76, 0x3237db17, 0xe5bc0654]); + + for _ in 0..1000 { + let mut a = G::rand(&mut rng); + let mut b = G::rand(&mut rng); + + // 2(a + b) + let mut tmp1 = a; + tmp1.add_assign(&b); + tmp1.double(); + + // 2a + 2b + a.double(); + b.double(); + + let mut tmp2 = a; + tmp2.add_assign(&b); + + let mut tmp3 = a; + tmp3.add_assign_mixed(&b.into_affine()); + + assert_eq!(tmp1, tmp2); + assert_eq!(tmp1, tmp3); + } +} + +fn random_multiplication_tests() { + let mut rng = XorShiftRng::from_seed([0x5dbe6259, 0x8d313d76, 0x3237db17, 0xe5bc0654]); + + for _ in 0..1000 { + let mut a = G::rand(&mut rng); + let mut b = G::rand(&mut rng); + let a_affine = a.into_affine(); + let b_affine = b.into_affine(); + + let s = G::Scalar::rand(&mut rng); + + // s ( a + b ) + let mut tmp1 = a; + tmp1.add_assign(&b); + tmp1.mul_assign(s); + + // sa + sb + a.mul_assign(s); + b.mul_assign(s); + + let mut tmp2 = a; + tmp2.add_assign(&b); + + // Affine multiplication + let mut tmp3 = a_affine.mul(s); + tmp3.add_assign(&b_affine.mul(s)); + + assert_eq!(tmp1, tmp2); + assert_eq!(tmp1, tmp3); + } +} + +fn random_addition_tests() { + let mut rng = XorShiftRng::from_seed([0x5dbe6259, 0x8d313d76, 0x3237db17, 0xe5bc0654]); + + for _ in 0..1000 { + let a = G::rand(&mut rng); + let b = G::rand(&mut rng); + let c = G::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!(aplusa, aplusamixed); + } + + let mut tmp = vec![G::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]); + } + } +} + +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 { + 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::>(); + + for i in &v { + + assert!(!i.is_normalized()); + } + + 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); + } +} + +fn random_encoding_tests() { + let mut rng = XorShiftRng::from_seed([0x5dbe6259, 0x8d313d76, 0x3237db17, 0xe5bc0654]); + + assert_eq!( + G::zero().into_uncompressed().into_affine().unwrap(), + G::zero() + ); + + assert_eq!( + G::zero().into_compressed().into_affine().unwrap(), + G::zero() + ); + + for _ in 0..1000 { + let mut r = G::Projective::rand(&mut rng).into_affine(); + + let uncompressed = r.into_uncompressed(); + let de_uncompressed = uncompressed.into_affine().unwrap(); + assert_eq!(de_uncompressed, r); + + let compressed = r.into_compressed(); + let de_compressed = compressed.into_affine().unwrap(); + assert_eq!(de_compressed, r); + + r.negate(); + + let compressed = r.into_compressed(); + let de_compressed = compressed.into_affine().unwrap(); + assert_eq!(de_compressed, r); + } +} diff --git a/pairing/src/tests/engine.rs b/pairing/src/tests/engine.rs new file mode 100644 index 0000000..5fc37c4 --- /dev/null +++ b/pairing/src/tests/engine.rs @@ -0,0 +1,126 @@ +use rand::{Rand, SeedableRng, XorShiftRng}; + +use crate::{CurveAffine, CurveProjective, Engine, Field, PrimeField}; + +pub fn engine_tests() { + let mut rng = XorShiftRng::from_seed([0x5dbe6259, 0x8d313d76, 0x3237db17, 0xe5bc0654]); + + for _ in 0..10 { + let a = E::G1::rand(&mut rng).into_affine(); + let b = E::G2::rand(&mut rng).into_affine(); + + assert!(a.pairing_with(&b) == b.pairing_with(&a)); + assert!(a.pairing_with(&b) == E::pairing(a, b)); + } + + for _ in 0..1000 { + let z1 = E::G1Affine::zero().prepare(); + let z2 = E::G2Affine::zero().prepare(); + + let a = E::G1::rand(&mut rng).into_affine().prepare(); + let b = E::G2::rand(&mut rng).into_affine().prepare(); + let c = E::G1::rand(&mut rng).into_affine().prepare(); + let d = E::G2::rand(&mut rng).into_affine().prepare(); + + assert_eq!( + E::Fqk::one(), + E::final_exponentiation(&E::miller_loop(&[(&z1, &b)])).unwrap() + ); + + assert_eq!( + E::Fqk::one(), + E::final_exponentiation(&E::miller_loop(&[(&a, &z2)])).unwrap() + ); + + assert_eq!( + E::final_exponentiation(&E::miller_loop(&[(&z1, &b), (&c, &d)])).unwrap(), + E::final_exponentiation(&E::miller_loop(&[(&a, &z2), (&c, &d)])).unwrap() + ); + + assert_eq!( + E::final_exponentiation(&E::miller_loop(&[(&a, &b), (&z1, &d)])).unwrap(), + E::final_exponentiation(&E::miller_loop(&[(&a, &b), (&c, &z2)])).unwrap() + ); + } + + random_bilinearity_tests::(); + random_miller_loop_tests::(); +} + +fn random_miller_loop_tests() { + let mut rng = XorShiftRng::from_seed([0x5dbe6259, 0x8d313d76, 0x3237db17, 0xe5bc0654]); + + // Exercise the miller loop for a reduced pairing + for _ in 0..1000 { + let a = E::G1::rand(&mut rng); + let b = E::G2::rand(&mut rng); + + let p2 = E::pairing(a, b); + + let a = a.into_affine().prepare(); + let b = b.into_affine().prepare(); + + let p1 = E::final_exponentiation(&E::miller_loop(&[(&a, &b)])).unwrap(); + + assert_eq!(p1, p2); + } + + // Exercise a double miller loop + for _ in 0..1000 { + let a = E::G1::rand(&mut rng); + let b = E::G2::rand(&mut rng); + let c = E::G1::rand(&mut rng); + let d = E::G2::rand(&mut rng); + + let ab = E::pairing(a, b); + let cd = E::pairing(c, d); + + let mut abcd = ab; + abcd.mul_assign(&cd); + + let a = a.into_affine().prepare(); + let b = b.into_affine().prepare(); + let c = c.into_affine().prepare(); + let d = d.into_affine().prepare(); + + let abcd_with_double_loop = + E::final_exponentiation(&E::miller_loop(&[(&a, &b), (&c, &d)])).unwrap(); + + assert_eq!(abcd, abcd_with_double_loop); + } +} + +fn random_bilinearity_tests() { + let mut rng = XorShiftRng::from_seed([0x5dbe6259, 0x8d313d76, 0x3237db17, 0xe5bc0654]); + + for _ in 0..1000 { + let a = E::G1::rand(&mut rng); + let b = E::G2::rand(&mut rng); + + let c = E::Fr::rand(&mut rng); + let d = E::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 = E::pairing(ac, bd); + let adbc = E::pairing(ad, bc); + + let mut cd = c; + cd.mul_assign(&d); + + let abcd = E::pairing(a, b).pow(cd.into_repr()); + + assert_eq!(acbd, adbc); + assert_eq!(acbd, abcd); + } +} diff --git a/pairing/src/tests/field.rs b/pairing/src/tests/field.rs new file mode 100644 index 0000000..55396a7 --- /dev/null +++ b/pairing/src/tests/field.rs @@ -0,0 +1,266 @@ +use ff::{Field, LegendreSymbol, PrimeField, SqrtField}; +use rand::{Rng, SeedableRng, XorShiftRng}; + +pub fn random_frobenius_tests>(characteristic: C, maxpower: usize) { + let mut rng = XorShiftRng::from_seed([0x5dbe6259, 0x8d313d76, 0x3237db17, 0xe5bc0654]); + + for _ in 0..100 { + for i in 0..(maxpower + 1) { + let mut a = F::rand(&mut rng); + let mut b = a; + + for _ in 0..i { + a = a.pow(&characteristic); + } + b.frobenius_map(i); + + assert_eq!(a, b); + } + } +} + +pub fn random_sqrt_tests() { + let mut rng = XorShiftRng::from_seed([0x5dbe6259, 0x8d313d76, 0x3237db17, 0xe5bc0654]); + + for _ in 0..10000 { + let a = F::rand(&mut rng); + let mut b = a; + b.square(); + assert_eq!(b.legendre(), LegendreSymbol::QuadraticResidue); + + let b = b.sqrt().unwrap(); + let mut negb = b; + negb.negate(); + + assert!(a == b || a == negb); + } + + let mut c = F::one(); + for _ in 0..10000 { + let mut b = c; + b.square(); + assert_eq!(b.legendre(), LegendreSymbol::QuadraticResidue); + + b = b.sqrt().unwrap(); + + if b != c { + b.negate(); + } + + assert_eq!(b, c); + + c.add_assign(&F::one()); + } +} + +pub fn random_field_tests() { + let mut rng = XorShiftRng::from_seed([0x5dbe6259, 0x8d313d76, 0x3237db17, 0xe5bc0654]); + + random_multiplication_tests::(&mut rng); + random_addition_tests::(&mut rng); + random_subtraction_tests::(&mut rng); + random_negation_tests::(&mut rng); + random_doubling_tests::(&mut rng); + random_squaring_tests::(&mut rng); + random_inversion_tests::(&mut rng); + random_expansion_tests::(&mut rng); + + assert!(F::zero().is_zero()); + { + let mut z = F::zero(); + z.negate(); + assert!(z.is_zero()); + } + + assert!(F::zero().inverse().is_none()); + + // Multiplication by zero + { + let mut a = F::rand(&mut rng); + a.mul_assign(&F::zero()); + assert!(a.is_zero()); + } + + // Addition by zero + { + let mut a = F::rand(&mut rng); + let copy = a; + a.add_assign(&F::zero()); + assert_eq!(a, copy); + } +} + +pub fn from_str_tests() { + { + let a = "84395729384759238745923745892374598234705297301958723458712394587103249587213984572934750213947582345792304758273458972349582734958273495872304598234"; + let b = "38495729084572938457298347502349857029384609283450692834058293405982304598230458230495820394850293845098234059823049582309485203948502938452093482039"; + let c = "3248875134290623212325429203829831876024364170316860259933542844758450336418538569901990710701240661702808867062612075657861768196242274635305077449545396068598317421057721935408562373834079015873933065667961469731886739181625866970316226171512545167081793907058686908697431878454091011239990119126"; + + let mut a = F::from_str(a).unwrap(); + let b = F::from_str(b).unwrap(); + let c = F::from_str(c).unwrap(); + + a.mul_assign(&b); + + assert_eq!(a, c); + } + + { + let mut rng = XorShiftRng::from_seed([0x5dbe6259, 0x8d313d76, 0x3237db17, 0xe5bc0654]); + + for _ in 0..1000 { + let n: u64 = rng.gen(); + + let a = F::from_str(&format!("{}", n)).unwrap(); + let b = F::from_repr(n.into()).unwrap(); + + assert_eq!(a, b); + } + } + + assert!(F::from_str("").is_none()); + assert!(F::from_str("0").unwrap().is_zero()); + assert!(F::from_str("00").is_none()); + assert!(F::from_str("00000000000").is_none()); +} + +fn random_multiplication_tests(rng: &mut R) { + for _ in 0..10000 { + let a = F::rand(rng); + let b = F::rand(rng); + let c = F::rand(rng); + + let mut t0 = a; // (a * b) * c + t0.mul_assign(&b); + t0.mul_assign(&c); + + let mut t1 = a; // (a * c) * b + t1.mul_assign(&c); + t1.mul_assign(&b); + + let mut t2 = b; // (b * c) * a + t2.mul_assign(&c); + t2.mul_assign(&a); + + assert_eq!(t0, t1); + assert_eq!(t1, t2); + } +} + +fn random_addition_tests(rng: &mut R) { + for _ in 0..10000 { + let a = F::rand(rng); + let b = F::rand(rng); + let c = F::rand(rng); + + let mut t0 = a; // (a + b) + c + t0.add_assign(&b); + t0.add_assign(&c); + + let mut t1 = a; // (a + c) + b + t1.add_assign(&c); + t1.add_assign(&b); + + let mut t2 = b; // (b + c) + a + t2.add_assign(&c); + t2.add_assign(&a); + + assert_eq!(t0, t1); + assert_eq!(t1, t2); + } +} + +fn random_subtraction_tests(rng: &mut R) { + for _ in 0..10000 { + let a = F::rand(rng); + let b = F::rand(rng); + + let mut t0 = a; // (a - b) + t0.sub_assign(&b); + + let mut t1 = b; // (b - a) + t1.sub_assign(&a); + + let mut t2 = t0; // (a - b) + (b - a) = 0 + t2.add_assign(&t1); + + assert!(t2.is_zero()); + } +} + +fn random_negation_tests(rng: &mut R) { + for _ in 0..10000 { + let a = F::rand(rng); + let mut b = a; + b.negate(); + b.add_assign(&a); + + assert!(b.is_zero()); + } +} + +fn random_doubling_tests(rng: &mut R) { + for _ in 0..10000 { + let mut a = F::rand(rng); + let mut b = a; + a.add_assign(&b); + b.double(); + + assert_eq!(a, b); + } +} + +fn random_squaring_tests(rng: &mut R) { + for _ in 0..10000 { + let mut a = F::rand(rng); + let mut b = a; + a.mul_assign(&b); + b.square(); + + assert_eq!(a, b); + } +} + +fn random_inversion_tests(rng: &mut R) { + assert!(F::zero().inverse().is_none()); + + for _ in 0..10000 { + let mut a = F::rand(rng); + let b = a.inverse().unwrap(); // probablistically nonzero + a.mul_assign(&b); + + assert_eq!(a, F::one()); + } +} + +fn random_expansion_tests(rng: &mut R) { + for _ in 0..10000 { + // Compare (a + b)(c + d) and (a*c + b*c + a*d + b*d) + + let a = F::rand(rng); + let b = F::rand(rng); + let c = F::rand(rng); + let d = F::rand(rng); + + let mut t0 = a; + t0.add_assign(&b); + let mut t1 = c; + t1.add_assign(&d); + t0.mul_assign(&t1); + + let mut t2 = a; + t2.mul_assign(&c); + let mut t3 = b; + t3.mul_assign(&c); + let mut t4 = a; + t4.mul_assign(&d); + let mut t5 = b; + t5.mul_assign(&d); + + t2.add_assign(&t3); + t2.add_assign(&t4); + t2.add_assign(&t5); + + assert_eq!(t0, t2); + } +} diff --git a/pairing/src/tests/mod.rs b/pairing/src/tests/mod.rs new file mode 100644 index 0000000..bc83958 --- /dev/null +++ b/pairing/src/tests/mod.rs @@ -0,0 +1,4 @@ +pub mod curve; +pub mod engine; +pub mod field; +pub mod repr; diff --git a/pairing/src/tests/repr.rs b/pairing/src/tests/repr.rs new file mode 100644 index 0000000..09dd441 --- /dev/null +++ b/pairing/src/tests/repr.rs @@ -0,0 +1,98 @@ +use ff::PrimeFieldRepr; +use rand::{SeedableRng, XorShiftRng}; + +pub fn random_repr_tests() { + random_encoding_tests::(); + random_shl_tests::(); + random_shr_tests::(); +} + +fn random_encoding_tests() { + let mut rng = XorShiftRng::from_seed([0x5dbe6259, 0x8d313d76, 0x3237db17, 0xe5bc0654]); + + for _ in 0..1000 { + let r = R::rand(&mut rng); + + // Big endian + { + let mut rdecoded = R::default(); + + let mut v: Vec = vec![]; + r.write_be(&mut v).unwrap(); + rdecoded.read_be(&v[0..]).unwrap(); + + assert_eq!(r, rdecoded); + } + + // Little endian + { + let mut rdecoded = R::default(); + + let mut v: Vec = vec![]; + r.write_le(&mut v).unwrap(); + rdecoded.read_le(&v[0..]).unwrap(); + + assert_eq!(r, rdecoded); + } + + { + let mut rdecoded_le = R::default(); + let mut rdecoded_be_flip = R::default(); + + let mut v: Vec = vec![]; + r.write_le(&mut v).unwrap(); + + // This reads in little-endian, so we are done. + rdecoded_le.read_le(&v[..]).unwrap(); + + // This reads in big-endian, so we perform a swap of the + // bytes beforehand. + let v: Vec = v.into_iter().rev().collect(); + rdecoded_be_flip.read_be(&v[..]).unwrap(); + + assert_eq!(rdecoded_le, rdecoded_be_flip); + } + } +} + +fn random_shl_tests() { + let mut rng = XorShiftRng::from_seed([0x5dbe6259, 0x8d313d76, 0x3237db17, 0xe5bc0654]); + + for _ in 0..100 { + let r = R::rand(&mut rng); + + for shift in 0..(r.num_bits() + 1) { + let mut r1 = r; + let mut r2 = r; + + for _ in 0..shift { + r1.mul2(); + } + + r2.shl(shift); + + assert_eq!(r1, r2); + } + } +} + +fn random_shr_tests() { + let mut rng = XorShiftRng::from_seed([0x5dbe6259, 0x8d313d76, 0x3237db17, 0xe5bc0654]); + + for _ in 0..100 { + let r = R::rand(&mut rng); + + for shift in 0..(r.num_bits() + 1) { + let mut r1 = r; + let mut r2 = r; + + for _ in 0..shift { + r1.div2(); + } + + r2.shr(shift); + + assert_eq!(r1, r2); + } + } +} diff --git a/pairing/src/wnaf.rs b/pairing/src/wnaf.rs new file mode 100644 index 0000000..69c6fd9 --- /dev/null +++ b/pairing/src/wnaf.rs @@ -0,0 +1,179 @@ +use super::{CurveProjective, PrimeField, PrimeFieldRepr}; + +/// Replaces the contents of `table` with a w-NAF window table for the given window size. +pub(crate) fn wnaf_table(table: &mut Vec, mut base: G, window: usize) { + table.truncate(0); + table.reserve(1 << (window - 1)); + + let mut dbl = base; + dbl.double(); + + for _ in 0..(1 << (window - 1)) { + table.push(base); + base.add_assign(&dbl); + } +} + +/// Replaces the contents of `wnaf` with the w-NAF representation of a scalar. +pub(crate) fn wnaf_form(wnaf: &mut Vec, mut c: S, window: usize) { + wnaf.truncate(0); + + while !c.is_zero() { + let mut u; + if c.is_odd() { + u = (c.as_ref()[0] % (1 << (window + 1))) as i64; + + if u > (1 << window) { + u -= 1 << (window + 1); + } + + if u > 0 { + c.sub_noborrow(&S::from(u as u64)); + } else { + c.add_nocarry(&S::from((-u) as u64)); + } + } else { + u = 0; + } + + wnaf.push(u); + + c.div2(); + } +} + +/// Performs w-NAF exponentiation with the provided window table and w-NAF form scalar. +/// +/// This function must be provided a `table` and `wnaf` that were constructed with +/// the same window size; otherwise, it may panic or produce invalid results. +pub(crate) fn wnaf_exp(table: &[G], wnaf: &[i64]) -> G { + let mut result = G::zero(); + + let mut found_one = false; + + for n in wnaf.iter().rev() { + if found_one { + result.double(); + } + + if *n != 0 { + found_one = true; + + if *n > 0 { + result.add_assign(&table[(n / 2) as usize]); + } else { + result.sub_assign(&table[((-n) / 2) as usize]); + } + } + } + + result +} + +/// A "w-ary non-adjacent form" exponentiation context. +#[derive(Debug)] +pub struct Wnaf { + base: B, + scalar: S, + window_size: W, +} + +impl Wnaf<(), Vec, Vec> { + /// Construct a new wNAF context without allocating. + pub fn new() -> Self { + Wnaf { + base: vec![], + scalar: vec![], + window_size: (), + } + } + + /// Given a base and a number of scalars, compute a window table and return a `Wnaf` object that + /// can perform exponentiations with `.scalar(..)`. + pub fn base(&mut self, base: G, num_scalars: usize) -> Wnaf> { + // Compute the appropriate window size based on the number of scalars. + let window_size = G::recommended_wnaf_for_num_scalars(num_scalars); + + // Compute a wNAF table for the provided base and window size. + wnaf_table(&mut self.base, base, window_size); + + // Return a Wnaf object that immutably borrows the computed base storage location, + // but mutably borrows the scalar storage location. + Wnaf { + base: &self.base[..], + scalar: &mut self.scalar, + window_size, + } + } + + /// Given a scalar, compute its wNAF representation and return a `Wnaf` object that can perform + /// exponentiations with `.base(..)`. + pub fn scalar( + &mut self, + scalar: <::Scalar as PrimeField>::Repr, + ) -> Wnaf, &[i64]> { + // Compute the appropriate window size for the scalar. + let window_size = G::recommended_wnaf_for_scalar(scalar); + + // Compute the wNAF form of the scalar. + wnaf_form(&mut self.scalar, scalar, window_size); + + // Return a Wnaf object that mutably borrows the base storage location, but + // immutably borrows the computed wNAF form scalar location. + Wnaf { + base: &mut self.base, + scalar: &self.scalar[..], + window_size, + } + } +} + +impl<'a, G: CurveProjective> Wnaf> { + /// Constructs new space for the scalar representation while borrowing + /// the computed window table, for sending the window table across threads. + pub fn shared(&self) -> Wnaf> { + Wnaf { + base: self.base, + scalar: vec![], + window_size: self.window_size, + } + } +} + +impl<'a, G: CurveProjective> Wnaf, &'a [i64]> { + /// Constructs new space for the window table while borrowing + /// the computed scalar representation, for sending the scalar representation + /// across threads. + pub fn shared(&self) -> Wnaf, &'a [i64]> { + Wnaf { + base: vec![], + scalar: self.scalar, + window_size: self.window_size, + } + } +} + +impl> Wnaf { + /// Performs exponentiation given a base. + pub fn base(&mut self, base: G) -> G + where + B: AsMut>, + { + wnaf_table(self.base.as_mut(), base, self.window_size); + wnaf_exp(self.base.as_mut(), self.scalar.as_ref()) + } +} + +impl>> Wnaf { + /// Performs exponentiation given a scalar. + pub fn scalar( + &mut self, + scalar: <::Scalar as PrimeField>::Repr, + ) -> G + where + B: AsRef<[G]>, + { + wnaf_form(self.scalar.as_mut(), scalar, self.window_size); + wnaf_exp(self.base.as_ref(), self.scalar.as_mut()) + } +}