commit a06216f24b488fa2a25b42366cb3d3614218a7b5 Author: Sean Bowe Date: Sat Jul 8 10:55:43 2017 -0600 Initial commit diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..4308d82 --- /dev/null +++ b/.gitignore @@ -0,0 +1,3 @@ +target/ +**/*.rs.bk +Cargo.lock diff --git a/Cargo.toml b/Cargo.toml new file mode 100644 index 0000000..f024106 --- /dev/null +++ b/Cargo.toml @@ -0,0 +1,13 @@ +[package] +name = "pairing" +version = "0.9.0" +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.3" diff --git a/LICENSE-APACHE b/LICENSE-APACHE new file mode 100644 index 0000000..1e5006d --- /dev/null +++ b/LICENSE-APACHE @@ -0,0 +1,202 @@ + 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/LICENSE-MIT b/LICENSE-MIT new file mode 100644 index 0000000..ed3a13f --- /dev/null +++ b/LICENSE-MIT @@ -0,0 +1,21 @@ +The MIT License (MIT) + +Copyright (c) 2017 Sean Bowe + +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/README.md b/README.md new file mode 100644 index 0000000..538a5c5 --- /dev/null +++ b/README.md @@ -0,0 +1,21 @@ +# 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/) + +## 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/src/bls12_381/README.md b/src/bls12_381/README.md new file mode 100644 index 0000000..f54fac3 --- /dev/null +++ b/src/bls12_381/README.md @@ -0,0 +1,57 @@ +# 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 + 4v. + +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 +``` diff --git a/src/bls12_381/ec.rs b/src/bls12_381/ec.rs new file mode 100644 index 0000000..4f0daf9 --- /dev/null +++ b/src/bls12_381/ec.rs @@ -0,0 +1,1122 @@ +macro_rules! curve_impl { + ( + $projective:ident, + $affine:ident, + $prepared:ident, + $basefield:ident, + $scalarfield:ident + ) => { + #[derive(Copy, Clone, PartialEq, Eq, Debug)] + pub struct $affine { + pub(crate) x: $basefield, + pub(crate) y: $basefield, + pub(crate) infinity: bool + } + + #[derive(Copy, Clone, Debug, Eq)] + pub struct $projective { + pub(crate) x: $basefield, + pub(crate) y: $basefield, + pub(crate) z: $basefield + } + + 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 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()); + + if y2 == x3b { + true + } else { + false + } + } + } + + fn is_in_correct_subgroup(&self) -> bool { + if self.mul($scalarfield::char()).is_zero() { + true + } else { + false + } + } + } + + impl CurveAffine for $affine { + type Scalar = $scalarfield; + type Base = $basefield; + type Prepared = $prepared; + type Projective = $projective; + + 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 is_valid(&self) -> bool { + self.is_on_curve() && self.is_in_correct_subgroup() + } + + fn mul::Repr>>(&self, by: S) -> $projective { + let mut res = $projective::zero(); + + for i in BitIterator::new(by.into()) + { + res.double(); + + if i { + res.add_assign_mixed(self); + } + } + + res + } + + fn negate(&mut self) { + if !self.is_zero() { + self.y.negate(); + } + } + + fn prepare(&self) -> Self::Prepared { + $prepared::from_affine(*self) + } + + fn to_projective(&self) -> $projective { + (*self).into() + } + } + + impl Rand for $projective { + fn rand(rng: &mut R) -> Self { + $affine::one().mul($scalarfield::rand(rng)) + } + } + + impl CurveProjective for $projective { + 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(); + + for i in BitIterator::new(other.into()) + { + res.double(); + + if i { + res.add_assign(self); + } + } + + *self = res; + } + + fn to_affine(&self) -> $affine { + (*self).into() + } + } + + // 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 rand::{Rand, Rng}; + use super::super::{Fq, Fr}; + use ::{CurveProjective, CurveAffine, PrimeField, Field, BitIterator}; + + curve_impl!(G1, G1Affine, G1Prepared, Fq, Fr); + + impl G1Affine { + fn get_generator() -> Self { + G1Affine { + x: super::super::fq::G1_GENERATOR_X, + y: super::super::fq::G1_GENERATOR_Y, + infinity: false + } + } + + fn get_coeff_b() -> Fq { + super::super::fq::B_COEFF + } + } + + #[derive(Clone)] + 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) + } + } + + #[cfg(test)] + use super::super::{FqRepr}; + + #[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_valid()); + + let mut g1 = G1::zero(); + + // Cofactor of G1 is 76329603384216526031706109802092473003. + // Calculated by: ((x-1)**2) // 3 + // where x is the BLS parameter. + for b in "111001011011001000110000000000010101010101010111100001010101101000110000000000101010101010101100000000000000001010101010101011" + .chars() + .map(|c| c == '1') + { + g1.double(); + + if b { + g1.add_assign_mixed(&p); + } + } + + if !g1.is_zero() { + assert_eq!(i, 4); + let g1 = G1Affine::from(g1); + + assert!(g1.is_valid()); + + 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()); + assert!(!p.is_valid()); + } + + // 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()); + assert!(!p.is_valid()); + } + + // 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()); + assert!(!p.is_valid()); + } + } + + #[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_valid()); + assert!(b.is_valid()); + assert!(c.is_valid()); + + let mut tmp1 = a.to_projective(); + tmp1.add_assign(&b.to_projective()); + assert_eq!(tmp1.to_affine(), c); + assert_eq!(tmp1, c.to_projective()); + + let mut tmp2 = a.to_projective(); + tmp2.add_assign_mixed(&b); + assert_eq!(tmp2.to_affine(), c); + assert_eq!(tmp2, c.to_projective()); + } + + #[test] + fn g1_curve_tests() { + ::tests::curve::curve_tests::(); + } + + #[cfg(test)] + use rand::{SeedableRng, XorShiftRng}; + + #[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 + }); + } +} + +pub mod g2 { + use rand::{Rand, Rng}; + use super::super::{Fq2, Fr}; + use ::{CurveProjective, CurveAffine, PrimeField, Field, BitIterator}; + + curve_impl!(G2, G2Affine, G2Prepared, Fq2, Fr); + + 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 + } + } + } + + #[derive(Clone)] + pub struct G2Prepared { + pub(crate) coeffs: Vec<(Fq2, Fq2, Fq2)>, + pub(crate) infinity: bool + } + + #[cfg(test)] + use super::super::{Fq, FqRepr}; + + #[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 yrepr = y.c1.into_repr(); + let mut negy = y; + negy.negate(); + let negyrepr = negy.c1.into_repr(); + + let p = G2Affine { + x: x, + y: if yrepr < negyrepr { y } else { negy }, + infinity: false + }; + + assert!(!p.is_valid()); + + let mut g2 = G2::zero(); + + // Cofactor of G2 is 305502333931268344200999753193121504214466019254188142667664032982267604182971884026507427359259977847832272839041616661285803823378372096355777062779109. + // Calculated by: ((x**8) - (4 * (x**7)) + (5 * (x**6)) - (4 * (x**4)) + (6 * (x**3)) - (4 * (x**2)) - (4*x) + 13) // 9 + // where x is the BLS parameter. + for b in "101110101010100001110101001010101000001010011100111111100010000100100011101010100000111100100101000011101101010001000000010110011011001000111011110010001010100011100001000010110101011101010100110100010100010000001011011001011100101101001111101110111111010011000101000111100011100101101001101100111101000001011101111001000010101001101111110001010010011101001100110100100011010111000010110000101101110110001101110011110000110111100001100011100001100111100011100001110001110001100011100011100100011100011100101" + .chars() + .map(|c| c == '1') + { + g2.double(); + + if b { + g2.add_assign_mixed(&p); + } + } + + if !g2.is_zero() { + assert_eq!(i, 2); + let g2 = G2Affine::from(g2); + + assert!(g2.is_valid()); + + 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()); + assert!(!p.is_valid()); + } + + // 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()); + assert!(!p.is_valid()); + } + + // 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()); + assert!(!p.is_valid()); + } + } + + #[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::(); + } + + #[cfg(test)] + use rand::{SeedableRng, XorShiftRng}; + + #[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 + }); + } +} + +pub use self::g1::*; +pub use self::g2::*; diff --git a/src/bls12_381/fq.rs b/src/bls12_381/fq.rs new file mode 100644 index 0000000..503d737 --- /dev/null +++ b/src/bls12_381/fq.rs @@ -0,0 +1,1748 @@ +use ::{Field, PrimeField, SqrtField, PrimeFieldRepr}; +use super::fq2::Fq2; + +// 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 q) mod q +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: usize = 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)] +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::Debug 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 From for FqRepr { + #[inline(always)] + fn from(val: u64) -> FqRepr { + use std::default::Default; + + let mut repr = Self::default(); + repr.0[0] = val; + repr + } +} + +impl Ord for FqRepr { + #[inline(always)] + fn cmp(&self, other: &FqRepr) -> ::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 FqRepr { + #[inline(always)] + fn partial_cmp(&self, other: &FqRepr) -> Option<::std::cmp::Ordering> { + 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 divn(&mut self, mut n: usize) { + 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 self.0.iter_mut() { + let tmp = *i >> 63; + *i <<= 1; + *i |= last; + last = tmp; + } + } + + #[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) -> bool { + let mut carry = 0; + + for (a, b) in self.0.iter_mut().zip(other.0.iter()) { + *a = ::adc(*a, *b, &mut carry); + } + + carry != 0 + } + + #[inline(always)] + fn sub_noborrow(&mut self, other: &FqRepr) -> bool { + let mut borrow = 0; + + for (a, b) in self.0.iter_mut().zip(other.0.iter()) { + *a = ::sbb(*a, *b, &mut borrow); + } + + borrow != 0 + } +} + +#[derive(Copy, Clone, PartialEq, Eq)] +pub struct Fq(FqRepr); + +impl ::std::fmt::Debug 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)); + for _ in 0..REPR_SHAVE_BITS { + tmp.0.div2(); + } + 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(()) + } + } + + 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 + } + + fn num_bits() -> u32 { + MODULUS_BITS + } + + fn capacity() -> u32 { + Self::num_bits() - 1 + } + + fn multiplicative_generator() -> Self { + Fq(GENERATOR) + } + + fn s() -> usize { + 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 tmp0 = r1 >> 63; + let r1 = r1 << 1; + let tmp1 = r2 >> 63; + let r2 = r2 << 1; + let r2 = r2 | tmp0; + let tmp0 = tmp1; + let tmp1 = r3 >> 63; + let r3 = r3 << 1; + let r3 = r3 | tmp0; + let tmp0 = tmp1; + let tmp1 = r4 >> 63; + let r4 = r4 << 1; + let r4 = r4 | tmp0; + let tmp0 = tmp1; + let tmp1 = r5 >> 63; + let r5 = r5 << 1; + let r5 = r5 | tmp0; + let tmp0 = tmp1; + let tmp1 = r6 >> 63; + let r6 = r6 << 1; + let r6 = r6 | tmp0; + let tmp0 = tmp1; + let tmp1 = r7 >> 63; + let r7 = r7 << 1; + let r7 = r7 | tmp0; + let tmp0 = tmp1; + let tmp1 = r8 >> 63; + let r8 = r8 << 1; + let r8 = r8 | tmp0; + let tmp0 = tmp1; + let tmp1 = r9 >> 63; + let r9 = r9 << 1; + let r9 = r9 | tmp0; + let tmp0 = tmp1; + let tmp1 = r10 >> 63; + let r10 = r10 << 1; + let r10 = r10 | tmp0; + let tmp0 = tmp1; + let r11 = tmp0; + 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 interally. + #[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 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) // 2) + let mut a1 = self.pow([0xee7fbfffffffeaaa, 0x7aaffffac54ffff, 0xd9cc34a83dac3d89, 0xd91dd2e13ce144af, 0x92c6e9ed90d2eb35, 0x680447a8e5ff9a6]); + let mut a0 = a1; + a0.square(); + a0.mul_assign(self); + + // if a0 == -1 + if a0.0 == FqRepr([0x43f5fffffffcaaae, 0x32b7fff2ed47fffd, 0x7e83a49a2e99d69, 0xeca8f3318332bb7a, 0xef148d1ea0f4c069, 0x40ab3263eff0206]) + { + 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::{SeedableRng, XorShiftRng, Rand}; + +#[test] +fn test_fq_repr_ordering() { + fn assert_equality(a: FqRepr, b: FqRepr) { + assert_eq!(a, b); + assert!(a.cmp(&b) == ::std::cmp::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_divn() { + let mut a = FqRepr([0xaa5cdd6172847ffd, 0x43242c06aed55287, 0x9ddd5b312f3dd104, 0xc5541fd48046b7e7, 0x16080cf4071e0b05, 0x1225f2901aea514e]); + a.divn(0); + assert_eq!( + a, + FqRepr([0xaa5cdd6172847ffd, 0x43242c06aed55287, 0x9ddd5b312f3dd104, 0xc5541fd48046b7e7, 0x16080cf4071e0b05, 0x1225f2901aea514e]) + ); + a.divn(1); + assert_eq!( + a, + FqRepr([0xd52e6eb0b9423ffe, 0x21921603576aa943, 0xceeead98979ee882, 0xe2aa0fea40235bf3, 0xb04067a038f0582, 0x912f9480d7528a7]) + ); + a.divn(50); + assert_eq!( + a, + FqRepr([0x8580d5daaa50f54b, 0xab6625e7ba208864, 0x83fa9008d6fcf3bb, 0x19e80e3c160b8aa, 0xbe52035d4a29c2c1, 0x244]) + ); + a.divn(130); + assert_eq!( + a, + FqRepr([0xa0fea40235bf3cee, 0x4067a038f0582e2a, 0x2f9480d7528a70b0, 0x91, 0x0, 0x0]) + ); + a.divn(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 a borrow + let mut qplusone = FqRepr([0xb9feffffffffaaab, 0x1eabfffeb153ffff, 0x6730d2a0f6b0f624, 0x64774b84f38512bf, 0x4b1ba7b6434bacd7, 0x1a0111ea397fe69a]); + assert!(qplusone.sub_noborrow(&FqRepr([0xb9feffffffffaaac, 0x1eabfffeb153ffff, 0x6730d2a0f6b0f624, 0x64774b84f38512bf, 0x4b1ba7b6434bacd7, 0x1a0111ea397fe69a]))); + + // Subtracting x from x should produce no borrow + let mut x = FqRepr([0xb9feffffffffaaac, 0x1eabfffeb153ffff, 0x6730d2a0f6b0f624, 0x64774b84f38512bf, 0x4b1ba7b6434bacd7, 0x1a0111ea397fe69a]); + assert!(!x.sub_noborrow(&FqRepr([0xb9feffffffffaaac, 0x1eabfffeb153ffff, 0x6730d2a0f6b0f624, 0x64774b84f38512bf, 0x4b1ba7b6434bacd7, 0x1a0111ea397fe69a]))) +} + +#[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 a carry + let mut x = FqRepr([0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff]); + assert!(x.add_nocarry(&FqRepr::from(1))); + + // Adding 1 to q should not produce a carry + let mut x = FqRepr([0xb9feffffffffaaab, 0x1eabfffeb153ffff, 0x6730d2a0f6b0f624, 0x64774b84f38512bf, 0x4b1ba7b6434bacd7, 0x1a0111ea397fe69a]); + assert!(!x.add_nocarry(&FqRepr::from(1))); +} + +#[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 + }); +} + +#[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()); + } +} + +#[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 + }); +} + +#[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); + } +} + +#[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 + }); +} + +#[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); + } +} + +#[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 + }); +} + +#[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); + } +} + +#[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() + }); +} + +#[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()); + } +} + +#[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 + }); +} + + +#[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); + } + } +} + +#[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() + }); +} + +#[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); + } +} + +#[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]) + }); +} + +#[test] +fn test_fq_repr_debug() { + 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_debug() { + 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); +} diff --git a/src/bls12_381/fq12.rs b/src/bls12_381/fq12.rs new file mode 100644 index 0000000..4df2282 --- /dev/null +++ b/src/bls12_381/fq12.rs @@ -0,0 +1,289 @@ +use rand::{Rng, Rand}; +use ::{Field}; +use super::fq6::Fq6; +use super::fq2::Fq2; +use super::fq::{FROBENIUS_COEFF_FQ12_C1}; + +/// An element of F_{q^12}, represented by c0 + c1 * w. +#[derive(Copy, Clone, Debug, Eq, PartialEq)] +pub struct Fq12 { + pub c0: Fq6, + pub c1: Fq6 +} + +impl Rand for Fq12 { + fn rand(rng: &mut R) -> Self { + Fq12 { + c0: rng.gen(), + c1: rng.gen() + } + } +} + +impl Fq12 { + pub fn unitary_inverse(&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); + } +} + +#[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 + }); +} + +#[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/src/bls12_381/fq2.rs b/src/bls12_381/fq2.rs new file mode 100644 index 0000000..55da940 --- /dev/null +++ b/src/bls12_381/fq2.rs @@ -0,0 +1,504 @@ +use rand::{Rng, Rand}; +use ::{Field, SqrtField}; +use super::fq::{Fq, FROBENIUS_COEFF_FQ2_C1, NEGATIVE_ONE}; + +/// An element of F_{q^2}, represented by c0 + c1 * u. +#[derive(Copy, Clone, Debug, Eq, PartialEq)] +pub struct Fq2 { + pub c0: Fq, + pub c1: Fq +} + +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); + } +} + +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 sqrt(&self) -> Option { + // Algorithm 9, https://eprint.iacr.org/2012/685.pdf + + if self.is_zero() { + return 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_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 ::PrimeField; + use super::fq::{FqRepr}; + + 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 ::PrimeField; + use super::fq::{FqRepr}; + + 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 ::PrimeField; + use super::fq::{FqRepr}; + + 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 ::PrimeField; + use super::fq::{FqRepr}; + + 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 ::PrimeField; + use super::fq::{FqRepr}; + + 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 ::PrimeField; + use super::fq::{FqRepr}; + + 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 ::PrimeField; + use super::fq::{FqRepr}; + + 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 ::PrimeField; + use super::fq::{FqRepr}; + + 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 ::PrimeField; + use super::fq::{FqRepr}; + + 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() + } + ); +} + +#[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); + } +} + +#[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 + }); +} + +#[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/src/bls12_381/fq6.rs b/src/bls12_381/fq6.rs new file mode 100644 index 0000000..81226da --- /dev/null +++ b/src/bls12_381/fq6.rs @@ -0,0 +1,372 @@ +use rand::{Rng, Rand}; +use ::{Field}; +use super::fq2::Fq2; +use super::fq::{FROBENIUS_COEFF_FQ6_C1, FROBENIUS_COEFF_FQ6_C2}; + +/// An element of F_{q^6}, 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 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/src/bls12_381/fr.rs b/src/bls12_381/fr.rs new file mode 100644 index 0000000..7c274ab --- /dev/null +++ b/src/bls12_381/fr.rs @@ -0,0 +1,1464 @@ +use ::{Field, PrimeField, SqrtField, PrimeFieldRepr}; + +// 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 r) mod r +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: usize = 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)] +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::Debug 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 From for FrRepr { + #[inline(always)] + fn from(val: u64) -> FrRepr { + use std::default::Default; + + 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 divn(&mut self, mut n: usize) { + 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 self.0.iter_mut() { + let tmp = *i >> 63; + *i <<= 1; + *i |= last; + last = tmp; + } + } + + #[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) -> bool { + let mut carry = 0; + + for (a, b) in self.0.iter_mut().zip(other.0.iter()) { + *a = ::adc(*a, *b, &mut carry); + } + + carry != 0 + } + + #[inline(always)] + fn sub_noborrow(&mut self, other: &FrRepr) -> bool { + let mut borrow = 0; + + for (a, b) in self.0.iter_mut().zip(other.0.iter()) { + *a = ::sbb(*a, *b, &mut borrow); + } + + borrow != 0 + } +} + +#[derive(Copy, Clone, PartialEq, Eq)] +pub struct Fr(FrRepr); + +impl ::std::fmt::Debug 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)); + for _ in 0..REPR_SHAVE_BITS { + tmp.0.div2(); + } + 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(()) + } + } + + 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 + } + + fn num_bits() -> u32 { + MODULUS_BITS + } + + fn capacity() -> u32 { + Self::num_bits() - 1 + } + + fn multiplicative_generator() -> Self { + Fr(GENERATOR) + } + + fn s() -> usize { + 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 tmp0 = r1 >> 63; + let r1 = r1 << 1; + let tmp1 = r2 >> 63; + let r2 = r2 << 1; + let r2 = r2 | tmp0; + let tmp0 = tmp1; + let tmp1 = r3 >> 63; + let r3 = r3 << 1; + let r3 = r3 | tmp0; + let tmp0 = tmp1; + let tmp1 = r4 >> 63; + let r4 = r4 << 1; + let r4 = r4 | tmp0; + let tmp0 = tmp1; + let tmp1 = r5 >> 63; + let r5 = r5 << 1; + let r5 = r5 | tmp0; + let tmp0 = tmp1; + let tmp1 = r6 >> 63; + let r6 = r6 << 1; + let r6 = r6 | tmp0; + let tmp0 = tmp1; + let r7 = tmp0; + 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 interally. + #[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 sqrt(&self) -> Option { + // Tonelli-Shank's algorithm for q mod 16 = 1 + // https://eprint.iacr.org/2012/685.pdf (page 12, algorithm 5) + + if self.is_zero() { + return Some(*self); + } + + // if self^((r - 1) // 2) != 1 + if self.pow([0x7fffffff80000000, 0xa9ded2017fff2dff, 0x199cec0404d0ec02, 0x39f6d3a994cebea4]) != Self::one() { + None + } else { + 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::{SeedableRng, XorShiftRng, Rand}; + +#[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_divn() { + let mut a = FrRepr([0xb33fbaec482a283f, 0x997de0d3a88cb3df, 0x9af62d2a9a0e5525, 0x36003ab08de70da1]); + a.divn(0); + assert_eq!( + a, + FrRepr([0xb33fbaec482a283f, 0x997de0d3a88cb3df, 0x9af62d2a9a0e5525, 0x36003ab08de70da1]) + ); + a.divn(1); + assert_eq!( + a, + FrRepr([0xd99fdd762415141f, 0xccbef069d44659ef, 0xcd7b16954d072a92, 0x1b001d5846f386d0]) + ); + a.divn(50); + assert_eq!( + a, + FrRepr([0xbc1a7511967bf667, 0xc5a55341caa4b32f, 0x75611bce1b4335e, 0x6c0]) + ); + a.divn(130); + assert_eq!( + a, + FrRepr([0x1d5846f386d0cd7, 0x1b0, 0x0, 0x0]) + ); + a.divn(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 a borrow + let mut qplusone = FrRepr([0xffffffff00000001, 0x53bda402fffe5bfe, 0x3339d80809a1d805, 0x73eda753299d7d48]); + assert!(qplusone.sub_noborrow(&FrRepr([0xffffffff00000002, 0x53bda402fffe5bfe, 0x3339d80809a1d805, 0x73eda753299d7d48]))); + + // Subtracting x from x should produce no borrow + let mut x = FrRepr([0xffffffff00000001, 0x53bda402fffe5bfe, 0x3339d80809a1d805, 0x73eda753299d7d48]); + assert!(!x.sub_noborrow(&FrRepr([0xffffffff00000001, 0x53bda402fffe5bfe, 0x3339d80809a1d805, 0x73eda753299d7d48]))) +} + +#[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 a carry + let mut x = FrRepr([0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff]); + assert!(x.add_nocarry(&FrRepr::from(1))); + + // Adding 1 to r should not produce a carry + let mut x = FrRepr([0xffffffff00000001, 0x53bda402fffe5bfe, 0x3339d80809a1d805, 0x73eda753299d7d48]); + assert!(!x.add_nocarry(&FrRepr::from(1))); +} + +#[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 + }); +} + +#[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()); + } +} + +#[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 + }); +} + +#[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); + } +} + +#[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 + }); +} + +#[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); + } +} + +#[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 + }); +} + + +#[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); + } +} + +#[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() + }); +} + +#[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()); + } +} + +#[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 + }); +} + + +#[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); + } + } +} + +#[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() + }); +} + +#[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); + } +} + +#[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]) + }); +} + +#[test] +fn test_fr_repr_debug() { + 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_debug() { + 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); +} diff --git a/src/bls12_381/mod.rs b/src/bls12_381/mod.rs new file mode 100644 index 0000000..43290fd --- /dev/null +++ b/src/bls12_381/mod.rs @@ -0,0 +1,459 @@ +mod fq; +mod fr; +mod fq2; +mod fq6; +mod fq12; +mod ec; + +pub use self::fr::{Fr, FrRepr}; +pub use self::fq::{Fq, FqRepr}; +pub use self::fq2::Fq2; +pub use self::fq12::Fq12; +pub use self::ec::{G1, G2, G1Affine, G2Affine, G1Prepared, G2Prepared}; + +use super::{Engine, CurveAffine, Field, BitIterator}; + +// The BLS parameter x for BLS12-381 is -0xd201000000010000 +const BLS_X: u64 = 0xd201000000010000; +const BLS_X_IS_NEGATIVE: bool = true; + +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::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); + } + + f + } + + fn final_exponentiation(r: &Fq12) -> Option { + let mut f1 = *r; + f1.unitary_inverse(); + + 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.unitary_inverse(); + } + } + + 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.unitary_inverse(); + y1.mul_assign(&y3); + y1.unitary_inverse(); + y1.mul_assign(&y2); + y2 = y1; + exp_by_x(&mut y2, x); + y3 = y2; + exp_by_x(&mut y3, x); + y1.unitary_inverse(); + y3.mul_assign(&y1); + y1.unitary_inverse(); + 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: coeffs, + infinity: false + } + } +} + +#[test] +fn bls12_engine_tests() { + ::tests::engine::engine_tests::(); +} + +#[cfg(test)] +use rand::{Rand, SeedableRng, XorShiftRng}; + +#[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/src/lib.rs b/src/lib.rs new file mode 100644 index 0000000..ebdfa73 --- /dev/null +++ b/src/lib.rs @@ -0,0 +1,409 @@ +#![feature(i128_type)] + +#![cfg_attr(test, feature(test))] +#[cfg(test)] +extern crate test; + +extern crate rand; + +#[cfg(test)] +pub mod tests; + +pub mod bls12_381; + +use std::fmt; + +/// An "engine" is a collection of types (fields, elliptic curve groups, etc.) +/// with well-defined relationships. In particular, the G1/G2 curve groups are +/// of prime order `r`, and are equipped with a bilinear pairing function. +pub trait Engine { + /// This is the scalar field of the G1/G2 groups. + type Fr: PrimeField; + + /// The projective representation of an element in G1. + type G1: CurveProjective + From; + + /// The affine representation of an element in G1. + type G1Affine: CurveAffine + From; + + /// The projective representation of an element in G2. + type G2: CurveProjective + From; + + /// The affine representation of an element in G2. + type G2Affine: CurveAffine + 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::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 + + rand::Rand + + 'static +{ + type Scalar: PrimeField; + 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 to_affine(&self) -> Self::Affine; +} + +/// 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 + + PartialEq + + Eq + + 'static +{ + type Scalar: PrimeField; + type Base: SqrtField; + type Projective: CurveProjective; + type Prepared: Clone + Send + Sync + 'static; + + /// 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; + + /// Determines if this point is on the curve and in the correct subgroup. + fn is_valid(&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; + + /// Converts this element into its affine representation. + fn to_projective(&self) -> Self::Projective; +} + +/// This trait represents an element of a field. +pub trait Field: Sized + + Eq + + Copy + + Clone + + Send + + Sync + + fmt::Debug + + '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(); + + for i in BitIterator::new(exp) { + res.square(); + 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 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 + + fmt::Debug + + 'static + + rand::Rand + + AsRef<[u64]> + + From +{ + /// Subtract another reprensetation from this one, returning the borrow bit. + fn sub_noborrow(&mut self, other: &Self) -> bool; + + /// Add another representation to this one, returning the carry bit. + fn add_nocarry(&mut self, other: &Self) -> bool; + + /// Compute the number of bits needed to encode this number. + 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 divn(&mut self, amt: usize); + + /// Performs a leftwise bitshift of this number, effectively multiplying + /// it by 2. Overflow is ignored. + fn mul2(&mut self); +} + +/// 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; + + /// Convert this prime field element into a biginteger representation. + fn from_repr(Self::Repr) -> Result; + + /// Convert a biginteger reprensentation 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; + + /// Returns how many bits are needed to represent an element of this + /// field. + fn num_bits() -> u32; + + /// Returns how many bits of information can be reliably stored in the + /// field element. + fn capacity() -> u32; + + /// Returns the multiplicative generator of `char()` - 1 order. This element + /// must also be quadratic nonresidue. + fn multiplicative_generator() -> Self; + + /// Returns s such that 2^s * t = `char()` - 1 with t odd. + fn s() -> usize; + + /// Returns the 2^s root of unity computed by exponentiating the `multiplicative_generator()` + /// by t. + fn root_of_unity() -> Self; +} + +pub struct BitIterator { + t: E, + n: usize +} + +impl> BitIterator { + pub fn new(t: E) -> Self { + let n = t.as_ref().len() * 64; + + BitIterator { + t: t, + n: 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()); +} + +/// Calculate a - b - borrow, returning the result and modifying +/// the borrow value. +#[inline(always)] +pub(crate) fn sbb(a: u64, b: u64, borrow: &mut u64) -> u64 { + let tmp = (1u128 << 64) + (a as u128) - (b as u128) - (*borrow as u128); + + *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(crate) fn adc(a: u64, b: u64, carry: &mut u64) -> u64 { + let tmp = (a as u128) + (b as u128) + (*carry as u128); + + *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(crate) fn mac_with_carry(a: u64, b: u64, c: u64, carry: &mut u64) -> u64 { + let tmp = (a as u128) + (b as u128) * (c as u128) + (*carry as u128); + + *carry = (tmp >> 64) as u64; + + tmp as u64 +} diff --git a/src/tests/curve.rs b/src/tests/curve.rs new file mode 100644 index 0000000..9e7c8dd --- /dev/null +++ b/src/tests/curve.rs @@ -0,0 +1,267 @@ +use rand::{SeedableRng, XorShiftRng, Rand}; + +use ::{CurveProjective, CurveAffine, 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.to_affine()); + + assert_eq!(z, z2); + assert_eq!(z, r); + } + + // Transformations + { + let a = G::rand(&mut rng); + let b = a.to_affine().to_projective(); + let c = a.to_affine().to_projective().to_affine().to_projective(); + assert_eq!(a, b); + assert_eq!(b, c); + } + + random_addition_tests::(); + random_multiplication_tests::(); + random_doubling_tests::(); + random_negation_tests::(); + random_transformation_tests::(); +} + +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.to_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.to_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.to_affine(); + let b_affine = b.to_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.to_affine(); + let b_affine = b.to_affine(); + let c_affine = c.to_affine(); + + // a + a should equal the doubling + { + let mut aplusa = a; + aplusa.add_assign(&a); + + let mut aplusamixed = a; + aplusamixed.add_assign_mixed(&a.to_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.to_projective(); + tmp[3].add_assign_mixed(&b_affine); + tmp[3].add_assign_mixed(&c_affine); + + // a + (b + c) + tmp[4] = b_affine.to_projective(); + tmp[4].add_assign_mixed(&c_affine); + tmp[4].add_assign_mixed(&a_affine); + + // (a + c) + b + tmp[5] = a_affine.to_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].to_affine(), tmp[j].to_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.to_affine(); + let g_projective = g_affine.to_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].to_affine().to_projective(); + } + + let expected_v = v.iter().map(|v| v.to_affine().to_projective()).collect::>(); + G::batch_normalization(&mut v); + + for i in &v { + assert!(i.is_normalized()); + } + + assert_eq!(v, expected_v); + } +} diff --git a/src/tests/engine.rs b/src/tests/engine.rs new file mode 100644 index 0000000..5e2c07b --- /dev/null +++ b/src/tests/engine.rs @@ -0,0 +1,120 @@ +use rand::{SeedableRng, XorShiftRng, Rand}; + +use ::{Engine, CurveProjective, CurveAffine, Field, PrimeField}; + +pub fn engine_tests() +{ + let mut rng = XorShiftRng::from_seed([0x5dbe6259, 0x8d313d76, 0x3237db17, 0xe5bc0654]); + + for _ in 0..1000 { + let z1 = E::G1Affine::zero().prepare(); + let z2 = E::G2Affine::zero().prepare(); + + let a = E::G1::rand(&mut rng).to_affine().prepare(); + let b = E::G2::rand(&mut rng).to_affine().prepare(); + let c = E::G1::rand(&mut rng).to_affine().prepare(); + let d = E::G2::rand(&mut rng).to_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.to_affine().prepare(); + let b = b.to_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.to_affine().prepare(); + let b = b.to_affine().prepare(); + let c = c.to_affine().prepare(); + let d = d.to_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/src/tests/field.rs b/src/tests/field.rs new file mode 100644 index 0000000..dbc9f8c --- /dev/null +++ b/src/tests/field.rs @@ -0,0 +1,229 @@ +use rand::{Rng, SeedableRng, XorShiftRng}; +use ::{SqrtField, Field}; + +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(); + + 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(); + 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); + } +} + +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/src/tests/mod.rs b/src/tests/mod.rs new file mode 100644 index 0000000..2cb00a7 --- /dev/null +++ b/src/tests/mod.rs @@ -0,0 +1,3 @@ +pub mod curve; +pub mod field; +pub mod engine;