diff --git a/pairing/.gitignore b/pairing/.gitignore new file mode 100644 index 0000000..4308d82 --- /dev/null +++ b/pairing/.gitignore @@ -0,0 +1,3 @@ +target/ +**/*.rs.bk +Cargo.lock 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..98725aa --- /dev/null +++ b/pairing/Cargo.toml @@ -0,0 +1,23 @@ +[package] +name = "pairing" + +# Remember to change version string in README.md. +version = "0.14.2" +authors = ["Sean Bowe "] +license = "MIT/Apache-2.0" + +description = "Pairing-friendly elliptic curve library" +documentation = "https://docs.rs/pairing/" +homepage = "https://github.com/ebfull/pairing" +repository = "https://github.com/ebfull/pairing" + +[dependencies] +rand = "0.4" +byteorder = "1" +clippy = { version = "0.0.200", optional = true } + +[features] +unstable-features = ["expose-arith"] +expose-arith = [] +u128-support = [] +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..d71d0c5 --- /dev/null +++ b/pairing/README.md @@ -0,0 +1,35 @@ +# pairing [![Crates.io](https://img.shields.io/crates/v/pairing.svg)](https://crates.io/crates/pairing) # + +This is a Rust crate for using pairing-friendly elliptic curves. Currently, only the [BLS12-381](https://z.cash/blog/new-snark-curve.html) construction is implemented. + +## [Documentation](https://docs.rs/pairing/) + +Bring the `pairing` crate into your project just as you normally would. + +If you're using a supported platform and the nightly Rust compiler, you can enable the `u128-support` feature for faster arithmetic. + +```toml +[dependencies.pairing] +version = "0.14" +features = ["u128-support"] +``` + +## 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..af4dba4 --- /dev/null +++ b/pairing/benches/bls12_381/fq.rs @@ -0,0 +1,268 @@ +use rand::{Rand, SeedableRng, XorShiftRng}; + +use pairing::bls12_381::*; +use pairing::{Field, PrimeField, PrimeFieldRepr, SqrtField}; + +#[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..226b850 --- /dev/null +++ b/pairing/benches/bls12_381/fq12.rs @@ -0,0 +1,94 @@ +use rand::{Rand, SeedableRng, XorShiftRng}; + +use pairing::bls12_381::*; +use pairing::Field; + +#[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..ec26e98 --- /dev/null +++ b/pairing/benches/bls12_381/fq2.rs @@ -0,0 +1,110 @@ +use rand::{Rand, SeedableRng, XorShiftRng}; + +use pairing::bls12_381::*; +use pairing::{Field, SqrtField}; + +#[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..7278629 --- /dev/null +++ b/pairing/benches/bls12_381/fr.rs @@ -0,0 +1,268 @@ +use rand::{Rand, SeedableRng, XorShiftRng}; + +use pairing::bls12_381::*; +use pairing::{Field, PrimeField, PrimeFieldRepr, SqrtField}; + +#[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/pairing_benches.rs b/pairing/benches/pairing_benches.rs new file mode 100644 index 0000000..424c4e7 --- /dev/null +++ b/pairing/benches/pairing_benches.rs @@ -0,0 +1,7 @@ +#![feature(test)] + +extern crate pairing; +extern crate rand; +extern crate test; + +mod bls12_381; 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..5cd5091 --- /dev/null +++ b/pairing/src/bls12_381/ec.rs @@ -0,0 +1,2026 @@ +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 rand::{Rand, Rng}; + use std::fmt; + use { + BitIterator, CurveAffine, CurveProjective, EncodedPoint, Engine, Field, GroupDecodingError, + PrimeField, PrimeFieldRepr, SqrtField, + }; + + 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 + } + } + + #[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() { + ::tests::curve::curve_tests::(); + } +} + +pub mod g2 { + use super::super::{Bls12, Fq, Fq12, Fq2, FqRepr, Fr, FrRepr}; + use super::g1::G1Affine; + use rand::{Rand, Rng}; + use std::fmt; + use { + BitIterator, CurveAffine, CurveProjective, EncodedPoint, Engine, Field, GroupDecodingError, + PrimeField, PrimeFieldRepr, SqrtField, + }; + + 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() { + ::tests::curve::curve_tests::(); + } +} + +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..738da38 --- /dev/null +++ b/pairing/src/bls12_381/fq.rs @@ -0,0 +1,2959 @@ +use super::fq2::Fq2; +use std::cmp::Ordering; +use {Field, PrimeField, PrimeFieldDecodingError, PrimeFieldRepr, SqrtField}; + +// q = 4002409555221667393417789825735904156556882819939007885332058136124031650490837864442687629129015664037894272559787 +const MODULUS: FqRepr = FqRepr([ + 0xb9feffffffffaaab, + 0x1eabfffeb153ffff, + 0x6730d2a0f6b0f624, + 0x64774b84f38512bf, + 0x4b1ba7b6434bacd7, + 0x1a0111ea397fe69a, +]); + +// The number of bits needed to represent the modulus. +const MODULUS_BITS: u32 = 381; + +// The number of bits that must be shaved from the beginning of +// the representation when randomly sampling. +const REPR_SHAVE_BITS: u32 = 3; + +// R = 2**384 % q +const R: FqRepr = FqRepr([ + 0x760900000002fffd, + 0xebf4000bc40c0002, + 0x5f48985753c758ba, + 0x77ce585370525745, + 0x5c071a97a256ec6d, + 0x15f65ec3fa80e493, +]); + +// R2 = R^2 % q +const R2: FqRepr = FqRepr([ + 0xf4df1f341c341746, + 0xa76e6a609d104f1, + 0x8de5476c4c95b6d5, + 0x67eb88a9939d83c0, + 0x9a793e85b519952d, + 0x11988fe592cae3aa, +]); + +// INV = -(q^{-1} mod 2^64) mod 2^64 +const INV: u64 = 0x89f3fffcfffcfffd; + +// GENERATOR = 2 (multiplicative generator of q-1 order, that is also quadratic nonresidue) +const GENERATOR: FqRepr = FqRepr([ + 0x321300000006554f, + 0xb93c0018d6c40005, + 0x57605e0db0ddbb51, + 0x8b256521ed1f9bcb, + 0x6cf28d7901622c03, + 0x11ebab9dbb81e28c, +]); + +// 2^s * t = MODULUS - 1 with t odd +const S: u32 = 1; + +// 2^s root of unity computed by GENERATOR^t +const ROOT_OF_UNITY: FqRepr = FqRepr([ + 0x43f5fffffffcaaae, + 0x32b7fff2ed47fffd, + 0x7e83a49a2e99d69, + 0xeca8f3318332bb7a, + 0xef148d1ea0f4c069, + 0x40ab3263eff0206, +]); + +// 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(Copy, Clone, PartialEq, Eq, Default, Debug)] +pub struct FqRepr(pub [u64; 6]); + +impl ::rand::Rand for FqRepr { + #[inline(always)] + fn rand(rng: &mut R) -> Self { + FqRepr(rng.gen()) + } +} + +impl ::std::fmt::Display for FqRepr { + fn fmt(&self, f: &mut ::std::fmt::Formatter) -> ::std::fmt::Result { + try!(write!(f, "0x")); + for i in self.0.iter().rev() { + try!(write!(f, "{:016x}", *i)); + } + + Ok(()) + } +} + +impl AsRef<[u64]> for FqRepr { + #[inline(always)] + fn as_ref(&self) -> &[u64] { + &self.0 + } +} + +impl AsMut<[u64]> for FqRepr { + #[inline(always)] + fn as_mut(&mut self) -> &mut [u64] { + &mut self.0 + } +} + +impl From for FqRepr { + #[inline(always)] + fn from(val: u64) -> FqRepr { + let mut repr = Self::default(); + repr.0[0] = val; + repr + } +} + +impl Ord for FqRepr { + #[inline(always)] + fn cmp(&self, other: &FqRepr) -> Ordering { + for (a, b) in self.0.iter().rev().zip(other.0.iter().rev()) { + if a < b { + return Ordering::Less; + } else if a > b { + return Ordering::Greater; + } + } + + Ordering::Equal + } +} + +impl PartialOrd for FqRepr { + #[inline(always)] + fn partial_cmp(&self, other: &FqRepr) -> Option { + Some(self.cmp(other)) + } +} + +impl PrimeFieldRepr for FqRepr { + #[inline(always)] + fn is_odd(&self) -> bool { + self.0[0] & 1 == 1 + } + + #[inline(always)] + fn is_even(&self) -> bool { + !self.is_odd() + } + + #[inline(always)] + fn is_zero(&self) -> bool { + self.0.iter().all(|&e| e == 0) + } + + #[inline(always)] + fn shr(&mut self, mut n: u32) { + if n >= 64 * 6 { + *self = Self::from(0); + return; + } + + while n >= 64 { + let mut t = 0; + for i in self.0.iter_mut().rev() { + ::std::mem::swap(&mut t, i); + } + n -= 64; + } + + if n > 0 { + let mut t = 0; + for i in self.0.iter_mut().rev() { + let t2 = *i << (64 - n); + *i >>= n; + *i |= t; + t = t2; + } + } + } + + #[inline(always)] + fn div2(&mut self) { + let mut t = 0; + for i in self.0.iter_mut().rev() { + let t2 = *i << 63; + *i >>= 1; + *i |= t; + t = t2; + } + } + + #[inline(always)] + fn mul2(&mut self) { + let mut last = 0; + for i in &mut self.0 { + let tmp = *i >> 63; + *i <<= 1; + *i |= last; + last = tmp; + } + } + + #[inline(always)] + fn shl(&mut self, mut n: u32) { + if n >= 64 * 6 { + *self = Self::from(0); + return; + } + + while n >= 64 { + let mut t = 0; + for i in &mut self.0 { + ::std::mem::swap(&mut t, i); + } + n -= 64; + } + + if n > 0 { + let mut t = 0; + for i in &mut self.0 { + let t2 = *i >> (64 - n); + *i <<= n; + *i |= t; + t = t2; + } + } + } + + #[inline(always)] + fn num_bits(&self) -> u32 { + let mut ret = (6 as u32) * 64; + for i in self.0.iter().rev() { + let leading = i.leading_zeros(); + ret -= leading; + if leading != 64 { + break; + } + } + + ret + } + + #[inline(always)] + fn add_nocarry(&mut self, other: &FqRepr) { + let mut carry = 0; + + for (a, b) in self.0.iter_mut().zip(other.0.iter()) { + *a = ::adc(*a, *b, &mut carry); + } + } + + #[inline(always)] + fn sub_noborrow(&mut self, other: &FqRepr) { + let mut borrow = 0; + + for (a, b) in self.0.iter_mut().zip(other.0.iter()) { + *a = ::sbb(*a, *b, &mut borrow); + } + } +} + +#[derive(Copy, Clone, PartialEq, Eq, Debug)] +pub struct Fq(FqRepr); + +/// `Fq` elements are ordered lexicographically. +impl Ord for Fq { + #[inline(always)] + fn cmp(&self, other: &Fq) -> Ordering { + self.into_repr().cmp(&other.into_repr()) + } +} + +impl PartialOrd for Fq { + #[inline(always)] + fn partial_cmp(&self, other: &Fq) -> Option { + Some(self.cmp(other)) + } +} + +impl ::std::fmt::Display for Fq { + fn fmt(&self, f: &mut ::std::fmt::Formatter) -> ::std::fmt::Result { + write!(f, "Fq({})", self.into_repr()) + } +} + +impl ::rand::Rand for Fq { + fn rand(rng: &mut R) -> Self { + loop { + let mut tmp = Fq(FqRepr::rand(rng)); + + // Mask away the unused bits at the beginning. + tmp.0.as_mut()[5] &= 0xffffffffffffffff >> REPR_SHAVE_BITS; + + if tmp.is_valid() { + return tmp; + } + } + } +} + +impl From for FqRepr { + fn from(e: Fq) -> FqRepr { + e.into_repr() + } +} + +impl PrimeField for Fq { + type Repr = FqRepr; + + fn from_repr(r: FqRepr) -> Result { + let mut r = Fq(r); + if r.is_valid() { + r.mul_assign(&Fq(R2)); + + Ok(r) + } else { + Err(PrimeFieldDecodingError::NotInField(format!("{}", r.0))) + } + } + + fn into_repr(&self) -> FqRepr { + let mut r = *self; + r.mont_reduce( + (self.0).0[0], + (self.0).0[1], + (self.0).0[2], + (self.0).0[3], + (self.0).0[4], + (self.0).0[5], + 0, + 0, + 0, + 0, + 0, + 0, + ); + r.0 + } + + fn char() -> FqRepr { + MODULUS + } + + const NUM_BITS: u32 = MODULUS_BITS; + + const CAPACITY: u32 = Self::NUM_BITS - 1; + + fn multiplicative_generator() -> Self { + Fq(GENERATOR) + } + + const S: u32 = S; + + fn root_of_unity() -> Self { + Fq(ROOT_OF_UNITY) + } +} + +impl Field for Fq { + #[inline] + fn zero() -> Self { + Fq(FqRepr::from(0)) + } + + #[inline] + fn one() -> Self { + Fq(R) + } + + #[inline] + fn is_zero(&self) -> bool { + self.0.is_zero() + } + + #[inline] + fn add_assign(&mut self, other: &Fq) { + // This cannot exceed the backing capacity. + self.0.add_nocarry(&other.0); + + // However, it may need to be reduced. + self.reduce(); + } + + #[inline] + fn double(&mut self) { + // This cannot exceed the backing capacity. + self.0.mul2(); + + // However, it may need to be reduced. + self.reduce(); + } + + #[inline] + fn sub_assign(&mut self, other: &Fq) { + // If `other` is larger than `self`, we'll need to add the modulus to self first. + if other.0 > self.0 { + self.0.add_nocarry(&MODULUS); + } + + self.0.sub_noborrow(&other.0); + } + + #[inline] + fn negate(&mut self) { + if !self.is_zero() { + let mut tmp = MODULUS; + tmp.sub_noborrow(&self.0); + self.0 = tmp; + } + } + + fn inverse(&self) -> Option { + if self.is_zero() { + None + } else { + // Guajardo Kumar Paar Pelzl + // Efficient Software-Implementation of Finite Fields with Applications to Cryptography + // Algorithm 16 (BEA for Inversion in Fp) + + let one = FqRepr::from(1); + + let mut u = self.0; + let mut v = MODULUS; + let mut b = Fq(R2); // Avoids unnecessary reduction step. + let mut c = Self::zero(); + + while u != one && v != one { + while u.is_even() { + u.div2(); + + if b.0.is_even() { + b.0.div2(); + } else { + b.0.add_nocarry(&MODULUS); + b.0.div2(); + } + } + + while v.is_even() { + v.div2(); + + if c.0.is_even() { + c.0.div2(); + } else { + c.0.add_nocarry(&MODULUS); + c.0.div2(); + } + } + + if v < u { + u.sub_noborrow(&v); + b.sub_assign(&c); + } else { + v.sub_noborrow(&u); + c.sub_assign(&b); + } + } + + if u == one { + Some(b) + } else { + Some(c) + } + } + } + + #[inline(always)] + fn frobenius_map(&mut self, _: usize) { + // This has no effect in a prime field. + } + + #[inline] + fn mul_assign(&mut self, other: &Fq) { + let mut carry = 0; + let r0 = ::mac_with_carry(0, (self.0).0[0], (other.0).0[0], &mut carry); + let r1 = ::mac_with_carry(0, (self.0).0[0], (other.0).0[1], &mut carry); + let r2 = ::mac_with_carry(0, (self.0).0[0], (other.0).0[2], &mut carry); + let r3 = ::mac_with_carry(0, (self.0).0[0], (other.0).0[3], &mut carry); + let r4 = ::mac_with_carry(0, (self.0).0[0], (other.0).0[4], &mut carry); + let r5 = ::mac_with_carry(0, (self.0).0[0], (other.0).0[5], &mut carry); + let r6 = carry; + let mut carry = 0; + let r1 = ::mac_with_carry(r1, (self.0).0[1], (other.0).0[0], &mut carry); + let r2 = ::mac_with_carry(r2, (self.0).0[1], (other.0).0[1], &mut carry); + let r3 = ::mac_with_carry(r3, (self.0).0[1], (other.0).0[2], &mut carry); + let r4 = ::mac_with_carry(r4, (self.0).0[1], (other.0).0[3], &mut carry); + let r5 = ::mac_with_carry(r5, (self.0).0[1], (other.0).0[4], &mut carry); + let r6 = ::mac_with_carry(r6, (self.0).0[1], (other.0).0[5], &mut carry); + let r7 = carry; + let mut carry = 0; + let r2 = ::mac_with_carry(r2, (self.0).0[2], (other.0).0[0], &mut carry); + let r3 = ::mac_with_carry(r3, (self.0).0[2], (other.0).0[1], &mut carry); + let r4 = ::mac_with_carry(r4, (self.0).0[2], (other.0).0[2], &mut carry); + let r5 = ::mac_with_carry(r5, (self.0).0[2], (other.0).0[3], &mut carry); + let r6 = ::mac_with_carry(r6, (self.0).0[2], (other.0).0[4], &mut carry); + let r7 = ::mac_with_carry(r7, (self.0).0[2], (other.0).0[5], &mut carry); + let r8 = carry; + let mut carry = 0; + let r3 = ::mac_with_carry(r3, (self.0).0[3], (other.0).0[0], &mut carry); + let r4 = ::mac_with_carry(r4, (self.0).0[3], (other.0).0[1], &mut carry); + let r5 = ::mac_with_carry(r5, (self.0).0[3], (other.0).0[2], &mut carry); + let r6 = ::mac_with_carry(r6, (self.0).0[3], (other.0).0[3], &mut carry); + let r7 = ::mac_with_carry(r7, (self.0).0[3], (other.0).0[4], &mut carry); + let r8 = ::mac_with_carry(r8, (self.0).0[3], (other.0).0[5], &mut carry); + let r9 = carry; + let mut carry = 0; + let r4 = ::mac_with_carry(r4, (self.0).0[4], (other.0).0[0], &mut carry); + let r5 = ::mac_with_carry(r5, (self.0).0[4], (other.0).0[1], &mut carry); + let r6 = ::mac_with_carry(r6, (self.0).0[4], (other.0).0[2], &mut carry); + let r7 = ::mac_with_carry(r7, (self.0).0[4], (other.0).0[3], &mut carry); + let r8 = ::mac_with_carry(r8, (self.0).0[4], (other.0).0[4], &mut carry); + let r9 = ::mac_with_carry(r9, (self.0).0[4], (other.0).0[5], &mut carry); + let r10 = carry; + let mut carry = 0; + let r5 = ::mac_with_carry(r5, (self.0).0[5], (other.0).0[0], &mut carry); + let r6 = ::mac_with_carry(r6, (self.0).0[5], (other.0).0[1], &mut carry); + let r7 = ::mac_with_carry(r7, (self.0).0[5], (other.0).0[2], &mut carry); + let r8 = ::mac_with_carry(r8, (self.0).0[5], (other.0).0[3], &mut carry); + let r9 = ::mac_with_carry(r9, (self.0).0[5], (other.0).0[4], &mut carry); + let r10 = ::mac_with_carry(r10, (self.0).0[5], (other.0).0[5], &mut carry); + let r11 = carry; + self.mont_reduce(r0, r1, r2, r3, r4, r5, r6, r7, r8, r9, r10, r11); + } + + #[inline] + fn square(&mut self) { + let mut carry = 0; + let r1 = ::mac_with_carry(0, (self.0).0[0], (self.0).0[1], &mut carry); + let r2 = ::mac_with_carry(0, (self.0).0[0], (self.0).0[2], &mut carry); + let r3 = ::mac_with_carry(0, (self.0).0[0], (self.0).0[3], &mut carry); + let r4 = ::mac_with_carry(0, (self.0).0[0], (self.0).0[4], &mut carry); + let r5 = ::mac_with_carry(0, (self.0).0[0], (self.0).0[5], &mut carry); + let r6 = carry; + let mut carry = 0; + let r3 = ::mac_with_carry(r3, (self.0).0[1], (self.0).0[2], &mut carry); + let r4 = ::mac_with_carry(r4, (self.0).0[1], (self.0).0[3], &mut carry); + let r5 = ::mac_with_carry(r5, (self.0).0[1], (self.0).0[4], &mut carry); + let r6 = ::mac_with_carry(r6, (self.0).0[1], (self.0).0[5], &mut carry); + let r7 = carry; + let mut carry = 0; + let r5 = ::mac_with_carry(r5, (self.0).0[2], (self.0).0[3], &mut carry); + let r6 = ::mac_with_carry(r6, (self.0).0[2], (self.0).0[4], &mut carry); + let r7 = ::mac_with_carry(r7, (self.0).0[2], (self.0).0[5], &mut carry); + let r8 = carry; + let mut carry = 0; + let r7 = ::mac_with_carry(r7, (self.0).0[3], (self.0).0[4], &mut carry); + let r8 = ::mac_with_carry(r8, (self.0).0[3], (self.0).0[5], &mut carry); + let r9 = carry; + let mut carry = 0; + let r9 = ::mac_with_carry(r9, (self.0).0[4], (self.0).0[5], &mut carry); + let r10 = carry; + + let r11 = r10 >> 63; + let r10 = (r10 << 1) | (r9 >> 63); + let r9 = (r9 << 1) | (r8 >> 63); + let r8 = (r8 << 1) | (r7 >> 63); + let r7 = (r7 << 1) | (r6 >> 63); + let r6 = (r6 << 1) | (r5 >> 63); + let r5 = (r5 << 1) | (r4 >> 63); + let r4 = (r4 << 1) | (r3 >> 63); + let r3 = (r3 << 1) | (r2 >> 63); + let r2 = (r2 << 1) | (r1 >> 63); + let r1 = r1 << 1; + + let mut carry = 0; + let r0 = ::mac_with_carry(0, (self.0).0[0], (self.0).0[0], &mut carry); + let r1 = ::adc(r1, 0, &mut carry); + let r2 = ::mac_with_carry(r2, (self.0).0[1], (self.0).0[1], &mut carry); + let r3 = ::adc(r3, 0, &mut carry); + let r4 = ::mac_with_carry(r4, (self.0).0[2], (self.0).0[2], &mut carry); + let r5 = ::adc(r5, 0, &mut carry); + let r6 = ::mac_with_carry(r6, (self.0).0[3], (self.0).0[3], &mut carry); + let r7 = ::adc(r7, 0, &mut carry); + let r8 = ::mac_with_carry(r8, (self.0).0[4], (self.0).0[4], &mut carry); + let r9 = ::adc(r9, 0, &mut carry); + let r10 = ::mac_with_carry(r10, (self.0).0[5], (self.0).0[5], &mut carry); + let r11 = ::adc(r11, 0, &mut carry); + self.mont_reduce(r0, r1, r2, r3, r4, r5, r6, r7, r8, r9, r10, r11); + } +} + +impl Fq { + /// Determines if the element is really in the field. This is only used + /// internally. + #[inline(always)] + fn is_valid(&self) -> bool { + self.0 < MODULUS + } + + /// Subtracts the modulus from this element if this element is not in the + /// field. Only used internally. + #[inline(always)] + fn reduce(&mut self) { + if !self.is_valid() { + self.0.sub_noborrow(&MODULUS); + } + } + + #[inline(always)] + fn mont_reduce( + &mut self, + r0: u64, + mut r1: u64, + mut r2: u64, + mut r3: u64, + mut r4: u64, + mut r5: u64, + mut r6: u64, + mut r7: u64, + mut r8: u64, + mut r9: u64, + mut r10: u64, + mut r11: u64, + ) { + // The Montgomery reduction here is based on Algorithm 14.32 in + // Handbook of Applied Cryptography + // . + + let k = r0.wrapping_mul(INV); + let mut carry = 0; + ::mac_with_carry(r0, k, MODULUS.0[0], &mut carry); + r1 = ::mac_with_carry(r1, k, MODULUS.0[1], &mut carry); + r2 = ::mac_with_carry(r2, k, MODULUS.0[2], &mut carry); + r3 = ::mac_with_carry(r3, k, MODULUS.0[3], &mut carry); + r4 = ::mac_with_carry(r4, k, MODULUS.0[4], &mut carry); + r5 = ::mac_with_carry(r5, k, MODULUS.0[5], &mut carry); + r6 = ::adc(r6, 0, &mut carry); + let carry2 = carry; + let k = r1.wrapping_mul(INV); + let mut carry = 0; + ::mac_with_carry(r1, k, MODULUS.0[0], &mut carry); + r2 = ::mac_with_carry(r2, k, MODULUS.0[1], &mut carry); + r3 = ::mac_with_carry(r3, k, MODULUS.0[2], &mut carry); + r4 = ::mac_with_carry(r4, k, MODULUS.0[3], &mut carry); + r5 = ::mac_with_carry(r5, k, MODULUS.0[4], &mut carry); + r6 = ::mac_with_carry(r6, k, MODULUS.0[5], &mut carry); + r7 = ::adc(r7, carry2, &mut carry); + let carry2 = carry; + let k = r2.wrapping_mul(INV); + let mut carry = 0; + ::mac_with_carry(r2, k, MODULUS.0[0], &mut carry); + r3 = ::mac_with_carry(r3, k, MODULUS.0[1], &mut carry); + r4 = ::mac_with_carry(r4, k, MODULUS.0[2], &mut carry); + r5 = ::mac_with_carry(r5, k, MODULUS.0[3], &mut carry); + r6 = ::mac_with_carry(r6, k, MODULUS.0[4], &mut carry); + r7 = ::mac_with_carry(r7, k, MODULUS.0[5], &mut carry); + r8 = ::adc(r8, carry2, &mut carry); + let carry2 = carry; + let k = r3.wrapping_mul(INV); + let mut carry = 0; + ::mac_with_carry(r3, k, MODULUS.0[0], &mut carry); + r4 = ::mac_with_carry(r4, k, MODULUS.0[1], &mut carry); + r5 = ::mac_with_carry(r5, k, MODULUS.0[2], &mut carry); + r6 = ::mac_with_carry(r6, k, MODULUS.0[3], &mut carry); + r7 = ::mac_with_carry(r7, k, MODULUS.0[4], &mut carry); + r8 = ::mac_with_carry(r8, k, MODULUS.0[5], &mut carry); + r9 = ::adc(r9, carry2, &mut carry); + let carry2 = carry; + let k = r4.wrapping_mul(INV); + let mut carry = 0; + ::mac_with_carry(r4, k, MODULUS.0[0], &mut carry); + r5 = ::mac_with_carry(r5, k, MODULUS.0[1], &mut carry); + r6 = ::mac_with_carry(r6, k, MODULUS.0[2], &mut carry); + r7 = ::mac_with_carry(r7, k, MODULUS.0[3], &mut carry); + r8 = ::mac_with_carry(r8, k, MODULUS.0[4], &mut carry); + r9 = ::mac_with_carry(r9, k, MODULUS.0[5], &mut carry); + r10 = ::adc(r10, carry2, &mut carry); + let carry2 = carry; + let k = r5.wrapping_mul(INV); + let mut carry = 0; + ::mac_with_carry(r5, k, MODULUS.0[0], &mut carry); + r6 = ::mac_with_carry(r6, k, MODULUS.0[1], &mut carry); + r7 = ::mac_with_carry(r7, k, MODULUS.0[2], &mut carry); + r8 = ::mac_with_carry(r8, k, MODULUS.0[3], &mut carry); + r9 = ::mac_with_carry(r9, k, MODULUS.0[4], &mut carry); + r10 = ::mac_with_carry(r10, k, MODULUS.0[5], &mut carry); + r11 = ::adc(r11, carry2, &mut carry); + (self.0).0[0] = r6; + (self.0).0[1] = r7; + (self.0).0[2] = r8; + (self.0).0[3] = r9; + (self.0).0[4] = r10; + (self.0).0[5] = r11; + self.reduce(); + } +} + +impl SqrtField for Fq { + fn legendre(&self) -> ::LegendreSymbol { + use LegendreSymbol::*; + + // s = self^((q - 1) // 2) + let s = self.pow([ + 0xdcff7fffffffd555, + 0xf55ffff58a9ffff, + 0xb39869507b587b12, + 0xb23ba5c279c2895f, + 0x258dd3db21a5d66b, + 0xd0088f51cbff34d, + ]); + if s == Fq::zero() { + Zero + } else if s == Fq::one() { + QuadraticResidue + } else { + QuadraticNonResidue + } + } + + fn sqrt(&self) -> Option { + // Shank's algorithm for q mod 4 = 3 + // https://eprint.iacr.org/2012/685.pdf (page 9, algorithm 2) + + // a1 = self^((q - 3) // 4) + let mut a1 = self.pow([ + 0xee7fbfffffffeaaa, + 0x7aaffffac54ffff, + 0xd9cc34a83dac3d89, + 0xd91dd2e13ce144af, + 0x92c6e9ed90d2eb35, + 0x680447a8e5ff9a6, + ]); + let mut a0 = a1; + a0.square(); + a0.mul_assign(self); + + if a0 == NEGATIVE_ONE { + None + } else { + a1.mul_assign(self); + Some(a1) + } + } +} + +#[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() { + 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() { + 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() { + 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() { + ::tests::field::random_field_tests::(); + ::tests::field::random_sqrt_tests::(); + ::tests::field::random_frobenius_tests::(Fq::char(), 13); + ::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() { + ::tests::repr::random_repr_tests::(); +} + +#[test] +fn test_fq_legendre() { + use LegendreSymbol::*; + + 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..2bec0b1 --- /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 rand::{Rand, Rng}; +use Field; + +/// 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 PrimeField; + + ::tests::field::random_field_tests::(); + ::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..18cd580 --- /dev/null +++ b/pairing/src/bls12_381/fq2.rs @@ -0,0 +1,908 @@ +use super::fq::{FROBENIUS_COEFF_FQ2_C1, Fq, NEGATIVE_ONE}; +use rand::{Rand, Rng}; +use {Field, SqrtField}; + +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) -> ::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 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 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 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 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 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 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 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 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 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 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 PrimeField; + + ::tests::field::random_field_tests::(); + ::tests::field::random_sqrt_tests::(); + ::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..c065f27 --- /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 rand::{Rand, Rng}; +use Field; + +/// 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 PrimeField; + + ::tests::field::random_field_tests::(); + ::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..4e9d6ab --- /dev/null +++ b/pairing/src/bls12_381/fr.rs @@ -0,0 +1,1614 @@ +use LegendreSymbol::*; +use {Field, PrimeField, PrimeFieldDecodingError, PrimeFieldRepr, SqrtField}; + +// r = 52435875175126190479447740508185965837690552500527637822603658699938581184513 +const MODULUS: FrRepr = FrRepr([ + 0xffffffff00000001, + 0x53bda402fffe5bfe, + 0x3339d80809a1d805, + 0x73eda753299d7d48, +]); + +// The number of bits needed to represent the modulus. +const MODULUS_BITS: u32 = 255; + +// The number of bits that must be shaved from the beginning of +// the representation when randomly sampling. +const REPR_SHAVE_BITS: u32 = 1; + +// R = 2**256 % r +const R: FrRepr = FrRepr([ + 0x1fffffffe, + 0x5884b7fa00034802, + 0x998c4fefecbc4ff5, + 0x1824b159acc5056f, +]); + +// R2 = R^2 % r +const R2: FrRepr = FrRepr([ + 0xc999e990f3f29c6d, + 0x2b6cedcb87925c23, + 0x5d314967254398f, + 0x748d9d99f59ff11, +]); + +// INV = -(r^{-1} mod 2^64) mod 2^64 +const INV: u64 = 0xfffffffeffffffff; + +// GENERATOR = 7 (multiplicative generator of r-1 order, that is also quadratic nonresidue) +const GENERATOR: FrRepr = FrRepr([ + 0xefffffff1, + 0x17e363d300189c0f, + 0xff9c57876f8457b0, + 0x351332208fc5a8c4, +]); + +// 2^s * t = MODULUS - 1 with t odd +const S: u32 = 32; + +// 2^s root of unity computed by GENERATOR^t +const ROOT_OF_UNITY: FrRepr = FrRepr([ + 0xb9b58d8c5f0e466a, + 0x5b1b4c801819d7ec, + 0xaf53ae352a31e64, + 0x5bf3adda19e9b27b, +]); + +#[derive(Copy, Clone, PartialEq, Eq, Default, Debug)] +pub struct FrRepr(pub [u64; 4]); + +impl ::rand::Rand for FrRepr { + #[inline(always)] + fn rand(rng: &mut R) -> Self { + FrRepr(rng.gen()) + } +} + +impl ::std::fmt::Display for FrRepr { + fn fmt(&self, f: &mut ::std::fmt::Formatter) -> ::std::fmt::Result { + try!(write!(f, "0x")); + for i in self.0.iter().rev() { + try!(write!(f, "{:016x}", *i)); + } + + Ok(()) + } +} + +impl AsRef<[u64]> for FrRepr { + #[inline(always)] + fn as_ref(&self) -> &[u64] { + &self.0 + } +} + +impl AsMut<[u64]> for FrRepr { + #[inline(always)] + fn as_mut(&mut self) -> &mut [u64] { + &mut self.0 + } +} + +impl From for FrRepr { + #[inline(always)] + fn from(val: u64) -> FrRepr { + let mut repr = Self::default(); + repr.0[0] = val; + repr + } +} + +impl Ord for FrRepr { + #[inline(always)] + fn cmp(&self, other: &FrRepr) -> ::std::cmp::Ordering { + for (a, b) in self.0.iter().rev().zip(other.0.iter().rev()) { + if a < b { + return ::std::cmp::Ordering::Less; + } else if a > b { + return ::std::cmp::Ordering::Greater; + } + } + + ::std::cmp::Ordering::Equal + } +} + +impl PartialOrd for FrRepr { + #[inline(always)] + fn partial_cmp(&self, other: &FrRepr) -> Option<::std::cmp::Ordering> { + Some(self.cmp(other)) + } +} + +impl PrimeFieldRepr for FrRepr { + #[inline(always)] + fn is_odd(&self) -> bool { + self.0[0] & 1 == 1 + } + + #[inline(always)] + fn is_even(&self) -> bool { + !self.is_odd() + } + + #[inline(always)] + fn is_zero(&self) -> bool { + self.0.iter().all(|&e| e == 0) + } + + #[inline(always)] + fn shr(&mut self, mut n: u32) { + if n >= 64 * 4 { + *self = Self::from(0); + return; + } + + while n >= 64 { + let mut t = 0; + for i in self.0.iter_mut().rev() { + ::std::mem::swap(&mut t, i); + } + n -= 64; + } + + if n > 0 { + let mut t = 0; + for i in self.0.iter_mut().rev() { + let t2 = *i << (64 - n); + *i >>= n; + *i |= t; + t = t2; + } + } + } + + #[inline(always)] + fn div2(&mut self) { + let mut t = 0; + for i in self.0.iter_mut().rev() { + let t2 = *i << 63; + *i >>= 1; + *i |= t; + t = t2; + } + } + + #[inline(always)] + fn mul2(&mut self) { + let mut last = 0; + for i in &mut self.0 { + let tmp = *i >> 63; + *i <<= 1; + *i |= last; + last = tmp; + } + } + + #[inline(always)] + fn shl(&mut self, mut n: u32) { + if n >= 64 * 4 { + *self = Self::from(0); + return; + } + + while n >= 64 { + let mut t = 0; + for i in &mut self.0 { + ::std::mem::swap(&mut t, i); + } + n -= 64; + } + + if n > 0 { + let mut t = 0; + for i in &mut self.0 { + let t2 = *i >> (64 - n); + *i <<= n; + *i |= t; + t = t2; + } + } + } + + #[inline(always)] + fn num_bits(&self) -> u32 { + let mut ret = (4 as u32) * 64; + for i in self.0.iter().rev() { + let leading = i.leading_zeros(); + ret -= leading; + if leading != 64 { + break; + } + } + + ret + } + + #[inline(always)] + fn add_nocarry(&mut self, other: &FrRepr) { + let mut carry = 0; + + for (a, b) in self.0.iter_mut().zip(other.0.iter()) { + *a = ::adc(*a, *b, &mut carry); + } + } + + #[inline(always)] + fn sub_noborrow(&mut self, other: &FrRepr) { + let mut borrow = 0; + + for (a, b) in self.0.iter_mut().zip(other.0.iter()) { + *a = ::sbb(*a, *b, &mut borrow); + } + } +} + +#[derive(Copy, Clone, PartialEq, Eq, Debug)] +pub struct Fr(FrRepr); + +impl ::std::fmt::Display for Fr { + fn fmt(&self, f: &mut ::std::fmt::Formatter) -> ::std::fmt::Result { + write!(f, "Fr({})", self.into_repr()) + } +} + +impl ::rand::Rand for Fr { + fn rand(rng: &mut R) -> Self { + loop { + let mut tmp = Fr(FrRepr::rand(rng)); + + // Mask away the unused bits at the beginning. + tmp.0.as_mut()[3] &= 0xffffffffffffffff >> REPR_SHAVE_BITS; + + if tmp.is_valid() { + return tmp; + } + } + } +} + +impl From for FrRepr { + fn from(e: Fr) -> FrRepr { + e.into_repr() + } +} + +impl PrimeField for Fr { + type Repr = FrRepr; + + fn from_repr(r: FrRepr) -> Result { + let mut r = Fr(r); + if r.is_valid() { + r.mul_assign(&Fr(R2)); + + Ok(r) + } else { + Err(PrimeFieldDecodingError::NotInField(format!("{}", r.0))) + } + } + + fn into_repr(&self) -> FrRepr { + let mut r = *self; + r.mont_reduce( + (self.0).0[0], + (self.0).0[1], + (self.0).0[2], + (self.0).0[3], + 0, + 0, + 0, + 0, + ); + r.0 + } + + fn char() -> FrRepr { + MODULUS + } + + const NUM_BITS: u32 = MODULUS_BITS; + + const CAPACITY: u32 = Self::NUM_BITS - 1; + + fn multiplicative_generator() -> Self { + Fr(GENERATOR) + } + + const S: u32 = S; + + fn root_of_unity() -> Self { + Fr(ROOT_OF_UNITY) + } +} + +impl Field for Fr { + #[inline] + fn zero() -> Self { + Fr(FrRepr::from(0)) + } + + #[inline] + fn one() -> Self { + Fr(R) + } + + #[inline] + fn is_zero(&self) -> bool { + self.0.is_zero() + } + + #[inline] + fn add_assign(&mut self, other: &Fr) { + // This cannot exceed the backing capacity. + self.0.add_nocarry(&other.0); + + // However, it may need to be reduced. + self.reduce(); + } + + #[inline] + fn double(&mut self) { + // This cannot exceed the backing capacity. + self.0.mul2(); + + // However, it may need to be reduced. + self.reduce(); + } + + #[inline] + fn sub_assign(&mut self, other: &Fr) { + // If `other` is larger than `self`, we'll need to add the modulus to self first. + if other.0 > self.0 { + self.0.add_nocarry(&MODULUS); + } + + self.0.sub_noborrow(&other.0); + } + + #[inline] + fn negate(&mut self) { + if !self.is_zero() { + let mut tmp = MODULUS; + tmp.sub_noborrow(&self.0); + self.0 = tmp; + } + } + + fn inverse(&self) -> Option { + if self.is_zero() { + None + } else { + // Guajardo Kumar Paar Pelzl + // Efficient Software-Implementation of Finite Fields with Applications to Cryptography + // Algorithm 16 (BEA for Inversion in Fp) + + let one = FrRepr::from(1); + + let mut u = self.0; + let mut v = MODULUS; + let mut b = Fr(R2); // Avoids unnecessary reduction step. + let mut c = Self::zero(); + + while u != one && v != one { + while u.is_even() { + u.div2(); + + if b.0.is_even() { + b.0.div2(); + } else { + b.0.add_nocarry(&MODULUS); + b.0.div2(); + } + } + + while v.is_even() { + v.div2(); + + if c.0.is_even() { + c.0.div2(); + } else { + c.0.add_nocarry(&MODULUS); + c.0.div2(); + } + } + + if v < u { + u.sub_noborrow(&v); + b.sub_assign(&c); + } else { + v.sub_noborrow(&u); + c.sub_assign(&b); + } + } + + if u == one { + Some(b) + } else { + Some(c) + } + } + } + + #[inline(always)] + fn frobenius_map(&mut self, _: usize) { + // This has no effect in a prime field. + } + + #[inline] + fn mul_assign(&mut self, other: &Fr) { + let mut carry = 0; + let r0 = ::mac_with_carry(0, (self.0).0[0], (other.0).0[0], &mut carry); + let r1 = ::mac_with_carry(0, (self.0).0[0], (other.0).0[1], &mut carry); + let r2 = ::mac_with_carry(0, (self.0).0[0], (other.0).0[2], &mut carry); + let r3 = ::mac_with_carry(0, (self.0).0[0], (other.0).0[3], &mut carry); + let r4 = carry; + let mut carry = 0; + let r1 = ::mac_with_carry(r1, (self.0).0[1], (other.0).0[0], &mut carry); + let r2 = ::mac_with_carry(r2, (self.0).0[1], (other.0).0[1], &mut carry); + let r3 = ::mac_with_carry(r3, (self.0).0[1], (other.0).0[2], &mut carry); + let r4 = ::mac_with_carry(r4, (self.0).0[1], (other.0).0[3], &mut carry); + let r5 = carry; + let mut carry = 0; + let r2 = ::mac_with_carry(r2, (self.0).0[2], (other.0).0[0], &mut carry); + let r3 = ::mac_with_carry(r3, (self.0).0[2], (other.0).0[1], &mut carry); + let r4 = ::mac_with_carry(r4, (self.0).0[2], (other.0).0[2], &mut carry); + let r5 = ::mac_with_carry(r5, (self.0).0[2], (other.0).0[3], &mut carry); + let r6 = carry; + let mut carry = 0; + let r3 = ::mac_with_carry(r3, (self.0).0[3], (other.0).0[0], &mut carry); + let r4 = ::mac_with_carry(r4, (self.0).0[3], (other.0).0[1], &mut carry); + let r5 = ::mac_with_carry(r5, (self.0).0[3], (other.0).0[2], &mut carry); + let r6 = ::mac_with_carry(r6, (self.0).0[3], (other.0).0[3], &mut carry); + let r7 = carry; + self.mont_reduce(r0, r1, r2, r3, r4, r5, r6, r7); + } + + #[inline] + fn square(&mut self) { + let mut carry = 0; + let r1 = ::mac_with_carry(0, (self.0).0[0], (self.0).0[1], &mut carry); + let r2 = ::mac_with_carry(0, (self.0).0[0], (self.0).0[2], &mut carry); + let r3 = ::mac_with_carry(0, (self.0).0[0], (self.0).0[3], &mut carry); + let r4 = carry; + let mut carry = 0; + let r3 = ::mac_with_carry(r3, (self.0).0[1], (self.0).0[2], &mut carry); + let r4 = ::mac_with_carry(r4, (self.0).0[1], (self.0).0[3], &mut carry); + let r5 = carry; + let mut carry = 0; + let r5 = ::mac_with_carry(r5, (self.0).0[2], (self.0).0[3], &mut carry); + let r6 = carry; + + let r7 = r6 >> 63; + let r6 = (r6 << 1) | (r5 >> 63); + let r5 = (r5 << 1) | (r4 >> 63); + let r4 = (r4 << 1) | (r3 >> 63); + let r3 = (r3 << 1) | (r2 >> 63); + let r2 = (r2 << 1) | (r1 >> 63); + let r1 = r1 << 1; + + let mut carry = 0; + let r0 = ::mac_with_carry(0, (self.0).0[0], (self.0).0[0], &mut carry); + let r1 = ::adc(r1, 0, &mut carry); + let r2 = ::mac_with_carry(r2, (self.0).0[1], (self.0).0[1], &mut carry); + let r3 = ::adc(r3, 0, &mut carry); + let r4 = ::mac_with_carry(r4, (self.0).0[2], (self.0).0[2], &mut carry); + let r5 = ::adc(r5, 0, &mut carry); + let r6 = ::mac_with_carry(r6, (self.0).0[3], (self.0).0[3], &mut carry); + let r7 = ::adc(r7, 0, &mut carry); + self.mont_reduce(r0, r1, r2, r3, r4, r5, r6, r7); + } +} + +impl Fr { + /// Determines if the element is really in the field. This is only used + /// internally. + #[inline(always)] + fn is_valid(&self) -> bool { + self.0 < MODULUS + } + + /// Subtracts the modulus from this element if this element is not in the + /// field. Only used internally. + #[inline(always)] + fn reduce(&mut self) { + if !self.is_valid() { + self.0.sub_noborrow(&MODULUS); + } + } + + #[inline(always)] + fn mont_reduce( + &mut self, + r0: u64, + mut r1: u64, + mut r2: u64, + mut r3: u64, + mut r4: u64, + mut r5: u64, + mut r6: u64, + mut r7: u64, + ) { + // The Montgomery reduction here is based on Algorithm 14.32 in + // Handbook of Applied Cryptography + // . + + let k = r0.wrapping_mul(INV); + let mut carry = 0; + ::mac_with_carry(r0, k, MODULUS.0[0], &mut carry); + r1 = ::mac_with_carry(r1, k, MODULUS.0[1], &mut carry); + r2 = ::mac_with_carry(r2, k, MODULUS.0[2], &mut carry); + r3 = ::mac_with_carry(r3, k, MODULUS.0[3], &mut carry); + r4 = ::adc(r4, 0, &mut carry); + let carry2 = carry; + let k = r1.wrapping_mul(INV); + let mut carry = 0; + ::mac_with_carry(r1, k, MODULUS.0[0], &mut carry); + r2 = ::mac_with_carry(r2, k, MODULUS.0[1], &mut carry); + r3 = ::mac_with_carry(r3, k, MODULUS.0[2], &mut carry); + r4 = ::mac_with_carry(r4, k, MODULUS.0[3], &mut carry); + r5 = ::adc(r5, carry2, &mut carry); + let carry2 = carry; + let k = r2.wrapping_mul(INV); + let mut carry = 0; + ::mac_with_carry(r2, k, MODULUS.0[0], &mut carry); + r3 = ::mac_with_carry(r3, k, MODULUS.0[1], &mut carry); + r4 = ::mac_with_carry(r4, k, MODULUS.0[2], &mut carry); + r5 = ::mac_with_carry(r5, k, MODULUS.0[3], &mut carry); + r6 = ::adc(r6, carry2, &mut carry); + let carry2 = carry; + let k = r3.wrapping_mul(INV); + let mut carry = 0; + ::mac_with_carry(r3, k, MODULUS.0[0], &mut carry); + r4 = ::mac_with_carry(r4, k, MODULUS.0[1], &mut carry); + r5 = ::mac_with_carry(r5, k, MODULUS.0[2], &mut carry); + r6 = ::mac_with_carry(r6, k, MODULUS.0[3], &mut carry); + r7 = ::adc(r7, carry2, &mut carry); + (self.0).0[0] = r4; + (self.0).0[1] = r5; + (self.0).0[2] = r6; + (self.0).0[3] = r7; + self.reduce(); + } +} + +impl SqrtField for Fr { + fn legendre(&self) -> ::LegendreSymbol { + // s = self^((r - 1) // 2) + let s = self.pow([ + 0x7fffffff80000000, + 0xa9ded2017fff2dff, + 0x199cec0404d0ec02, + 0x39f6d3a994cebea4, + ]); + if s == Self::zero() { + Zero + } else if s == Self::one() { + QuadraticResidue + } else { + QuadraticNonResidue + } + } + + fn sqrt(&self) -> Option { + // Tonelli-Shank's algorithm for q mod 16 = 1 + // https://eprint.iacr.org/2012/685.pdf (page 12, algorithm 5) + match self.legendre() { + Zero => Some(*self), + QuadraticNonResidue => None, + QuadraticResidue => { + let mut c = Fr(ROOT_OF_UNITY); + // r = self^((t + 1) // 2) + let mut r = self.pow([ + 0x7fff2dff80000000, + 0x4d0ec02a9ded201, + 0x94cebea4199cec04, + 0x39f6d3a9, + ]); + // t = self^t + let mut t = self.pow([ + 0xfffe5bfeffffffff, + 0x9a1d80553bda402, + 0x299d7d483339d808, + 0x73eda753, + ]); + let mut m = S; + + while t != Self::one() { + let mut i = 1; + { + let mut t2i = t; + t2i.square(); + loop { + if t2i == Self::one() { + break; + } + t2i.square(); + i += 1; + } + } + + for _ in 0..(m - i - 1) { + c.square(); + } + r.mul_assign(&c); + c.square(); + t.mul_assign(&c); + m = i; + } + + Some(r) + } + } + } +} + +#[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() { + 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() { + 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() { + 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() { + ::tests::field::random_field_tests::(); + ::tests::field::random_sqrt_tests::(); + ::tests::field::random_frobenius_tests::(Fr::char(), 13); + ::tests::field::from_str_tests::(); +} + +#[test] +fn fr_repr_tests() { + ::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..a5db4b5 --- /dev/null +++ b/pairing/src/bls12_381/mod.rs @@ -0,0 +1,364 @@ +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::{BitIterator, CurveAffine, Engine, Field}; + +// 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 Engine for Bls12 { + type Fr = Fr; + 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() { + ::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..bf6c595 --- /dev/null +++ b/pairing/src/bls12_381/tests/mod.rs @@ -0,0 +1,611 @@ +use super::*; +use *; + +#[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/lib.rs b/pairing/src/lib.rs new file mode 100644 index 0000000..08365f1 --- /dev/null +++ b/pairing/src/lib.rs @@ -0,0 +1,758 @@ +// `clippy` is a code linting tool for improving code quality by catching +// common mistakes or strange code patterns. If the `clippy` feature is +// provided, it is enabled and all compiler warnings are prohibited. +#![cfg_attr(feature = "clippy", deny(warnings))] +#![cfg_attr(feature = "clippy", feature(plugin))] +#![cfg_attr(feature = "clippy", plugin(clippy))] +#![cfg_attr(feature = "clippy", allow(inline_always))] +#![cfg_attr(feature = "clippy", allow(too_many_arguments))] +#![cfg_attr(feature = "clippy", allow(unreadable_literal))] +#![cfg_attr(feature = "clippy", allow(many_single_char_names))] +#![cfg_attr(feature = "clippy", allow(new_without_default_derive))] +#![cfg_attr(feature = "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 mod bls12_381; + +mod wnaf; +pub use self::wnaf::Wnaf; + +use std::error::Error; +use std::fmt; +use std::io::{self, Read, Write}; + +/// 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: Sized + 'static + Clone { + /// This is the scalar field of the G1/G2 groups. + type Fr: PrimeField + SqrtField; + + /// 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; + + /// 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(&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) + } +} + +/// 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; +} + +/// This trait represents an element of a field. +pub trait Field: + Sized + Eq + Copy + Clone + Send + Sync + fmt::Debug + fmt::Display + 'static + rand::Rand +{ + /// Returns the zero element of the field, the additive identity. + fn zero() -> Self; + + /// Returns the one element of the field, the multiplicative identity. + fn one() -> Self; + + /// Returns true iff this element is zero. + fn is_zero(&self) -> bool; + + /// Squares this element. + fn square(&mut self); + + /// Doubles this element. + fn double(&mut self); + + /// Negates this element. + fn negate(&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); + + /// Multiplies another element by this element. + fn mul_assign(&mut self, other: &Self); + + /// Computes the multiplicative inverse of this element, if nonzero. + fn inverse(&self) -> Option; + + /// Exponentiates this element by a power of the base prime modulus via + /// the Frobenius automorphism. + fn frobenius_map(&mut self, power: usize); + + /// Exponentiates this element by a number represented with `u64` limbs, + /// least significant digit first. + fn pow>(&self, exp: S) -> Self { + let mut res = Self::one(); + + let mut found_one = false; + + for i in BitIterator::new(exp) { + if found_one { + res.square(); + } else { + found_one = i; + } + + if i { + res.mul_assign(self); + } + } + + res + } +} + +/// This trait represents an element of a field that has a square root operation described for it. +pub trait SqrtField: Field { + /// Returns the Legendre symbol of the field element. + fn legendre(&self) -> LegendreSymbol; + + /// Returns the square root of the field element, if it is + /// quadratic residue. + fn sqrt(&self) -> Option; +} + +/// This trait represents a wrapper around a biginteger which can encode any element of a particular +/// prime field. It is a smart wrapper around a sequence of `u64` limbs, least-significant digit +/// first. +pub trait PrimeFieldRepr: + Sized + + Copy + + Clone + + Eq + + Ord + + Send + + Sync + + Default + + fmt::Debug + + fmt::Display + + 'static + + rand::Rand + + AsRef<[u64]> + + AsMut<[u64]> + + From +{ + /// Subtract another represetation from this one. + fn sub_noborrow(&mut self, other: &Self); + + /// Add another representation to this one. + fn add_nocarry(&mut self, other: &Self); + + /// Compute the number of bits needed to encode this number. Always a + /// multiple of 64. + fn num_bits(&self) -> u32; + + /// Returns true iff this number is zero. + fn is_zero(&self) -> bool; + + /// Returns true iff this number is odd. + fn is_odd(&self) -> bool; + + /// Returns true iff this number is even. + fn is_even(&self) -> bool; + + /// Performs a rightwise bitshift of this number, effectively dividing + /// it by 2. + fn div2(&mut self); + + /// Performs a rightwise bitshift of this number by some amount. + fn shr(&mut self, amt: u32); + + /// Performs a leftwise bitshift of this number, effectively multiplying + /// it by 2. Overflow is ignored. + fn mul2(&mut self); + + /// Performs a leftwise bitshift of this number by some amount. + fn shl(&mut self, amt: u32); + + /// Writes this `PrimeFieldRepr` as a big endian integer. + fn write_be(&self, mut writer: W) -> io::Result<()> { + use byteorder::{BigEndian, WriteBytesExt}; + + for digit in self.as_ref().iter().rev() { + writer.write_u64::(*digit)?; + } + + Ok(()) + } + + /// Reads a big endian integer into this representation. + fn read_be(&mut self, mut reader: R) -> io::Result<()> { + use byteorder::{BigEndian, ReadBytesExt}; + + for digit in self.as_mut().iter_mut().rev() { + *digit = reader.read_u64::()?; + } + + Ok(()) + } + + /// Writes this `PrimeFieldRepr` as a little endian integer. + fn write_le(&self, mut writer: W) -> io::Result<()> { + use byteorder::{LittleEndian, WriteBytesExt}; + + for digit in self.as_ref().iter() { + writer.write_u64::(*digit)?; + } + + Ok(()) + } + + /// Reads a little endian integer into this representation. + fn read_le(&mut self, mut reader: R) -> io::Result<()> { + use byteorder::{LittleEndian, ReadBytesExt}; + + for digit in self.as_mut().iter_mut() { + *digit = reader.read_u64::()?; + } + + Ok(()) + } +} + +#[derive(Debug, PartialEq)] +pub enum LegendreSymbol { + Zero = 0, + QuadraticResidue = 1, + QuadraticNonResidue = -1, +} + +/// An error that may occur when trying to interpret a `PrimeFieldRepr` as a +/// `PrimeField` element. +#[derive(Debug)] +pub enum PrimeFieldDecodingError { + /// The encoded value is not in the field + NotInField(String), +} + +impl Error for PrimeFieldDecodingError { + fn description(&self) -> &str { + match *self { + PrimeFieldDecodingError::NotInField(..) => "not an element of the field", + } + } +} + +impl fmt::Display for PrimeFieldDecodingError { + fn fmt(&self, f: &mut fmt::Formatter) -> Result<(), fmt::Error> { + match *self { + PrimeFieldDecodingError::NotInField(ref repr) => { + write!(f, "{} is not an element of the field", repr) + } + } + } +} + +/// 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()), + } + } +} + +/// This represents an element of a prime field. +pub trait PrimeField: Field { + /// The prime field can be converted back and forth into this biginteger + /// representation. + type Repr: PrimeFieldRepr + From; + + /// Interpret a string of numbers as a (congruent) prime field element. + /// Does not accept unnecessary leading zeroes or a blank string. + fn from_str(s: &str) -> Option { + if s.is_empty() { + return None; + } + + if s == "0" { + return Some(Self::zero()); + } + + let mut res = Self::zero(); + + let ten = Self::from_repr(Self::Repr::from(10)).unwrap(); + + let mut first_digit = true; + + for c in s.chars() { + match c.to_digit(10) { + Some(c) => { + if first_digit { + if c == 0 { + return None; + } + + first_digit = false; + } + + res.mul_assign(&ten); + res.add_assign(&Self::from_repr(Self::Repr::from(u64::from(c))).unwrap()); + } + None => { + return None; + } + } + } + + Some(res) + } + + /// Convert this prime field element into a biginteger representation. + fn from_repr(Self::Repr) -> Result; + + /// Convert a biginteger representation into a prime field element, if + /// the number is an element of the field. + fn into_repr(&self) -> Self::Repr; + + /// Returns the field characteristic; the modulus. + fn char() -> Self::Repr; + + /// How many bits are needed to represent an element of this field. + const NUM_BITS: u32; + + /// How many bits of information can be reliably stored in the field element. + const CAPACITY: u32; + + /// Returns the multiplicative generator of `char()` - 1 order. This element + /// must also be quadratic nonresidue. + fn multiplicative_generator() -> Self; + + /// 2^s * t = `char()` - 1 with t odd. + const S: u32; + + /// Returns the 2^s root of unity computed by exponentiating the `multiplicative_generator()` + /// by t. + fn root_of_unity() -> Self; +} + +#[derive(Debug)] +pub struct BitIterator { + t: E, + n: usize, +} + +impl> BitIterator { + pub fn new(t: E) -> Self { + let n = t.as_ref().len() * 64; + + BitIterator { t, n } + } +} + +impl> Iterator for BitIterator { + type Item = bool; + + fn next(&mut self) -> Option { + if self.n == 0 { + None + } else { + self.n -= 1; + let part = self.n / 64; + let bit = self.n - (64 * part); + + Some(self.t.as_ref()[part] & (1 << bit) > 0) + } + } +} + +#[test] +fn test_bit_iterator() { + let mut a = BitIterator::new([0xa953d79b83f6ab59, 0x6dea2059e200bd39]); + let expected = "01101101111010100010000001011001111000100000000010111101001110011010100101010011110101111001101110000011111101101010101101011001"; + + for e in expected.chars() { + assert!(a.next().unwrap() == (e == '1')); + } + + assert!(a.next().is_none()); + + let expected = "1010010101111110101010000101101011101000011101110101001000011001100100100011011010001011011011010001011011101100110100111011010010110001000011110100110001100110011101101000101100011100100100100100001010011101010111110011101011000011101000111011011101011001"; + + let mut a = BitIterator::new([ + 0x429d5f3ac3a3b759, + 0xb10f4c66768b1c92, + 0x92368b6d16ecd3b4, + 0xa57ea85ae8775219, + ]); + + for e in expected.chars() { + assert!(a.next().unwrap() == (e == '1')); + } + + assert!(a.next().is_none()); +} + +#[cfg(not(feature = "expose-arith"))] +use self::arith_impl::*; + +#[cfg(feature = "expose-arith")] +pub use self::arith_impl::*; + +#[cfg(feature = "u128-support")] +mod arith_impl { + /// Calculate a - b - borrow, returning the result and modifying + /// the borrow value. + #[inline(always)] + pub fn sbb(a: u64, b: u64, borrow: &mut u64) -> u64 { + let tmp = (1u128 << 64) + u128::from(a) - u128::from(b) - u128::from(*borrow); + + *borrow = if tmp >> 64 == 0 { 1 } else { 0 }; + + tmp as u64 + } + + /// Calculate a + b + carry, returning the sum and modifying the + /// carry value. + #[inline(always)] + pub fn adc(a: u64, b: u64, carry: &mut u64) -> u64 { + let tmp = u128::from(a) + u128::from(b) + u128::from(*carry); + + *carry = (tmp >> 64) as u64; + + tmp as u64 + } + + /// Calculate a + (b * c) + carry, returning the least significant digit + /// and setting carry to the most significant digit. + #[inline(always)] + pub fn mac_with_carry(a: u64, b: u64, c: u64, carry: &mut u64) -> u64 { + let tmp = (u128::from(a)) + u128::from(b) * u128::from(c) + u128::from(*carry); + + *carry = (tmp >> 64) as u64; + + tmp as u64 + } +} + +#[cfg(not(feature = "u128-support"))] +mod arith_impl { + #[inline(always)] + fn split_u64(i: u64) -> (u64, u64) { + (i >> 32, i & 0xFFFFFFFF) + } + + #[inline(always)] + fn combine_u64(hi: u64, lo: u64) -> u64 { + (hi << 32) | lo + } + + /// Calculate a - b - borrow, returning the result and modifying + /// the borrow value. + #[inline(always)] + pub fn sbb(a: u64, b: u64, borrow: &mut u64) -> u64 { + let (a_hi, a_lo) = split_u64(a); + let (b_hi, b_lo) = split_u64(b); + let (b, r0) = split_u64((1 << 32) + a_lo - b_lo - *borrow); + let (b, r1) = split_u64((1 << 32) + a_hi - b_hi - ((b == 0) as u64)); + + *borrow = (b == 0) as u64; + + combine_u64(r1, r0) + } + + /// Calculate a + b + carry, returning the sum and modifying the + /// carry value. + #[inline(always)] + pub fn adc(a: u64, b: u64, carry: &mut u64) -> u64 { + let (a_hi, a_lo) = split_u64(a); + let (b_hi, b_lo) = split_u64(b); + let (carry_hi, carry_lo) = split_u64(*carry); + + let (t, r0) = split_u64(a_lo + b_lo + carry_lo); + let (t, r1) = split_u64(t + a_hi + b_hi + carry_hi); + + *carry = t; + + combine_u64(r1, r0) + } + + /// Calculate a + (b * c) + carry, returning the least significant digit + /// and setting carry to the most significant digit. + #[inline(always)] + pub fn mac_with_carry(a: u64, b: u64, c: u64, carry: &mut u64) -> u64 { + /* + [ b_hi | b_lo ] + [ c_hi | c_lo ] * + ------------------------------------------- + [ b_lo * c_lo ] <-- w + [ b_hi * c_lo ] <-- x + [ b_lo * c_hi ] <-- y + [ b_hi * c_lo ] <-- z + [ a_hi | a_lo ] + [ C_hi | C_lo ] + */ + + let (a_hi, a_lo) = split_u64(a); + let (b_hi, b_lo) = split_u64(b); + let (c_hi, c_lo) = split_u64(c); + let (carry_hi, carry_lo) = split_u64(*carry); + + let (w_hi, w_lo) = split_u64(b_lo * c_lo); + let (x_hi, x_lo) = split_u64(b_hi * c_lo); + let (y_hi, y_lo) = split_u64(b_lo * c_hi); + let (z_hi, z_lo) = split_u64(b_hi * c_hi); + + let (t, r0) = split_u64(w_lo + a_lo + carry_lo); + let (t, r1) = split_u64(t + w_hi + x_lo + y_lo + a_hi + carry_hi); + let (t, r2) = split_u64(t + x_hi + y_hi + z_lo); + let (_, r3) = split_u64(t + z_hi); + + *carry = combine_u64(r3, r2); + + combine_u64(r1, r0) + } +} diff --git a/pairing/src/tests/curve.rs b/pairing/src/tests/curve.rs new file mode 100644 index 0000000..1480b74 --- /dev/null +++ b/pairing/src/tests/curve.rs @@ -0,0 +1,420 @@ +use rand::{Rand, Rng, SeedableRng, XorShiftRng}; + +use {CurveAffine, CurveProjective, EncodedPoint, Field}; + +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_transformation_tests::(); + random_wnaf_tests::(); + random_encoding_tests::(); +} + +fn random_wnaf_tests() { + use wnaf::*; + use PrimeField; + + 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]); + } + } +} + +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::>(); + + 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..52ff4e0 --- /dev/null +++ b/pairing/src/tests/engine.rs @@ -0,0 +1,126 @@ +use rand::{Rand, SeedableRng, XorShiftRng}; + +use {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..74422fd --- /dev/null +++ b/pairing/src/tests/field.rs @@ -0,0 +1,266 @@ +use rand::{Rng, SeedableRng, XorShiftRng}; +use {Field, LegendreSymbol, PrimeField, SqrtField}; + +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..681a476 --- /dev/null +++ b/pairing/src/tests/repr.rs @@ -0,0 +1,98 @@ +use rand::{SeedableRng, XorShiftRng}; +use PrimeFieldRepr; + +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()) + } +}