diff --git a/bls12_381/.github/workflows/ci.yml b/bls12_381/.github/workflows/ci.yml new file mode 100644 index 0000000..39066db --- /dev/null +++ b/bls12_381/.github/workflows/ci.yml @@ -0,0 +1,95 @@ +name: CI checks + +on: [push, pull_request] + +jobs: + lint: + name: Lint + runs-on: ubuntu-latest + + steps: + - uses: actions/checkout@v1 + - uses: actions-rs/toolchain@v1 + with: + toolchain: 1.36.0 + override: true + + # Ensure all code has been formatted with rustfmt + - run: rustup component add rustfmt + - name: Check formatting + uses: actions-rs/cargo@v1 + with: + command: fmt + args: -- --check --color always + + test: + name: Test on ${{ matrix.os }} + runs-on: ${{ matrix.os }} + strategy: + matrix: + os: [ubuntu-latest, windows-latest, macOS-latest] + + steps: + - uses: actions/checkout@v1 + - uses: actions-rs/toolchain@v1 + with: + toolchain: 1.36.0 + override: true + - name: cargo fetch + uses: actions-rs/cargo@v1 + with: + command: fetch + - name: Build tests + uses: actions-rs/cargo@v1 + with: + command: build + args: --verbose --release --tests + - name: Run tests + uses: actions-rs/cargo@v1 + with: + command: test + args: --verbose --release + + no-std: + name: Check no-std compatibility + runs-on: ubuntu-latest + + steps: + - uses: actions/checkout@v1 + - uses: actions-rs/toolchain@v1 + with: + toolchain: 1.36.0 + override: true + - run: rustup target add thumbv6m-none-eabi + - name: cargo fetch + uses: actions-rs/cargo@v1 + with: + command: fetch + - name: Build + uses: actions-rs/cargo@v1 + with: + command: build + args: --verbose --target thumbv6m-none-eabi --no-default-features --features groups,pairings + + doc-links: + name: Nightly lint + runs-on: ubuntu-latest + + steps: + - uses: actions/checkout@v1 + - uses: actions-rs/toolchain@v1 + with: + toolchain: nightly + override: true + - name: cargo fetch + uses: actions-rs/cargo@v1 + with: + command: fetch + + # Ensure intra-documentation links all resolve correctly + # Requires #![deny(intra_doc_link_resolution_failure)] in crate. + - name: Check intra-doc links + uses: actions-rs/cargo@v1 + with: + command: doc + args: --document-private-items diff --git a/bls12_381/.gitignore b/bls12_381/.gitignore new file mode 100644 index 0000000..2f88dba --- /dev/null +++ b/bls12_381/.gitignore @@ -0,0 +1,3 @@ +/target +**/*.rs.bk +Cargo.lock \ No newline at end of file diff --git a/bls12_381/COPYRIGHT b/bls12_381/COPYRIGHT new file mode 100644 index 0000000..7764b86 --- /dev/null +++ b/bls12_381/COPYRIGHT @@ -0,0 +1,14 @@ +Copyrights in the "bls12_381" library are retained by their contributors. No +copyright assignment is required to contribute to the "bls12_381" library. + +The "bls12_381" library is licensed under either of + + * Apache License, Version 2.0, (see ./LICENSE-APACHE or http://www.apache.org/licenses/LICENSE-2.0) + * MIT license (see ./LICENSE-MIT or http://opensource.org/licenses/MIT) + +at your option. + +Unless you explicitly state otherwise, any contribution intentionally +submitted for inclusion in the work by you, as defined in the Apache-2.0 +license, shall be dual licensed as above, without any additional terms or +conditions. diff --git a/bls12_381/Cargo.toml b/bls12_381/Cargo.toml new file mode 100644 index 0000000..0bfb0d4 --- /dev/null +++ b/bls12_381/Cargo.toml @@ -0,0 +1,32 @@ +[package] +authors = ["Sean Bowe "] +description = "Implementation of the BLS12-381 pairing-friendly elliptic curve construction" +documentation = "https://docs.rs/bls12_381/" +homepage = "https://github.com/zkcrypto/bls12_381" +license = "MIT/Apache-2.0" +name = "bls12_381" +repository = "https://github.com/zkcrypto/bls12_381" +version = "0.1.0" +edition = "2018" + +[package.metadata.docs.rs] +rustdoc-args = [ "--html-in-header", "katex-header.html" ] + +[dev-dependencies] +criterion = "0.2.11" + +[[bench]] +name = "groups" +harness = false +required-features = ["groups"] + +[dependencies.subtle] +version = "2.2.1" +default-features = false + +[features] +default = ["groups", "pairings", "alloc"] +groups = [] +pairings = ["groups"] +alloc = [] +nightly = ["subtle/nightly"] diff --git a/bls12_381/LICENSE-APACHE b/bls12_381/LICENSE-APACHE new file mode 100644 index 0000000..16fe87b --- /dev/null +++ b/bls12_381/LICENSE-APACHE @@ -0,0 +1,201 @@ + Apache License + Version 2.0, January 2004 + http://www.apache.org/licenses/ + +TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + +1. Definitions. + + "License" shall mean the terms and conditions for use, reproduction, + and distribution as defined by Sections 1 through 9 of this document. + + "Licensor" shall mean the copyright owner or entity authorized by + the copyright owner that is granting the License. + + "Legal Entity" shall mean the union of the acting entity and all + other entities that control, are controlled by, or are under common + control with that entity. For the purposes of this definition, + "control" means (i) the power, direct or indirect, to cause the + direction or management of such entity, whether by contract or + otherwise, or (ii) ownership of fifty percent (50%) or more of the + outstanding shares, or (iii) beneficial ownership of such entity. + + "You" (or "Your") shall mean an individual or Legal Entity + exercising permissions granted by this License. + + "Source" form shall mean the preferred form for making modifications, + including but not limited to software source code, documentation + source, and configuration files. + + "Object" form shall mean any form resulting from mechanical + transformation or translation of a Source form, including but + not limited to compiled object code, generated documentation, + and conversions to other media types. + + "Work" shall mean the work of authorship, whether in Source or + Object form, made available under the License, as indicated by a + copyright notice that is included in or attached to the work + (an example is provided in the Appendix below). + + "Derivative Works" shall mean any work, whether in Source or Object + form, that is based on (or derived from) the Work and for which the + editorial revisions, annotations, elaborations, or other modifications + represent, as a whole, an original work of authorship. For the purposes + of this License, Derivative Works shall not include works that remain + separable from, or merely link (or bind by name) to the interfaces of, + the Work and Derivative Works thereof. + + "Contribution" shall mean any work of authorship, including + the original version of the Work and any modifications or additions + to that Work or Derivative Works thereof, that is intentionally + submitted to Licensor for inclusion in the Work by the copyright owner + or by an individual or Legal Entity authorized to submit on behalf of + the copyright owner. For the purposes of this definition, "submitted" + means any form of electronic, verbal, or written communication sent + to the Licensor or its representatives, including but not limited to + communication on electronic mailing lists, source code control systems, + and issue tracking systems that are managed by, or on behalf of, the + Licensor for the purpose of discussing and improving the Work, but + excluding communication that is conspicuously marked or otherwise + designated in writing by the copyright owner as "Not a Contribution." + + "Contributor" shall mean Licensor and any individual or Legal Entity + on behalf of whom a Contribution has been received by Licensor and + subsequently incorporated within the Work. + +2. Grant of Copyright License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + copyright license to reproduce, prepare Derivative Works of, + publicly display, publicly perform, sublicense, and distribute the + Work and such Derivative Works in Source or Object form. + +3. Grant of Patent License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + (except as stated in this section) patent license to make, have made, + use, offer to sell, sell, import, and otherwise transfer the Work, + where such license applies only to those patent claims licensable + by such Contributor that are necessarily infringed by their + Contribution(s) alone or by combination of their Contribution(s) + with the Work to which such Contribution(s) was submitted. If You + institute patent litigation against any entity (including a + cross-claim or counterclaim in a lawsuit) alleging that the Work + or a Contribution incorporated within the Work constitutes direct + or contributory patent infringement, then any patent licenses + granted to You under this License for that Work shall terminate + as of the date such litigation is filed. + +4. Redistribution. You may reproduce and distribute copies of the + Work or Derivative Works thereof in any medium, with or without + modifications, and in Source or Object form, provided that You + meet the following conditions: + + (a) You must give any other recipients of the Work or + Derivative Works a copy of this License; and + + (b) You must cause any modified files to carry prominent notices + stating that You changed the files; and + + (c) You must retain, in the Source form of any Derivative Works + that You distribute, all copyright, patent, trademark, and + attribution notices from the Source form of the Work, + excluding those notices that do not pertain to any part of + the Derivative Works; and + + (d) If the Work includes a "NOTICE" text file as part of its + distribution, then any Derivative Works that You distribute must + include a readable copy of the attribution notices contained + within such NOTICE file, excluding those notices that do not + pertain to any part of the Derivative Works, in at least one + of the following places: within a NOTICE text file distributed + as part of the Derivative Works; within the Source form or + documentation, if provided along with the Derivative Works; or, + within a display generated by the Derivative Works, if and + wherever such third-party notices normally appear. The contents + of the NOTICE file are for informational purposes only and + do not modify the License. You may add Your own attribution + notices within Derivative Works that You distribute, alongside + or as an addendum to the NOTICE text from the Work, provided + that such additional attribution notices cannot be construed + as modifying the License. + + You may add Your own copyright statement to Your modifications and + may provide additional or different license terms and conditions + for use, reproduction, or distribution of Your modifications, or + for any such Derivative Works as a whole, provided Your use, + reproduction, and distribution of the Work otherwise complies with + the conditions stated in this License. + +5. Submission of Contributions. Unless You explicitly state otherwise, + any Contribution intentionally submitted for inclusion in the Work + by You to the Licensor shall be under the terms and conditions of + this License, without any additional terms or conditions. + Notwithstanding the above, nothing herein shall supersede or modify + the terms of any separate license agreement you may have executed + with Licensor regarding such Contributions. + +6. Trademarks. This License does not grant permission to use the trade + names, trademarks, service marks, or product names of the Licensor, + except as required for reasonable and customary use in describing the + origin of the Work and reproducing the content of the NOTICE file. + +7. Disclaimer of Warranty. Unless required by applicable law or + agreed to in writing, Licensor provides the Work (and each + Contributor provides its Contributions) on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied, including, without limitation, any warranties or conditions + of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A + PARTICULAR PURPOSE. You are solely responsible for determining the + appropriateness of using or redistributing the Work and assume any + risks associated with Your exercise of permissions under this License. + +8. Limitation of Liability. In no event and under no legal theory, + whether in tort (including negligence), contract, or otherwise, + unless required by applicable law (such as deliberate and grossly + negligent acts) or agreed to in writing, shall any Contributor be + liable to You for damages, including any direct, indirect, special, + incidental, or consequential damages of any character arising as a + result of this License or out of the use or inability to use the + Work (including but not limited to damages for loss of goodwill, + work stoppage, computer failure or malfunction, or any and all + other commercial damages or losses), even if such Contributor + has been advised of the possibility of such damages. + +9. Accepting Warranty or Additional Liability. While redistributing + the Work or Derivative Works thereof, You may choose to offer, + and charge a fee for, acceptance of support, warranty, indemnity, + or other liability obligations and/or rights consistent with this + License. However, in accepting such obligations, You may act only + on Your own behalf and on Your sole responsibility, not on behalf + of any other Contributor, and only if You agree to indemnify, + defend, and hold each Contributor harmless for any liability + incurred by, or claims asserted against, such Contributor by reason + of your accepting any such warranty or additional liability. + +END OF TERMS AND CONDITIONS + +APPENDIX: How to apply the Apache License to your work. + + To apply the Apache License to your work, attach the following + boilerplate notice, with the fields enclosed by brackets "[]" + replaced with your own identifying information. (Don't include + the brackets!) The text should be enclosed in the appropriate + comment syntax for the file format. We also recommend that a + file or class name and description of purpose be included on the + same "printed page" as the copyright notice for easier + identification within third-party archives. + +Copyright [yyyy] [name of copyright owner] + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. diff --git a/bls12_381/LICENSE-MIT b/bls12_381/LICENSE-MIT new file mode 100644 index 0000000..31aa793 --- /dev/null +++ b/bls12_381/LICENSE-MIT @@ -0,0 +1,23 @@ +Permission is hereby granted, free of charge, to any +person obtaining a copy of this software and associated +documentation files (the "Software"), to deal in the +Software without restriction, including without +limitation the rights to use, copy, modify, merge, +publish, distribute, sublicense, and/or sell copies of +the Software, and to permit persons to whom the Software +is furnished to do so, subject to the following +conditions: + +The above copyright notice and this permission notice +shall be included in all copies or substantial portions +of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF +ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED +TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A +PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT +SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY +CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION +OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR +IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER +DEALINGS IN THE SOFTWARE. diff --git a/bls12_381/README.md b/bls12_381/README.md new file mode 100644 index 0000000..ba61f30 --- /dev/null +++ b/bls12_381/README.md @@ -0,0 +1,63 @@ +# bls12_381 [![Crates.io](https://img.shields.io/crates/v/bls12_381.svg)](https://crates.io/crates/bls12_381) # + +This crate provides an implementation of the BLS12-381 pairing-friendly elliptic curve construction. + +* **This implementation has not been reviewed or audited. Use at your own risk.** +* This implementation targets Rust `1.36` or later. +* This implementation does not require the Rust standard library. +* All operations are constant time unless explicitly noted. + +## Features + +* `groups` (on by default): Enables APIs for performing group arithmetic with G1, G2, and GT. +* `pairings` (on by default): Enables some APIs for performing pairings. +* `alloc` (on by default): Enables APIs that require an allocator; these include pairing optimizations. +* `nightly`: Enables `subtle/nightly` which tries to prevent compiler optimizations that could jeopardize constant time operations. Requires the nightly Rust compiler. + +## [Documentation](https://docs.rs/bls12_381) + +## Curve Description + +BLS12-381 is a pairing-friendly elliptic curve construction from the [BLS family](https://eprint.iacr.org/2002/088), with embedding degree 12. It is built over a 381-bit prime field `GF(p)` with... + +* z = `-0xd201000000010000` +* p = (z - 1)2(z4 - z2 + 1) / 3 + z + * = `0x1a0111ea397fe69a4b1ba7b6434bacd764774b84f38512bf6730d2a0f6b0f6241eabfffeb153ffffb9feffffffffaaab` +* q = z4 - z2 + 1 + * = `0x73eda753299d7d483339d80809a1d80553bda402fffe5bfeffffffff00000001` + +... yielding two **source groups** G1 and G2, each of 255-bit prime order `q`, such that an efficiently computable non-degenerate bilinear pairing function `e` exists into a third **target group** GT. Specifically, G1 is the `q`-order subgroup of E(Fp) : y2 = x3 + 4 and G2 is the `q`-order subgroup of E'(Fp2) : y2 = x3 + 4(u + 1) where the extention field Fp2 is defined as Fp(u) / (u2 + 1). + +BLS12-381 is chosen so that `z` has small Hamming weight (to improve pairing performance) and also so that `GF(q)` has a large 232 primitive root of unity for performing radix-2 fast Fourier transforms for efficient multi-point evaluation and interpolation. It is also chosen so that it exists in a particularly efficient and rigid subfamily of BLS12 curves. + +### Curve Security + +Pairing-friendly elliptic curve constructions are (necessarily) less secure than conventional elliptic curves due to their small "embedding degree". Given a small enough embedding degree, the pairing function itself would allow for a break in DLP hardness if it projected into a weak target group, as weaknesses in this target group are immediately translated into weaknesses in the source group. + +In order to achieve reasonable security without an unreasonably expensive pairing function, a careful choice of embedding degree, base field characteristic and prime subgroup order must be made. BLS12-381 uses an embedding degree of 12 to ensure fast pairing performance but a choice of a 381-bit base field characteristic to yeild a 255-bit subgroup order (for protection against [Pollard's rho algorithm](https://en.wikipedia.org/wiki/Pollard%27s_rho_algorithm)) while reaching close to a 128-bit security level. + +There are [known optimizations](https://ellipticnews.wordpress.com/2016/05/02/kim-barbulescu-variant-of-the-number-field-sieve-to-compute-discrete-logarithms-in-finite-fields/) of the [Number Field Sieve algorithm](https://en.wikipedia.org/wiki/General_number_field_sieve) which could be used to weaken DLP security in the target group by taking advantage of its structure, as it is a multiplicative subgroup of a low-degree extension field. However, these attacks require an (as of yet unknown) efficient algorithm for scanning a large space of polynomials. Even if the attack were practical it would only reduce security to roughly 117 to 120 bits. (This contrasts with 254-bit BN curves which usually have less than 100 bits of security in the same situation.) + +### Alternative Curves + +Applications may wish to exchange pairing performance and/or G2 performance by using BLS24 or KSS16 curves which conservatively target 128-bit security. In applications that need cycles of elliptic curves for e.g. arbitrary proof composition, MNT6/MNT4 curve cycles are known that target the 128-bit security level. In applications that only need fixed-depth proof composition, curves of this form have been constructed as part of Zexe. + +## Acknowledgements + +Please see `Cargo.toml` for a list of primary authors of this codebase. + +## 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/bls12_381/RELEASES.md b/bls12_381/RELEASES.md new file mode 100644 index 0000000..69afd52 --- /dev/null +++ b/bls12_381/RELEASES.md @@ -0,0 +1,3 @@ +# 0.1.0 + +Initial release. diff --git a/bls12_381/benches/groups.rs b/bls12_381/benches/groups.rs new file mode 100644 index 0000000..87c80d0 --- /dev/null +++ b/bls12_381/benches/groups.rs @@ -0,0 +1,170 @@ +#[macro_use] +extern crate criterion; + +extern crate bls12_381; +use bls12_381::*; + +use criterion::{black_box, Criterion}; + +fn criterion_benchmark(c: &mut Criterion) { + // Pairings + { + let g = G1Affine::generator(); + let h = G2Affine::generator(); + c.bench_function("full pairing", move |b| { + b.iter(|| pairing(black_box(&g), black_box(&h))) + }); + c.bench_function("G2 preparation for pairing", move |b| { + b.iter(|| G2Prepared::from(h)) + }); + let prep = G2Prepared::from(h); + c.bench_function("miller loop for pairing", move |b| { + b.iter(|| multi_miller_loop(&[(&g, &prep)])) + }); + let prep = G2Prepared::from(h); + let r = multi_miller_loop(&[(&g, &prep)]); + c.bench_function("final exponentiation for pairing", move |b| { + b.iter(|| r.final_exponentiation()) + }); + } + // G1Affine + { + let name = "G1Affine"; + let a = G1Affine::generator(); + let s = Scalar::from_raw([1, 2, 3, 4]); + let compressed = [0u8; 48]; + let uncompressed = [0u8; 96]; + c.bench_function(&format!("{} check on curve", name), move |b| { + b.iter(|| black_box(a).is_on_curve()) + }); + c.bench_function(&format!("{} check equality", name), move |b| { + b.iter(|| black_box(a) == black_box(a)) + }); + c.bench_function(&format!("{} scalar multiplication", name), move |b| { + b.iter(|| black_box(a) * black_box(s)) + }); + c.bench_function(&format!("{} subgroup check", name), move |b| { + b.iter(|| black_box(a).is_torsion_free()) + }); + c.bench_function( + &format!("{} deserialize compressed point", name), + move |b| b.iter(|| G1Affine::from_compressed(black_box(&compressed))), + ); + c.bench_function( + &format!("{} deserialize uncompressed point", name), + move |b| b.iter(|| G1Affine::from_uncompressed(black_box(&uncompressed))), + ); + } + + // G1Projective + { + let name = "G1Projective"; + let a = G1Projective::generator(); + let a_affine = G1Affine::generator(); + let s = Scalar::from_raw([1, 2, 3, 4]); + + const N: usize = 10000; + let v = vec![G1Projective::generator(); N]; + let mut q = vec![G1Affine::identity(); N]; + + c.bench_function(&format!("{} check on curve", name), move |b| { + b.iter(|| black_box(a).is_on_curve()) + }); + c.bench_function(&format!("{} check equality", name), move |b| { + b.iter(|| black_box(a) == black_box(a)) + }); + c.bench_function(&format!("{} to affine", name), move |b| { + b.iter(|| G1Affine::from(black_box(a))) + }); + c.bench_function(&format!("{} doubling", name), move |b| { + b.iter(|| black_box(a).double()) + }); + c.bench_function(&format!("{} addition", name), move |b| { + b.iter(|| black_box(a).add(&a)) + }); + c.bench_function(&format!("{} mixed addition", name), move |b| { + b.iter(|| black_box(a).add_mixed(&a_affine)) + }); + c.bench_function(&format!("{} scalar multiplication", name), move |b| { + b.iter(|| black_box(a) * black_box(s)) + }); + c.bench_function(&format!("{} batch to affine n={}", name, N), move |b| { + b.iter(|| { + G1Projective::batch_normalize(black_box(&v), black_box(&mut q)); + black_box(&q)[0] + }) + }); + } + + // G2Affine + { + let name = "G2Affine"; + let a = G2Affine::generator(); + let s = Scalar::from_raw([1, 2, 3, 4]); + let compressed = [0u8; 96]; + let uncompressed = [0u8; 192]; + c.bench_function(&format!("{} check on curve", name), move |b| { + b.iter(|| black_box(a).is_on_curve()) + }); + c.bench_function(&format!("{} check equality", name), move |b| { + b.iter(|| black_box(a) == black_box(a)) + }); + c.bench_function(&format!("{} scalar multiplication", name), move |b| { + b.iter(|| black_box(a) * black_box(s)) + }); + c.bench_function(&format!("{} subgroup check", name), move |b| { + b.iter(|| black_box(a).is_torsion_free()) + }); + c.bench_function( + &format!("{} deserialize compressed point", name), + move |b| b.iter(|| G2Affine::from_compressed(black_box(&compressed))), + ); + c.bench_function( + &format!("{} deserialize uncompressed point", name), + move |b| b.iter(|| G2Affine::from_uncompressed(black_box(&uncompressed))), + ); + } + + // G2Projective + { + let name = "G2Projective"; + let a = G2Projective::generator(); + let a_affine = G2Affine::generator(); + let s = Scalar::from_raw([1, 2, 3, 4]); + + const N: usize = 10000; + let v = vec![G2Projective::generator(); N]; + let mut q = vec![G2Affine::identity(); N]; + + c.bench_function(&format!("{} check on curve", name), move |b| { + b.iter(|| black_box(a).is_on_curve()) + }); + c.bench_function(&format!("{} check equality", name), move |b| { + b.iter(|| black_box(a) == black_box(a)) + }); + c.bench_function(&format!("{} to affine", name), move |b| { + b.iter(|| G2Affine::from(black_box(a))) + }); + c.bench_function(&format!("{} doubling", name), move |b| { + b.iter(|| black_box(a).double()) + }); + c.bench_function(&format!("{} addition", name), move |b| { + b.iter(|| black_box(a).add(&a)) + }); + c.bench_function(&format!("{} mixed addition", name), move |b| { + b.iter(|| black_box(a).add_mixed(&a_affine)) + }); + c.bench_function(&format!("{} scalar multiplication", name), move |b| { + b.iter(|| black_box(a) * black_box(s)) + }); + c.bench_function(&format!("{} batch to affine n={}", name, N), move |b| { + b.iter(|| { + G2Projective::batch_normalize(black_box(&v), black_box(&mut q)); + black_box(&q)[0] + }) + }); + } +} + +criterion_group!(benches, criterion_benchmark); +criterion_main!(benches); diff --git a/bls12_381/katex-header.html b/bls12_381/katex-header.html new file mode 100644 index 0000000..98e8590 --- /dev/null +++ b/bls12_381/katex-header.html @@ -0,0 +1,15 @@ + + + + \ No newline at end of file diff --git a/bls12_381/rust-toolchain b/bls12_381/rust-toolchain new file mode 100644 index 0000000..d70132e --- /dev/null +++ b/bls12_381/rust-toolchain @@ -0,0 +1 @@ +1.36.0 \ No newline at end of file diff --git a/bls12_381/src/fp.rs b/bls12_381/src/fp.rs new file mode 100644 index 0000000..1a25fdf --- /dev/null +++ b/bls12_381/src/fp.rs @@ -0,0 +1,859 @@ +//! This module provides an implementation of the BLS12-381 base field `GF(p)` +//! where `p = 0x1a0111ea397fe69a4b1ba7b6434bacd764774b84f38512bf6730d2a0f6b0f6241eabfffeb153ffffb9feffffffffaaab` + +use core::convert::TryFrom; +use core::fmt; +use core::ops::{Add, AddAssign, Mul, MulAssign, Neg, Sub, SubAssign}; + +use subtle::{Choice, ConditionallySelectable, ConstantTimeEq, CtOption}; + +use crate::util::{adc, mac, sbb}; + +// The internal representation of this type is six 64-bit unsigned +// integers in little-endian order. `Fp` values are always in +// Montgomery form; i.e., Scalar(a) = aR mod p, with R = 2^384. +#[derive(Copy, Clone)] +pub struct Fp([u64; 6]); + +impl fmt::Debug for Fp { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + let tmp = self.to_bytes(); + write!(f, "0x")?; + for &b in tmp.iter() { + write!(f, "{:02x}", b)?; + } + Ok(()) + } +} + +impl Default for Fp { + fn default() -> Self { + Fp::zero() + } +} + +impl ConstantTimeEq for Fp { + fn ct_eq(&self, other: &Self) -> Choice { + self.0[0].ct_eq(&other.0[0]) + & self.0[1].ct_eq(&other.0[1]) + & self.0[2].ct_eq(&other.0[2]) + & self.0[3].ct_eq(&other.0[3]) + & self.0[4].ct_eq(&other.0[4]) + & self.0[5].ct_eq(&other.0[5]) + } +} + +impl Eq for Fp {} +impl PartialEq for Fp { + #[inline] + fn eq(&self, other: &Self) -> bool { + self.ct_eq(other).unwrap_u8() == 1 + } +} + +impl ConditionallySelectable for Fp { + fn conditional_select(a: &Self, b: &Self, choice: Choice) -> Self { + Fp([ + u64::conditional_select(&a.0[0], &b.0[0], choice), + u64::conditional_select(&a.0[1], &b.0[1], choice), + u64::conditional_select(&a.0[2], &b.0[2], choice), + u64::conditional_select(&a.0[3], &b.0[3], choice), + u64::conditional_select(&a.0[4], &b.0[4], choice), + u64::conditional_select(&a.0[5], &b.0[5], choice), + ]) + } +} + +/// p = 4002409555221667393417789825735904156556882819939007885332058136124031650490837864442687629129015664037894272559787 +const MODULUS: [u64; 6] = [ + 0xb9feffffffffaaab, + 0x1eabfffeb153ffff, + 0x6730d2a0f6b0f624, + 0x64774b84f38512bf, + 0x4b1ba7b6434bacd7, + 0x1a0111ea397fe69a, +]; + +/// INV = -(p^{-1} mod 2^64) mod 2^64 +const INV: u64 = 0x89f3fffcfffcfffd; + +/// R = 2^384 mod p +const R: Fp = Fp([ + 0x760900000002fffd, + 0xebf4000bc40c0002, + 0x5f48985753c758ba, + 0x77ce585370525745, + 0x5c071a97a256ec6d, + 0x15f65ec3fa80e493, +]); + +/// R2 = 2^(384*2) mod p +const R2: Fp = Fp([ + 0xf4df1f341c341746, + 0xa76e6a609d104f1, + 0x8de5476c4c95b6d5, + 0x67eb88a9939d83c0, + 0x9a793e85b519952d, + 0x11988fe592cae3aa, +]); + +impl<'a> Neg for &'a Fp { + type Output = Fp; + + #[inline] + fn neg(self) -> Fp { + self.neg() + } +} + +impl Neg for Fp { + type Output = Fp; + + #[inline] + fn neg(self) -> Fp { + -&self + } +} + +impl<'a, 'b> Sub<&'b Fp> for &'a Fp { + type Output = Fp; + + #[inline] + fn sub(self, rhs: &'b Fp) -> Fp { + self.sub(rhs) + } +} + +impl<'a, 'b> Add<&'b Fp> for &'a Fp { + type Output = Fp; + + #[inline] + fn add(self, rhs: &'b Fp) -> Fp { + self.add(rhs) + } +} + +impl<'a, 'b> Mul<&'b Fp> for &'a Fp { + type Output = Fp; + + #[inline] + fn mul(self, rhs: &'b Fp) -> Fp { + self.mul(rhs) + } +} + +impl_binops_additive!(Fp, Fp); +impl_binops_multiplicative!(Fp, Fp); + +impl Fp { + /// Returns zero, the additive identity. + #[inline] + pub const fn zero() -> Fp { + Fp([0, 0, 0, 0, 0, 0]) + } + + /// Returns one, the multiplicative identity. + #[inline] + pub const fn one() -> Fp { + R + } + + pub fn is_zero(&self) -> Choice { + self.ct_eq(&Fp::zero()) + } + + /// Attempts to convert a little-endian byte representation of + /// a scalar into an `Fp`, failing if the input is not canonical. + pub fn from_bytes(bytes: &[u8; 48]) -> CtOption { + let mut tmp = Fp([0, 0, 0, 0, 0, 0]); + + tmp.0[5] = u64::from_be_bytes(<[u8; 8]>::try_from(&bytes[0..8]).unwrap()); + tmp.0[4] = u64::from_be_bytes(<[u8; 8]>::try_from(&bytes[8..16]).unwrap()); + tmp.0[3] = u64::from_be_bytes(<[u8; 8]>::try_from(&bytes[16..24]).unwrap()); + tmp.0[2] = u64::from_be_bytes(<[u8; 8]>::try_from(&bytes[24..32]).unwrap()); + tmp.0[1] = u64::from_be_bytes(<[u8; 8]>::try_from(&bytes[32..40]).unwrap()); + tmp.0[0] = u64::from_be_bytes(<[u8; 8]>::try_from(&bytes[40..48]).unwrap()); + + // Try to subtract the modulus + let (_, borrow) = sbb(tmp.0[0], MODULUS[0], 0); + let (_, borrow) = sbb(tmp.0[1], MODULUS[1], borrow); + let (_, borrow) = sbb(tmp.0[2], MODULUS[2], borrow); + let (_, borrow) = sbb(tmp.0[3], MODULUS[3], borrow); + let (_, borrow) = sbb(tmp.0[4], MODULUS[4], borrow); + let (_, borrow) = sbb(tmp.0[5], MODULUS[5], borrow); + + // If the element is smaller than MODULUS then the + // subtraction will underflow, producing a borrow value + // of 0xffff...ffff. Otherwise, it'll be zero. + let is_some = (borrow as u8) & 1; + + // Convert to Montgomery form by computing + // (a.R^0 * R^2) / R = a.R + tmp *= &R2; + + CtOption::new(tmp, Choice::from(is_some)) + } + + /// Converts an element of `Fp` into a byte representation in + /// big-endian byte order. + pub fn to_bytes(&self) -> [u8; 48] { + // Turn into canonical form by computing + // (a.R) / R = a + let tmp = Fp::montgomery_reduce( + self.0[0], self.0[1], self.0[2], self.0[3], self.0[4], self.0[5], 0, 0, 0, 0, 0, 0, + ); + + let mut res = [0; 48]; + res[0..8].copy_from_slice(&tmp.0[5].to_be_bytes()); + res[8..16].copy_from_slice(&tmp.0[4].to_be_bytes()); + res[16..24].copy_from_slice(&tmp.0[3].to_be_bytes()); + res[24..32].copy_from_slice(&tmp.0[2].to_be_bytes()); + res[32..40].copy_from_slice(&tmp.0[1].to_be_bytes()); + res[40..48].copy_from_slice(&tmp.0[0].to_be_bytes()); + + res + } + + /// Returns whether or not this element is strictly lexicographically + /// larger than its negation. + pub fn lexicographically_largest(&self) -> Choice { + // This can be determined by checking to see if the element is + // larger than (p - 1) // 2. If we subtract by ((p - 1) // 2) + 1 + // and there is no underflow, then the element must be larger than + // (p - 1) // 2. + + // First, because self is in Montgomery form we need to reduce it + let tmp = Fp::montgomery_reduce( + self.0[0], self.0[1], self.0[2], self.0[3], self.0[4], self.0[5], 0, 0, 0, 0, 0, 0, + ); + + let (_, borrow) = sbb(tmp.0[0], 0xdcff7fffffffd556, 0); + let (_, borrow) = sbb(tmp.0[1], 0x0f55ffff58a9ffff, borrow); + let (_, borrow) = sbb(tmp.0[2], 0xb39869507b587b12, borrow); + let (_, borrow) = sbb(tmp.0[3], 0xb23ba5c279c2895f, borrow); + let (_, borrow) = sbb(tmp.0[4], 0x258dd3db21a5d66b, borrow); + let (_, borrow) = sbb(tmp.0[5], 0x0d0088f51cbff34d, borrow); + + // If the element was smaller, the subtraction will underflow + // producing a borrow value of 0xffff...ffff, otherwise it will + // be zero. We create a Choice representing true if there was + // overflow (and so this element is not lexicographically larger + // than its negation) and then negate it. + + !Choice::from((borrow as u8) & 1) + } + + /// Constructs an element of `Fp` without checking that it is + /// canonical. + pub const fn from_raw_unchecked(v: [u64; 6]) -> Fp { + Fp(v) + } + + /// Although this is labeled "vartime", it is only + /// variable time with respect to the exponent. It + /// is also not exposed in the public API. + pub fn pow_vartime(&self, by: &[u64; 6]) -> Self { + let mut res = Self::one(); + for e in by.iter().rev() { + for i in (0..64).rev() { + res = res.square(); + + if ((*e >> i) & 1) == 1 { + res *= self; + } + } + } + res + } + + #[inline] + pub fn sqrt(&self) -> CtOption { + // We use Shank's method, as p = 3 (mod 4). This means + // we only need to exponentiate by (p+1)/4. This only + // works for elements that are actually quadratic residue, + // so we check that we got the correct result at the end. + + let sqrt = self.pow_vartime(&[ + 0xee7fbfffffffeaab, + 0x7aaffffac54ffff, + 0xd9cc34a83dac3d89, + 0xd91dd2e13ce144af, + 0x92c6e9ed90d2eb35, + 0x680447a8e5ff9a6, + ]); + + CtOption::new(sqrt, sqrt.square().ct_eq(self)) + } + + #[inline] + /// Computes the multiplicative inverse of this field + /// element, returning None in the case that this element + /// is zero. + pub fn invert(&self) -> CtOption { + // Exponentiate by p - 2 + let t = self.pow_vartime(&[ + 0xb9feffffffffaaa9, + 0x1eabfffeb153ffff, + 0x6730d2a0f6b0f624, + 0x64774b84f38512bf, + 0x4b1ba7b6434bacd7, + 0x1a0111ea397fe69a, + ]); + + CtOption::new(t, !self.is_zero()) + } + + #[inline] + const fn subtract_p(&self) -> Fp { + let (r0, borrow) = sbb(self.0[0], MODULUS[0], 0); + let (r1, borrow) = sbb(self.0[1], MODULUS[1], borrow); + let (r2, borrow) = sbb(self.0[2], MODULUS[2], borrow); + let (r3, borrow) = sbb(self.0[3], MODULUS[3], borrow); + let (r4, borrow) = sbb(self.0[4], MODULUS[4], borrow); + let (r5, borrow) = sbb(self.0[5], MODULUS[5], borrow); + + // If underflow occurred on the final limb, borrow = 0xfff...fff, otherwise + // borrow = 0x000...000. Thus, we use it as a mask! + let r0 = (self.0[0] & borrow) | (r0 & !borrow); + let r1 = (self.0[1] & borrow) | (r1 & !borrow); + let r2 = (self.0[2] & borrow) | (r2 & !borrow); + let r3 = (self.0[3] & borrow) | (r3 & !borrow); + let r4 = (self.0[4] & borrow) | (r4 & !borrow); + let r5 = (self.0[5] & borrow) | (r5 & !borrow); + + Fp([r0, r1, r2, r3, r4, r5]) + } + + #[inline] + pub const fn add(&self, rhs: &Fp) -> Fp { + let (d0, carry) = adc(self.0[0], rhs.0[0], 0); + let (d1, carry) = adc(self.0[1], rhs.0[1], carry); + let (d2, carry) = adc(self.0[2], rhs.0[2], carry); + let (d3, carry) = adc(self.0[3], rhs.0[3], carry); + let (d4, carry) = adc(self.0[4], rhs.0[4], carry); + let (d5, _) = adc(self.0[5], rhs.0[5], carry); + + // Attempt to subtract the modulus, to ensure the value + // is smaller than the modulus. + (&Fp([d0, d1, d2, d3, d4, d5])).subtract_p() + } + + #[inline] + pub const fn neg(&self) -> Fp { + let (d0, borrow) = sbb(MODULUS[0], self.0[0], 0); + let (d1, borrow) = sbb(MODULUS[1], self.0[1], borrow); + let (d2, borrow) = sbb(MODULUS[2], self.0[2], borrow); + let (d3, borrow) = sbb(MODULUS[3], self.0[3], borrow); + let (d4, borrow) = sbb(MODULUS[4], self.0[4], borrow); + let (d5, _) = sbb(MODULUS[5], self.0[5], borrow); + + // Let's use a mask if `self` was zero, which would mean + // the result of the subtraction is p. + let mask = (((self.0[0] | self.0[1] | self.0[2] | self.0[3] | self.0[4] | self.0[5]) == 0) + as u64) + .wrapping_sub(1); + + Fp([ + d0 & mask, + d1 & mask, + d2 & mask, + d3 & mask, + d4 & mask, + d5 & mask, + ]) + } + + #[inline] + pub const fn sub(&self, rhs: &Fp) -> Fp { + (&rhs.neg()).add(self) + } + + #[inline(always)] + const fn montgomery_reduce( + t0: u64, + t1: u64, + t2: u64, + t3: u64, + t4: u64, + t5: u64, + t6: u64, + t7: u64, + t8: u64, + t9: u64, + t10: u64, + t11: u64, + ) -> Self { + // The Montgomery reduction here is based on Algorithm 14.32 in + // Handbook of Applied Cryptography + // . + + let k = t0.wrapping_mul(INV); + let (_, carry) = mac(t0, k, MODULUS[0], 0); + let (r1, carry) = mac(t1, k, MODULUS[1], carry); + let (r2, carry) = mac(t2, k, MODULUS[2], carry); + let (r3, carry) = mac(t3, k, MODULUS[3], carry); + let (r4, carry) = mac(t4, k, MODULUS[4], carry); + let (r5, carry) = mac(t5, k, MODULUS[5], carry); + let (r6, r7) = adc(t6, 0, carry); + + let k = r1.wrapping_mul(INV); + let (_, carry) = mac(r1, k, MODULUS[0], 0); + let (r2, carry) = mac(r2, k, MODULUS[1], carry); + let (r3, carry) = mac(r3, k, MODULUS[2], carry); + let (r4, carry) = mac(r4, k, MODULUS[3], carry); + let (r5, carry) = mac(r5, k, MODULUS[4], carry); + let (r6, carry) = mac(r6, k, MODULUS[5], carry); + let (r7, r8) = adc(t7, r7, carry); + + let k = r2.wrapping_mul(INV); + let (_, carry) = mac(r2, k, MODULUS[0], 0); + let (r3, carry) = mac(r3, k, MODULUS[1], carry); + let (r4, carry) = mac(r4, k, MODULUS[2], carry); + let (r5, carry) = mac(r5, k, MODULUS[3], carry); + let (r6, carry) = mac(r6, k, MODULUS[4], carry); + let (r7, carry) = mac(r7, k, MODULUS[5], carry); + let (r8, r9) = adc(t8, r8, carry); + + let k = r3.wrapping_mul(INV); + let (_, carry) = mac(r3, k, MODULUS[0], 0); + let (r4, carry) = mac(r4, k, MODULUS[1], carry); + let (r5, carry) = mac(r5, k, MODULUS[2], carry); + let (r6, carry) = mac(r6, k, MODULUS[3], carry); + let (r7, carry) = mac(r7, k, MODULUS[4], carry); + let (r8, carry) = mac(r8, k, MODULUS[5], carry); + let (r9, r10) = adc(t9, r9, carry); + + let k = r4.wrapping_mul(INV); + let (_, carry) = mac(r4, k, MODULUS[0], 0); + let (r5, carry) = mac(r5, k, MODULUS[1], carry); + let (r6, carry) = mac(r6, k, MODULUS[2], carry); + let (r7, carry) = mac(r7, k, MODULUS[3], carry); + let (r8, carry) = mac(r8, k, MODULUS[4], carry); + let (r9, carry) = mac(r9, k, MODULUS[5], carry); + let (r10, r11) = adc(t10, r10, carry); + + let k = r5.wrapping_mul(INV); + let (_, carry) = mac(r5, k, MODULUS[0], 0); + let (r6, carry) = mac(r6, k, MODULUS[1], carry); + let (r7, carry) = mac(r7, k, MODULUS[2], carry); + let (r8, carry) = mac(r8, k, MODULUS[3], carry); + let (r9, carry) = mac(r9, k, MODULUS[4], carry); + let (r10, carry) = mac(r10, k, MODULUS[5], carry); + let (r11, _) = adc(t11, r11, carry); + + // Attempt to subtract the modulus, to ensure the value + // is smaller than the modulus. + (&Fp([r6, r7, r8, r9, r10, r11])).subtract_p() + } + + #[inline] + pub const fn mul(&self, rhs: &Fp) -> Fp { + let (t0, carry) = mac(0, self.0[0], rhs.0[0], 0); + let (t1, carry) = mac(0, self.0[0], rhs.0[1], carry); + let (t2, carry) = mac(0, self.0[0], rhs.0[2], carry); + let (t3, carry) = mac(0, self.0[0], rhs.0[3], carry); + let (t4, carry) = mac(0, self.0[0], rhs.0[4], carry); + let (t5, t6) = mac(0, self.0[0], rhs.0[5], carry); + + let (t1, carry) = mac(t1, self.0[1], rhs.0[0], 0); + let (t2, carry) = mac(t2, self.0[1], rhs.0[1], carry); + let (t3, carry) = mac(t3, self.0[1], rhs.0[2], carry); + let (t4, carry) = mac(t4, self.0[1], rhs.0[3], carry); + let (t5, carry) = mac(t5, self.0[1], rhs.0[4], carry); + let (t6, t7) = mac(t6, self.0[1], rhs.0[5], carry); + + let (t2, carry) = mac(t2, self.0[2], rhs.0[0], 0); + let (t3, carry) = mac(t3, self.0[2], rhs.0[1], carry); + let (t4, carry) = mac(t4, self.0[2], rhs.0[2], carry); + let (t5, carry) = mac(t5, self.0[2], rhs.0[3], carry); + let (t6, carry) = mac(t6, self.0[2], rhs.0[4], carry); + let (t7, t8) = mac(t7, self.0[2], rhs.0[5], carry); + + let (t3, carry) = mac(t3, self.0[3], rhs.0[0], 0); + let (t4, carry) = mac(t4, self.0[3], rhs.0[1], carry); + let (t5, carry) = mac(t5, self.0[3], rhs.0[2], carry); + let (t6, carry) = mac(t6, self.0[3], rhs.0[3], carry); + let (t7, carry) = mac(t7, self.0[3], rhs.0[4], carry); + let (t8, t9) = mac(t8, self.0[3], rhs.0[5], carry); + + let (t4, carry) = mac(t4, self.0[4], rhs.0[0], 0); + let (t5, carry) = mac(t5, self.0[4], rhs.0[1], carry); + let (t6, carry) = mac(t6, self.0[4], rhs.0[2], carry); + let (t7, carry) = mac(t7, self.0[4], rhs.0[3], carry); + let (t8, carry) = mac(t8, self.0[4], rhs.0[4], carry); + let (t9, t10) = mac(t9, self.0[4], rhs.0[5], carry); + + let (t5, carry) = mac(t5, self.0[5], rhs.0[0], 0); + let (t6, carry) = mac(t6, self.0[5], rhs.0[1], carry); + let (t7, carry) = mac(t7, self.0[5], rhs.0[2], carry); + let (t8, carry) = mac(t8, self.0[5], rhs.0[3], carry); + let (t9, carry) = mac(t9, self.0[5], rhs.0[4], carry); + let (t10, t11) = mac(t10, self.0[5], rhs.0[5], carry); + + Self::montgomery_reduce(t0, t1, t2, t3, t4, t5, t6, t7, t8, t9, t10, t11) + } + + /// Squares this element. + #[inline] + pub const fn square(&self) -> Self { + let (t1, carry) = mac(0, self.0[0], self.0[1], 0); + let (t2, carry) = mac(0, self.0[0], self.0[2], carry); + let (t3, carry) = mac(0, self.0[0], self.0[3], carry); + let (t4, carry) = mac(0, self.0[0], self.0[4], carry); + let (t5, t6) = mac(0, self.0[0], self.0[5], carry); + + let (t3, carry) = mac(t3, self.0[1], self.0[2], 0); + let (t4, carry) = mac(t4, self.0[1], self.0[3], carry); + let (t5, carry) = mac(t5, self.0[1], self.0[4], carry); + let (t6, t7) = mac(t6, self.0[1], self.0[5], carry); + + let (t5, carry) = mac(t5, self.0[2], self.0[3], 0); + let (t6, carry) = mac(t6, self.0[2], self.0[4], carry); + let (t7, t8) = mac(t7, self.0[2], self.0[5], carry); + + let (t7, carry) = mac(t7, self.0[3], self.0[4], 0); + let (t8, t9) = mac(t8, self.0[3], self.0[5], carry); + + let (t9, t10) = mac(t9, self.0[4], self.0[5], 0); + + let t11 = t10 >> 63; + let t10 = (t10 << 1) | (t9 >> 63); + let t9 = (t9 << 1) | (t8 >> 63); + let t8 = (t8 << 1) | (t7 >> 63); + let t7 = (t7 << 1) | (t6 >> 63); + let t6 = (t6 << 1) | (t5 >> 63); + let t5 = (t5 << 1) | (t4 >> 63); + let t4 = (t4 << 1) | (t3 >> 63); + let t3 = (t3 << 1) | (t2 >> 63); + let t2 = (t2 << 1) | (t1 >> 63); + let t1 = t1 << 1; + + let (t0, carry) = mac(0, self.0[0], self.0[0], 0); + let (t1, carry) = adc(t1, 0, carry); + let (t2, carry) = mac(t2, self.0[1], self.0[1], carry); + let (t3, carry) = adc(t3, 0, carry); + let (t4, carry) = mac(t4, self.0[2], self.0[2], carry); + let (t5, carry) = adc(t5, 0, carry); + let (t6, carry) = mac(t6, self.0[3], self.0[3], carry); + let (t7, carry) = adc(t7, 0, carry); + let (t8, carry) = mac(t8, self.0[4], self.0[4], carry); + let (t9, carry) = adc(t9, 0, carry); + let (t10, carry) = mac(t10, self.0[5], self.0[5], carry); + let (t11, _) = adc(t11, 0, carry); + + Self::montgomery_reduce(t0, t1, t2, t3, t4, t5, t6, t7, t8, t9, t10, t11) + } +} + +#[test] +fn test_conditional_selection() { + let a = Fp([1, 2, 3, 4, 5, 6]); + let b = Fp([7, 8, 9, 10, 11, 12]); + + assert_eq!( + ConditionallySelectable::conditional_select(&a, &b, Choice::from(0u8)), + a + ); + assert_eq!( + ConditionallySelectable::conditional_select(&a, &b, Choice::from(1u8)), + b + ); +} + +#[test] +fn test_equality() { + fn is_equal(a: &Fp, b: &Fp) -> bool { + let eq = a == b; + let ct_eq = a.ct_eq(&b); + + assert_eq!(eq, ct_eq.unwrap_u8() == 1); + + eq + } + + assert!(is_equal(&Fp([1, 2, 3, 4, 5, 6]), &Fp([1, 2, 3, 4, 5, 6]))); + + assert!(!is_equal(&Fp([7, 2, 3, 4, 5, 6]), &Fp([1, 2, 3, 4, 5, 6]))); + assert!(!is_equal(&Fp([1, 7, 3, 4, 5, 6]), &Fp([1, 2, 3, 4, 5, 6]))); + assert!(!is_equal(&Fp([1, 2, 7, 4, 5, 6]), &Fp([1, 2, 3, 4, 5, 6]))); + assert!(!is_equal(&Fp([1, 2, 3, 7, 5, 6]), &Fp([1, 2, 3, 4, 5, 6]))); + assert!(!is_equal(&Fp([1, 2, 3, 4, 7, 6]), &Fp([1, 2, 3, 4, 5, 6]))); + assert!(!is_equal(&Fp([1, 2, 3, 4, 5, 7]), &Fp([1, 2, 3, 4, 5, 6]))); +} + +#[test] +fn test_squaring() { + let a = Fp([ + 0xd215d2768e83191b, + 0x5085d80f8fb28261, + 0xce9a032ddf393a56, + 0x3e9c4fff2ca0c4bb, + 0x6436b6f7f4d95dfb, + 0x10606628ad4a4d90, + ]); + let b = Fp([ + 0x33d9c42a3cb3e235, + 0xdad11a094c4cd455, + 0xa2f144bd729aaeba, + 0xd4150932be9ffeac, + 0xe27bc7c47d44ee50, + 0x14b6a78d3ec7a560, + ]); + + assert_eq!(a.square(), b); +} + +#[test] +fn test_multiplication() { + let a = Fp([ + 0x397a38320170cd4, + 0x734c1b2c9e761d30, + 0x5ed255ad9a48beb5, + 0x95a3c6b22a7fcfc, + 0x2294ce75d4e26a27, + 0x13338bd870011ebb, + ]); + let b = Fp([ + 0xb9c3c7c5b1196af7, + 0x2580e2086ce335c1, + 0xf49aed3d8a57ef42, + 0x41f281e49846e878, + 0xe0762346c38452ce, + 0x652e89326e57dc0, + ]); + let c = Fp([ + 0xf96ef3d711ab5355, + 0xe8d459ea00f148dd, + 0x53f7354a5f00fa78, + 0x9e34a4f3125c5f83, + 0x3fbe0c47ca74c19e, + 0x1b06a8bbd4adfe4, + ]); + + assert_eq!(a * b, c); +} + +#[test] +fn test_addition() { + let a = Fp([ + 0x5360bb5978678032, + 0x7dd275ae799e128e, + 0x5c5b5071ce4f4dcf, + 0xcdb21f93078dbb3e, + 0xc32365c5e73f474a, + 0x115a2a5489babe5b, + ]); + let b = Fp([ + 0x9fd287733d23dda0, + 0xb16bf2af738b3554, + 0x3e57a75bd3cc6d1d, + 0x900bc0bd627fd6d6, + 0xd319a080efb245fe, + 0x15fdcaa4e4bb2091, + ]); + let c = Fp([ + 0x393442ccb58bb327, + 0x1092685f3bd547e3, + 0x3382252cab6ac4c9, + 0xf94694cb76887f55, + 0x4b215e9093a5e071, + 0xd56e30f34f5f853, + ]); + + assert_eq!(a + b, c); +} + +#[test] +fn test_subtraction() { + let a = Fp([ + 0x5360bb5978678032, + 0x7dd275ae799e128e, + 0x5c5b5071ce4f4dcf, + 0xcdb21f93078dbb3e, + 0xc32365c5e73f474a, + 0x115a2a5489babe5b, + ]); + let b = Fp([ + 0x9fd287733d23dda0, + 0xb16bf2af738b3554, + 0x3e57a75bd3cc6d1d, + 0x900bc0bd627fd6d6, + 0xd319a080efb245fe, + 0x15fdcaa4e4bb2091, + ]); + let c = Fp([ + 0x6d8d33e63b434d3d, + 0xeb1282fdb766dd39, + 0x85347bb6f133d6d5, + 0xa21daa5a9892f727, + 0x3b256cfb3ad8ae23, + 0x155d7199de7f8464, + ]); + + assert_eq!(a - b, c); +} + +#[test] +fn test_negation() { + let a = Fp([ + 0x5360bb5978678032, + 0x7dd275ae799e128e, + 0x5c5b5071ce4f4dcf, + 0xcdb21f93078dbb3e, + 0xc32365c5e73f474a, + 0x115a2a5489babe5b, + ]); + let b = Fp([ + 0x669e44a687982a79, + 0xa0d98a5037b5ed71, + 0xad5822f2861a854, + 0x96c52bf1ebf75781, + 0x87f841f05c0c658c, + 0x8a6e795afc5283e, + ]); + + assert_eq!(-a, b); +} + +#[test] +fn test_debug() { + assert_eq!( + format!( + "{:?}", + Fp([0x5360bb5978678032, 0x7dd275ae799e128e, 0x5c5b5071ce4f4dcf, 0xcdb21f93078dbb3e, 0xc32365c5e73f474a, 0x115a2a5489babe5b]) + ), + "0x104bf052ad3bc99bcb176c24a06a6c3aad4eaf2308fc4d282e106c84a757d061052630515305e59bdddf8111bfdeb704" + ); +} + +#[test] +fn test_from_bytes() { + let mut a = Fp([ + 0xdc906d9be3f95dc8, + 0x8755caf7459691a1, + 0xcff1a7f4e9583ab3, + 0x9b43821f849e2284, + 0xf57554f3a2974f3f, + 0x85dbea84ed47f79, + ]); + + for _ in 0..100 { + a = a.square(); + let tmp = a.to_bytes(); + let b = Fp::from_bytes(&tmp).unwrap(); + + assert_eq!(a, b); + } + + assert_eq!( + -Fp::one(), + Fp::from_bytes(&[ + 26, 1, 17, 234, 57, 127, 230, 154, 75, 27, 167, 182, 67, 75, 172, 215, 100, 119, 75, + 132, 243, 133, 18, 191, 103, 48, 210, 160, 246, 176, 246, 36, 30, 171, 255, 254, 177, + 83, 255, 255, 185, 254, 255, 255, 255, 255, 170, 170 + ]) + .unwrap() + ); + + assert!( + Fp::from_bytes(&[ + 27, 1, 17, 234, 57, 127, 230, 154, 75, 27, 167, 182, 67, 75, 172, 215, 100, 119, 75, + 132, 243, 133, 18, 191, 103, 48, 210, 160, 246, 176, 246, 36, 30, 171, 255, 254, 177, + 83, 255, 255, 185, 254, 255, 255, 255, 255, 170, 170 + ]) + .is_none() + .unwrap_u8() + == 1 + ); + + assert!(Fp::from_bytes(&[0xff; 48]).is_none().unwrap_u8() == 1); +} + +#[test] +fn test_sqrt() { + // a = 4 + let a = Fp::from_raw_unchecked([ + 0xaa270000000cfff3, + 0x53cc0032fc34000a, + 0x478fe97a6b0a807f, + 0xb1d37ebee6ba24d7, + 0x8ec9733bbf78ab2f, + 0x9d645513d83de7e, + ]); + + assert_eq!( + // sqrt(4) = -2 + -a.sqrt().unwrap(), + // 2 + Fp::from_raw_unchecked([ + 0x321300000006554f, + 0xb93c0018d6c40005, + 0x57605e0db0ddbb51, + 0x8b256521ed1f9bcb, + 0x6cf28d7901622c03, + 0x11ebab9dbb81e28c + ]) + ); +} + +#[test] +fn test_inversion() { + let a = Fp([ + 0x43b43a5078ac2076, + 0x1ce0763046f8962b, + 0x724a5276486d735c, + 0x6f05c2a6282d48fd, + 0x2095bd5bb4ca9331, + 0x3b35b3894b0f7da, + ]); + let b = Fp([ + 0x69ecd7040952148f, + 0x985ccc2022190f55, + 0xe19bba36a9ad2f41, + 0x19bb16c95219dbd8, + 0x14dcacfdfb478693, + 0x115ff58afff9a8e1, + ]); + + assert_eq!(a.invert().unwrap(), b); + assert!(Fp::zero().invert().is_none().unwrap_u8() == 1); +} + +#[test] +fn test_lexicographic_largest() { + assert!(!bool::from(Fp::zero().lexicographically_largest())); + assert!(!bool::from(Fp::one().lexicographically_largest())); + assert!(!bool::from( + Fp::from_raw_unchecked([ + 0xa1fafffffffe5557, + 0x995bfff976a3fffe, + 0x3f41d24d174ceb4, + 0xf6547998c1995dbd, + 0x778a468f507a6034, + 0x20559931f7f8103 + ]) + .lexicographically_largest() + )); + assert!(bool::from( + Fp::from_raw_unchecked([ + 0x1804000000015554, + 0x855000053ab00001, + 0x633cb57c253c276f, + 0x6e22d1ec31ebb502, + 0xd3916126f2d14ca2, + 0x17fbb8571a006596 + ]) + .lexicographically_largest() + )); + assert!(bool::from( + Fp::from_raw_unchecked([ + 0x43f5fffffffcaaae, + 0x32b7fff2ed47fffd, + 0x7e83a49a2e99d69, + 0xeca8f3318332bb7a, + 0xef148d1ea0f4c069, + 0x40ab3263eff0206 + ]) + .lexicographically_largest() + )); +} diff --git a/bls12_381/src/fp12.rs b/bls12_381/src/fp12.rs new file mode 100644 index 0000000..de9b540 --- /dev/null +++ b/bls12_381/src/fp12.rs @@ -0,0 +1,638 @@ +use crate::fp::*; +use crate::fp2::*; +use crate::fp6::*; + +use core::fmt; +use core::ops::{Add, AddAssign, Mul, MulAssign, Neg, Sub, SubAssign}; +use subtle::{Choice, ConditionallySelectable, ConstantTimeEq, CtOption}; + +/// This represents an element $c_0 + c_1 w$ of $\mathbb{F}_{p^12} = \mathbb{F}_{p^6} / w^2 - v$. +pub struct Fp12 { + pub c0: Fp6, + pub c1: Fp6, +} + +impl From for Fp12 { + fn from(f: Fp) -> Fp12 { + Fp12 { + c0: Fp6::from(f), + c1: Fp6::zero(), + } + } +} + +impl From for Fp12 { + fn from(f: Fp2) -> Fp12 { + Fp12 { + c0: Fp6::from(f), + c1: Fp6::zero(), + } + } +} + +impl From for Fp12 { + fn from(f: Fp6) -> Fp12 { + Fp12 { + c0: f, + c1: Fp6::zero(), + } + } +} + +impl PartialEq for Fp12 { + fn eq(&self, other: &Fp12) -> bool { + self.ct_eq(other).into() + } +} + +impl Copy for Fp12 {} +impl Clone for Fp12 { + #[inline] + fn clone(&self) -> Self { + *self + } +} + +impl Default for Fp12 { + fn default() -> Self { + Fp12::zero() + } +} + +impl fmt::Debug for Fp12 { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + write!(f, "{:?} + ({:?})*w", self.c0, self.c1) + } +} + +impl ConditionallySelectable for Fp12 { + #[inline(always)] + fn conditional_select(a: &Self, b: &Self, choice: Choice) -> Self { + Fp12 { + c0: Fp6::conditional_select(&a.c0, &b.c0, choice), + c1: Fp6::conditional_select(&a.c1, &b.c1, choice), + } + } +} + +impl ConstantTimeEq for Fp12 { + #[inline(always)] + fn ct_eq(&self, other: &Self) -> Choice { + self.c0.ct_eq(&other.c0) & self.c1.ct_eq(&other.c1) + } +} + +impl Fp12 { + #[inline] + pub fn zero() -> Self { + Fp12 { + c0: Fp6::zero(), + c1: Fp6::zero(), + } + } + + #[inline] + pub fn one() -> Self { + Fp12 { + c0: Fp6::one(), + c1: Fp6::zero(), + } + } + + pub fn mul_by_014(&self, c0: &Fp2, c1: &Fp2, c4: &Fp2) -> Fp12 { + let aa = self.c0.mul_by_01(c0, c1); + let bb = self.c1.mul_by_1(c4); + let o = c1 + c4; + let c1 = self.c1 + self.c0; + let c1 = c1.mul_by_01(c0, &o); + let c1 = c1 - aa - bb; + let c0 = bb; + let c0 = c0.mul_by_nonresidue(); + let c0 = c0 + aa; + + Fp12 { c0, c1 } + } + + #[inline(always)] + pub fn is_zero(&self) -> Choice { + self.c0.is_zero() & self.c1.is_zero() + } + + #[inline(always)] + pub fn conjugate(&self) -> Self { + Fp12 { + c0: self.c0, + c1: -self.c1, + } + } + + /// Raises this element to p. + #[inline(always)] + pub fn frobenius_map(&self) -> Self { + let c0 = self.c0.frobenius_map(); + let c1 = self.c1.frobenius_map(); + + // c1 = c1 * (u + 1)^((p - 1) / 6) + let c1 = c1 + * Fp6::from(Fp2 { + c0: Fp::from_raw_unchecked([ + 0x7089552b319d465, + 0xc6695f92b50a8313, + 0x97e83cccd117228f, + 0xa35baecab2dc29ee, + 0x1ce393ea5daace4d, + 0x8f2220fb0fb66eb, + ]), + c1: Fp::from_raw_unchecked([ + 0xb2f66aad4ce5d646, + 0x5842a06bfc497cec, + 0xcf4895d42599d394, + 0xc11b9cba40a8e8d0, + 0x2e3813cbe5a0de89, + 0x110eefda88847faf, + ]), + }); + + Fp12 { c0, c1 } + } + + #[inline] + pub fn square(&self) -> Self { + let ab = self.c0 * self.c1; + let c0c1 = self.c0 + self.c1; + let c0 = self.c1.mul_by_nonresidue(); + let c0 = c0 + self.c0; + let c0 = c0 * c0c1; + let c0 = c0 - ab; + let c1 = ab + ab; + let c0 = c0 - ab.mul_by_nonresidue(); + + Fp12 { c0, c1 } + } + + pub fn invert(&self) -> CtOption { + (self.c0.square() - self.c1.square().mul_by_nonresidue()) + .invert() + .map(|t| Fp12 { + c0: self.c0 * t, + c1: self.c1 * -t, + }) + } +} + +impl<'a, 'b> Mul<&'b Fp12> for &'a Fp12 { + type Output = Fp12; + + #[inline] + fn mul(self, other: &'b Fp12) -> Self::Output { + let aa = self.c0 * other.c0; + let bb = self.c1 * other.c1; + let o = other.c0 + other.c1; + let c1 = self.c1 + self.c0; + let c1 = c1 * o; + let c1 = c1 - aa; + let c1 = c1 - bb; + let c0 = bb.mul_by_nonresidue(); + let c0 = c0 + aa; + + Fp12 { c0, c1 } + } +} + +impl<'a, 'b> Add<&'b Fp12> for &'a Fp12 { + type Output = Fp12; + + #[inline] + fn add(self, rhs: &'b Fp12) -> Self::Output { + Fp12 { + c0: self.c0 + rhs.c0, + c1: self.c1 + rhs.c1, + } + } +} + +impl<'a> Neg for &'a Fp12 { + type Output = Fp12; + + #[inline] + fn neg(self) -> Self::Output { + Fp12 { + c0: -self.c0, + c1: -self.c1, + } + } +} + +impl Neg for Fp12 { + type Output = Fp12; + + #[inline] + fn neg(self) -> Self::Output { + -&self + } +} + +impl<'a, 'b> Sub<&'b Fp12> for &'a Fp12 { + type Output = Fp12; + + #[inline] + fn sub(self, rhs: &'b Fp12) -> Self::Output { + Fp12 { + c0: self.c0 - rhs.c0, + c1: self.c1 - rhs.c1, + } + } +} + +impl_binops_additive!(Fp12, Fp12); +impl_binops_multiplicative!(Fp12, Fp12); + +#[test] +fn test_arithmetic() { + use crate::fp::*; + use crate::fp2::*; + + let a = Fp12 { + c0: Fp6 { + c0: Fp2 { + c0: Fp::from_raw_unchecked([ + 0x47f9cb98b1b82d58, + 0x5fe911eba3aa1d9d, + 0x96bf1b5f4dd81db3, + 0x8100d27cc9259f5b, + 0xafa20b9674640eab, + 0x9bbcea7d8d9497d, + ]), + c1: Fp::from_raw_unchecked([ + 0x303cb98b1662daa, + 0xd93110aa0a621d5a, + 0xbfa9820c5be4a468, + 0xba3643ecb05a348, + 0xdc3534bb1f1c25a6, + 0x6c305bb19c0e1c1, + ]), + }, + c1: Fp2 { + c0: Fp::from_raw_unchecked([ + 0x46f9cb98b162d858, + 0xbe9109cf7aa1d57, + 0xc791bc55fece41d2, + 0xf84c57704e385ec2, + 0xcb49c1d9c010e60f, + 0xacdb8e158bfe3c8, + ]), + c1: Fp::from_raw_unchecked([ + 0x8aefcb98b15f8306, + 0x3ea1108fe4f21d54, + 0xcf79f69fa1b7df3b, + 0xe4f54aa1d16b1a3c, + 0xba5e4ef86105a679, + 0xed86c0797bee5cf, + ]), + }, + c2: Fp2 { + c0: Fp::from_raw_unchecked([ + 0xcee5cb98b15c2db4, + 0x71591082d23a1d51, + 0xd76230e944a17ca4, + 0xd19e3dd3549dd5b6, + 0xa972dc1701fa66e3, + 0x12e31f2dd6bde7d6, + ]), + c1: Fp::from_raw_unchecked([ + 0xad2acb98b1732d9d, + 0x2cfd10dd06961d64, + 0x7396b86c6ef24e8, + 0xbd76e2fdb1bfc820, + 0x6afea7f6de94d0d5, + 0x10994b0c5744c040, + ]), + }, + }, + c1: Fp6 { + c0: Fp2 { + c0: Fp::from_raw_unchecked([ + 0x47f9cb98b1b82d58, + 0x5fe911eba3aa1d9d, + 0x96bf1b5f4dd81db3, + 0x8100d27cc9259f5b, + 0xafa20b9674640eab, + 0x9bbcea7d8d9497d, + ]), + c1: Fp::from_raw_unchecked([ + 0x303cb98b1662daa, + 0xd93110aa0a621d5a, + 0xbfa9820c5be4a468, + 0xba3643ecb05a348, + 0xdc3534bb1f1c25a6, + 0x6c305bb19c0e1c1, + ]), + }, + c1: Fp2 { + c0: Fp::from_raw_unchecked([ + 0x46f9cb98b162d858, + 0xbe9109cf7aa1d57, + 0xc791bc55fece41d2, + 0xf84c57704e385ec2, + 0xcb49c1d9c010e60f, + 0xacdb8e158bfe3c8, + ]), + c1: Fp::from_raw_unchecked([ + 0x8aefcb98b15f8306, + 0x3ea1108fe4f21d54, + 0xcf79f69fa1b7df3b, + 0xe4f54aa1d16b1a3c, + 0xba5e4ef86105a679, + 0xed86c0797bee5cf, + ]), + }, + c2: Fp2 { + c0: Fp::from_raw_unchecked([ + 0xcee5cb98b15c2db4, + 0x71591082d23a1d51, + 0xd76230e944a17ca4, + 0xd19e3dd3549dd5b6, + 0xa972dc1701fa66e3, + 0x12e31f2dd6bde7d6, + ]), + c1: Fp::from_raw_unchecked([ + 0xad2acb98b1732d9d, + 0x2cfd10dd06961d64, + 0x7396b86c6ef24e8, + 0xbd76e2fdb1bfc820, + 0x6afea7f6de94d0d5, + 0x10994b0c5744c040, + ]), + }, + }, + }; + + let b = Fp12 { + c0: Fp6 { + c0: Fp2 { + c0: Fp::from_raw_unchecked([ + 0x47f9cb98b1b82d58, + 0x5fe911eba3aa1d9d, + 0x96bf1b5f4dd81db3, + 0x8100d272c9259f5b, + 0xafa20b9674640eab, + 0x9bbcea7d8d9497d, + ]), + c1: Fp::from_raw_unchecked([ + 0x303cb98b1662daa, + 0xd93110aa0a621d5a, + 0xbfa9820c5be4a468, + 0xba3643ecb05a348, + 0xdc3534bb1f1c25a6, + 0x6c305bb19c0e1c1, + ]), + }, + c1: Fp2 { + c0: Fp::from_raw_unchecked([ + 0x46f9cb98b162d858, + 0xbe9109cf7aa1d57, + 0xc791bc55fece41d2, + 0xf84c57704e385ec2, + 0xcb49c1d9c010e60f, + 0xacdb8e158bfe348, + ]), + c1: Fp::from_raw_unchecked([ + 0x8aefcb98b15f8306, + 0x3ea1108fe4f21d54, + 0xcf79f69fa1b7df3b, + 0xe4f54aa1d16b1a3c, + 0xba5e4ef86105a679, + 0xed86c0797bee5cf, + ]), + }, + c2: Fp2 { + c0: Fp::from_raw_unchecked([ + 0xcee5cb98b15c2db4, + 0x71591082d23a1d51, + 0xd76230e944a17ca4, + 0xd19e3dd3549dd5b6, + 0xa972dc1701fa66e3, + 0x12e31f2dd6bde7d6, + ]), + c1: Fp::from_raw_unchecked([ + 0xad2acb98b1732d9d, + 0x2cfd10dd06961d64, + 0x7396b86c6ef24e8, + 0xbd76e2fdb1bfc820, + 0x6afea7f6de94d0d5, + 0x10994b0c5744c040, + ]), + }, + }, + c1: Fp6 { + c0: Fp2 { + c0: Fp::from_raw_unchecked([ + 0x47f9cb98b1b82d58, + 0x5fe911eba3aa1d9d, + 0x96bf1b5f4dd21db3, + 0x8100d27cc9259f5b, + 0xafa20b9674640eab, + 0x9bbcea7d8d9497d, + ]), + c1: Fp::from_raw_unchecked([ + 0x303cb98b1662daa, + 0xd93110aa0a621d5a, + 0xbfa9820c5be4a468, + 0xba3643ecb05a348, + 0xdc3534bb1f1c25a6, + 0x6c305bb19c0e1c1, + ]), + }, + c1: Fp2 { + c0: Fp::from_raw_unchecked([ + 0x46f9cb98b162d858, + 0xbe9109cf7aa1d57, + 0xc791bc55fece41d2, + 0xf84c57704e385ec2, + 0xcb49c1d9c010e60f, + 0xacdb8e158bfe3c8, + ]), + c1: Fp::from_raw_unchecked([ + 0x8aefcb98b15f8306, + 0x3ea1108fe4f21d54, + 0xcf79f69fa117df3b, + 0xe4f54aa1d16b1a3c, + 0xba5e4ef86105a679, + 0xed86c0797bee5cf, + ]), + }, + c2: Fp2 { + c0: Fp::from_raw_unchecked([ + 0xcee5cb98b15c2db4, + 0x71591082d23a1d51, + 0xd76230e944a17ca4, + 0xd19e3dd3549dd5b6, + 0xa972dc1701fa66e3, + 0x12e31f2dd6bde7d6, + ]), + c1: Fp::from_raw_unchecked([ + 0xad2acb98b1732d9d, + 0x2cfd10dd06961d64, + 0x7396b86c6ef24e8, + 0xbd76e2fdb1bfc820, + 0x6afea7f6de94d0d5, + 0x10994b0c5744c040, + ]), + }, + }, + }; + + let c = Fp12 { + c0: Fp6 { + c0: Fp2 { + c0: Fp::from_raw_unchecked([ + 0x47f9cb9871b82d58, + 0x5fe911eba3aa1d9d, + 0x96bf1b5f4dd81db3, + 0x8100d27cc9259f5b, + 0xafa20b9674640eab, + 0x9bbcea7d8d9497d, + ]), + c1: Fp::from_raw_unchecked([ + 0x303cb98b1662daa, + 0xd93110aa0a621d5a, + 0xbfa9820c5be4a468, + 0xba3643ecb05a348, + 0xdc3534bb1f1c25a6, + 0x6c305bb19c0e1c1, + ]), + }, + c1: Fp2 { + c0: Fp::from_raw_unchecked([ + 0x46f9cb98b162d858, + 0xbe9109cf7aa1d57, + 0x7791bc55fece41d2, + 0xf84c57704e385ec2, + 0xcb49c1d9c010e60f, + 0xacdb8e158bfe3c8, + ]), + c1: Fp::from_raw_unchecked([ + 0x8aefcb98b15f8306, + 0x3ea1108fe4f21d54, + 0xcf79f69fa1b7df3b, + 0xe4f54aa1d16b133c, + 0xba5e4ef86105a679, + 0xed86c0797bee5cf, + ]), + }, + c2: Fp2 { + c0: Fp::from_raw_unchecked([ + 0xcee5cb98b15c2db4, + 0x71591082d23a1d51, + 0xd76240e944a17ca4, + 0xd19e3dd3549dd5b6, + 0xa972dc1701fa66e3, + 0x12e31f2dd6bde7d6, + ]), + c1: Fp::from_raw_unchecked([ + 0xad2acb98b1732d9d, + 0x2cfd10dd06961d64, + 0x7396b86c6ef24e8, + 0xbd76e2fdb1bfc820, + 0x6afea7f6de94d0d5, + 0x10994b0c1744c040, + ]), + }, + }, + c1: Fp6 { + c0: Fp2 { + c0: Fp::from_raw_unchecked([ + 0x47f9cb98b1b82d58, + 0x5fe911eba3aa1d9d, + 0x96bf1b5f4dd81db3, + 0x8100d27cc9259f5b, + 0xafa20b9674640eab, + 0x9bbcea7d8d9497d, + ]), + c1: Fp::from_raw_unchecked([ + 0x303cb98b1662daa, + 0xd93110aa0a621d5a, + 0xbfa9820c5be4a468, + 0xba3643ecb05a348, + 0xdc3534bb1f1c25a6, + 0x6c305bb19c0e1c1, + ]), + }, + c1: Fp2 { + c0: Fp::from_raw_unchecked([ + 0x46f9cb98b162d858, + 0xbe9109cf7aa1d57, + 0xc791bc55fece41d2, + 0xf84c57704e385ec2, + 0xcb49c1d3c010e60f, + 0xacdb8e158bfe3c8, + ]), + c1: Fp::from_raw_unchecked([ + 0x8aefcb98b15f8306, + 0x3ea1108fe4f21d54, + 0xcf79f69fa1b7df3b, + 0xe4f54aa1d16b1a3c, + 0xba5e4ef86105a679, + 0xed86c0797bee5cf, + ]), + }, + c2: Fp2 { + c0: Fp::from_raw_unchecked([ + 0xcee5cb98b15c2db4, + 0x71591082d23a1d51, + 0xd76230e944a17ca4, + 0xd19e3dd3549dd5b6, + 0xa972dc1701fa66e3, + 0x12e31f2dd6bde7d6, + ]), + c1: Fp::from_raw_unchecked([ + 0xad2acb98b1732d9d, + 0x2cfd10dd06961d64, + 0x7396b86c6ef24e8, + 0xbd76e2fdb1bfc820, + 0x6afea7f6de94d0d5, + 0x10994b0c57441040, + ]), + }, + }, + }; + + // because a and b and c are similar to each other and + // I was lazy, this is just some arbitrary way to make + // them a little more different + let a = &a.square().invert().unwrap().square() + &c; + let b = &b.square().invert().unwrap().square() + &a; + let c = &c.square().invert().unwrap().square() + &b; + + assert_eq!(a.square(), &a * &a); + assert_eq!(b.square(), &b * &b); + assert_eq!(c.square(), &c * &c); + + assert_eq!( + (a + b) * c.square(), + &(&(&c * &c) * &a) + &(&(&c * &c) * &b) + ); + + assert_eq!( + &a.invert().unwrap() * &b.invert().unwrap(), + (&a * &b).invert().unwrap() + ); + assert_eq!(&a.invert().unwrap() * &a, Fp12::one()); + + assert!(a != a.frobenius_map()); + assert_eq!( + a, + a.frobenius_map() + .frobenius_map() + .frobenius_map() + .frobenius_map() + .frobenius_map() + .frobenius_map() + .frobenius_map() + .frobenius_map() + .frobenius_map() + .frobenius_map() + .frobenius_map() + .frobenius_map() + ); +} diff --git a/bls12_381/src/fp2.rs b/bls12_381/src/fp2.rs new file mode 100644 index 0000000..4cd0a23 --- /dev/null +++ b/bls12_381/src/fp2.rs @@ -0,0 +1,868 @@ +//! This module implements arithmetic over the quadratic extension field Fp2. + +use core::fmt; +use core::ops::{Add, AddAssign, Mul, MulAssign, Neg, Sub, SubAssign}; + +use subtle::{Choice, ConditionallySelectable, ConstantTimeEq, CtOption}; + +use crate::fp::Fp; + +#[derive(Copy, Clone)] +pub struct Fp2 { + pub c0: Fp, + pub c1: Fp, +} + +impl fmt::Debug for Fp2 { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + write!(f, "{:?} + {:?}*u", self.c0, self.c1) + } +} + +impl Default for Fp2 { + fn default() -> Self { + Fp2::zero() + } +} + +impl From for Fp2 { + fn from(f: Fp) -> Fp2 { + Fp2 { + c0: f, + c1: Fp::zero(), + } + } +} + +impl ConstantTimeEq for Fp2 { + fn ct_eq(&self, other: &Self) -> Choice { + self.c0.ct_eq(&other.c0) & self.c1.ct_eq(&other.c1) + } +} + +impl Eq for Fp2 {} +impl PartialEq for Fp2 { + #[inline] + fn eq(&self, other: &Self) -> bool { + self.ct_eq(other).unwrap_u8() == 1 + } +} + +impl ConditionallySelectable for Fp2 { + fn conditional_select(a: &Self, b: &Self, choice: Choice) -> Self { + Fp2 { + c0: Fp::conditional_select(&a.c0, &b.c0, choice), + c1: Fp::conditional_select(&a.c1, &b.c1, choice), + } + } +} + +impl<'a> Neg for &'a Fp2 { + type Output = Fp2; + + #[inline] + fn neg(self) -> Fp2 { + self.neg() + } +} + +impl Neg for Fp2 { + type Output = Fp2; + + #[inline] + fn neg(self) -> Fp2 { + -&self + } +} + +impl<'a, 'b> Sub<&'b Fp2> for &'a Fp2 { + type Output = Fp2; + + #[inline] + fn sub(self, rhs: &'b Fp2) -> Fp2 { + self.sub(rhs) + } +} + +impl<'a, 'b> Add<&'b Fp2> for &'a Fp2 { + type Output = Fp2; + + #[inline] + fn add(self, rhs: &'b Fp2) -> Fp2 { + self.add(rhs) + } +} + +impl<'a, 'b> Mul<&'b Fp2> for &'a Fp2 { + type Output = Fp2; + + #[inline] + fn mul(self, rhs: &'b Fp2) -> Fp2 { + self.mul(rhs) + } +} + +impl_binops_additive!(Fp2, Fp2); +impl_binops_multiplicative!(Fp2, Fp2); + +impl Fp2 { + #[inline] + pub const fn zero() -> Fp2 { + Fp2 { + c0: Fp::zero(), + c1: Fp::zero(), + } + } + + #[inline] + pub const fn one() -> Fp2 { + Fp2 { + c0: Fp::one(), + c1: Fp::zero(), + } + } + + pub fn is_zero(&self) -> Choice { + self.c0.is_zero() & self.c1.is_zero() + } + + /// Raises this element to p. + #[inline(always)] + pub fn frobenius_map(&self) -> Self { + // This is always just a conjugation. If you're curious why, here's + // an article about it: https://alicebob.cryptoland.net/the-frobenius-endomorphism-with-finite-fields/ + self.conjugate() + } + + #[inline(always)] + pub fn conjugate(&self) -> Self { + Fp2 { + c0: self.c0, + c1: -self.c1, + } + } + + #[inline(always)] + pub fn mul_by_nonresidue(&self) -> Fp2 { + // Multiply a + bu by u + 1, getting + // au + a + bu^2 + bu + // and because u^2 = -1, we get + // (a - b) + (a + b)u + + Fp2 { + c0: self.c0 - self.c1, + c1: self.c0 + self.c1, + } + } + + /// Returns whether or not this element is strictly lexicographically + /// larger than its negation. + #[inline] + pub fn lexicographically_largest(&self) -> Choice { + // If this element's c1 coefficient is lexicographically largest + // then it is lexicographically largest. Otherwise, in the event + // the c1 coefficient is zero and the c0 coefficient is + // lexicographically largest, then this element is lexicographically + // largest. + + self.c1.lexicographically_largest() + | (self.c1.is_zero() & self.c0.lexicographically_largest()) + } + + pub const fn square(&self) -> Fp2 { + // Complex squaring: + // + // v0 = c0 * c1 + // c0' = (c0 + c1) * (c0 + \beta*c1) - v0 - \beta * v0 + // c1' = 2 * v0 + // + // In BLS12-381's F_{p^2}, our \beta is -1 so we + // can modify this formula: + // + // c0' = (c0 + c1) * (c0 - c1) + // c1' = 2 * c0 * c1 + + let a = (&self.c0).add(&self.c1); + let b = (&self.c0).sub(&self.c1); + let c = (&self.c0).add(&self.c0); + + Fp2 { + c0: (&a).mul(&b), + c1: (&c).mul(&self.c1), + } + } + + pub const fn mul(&self, rhs: &Fp2) -> Fp2 { + // Karatsuba multiplication: + // + // v0 = a0 * b0 + // v1 = a1 * b1 + // c0 = v0 + \beta * v1 + // c1 = (a0 + a1) * (b0 + b1) - v0 - v1 + // + // In BLS12-381's F_{p^2}, our \beta is -1 so we + // can modify this formula. (Also, since we always + // subtract v1, we can compute v1 = -a1 * b1.) + // + // v0 = a0 * b0 + // v1 = (-a1) * b1 + // c0 = v0 + v1 + // c1 = (a0 + a1) * (b0 + b1) - v0 + v1 + + let v0 = (&self.c0).mul(&rhs.c0); + let v1 = (&(&self.c1).neg()).mul(&rhs.c1); + let c0 = (&v0).add(&v1); + let c1 = (&(&self.c0).add(&self.c1)).mul(&(&rhs.c0).add(&rhs.c1)); + let c1 = (&c1).sub(&v0); + let c1 = (&c1).add(&v1); + + Fp2 { c0, c1 } + } + + pub const fn add(&self, rhs: &Fp2) -> Fp2 { + Fp2 { + c0: (&self.c0).add(&rhs.c0), + c1: (&self.c1).add(&rhs.c1), + } + } + + pub const fn sub(&self, rhs: &Fp2) -> Fp2 { + Fp2 { + c0: (&self.c0).sub(&rhs.c0), + c1: (&self.c1).sub(&rhs.c1), + } + } + + pub const fn neg(&self) -> Fp2 { + Fp2 { + c0: (&self.c0).neg(), + c1: (&self.c1).neg(), + } + } + + pub fn sqrt(&self) -> CtOption { + // Algorithm 9, https://eprint.iacr.org/2012/685.pdf + // with constant time modifications. + + CtOption::new(Fp2::zero(), self.is_zero()).or_else(|| { + // a1 = self^((p - 3) / 4) + let a1 = self.pow_vartime(&[ + 0xee7fbfffffffeaaa, + 0x7aaffffac54ffff, + 0xd9cc34a83dac3d89, + 0xd91dd2e13ce144af, + 0x92c6e9ed90d2eb35, + 0x680447a8e5ff9a6, + ]); + + // alpha = a1^2 * self = self^((p - 3) / 2 + 1) = self^((p - 1) / 2) + let alpha = a1.square() * self; + + // x0 = self^((p + 1) / 4) + let x0 = a1 * self; + + // In the event that alpha = -1, the element is order p - 1 and so + // we're just trying to get the square of an element of the subfield + // Fp. This is given by x0 * u, since u = sqrt(-1). Since the element + // x0 = a + bu has b = 0, the solution is therefore au. + CtOption::new( + Fp2 { + c0: -x0.c1, + c1: x0.c0, + }, + alpha.ct_eq(&(&Fp2::one()).neg()), + ) + // Otherwise, the correct solution is (1 + alpha)^((q - 1) // 2) * x0 + .or_else(|| { + CtOption::new( + (alpha + Fp2::one()).pow_vartime(&[ + 0xdcff7fffffffd555, + 0xf55ffff58a9ffff, + 0xb39869507b587b12, + 0xb23ba5c279c2895f, + 0x258dd3db21a5d66b, + 0xd0088f51cbff34d, + ]) * x0, + Choice::from(1), + ) + }) + // Only return the result if it's really the square root (and so + // self is actually quadratic nonresidue) + .and_then(|sqrt| CtOption::new(sqrt, sqrt.square().ct_eq(self))) + }) + } + + /// Computes the multiplicative inverse of this field + /// element, returning None in the case that this element + /// is zero. + pub fn invert(&self) -> CtOption { + // We wish to find the multiplicative inverse of a nonzero + // element a + bu in Fp2. We leverage an identity + // + // (a + bu)(a - bu) = a^2 + b^2 + // + // which holds because u^2 = -1. This can be rewritten as + // + // (a + bu)(a - bu)/(a^2 + b^2) = 1 + // + // because a^2 + b^2 = 0 has no nonzero solutions for (a, b). + // This gives that (a - bu)/(a^2 + b^2) is the inverse + // of (a + bu). Importantly, this can be computing using + // only a single inversion in Fp. + + (self.c0.square() + self.c1.square()).invert().map(|t| Fp2 { + c0: self.c0 * t, + c1: self.c1 * -t, + }) + } + + /// Although this is labeled "vartime", it is only + /// variable time with respect to the exponent. It + /// is also not exposed in the public API. + pub fn pow_vartime(&self, by: &[u64; 6]) -> Self { + let mut res = Self::one(); + for e in by.iter().rev() { + for i in (0..64).rev() { + res = res.square(); + + if ((*e >> i) & 1) == 1 { + res *= self; + } + } + } + res + } +} + +#[test] +fn test_conditional_selection() { + let a = Fp2 { + c0: Fp::from_raw_unchecked([1, 2, 3, 4, 5, 6]), + c1: Fp::from_raw_unchecked([7, 8, 9, 10, 11, 12]), + }; + let b = Fp2 { + c0: Fp::from_raw_unchecked([13, 14, 15, 16, 17, 18]), + c1: Fp::from_raw_unchecked([19, 20, 21, 22, 23, 24]), + }; + + assert_eq!( + ConditionallySelectable::conditional_select(&a, &b, Choice::from(0u8)), + a + ); + assert_eq!( + ConditionallySelectable::conditional_select(&a, &b, Choice::from(1u8)), + b + ); +} + +#[test] +fn test_equality() { + fn is_equal(a: &Fp2, b: &Fp2) -> bool { + let eq = a == b; + let ct_eq = a.ct_eq(&b); + + assert_eq!(eq, ct_eq.unwrap_u8() == 1); + + eq + } + + assert!(is_equal( + &Fp2 { + c0: Fp::from_raw_unchecked([1, 2, 3, 4, 5, 6]), + c1: Fp::from_raw_unchecked([7, 8, 9, 10, 11, 12]), + }, + &Fp2 { + c0: Fp::from_raw_unchecked([1, 2, 3, 4, 5, 6]), + c1: Fp::from_raw_unchecked([7, 8, 9, 10, 11, 12]), + } + )); + + assert!(!is_equal( + &Fp2 { + c0: Fp::from_raw_unchecked([2, 2, 3, 4, 5, 6]), + c1: Fp::from_raw_unchecked([7, 8, 9, 10, 11, 12]), + }, + &Fp2 { + c0: Fp::from_raw_unchecked([1, 2, 3, 4, 5, 6]), + c1: Fp::from_raw_unchecked([7, 8, 9, 10, 11, 12]), + } + )); + + assert!(!is_equal( + &Fp2 { + c0: Fp::from_raw_unchecked([1, 2, 3, 4, 5, 6]), + c1: Fp::from_raw_unchecked([2, 8, 9, 10, 11, 12]), + }, + &Fp2 { + c0: Fp::from_raw_unchecked([1, 2, 3, 4, 5, 6]), + c1: Fp::from_raw_unchecked([7, 8, 9, 10, 11, 12]), + } + )); +} + +#[test] +fn test_squaring() { + let a = Fp2 { + c0: Fp::from_raw_unchecked([ + 0xc9a2183163ee70d4, + 0xbc3770a7196b5c91, + 0xa247f8c1304c5f44, + 0xb01fc2a3726c80b5, + 0xe1d293e5bbd919c9, + 0x4b78e80020ef2ca, + ]), + c1: Fp::from_raw_unchecked([ + 0x952ea4460462618f, + 0x238d5eddf025c62f, + 0xf6c94b012ea92e72, + 0x3ce24eac1c93808, + 0x55950f945da483c, + 0x10a768d0df4eabc, + ]), + }; + let b = Fp2 { + c0: Fp::from_raw_unchecked([ + 0xa1e09175a4d2c1fe, + 0x8b33acfc204eff12, + 0xe24415a11b456e42, + 0x61d996b1b6ee1936, + 0x1164dbe8667c853c, + 0x788557acc7d9c79, + ]), + c1: Fp::from_raw_unchecked([ + 0xda6a87cc6f48fa36, + 0xfc7b488277c1903, + 0x9445ac4adc448187, + 0x2616d5bc9099209, + 0xdbed46772db58d48, + 0x11b94d5076c7b7b1, + ]), + }; + + assert_eq!(a.square(), b); +} + +#[test] +fn test_multiplication() { + let a = Fp2 { + c0: Fp::from_raw_unchecked([ + 0xc9a2183163ee70d4, + 0xbc3770a7196b5c91, + 0xa247f8c1304c5f44, + 0xb01fc2a3726c80b5, + 0xe1d293e5bbd919c9, + 0x4b78e80020ef2ca, + ]), + c1: Fp::from_raw_unchecked([ + 0x952ea4460462618f, + 0x238d5eddf025c62f, + 0xf6c94b012ea92e72, + 0x3ce24eac1c93808, + 0x55950f945da483c, + 0x10a768d0df4eabc, + ]), + }; + let b = Fp2 { + c0: Fp::from_raw_unchecked([ + 0xa1e09175a4d2c1fe, + 0x8b33acfc204eff12, + 0xe24415a11b456e42, + 0x61d996b1b6ee1936, + 0x1164dbe8667c853c, + 0x788557acc7d9c79, + ]), + c1: Fp::from_raw_unchecked([ + 0xda6a87cc6f48fa36, + 0xfc7b488277c1903, + 0x9445ac4adc448187, + 0x2616d5bc9099209, + 0xdbed46772db58d48, + 0x11b94d5076c7b7b1, + ]), + }; + let c = Fp2 { + c0: Fp::from_raw_unchecked([ + 0xf597483e27b4e0f7, + 0x610fbadf811dae5f, + 0x8432af917714327a, + 0x6a9a9603cf88f09e, + 0xf05a7bf8bad0eb01, + 0x9549131c003ffae, + ]), + c1: Fp::from_raw_unchecked([ + 0x963b02d0f93d37cd, + 0xc95ce1cdb30a73d4, + 0x308725fa3126f9b8, + 0x56da3c167fab0d50, + 0x6b5086b5f4b6d6af, + 0x9c39f062f18e9f2, + ]), + }; + + assert_eq!(a * b, c); +} + +#[test] +fn test_addition() { + let a = Fp2 { + c0: Fp::from_raw_unchecked([ + 0xc9a2183163ee70d4, + 0xbc3770a7196b5c91, + 0xa247f8c1304c5f44, + 0xb01fc2a3726c80b5, + 0xe1d293e5bbd919c9, + 0x4b78e80020ef2ca, + ]), + c1: Fp::from_raw_unchecked([ + 0x952ea4460462618f, + 0x238d5eddf025c62f, + 0xf6c94b012ea92e72, + 0x3ce24eac1c93808, + 0x55950f945da483c, + 0x10a768d0df4eabc, + ]), + }; + let b = Fp2 { + c0: Fp::from_raw_unchecked([ + 0xa1e09175a4d2c1fe, + 0x8b33acfc204eff12, + 0xe24415a11b456e42, + 0x61d996b1b6ee1936, + 0x1164dbe8667c853c, + 0x788557acc7d9c79, + ]), + c1: Fp::from_raw_unchecked([ + 0xda6a87cc6f48fa36, + 0xfc7b488277c1903, + 0x9445ac4adc448187, + 0x2616d5bc9099209, + 0xdbed46772db58d48, + 0x11b94d5076c7b7b1, + ]), + }; + let c = Fp2 { + c0: Fp::from_raw_unchecked([ + 0x6b82a9a708c132d2, + 0x476b1da339ba5ba4, + 0x848c0e624b91cd87, + 0x11f95955295a99ec, + 0xf3376fce22559f06, + 0xc3fe3face8c8f43, + ]), + c1: Fp::from_raw_unchecked([ + 0x6f992c1273ab5bc5, + 0x3355136617a1df33, + 0x8b0ef74c0aedaff9, + 0x62f92468ad2ca12, + 0xe1469770738fd584, + 0x12c3c3dd84bca26d, + ]), + }; + + assert_eq!(a + b, c); +} + +#[test] +fn test_subtraction() { + let a = Fp2 { + c0: Fp::from_raw_unchecked([ + 0xc9a2183163ee70d4, + 0xbc3770a7196b5c91, + 0xa247f8c1304c5f44, + 0xb01fc2a3726c80b5, + 0xe1d293e5bbd919c9, + 0x4b78e80020ef2ca, + ]), + c1: Fp::from_raw_unchecked([ + 0x952ea4460462618f, + 0x238d5eddf025c62f, + 0xf6c94b012ea92e72, + 0x3ce24eac1c93808, + 0x55950f945da483c, + 0x10a768d0df4eabc, + ]), + }; + let b = Fp2 { + c0: Fp::from_raw_unchecked([ + 0xa1e09175a4d2c1fe, + 0x8b33acfc204eff12, + 0xe24415a11b456e42, + 0x61d996b1b6ee1936, + 0x1164dbe8667c853c, + 0x788557acc7d9c79, + ]), + c1: Fp::from_raw_unchecked([ + 0xda6a87cc6f48fa36, + 0xfc7b488277c1903, + 0x9445ac4adc448187, + 0x2616d5bc9099209, + 0xdbed46772db58d48, + 0x11b94d5076c7b7b1, + ]), + }; + let c = Fp2 { + c0: Fp::from_raw_unchecked([ + 0xe1c086bbbf1b5981, + 0x4fafc3a9aa705d7e, + 0x2734b5c10bb7e726, + 0xb2bd7776af037a3e, + 0x1b895fb398a84164, + 0x17304aef6f113cec, + ]), + c1: Fp::from_raw_unchecked([ + 0x74c31c7995191204, + 0x3271aa5479fdad2b, + 0xc9b471574915a30f, + 0x65e40313ec44b8be, + 0x7487b2385b7067cb, + 0x9523b26d0ad19a4, + ]), + }; + + assert_eq!(a - b, c); +} + +#[test] +fn test_negation() { + let a = Fp2 { + c0: Fp::from_raw_unchecked([ + 0xc9a2183163ee70d4, + 0xbc3770a7196b5c91, + 0xa247f8c1304c5f44, + 0xb01fc2a3726c80b5, + 0xe1d293e5bbd919c9, + 0x4b78e80020ef2ca, + ]), + c1: Fp::from_raw_unchecked([ + 0x952ea4460462618f, + 0x238d5eddf025c62f, + 0xf6c94b012ea92e72, + 0x3ce24eac1c93808, + 0x55950f945da483c, + 0x10a768d0df4eabc, + ]), + }; + let b = Fp2 { + c0: Fp::from_raw_unchecked([ + 0xf05ce7ce9c1139d7, + 0x62748f5797e8a36d, + 0xc4e8d9dfc66496df, + 0xb45788e181189209, + 0x694913d08772930d, + 0x1549836a3770f3cf, + ]), + c1: Fp::from_raw_unchecked([ + 0x24d05bb9fb9d491c, + 0xfb1ea120c12e39d0, + 0x7067879fc807c7b1, + 0x60a9269a31bbdab6, + 0x45c256bcfd71649b, + 0x18f69b5d2b8afbde, + ]), + }; + + assert_eq!(-a, b); +} + +#[test] +fn test_sqrt() { + // a = 1488924004771393321054797166853618474668089414631333405711627789629391903630694737978065425271543178763948256226639*u + 784063022264861764559335808165825052288770346101304131934508881646553551234697082295473567906267937225174620141295 + let a = Fp2 { + c0: Fp::from_raw_unchecked([ + 0x2beed14627d7f9e9, + 0xb6614e06660e5dce, + 0x6c4cc7c2f91d42c, + 0x996d78474b7a63cc, + 0xebaebc4c820d574e, + 0x18865e12d93fd845, + ]), + c1: Fp::from_raw_unchecked([ + 0x7d828664baf4f566, + 0xd17e663996ec7339, + 0x679ead55cb4078d0, + 0xfe3b2260e001ec28, + 0x305993d043d91b68, + 0x626f03c0489b72d, + ]), + }; + + assert_eq!(a.sqrt().unwrap().square(), a); + + // b = 5, which is a generator of the p - 1 order + // multiplicative subgroup + let b = Fp2 { + c0: Fp::from_raw_unchecked([ + 0x6631000000105545, + 0x211400400eec000d, + 0x3fa7af30c820e316, + 0xc52a8b8d6387695d, + 0x9fb4e61d1e83eac5, + 0x5cb922afe84dc7, + ]), + c1: Fp::zero(), + }; + + assert_eq!(b.sqrt().unwrap().square(), b); + + // c = 25, which is a generator of the (p - 1) / 2 order + // multiplicative subgroup + let c = Fp2 { + c0: Fp::from_raw_unchecked([ + 0x44f600000051ffae, + 0x86b8014199480043, + 0xd7159952f1f3794a, + 0x755d6e3dfe1ffc12, + 0xd36cd6db5547e905, + 0x2f8c8ecbf1867bb, + ]), + c1: Fp::zero(), + }; + + assert_eq!(c.sqrt().unwrap().square(), c); + + // 2155129644831861015726826462986972654175647013268275306775721078997042729172900466542651176384766902407257452753362*u + 2796889544896299244102912275102369318775038861758288697415827248356648685135290329705805931514906495247464901062529 + // is nonsquare. + assert!(bool::from( + Fp2 { + c0: Fp::from_raw_unchecked([ + 0xc5fa1bc8fd00d7f6, + 0x3830ca454606003b, + 0x2b287f1104b102da, + 0xa7fb30f28230f23e, + 0x339cdb9ee953dbf0, + 0xd78ec51d989fc57 + ]), + c1: Fp::from_raw_unchecked([ + 0x27ec4898cf87f613, + 0x9de1394e1abb05a5, + 0x947f85dc170fc14, + 0x586fbc696b6114b7, + 0x2b3475a4077d7169, + 0x13e1c895cc4b6c22 + ]) + } + .sqrt() + .is_none() + )); +} + +#[test] +fn test_inversion() { + let a = Fp2 { + c0: Fp::from_raw_unchecked([ + 0x1128ecad67549455, + 0x9e7a1cff3a4ea1a8, + 0xeb208d51e08bcf27, + 0xe98ad40811f5fc2b, + 0x736c3a59232d511d, + 0x10acd42d29cfcbb6, + ]), + c1: Fp::from_raw_unchecked([ + 0xd328e37cc2f58d41, + 0x948df0858a605869, + 0x6032f9d56f93a573, + 0x2be483ef3fffdc87, + 0x30ef61f88f483c2a, + 0x1333f55a35725be0, + ]), + }; + + let b = Fp2 { + c0: Fp::from_raw_unchecked([ + 0x581a1333d4f48a6, + 0x58242f6ef0748500, + 0x292c955349e6da5, + 0xba37721ddd95fcd0, + 0x70d167903aa5dfc5, + 0x11895e118b58a9d5, + ]), + c1: Fp::from_raw_unchecked([ + 0xeda09d2d7a85d17, + 0x8808e137a7d1a2cf, + 0x43ae2625c1ff21db, + 0xf85ac9fdf7a74c64, + 0x8fccdda5b8da9738, + 0x8e84f0cb32cd17d, + ]), + }; + + assert_eq!(a.invert().unwrap(), b); + + assert!(Fp2::zero().invert().is_none().unwrap_u8() == 1); +} + +#[test] +fn test_lexicographic_largest() { + assert!(!bool::from(Fp2::zero().lexicographically_largest())); + assert!(!bool::from(Fp2::one().lexicographically_largest())); + assert!(bool::from( + Fp2 { + c0: Fp::from_raw_unchecked([ + 0x1128ecad67549455, + 0x9e7a1cff3a4ea1a8, + 0xeb208d51e08bcf27, + 0xe98ad40811f5fc2b, + 0x736c3a59232d511d, + 0x10acd42d29cfcbb6, + ]), + c1: Fp::from_raw_unchecked([ + 0xd328e37cc2f58d41, + 0x948df0858a605869, + 0x6032f9d56f93a573, + 0x2be483ef3fffdc87, + 0x30ef61f88f483c2a, + 0x1333f55a35725be0, + ]), + } + .lexicographically_largest() + )); + assert!(!bool::from( + Fp2 { + c0: -Fp::from_raw_unchecked([ + 0x1128ecad67549455, + 0x9e7a1cff3a4ea1a8, + 0xeb208d51e08bcf27, + 0xe98ad40811f5fc2b, + 0x736c3a59232d511d, + 0x10acd42d29cfcbb6, + ]), + c1: -Fp::from_raw_unchecked([ + 0xd328e37cc2f58d41, + 0x948df0858a605869, + 0x6032f9d56f93a573, + 0x2be483ef3fffdc87, + 0x30ef61f88f483c2a, + 0x1333f55a35725be0, + ]), + } + .lexicographically_largest() + )); + assert!(!bool::from( + Fp2 { + c0: Fp::from_raw_unchecked([ + 0x1128ecad67549455, + 0x9e7a1cff3a4ea1a8, + 0xeb208d51e08bcf27, + 0xe98ad40811f5fc2b, + 0x736c3a59232d511d, + 0x10acd42d29cfcbb6, + ]), + c1: Fp::zero(), + } + .lexicographically_largest() + )); + assert!(bool::from( + Fp2 { + c0: -Fp::from_raw_unchecked([ + 0x1128ecad67549455, + 0x9e7a1cff3a4ea1a8, + 0xeb208d51e08bcf27, + 0xe98ad40811f5fc2b, + 0x736c3a59232d511d, + 0x10acd42d29cfcbb6, + ]), + c1: Fp::zero(), + } + .lexicographically_largest() + )); +} diff --git a/bls12_381/src/fp6.rs b/bls12_381/src/fp6.rs new file mode 100644 index 0000000..50ed2eb --- /dev/null +++ b/bls12_381/src/fp6.rs @@ -0,0 +1,507 @@ +use crate::fp::*; +use crate::fp2::*; + +use core::fmt; +use core::ops::{Add, AddAssign, Mul, MulAssign, Neg, Sub, SubAssign}; +use subtle::{Choice, ConditionallySelectable, ConstantTimeEq, CtOption}; + +/// This represents an element $c_0 + c_1 v + c_2 v^2$ of $\mathbb{F}_{p^6} = \mathbb{F}_{p^2} / v^3 - u - 1$. +pub struct Fp6 { + pub c0: Fp2, + pub c1: Fp2, + pub c2: Fp2, +} + +impl From for Fp6 { + fn from(f: Fp) -> Fp6 { + Fp6 { + c0: Fp2::from(f), + c1: Fp2::zero(), + c2: Fp2::zero(), + } + } +} + +impl From for Fp6 { + fn from(f: Fp2) -> Fp6 { + Fp6 { + c0: f, + c1: Fp2::zero(), + c2: Fp2::zero(), + } + } +} + +impl PartialEq for Fp6 { + fn eq(&self, other: &Fp6) -> bool { + self.ct_eq(other).into() + } +} + +impl Copy for Fp6 {} +impl Clone for Fp6 { + #[inline] + fn clone(&self) -> Self { + *self + } +} + +impl Default for Fp6 { + fn default() -> Self { + Fp6::zero() + } +} + +impl fmt::Debug for Fp6 { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + write!(f, "{:?} + ({:?})*v + ({:?})*v^2", self.c0, self.c1, self.c2) + } +} + +impl ConditionallySelectable for Fp6 { + #[inline(always)] + fn conditional_select(a: &Self, b: &Self, choice: Choice) -> Self { + Fp6 { + c0: Fp2::conditional_select(&a.c0, &b.c0, choice), + c1: Fp2::conditional_select(&a.c1, &b.c1, choice), + c2: Fp2::conditional_select(&a.c2, &b.c2, choice), + } + } +} + +impl ConstantTimeEq for Fp6 { + #[inline(always)] + fn ct_eq(&self, other: &Self) -> Choice { + self.c0.ct_eq(&other.c0) & self.c1.ct_eq(&other.c1) & self.c2.ct_eq(&other.c2) + } +} + +impl Fp6 { + #[inline] + pub fn zero() -> Self { + Fp6 { + c0: Fp2::zero(), + c1: Fp2::zero(), + c2: Fp2::zero(), + } + } + + #[inline] + pub fn one() -> Self { + Fp6 { + c0: Fp2::one(), + c1: Fp2::zero(), + c2: Fp2::zero(), + } + } + + pub fn mul_by_1(&self, c1: &Fp2) -> Fp6 { + let b_b = self.c1 * c1; + + let t1 = (self.c1 + self.c2) * c1 - b_b; + let t1 = t1.mul_by_nonresidue(); + + let t2 = (self.c0 + self.c1) * c1 - b_b; + + Fp6 { + c0: t1, + c1: t2, + c2: b_b, + } + } + + pub fn mul_by_01(&self, c0: &Fp2, c1: &Fp2) -> Fp6 { + let a_a = self.c0 * c0; + let b_b = self.c1 * c1; + + let t1 = (self.c1 + self.c2) * c1 - b_b; + let t1 = t1.mul_by_nonresidue() + a_a; + + let t2 = (c0 + c1) * (self.c0 + self.c1) - a_a - b_b; + + let t3 = (self.c0 + self.c2) * c0 - a_a + b_b; + + Fp6 { + c0: t1, + c1: t2, + c2: t3, + } + } + + /// Multiply by quadratic nonresidue v. + pub fn mul_by_nonresidue(&self) -> Self { + // Given a + bv + cv^2, this produces + // av + bv^2 + cv^3 + // but because v^3 = u + 1, we have + // c(u + 1) + av + v^2 + + Fp6 { + c0: self.c2.mul_by_nonresidue(), + c1: self.c0, + c2: self.c1, + } + } + + /// Raises this element to p. + #[inline(always)] + pub fn frobenius_map(&self) -> Self { + let c0 = self.c0.frobenius_map(); + let c1 = self.c1.frobenius_map(); + let c2 = self.c2.frobenius_map(); + + // c1 = c1 * (u + 1)^((p - 1) / 3) + let c1 = c1 + * Fp2 { + c0: Fp::zero(), + c1: Fp::from_raw_unchecked([ + 0xcd03c9e48671f071, + 0x5dab22461fcda5d2, + 0x587042afd3851b95, + 0x8eb60ebe01bacb9e, + 0x3f97d6e83d050d2, + 0x18f0206554638741, + ]), + }; + + // c2 = c2 * (u + 1)^((2p - 2) / 3) + let c2 = c2 + * Fp2 { + c0: Fp::from_raw_unchecked([ + 0x890dc9e4867545c3, + 0x2af322533285a5d5, + 0x50880866309b7e2c, + 0xa20d1b8c7e881024, + 0x14e4f04fe2db9068, + 0x14e56d3f1564853a, + ]), + c1: Fp::zero(), + }; + + Fp6 { c0, c1, c2 } + } + + #[inline(always)] + pub fn is_zero(&self) -> Choice { + self.c0.is_zero() & self.c1.is_zero() & self.c2.is_zero() + } + + #[inline] + pub fn square(&self) -> Self { + let s0 = self.c0.square(); + let ab = self.c0 * self.c1; + let s1 = ab + ab; + let s2 = (self.c0 - self.c1 + self.c2).square(); + let bc = self.c1 * self.c2; + let s3 = bc + bc; + let s4 = self.c2.square(); + + Fp6 { + c0: s3.mul_by_nonresidue() + s0, + c1: s4.mul_by_nonresidue() + s1, + c2: s1 + s2 + s3 - s0 - s4, + } + } + + #[inline] + pub fn invert(&self) -> CtOption { + let c0 = (self.c1 * self.c2).mul_by_nonresidue(); + let c0 = self.c0.square() - c0; + + let c1 = self.c2.square().mul_by_nonresidue(); + let c1 = c1 - (self.c0 * self.c1); + + let c2 = self.c1.square(); + let c2 = c2 - (self.c0 * self.c2); + + let tmp = ((self.c1 * c2) + (self.c2 * c1)).mul_by_nonresidue(); + let tmp = tmp + (self.c0 * c0); + + tmp.invert().map(|t| Fp6 { + c0: t * c0, + c1: t * c1, + c2: t * c2, + }) + } +} + +impl<'a, 'b> Mul<&'b Fp6> for &'a Fp6 { + type Output = Fp6; + + #[inline] + fn mul(self, other: &'b Fp6) -> Self::Output { + let aa = self.c0 * other.c0; + let bb = self.c1 * other.c1; + let cc = self.c2 * other.c2; + + let t1 = other.c1 + other.c2; + let tmp = self.c1 + self.c2; + let t1 = t1 * tmp; + let t1 = t1 - bb; + let t1 = t1 - cc; + let t1 = t1.mul_by_nonresidue(); + let t1 = t1 + aa; + + let t3 = other.c0 + other.c2; + let tmp = self.c0 + self.c2; + let t3 = t3 * tmp; + let t3 = t3 - aa; + let t3 = t3 + bb; + let t3 = t3 - cc; + + let t2 = other.c0 + other.c1; + let tmp = self.c0 + self.c1; + let t2 = t2 * tmp; + let t2 = t2 - aa; + let t2 = t2 - bb; + let cc = cc.mul_by_nonresidue(); + let t2 = t2 + cc; + + Fp6 { + c0: t1, + c1: t2, + c2: t3, + } + } +} + +impl<'a, 'b> Add<&'b Fp6> for &'a Fp6 { + type Output = Fp6; + + #[inline] + fn add(self, rhs: &'b Fp6) -> Self::Output { + Fp6 { + c0: self.c0 + rhs.c0, + c1: self.c1 + rhs.c1, + c2: self.c2 + rhs.c2, + } + } +} + +impl<'a> Neg for &'a Fp6 { + type Output = Fp6; + + #[inline] + fn neg(self) -> Self::Output { + Fp6 { + c0: -self.c0, + c1: -self.c1, + c2: -self.c2, + } + } +} + +impl Neg for Fp6 { + type Output = Fp6; + + #[inline] + fn neg(self) -> Self::Output { + -&self + } +} + +impl<'a, 'b> Sub<&'b Fp6> for &'a Fp6 { + type Output = Fp6; + + #[inline] + fn sub(self, rhs: &'b Fp6) -> Self::Output { + Fp6 { + c0: self.c0 - rhs.c0, + c1: self.c1 - rhs.c1, + c2: self.c2 - rhs.c2, + } + } +} + +impl_binops_additive!(Fp6, Fp6); +impl_binops_multiplicative!(Fp6, Fp6); + +#[test] +fn test_arithmetic() { + use crate::fp::*; + + let a = Fp6 { + c0: Fp2 { + c0: Fp::from_raw_unchecked([ + 0x47f9cb98b1b82d58, + 0x5fe911eba3aa1d9d, + 0x96bf1b5f4dd81db3, + 0x8100d27cc9259f5b, + 0xafa20b9674640eab, + 0x9bbcea7d8d9497d, + ]), + c1: Fp::from_raw_unchecked([ + 0x303cb98b1662daa, + 0xd93110aa0a621d5a, + 0xbfa9820c5be4a468, + 0xba3643ecb05a348, + 0xdc3534bb1f1c25a6, + 0x6c305bb19c0e1c1, + ]), + }, + c1: Fp2 { + c0: Fp::from_raw_unchecked([ + 0x46f9cb98b162d858, + 0xbe9109cf7aa1d57, + 0xc791bc55fece41d2, + 0xf84c57704e385ec2, + 0xcb49c1d9c010e60f, + 0xacdb8e158bfe3c8, + ]), + c1: Fp::from_raw_unchecked([ + 0x8aefcb98b15f8306, + 0x3ea1108fe4f21d54, + 0xcf79f69fa1b7df3b, + 0xe4f54aa1d16b1a3c, + 0xba5e4ef86105a679, + 0xed86c0797bee5cf, + ]), + }, + c2: Fp2 { + c0: Fp::from_raw_unchecked([ + 0xcee5cb98b15c2db4, + 0x71591082d23a1d51, + 0xd76230e944a17ca4, + 0xd19e3dd3549dd5b6, + 0xa972dc1701fa66e3, + 0x12e31f2dd6bde7d6, + ]), + c1: Fp::from_raw_unchecked([ + 0xad2acb98b1732d9d, + 0x2cfd10dd06961d64, + 0x7396b86c6ef24e8, + 0xbd76e2fdb1bfc820, + 0x6afea7f6de94d0d5, + 0x10994b0c5744c040, + ]), + }, + }; + + let b = Fp6 { + c0: Fp2 { + c0: Fp::from_raw_unchecked([ + 0xf120cb98b16fd84b, + 0x5fb510cff3de1d61, + 0xf21a5d069d8c251, + 0xaa1fd62f34f2839a, + 0x5a1335157f89913f, + 0x14a3fe329643c247, + ]), + c1: Fp::from_raw_unchecked([ + 0x3516cb98b16c82f9, + 0x926d10c2e1261d5f, + 0x1709e01a0cc25fba, + 0x96c8c960b8253f14, + 0x4927c234207e51a9, + 0x18aeb158d542c44e, + ]), + }, + c1: Fp2 { + c0: Fp::from_raw_unchecked([ + 0xbf0dcb98b16982fc, + 0xa67910b71d1a1d5c, + 0xb7c147c2b8fb06ff, + 0x1efa710d47d2e7ce, + 0xed20a79c7e27653c, + 0x2b85294dac1dfba, + ]), + c1: Fp::from_raw_unchecked([ + 0x9d52cb98b18082e5, + 0x621d111151761d6f, + 0xe79882603b48af43, + 0xad31637a4f4da37, + 0xaeac737c5ac1cf2e, + 0x6e7e735b48b824, + ]), + }, + c2: Fp2 { + c0: Fp::from_raw_unchecked([ + 0xe148cb98b17d2d93, + 0x94d511043ebe1d6c, + 0xef80bca9de324cac, + 0xf77c0969282795b1, + 0x9dc1009afbb68f97, + 0x47931999a47ba2b, + ]), + c1: Fp::from_raw_unchecked([ + 0x253ecb98b179d841, + 0xc78d10f72c061d6a, + 0xf768f6f3811bea15, + 0xe424fc9aab5a512b, + 0x8cd58db99cab5001, + 0x883e4bfd946bc32, + ]), + }, + }; + + let c = Fp6 { + c0: Fp2 { + c0: Fp::from_raw_unchecked([ + 0x6934cb98b17682ef, + 0xfa4510ea194e1d67, + 0xff51313d2405877e, + 0xd0cdefcc2e8d0ca5, + 0x7bea1ad83da0106b, + 0xc8e97e61845be39, + ]), + c1: Fp::from_raw_unchecked([ + 0x4779cb98b18d82d8, + 0xb5e911444daa1d7a, + 0x2f286bdaa6532fc2, + 0xbca694f68baeff0f, + 0x3d75e6b81a3a7a5d, + 0xa44c3c498cc96a3, + ]), + }, + c1: Fp2 { + c0: Fp::from_raw_unchecked([ + 0x8b6fcb98b18a2d86, + 0xe8a111373af21d77, + 0x3710a624493ccd2b, + 0xa94f88280ee1ba89, + 0x2c8a73d6bb2f3ac7, + 0xe4f76ead7cb98aa, + ]), + c1: Fp::from_raw_unchecked([ + 0xcf65cb98b186d834, + 0x1b59112a283a1d74, + 0x3ef8e06dec266a95, + 0x95f87b5992147603, + 0x1b9f00f55c23fb31, + 0x125a2a1116ca9ab1, + ]), + }, + c2: Fp2 { + c0: Fp::from_raw_unchecked([ + 0x135bcb98b18382e2, + 0x4e11111d15821d72, + 0x46e11ab78f1007fe, + 0x82a16e8b1547317d, + 0xab38e13fd18bb9b, + 0x1664dd3755c99cb8, + ]), + c1: Fp::from_raw_unchecked([ + 0xce65cb98b1318334, + 0xc7590fdb7c3a1d2e, + 0x6fcb81649d1c8eb3, + 0xd44004d1727356a, + 0x3746b738a7d0d296, + 0x136c144a96b134fc, + ]), + }, + }; + + assert_eq!(a.square(), &a * &a); + assert_eq!(b.square(), &b * &b); + assert_eq!(c.square(), &c * &c); + + assert_eq!( + (a + b) * c.square(), + &(&(&c * &c) * &a) + &(&(&c * &c) * &b) + ); + + assert_eq!( + &a.invert().unwrap() * &b.invert().unwrap(), + (&a * &b).invert().unwrap() + ); + assert_eq!(&a.invert().unwrap() * &a, Fp6::one()); +} diff --git a/bls12_381/src/g1.rs b/bls12_381/src/g1.rs new file mode 100644 index 0000000..aa90dc1 --- /dev/null +++ b/bls12_381/src/g1.rs @@ -0,0 +1,1343 @@ +//! This module provides an implementation of the $\mathbb{G}_1$ group of BLS12-381. + +use core::ops::{Add, AddAssign, Mul, MulAssign, Neg, Sub, SubAssign}; + +use subtle::{Choice, ConditionallySelectable, ConstantTimeEq, CtOption}; + +use crate::fp::Fp; +use crate::Scalar; + +/// This is an element of $\mathbb{G}_1$ represented in the affine coordinate space. +/// It is ideal to keep elements in this representation to reduce memory usage and +/// improve performance through the use of mixed curve model arithmetic. +/// +/// Values of `G1Affine` are guaranteed to be in the $q$-order subgroup unless an +/// "unchecked" API was misused. +#[derive(Copy, Clone, Debug)] +pub struct G1Affine { + pub(crate) x: Fp, + pub(crate) y: Fp, + infinity: Choice, +} + +impl Default for G1Affine { + fn default() -> G1Affine { + G1Affine::identity() + } +} + +impl<'a> From<&'a G1Projective> for G1Affine { + fn from(p: &'a G1Projective) -> G1Affine { + let zinv = p.z.invert().unwrap_or(Fp::zero()); + let zinv2 = zinv.square(); + let x = p.x * zinv2; + let zinv3 = zinv2 * zinv; + let y = p.y * zinv3; + + let tmp = G1Affine { + x, + y, + infinity: Choice::from(0u8), + }; + + G1Affine::conditional_select(&tmp, &G1Affine::identity(), zinv.is_zero()) + } +} + +impl From for G1Affine { + fn from(p: G1Projective) -> G1Affine { + G1Affine::from(&p) + } +} + +impl ConstantTimeEq for G1Affine { + fn ct_eq(&self, other: &Self) -> Choice { + // The only cases in which two points are equal are + // 1. infinity is set on both + // 2. infinity is not set on both, and their coordinates are equal + + (self.infinity & other.infinity) + | ((!self.infinity) + & (!other.infinity) + & self.x.ct_eq(&other.x) + & self.y.ct_eq(&other.y)) + } +} + +impl ConditionallySelectable for G1Affine { + fn conditional_select(a: &Self, b: &Self, choice: Choice) -> Self { + G1Affine { + x: Fp::conditional_select(&a.x, &b.x, choice), + y: Fp::conditional_select(&a.y, &b.y, choice), + infinity: Choice::conditional_select(&a.infinity, &b.infinity, choice), + } + } +} + +impl Eq for G1Affine {} +impl PartialEq for G1Affine { + #[inline] + fn eq(&self, other: &Self) -> bool { + bool::from(self.ct_eq(other)) + } +} + +impl<'a> Neg for &'a G1Affine { + type Output = G1Affine; + + #[inline] + fn neg(self) -> G1Affine { + G1Affine { + x: self.x, + y: Fp::conditional_select(&-self.y, &Fp::one(), self.infinity), + infinity: self.infinity, + } + } +} + +impl Neg for G1Affine { + type Output = G1Affine; + + #[inline] + fn neg(self) -> G1Affine { + -&self + } +} + +impl<'a, 'b> Add<&'b G1Projective> for &'a G1Affine { + type Output = G1Projective; + + #[inline] + fn add(self, rhs: &'b G1Projective) -> G1Projective { + rhs.add_mixed(self) + } +} + +impl<'a, 'b> Add<&'b G1Affine> for &'a G1Projective { + type Output = G1Projective; + + #[inline] + fn add(self, rhs: &'b G1Affine) -> G1Projective { + self.add_mixed(rhs) + } +} + +impl<'a, 'b> Sub<&'b G1Projective> for &'a G1Affine { + type Output = G1Projective; + + #[inline] + fn sub(self, rhs: &'b G1Projective) -> G1Projective { + self + (-rhs) + } +} + +impl<'a, 'b> Sub<&'b G1Affine> for &'a G1Projective { + type Output = G1Projective; + + #[inline] + fn sub(self, rhs: &'b G1Affine) -> G1Projective { + self + (-rhs) + } +} + +impl_binops_additive!(G1Projective, G1Affine); +impl_binops_additive_specify_output!(G1Affine, G1Projective, G1Projective); + +const B: Fp = Fp::from_raw_unchecked([ + 0xaa270000000cfff3, + 0x53cc0032fc34000a, + 0x478fe97a6b0a807f, + 0xb1d37ebee6ba24d7, + 0x8ec9733bbf78ab2f, + 0x9d645513d83de7e, +]); + +impl G1Affine { + /// Returns the identity of the group: the point at infinity. + pub fn identity() -> G1Affine { + G1Affine { + x: Fp::zero(), + y: Fp::one(), + infinity: Choice::from(1u8), + } + } + + /// Returns a fixed generator of the group. See [`notes::design`](notes/design/index.html#fixed-generators) + /// for how this generator is chosen. + pub fn generator() -> G1Affine { + G1Affine { + x: Fp::from_raw_unchecked([ + 0x5cb38790fd530c16, + 0x7817fc679976fff5, + 0x154f95c7143ba1c1, + 0xf0ae6acdf3d0e747, + 0xedce6ecc21dbf440, + 0x120177419e0bfb75, + ]), + y: Fp::from_raw_unchecked([ + 0xbaac93d50ce72271, + 0x8c22631a7918fd8e, + 0xdd595f13570725ce, + 0x51ac582950405194, + 0xe1c8c3fad0059c0, + 0xbbc3efc5008a26a, + ]), + infinity: Choice::from(0u8), + } + } + + /// Serializes this element into compressed form. See [`notes::serialization`](crate::notes::serialization) + /// for details about how group elements are serialized. + pub fn to_compressed(&self) -> [u8; 48] { + // Strictly speaking, self.x is zero already when self.infinity is true, but + // to guard against implementation mistakes we do not assume this. + let mut res = Fp::conditional_select(&self.x, &Fp::zero(), self.infinity).to_bytes(); + + // This point is in compressed form, so we set the most significant bit. + res[0] |= 1u8 << 7; + + // Is this point at infinity? If so, set the second-most significant bit. + res[0] |= u8::conditional_select(&0u8, &(1u8 << 6), self.infinity); + + // Is the y-coordinate the lexicographically largest of the two associated with the + // x-coordinate? If so, set the third-most significant bit so long as this is not + // the point at infinity. + res[0] |= u8::conditional_select( + &0u8, + &(1u8 << 5), + (!self.infinity) & self.y.lexicographically_largest(), + ); + + res + } + + /// Serializes this element into uncompressed form. See [`notes::serialization`](crate::notes::serialization) + /// for details about how group elements are serialized. + pub fn to_uncompressed(&self) -> [u8; 96] { + let mut res = [0; 96]; + + res[0..48].copy_from_slice( + &Fp::conditional_select(&self.x, &Fp::zero(), self.infinity).to_bytes()[..], + ); + res[48..96].copy_from_slice( + &Fp::conditional_select(&self.y, &Fp::zero(), self.infinity).to_bytes()[..], + ); + + // Is this point at infinity? If so, set the second-most significant bit. + res[0] |= u8::conditional_select(&0u8, &(1u8 << 6), self.infinity); + + res + } + + /// Attempts to deserialize an uncompressed element. See [`notes::serialization`](crate::notes::serialization) + /// for details about how group elements are serialized. + pub fn from_uncompressed(bytes: &[u8; 96]) -> CtOption { + Self::from_uncompressed_unchecked(bytes) + .and_then(|p| CtOption::new(p, p.is_on_curve() & p.is_torsion_free())) + } + + /// Attempts to deserialize an uncompressed element, not checking if the + /// element is on the curve and not checking if it is in the correct subgroup. + /// **This is dangerous to call unless you trust the bytes you are reading; otherwise, + /// API invariants may be broken.** Please consider using `from_uncompressed()` instead. + pub fn from_uncompressed_unchecked(bytes: &[u8; 96]) -> CtOption { + // Obtain the three flags from the start of the byte sequence + let compression_flag_set = Choice::from((bytes[0] >> 7) & 1); + let infinity_flag_set = Choice::from((bytes[0] >> 6) & 1); + let sort_flag_set = Choice::from((bytes[0] >> 5) & 1); + + // Attempt to obtain the x-coordinate + let x = { + let mut tmp = [0; 48]; + tmp.copy_from_slice(&bytes[0..48]); + + // Mask away the flag bits + tmp[0] &= 0b0001_1111; + + Fp::from_bytes(&tmp) + }; + + // Attempt to obtain the y-coordinate + let y = { + let mut tmp = [0; 48]; + tmp.copy_from_slice(&bytes[48..96]); + + Fp::from_bytes(&tmp) + }; + + x.and_then(|x| { + y.and_then(|y| { + // Create a point representing this value + let p = G1Affine::conditional_select( + &G1Affine { + x, + y, + infinity: infinity_flag_set, + }, + &G1Affine::identity(), + infinity_flag_set, + ); + + CtOption::new( + p, + // If the infinity flag is set, the x and y coordinates should have been zero. + ((!infinity_flag_set) | (infinity_flag_set & x.is_zero() & y.is_zero())) & + // The compression flag should not have been set, as this is an uncompressed element + (!compression_flag_set) & + // The sort flag should not have been set, as this is an uncompressed element + (!sort_flag_set), + ) + }) + }) + } + + /// Attempts to deserialize a compressed element. See [`notes::serialization`](crate::notes::serialization) + /// for details about how group elements are serialized. + pub fn from_compressed(bytes: &[u8; 48]) -> CtOption { + // We already know the point is on the curve because this is established + // by the y-coordinate recovery procedure in from_compressed_unchecked(). + + Self::from_compressed_unchecked(bytes).and_then(|p| CtOption::new(p, p.is_torsion_free())) + } + + /// Attempts to deserialize an uncompressed element, not checking if the + /// element is in the correct subgroup. + /// **This is dangerous to call unless you trust the bytes you are reading; otherwise, + /// API invariants may be broken.** Please consider using `from_compressed()` instead. + pub fn from_compressed_unchecked(bytes: &[u8; 48]) -> CtOption { + // Obtain the three flags from the start of the byte sequence + let compression_flag_set = Choice::from((bytes[0] >> 7) & 1); + let infinity_flag_set = Choice::from((bytes[0] >> 6) & 1); + let sort_flag_set = Choice::from((bytes[0] >> 5) & 1); + + // Attempt to obtain the x-coordinate + let x = { + let mut tmp = [0; 48]; + tmp.copy_from_slice(&bytes[0..48]); + + // Mask away the flag bits + tmp[0] &= 0b0001_1111; + + Fp::from_bytes(&tmp) + }; + + x.and_then(|x| { + // If the infinity flag is set, return the value assuming + // the x-coordinate is zero and the sort bit is not set. + // + // Otherwise, return a recovered point (assuming the correct + // y-coordinate can be found) so long as the infinity flag + // was not set. + CtOption::new( + G1Affine::identity(), + infinity_flag_set & // Infinity flag should be set + compression_flag_set & // Compression flag should be set + (!sort_flag_set) & // Sort flag should not be set + x.is_zero(), // The x-coordinate should be zero + ) + .or_else(|| { + // Recover a y-coordinate given x by y = sqrt(x^3 + 4) + ((x.square() * x) + B).sqrt().and_then(|y| { + // Switch to the correct y-coordinate if necessary. + let y = Fp::conditional_select( + &y, + &-y, + y.lexicographically_largest() ^ sort_flag_set, + ); + + CtOption::new( + G1Affine { + x, + y, + infinity: infinity_flag_set, + }, + (!infinity_flag_set) & // Infinity flag should not be set + compression_flag_set, // Compression flag should be set + ) + }) + }) + }) + } + + /// Returns true if this element is the identity (the point at infinity). + #[inline] + pub fn is_identity(&self) -> Choice { + self.infinity + } + + /// Returns true if this point is free of an $h$-torsion component, and so it + /// exists within the $q$-order subgroup $\mathbb{G}_1$. This should always return true + /// unless an "unchecked" API was used. + pub fn is_torsion_free(&self) -> Choice { + const FQ_MODULUS_BYTES: [u8; 32] = [ + 1, 0, 0, 0, 255, 255, 255, 255, 254, 91, 254, 255, 2, 164, 189, 83, 5, 216, 161, 9, 8, + 216, 57, 51, 72, 125, 157, 41, 83, 167, 237, 115, + ]; + + // Clear the r-torsion from the point and check if it is the identity + G1Projective::from(*self) + .multiply(&FQ_MODULUS_BYTES) + .is_identity() + } + + /// Returns true if this point is on the curve. This should always return + /// true unless an "unchecked" API was used. + pub fn is_on_curve(&self) -> Choice { + // y^2 - x^3 ?= 4 + (self.y.square() - (self.x.square() * self.x)).ct_eq(&B) | self.infinity + } +} + +/// This is an element of $\mathbb{G}_1$ represented in the projective coordinate space. +#[derive(Copy, Clone, Debug)] +pub struct G1Projective { + x: Fp, + y: Fp, + z: Fp, +} + +impl<'a> From<&'a G1Affine> for G1Projective { + fn from(p: &'a G1Affine) -> G1Projective { + G1Projective { + x: p.x, + y: p.y, + z: Fp::conditional_select(&Fp::one(), &Fp::zero(), p.infinity), + } + } +} + +impl From for G1Projective { + fn from(p: G1Affine) -> G1Projective { + G1Projective::from(&p) + } +} + +impl ConstantTimeEq for G1Projective { + fn ct_eq(&self, other: &Self) -> Choice { + // Is (xz^2, yz^3, z) equal to (x'z'^2, yz'^3, z') when converted to affine? + + let z = other.z.square(); + let x1 = self.x * z; + let z = z * other.z; + let y1 = self.y * z; + let z = self.z.square(); + let x2 = other.x * z; + let z = z * self.z; + let y2 = other.y * z; + + let self_is_zero = self.z.is_zero(); + let other_is_zero = other.z.is_zero(); + + (self_is_zero & other_is_zero) // Both point at infinity + | ((!self_is_zero) & (!other_is_zero) & x1.ct_eq(&x2) & y1.ct_eq(&y2)) // Neither point at infinity, coordinates are the same + } +} + +impl ConditionallySelectable for G1Projective { + fn conditional_select(a: &Self, b: &Self, choice: Choice) -> Self { + G1Projective { + x: Fp::conditional_select(&a.x, &b.x, choice), + y: Fp::conditional_select(&a.y, &b.y, choice), + z: Fp::conditional_select(&a.z, &b.z, choice), + } + } +} + +impl Eq for G1Projective {} +impl PartialEq for G1Projective { + #[inline] + fn eq(&self, other: &Self) -> bool { + bool::from(self.ct_eq(other)) + } +} + +impl<'a> Neg for &'a G1Projective { + type Output = G1Projective; + + #[inline] + fn neg(self) -> G1Projective { + G1Projective { + x: self.x, + y: -self.y, + z: self.z, + } + } +} + +impl Neg for G1Projective { + type Output = G1Projective; + + #[inline] + fn neg(self) -> G1Projective { + -&self + } +} + +impl<'a, 'b> Add<&'b G1Projective> for &'a G1Projective { + type Output = G1Projective; + + #[inline] + fn add(self, rhs: &'b G1Projective) -> G1Projective { + self.add(rhs) + } +} + +impl<'a, 'b> Sub<&'b G1Projective> for &'a G1Projective { + type Output = G1Projective; + + #[inline] + fn sub(self, rhs: &'b G1Projective) -> G1Projective { + self + (-rhs) + } +} + +impl<'a, 'b> Mul<&'b Scalar> for &'a G1Projective { + type Output = G1Projective; + + fn mul(self, other: &'b Scalar) -> Self::Output { + self.multiply(&other.to_bytes()) + } +} + +impl<'a, 'b> Mul<&'b Scalar> for &'a G1Affine { + type Output = G1Projective; + + fn mul(self, other: &'b Scalar) -> Self::Output { + G1Projective::from(self).multiply(&other.to_bytes()) + } +} + +impl_binops_additive!(G1Projective, G1Projective); +impl_binops_multiplicative!(G1Projective, Scalar); +impl_binops_multiplicative_mixed!(G1Affine, Scalar, G1Projective); + +impl G1Projective { + /// Returns the identity of the group: the point at infinity. + pub fn identity() -> G1Projective { + G1Projective { + x: Fp::zero(), + y: Fp::one(), + z: Fp::zero(), + } + } + + /// Returns a fixed generator of the group. See [`notes::design`](notes/design/index.html#fixed-generators) + /// for how this generator is chosen. + pub fn generator() -> G1Projective { + G1Projective { + x: Fp::from_raw_unchecked([ + 0x5cb38790fd530c16, + 0x7817fc679976fff5, + 0x154f95c7143ba1c1, + 0xf0ae6acdf3d0e747, + 0xedce6ecc21dbf440, + 0x120177419e0bfb75, + ]), + y: Fp::from_raw_unchecked([ + 0xbaac93d50ce72271, + 0x8c22631a7918fd8e, + 0xdd595f13570725ce, + 0x51ac582950405194, + 0xe1c8c3fad0059c0, + 0xbbc3efc5008a26a, + ]), + z: Fp::one(), + } + } + + /// Computes the doubling of this point. + pub fn double(&self) -> G1Projective { + // http://www.hyperelliptic.org/EFD/g1p/auto-shortw-jacobian-0.html#doubling-dbl-2009-l + // + // There are no points of order 2. + + let a = self.x.square(); + let b = self.y.square(); + let c = b.square(); + let d = self.x + b; + let d = d.square(); + let d = d - a - c; + let d = d + d; + let e = a + a + a; + let f = e.square(); + let z3 = self.z * self.y; + let z3 = z3 + z3; + let x3 = f - (d + d); + let c = c + c; + let c = c + c; + let c = c + c; + let y3 = e * (d - x3) - c; + + let tmp = G1Projective { + x: x3, + y: y3, + z: z3, + }; + + G1Projective::conditional_select(&tmp, &G1Projective::identity(), self.is_identity()) + } + + /// Adds this point to another point. + pub fn add(&self, rhs: &G1Projective) -> G1Projective { + // This Jacobian point addition technique is based on the implementation in libsecp256k1, + // which assumes that rhs has z=1. Let's address the case of zero z-coordinates generally. + + // If self is the identity, return rhs. Otherwise, return self. The other cases will be + // predicated on neither self nor rhs being the identity. + let f1 = self.is_identity(); + let res = G1Projective::conditional_select(self, rhs, f1); + let f2 = rhs.is_identity(); + + // If neither are the identity but x1 = x2 and y1 != y2, then return the identity + let z = rhs.z.square(); + let u1 = self.x * z; + let z = z * rhs.z; + let s1 = self.y * z; + let z = self.z.square(); + let u2 = rhs.x * z; + let z = z * self.z; + let s2 = rhs.y * z; + let f3 = u1.ct_eq(&u2) & (!s1.ct_eq(&s2)); + let res = + G1Projective::conditional_select(&res, &G1Projective::identity(), (!f1) & (!f2) & f3); + + let t = u1 + u2; + let m = s1 + s2; + let rr = t.square(); + let m_alt = -u2; + let tt = u1 * m_alt; + let rr = rr + tt; + + // Correct for x1 != x2 but y1 = -y2, which can occur because p - 1 is divisible by 3. + // libsecp256k1 does this by substituting in an alternative (defined) expression for lambda. + let degenerate = m.is_zero() & rr.is_zero(); + let rr_alt = s1 + s1; + let m_alt = m_alt + u1; + let rr_alt = Fp::conditional_select(&rr_alt, &rr, !degenerate); + let m_alt = Fp::conditional_select(&m_alt, &m, !degenerate); + + let n = m_alt.square(); + let q = n * t; + + let n = n.square(); + let n = Fp::conditional_select(&n, &m, degenerate); + let t = rr_alt.square(); + let z3 = m_alt * self.z * rhs.z; // We allow rhs.z != 1, so we must account for this. + let z3 = z3 + z3; + let q = -q; + let t = t + q; + let x3 = t; + let t = t + t; + let t = t + q; + let t = t * rr_alt; + let t = t + n; + let y3 = -t; + let x3 = x3 + x3; + let x3 = x3 + x3; + let y3 = y3 + y3; + let y3 = y3 + y3; + + let tmp = G1Projective { + x: x3, + y: y3, + z: z3, + }; + + G1Projective::conditional_select(&res, &tmp, (!f1) & (!f2) & (!f3)) + } + + /// Adds this point to another point in the affine model. + pub fn add_mixed(&self, rhs: &G1Affine) -> G1Projective { + // This Jacobian point addition technique is based on the implementation in libsecp256k1, + // which assumes that rhs has z=1. Let's address the case of zero z-coordinates generally. + + // If self is the identity, return rhs. Otherwise, return self. The other cases will be + // predicated on neither self nor rhs being the identity. + let f1 = self.is_identity(); + let res = G1Projective::conditional_select(self, &G1Projective::from(rhs), f1); + let f2 = rhs.is_identity(); + + // If neither are the identity but x1 = x2 and y1 != y2, then return the identity + let u1 = self.x; + let s1 = self.y; + let z = self.z.square(); + let u2 = rhs.x * z; + let z = z * self.z; + let s2 = rhs.y * z; + let f3 = u1.ct_eq(&u2) & (!s1.ct_eq(&s2)); + let res = + G1Projective::conditional_select(&res, &G1Projective::identity(), (!f1) & (!f2) & f3); + + let t = u1 + u2; + let m = s1 + s2; + let rr = t.square(); + let m_alt = -u2; + let tt = u1 * m_alt; + let rr = rr + tt; + + // Correct for x1 != x2 but y1 = -y2, which can occur because p - 1 is divisible by 3. + // libsecp256k1 does this by substituting in an alternative (defined) expression for lambda. + let degenerate = m.is_zero() & rr.is_zero(); + let rr_alt = s1 + s1; + let m_alt = m_alt + u1; + let rr_alt = Fp::conditional_select(&rr_alt, &rr, !degenerate); + let m_alt = Fp::conditional_select(&m_alt, &m, !degenerate); + + let n = m_alt.square(); + let q = n * t; + + let n = n.square(); + let n = Fp::conditional_select(&n, &m, degenerate); + let t = rr_alt.square(); + let z3 = m_alt * self.z; + let z3 = z3 + z3; + let q = -q; + let t = t + q; + let x3 = t; + let t = t + t; + let t = t + q; + let t = t * rr_alt; + let t = t + n; + let y3 = -t; + let x3 = x3 + x3; + let x3 = x3 + x3; + let y3 = y3 + y3; + let y3 = y3 + y3; + + let tmp = G1Projective { + x: x3, + y: y3, + z: z3, + }; + + G1Projective::conditional_select(&res, &tmp, (!f1) & (!f2) & (!f3)) + } + + fn multiply(&self, by: &[u8; 32]) -> G1Projective { + let mut acc = G1Projective::identity(); + + // This is a simple double-and-add implementation of point + // multiplication, moving from most significant to least + // significant bit of the scalar. + // + // We skip the leading bit because it's always unset for Fq + // elements. + for bit in by + .iter() + .rev() + .flat_map(|byte| (0..8).rev().map(move |i| Choice::from((byte >> i) & 1u8))) + .skip(1) + { + acc = acc.double(); + acc = G1Projective::conditional_select(&acc, &(acc + self), bit); + } + + acc + } + + /// Converts a batch of `G1Projective` elements into `G1Affine` elements. This + /// function will panic if `p.len() != q.len()`. + pub fn batch_normalize(p: &[Self], q: &mut [G1Affine]) { + assert_eq!(p.len(), q.len()); + + let mut acc = Fp::one(); + for (p, q) in p.iter().zip(q.iter_mut()) { + // We use the `x` field of `G1Affine` to store the product + // of previous z-coordinates seen. + q.x = acc; + + // We will end up skipping all identities in p + acc = Fp::conditional_select(&(acc * p.z), &acc, p.is_identity()); + } + + // This is the inverse, as all z-coordinates are nonzero and the ones + // that are not are skipped. + acc = acc.invert().unwrap(); + + for (p, q) in p.iter().rev().zip(q.iter_mut().rev()) { + let skip = p.is_identity(); + + // Compute tmp = 1/z + let tmp = q.x * acc; + + // Cancel out z-coordinate in denominator of `acc` + acc = Fp::conditional_select(&(acc * p.z), &acc, skip); + + // Set the coordinates to the correct value + let tmp2 = tmp.square(); + let tmp3 = tmp2 * tmp; + + q.x = p.x * tmp2; + q.y = p.y * tmp3; + q.infinity = Choice::from(0u8); + + *q = G1Affine::conditional_select(&q, &G1Affine::identity(), skip); + } + } + + /// Returns true if this element is the identity (the point at infinity). + #[inline] + pub fn is_identity(&self) -> Choice { + self.z.is_zero() + } + + /// Returns true if this point is on the curve. This should always return + /// true unless an "unchecked" API was used. + pub fn is_on_curve(&self) -> Choice { + // Y^2 - X^3 = 4(Z^6) + + (self.y.square() - (self.x.square() * self.x)) + .ct_eq(&((self.z.square() * self.z).square() * B)) + | self.z.is_zero() + } +} + +#[test] +fn test_is_on_curve() { + assert!(bool::from(G1Affine::identity().is_on_curve())); + assert!(bool::from(G1Affine::generator().is_on_curve())); + assert!(bool::from(G1Projective::identity().is_on_curve())); + assert!(bool::from(G1Projective::generator().is_on_curve())); + + let z = Fp::from_raw_unchecked([ + 0xba7afa1f9a6fe250, + 0xfa0f5b595eafe731, + 0x3bdc477694c306e7, + 0x2149be4b3949fa24, + 0x64aa6e0649b2078c, + 0x12b108ac33643c3e, + ]); + + let gen = G1Affine::generator(); + let mut test = G1Projective { + x: gen.x * (z.square()), + y: gen.y * (z.square() * z), + z, + }; + + assert!(bool::from(test.is_on_curve())); + + test.x = z; + assert!(!bool::from(test.is_on_curve())); +} + +#[test] +fn test_affine_point_equality() { + let a = G1Affine::generator(); + let b = G1Affine::identity(); + + assert!(a == a); + assert!(b == b); + assert!(a != b); + assert!(b != a); +} + +#[test] +fn test_projective_point_equality() { + let a = G1Projective::generator(); + let b = G1Projective::identity(); + + assert!(a == a); + assert!(b == b); + assert!(a != b); + assert!(b != a); + + let z = Fp::from_raw_unchecked([ + 0xba7afa1f9a6fe250, + 0xfa0f5b595eafe731, + 0x3bdc477694c306e7, + 0x2149be4b3949fa24, + 0x64aa6e0649b2078c, + 0x12b108ac33643c3e, + ]); + + let mut c = G1Projective { + x: a.x * (z.square()), + y: a.y * (z.square() * z), + z, + }; + assert!(bool::from(c.is_on_curve())); + + assert!(a == c); + assert!(b != c); + assert!(c == a); + assert!(c != b); + + c.y = -c.y; + assert!(bool::from(c.is_on_curve())); + + assert!(a != c); + assert!(b != c); + assert!(c != a); + assert!(c != b); + + c.y = -c.y; + c.x = z; + assert!(!bool::from(c.is_on_curve())); + assert!(a != b); + assert!(a != c); + assert!(b != c); +} + +#[test] +fn test_conditionally_select_affine() { + let a = G1Affine::generator(); + let b = G1Affine::identity(); + + assert_eq!(G1Affine::conditional_select(&a, &b, Choice::from(0u8)), a); + assert_eq!(G1Affine::conditional_select(&a, &b, Choice::from(1u8)), b); +} + +#[test] +fn test_conditionally_select_projective() { + let a = G1Projective::generator(); + let b = G1Projective::identity(); + + assert_eq!( + G1Projective::conditional_select(&a, &b, Choice::from(0u8)), + a + ); + assert_eq!( + G1Projective::conditional_select(&a, &b, Choice::from(1u8)), + b + ); +} + +#[test] +fn test_projective_to_affine() { + let a = G1Projective::generator(); + let b = G1Projective::identity(); + + assert!(bool::from(G1Affine::from(a).is_on_curve())); + assert!(!bool::from(G1Affine::from(a).is_identity())); + assert!(bool::from(G1Affine::from(b).is_on_curve())); + assert!(bool::from(G1Affine::from(b).is_identity())); + + let z = Fp::from_raw_unchecked([ + 0xba7afa1f9a6fe250, + 0xfa0f5b595eafe731, + 0x3bdc477694c306e7, + 0x2149be4b3949fa24, + 0x64aa6e0649b2078c, + 0x12b108ac33643c3e, + ]); + + let c = G1Projective { + x: a.x * (z.square()), + y: a.y * (z.square() * z), + z, + }; + + assert_eq!(G1Affine::from(c), G1Affine::generator()); +} + +#[test] +fn test_affine_to_projective() { + let a = G1Affine::generator(); + let b = G1Affine::identity(); + + assert!(bool::from(G1Projective::from(a).is_on_curve())); + assert!(!bool::from(G1Projective::from(a).is_identity())); + assert!(bool::from(G1Projective::from(b).is_on_curve())); + assert!(bool::from(G1Projective::from(b).is_identity())); +} + +#[test] +fn test_doubling() { + { + let tmp = G1Projective::identity().double(); + assert!(bool::from(tmp.is_identity())); + assert!(bool::from(tmp.is_on_curve())); + } + { + let tmp = G1Projective::generator().double(); + assert!(!bool::from(tmp.is_identity())); + assert!(bool::from(tmp.is_on_curve())); + + assert_eq!( + G1Affine::from(tmp), + G1Affine { + x: Fp::from_raw_unchecked([ + 0x53e978ce58a9ba3c, + 0x3ea0583c4f3d65f9, + 0x4d20bb47f0012960, + 0xa54c664ae5b2b5d9, + 0x26b552a39d7eb21f, + 0x8895d26e68785 + ]), + y: Fp::from_raw_unchecked([ + 0x70110b3298293940, + 0xda33c5393f1f6afc, + 0xb86edfd16a5aa785, + 0xaec6d1c9e7b1c895, + 0x25cfc2b522d11720, + 0x6361c83f8d09b15 + ]), + infinity: Choice::from(0u8) + } + ); + } +} + +#[test] +fn test_projective_addition() { + { + let a = G1Projective::identity(); + let b = G1Projective::identity(); + let c = a + b; + assert!(bool::from(c.is_identity())); + assert!(bool::from(c.is_on_curve())); + } + { + let a = G1Projective::identity(); + let mut b = G1Projective::generator(); + { + let z = Fp::from_raw_unchecked([ + 0xba7afa1f9a6fe250, + 0xfa0f5b595eafe731, + 0x3bdc477694c306e7, + 0x2149be4b3949fa24, + 0x64aa6e0649b2078c, + 0x12b108ac33643c3e, + ]); + + b = G1Projective { + x: b.x * (z.square()), + y: b.y * (z.square() * z), + z, + }; + } + let c = a + b; + assert!(!bool::from(c.is_identity())); + assert!(bool::from(c.is_on_curve())); + assert!(c == G1Projective::generator()); + } + { + let a = G1Projective::identity(); + let mut b = G1Projective::generator(); + { + let z = Fp::from_raw_unchecked([ + 0xba7afa1f9a6fe250, + 0xfa0f5b595eafe731, + 0x3bdc477694c306e7, + 0x2149be4b3949fa24, + 0x64aa6e0649b2078c, + 0x12b108ac33643c3e, + ]); + + b = G1Projective { + x: b.x * (z.square()), + y: b.y * (z.square() * z), + z, + }; + } + let c = b + a; + assert!(!bool::from(c.is_identity())); + assert!(bool::from(c.is_on_curve())); + assert!(c == G1Projective::generator()); + } + { + let a = G1Projective::generator().double().double(); // 4P + let b = G1Projective::generator().double(); // 2P + let c = a + b; + + let mut d = G1Projective::generator(); + for _ in 0..5 { + d = d + G1Projective::generator(); + } + assert!(!bool::from(c.is_identity())); + assert!(bool::from(c.is_on_curve())); + assert!(!bool::from(d.is_identity())); + assert!(bool::from(d.is_on_curve())); + assert_eq!(c, d); + } + + // Degenerate case + { + let beta = Fp::from_raw_unchecked([ + 0xcd03c9e48671f071, + 0x5dab22461fcda5d2, + 0x587042afd3851b95, + 0x8eb60ebe01bacb9e, + 0x3f97d6e83d050d2, + 0x18f0206554638741, + ]); + let beta = beta.square(); + let a = G1Projective::generator().double().double(); + let b = G1Projective { + x: a.x * beta, + y: -a.y, + z: a.z, + }; + assert!(bool::from(a.is_on_curve())); + assert!(bool::from(b.is_on_curve())); + + let c = a + b; + assert_eq!( + G1Affine::from(c), + G1Affine::from(G1Projective { + x: Fp::from_raw_unchecked([ + 0x29e1e987ef68f2d0, + 0xc5f3ec531db03233, + 0xacd6c4b6ca19730f, + 0x18ad9e827bc2bab7, + 0x46e3b2c5785cc7a9, + 0x7e571d42d22ddd6 + ]), + y: Fp::from_raw_unchecked([ + 0x94d117a7e5a539e7, + 0x8e17ef673d4b5d22, + 0x9d746aaf508a33ea, + 0x8c6d883d2516c9a2, + 0xbc3b8d5fb0447f7, + 0x7bfa4c7210f4f44 + ]), + z: Fp::one() + }) + ); + assert!(!bool::from(c.is_identity())); + assert!(bool::from(c.is_on_curve())); + } +} + +#[test] +fn test_mixed_addition() { + { + let a = G1Affine::identity(); + let b = G1Projective::identity(); + let c = a + b; + assert!(bool::from(c.is_identity())); + assert!(bool::from(c.is_on_curve())); + } + { + let a = G1Affine::identity(); + let mut b = G1Projective::generator(); + { + let z = Fp::from_raw_unchecked([ + 0xba7afa1f9a6fe250, + 0xfa0f5b595eafe731, + 0x3bdc477694c306e7, + 0x2149be4b3949fa24, + 0x64aa6e0649b2078c, + 0x12b108ac33643c3e, + ]); + + b = G1Projective { + x: b.x * (z.square()), + y: b.y * (z.square() * z), + z, + }; + } + let c = a + b; + assert!(!bool::from(c.is_identity())); + assert!(bool::from(c.is_on_curve())); + assert!(c == G1Projective::generator()); + } + { + let a = G1Affine::identity(); + let mut b = G1Projective::generator(); + { + let z = Fp::from_raw_unchecked([ + 0xba7afa1f9a6fe250, + 0xfa0f5b595eafe731, + 0x3bdc477694c306e7, + 0x2149be4b3949fa24, + 0x64aa6e0649b2078c, + 0x12b108ac33643c3e, + ]); + + b = G1Projective { + x: b.x * (z.square()), + y: b.y * (z.square() * z), + z, + }; + } + let c = b + a; + assert!(!bool::from(c.is_identity())); + assert!(bool::from(c.is_on_curve())); + assert!(c == G1Projective::generator()); + } + { + let a = G1Projective::generator().double().double(); // 4P + let b = G1Projective::generator().double(); // 2P + let c = a + b; + + let mut d = G1Projective::generator(); + for _ in 0..5 { + d = d + G1Affine::generator(); + } + assert!(!bool::from(c.is_identity())); + assert!(bool::from(c.is_on_curve())); + assert!(!bool::from(d.is_identity())); + assert!(bool::from(d.is_on_curve())); + assert_eq!(c, d); + } + + // Degenerate case + { + let beta = Fp::from_raw_unchecked([ + 0xcd03c9e48671f071, + 0x5dab22461fcda5d2, + 0x587042afd3851b95, + 0x8eb60ebe01bacb9e, + 0x3f97d6e83d050d2, + 0x18f0206554638741, + ]); + let beta = beta.square(); + let a = G1Projective::generator().double().double(); + let b = G1Projective { + x: a.x * beta, + y: -a.y, + z: a.z, + }; + let a = G1Affine::from(a); + assert!(bool::from(a.is_on_curve())); + assert!(bool::from(b.is_on_curve())); + + let c = a + b; + assert_eq!( + G1Affine::from(c), + G1Affine::from(G1Projective { + x: Fp::from_raw_unchecked([ + 0x29e1e987ef68f2d0, + 0xc5f3ec531db03233, + 0xacd6c4b6ca19730f, + 0x18ad9e827bc2bab7, + 0x46e3b2c5785cc7a9, + 0x7e571d42d22ddd6 + ]), + y: Fp::from_raw_unchecked([ + 0x94d117a7e5a539e7, + 0x8e17ef673d4b5d22, + 0x9d746aaf508a33ea, + 0x8c6d883d2516c9a2, + 0xbc3b8d5fb0447f7, + 0x7bfa4c7210f4f44 + ]), + z: Fp::one() + }) + ); + assert!(!bool::from(c.is_identity())); + assert!(bool::from(c.is_on_curve())); + } +} + +#[test] +fn test_projective_negation_and_subtraction() { + let a = G1Projective::generator().double(); + assert_eq!(a + (-a), G1Projective::identity()); + assert_eq!(a + (-a), a - a); +} + +#[test] +fn test_affine_negation_and_subtraction() { + let a = G1Affine::generator(); + assert_eq!(G1Projective::from(a) + (-a), G1Projective::identity()); + assert_eq!(G1Projective::from(a) + (-a), G1Projective::from(a) - a); +} + +#[test] +fn test_projective_scalar_multiplication() { + let g = G1Projective::generator(); + let a = Scalar::from_raw([ + 0x2b568297a56da71c, + 0xd8c39ecb0ef375d1, + 0x435c38da67bfbf96, + 0x8088a05026b659b2, + ]); + let b = Scalar::from_raw([ + 0x785fdd9b26ef8b85, + 0xc997f25837695c18, + 0x4c8dbc39e7b756c1, + 0x70d9b6cc6d87df20, + ]); + let c = a * b; + + assert_eq!((g * a) * b, g * c); +} + +#[test] +fn test_affine_scalar_multiplication() { + let g = G1Affine::generator(); + let a = Scalar::from_raw([ + 0x2b568297a56da71c, + 0xd8c39ecb0ef375d1, + 0x435c38da67bfbf96, + 0x8088a05026b659b2, + ]); + let b = Scalar::from_raw([ + 0x785fdd9b26ef8b85, + 0xc997f25837695c18, + 0x4c8dbc39e7b756c1, + 0x70d9b6cc6d87df20, + ]); + let c = a * b; + + assert_eq!(G1Affine::from(g * a) * b, g * c); +} + +#[test] +fn test_is_torsion_free() { + let a = G1Affine { + x: Fp::from_raw_unchecked([ + 0xabaf895b97e43c8, + 0xba4c6432eb9b61b0, + 0x12506f52adfe307f, + 0x75028c3439336b72, + 0x84744f05b8e9bd71, + 0x113d554fb09554f7, + ]), + y: Fp::from_raw_unchecked([ + 0x73e90e88f5cf01c0, + 0x37007b65dd3197e2, + 0x5cf9a1992f0d7c78, + 0x4f83c10b9eb3330d, + 0xf6a63f6f07f60961, + 0xc53b5b97e634df3, + ]), + infinity: Choice::from(0u8), + }; + assert!(!bool::from(a.is_torsion_free())); + + assert!(bool::from(G1Affine::identity().is_torsion_free())); + assert!(bool::from(G1Affine::generator().is_torsion_free())); +} + +#[test] +fn test_batch_normalize() { + let a = G1Projective::generator().double(); + let b = a.double(); + let c = b.double(); + + for a_identity in (0..1).map(|n| n == 1) { + for b_identity in (0..1).map(|n| n == 1) { + for c_identity in (0..1).map(|n| n == 1) { + let mut v = [a, b, c]; + if a_identity { + v[0] = G1Projective::identity() + } + if b_identity { + v[1] = G1Projective::identity() + } + if c_identity { + v[2] = G1Projective::identity() + } + + let mut t = [ + G1Affine::identity(), + G1Affine::identity(), + G1Affine::identity(), + ]; + let expected = [ + G1Affine::from(v[0]), + G1Affine::from(v[1]), + G1Affine::from(v[2]), + ]; + + G1Projective::batch_normalize(&v[..], &mut t[..]); + + assert_eq!(&t[..], &expected[..]); + } + } + } +} diff --git a/bls12_381/src/g2.rs b/bls12_381/src/g2.rs new file mode 100644 index 0000000..136cd03 --- /dev/null +++ b/bls12_381/src/g2.rs @@ -0,0 +1,1591 @@ +//! This module provides an implementation of the $\mathbb{G}_2$ group of BLS12-381. + +use core::ops::{Add, AddAssign, Mul, MulAssign, Neg, Sub, SubAssign}; + +use subtle::{Choice, ConditionallySelectable, ConstantTimeEq, CtOption}; + +use crate::fp::Fp; +use crate::fp2::Fp2; +use crate::Scalar; + +/// This is an element of $\mathbb{G}_2$ represented in the affine coordinate space. +/// It is ideal to keep elements in this representation to reduce memory usage and +/// improve performance through the use of mixed curve model arithmetic. +/// +/// Values of `G2Affine` are guaranteed to be in the $q$-order subgroup unless an +/// "unchecked" API was misused. +#[derive(Copy, Clone, Debug)] +pub struct G2Affine { + pub(crate) x: Fp2, + pub(crate) y: Fp2, + infinity: Choice, +} + +impl Default for G2Affine { + fn default() -> G2Affine { + G2Affine::identity() + } +} + +impl<'a> From<&'a G2Projective> for G2Affine { + fn from(p: &'a G2Projective) -> G2Affine { + let zinv = p.z.invert().unwrap_or(Fp2::zero()); + let zinv2 = zinv.square(); + let x = p.x * zinv2; + let zinv3 = zinv2 * zinv; + let y = p.y * zinv3; + + let tmp = G2Affine { + x, + y, + infinity: Choice::from(0u8), + }; + + G2Affine::conditional_select(&tmp, &G2Affine::identity(), zinv.is_zero()) + } +} + +impl From for G2Affine { + fn from(p: G2Projective) -> G2Affine { + G2Affine::from(&p) + } +} + +impl ConstantTimeEq for G2Affine { + fn ct_eq(&self, other: &Self) -> Choice { + // The only cases in which two points are equal are + // 1. infinity is set on both + // 2. infinity is not set on both, and their coordinates are equal + + (self.infinity & other.infinity) + | ((!self.infinity) + & (!other.infinity) + & self.x.ct_eq(&other.x) + & self.y.ct_eq(&other.y)) + } +} + +impl ConditionallySelectable for G2Affine { + fn conditional_select(a: &Self, b: &Self, choice: Choice) -> Self { + G2Affine { + x: Fp2::conditional_select(&a.x, &b.x, choice), + y: Fp2::conditional_select(&a.y, &b.y, choice), + infinity: Choice::conditional_select(&a.infinity, &b.infinity, choice), + } + } +} + +impl Eq for G2Affine {} +impl PartialEq for G2Affine { + #[inline] + fn eq(&self, other: &Self) -> bool { + bool::from(self.ct_eq(other)) + } +} + +impl<'a> Neg for &'a G2Affine { + type Output = G2Affine; + + #[inline] + fn neg(self) -> G2Affine { + G2Affine { + x: self.x, + y: Fp2::conditional_select(&-self.y, &Fp2::one(), self.infinity), + infinity: self.infinity, + } + } +} + +impl Neg for G2Affine { + type Output = G2Affine; + + #[inline] + fn neg(self) -> G2Affine { + -&self + } +} + +impl<'a, 'b> Add<&'b G2Projective> for &'a G2Affine { + type Output = G2Projective; + + #[inline] + fn add(self, rhs: &'b G2Projective) -> G2Projective { + rhs.add_mixed(self) + } +} + +impl<'a, 'b> Add<&'b G2Affine> for &'a G2Projective { + type Output = G2Projective; + + #[inline] + fn add(self, rhs: &'b G2Affine) -> G2Projective { + self.add_mixed(rhs) + } +} + +impl<'a, 'b> Sub<&'b G2Projective> for &'a G2Affine { + type Output = G2Projective; + + #[inline] + fn sub(self, rhs: &'b G2Projective) -> G2Projective { + self + (-rhs) + } +} + +impl<'a, 'b> Sub<&'b G2Affine> for &'a G2Projective { + type Output = G2Projective; + + #[inline] + fn sub(self, rhs: &'b G2Affine) -> G2Projective { + self + (-rhs) + } +} + +impl_binops_additive!(G2Projective, G2Affine); +impl_binops_additive_specify_output!(G2Affine, G2Projective, G2Projective); + +const B: Fp2 = Fp2 { + c0: Fp::from_raw_unchecked([ + 0xaa270000000cfff3, + 0x53cc0032fc34000a, + 0x478fe97a6b0a807f, + 0xb1d37ebee6ba24d7, + 0x8ec9733bbf78ab2f, + 0x9d645513d83de7e, + ]), + c1: Fp::from_raw_unchecked([ + 0xaa270000000cfff3, + 0x53cc0032fc34000a, + 0x478fe97a6b0a807f, + 0xb1d37ebee6ba24d7, + 0x8ec9733bbf78ab2f, + 0x9d645513d83de7e, + ]), +}; + +impl G2Affine { + /// Returns the identity of the group: the point at infinity. + pub fn identity() -> G2Affine { + G2Affine { + x: Fp2::zero(), + y: Fp2::one(), + infinity: Choice::from(1u8), + } + } + + /// Returns a fixed generator of the group. See [`notes::design`](notes/design/index.html#fixed-generators) + /// for how this generator is chosen. + pub fn generator() -> G2Affine { + G2Affine { + x: Fp2 { + c0: Fp::from_raw_unchecked([ + 0xf5f28fa202940a10, + 0xb3f5fb2687b4961a, + 0xa1a893b53e2ae580, + 0x9894999d1a3caee9, + 0x6f67b7631863366b, + 0x58191924350bcd7, + ]), + c1: Fp::from_raw_unchecked([ + 0xa5a9c0759e23f606, + 0xaaa0c59dbccd60c3, + 0x3bb17e18e2867806, + 0x1b1ab6cc8541b367, + 0xc2b6ed0ef2158547, + 0x11922a097360edf3, + ]), + }, + y: Fp2 { + c0: Fp::from_raw_unchecked([ + 0x4c730af860494c4a, + 0x597cfa1f5e369c5a, + 0xe7e6856caa0a635a, + 0xbbefb5e96e0d495f, + 0x7d3a975f0ef25a2, + 0x83fd8e7e80dae5, + ]), + c1: Fp::from_raw_unchecked([ + 0xadc0fc92df64b05d, + 0x18aa270a2b1461dc, + 0x86adac6a3be4eba0, + 0x79495c4ec93da33a, + 0xe7175850a43ccaed, + 0xb2bc2a163de1bf2, + ]), + }, + infinity: Choice::from(0u8), + } + } + + /// Serializes this element into compressed form. See [`notes::serialization`](crate::notes::serialization) + /// for details about how group elements are serialized. + pub fn to_compressed(&self) -> [u8; 96] { + // Strictly speaking, self.x is zero already when self.infinity is true, but + // to guard against implementation mistakes we do not assume this. + let x = Fp2::conditional_select(&self.x, &Fp2::zero(), self.infinity); + + let mut res = [0; 96]; + + (&mut res[0..48]).copy_from_slice(&x.c1.to_bytes()[..]); + (&mut res[48..96]).copy_from_slice(&x.c0.to_bytes()[..]); + + // This point is in compressed form, so we set the most significant bit. + res[0] |= 1u8 << 7; + + // Is this point at infinity? If so, set the second-most significant bit. + res[0] |= u8::conditional_select(&0u8, &(1u8 << 6), self.infinity); + + // Is the y-coordinate the lexicographically largest of the two associated with the + // x-coordinate? If so, set the third-most significant bit so long as this is not + // the point at infinity. + res[0] |= u8::conditional_select( + &0u8, + &(1u8 << 5), + (!self.infinity) & self.y.lexicographically_largest(), + ); + + res + } + + /// Serializes this element into uncompressed form. See [`notes::serialization`](crate::notes::serialization) + /// for details about how group elements are serialized. + pub fn to_uncompressed(&self) -> [u8; 192] { + let mut res = [0; 192]; + + let x = Fp2::conditional_select(&self.x, &Fp2::zero(), self.infinity); + let y = Fp2::conditional_select(&self.y, &Fp2::zero(), self.infinity); + + res[0..48].copy_from_slice(&x.c1.to_bytes()[..]); + res[48..96].copy_from_slice(&x.c0.to_bytes()[..]); + res[96..144].copy_from_slice(&y.c1.to_bytes()[..]); + res[144..192].copy_from_slice(&y.c0.to_bytes()[..]); + + // Is this point at infinity? If so, set the second-most significant bit. + res[0] |= u8::conditional_select(&0u8, &(1u8 << 6), self.infinity); + + res + } + + /// Attempts to deserialize an uncompressed element. See [`notes::serialization`](crate::notes::serialization) + /// for details about how group elements are serialized. + pub fn from_uncompressed(bytes: &[u8; 192]) -> CtOption { + Self::from_uncompressed_unchecked(bytes) + .and_then(|p| CtOption::new(p, p.is_on_curve() & p.is_torsion_free())) + } + + /// Attempts to deserialize an uncompressed element, not checking if the + /// element is on the curve and not checking if it is in the correct subgroup. + /// **This is dangerous to call unless you trust the bytes you are reading; otherwise, + /// API invariants may be broken.** Please consider using `from_uncompressed()` instead. + pub fn from_uncompressed_unchecked(bytes: &[u8; 192]) -> CtOption { + // Obtain the three flags from the start of the byte sequence + let compression_flag_set = Choice::from((bytes[0] >> 7) & 1); + let infinity_flag_set = Choice::from((bytes[0] >> 6) & 1); + let sort_flag_set = Choice::from((bytes[0] >> 5) & 1); + + // Attempt to obtain the x-coordinate + let xc1 = { + let mut tmp = [0; 48]; + tmp.copy_from_slice(&bytes[0..48]); + + // Mask away the flag bits + tmp[0] &= 0b0001_1111; + + Fp::from_bytes(&tmp) + }; + let xc0 = { + let mut tmp = [0; 48]; + tmp.copy_from_slice(&bytes[48..96]); + + Fp::from_bytes(&tmp) + }; + + // Attempt to obtain the y-coordinate + let yc1 = { + let mut tmp = [0; 48]; + tmp.copy_from_slice(&bytes[96..144]); + + Fp::from_bytes(&tmp) + }; + let yc0 = { + let mut tmp = [0; 48]; + tmp.copy_from_slice(&bytes[144..192]); + + Fp::from_bytes(&tmp) + }; + + xc1.and_then(|xc1| { + xc0.and_then(|xc0| { + yc1.and_then(|yc1| { + yc0.and_then(|yc0| { + let x = Fp2 { + c0: xc0, + c1: xc1 + }; + let y = Fp2 { + c0: yc0, + c1: yc1 + }; + + // Create a point representing this value + let p = G2Affine::conditional_select( + &G2Affine { + x, + y, + infinity: infinity_flag_set, + }, + &G2Affine::identity(), + infinity_flag_set, + ); + + CtOption::new( + p, + // If the infinity flag is set, the x and y coordinates should have been zero. + ((!infinity_flag_set) | (infinity_flag_set & x.is_zero() & y.is_zero())) & + // The compression flag should not have been set, as this is an uncompressed element + (!compression_flag_set) & + // The sort flag should not have been set, as this is an uncompressed element + (!sort_flag_set), + ) + }) + }) + }) + }) + } + + /// Attempts to deserialize a compressed element. See [`notes::serialization`](crate::notes::serialization) + /// for details about how group elements are serialized. + pub fn from_compressed(bytes: &[u8; 96]) -> CtOption { + // We already know the point is on the curve because this is established + // by the y-coordinate recovery procedure in from_compressed_unchecked(). + + Self::from_compressed_unchecked(bytes).and_then(|p| CtOption::new(p, p.is_torsion_free())) + } + + /// Attempts to deserialize an uncompressed element, not checking if the + /// element is in the correct subgroup. + /// **This is dangerous to call unless you trust the bytes you are reading; otherwise, + /// API invariants may be broken.** Please consider using `from_compressed()` instead. + pub fn from_compressed_unchecked(bytes: &[u8; 96]) -> CtOption { + // Obtain the three flags from the start of the byte sequence + let compression_flag_set = Choice::from((bytes[0] >> 7) & 1); + let infinity_flag_set = Choice::from((bytes[0] >> 6) & 1); + let sort_flag_set = Choice::from((bytes[0] >> 5) & 1); + + // Attempt to obtain the x-coordinate + let xc1 = { + let mut tmp = [0; 48]; + tmp.copy_from_slice(&bytes[0..48]); + + // Mask away the flag bits + tmp[0] &= 0b0001_1111; + + Fp::from_bytes(&tmp) + }; + let xc0 = { + let mut tmp = [0; 48]; + tmp.copy_from_slice(&bytes[48..96]); + + Fp::from_bytes(&tmp) + }; + + xc1.and_then(|xc1| { + xc0.and_then(|xc0| { + let x = Fp2 { c0: xc0, c1: xc1 }; + + // If the infinity flag is set, return the value assuming + // the x-coordinate is zero and the sort bit is not set. + // + // Otherwise, return a recovered point (assuming the correct + // y-coordinate can be found) so long as the infinity flag + // was not set. + CtOption::new( + G2Affine::identity(), + infinity_flag_set & // Infinity flag should be set + compression_flag_set & // Compression flag should be set + (!sort_flag_set) & // Sort flag should not be set + x.is_zero(), // The x-coordinate should be zero + ) + .or_else(|| { + // Recover a y-coordinate given x by y = sqrt(x^3 + 4) + ((x.square() * x) + B).sqrt().and_then(|y| { + // Switch to the correct y-coordinate if necessary. + let y = Fp2::conditional_select( + &y, + &-y, + y.lexicographically_largest() ^ sort_flag_set, + ); + + CtOption::new( + G2Affine { + x, + y, + infinity: infinity_flag_set, + }, + (!infinity_flag_set) & // Infinity flag should not be set + compression_flag_set, // Compression flag should be set + ) + }) + }) + }) + }) + } + + /// Returns true if this element is the identity (the point at infinity). + #[inline] + pub fn is_identity(&self) -> Choice { + self.infinity + } + + /// Returns true if this point is free of an $h$-torsion component, and so it + /// exists within the $q$-order subgroup $\mathbb{G}_2$. This should always return true + /// unless an "unchecked" API was used. + pub fn is_torsion_free(&self) -> Choice { + const FQ_MODULUS_BYTES: [u8; 32] = [ + 1, 0, 0, 0, 255, 255, 255, 255, 254, 91, 254, 255, 2, 164, 189, 83, 5, 216, 161, 9, 8, + 216, 57, 51, 72, 125, 157, 41, 83, 167, 237, 115, + ]; + + // Clear the r-torsion from the point and check if it is the identity + G2Projective::from(*self) + .multiply(&FQ_MODULUS_BYTES) + .is_identity() + } + + /// Returns true if this point is on the curve. This should always return + /// true unless an "unchecked" API was used. + pub fn is_on_curve(&self) -> Choice { + // y^2 - x^3 ?= 4(u + 1) + (self.y.square() - (self.x.square() * self.x)).ct_eq(&B) | self.infinity + } +} + +/// This is an element of $\mathbb{G}_2$ represented in the projective coordinate space. +#[derive(Copy, Clone, Debug)] +pub struct G2Projective { + pub(crate) x: Fp2, + pub(crate) y: Fp2, + pub(crate) z: Fp2, +} + +impl<'a> From<&'a G2Affine> for G2Projective { + fn from(p: &'a G2Affine) -> G2Projective { + G2Projective { + x: p.x, + y: p.y, + z: Fp2::conditional_select(&Fp2::one(), &Fp2::zero(), p.infinity), + } + } +} + +impl From for G2Projective { + fn from(p: G2Affine) -> G2Projective { + G2Projective::from(&p) + } +} + +impl ConstantTimeEq for G2Projective { + fn ct_eq(&self, other: &Self) -> Choice { + // Is (xz^2, yz^3, z) equal to (x'z'^2, yz'^3, z') when converted to affine? + + let z = other.z.square(); + let x1 = self.x * z; + let z = z * other.z; + let y1 = self.y * z; + let z = self.z.square(); + let x2 = other.x * z; + let z = z * self.z; + let y2 = other.y * z; + + let self_is_zero = self.z.is_zero(); + let other_is_zero = other.z.is_zero(); + + (self_is_zero & other_is_zero) // Both point at infinity + | ((!self_is_zero) & (!other_is_zero) & x1.ct_eq(&x2) & y1.ct_eq(&y2)) // Neither point at infinity, coordinates are the same + } +} + +impl ConditionallySelectable for G2Projective { + fn conditional_select(a: &Self, b: &Self, choice: Choice) -> Self { + G2Projective { + x: Fp2::conditional_select(&a.x, &b.x, choice), + y: Fp2::conditional_select(&a.y, &b.y, choice), + z: Fp2::conditional_select(&a.z, &b.z, choice), + } + } +} + +impl Eq for G2Projective {} +impl PartialEq for G2Projective { + #[inline] + fn eq(&self, other: &Self) -> bool { + bool::from(self.ct_eq(other)) + } +} + +impl<'a> Neg for &'a G2Projective { + type Output = G2Projective; + + #[inline] + fn neg(self) -> G2Projective { + G2Projective { + x: self.x, + y: -self.y, + z: self.z, + } + } +} + +impl Neg for G2Projective { + type Output = G2Projective; + + #[inline] + fn neg(self) -> G2Projective { + -&self + } +} + +impl<'a, 'b> Add<&'b G2Projective> for &'a G2Projective { + type Output = G2Projective; + + #[inline] + fn add(self, rhs: &'b G2Projective) -> G2Projective { + self.add(rhs) + } +} + +impl<'a, 'b> Sub<&'b G2Projective> for &'a G2Projective { + type Output = G2Projective; + + #[inline] + fn sub(self, rhs: &'b G2Projective) -> G2Projective { + self + (-rhs) + } +} + +impl<'a, 'b> Mul<&'b Scalar> for &'a G2Projective { + type Output = G2Projective; + + fn mul(self, other: &'b Scalar) -> Self::Output { + self.multiply(&other.to_bytes()) + } +} + +impl<'a, 'b> Mul<&'b Scalar> for &'a G2Affine { + type Output = G2Projective; + + fn mul(self, other: &'b Scalar) -> Self::Output { + G2Projective::from(self).multiply(&other.to_bytes()) + } +} + +impl_binops_additive!(G2Projective, G2Projective); +impl_binops_multiplicative!(G2Projective, Scalar); +impl_binops_multiplicative_mixed!(G2Affine, Scalar, G2Projective); + +impl G2Projective { + /// Returns the identity of the group: the point at infinity. + pub fn identity() -> G2Projective { + G2Projective { + x: Fp2::zero(), + y: Fp2::one(), + z: Fp2::zero(), + } + } + + /// Returns a fixed generator of the group. See [`notes::design`](notes/design/index.html#fixed-generators) + /// for how this generator is chosen. + pub fn generator() -> G2Projective { + G2Projective { + x: Fp2 { + c0: Fp::from_raw_unchecked([ + 0xf5f28fa202940a10, + 0xb3f5fb2687b4961a, + 0xa1a893b53e2ae580, + 0x9894999d1a3caee9, + 0x6f67b7631863366b, + 0x58191924350bcd7, + ]), + c1: Fp::from_raw_unchecked([ + 0xa5a9c0759e23f606, + 0xaaa0c59dbccd60c3, + 0x3bb17e18e2867806, + 0x1b1ab6cc8541b367, + 0xc2b6ed0ef2158547, + 0x11922a097360edf3, + ]), + }, + y: Fp2 { + c0: Fp::from_raw_unchecked([ + 0x4c730af860494c4a, + 0x597cfa1f5e369c5a, + 0xe7e6856caa0a635a, + 0xbbefb5e96e0d495f, + 0x7d3a975f0ef25a2, + 0x83fd8e7e80dae5, + ]), + c1: Fp::from_raw_unchecked([ + 0xadc0fc92df64b05d, + 0x18aa270a2b1461dc, + 0x86adac6a3be4eba0, + 0x79495c4ec93da33a, + 0xe7175850a43ccaed, + 0xb2bc2a163de1bf2, + ]), + }, + z: Fp2::one(), + } + } + + /// Computes the doubling of this point. + pub fn double(&self) -> G2Projective { + // http://www.hyperelliptic.org/EFD/g2p/auto-shortw-jacobian-0.html#doubling-dbl-2009-l + // + // There are no points of order 2. + + let a = self.x.square(); + let b = self.y.square(); + let c = b.square(); + let d = self.x + b; + let d = d.square(); + let d = d - a - c; + let d = d + d; + let e = a + a + a; + let f = e.square(); + let z3 = self.z * self.y; + let z3 = z3 + z3; + let x3 = f - (d + d); + let c = c + c; + let c = c + c; + let c = c + c; + let y3 = e * (d - x3) - c; + + let tmp = G2Projective { + x: x3, + y: y3, + z: z3, + }; + + G2Projective::conditional_select(&tmp, &G2Projective::identity(), self.is_identity()) + } + + /// Adds this point to another point. + pub fn add(&self, rhs: &G2Projective) -> G2Projective { + // This Jacobian point addition technique is based on the implementation in libsecp256k1, + // which assumes that rhs has z=1. Let's address the case of zero z-coordinates generally. + + // If self is the identity, return rhs. Otherwise, return self. The other cases will be + // predicated on neither self nor rhs being the identity. + let f1 = self.is_identity(); + let res = G2Projective::conditional_select(self, rhs, f1); + let f2 = rhs.is_identity(); + + // If neither are the identity but x1 = x2 and y1 != y2, then return the identity + let z = rhs.z.square(); + let u1 = self.x * z; + let z = z * rhs.z; + let s1 = self.y * z; + let z = self.z.square(); + let u2 = rhs.x * z; + let z = z * self.z; + let s2 = rhs.y * z; + let f3 = u1.ct_eq(&u2) & (!s1.ct_eq(&s2)); + let res = + G2Projective::conditional_select(&res, &G2Projective::identity(), (!f1) & (!f2) & f3); + + let t = u1 + u2; + let m = s1 + s2; + let rr = t.square(); + let m_alt = -u2; + let tt = u1 * m_alt; + let rr = rr + tt; + + // Correct for x1 != x2 but y1 = -y2, which can occur because p - 1 is divisible by 3. + // libsecp256k1 does this by substituting in an alternative (defined) expression for lambda. + let degenerate = m.is_zero() & rr.is_zero(); + let rr_alt = s1 + s1; + let m_alt = m_alt + u1; + let rr_alt = Fp2::conditional_select(&rr_alt, &rr, !degenerate); + let m_alt = Fp2::conditional_select(&m_alt, &m, !degenerate); + + let n = m_alt.square(); + let q = n * t; + + let n = n.square(); + let n = Fp2::conditional_select(&n, &m, degenerate); + let t = rr_alt.square(); + let z3 = m_alt * self.z * rhs.z; // We allow rhs.z != 1, so we must account for this. + let z3 = z3 + z3; + let q = -q; + let t = t + q; + let x3 = t; + let t = t + t; + let t = t + q; + let t = t * rr_alt; + let t = t + n; + let y3 = -t; + let x3 = x3 + x3; + let x3 = x3 + x3; + let y3 = y3 + y3; + let y3 = y3 + y3; + + let tmp = G2Projective { + x: x3, + y: y3, + z: z3, + }; + + G2Projective::conditional_select(&res, &tmp, (!f1) & (!f2) & (!f3)) + } + + /// Adds this point to another point in the affine model. + pub fn add_mixed(&self, rhs: &G2Affine) -> G2Projective { + // This Jacobian point addition technique is based on the implementation in libsecp256k1, + // which assumes that rhs has z=1. Let's address the case of zero z-coordinates generally. + + // If self is the identity, return rhs. Otherwise, return self. The other cases will be + // predicated on neither self nor rhs being the identity. + let f1 = self.is_identity(); + let res = G2Projective::conditional_select(self, &G2Projective::from(rhs), f1); + let f2 = rhs.is_identity(); + + // If neither are the identity but x1 = x2 and y1 != y2, then return the identity + let u1 = self.x; + let s1 = self.y; + let z = self.z.square(); + let u2 = rhs.x * z; + let z = z * self.z; + let s2 = rhs.y * z; + let f3 = u1.ct_eq(&u2) & (!s1.ct_eq(&s2)); + let res = + G2Projective::conditional_select(&res, &G2Projective::identity(), (!f1) & (!f2) & f3); + + let t = u1 + u2; + let m = s1 + s2; + let rr = t.square(); + let m_alt = -u2; + let tt = u1 * m_alt; + let rr = rr + tt; + + // Correct for x1 != x2 but y1 = -y2, which can occur because p - 1 is divisible by 3. + // libsecp256k1 does this by substituting in an alternative (defined) expression for lambda. + let degenerate = m.is_zero() & rr.is_zero(); + let rr_alt = s1 + s1; + let m_alt = m_alt + u1; + let rr_alt = Fp2::conditional_select(&rr_alt, &rr, !degenerate); + let m_alt = Fp2::conditional_select(&m_alt, &m, !degenerate); + + let n = m_alt.square(); + let q = n * t; + + let n = n.square(); + let n = Fp2::conditional_select(&n, &m, degenerate); + let t = rr_alt.square(); + let z3 = m_alt * self.z; + let z3 = z3 + z3; + let q = -q; + let t = t + q; + let x3 = t; + let t = t + t; + let t = t + q; + let t = t * rr_alt; + let t = t + n; + let y3 = -t; + let x3 = x3 + x3; + let x3 = x3 + x3; + let y3 = y3 + y3; + let y3 = y3 + y3; + + let tmp = G2Projective { + x: x3, + y: y3, + z: z3, + }; + + G2Projective::conditional_select(&res, &tmp, (!f1) & (!f2) & (!f3)) + } + + fn multiply(&self, by: &[u8; 32]) -> G2Projective { + let mut acc = G2Projective::identity(); + + // This is a simple double-and-add implementation of point + // multiplication, moving from most significant to least + // significant bit of the scalar. + // + // We skip the leading bit because it's always unset for Fq + // elements. + for bit in by + .iter() + .rev() + .flat_map(|byte| (0..8).rev().map(move |i| Choice::from((byte >> i) & 1u8))) + .skip(1) + { + acc = acc.double(); + acc = G2Projective::conditional_select(&acc, &(acc + self), bit); + } + + acc + } + + /// Converts a batch of `G2Projective` elements into `G2Affine` elements. This + /// function will panic if `p.len() != q.len()`. + pub fn batch_normalize(p: &[Self], q: &mut [G2Affine]) { + assert_eq!(p.len(), q.len()); + + let mut acc = Fp2::one(); + for (p, q) in p.iter().zip(q.iter_mut()) { + // We use the `x` field of `G2Affine` to store the product + // of previous z-coordinates seen. + q.x = acc; + + // We will end up skipping all identities in p + acc = Fp2::conditional_select(&(acc * p.z), &acc, p.is_identity()); + } + + // This is the inverse, as all z-coordinates are nonzero and the ones + // that are not are skipped. + acc = acc.invert().unwrap(); + + for (p, q) in p.iter().rev().zip(q.iter_mut().rev()) { + let skip = p.is_identity(); + + // Compute tmp = 1/z + let tmp = q.x * acc; + + // Cancel out z-coordinate in denominator of `acc` + acc = Fp2::conditional_select(&(acc * p.z), &acc, skip); + + // Set the coordinates to the correct value + let tmp2 = tmp.square(); + let tmp3 = tmp2 * tmp; + + q.x = p.x * tmp2; + q.y = p.y * tmp3; + q.infinity = Choice::from(0u8); + + *q = G2Affine::conditional_select(&q, &G2Affine::identity(), skip); + } + } + + /// Returns true if this element is the identity (the point at infinity). + #[inline] + pub fn is_identity(&self) -> Choice { + self.z.is_zero() + } + + /// Returns true if this point is on the curve. This should always return + /// true unless an "unchecked" API was used. + pub fn is_on_curve(&self) -> Choice { + // Y^2 - X^3 = 4(u + 1)(Z^6) + + (self.y.square() - (self.x.square() * self.x)) + .ct_eq(&((self.z.square() * self.z).square() * B)) + | self.z.is_zero() + } +} + +#[test] +fn test_is_on_curve() { + assert!(bool::from(G2Affine::identity().is_on_curve())); + assert!(bool::from(G2Affine::generator().is_on_curve())); + assert!(bool::from(G2Projective::identity().is_on_curve())); + assert!(bool::from(G2Projective::generator().is_on_curve())); + + let z = Fp2 { + c0: Fp::from_raw_unchecked([ + 0xba7afa1f9a6fe250, + 0xfa0f5b595eafe731, + 0x3bdc477694c306e7, + 0x2149be4b3949fa24, + 0x64aa6e0649b2078c, + 0x12b108ac33643c3e, + ]), + c1: Fp::from_raw_unchecked([ + 0x125325df3d35b5a8, + 0xdc469ef5555d7fe3, + 0x2d716d2443106a9, + 0x5a1db59a6ff37d0, + 0x7cf7784e5300bb8f, + 0x16a88922c7a5e844, + ]), + }; + + let gen = G2Affine::generator(); + let mut test = G2Projective { + x: gen.x * (z.square()), + y: gen.y * (z.square() * z), + z, + }; + + assert!(bool::from(test.is_on_curve())); + + test.x = z; + assert!(!bool::from(test.is_on_curve())); +} + +#[test] +fn test_affine_point_equality() { + let a = G2Affine::generator(); + let b = G2Affine::identity(); + + assert!(a == a); + assert!(b == b); + assert!(a != b); + assert!(b != a); +} + +#[test] +fn test_projective_point_equality() { + let a = G2Projective::generator(); + let b = G2Projective::identity(); + + assert!(a == a); + assert!(b == b); + assert!(a != b); + assert!(b != a); + + let z = Fp2 { + c0: Fp::from_raw_unchecked([ + 0xba7afa1f9a6fe250, + 0xfa0f5b595eafe731, + 0x3bdc477694c306e7, + 0x2149be4b3949fa24, + 0x64aa6e0649b2078c, + 0x12b108ac33643c3e, + ]), + c1: Fp::from_raw_unchecked([ + 0x125325df3d35b5a8, + 0xdc469ef5555d7fe3, + 0x2d716d2443106a9, + 0x5a1db59a6ff37d0, + 0x7cf7784e5300bb8f, + 0x16a88922c7a5e844, + ]), + }; + + let mut c = G2Projective { + x: a.x * (z.square()), + y: a.y * (z.square() * z), + z, + }; + assert!(bool::from(c.is_on_curve())); + + assert!(a == c); + assert!(b != c); + assert!(c == a); + assert!(c != b); + + c.y = -c.y; + assert!(bool::from(c.is_on_curve())); + + assert!(a != c); + assert!(b != c); + assert!(c != a); + assert!(c != b); + + c.y = -c.y; + c.x = z; + assert!(!bool::from(c.is_on_curve())); + assert!(a != b); + assert!(a != c); + assert!(b != c); +} + +#[test] +fn test_conditionally_select_affine() { + let a = G2Affine::generator(); + let b = G2Affine::identity(); + + assert_eq!(G2Affine::conditional_select(&a, &b, Choice::from(0u8)), a); + assert_eq!(G2Affine::conditional_select(&a, &b, Choice::from(1u8)), b); +} + +#[test] +fn test_conditionally_select_projective() { + let a = G2Projective::generator(); + let b = G2Projective::identity(); + + assert_eq!( + G2Projective::conditional_select(&a, &b, Choice::from(0u8)), + a + ); + assert_eq!( + G2Projective::conditional_select(&a, &b, Choice::from(1u8)), + b + ); +} + +#[test] +fn test_projective_to_affine() { + let a = G2Projective::generator(); + let b = G2Projective::identity(); + + assert!(bool::from(G2Affine::from(a).is_on_curve())); + assert!(!bool::from(G2Affine::from(a).is_identity())); + assert!(bool::from(G2Affine::from(b).is_on_curve())); + assert!(bool::from(G2Affine::from(b).is_identity())); + + let z = Fp2 { + c0: Fp::from_raw_unchecked([ + 0xba7afa1f9a6fe250, + 0xfa0f5b595eafe731, + 0x3bdc477694c306e7, + 0x2149be4b3949fa24, + 0x64aa6e0649b2078c, + 0x12b108ac33643c3e, + ]), + c1: Fp::from_raw_unchecked([ + 0x125325df3d35b5a8, + 0xdc469ef5555d7fe3, + 0x2d716d2443106a9, + 0x5a1db59a6ff37d0, + 0x7cf7784e5300bb8f, + 0x16a88922c7a5e844, + ]), + }; + + let c = G2Projective { + x: a.x * (z.square()), + y: a.y * (z.square() * z), + z, + }; + + assert_eq!(G2Affine::from(c), G2Affine::generator()); +} + +#[test] +fn test_affine_to_projective() { + let a = G2Affine::generator(); + let b = G2Affine::identity(); + + assert!(bool::from(G2Projective::from(a).is_on_curve())); + assert!(!bool::from(G2Projective::from(a).is_identity())); + assert!(bool::from(G2Projective::from(b).is_on_curve())); + assert!(bool::from(G2Projective::from(b).is_identity())); +} + +#[test] +fn test_doubling() { + { + let tmp = G2Projective::identity().double(); + assert!(bool::from(tmp.is_identity())); + assert!(bool::from(tmp.is_on_curve())); + } + { + let tmp = G2Projective::generator().double(); + assert!(!bool::from(tmp.is_identity())); + assert!(bool::from(tmp.is_on_curve())); + + assert_eq!( + G2Affine::from(tmp), + G2Affine { + x: Fp2 { + c0: Fp::from_raw_unchecked([ + 0xe9d9e2da9620f98b, + 0x54f1199346b97f36, + 0x3db3b820376bed27, + 0xcfdb31c9b0b64f4c, + 0x41d7c12786354493, + 0x5710794c255c064 + ]), + c1: Fp::from_raw_unchecked([ + 0xd6c1d3ca6ea0d06e, + 0xda0cbd905595489f, + 0x4f5352d43479221d, + 0x8ade5d736f8c97e0, + 0x48cc8433925ef70e, + 0x8d7ea71ea91ef81 + ]), + }, + y: Fp2 { + c0: Fp::from_raw_unchecked([ + 0x15ba26eb4b0d186f, + 0xd086d64b7e9e01e, + 0xc8b848dd652f4c78, + 0xeecf46a6123bae4f, + 0x255e8dd8b6dc812a, + 0x164142af21dcf93f + ]), + c1: Fp::from_raw_unchecked([ + 0xf9b4a1a895984db4, + 0xd417b114cccff748, + 0x6856301fc89f086e, + 0x41c777878931e3da, + 0x3556b155066a2105, + 0xacf7d325cb89cf + ]), + }, + infinity: Choice::from(0u8) + } + ); + } +} + +#[test] +fn test_projective_addition() { + { + let a = G2Projective::identity(); + let b = G2Projective::identity(); + let c = a + b; + assert!(bool::from(c.is_identity())); + assert!(bool::from(c.is_on_curve())); + } + { + let a = G2Projective::identity(); + let mut b = G2Projective::generator(); + { + let z = Fp2 { + c0: Fp::from_raw_unchecked([ + 0xba7afa1f9a6fe250, + 0xfa0f5b595eafe731, + 0x3bdc477694c306e7, + 0x2149be4b3949fa24, + 0x64aa6e0649b2078c, + 0x12b108ac33643c3e, + ]), + c1: Fp::from_raw_unchecked([ + 0x125325df3d35b5a8, + 0xdc469ef5555d7fe3, + 0x2d716d2443106a9, + 0x5a1db59a6ff37d0, + 0x7cf7784e5300bb8f, + 0x16a88922c7a5e844, + ]), + }; + + b = G2Projective { + x: b.x * (z.square()), + y: b.y * (z.square() * z), + z, + }; + } + let c = a + b; + assert!(!bool::from(c.is_identity())); + assert!(bool::from(c.is_on_curve())); + assert!(c == G2Projective::generator()); + } + { + let a = G2Projective::identity(); + let mut b = G2Projective::generator(); + { + let z = Fp2 { + c0: Fp::from_raw_unchecked([ + 0xba7afa1f9a6fe250, + 0xfa0f5b595eafe731, + 0x3bdc477694c306e7, + 0x2149be4b3949fa24, + 0x64aa6e0649b2078c, + 0x12b108ac33643c3e, + ]), + c1: Fp::from_raw_unchecked([ + 0x125325df3d35b5a8, + 0xdc469ef5555d7fe3, + 0x2d716d2443106a9, + 0x5a1db59a6ff37d0, + 0x7cf7784e5300bb8f, + 0x16a88922c7a5e844, + ]), + }; + + b = G2Projective { + x: b.x * (z.square()), + y: b.y * (z.square() * z), + z, + }; + } + let c = b + a; + assert!(!bool::from(c.is_identity())); + assert!(bool::from(c.is_on_curve())); + assert!(c == G2Projective::generator()); + } + { + let a = G2Projective::generator().double().double(); // 4P + let b = G2Projective::generator().double(); // 2P + let c = a + b; + + let mut d = G2Projective::generator(); + for _ in 0..5 { + d = d + G2Projective::generator(); + } + assert!(!bool::from(c.is_identity())); + assert!(bool::from(c.is_on_curve())); + assert!(!bool::from(d.is_identity())); + assert!(bool::from(d.is_on_curve())); + assert_eq!(c, d); + } + + // Degenerate case + { + let beta = Fp2 { + c0: Fp::from_raw_unchecked([ + 0xcd03c9e48671f071, + 0x5dab22461fcda5d2, + 0x587042afd3851b95, + 0x8eb60ebe01bacb9e, + 0x3f97d6e83d050d2, + 0x18f0206554638741, + ]), + c1: Fp::zero(), + }; + let beta = beta.square(); + let a = G2Projective::generator().double().double(); + let b = G2Projective { + x: a.x * beta, + y: -a.y, + z: a.z, + }; + assert!(bool::from(a.is_on_curve())); + assert!(bool::from(b.is_on_curve())); + + let c = a + b; + assert_eq!( + G2Affine::from(c), + G2Affine::from(G2Projective { + x: Fp2 { + c0: Fp::from_raw_unchecked([ + 0x705abc799ca773d3, + 0xfe132292c1d4bf08, + 0xf37ece3e07b2b466, + 0x887e1c43f447e301, + 0x1e0970d033bc77e8, + 0x1985c81e20a693f2 + ]), + c1: Fp::from_raw_unchecked([ + 0x1d79b25db36ab924, + 0x23948e4d529639d3, + 0x471ba7fb0d006297, + 0x2c36d4b4465dc4c0, + 0x82bbc3cfec67f538, + 0x51d2728b67bf952 + ]) + }, + y: Fp2 { + c0: Fp::from_raw_unchecked([ + 0x41b1bbf6576c0abf, + 0xb6cc93713f7a0f9a, + 0x6b65b43e48f3f01f, + 0xfb7a4cfcaf81be4f, + 0x3e32dadc6ec22cb6, + 0xbb0fc49d79807e3 + ]), + c1: Fp::from_raw_unchecked([ + 0x7d1397788f5f2ddf, + 0xab2907144ff0d8e8, + 0x5b7573e0cdb91f92, + 0x4cb8932dd31daf28, + 0x62bbfac6db052a54, + 0x11f95c16d14c3bbe + ]) + }, + z: Fp2::one() + }) + ); + assert!(!bool::from(c.is_identity())); + assert!(bool::from(c.is_on_curve())); + } +} + +#[test] +fn test_mixed_addition() { + { + let a = G2Affine::identity(); + let b = G2Projective::identity(); + let c = a + b; + assert!(bool::from(c.is_identity())); + assert!(bool::from(c.is_on_curve())); + } + { + let a = G2Affine::identity(); + let mut b = G2Projective::generator(); + { + let z = Fp2 { + c0: Fp::from_raw_unchecked([ + 0xba7afa1f9a6fe250, + 0xfa0f5b595eafe731, + 0x3bdc477694c306e7, + 0x2149be4b3949fa24, + 0x64aa6e0649b2078c, + 0x12b108ac33643c3e, + ]), + c1: Fp::from_raw_unchecked([ + 0x125325df3d35b5a8, + 0xdc469ef5555d7fe3, + 0x2d716d2443106a9, + 0x5a1db59a6ff37d0, + 0x7cf7784e5300bb8f, + 0x16a88922c7a5e844, + ]), + }; + + b = G2Projective { + x: b.x * (z.square()), + y: b.y * (z.square() * z), + z, + }; + } + let c = a + b; + assert!(!bool::from(c.is_identity())); + assert!(bool::from(c.is_on_curve())); + assert!(c == G2Projective::generator()); + } + { + let a = G2Affine::identity(); + let mut b = G2Projective::generator(); + { + let z = Fp2 { + c0: Fp::from_raw_unchecked([ + 0xba7afa1f9a6fe250, + 0xfa0f5b595eafe731, + 0x3bdc477694c306e7, + 0x2149be4b3949fa24, + 0x64aa6e0649b2078c, + 0x12b108ac33643c3e, + ]), + c1: Fp::from_raw_unchecked([ + 0x125325df3d35b5a8, + 0xdc469ef5555d7fe3, + 0x2d716d2443106a9, + 0x5a1db59a6ff37d0, + 0x7cf7784e5300bb8f, + 0x16a88922c7a5e844, + ]), + }; + + b = G2Projective { + x: b.x * (z.square()), + y: b.y * (z.square() * z), + z, + }; + } + let c = b + a; + assert!(!bool::from(c.is_identity())); + assert!(bool::from(c.is_on_curve())); + assert!(c == G2Projective::generator()); + } + { + let a = G2Projective::generator().double().double(); // 4P + let b = G2Projective::generator().double(); // 2P + let c = a + b; + + let mut d = G2Projective::generator(); + for _ in 0..5 { + d = d + G2Affine::generator(); + } + assert!(!bool::from(c.is_identity())); + assert!(bool::from(c.is_on_curve())); + assert!(!bool::from(d.is_identity())); + assert!(bool::from(d.is_on_curve())); + assert_eq!(c, d); + } + + // Degenerate case + { + let beta = Fp2 { + c0: Fp::from_raw_unchecked([ + 0xcd03c9e48671f071, + 0x5dab22461fcda5d2, + 0x587042afd3851b95, + 0x8eb60ebe01bacb9e, + 0x3f97d6e83d050d2, + 0x18f0206554638741, + ]), + c1: Fp::zero(), + }; + let beta = beta.square(); + let a = G2Projective::generator().double().double(); + let b = G2Projective { + x: a.x * beta, + y: -a.y, + z: a.z, + }; + let a = G2Affine::from(a); + assert!(bool::from(a.is_on_curve())); + assert!(bool::from(b.is_on_curve())); + + let c = a + b; + assert_eq!( + G2Affine::from(c), + G2Affine::from(G2Projective { + x: Fp2 { + c0: Fp::from_raw_unchecked([ + 0x705abc799ca773d3, + 0xfe132292c1d4bf08, + 0xf37ece3e07b2b466, + 0x887e1c43f447e301, + 0x1e0970d033bc77e8, + 0x1985c81e20a693f2 + ]), + c1: Fp::from_raw_unchecked([ + 0x1d79b25db36ab924, + 0x23948e4d529639d3, + 0x471ba7fb0d006297, + 0x2c36d4b4465dc4c0, + 0x82bbc3cfec67f538, + 0x51d2728b67bf952 + ]) + }, + y: Fp2 { + c0: Fp::from_raw_unchecked([ + 0x41b1bbf6576c0abf, + 0xb6cc93713f7a0f9a, + 0x6b65b43e48f3f01f, + 0xfb7a4cfcaf81be4f, + 0x3e32dadc6ec22cb6, + 0xbb0fc49d79807e3 + ]), + c1: Fp::from_raw_unchecked([ + 0x7d1397788f5f2ddf, + 0xab2907144ff0d8e8, + 0x5b7573e0cdb91f92, + 0x4cb8932dd31daf28, + 0x62bbfac6db052a54, + 0x11f95c16d14c3bbe + ]) + }, + z: Fp2::one() + }) + ); + assert!(!bool::from(c.is_identity())); + assert!(bool::from(c.is_on_curve())); + } +} + +#[test] +fn test_projective_negation_and_subtraction() { + let a = G2Projective::generator().double(); + assert_eq!(a + (-a), G2Projective::identity()); + assert_eq!(a + (-a), a - a); +} + +#[test] +fn test_affine_negation_and_subtraction() { + let a = G2Affine::generator(); + assert_eq!(G2Projective::from(a) + (-a), G2Projective::identity()); + assert_eq!(G2Projective::from(a) + (-a), G2Projective::from(a) - a); +} + +#[test] +fn test_projective_scalar_multiplication() { + let g = G2Projective::generator(); + let a = Scalar::from_raw([ + 0x2b568297a56da71c, + 0xd8c39ecb0ef375d1, + 0x435c38da67bfbf96, + 0x8088a05026b659b2, + ]); + let b = Scalar::from_raw([ + 0x785fdd9b26ef8b85, + 0xc997f25837695c18, + 0x4c8dbc39e7b756c1, + 0x70d9b6cc6d87df20, + ]); + let c = a * b; + + assert_eq!((g * a) * b, g * c); +} + +#[test] +fn test_affine_scalar_multiplication() { + let g = G2Affine::generator(); + let a = Scalar::from_raw([ + 0x2b568297a56da71c, + 0xd8c39ecb0ef375d1, + 0x435c38da67bfbf96, + 0x8088a05026b659b2, + ]); + let b = Scalar::from_raw([ + 0x785fdd9b26ef8b85, + 0xc997f25837695c18, + 0x4c8dbc39e7b756c1, + 0x70d9b6cc6d87df20, + ]); + let c = a * b; + + assert_eq!(G2Affine::from(g * a) * b, g * c); +} + +#[test] +fn test_is_torsion_free() { + let a = G2Affine { + x: Fp2 { + c0: Fp::from_raw_unchecked([ + 0x89f550c813db6431, + 0xa50be8c456cd8a1a, + 0xa45b374114cae851, + 0xbb6190f5bf7fff63, + 0x970ca02c3ba80bc7, + 0x2b85d24e840fbac, + ]), + c1: Fp::from_raw_unchecked([ + 0x6888bc53d70716dc, + 0x3dea6b4117682d70, + 0xd8f5f930500ca354, + 0x6b5ecb6556f5c155, + 0xc96bef0434778ab0, + 0x5081505515006ad, + ]), + }, + y: Fp2 { + c0: Fp::from_raw_unchecked([ + 0x3cf1ea0d434b0f40, + 0x1a0dc610e603e333, + 0x7f89956160c72fa0, + 0x25ee03decf6431c5, + 0xeee8e206ec0fe137, + 0x97592b226dfef28, + ]), + c1: Fp::from_raw_unchecked([ + 0x71e8bb5f29247367, + 0xa5fe049e211831ce, + 0xce6b354502a3896, + 0x93b012000997314e, + 0x6759f3b6aa5b42ac, + 0x156944c4dfe92bbb, + ]), + }, + infinity: Choice::from(0u8), + }; + assert!(!bool::from(a.is_torsion_free())); + + assert!(bool::from(G2Affine::identity().is_torsion_free())); + assert!(bool::from(G2Affine::generator().is_torsion_free())); +} + +#[test] +fn test_batch_normalize() { + let a = G2Projective::generator().double(); + let b = a.double(); + let c = b.double(); + + for a_identity in (0..1).map(|n| n == 1) { + for b_identity in (0..1).map(|n| n == 1) { + for c_identity in (0..1).map(|n| n == 1) { + let mut v = [a, b, c]; + if a_identity { + v[0] = G2Projective::identity() + } + if b_identity { + v[1] = G2Projective::identity() + } + if c_identity { + v[2] = G2Projective::identity() + } + + let mut t = [ + G2Affine::identity(), + G2Affine::identity(), + G2Affine::identity(), + ]; + let expected = [ + G2Affine::from(v[0]), + G2Affine::from(v[1]), + G2Affine::from(v[2]), + ]; + + G2Projective::batch_normalize(&v[..], &mut t[..]); + + assert_eq!(&t[..], &expected[..]); + } + } + } +} diff --git a/bls12_381/src/lib.rs b/bls12_381/src/lib.rs new file mode 100644 index 0000000..e6c0e47 --- /dev/null +++ b/bls12_381/src/lib.rs @@ -0,0 +1,81 @@ +//! # `bls12_381` +//! +//! This crate provides an implementation of the BLS12-381 pairing-friendly elliptic +//! curve construction. +//! +//! * **This implementation has not been reviewed or audited. Use at your own risk.** +//! * This implementation targets Rust `1.36` or later. +//! * This implementation does not require the Rust standard library. +//! * All operations are constant time unless explicitly noted. + +#![no_std] +// Catch documentation errors caused by code changes. +#![deny(intra_doc_link_resolution_failure)] +#![deny(missing_debug_implementations)] +#![deny(missing_docs)] +#![deny(unsafe_code)] +#![allow(clippy::too_many_arguments)] +#![allow(clippy::unreadable_literal)] +#![allow(clippy::many_single_char_names)] +// This lint is described at +// https://rust-lang.github.io/rust-clippy/master/index.html#suspicious_arithmetic_impl +// In our library, some of the arithmetic involving extension fields will necessarily +// involve various binary operators, and so this lint is triggered unnecessarily. +#![allow(clippy::suspicious_arithmetic_impl)] + +#[cfg(feature = "alloc")] +extern crate alloc; + +#[cfg(test)] +#[macro_use] +extern crate std; + +#[cfg(test)] +#[cfg(feature = "groups")] +mod tests; + +#[macro_use] +mod util; + +/// Notes about how the BLS12-381 elliptic curve is designed, specified +/// and implemented by this library. +pub mod notes { + pub mod design; + pub mod serialization; +} + +mod scalar; + +pub use scalar::Scalar; + +#[cfg(feature = "groups")] +mod fp; +#[cfg(feature = "groups")] +mod fp2; +#[cfg(feature = "groups")] +mod g1; +#[cfg(feature = "groups")] +mod g2; + +#[cfg(feature = "groups")] +pub use g1::{G1Affine, G1Projective}; +#[cfg(feature = "groups")] +pub use g2::{G2Affine, G2Projective}; + +#[cfg(feature = "groups")] +mod fp12; +#[cfg(feature = "groups")] +mod fp6; + +// The BLS parameter x for BLS12-381 is -0xd201000000010000 +const BLS_X: u64 = 0xd201000000010000; +const BLS_X_IS_NEGATIVE: bool = true; + +#[cfg(feature = "pairings")] +mod pairings; + +#[cfg(feature = "pairings")] +pub use pairings::{pairing, Gt, MillerLoopResult}; + +#[cfg(all(feature = "pairings", feature = "alloc"))] +pub use pairings::{multi_miller_loop, G2Prepared}; diff --git a/bls12_381/src/notes/design.rs b/bls12_381/src/notes/design.rs new file mode 100644 index 0000000..d245260 --- /dev/null +++ b/bls12_381/src/notes/design.rs @@ -0,0 +1,63 @@ +//! # Design of BLS12-381 +//! ## Fixed Generators +//! +//! Although any generator produced by hashing to $\mathbb{G}_1$ or $\mathbb{G}_2$ is +//! safe to use in a cryptographic protocol, we specify some simple, fixed generators. +//! +//! In order to derive these generators, we select the lexicographically smallest +//! valid $x$-coordinate and the lexicographically smallest corresponding $y$-coordinate, +//! and then scale the resulting point by the cofactor, such that the result is not the +//! identity. This results in the following fixed generators: +//! +//! 1. $\mathbb{G}_1$ +//! * $x = 3685416753713387016781088315183077757961620795782546409894578378688607592378376318836054947676345821548104185464507$ +//! * $y = 1339506544944476473020471379941921221584933875938349620426543736416511423956333506472724655353366534992391756441569$ +//! 2. $\mathbb{G}_2$ +//! * $x = 352701069587466618187139116011060144890029952792775240219908644239793785735715026873347600343865175952761926303160 + 3059144344244213709971259814753781636986470325476647558659373206291635324768958432433509563104347017837885763365758 u$ +//! * $y = 1985150602287291935568054521177171638300868978215655730859378665066344726373823718423869104263333984641494340347905 + 927553665492332455747201965776037880757740193453592970025027978793976877002675564980949289727957565575433344219582 u$ +//! +//! This can be derived using the following sage script: +//! +//! ```norun +//! param = -0xd201000000010000 +//! def r(x): +//! return (x**4) - (x**2) + 1 +//! def q(x): +//! return (((x - 1) ** 2) * ((x**4) - (x**2) + 1) // 3) + x +//! def g1_h(x): +//! return ((x-1)**2) // 3 +//! def g2_h(x): +//! return ((x**8) - (4 * (x**7)) + (5 * (x**6)) - (4 * (x**4)) + (6 * (x**3)) - (4 * (x**2)) - (4*x) + 13) // 9 +//! q = q(param) +//! r = r(param) +//! Fq = GF(q) +//! ec = EllipticCurve(Fq, [0, 4]) +//! def psqrt(v): +//! assert(not v.is_zero()) +//! a = sqrt(v) +//! b = -a +//! if a < b: +//! return a +//! else: +//! return b +//! for x in range(0,100): +//! rhs = Fq(x)^3 + 4 +//! if rhs.is_square(): +//! y = psqrt(rhs) +//! p = ec(x, y) * g1_h(param) +//! if (not p.is_zero()) and (p * r).is_zero(): +//! print "g1 generator: %s" % p +//! break +//! Fqx. = PolynomialRing(Fq, 'j') +//! Fq2. = GF(q^2, modulus=j^2 + 1) +//! ec2 = EllipticCurve(Fq2, [0, (4 * (1 + i))]) +//! assert(ec2.order() == (r * g2_h(param))) +//! for x in range(0,100): +//! rhs = (Fq2(x))^3 + (4 * (1 + i)) +//! if rhs.is_square(): +//! y = psqrt(rhs) +//! p = ec2(Fq2(x), y) * g2_h(param) +//! if (not p.is_zero()) and (p * r).is_zero(): +//! print "g2 generator: %s" % p +//! break +//! ``` diff --git a/bls12_381/src/notes/serialization.rs b/bls12_381/src/notes/serialization.rs new file mode 100644 index 0000000..ded752e --- /dev/null +++ b/bls12_381/src/notes/serialization.rs @@ -0,0 +1,29 @@ +//! # BLS12-381 serialization +//! +//! * $\mathbb{F}\_p$ elements are encoded in big-endian form. They occupy 48 +//! bytes in this form. +//! * $\mathbb{F}\_{p^2}$ elements are encoded in big-endian form, meaning that +//! the $\mathbb{F}\_{p^2}$ element $c\_0 + c\_1 \cdot u$ is represented by the +//! $\mathbb{F}\_p$ element $c\_1$ followed by the $\mathbb{F}\_p$ element $c\_0$. +//! This means $\mathbb{F}_{p^2}$ elements occupy 96 bytes in this form. +//! * The group $\mathbb{G}\_1$ uses $\mathbb{F}\_p$ elements for coordinates. The +//! group $\mathbb{G}\_2$ uses $\mathbb{F}_{p^2}$ elements for coordinates. +//! * $\mathbb{G}\_1$ and $\mathbb{G}\_2$ elements can be encoded in uncompressed +//! form (the x-coordinate followed by the y-coordinate) or in compressed form +//! (just the x-coordinate). $\mathbb{G}\_1$ elements occupy 96 bytes in +//! uncompressed form, and 48 bytes in compressed form. $\mathbb{G}\_2$ +//! elements occupy 192 bytes in uncompressed form, and 96 bytes in compressed +//! form. +//! +//! The most-significant three bits of a $\mathbb{G}\_1$ or $\mathbb{G}\_2$ +//! encoding should be masked away before the coordinate(s) are interpreted. +//! These bits are used to unambiguously represent the underlying element: +//! * The most significant bit, when set, indicates that the point is in +//! compressed form. Otherwise, the point is in uncompressed form. +//! * The second-most significant bit indicates that the point is at infinity. +//! If this bit is set, the remaining bits of the group element's encoding +//! should be set to zero. +//! * The third-most significant bit is set if (and only if) this point is in +//! compressed form _and_ it is not the point at infinity _and_ its +//! y-coordinate is the lexicographically largest of the two associated with +//! the encoded x-coordinate. diff --git a/bls12_381/src/pairings.rs b/bls12_381/src/pairings.rs new file mode 100644 index 0000000..459d501 --- /dev/null +++ b/bls12_381/src/pairings.rs @@ -0,0 +1,654 @@ +use crate::fp12::Fp12; +use crate::fp2::Fp2; +use crate::fp6::Fp6; +use crate::{G1Affine, G2Affine, G2Projective, Scalar, BLS_X, BLS_X_IS_NEGATIVE}; + +use core::ops::{Add, AddAssign, Mul, MulAssign, Neg, Sub, SubAssign}; + +use subtle::{Choice, ConditionallySelectable, ConstantTimeEq}; + +#[cfg(feature = "alloc")] +use alloc::vec::Vec; + +/// Represents results of a Miller loop, one of the most expensive portions +/// of the pairing function. `MillerLoopResult`s cannot be compared with each +/// other until `.final_exponentiation()` is called, which is also expensive. +#[derive(Copy, Clone, Debug)] +pub struct MillerLoopResult(pub(crate) Fp12); + +impl ConditionallySelectable for MillerLoopResult { + fn conditional_select(a: &Self, b: &Self, choice: Choice) -> Self { + MillerLoopResult(Fp12::conditional_select(&a.0, &b.0, choice)) + } +} + +impl MillerLoopResult { + /// This performs a "final exponentiation" routine to convert the result + /// of a Miller loop into an element of `Gt` with help of efficient squaring + /// operation in the so-called `cyclotomic subgroup` of `Fq6` so that + /// it can be compared with other elements of `Gt`. + pub fn final_exponentiation(&self) -> Gt { + #[must_use] + fn fp4_square(a: Fp2, b: Fp2) -> (Fp2, Fp2) { + let t0 = a.square(); + let t1 = b.square(); + let mut t2 = t1.mul_by_nonresidue(); + let c0 = t2 + t0; + t2 = a + b; + t2 = t2.square(); + t2 -= t0; + let c1 = t2 - t1; + + (c0, c1) + } + // Adaptation of Algorithm 5.5.4, Guide to Pairing-Based Cryptography + // Faster Squaring in the Cyclotomic Subgroup of Sixth Degree Extensions + // https://eprint.iacr.org/2009/565.pdf + #[must_use] + fn cyclotomic_square(f: Fp12) -> Fp12 { + let mut z0 = f.c0.c0.clone(); + let mut z4 = f.c0.c1.clone(); + let mut z3 = f.c0.c2.clone(); + let mut z2 = f.c1.c0.clone(); + let mut z1 = f.c1.c1.clone(); + let mut z5 = f.c1.c2.clone(); + + let (t0, t1) = fp4_square(z0, z1); + + // For A + z0 = t0 - z0; + z0 += z0 + t0; + + z1 = t1 + z1; + z1 += z1 + t1; + + let (mut t0, t1) = fp4_square(z2, z3); + let (t2, t3) = fp4_square(z4, z5); + + // For C + z4 = t0 - z4; + z4 += z4 + t0; + + z5 = t1 + z5; + z5 += z5 + t1; + + // For B + t0 = t3.mul_by_nonresidue(); + z2 = t0 + z2; + z2 += z2 + t0; + + z3 = t2 - z3; + z3 += z3 + t2; + + Fp12 { + c0: Fp6 { + c0: z0, + c1: z4, + c2: z3, + }, + c1: Fp6 { + c0: z2, + c1: z1, + c2: z5, + }, + } + } + #[must_use] + fn cycolotomic_exp(f: Fp12) -> Fp12 { + let x = BLS_X; + let mut tmp = Fp12::one(); + let mut found_one = false; + for i in (0..64).rev().map(|b| ((x >> b) & 1) == 1) { + if found_one { + tmp = cyclotomic_square(tmp) + } else { + found_one = i; + } + + if i { + tmp *= f; + } + } + + tmp.conjugate() + } + + let mut f = self.0.clone(); + let mut t0 = f + .frobenius_map() + .frobenius_map() + .frobenius_map() + .frobenius_map() + .frobenius_map() + .frobenius_map(); + Gt(f.invert() + .map(|mut t1| { + let mut t2 = t0 * t1; + t1 = t2.clone(); + t2 = t2.frobenius_map().frobenius_map(); + t2 *= t1; + t1 = cyclotomic_square(t2).conjugate(); + let mut t3 = cycolotomic_exp(t2); + let mut t4 = cyclotomic_square(t3); + let mut t5 = t1 * t3; + t1 = cycolotomic_exp(t5); + t0 = cycolotomic_exp(t1); + let mut t6 = cycolotomic_exp(t0); + t6 *= t4; + t4 = cycolotomic_exp(t6); + t5 = t5.conjugate(); + t4 *= t5 * t2; + t5 = t2.conjugate(); + t1 *= t2; + t1 = t1.frobenius_map().frobenius_map().frobenius_map(); + t6 *= t5; + t6 = t6.frobenius_map(); + t3 *= t0; + t3 = t3.frobenius_map().frobenius_map(); + t3 *= t1; + t3 *= t6; + f = t3 * t4; + + f + }) + // We unwrap() because `MillerLoopResult` can only be constructed + // by a function within this crate, and we uphold the invariant + // that the enclosed value is nonzero. + .unwrap()) + } +} + +impl<'a, 'b> Add<&'b MillerLoopResult> for &'a MillerLoopResult { + type Output = MillerLoopResult; + + #[inline] + fn add(self, rhs: &'b MillerLoopResult) -> MillerLoopResult { + MillerLoopResult(self.0 * rhs.0) + } +} + +impl_add_binop_specify_output!(MillerLoopResult, MillerLoopResult, MillerLoopResult); + +/// This is an element of $\mathbb{G}_T$, the target group of the pairing function. As with +/// $\mathbb{G}_1$ and $\mathbb{G}_2$ this group has order $q$. +/// +/// Typically, $\mathbb{G}_T$ is written multiplicatively but we will write it additively to +/// keep code and abstractions consistent. +#[derive(Copy, Clone, Debug)] +pub struct Gt(pub(crate) Fp12); + +impl ConstantTimeEq for Gt { + fn ct_eq(&self, other: &Self) -> Choice { + self.0.ct_eq(&other.0) + } +} + +impl ConditionallySelectable for Gt { + fn conditional_select(a: &Self, b: &Self, choice: Choice) -> Self { + Gt(Fp12::conditional_select(&a.0, &b.0, choice)) + } +} + +impl Eq for Gt {} +impl PartialEq for Gt { + #[inline] + fn eq(&self, other: &Self) -> bool { + bool::from(self.ct_eq(other)) + } +} + +impl Gt { + /// Returns the group identity, which is $1$. + pub fn identity() -> Gt { + Gt(Fp12::one()) + } + + /// Doubles this group element. + pub fn double(&self) -> Gt { + Gt(self.0.square()) + } +} + +impl<'a> Neg for &'a Gt { + type Output = Gt; + + #[inline] + fn neg(self) -> Gt { + // The element is unitary, so we just conjugate. + Gt(self.0.conjugate()) + } +} + +impl Neg for Gt { + type Output = Gt; + + #[inline] + fn neg(self) -> Gt { + -&self + } +} + +impl<'a, 'b> Add<&'b Gt> for &'a Gt { + type Output = Gt; + + #[inline] + fn add(self, rhs: &'b Gt) -> Gt { + Gt(self.0 * rhs.0) + } +} + +impl<'a, 'b> Sub<&'b Gt> for &'a Gt { + type Output = Gt; + + #[inline] + fn sub(self, rhs: &'b Gt) -> Gt { + self + (-rhs) + } +} + +impl<'a, 'b> Mul<&'b Scalar> for &'a Gt { + type Output = Gt; + + fn mul(self, other: &'b Scalar) -> Self::Output { + let mut acc = Gt::identity(); + + // This is a simple double-and-add implementation of group element + // multiplication, moving from most significant to least + // significant bit of the scalar. + // + // We skip the leading bit because it's always unset for Fq + // elements. + for bit in other + .to_bytes() + .iter() + .rev() + .flat_map(|byte| (0..8).rev().map(move |i| Choice::from((byte >> i) & 1u8))) + .skip(1) + { + acc = acc.double(); + acc = Gt::conditional_select(&acc, &(acc + self), bit); + } + + acc + } +} + +impl_binops_additive!(Gt, Gt); +impl_binops_multiplicative!(Gt, Scalar); + +#[cfg(feature = "alloc")] +#[derive(Clone, Debug)] +/// This structure contains cached computations pertaining to a $\mathbb{G}_2$ +/// element as part of the pairing function (specifically, the Miller loop) and +/// so should be computed whenever a $\mathbb{G}_2$ element is being used in +/// multiple pairings or is otherwise known in advance. This should be used in +/// conjunction with the [`multi_miller_loop`](crate::multi_miller_loop) +/// function provided by this crate. +/// +/// Requires the `alloc` and `pairing` crate features to be enabled. +pub struct G2Prepared { + infinity: Choice, + coeffs: Vec<(Fp2, Fp2, Fp2)>, +} + +#[cfg(feature = "alloc")] +impl From for G2Prepared { + fn from(q: G2Affine) -> G2Prepared { + struct Adder { + cur: G2Projective, + base: G2Affine, + coeffs: Vec<(Fp2, Fp2, Fp2)>, + } + + impl MillerLoopDriver for Adder { + type Output = (); + + fn doubling_step(&mut self, _: Self::Output) -> Self::Output { + let coeffs = doubling_step(&mut self.cur); + self.coeffs.push(coeffs); + } + fn addition_step(&mut self, _: Self::Output) -> Self::Output { + let coeffs = addition_step(&mut self.cur, &self.base); + self.coeffs.push(coeffs); + } + fn square_output(_: Self::Output) -> Self::Output { + () + } + fn conjugate(_: Self::Output) -> Self::Output { + () + } + fn one() -> Self::Output { + () + } + } + + let is_identity = q.is_identity(); + let q = G2Affine::conditional_select(&q, &G2Affine::generator(), is_identity); + + let mut adder = Adder { + cur: G2Projective::from(q), + base: q, + coeffs: Vec::with_capacity(68), + }; + + miller_loop(&mut adder); + + assert_eq!(adder.coeffs.len(), 68); + + G2Prepared { + infinity: is_identity, + coeffs: adder.coeffs, + } + } +} + +#[cfg(feature = "alloc")] +/// Computes $$\sum_{i=1}^n \textbf{ML}(a_i, b_i)$$ given a series of terms +/// $$(a_1, b_1), (a_2, b_2), ..., (a_n, b_n).$$ +/// +/// Requires the `alloc` and `pairing` crate features to be enabled. +pub fn multi_miller_loop(terms: &[(&G1Affine, &G2Prepared)]) -> MillerLoopResult { + struct Adder<'a, 'b, 'c> { + terms: &'c [(&'a G1Affine, &'b G2Prepared)], + index: usize, + } + + impl<'a, 'b, 'c> MillerLoopDriver for Adder<'a, 'b, 'c> { + type Output = Fp12; + + fn doubling_step(&mut self, mut f: Self::Output) -> Self::Output { + let index = self.index; + for term in self.terms { + let either_identity = term.0.is_identity() | term.1.infinity; + + let new_f = ell(f, &term.1.coeffs[index], term.0); + f = Fp12::conditional_select(&new_f, &f, either_identity); + } + self.index += 1; + + f + } + fn addition_step(&mut self, mut f: Self::Output) -> Self::Output { + let index = self.index; + for term in self.terms { + let either_identity = term.0.is_identity() | term.1.infinity; + + let new_f = ell(f, &term.1.coeffs[index], term.0); + f = Fp12::conditional_select(&new_f, &f, either_identity); + } + self.index += 1; + + f + } + fn square_output(f: Self::Output) -> Self::Output { + f.square() + } + fn conjugate(f: Self::Output) -> Self::Output { + f.conjugate() + } + fn one() -> Self::Output { + Fp12::one() + } + } + + let mut adder = Adder { terms, index: 0 }; + + let tmp = miller_loop(&mut adder); + + MillerLoopResult(tmp) +} + +/// Invoke the pairing function without the use of precomputation and other optimizations. +pub fn pairing(p: &G1Affine, q: &G2Affine) -> Gt { + struct Adder { + cur: G2Projective, + base: G2Affine, + p: G1Affine, + } + + impl MillerLoopDriver for Adder { + type Output = Fp12; + + fn doubling_step(&mut self, f: Self::Output) -> Self::Output { + let coeffs = doubling_step(&mut self.cur); + ell(f, &coeffs, &self.p) + } + fn addition_step(&mut self, f: Self::Output) -> Self::Output { + let coeffs = addition_step(&mut self.cur, &self.base); + ell(f, &coeffs, &self.p) + } + fn square_output(f: Self::Output) -> Self::Output { + f.square() + } + fn conjugate(f: Self::Output) -> Self::Output { + f.conjugate() + } + fn one() -> Self::Output { + Fp12::one() + } + } + + let either_identity = p.is_identity() | q.is_identity(); + let p = G1Affine::conditional_select(&p, &G1Affine::generator(), either_identity); + let q = G2Affine::conditional_select(&q, &G2Affine::generator(), either_identity); + + let mut adder = Adder { + cur: G2Projective::from(q), + base: q, + p, + }; + + let tmp = miller_loop(&mut adder); + let tmp = MillerLoopResult(Fp12::conditional_select( + &tmp, + &Fp12::one(), + either_identity, + )); + tmp.final_exponentiation() +} + +trait MillerLoopDriver { + type Output; + + fn doubling_step(&mut self, f: Self::Output) -> Self::Output; + fn addition_step(&mut self, f: Self::Output) -> Self::Output; + fn square_output(f: Self::Output) -> Self::Output; + fn conjugate(f: Self::Output) -> Self::Output; + fn one() -> Self::Output; +} + +/// This is a "generic" implementation of the Miller loop to avoid duplicating code +/// structure elsewhere; instead, we'll write concrete instantiations of +/// `MillerLoopDriver` for whatever purposes we need (such as caching modes). +fn miller_loop(driver: &mut D) -> D::Output { + let mut f = D::one(); + + let mut found_one = false; + for i in (0..64).rev().map(|b| (((BLS_X >> 1) >> b) & 1) == 1) { + if !found_one { + found_one = i; + continue; + } + + f = driver.doubling_step(f); + + if i { + f = driver.addition_step(f); + } + + f = D::square_output(f); + } + + f = driver.doubling_step(f); + + if BLS_X_IS_NEGATIVE { + f = D::conjugate(f); + } + + f +} + +fn ell(f: Fp12, coeffs: &(Fp2, Fp2, Fp2), p: &G1Affine) -> Fp12 { + let mut c0 = coeffs.0; + let mut c1 = coeffs.1; + + c0.c0 *= p.y; + c0.c1 *= p.y; + + c1.c0 *= p.x; + c1.c1 *= p.x; + + f.mul_by_014(&coeffs.2, &c1, &c0) +} + +fn doubling_step(r: &mut G2Projective) -> (Fp2, Fp2, Fp2) { + // Adaptation of Algorithm 26, https://eprint.iacr.org/2010/354.pdf + let tmp0 = r.x.square(); + let tmp1 = r.y.square(); + let tmp2 = tmp1.square(); + let tmp3 = (tmp1 + r.x).square() - tmp0 - tmp2; + let tmp3 = tmp3 + tmp3; + let tmp4 = tmp0 + tmp0 + tmp0; + let tmp6 = r.x + tmp4; + let tmp5 = tmp4.square(); + let zsquared = r.z.square(); + r.x = tmp5 - tmp3 - tmp3; + r.z = (r.z + r.y).square() - tmp1 - zsquared; + r.y = (tmp3 - r.x) * tmp4; + let tmp2 = tmp2 + tmp2; + let tmp2 = tmp2 + tmp2; + let tmp2 = tmp2 + tmp2; + r.y -= tmp2; + let tmp3 = tmp4 * zsquared; + let tmp3 = tmp3 + tmp3; + let tmp3 = -tmp3; + let tmp6 = tmp6.square() - tmp0 - tmp5; + let tmp1 = tmp1 + tmp1; + let tmp1 = tmp1 + tmp1; + let tmp6 = tmp6 - tmp1; + let tmp0 = r.z * zsquared; + let tmp0 = tmp0 + tmp0; + + (tmp0, tmp3, tmp6) +} + +fn addition_step(r: &mut G2Projective, q: &G2Affine) -> (Fp2, Fp2, Fp2) { + // Adaptation of Algorithm 27, https://eprint.iacr.org/2010/354.pdf + let zsquared = r.z.square(); + let ysquared = q.y.square(); + let t0 = zsquared * q.x; + let t1 = ((q.y + r.z).square() - ysquared - zsquared) * zsquared; + let t2 = t0 - r.x; + let t3 = t2.square(); + let t4 = t3 + t3; + let t4 = t4 + t4; + let t5 = t4 * t2; + let t6 = t1 - r.y - r.y; + let t9 = t6 * q.x; + let t7 = t4 * r.x; + r.x = t6.square() - t5 - t7 - t7; + r.z = (r.z + t2).square() - zsquared - t3; + let t10 = q.y + r.z; + let t8 = (t7 - r.x) * t6; + let t0 = r.y * t5; + let t0 = t0 + t0; + r.y = t8 - t0; + let t10 = t10.square() - ysquared; + let ztsquared = r.z.square(); + let t10 = t10 - ztsquared; + let t9 = t9 + t9 - t10; + let t10 = r.z + r.z; + let t6 = -t6; + let t1 = t6 + t6; + + (t10, t1, t9) +} + +#[test] +fn test_bilinearity() { + use crate::Scalar; + + let a = Scalar::from_raw([1, 2, 3, 4]).invert().unwrap().square(); + let b = Scalar::from_raw([5, 6, 7, 8]).invert().unwrap().square(); + let c = a * b; + + let g = G1Affine::from(G1Affine::generator() * a); + let h = G2Affine::from(G2Affine::generator() * b); + let p = pairing(&g, &h); + + assert!(p != Gt::identity()); + + let expected = G1Affine::from(G1Affine::generator() * c); + + assert_eq!(p, pairing(&expected, &G2Affine::generator())); + assert_eq!( + p, + pairing(&G1Affine::generator(), &G2Affine::generator()) * c + ); +} + +#[test] +fn test_unitary() { + let g = G1Affine::generator(); + let h = G2Affine::generator(); + let p = -pairing(&g, &h); + let q = pairing(&g, &-h); + let r = pairing(&-g, &h); + + assert_eq!(p, q); + assert_eq!(q, r); +} + +#[cfg(feature = "alloc")] +#[test] +fn test_multi_miller_loop() { + let a1 = G1Affine::generator(); + let b1 = G2Affine::generator(); + + let a2 = G1Affine::from( + G1Affine::generator() * Scalar::from_raw([1, 2, 3, 4]).invert().unwrap().square(), + ); + let b2 = G2Affine::from( + G2Affine::generator() * Scalar::from_raw([4, 2, 2, 4]).invert().unwrap().square(), + ); + + let a3 = G1Affine::identity(); + let b3 = G2Affine::from( + G2Affine::generator() * Scalar::from_raw([9, 2, 2, 4]).invert().unwrap().square(), + ); + + let a4 = G1Affine::from( + G1Affine::generator() * Scalar::from_raw([5, 5, 5, 5]).invert().unwrap().square(), + ); + let b4 = G2Affine::identity(); + + let a5 = G1Affine::from( + G1Affine::generator() * Scalar::from_raw([323, 32, 3, 1]).invert().unwrap().square(), + ); + let b5 = G2Affine::from( + G2Affine::generator() * Scalar::from_raw([4, 2, 2, 9099]).invert().unwrap().square(), + ); + + let b1_prepared = G2Prepared::from(b1); + let b2_prepared = G2Prepared::from(b2); + let b3_prepared = G2Prepared::from(b3); + let b4_prepared = G2Prepared::from(b4); + let b5_prepared = G2Prepared::from(b5); + + let expected = pairing(&a1, &b1) + + pairing(&a2, &b2) + + pairing(&a3, &b3) + + pairing(&a4, &b4) + + pairing(&a5, &b5); + + let test = multi_miller_loop(&[ + (&a1, &b1_prepared), + (&a2, &b2_prepared), + (&a3, &b3_prepared), + (&a4, &b4_prepared), + (&a5, &b5_prepared), + ]) + .final_exponentiation(); + + assert_eq!(expected, test); +} diff --git a/bls12_381/src/scalar.rs b/bls12_381/src/scalar.rs new file mode 100644 index 0000000..d4a7ab2 --- /dev/null +++ b/bls12_381/src/scalar.rs @@ -0,0 +1,1076 @@ +//! This module provides an implementation of the BLS12-381 scalar field $\mathbb{F}_q$ +//! where `q = 0x73eda753299d7d483339d80809a1d80553bda402fffe5bfeffffffff00000001` + +use core::convert::TryFrom; +use core::fmt; +use core::ops::{Add, AddAssign, Mul, MulAssign, Neg, Sub, SubAssign}; + +use subtle::{Choice, ConditionallySelectable, ConstantTimeEq, CtOption}; + +use crate::util::{adc, mac, sbb}; + +/// Represents an element of the scalar field $\mathbb{F}_q$ of the BLS12-381 elliptic +/// curve construction. +// The internal representation of this type is four 64-bit unsigned +// integers in little-endian order. `Scalar` values are always in +// Montgomery form; i.e., Scalar(a) = aR mod q, with R = 2^256. +#[derive(Clone, Copy, Eq)] +pub struct Scalar(pub(crate) [u64; 4]); + +impl fmt::Debug for Scalar { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + let tmp = self.to_bytes(); + write!(f, "0x")?; + for &b in tmp.iter().rev() { + write!(f, "{:02x}", b)?; + } + Ok(()) + } +} + +impl From for Scalar { + fn from(val: u64) -> Scalar { + Scalar([val, 0, 0, 0]) * R2 + } +} + +impl ConstantTimeEq for Scalar { + fn ct_eq(&self, other: &Self) -> Choice { + self.0[0].ct_eq(&other.0[0]) + & self.0[1].ct_eq(&other.0[1]) + & self.0[2].ct_eq(&other.0[2]) + & self.0[3].ct_eq(&other.0[3]) + } +} + +impl PartialEq for Scalar { + #[inline] + fn eq(&self, other: &Self) -> bool { + self.ct_eq(other).unwrap_u8() == 1 + } +} + +impl ConditionallySelectable for Scalar { + fn conditional_select(a: &Self, b: &Self, choice: Choice) -> Self { + Scalar([ + u64::conditional_select(&a.0[0], &b.0[0], choice), + u64::conditional_select(&a.0[1], &b.0[1], choice), + u64::conditional_select(&a.0[2], &b.0[2], choice), + u64::conditional_select(&a.0[3], &b.0[3], choice), + ]) + } +} + +/// Constant representing the modulus +/// q = 0x73eda753299d7d483339d80809a1d80553bda402fffe5bfeffffffff00000001 +const MODULUS: Scalar = Scalar([ + 0xffffffff00000001, + 0x53bda402fffe5bfe, + 0x3339d80809a1d805, + 0x73eda753299d7d48, +]); + +impl<'a> Neg for &'a Scalar { + type Output = Scalar; + + #[inline] + fn neg(self) -> Scalar { + self.neg() + } +} + +impl Neg for Scalar { + type Output = Scalar; + + #[inline] + fn neg(self) -> Scalar { + -&self + } +} + +impl<'a, 'b> Sub<&'b Scalar> for &'a Scalar { + type Output = Scalar; + + #[inline] + fn sub(self, rhs: &'b Scalar) -> Scalar { + self.sub(rhs) + } +} + +impl<'a, 'b> Add<&'b Scalar> for &'a Scalar { + type Output = Scalar; + + #[inline] + fn add(self, rhs: &'b Scalar) -> Scalar { + self.add(rhs) + } +} + +impl<'a, 'b> Mul<&'b Scalar> for &'a Scalar { + type Output = Scalar; + + #[inline] + fn mul(self, rhs: &'b Scalar) -> Scalar { + self.mul(rhs) + } +} + +impl_binops_additive!(Scalar, Scalar); +impl_binops_multiplicative!(Scalar, Scalar); + +/// INV = -(q^{-1} mod 2^64) mod 2^64 +const INV: u64 = 0xfffffffeffffffff; + +/// R = 2^256 mod q +const R: Scalar = Scalar([ + 0x00000001fffffffe, + 0x5884b7fa00034802, + 0x998c4fefecbc4ff5, + 0x1824b159acc5056f, +]); + +/// R^2 = 2^512 mod q +const R2: Scalar = Scalar([ + 0xc999e990f3f29c6d, + 0x2b6cedcb87925c23, + 0x05d314967254398f, + 0x0748d9d99f59ff11, +]); + +/// R^3 = 2^768 mod q +const R3: Scalar = Scalar([ + 0xc62c1807439b73af, + 0x1b3e0d188cf06990, + 0x73d13c71c7b5f418, + 0x6e2a5bb9c8db33e9, +]); + +const S: u32 = 32; + +/// GENERATOR^t where t * 2^s + 1 = q +/// with t odd. In other words, this +/// is a 2^s root of unity. +/// +/// `GENERATOR = 7 mod q` is a generator +/// of the q - 1 order multiplicative +/// subgroup. +const ROOT_OF_UNITY: Scalar = Scalar([ + 0xb9b58d8c5f0e466a, + 0x5b1b4c801819d7ec, + 0x0af53ae352a31e64, + 0x5bf3adda19e9b27b, +]); + +impl Default for Scalar { + #[inline] + fn default() -> Self { + Self::zero() + } +} + +impl Scalar { + /// Returns zero, the additive identity. + #[inline] + pub const fn zero() -> Scalar { + Scalar([0, 0, 0, 0]) + } + + /// Returns one, the multiplicative identity. + #[inline] + pub const fn one() -> Scalar { + R + } + + /// Doubles this field element. + #[inline] + pub const fn double(&self) -> Scalar { + // TODO: This can be achieved more efficiently with a bitshift. + self.add(self) + } + + /// Attempts to convert a little-endian byte representation of + /// a scalar into a `Scalar`, failing if the input is not canonical. + pub fn from_bytes(bytes: &[u8; 32]) -> CtOption { + let mut tmp = Scalar([0, 0, 0, 0]); + + tmp.0[0] = u64::from_le_bytes(<[u8; 8]>::try_from(&bytes[0..8]).unwrap()); + tmp.0[1] = u64::from_le_bytes(<[u8; 8]>::try_from(&bytes[8..16]).unwrap()); + tmp.0[2] = u64::from_le_bytes(<[u8; 8]>::try_from(&bytes[16..24]).unwrap()); + tmp.0[3] = u64::from_le_bytes(<[u8; 8]>::try_from(&bytes[24..32]).unwrap()); + + // Try to subtract the modulus + let (_, borrow) = sbb(tmp.0[0], MODULUS.0[0], 0); + let (_, borrow) = sbb(tmp.0[1], MODULUS.0[1], borrow); + let (_, borrow) = sbb(tmp.0[2], MODULUS.0[2], borrow); + let (_, borrow) = sbb(tmp.0[3], MODULUS.0[3], borrow); + + // If the element is smaller than MODULUS then the + // subtraction will underflow, producing a borrow value + // of 0xffff...ffff. Otherwise, it'll be zero. + let is_some = (borrow as u8) & 1; + + // Convert to Montgomery form by computing + // (a.R^0 * R^2) / R = a.R + tmp *= &R2; + + CtOption::new(tmp, Choice::from(is_some)) + } + + /// Converts an element of `Scalar` into a byte representation in + /// little-endian byte order. + pub fn to_bytes(&self) -> [u8; 32] { + // Turn into canonical form by computing + // (a.R) / R = a + let tmp = Scalar::montgomery_reduce(self.0[0], self.0[1], self.0[2], self.0[3], 0, 0, 0, 0); + + let mut res = [0; 32]; + res[0..8].copy_from_slice(&tmp.0[0].to_le_bytes()); + res[8..16].copy_from_slice(&tmp.0[1].to_le_bytes()); + res[16..24].copy_from_slice(&tmp.0[2].to_le_bytes()); + res[24..32].copy_from_slice(&tmp.0[3].to_le_bytes()); + + res + } + + /// Converts a 512-bit little endian integer into + /// a `Scalar` by reducing by the modulus. + pub fn from_bytes_wide(bytes: &[u8; 64]) -> Scalar { + Scalar::from_u512([ + u64::from_le_bytes(<[u8; 8]>::try_from(&bytes[0..8]).unwrap()), + u64::from_le_bytes(<[u8; 8]>::try_from(&bytes[8..16]).unwrap()), + u64::from_le_bytes(<[u8; 8]>::try_from(&bytes[16..24]).unwrap()), + u64::from_le_bytes(<[u8; 8]>::try_from(&bytes[24..32]).unwrap()), + u64::from_le_bytes(<[u8; 8]>::try_from(&bytes[32..40]).unwrap()), + u64::from_le_bytes(<[u8; 8]>::try_from(&bytes[40..48]).unwrap()), + u64::from_le_bytes(<[u8; 8]>::try_from(&bytes[48..56]).unwrap()), + u64::from_le_bytes(<[u8; 8]>::try_from(&bytes[56..64]).unwrap()), + ]) + } + + fn from_u512(limbs: [u64; 8]) -> Scalar { + // We reduce an arbitrary 512-bit number by decomposing it into two 256-bit digits + // with the higher bits multiplied by 2^256. Thus, we perform two reductions + // + // 1. the lower bits are multiplied by R^2, as normal + // 2. the upper bits are multiplied by R^2 * 2^256 = R^3 + // + // and computing their sum in the field. It remains to see that arbitrary 256-bit + // numbers can be placed into Montgomery form safely using the reduction. The + // reduction works so long as the product is less than R=2^256 multipled by + // the modulus. This holds because for any `c` smaller than the modulus, we have + // that (2^256 - 1)*c is an acceptable product for the reduction. Therefore, the + // reduction always works so long as `c` is in the field; in this case it is either the + // constant `R2` or `R3`. + let d0 = Scalar([limbs[0], limbs[1], limbs[2], limbs[3]]); + let d1 = Scalar([limbs[4], limbs[5], limbs[6], limbs[7]]); + // Convert to Montgomery form + d0 * R2 + d1 * R3 + } + + /// Converts from an integer represented in little endian + /// into its (congruent) `Scalar` representation. + pub const fn from_raw(val: [u64; 4]) -> Self { + (&Scalar(val)).mul(&R2) + } + + /// Squares this element. + #[inline] + pub const fn square(&self) -> Scalar { + let (r1, carry) = mac(0, self.0[0], self.0[1], 0); + let (r2, carry) = mac(0, self.0[0], self.0[2], carry); + let (r3, r4) = mac(0, self.0[0], self.0[3], carry); + + let (r3, carry) = mac(r3, self.0[1], self.0[2], 0); + let (r4, r5) = mac(r4, self.0[1], self.0[3], carry); + + let (r5, r6) = mac(r5, self.0[2], self.0[3], 0); + + let r7 = r6 >> 63; + let r6 = (r6 << 1) | (r5 >> 63); + let r5 = (r5 << 1) | (r4 >> 63); + let r4 = (r4 << 1) | (r3 >> 63); + let r3 = (r3 << 1) | (r2 >> 63); + let r2 = (r2 << 1) | (r1 >> 63); + let r1 = r1 << 1; + + let (r0, carry) = mac(0, self.0[0], self.0[0], 0); + let (r1, carry) = adc(0, r1, carry); + let (r2, carry) = mac(r2, self.0[1], self.0[1], carry); + let (r3, carry) = adc(0, r3, carry); + let (r4, carry) = mac(r4, self.0[2], self.0[2], carry); + let (r5, carry) = adc(0, r5, carry); + let (r6, carry) = mac(r6, self.0[3], self.0[3], carry); + let (r7, _) = adc(0, r7, carry); + + Scalar::montgomery_reduce(r0, r1, r2, r3, r4, r5, r6, r7) + } + + /// Computes the square root of this element, if it exists. + pub fn sqrt(&self) -> CtOption { + // Tonelli-Shank's algorithm for q mod 16 = 1 + // https://eprint.iacr.org/2012/685.pdf (page 12, algorithm 5) + + // w = self^((t - 1) // 2) + // = self^6104339283789297388802252303364915521546564123189034618274734669823 + let w = self.pow_vartime(&[ + 0x7fff2dff7fffffff, + 0x04d0ec02a9ded201, + 0x94cebea4199cec04, + 0x0000000039f6d3a9, + ]); + + let mut v = S; + let mut x = self * w; + let mut b = x * w; + + // Initialize z as the 2^S root of unity. + let mut z = ROOT_OF_UNITY; + + for max_v in (1..=S).rev() { + let mut k = 1; + let mut tmp = b.square(); + let mut j_less_than_v: Choice = 1.into(); + + for j in 2..max_v { + let tmp_is_one = tmp.ct_eq(&Scalar::one()); + let squared = Scalar::conditional_select(&tmp, &z, tmp_is_one).square(); + tmp = Scalar::conditional_select(&squared, &tmp, tmp_is_one); + let new_z = Scalar::conditional_select(&z, &squared, tmp_is_one); + j_less_than_v &= !j.ct_eq(&v); + k = u32::conditional_select(&j, &k, tmp_is_one); + z = Scalar::conditional_select(&z, &new_z, j_less_than_v); + } + + let result = x * z; + x = Scalar::conditional_select(&result, &x, b.ct_eq(&Scalar::one())); + z = z.square(); + b *= z; + v = k; + } + + CtOption::new( + x, + (x * x).ct_eq(self), // Only return Some if it's the square root. + ) + } + + /// Exponentiates `self` by `by`, where `by` is a + /// little-endian order integer exponent. + pub fn pow(&self, by: &[u64; 4]) -> Self { + let mut res = Self::one(); + for e in by.iter().rev() { + for i in (0..64).rev() { + res = res.square(); + let mut tmp = res; + tmp *= self; + res.conditional_assign(&tmp, (((*e >> i) & 0x1) as u8).into()); + } + } + res + } + + /// Exponentiates `self` by `by`, where `by` is a + /// little-endian order integer exponent. + /// + /// **This operation is variable time with respect + /// to the exponent.** If the exponent is fixed, + /// this operation is effectively constant time. + pub fn pow_vartime(&self, by: &[u64; 4]) -> Self { + let mut res = Self::one(); + for e in by.iter().rev() { + for i in (0..64).rev() { + res = res.square(); + + if ((*e >> i) & 1) == 1 { + res.mul_assign(self); + } + } + } + res + } + + /// Computes the multiplicative inverse of this element, + /// failing if the element is zero. + pub fn invert(&self) -> CtOption { + #[inline(always)] + fn square_assign_multi(n: &mut Scalar, num_times: usize) { + for _ in 0..num_times { + *n = n.square(); + } + } + // found using https://github.com/kwantam/addchain + let mut t0 = self.square(); + let mut t1 = t0 * self; + let mut t16 = t0.square(); + let mut t6 = t16.square(); + let mut t5 = t6 * t0; + t0 = t6 * t16; + let mut t12 = t5 * t16; + let mut t2 = t6.square(); + let mut t7 = t5 * t6; + let mut t15 = t0 * t5; + let mut t17 = t12.square(); + t1 *= t17; + let mut t3 = t7 * t2; + let t8 = t1 * t17; + let t4 = t8 * t2; + let t9 = t8 * t7; + t7 = t4 * t5; + let t11 = t4 * t17; + t5 = t9 * t17; + let t14 = t7 * t15; + let t13 = t11 * t12; + t12 = t11 * t17; + t15 *= &t12; + t16 *= &t15; + t3 *= &t16; + t17 *= &t3; + t0 *= &t17; + t6 *= &t0; + t2 *= &t6; + square_assign_multi(&mut t0, 8); + t0 *= &t17; + square_assign_multi(&mut t0, 9); + t0 *= &t16; + square_assign_multi(&mut t0, 9); + t0 *= &t15; + square_assign_multi(&mut t0, 9); + t0 *= &t15; + square_assign_multi(&mut t0, 7); + t0 *= &t14; + square_assign_multi(&mut t0, 7); + t0 *= &t13; + square_assign_multi(&mut t0, 10); + t0 *= &t12; + square_assign_multi(&mut t0, 9); + t0 *= &t11; + square_assign_multi(&mut t0, 8); + t0 *= &t8; + square_assign_multi(&mut t0, 8); + t0 *= self; + square_assign_multi(&mut t0, 14); + t0 *= &t9; + square_assign_multi(&mut t0, 10); + t0 *= &t8; + square_assign_multi(&mut t0, 15); + t0 *= &t7; + square_assign_multi(&mut t0, 10); + t0 *= &t6; + square_assign_multi(&mut t0, 8); + t0 *= &t5; + square_assign_multi(&mut t0, 16); + t0 *= &t3; + square_assign_multi(&mut t0, 8); + t0 *= &t2; + square_assign_multi(&mut t0, 7); + t0 *= &t4; + square_assign_multi(&mut t0, 9); + t0 *= &t2; + square_assign_multi(&mut t0, 8); + t0 *= &t3; + square_assign_multi(&mut t0, 8); + t0 *= &t2; + square_assign_multi(&mut t0, 8); + t0 *= &t2; + square_assign_multi(&mut t0, 8); + t0 *= &t2; + square_assign_multi(&mut t0, 8); + t0 *= &t3; + square_assign_multi(&mut t0, 8); + t0 *= &t2; + square_assign_multi(&mut t0, 8); + t0 *= &t2; + square_assign_multi(&mut t0, 5); + t0 *= &t1; + square_assign_multi(&mut t0, 5); + t0 *= &t1; + + CtOption::new(t0, !self.ct_eq(&Self::zero())) + } + + #[inline(always)] + const fn montgomery_reduce( + r0: u64, + r1: u64, + r2: u64, + r3: u64, + r4: u64, + r5: u64, + r6: u64, + r7: u64, + ) -> Self { + // The Montgomery reduction here is based on Algorithm 14.32 in + // Handbook of Applied Cryptography + // . + + let k = r0.wrapping_mul(INV); + let (_, carry) = mac(r0, k, MODULUS.0[0], 0); + let (r1, carry) = mac(r1, k, MODULUS.0[1], carry); + let (r2, carry) = mac(r2, k, MODULUS.0[2], carry); + let (r3, carry) = mac(r3, k, MODULUS.0[3], carry); + let (r4, carry2) = adc(r4, 0, carry); + + let k = r1.wrapping_mul(INV); + let (_, carry) = mac(r1, k, MODULUS.0[0], 0); + let (r2, carry) = mac(r2, k, MODULUS.0[1], carry); + let (r3, carry) = mac(r3, k, MODULUS.0[2], carry); + let (r4, carry) = mac(r4, k, MODULUS.0[3], carry); + let (r5, carry2) = adc(r5, carry2, carry); + + let k = r2.wrapping_mul(INV); + let (_, carry) = mac(r2, k, MODULUS.0[0], 0); + let (r3, carry) = mac(r3, k, MODULUS.0[1], carry); + let (r4, carry) = mac(r4, k, MODULUS.0[2], carry); + let (r5, carry) = mac(r5, k, MODULUS.0[3], carry); + let (r6, carry2) = adc(r6, carry2, carry); + + let k = r3.wrapping_mul(INV); + let (_, carry) = mac(r3, k, MODULUS.0[0], 0); + let (r4, carry) = mac(r4, k, MODULUS.0[1], carry); + let (r5, carry) = mac(r5, k, MODULUS.0[2], carry); + let (r6, carry) = mac(r6, k, MODULUS.0[3], carry); + let (r7, _) = adc(r7, carry2, carry); + + // Result may be within MODULUS of the correct value + (&Scalar([r4, r5, r6, r7])).sub(&MODULUS) + } + + /// Multiplies `rhs` by `self`, returning the result. + #[inline] + pub const fn mul(&self, rhs: &Self) -> Self { + // Schoolbook multiplication + + let (r0, carry) = mac(0, self.0[0], rhs.0[0], 0); + let (r1, carry) = mac(0, self.0[0], rhs.0[1], carry); + let (r2, carry) = mac(0, self.0[0], rhs.0[2], carry); + let (r3, r4) = mac(0, self.0[0], rhs.0[3], carry); + + let (r1, carry) = mac(r1, self.0[1], rhs.0[0], 0); + let (r2, carry) = mac(r2, self.0[1], rhs.0[1], carry); + let (r3, carry) = mac(r3, self.0[1], rhs.0[2], carry); + let (r4, r5) = mac(r4, self.0[1], rhs.0[3], carry); + + let (r2, carry) = mac(r2, self.0[2], rhs.0[0], 0); + let (r3, carry) = mac(r3, self.0[2], rhs.0[1], carry); + let (r4, carry) = mac(r4, self.0[2], rhs.0[2], carry); + let (r5, r6) = mac(r5, self.0[2], rhs.0[3], carry); + + let (r3, carry) = mac(r3, self.0[3], rhs.0[0], 0); + let (r4, carry) = mac(r4, self.0[3], rhs.0[1], carry); + let (r5, carry) = mac(r5, self.0[3], rhs.0[2], carry); + let (r6, r7) = mac(r6, self.0[3], rhs.0[3], carry); + + Scalar::montgomery_reduce(r0, r1, r2, r3, r4, r5, r6, r7) + } + + /// Subtracts `rhs` from `self`, returning the result. + #[inline] + pub const fn sub(&self, rhs: &Self) -> Self { + let (d0, borrow) = sbb(self.0[0], rhs.0[0], 0); + let (d1, borrow) = sbb(self.0[1], rhs.0[1], borrow); + let (d2, borrow) = sbb(self.0[2], rhs.0[2], borrow); + let (d3, borrow) = sbb(self.0[3], rhs.0[3], borrow); + + // If underflow occurred on the final limb, borrow = 0xfff...fff, otherwise + // borrow = 0x000...000. Thus, we use it as a mask to conditionally add the modulus. + let (d0, carry) = adc(d0, MODULUS.0[0] & borrow, 0); + let (d1, carry) = adc(d1, MODULUS.0[1] & borrow, carry); + let (d2, carry) = adc(d2, MODULUS.0[2] & borrow, carry); + let (d3, _) = adc(d3, MODULUS.0[3] & borrow, carry); + + Scalar([d0, d1, d2, d3]) + } + + /// Adds `rhs` to `self`, returning the result. + #[inline] + pub const fn add(&self, rhs: &Self) -> Self { + let (d0, carry) = adc(self.0[0], rhs.0[0], 0); + let (d1, carry) = adc(self.0[1], rhs.0[1], carry); + let (d2, carry) = adc(self.0[2], rhs.0[2], carry); + let (d3, _) = adc(self.0[3], rhs.0[3], carry); + + // Attempt to subtract the modulus, to ensure the value + // is smaller than the modulus. + (&Scalar([d0, d1, d2, d3])).sub(&MODULUS) + } + + /// Negates `self`. + #[inline] + pub const fn neg(&self) -> Self { + // Subtract `self` from `MODULUS` to negate. Ignore the final + // borrow because it cannot underflow; self is guaranteed to + // be in the field. + let (d0, borrow) = sbb(MODULUS.0[0], self.0[0], 0); + let (d1, borrow) = sbb(MODULUS.0[1], self.0[1], borrow); + let (d2, borrow) = sbb(MODULUS.0[2], self.0[2], borrow); + let (d3, _) = sbb(MODULUS.0[3], self.0[3], borrow); + + // `tmp` could be `MODULUS` if `self` was zero. Create a mask that is + // zero if `self` was zero, and `u64::max_value()` if self was nonzero. + let mask = (((self.0[0] | self.0[1] | self.0[2] | self.0[3]) == 0) as u64).wrapping_sub(1); + + Scalar([d0 & mask, d1 & mask, d2 & mask, d3 & mask]) + } +} + +impl<'a> From<&'a Scalar> for [u8; 32] { + fn from(value: &'a Scalar) -> [u8; 32] { + value.to_bytes() + } +} + +#[test] +fn test_inv() { + // Compute -(q^{-1} mod 2^64) mod 2^64 by exponentiating + // by totient(2**64) - 1 + + let mut inv = 1u64; + for _ in 0..63 { + inv = inv.wrapping_mul(inv); + inv = inv.wrapping_mul(MODULUS.0[0]); + } + inv = inv.wrapping_neg(); + + assert_eq!(inv, INV); +} + +#[cfg(feature = "std")] +#[test] +fn test_debug() { + assert_eq!( + format!("{:?}", Scalar::zero()), + "0x0000000000000000000000000000000000000000000000000000000000000000" + ); + assert_eq!( + format!("{:?}", Scalar::one()), + "0x0000000000000000000000000000000000000000000000000000000000000001" + ); + assert_eq!( + format!("{:?}", R2), + "0x1824b159acc5056f998c4fefecbc4ff55884b7fa0003480200000001fffffffe" + ); +} + +#[test] +fn test_equality() { + assert_eq!(Scalar::zero(), Scalar::zero()); + assert_eq!(Scalar::one(), Scalar::one()); + assert_eq!(R2, R2); + + assert!(Scalar::zero() != Scalar::one()); + assert!(Scalar::one() != R2); +} + +#[test] +fn test_to_bytes() { + assert_eq!( + Scalar::zero().to_bytes(), + [ + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0 + ] + ); + + assert_eq!( + Scalar::one().to_bytes(), + [ + 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0 + ] + ); + + assert_eq!( + R2.to_bytes(), + [ + 254, 255, 255, 255, 1, 0, 0, 0, 2, 72, 3, 0, 250, 183, 132, 88, 245, 79, 188, 236, 239, + 79, 140, 153, 111, 5, 197, 172, 89, 177, 36, 24 + ] + ); + + assert_eq!( + (-&Scalar::one()).to_bytes(), + [ + 0, 0, 0, 0, 255, 255, 255, 255, 254, 91, 254, 255, 2, 164, 189, 83, 5, 216, 161, 9, 8, + 216, 57, 51, 72, 125, 157, 41, 83, 167, 237, 115 + ] + ); +} + +#[test] +fn test_from_bytes() { + assert_eq!( + Scalar::from_bytes(&[ + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0 + ]) + .unwrap(), + Scalar::zero() + ); + + assert_eq!( + Scalar::from_bytes(&[ + 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0 + ]) + .unwrap(), + Scalar::one() + ); + + assert_eq!( + Scalar::from_bytes(&[ + 254, 255, 255, 255, 1, 0, 0, 0, 2, 72, 3, 0, 250, 183, 132, 88, 245, 79, 188, 236, 239, + 79, 140, 153, 111, 5, 197, 172, 89, 177, 36, 24 + ]) + .unwrap(), + R2 + ); + + // -1 should work + assert!( + Scalar::from_bytes(&[ + 0, 0, 0, 0, 255, 255, 255, 255, 254, 91, 254, 255, 2, 164, 189, 83, 5, 216, 161, 9, 8, + 216, 57, 51, 72, 125, 157, 41, 83, 167, 237, 115 + ]) + .is_some() + .unwrap_u8() + == 1 + ); + + // modulus is invalid + assert!( + Scalar::from_bytes(&[ + 1, 0, 0, 0, 255, 255, 255, 255, 254, 91, 254, 255, 2, 164, 189, 83, 5, 216, 161, 9, 8, + 216, 57, 51, 72, 125, 157, 41, 83, 167, 237, 115 + ]) + .is_none() + .unwrap_u8() + == 1 + ); + + // Anything larger than the modulus is invalid + assert!( + Scalar::from_bytes(&[ + 2, 0, 0, 0, 255, 255, 255, 255, 254, 91, 254, 255, 2, 164, 189, 83, 5, 216, 161, 9, 8, + 216, 57, 51, 72, 125, 157, 41, 83, 167, 237, 115 + ]) + .is_none() + .unwrap_u8() + == 1 + ); + assert!( + Scalar::from_bytes(&[ + 1, 0, 0, 0, 255, 255, 255, 255, 254, 91, 254, 255, 2, 164, 189, 83, 5, 216, 161, 9, 8, + 216, 58, 51, 72, 125, 157, 41, 83, 167, 237, 115 + ]) + .is_none() + .unwrap_u8() + == 1 + ); + assert!( + Scalar::from_bytes(&[ + 1, 0, 0, 0, 255, 255, 255, 255, 254, 91, 254, 255, 2, 164, 189, 83, 5, 216, 161, 9, 8, + 216, 57, 51, 72, 125, 157, 41, 83, 167, 237, 116 + ]) + .is_none() + .unwrap_u8() + == 1 + ); +} + +#[test] +fn test_from_u512_zero() { + assert_eq!( + Scalar::zero(), + Scalar::from_u512([ + MODULUS.0[0], + MODULUS.0[1], + MODULUS.0[2], + MODULUS.0[3], + 0, + 0, + 0, + 0 + ]) + ); +} + +#[test] +fn test_from_u512_r() { + assert_eq!(R, Scalar::from_u512([1, 0, 0, 0, 0, 0, 0, 0])); +} + +#[test] +fn test_from_u512_r2() { + assert_eq!(R2, Scalar::from_u512([0, 0, 0, 0, 1, 0, 0, 0])); +} + +#[test] +fn test_from_u512_max() { + let max_u64 = 0xffffffffffffffff; + assert_eq!( + R3 - R, + Scalar::from_u512([max_u64, max_u64, max_u64, max_u64, max_u64, max_u64, max_u64, max_u64]) + ); +} + +#[test] +fn test_from_bytes_wide_r2() { + assert_eq!( + R2, + Scalar::from_bytes_wide(&[ + 254, 255, 255, 255, 1, 0, 0, 0, 2, 72, 3, 0, 250, 183, 132, 88, 245, 79, 188, 236, 239, + 79, 140, 153, 111, 5, 197, 172, 89, 177, 36, 24, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + ]) + ); +} + +#[test] +fn test_from_bytes_wide_negative_one() { + assert_eq!( + -&Scalar::one(), + Scalar::from_bytes_wide(&[ + 0, 0, 0, 0, 255, 255, 255, 255, 254, 91, 254, 255, 2, 164, 189, 83, 5, 216, 161, 9, 8, + 216, 57, 51, 72, 125, 157, 41, 83, 167, 237, 115, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + ]) + ); +} + +#[test] +fn test_from_bytes_wide_maximum() { + assert_eq!( + Scalar([ + 0xc62c1805439b73b1, + 0xc2b9551e8ced218e, + 0xda44ec81daf9a422, + 0x5605aa601c162e79 + ]), + Scalar::from_bytes_wide(&[0xff; 64]) + ); +} + +#[test] +fn test_zero() { + assert_eq!(Scalar::zero(), -&Scalar::zero()); + assert_eq!(Scalar::zero(), Scalar::zero() + Scalar::zero()); + assert_eq!(Scalar::zero(), Scalar::zero() - Scalar::zero()); + assert_eq!(Scalar::zero(), Scalar::zero() * Scalar::zero()); +} + +#[cfg(test)] +const LARGEST: Scalar = Scalar([ + 0xffffffff00000000, + 0x53bda402fffe5bfe, + 0x3339d80809a1d805, + 0x73eda753299d7d48, +]); + +#[test] +fn test_addition() { + let mut tmp = LARGEST; + tmp += &LARGEST; + + assert_eq!( + tmp, + Scalar([ + 0xfffffffeffffffff, + 0x53bda402fffe5bfe, + 0x3339d80809a1d805, + 0x73eda753299d7d48 + ]) + ); + + let mut tmp = LARGEST; + tmp += &Scalar([1, 0, 0, 0]); + + assert_eq!(tmp, Scalar::zero()); +} + +#[test] +fn test_negation() { + let tmp = -&LARGEST; + + assert_eq!(tmp, Scalar([1, 0, 0, 0])); + + let tmp = -&Scalar::zero(); + assert_eq!(tmp, Scalar::zero()); + let tmp = -&Scalar([1, 0, 0, 0]); + assert_eq!(tmp, LARGEST); +} + +#[test] +fn test_subtraction() { + let mut tmp = LARGEST; + tmp -= &LARGEST; + + assert_eq!(tmp, Scalar::zero()); + + let mut tmp = Scalar::zero(); + tmp -= &LARGEST; + + let mut tmp2 = MODULUS; + tmp2 -= &LARGEST; + + assert_eq!(tmp, tmp2); +} + +#[test] +fn test_multiplication() { + let mut cur = LARGEST; + + for _ in 0..100 { + let mut tmp = cur; + tmp *= &cur; + + let mut tmp2 = Scalar::zero(); + for b in cur + .to_bytes() + .iter() + .rev() + .flat_map(|byte| (0..8).rev().map(move |i| ((byte >> i) & 1u8) == 1u8)) + { + let tmp3 = tmp2; + tmp2.add_assign(&tmp3); + + if b { + tmp2.add_assign(&cur); + } + } + + assert_eq!(tmp, tmp2); + + cur.add_assign(&LARGEST); + } +} + +#[test] +fn test_squaring() { + let mut cur = LARGEST; + + for _ in 0..100 { + let mut tmp = cur; + tmp = tmp.square(); + + let mut tmp2 = Scalar::zero(); + for b in cur + .to_bytes() + .iter() + .rev() + .flat_map(|byte| (0..8).rev().map(move |i| ((byte >> i) & 1u8) == 1u8)) + { + let tmp3 = tmp2; + tmp2.add_assign(&tmp3); + + if b { + tmp2.add_assign(&cur); + } + } + + assert_eq!(tmp, tmp2); + + cur.add_assign(&LARGEST); + } +} + +#[test] +fn test_inversion() { + assert_eq!(Scalar::zero().invert().is_none().unwrap_u8(), 1); + assert_eq!(Scalar::one().invert().unwrap(), Scalar::one()); + assert_eq!((-&Scalar::one()).invert().unwrap(), -&Scalar::one()); + + let mut tmp = R2; + + for _ in 0..100 { + let mut tmp2 = tmp.invert().unwrap(); + tmp2.mul_assign(&tmp); + + assert_eq!(tmp2, Scalar::one()); + + tmp.add_assign(&R2); + } +} + +#[test] +fn test_invert_is_pow() { + let q_minus_2 = [ + 0xfffffffeffffffff, + 0x53bda402fffe5bfe, + 0x3339d80809a1d805, + 0x73eda753299d7d48, + ]; + + let mut r1 = R; + let mut r2 = R; + let mut r3 = R; + + for _ in 0..100 { + r1 = r1.invert().unwrap(); + r2 = r2.pow_vartime(&q_minus_2); + r3 = r3.pow(&q_minus_2); + + assert_eq!(r1, r2); + assert_eq!(r2, r3); + // Add R so we check something different next time around + r1.add_assign(&R); + r2 = r1; + r3 = r1; + } +} + +#[test] +fn test_sqrt() { + { + assert_eq!(Scalar::zero().sqrt().unwrap(), Scalar::zero()); + } + + let mut square = Scalar([ + 0x46cd85a5f273077e, + 0x1d30c47dd68fc735, + 0x77f656f60beca0eb, + 0x494aa01bdf32468d, + ]); + + let mut none_count = 0; + + for _ in 0..100 { + let square_root = square.sqrt(); + if square_root.is_none().unwrap_u8() == 1 { + none_count += 1; + } else { + assert_eq!(square_root.unwrap() * square_root.unwrap(), square); + } + square -= Scalar::one(); + } + + assert_eq!(49, none_count); +} + +#[test] +fn test_from_raw() { + assert_eq!( + Scalar::from_raw([ + 0x1fffffffd, + 0x5884b7fa00034802, + 0x998c4fefecbc4ff5, + 0x1824b159acc5056f + ]), + Scalar::from_raw([0xffffffffffffffff; 4]) + ); + + assert_eq!(Scalar::from_raw(MODULUS.0), Scalar::zero()); + + assert_eq!(Scalar::from_raw([1, 0, 0, 0]), R); +} + +#[test] +fn test_double() { + let a = Scalar::from_raw([ + 0x1fff3231233ffffd, + 0x4884b7fa00034802, + 0x998c4fefecbc4ff3, + 0x1824b159acc50562, + ]); + + assert_eq!(a.double(), a + a); +} diff --git a/bls12_381/src/tests/g1_compressed_valid_test_vectors.dat b/bls12_381/src/tests/g1_compressed_valid_test_vectors.dat new file mode 100644 index 0000000..ea8cd67 Binary files /dev/null and b/bls12_381/src/tests/g1_compressed_valid_test_vectors.dat differ diff --git a/bls12_381/src/tests/g1_uncompressed_valid_test_vectors.dat b/bls12_381/src/tests/g1_uncompressed_valid_test_vectors.dat new file mode 100644 index 0000000..86abfba Binary files /dev/null and b/bls12_381/src/tests/g1_uncompressed_valid_test_vectors.dat differ diff --git a/bls12_381/src/tests/g2_compressed_valid_test_vectors.dat b/bls12_381/src/tests/g2_compressed_valid_test_vectors.dat new file mode 100644 index 0000000..a40bbe2 Binary files /dev/null and b/bls12_381/src/tests/g2_compressed_valid_test_vectors.dat differ diff --git a/bls12_381/src/tests/g2_uncompressed_valid_test_vectors.dat b/bls12_381/src/tests/g2_uncompressed_valid_test_vectors.dat new file mode 100644 index 0000000..92e4bc5 Binary files /dev/null and b/bls12_381/src/tests/g2_uncompressed_valid_test_vectors.dat differ diff --git a/bls12_381/src/tests/mod.rs b/bls12_381/src/tests/mod.rs new file mode 100644 index 0000000..125321b --- /dev/null +++ b/bls12_381/src/tests/mod.rs @@ -0,0 +1,230 @@ +use super::*; + +macro_rules! test_vectors { + ($projective:ident, $affine:ident, $serialize:ident, $deserialize:ident, $expected:ident) => { + let mut e = $projective::identity(); + + let mut v = vec![]; + { + let mut expected = $expected; + for _ in 0..1000 { + let e_affine = $affine::from(e); + let encoded = e_affine.$serialize(); + v.extend_from_slice(&encoded[..]); + + let mut decoded = encoded; + let len_of_encoding = decoded.len(); + (&mut decoded[..]).copy_from_slice(&expected[0..len_of_encoding]); + expected = &expected[len_of_encoding..]; + let decoded = $affine::$deserialize(&decoded).unwrap(); + assert_eq!(e_affine, decoded); + + e = &e + &$projective::generator(); + } + } + + assert_eq!(&v[..], $expected); + }; +} + +#[test] +fn g1_uncompressed_valid_test_vectors() { + let bytes: &'static [u8] = include_bytes!("g1_uncompressed_valid_test_vectors.dat"); + test_vectors!( + G1Projective, + G1Affine, + to_uncompressed, + from_uncompressed, + bytes + ); +} + +#[test] +fn g1_compressed_valid_test_vectors() { + let bytes: &'static [u8] = include_bytes!("g1_compressed_valid_test_vectors.dat"); + test_vectors!( + G1Projective, + G1Affine, + to_compressed, + from_compressed, + bytes + ); +} + +#[test] +fn g2_uncompressed_valid_test_vectors() { + let bytes: &'static [u8] = include_bytes!("g2_uncompressed_valid_test_vectors.dat"); + test_vectors!( + G2Projective, + G2Affine, + to_uncompressed, + from_uncompressed, + bytes + ); +} + +#[test] +fn g2_compressed_valid_test_vectors() { + let bytes: &'static [u8] = include_bytes!("g2_compressed_valid_test_vectors.dat"); + test_vectors!( + G2Projective, + G2Affine, + to_compressed, + from_compressed, + bytes + ); +} + +#[test] +fn test_pairing_result_against_relic() { + /* + Sent to me from Diego Aranha (author of RELIC library): + 1250EBD871FC0A92 A7B2D83168D0D727 272D441BEFA15C50 3DD8E90CE98DB3E7 B6D194F60839C508 A84305AACA1789B6 + 089A1C5B46E5110B 86750EC6A5323488 68A84045483C92B7 AF5AF689452EAFAB F1A8943E50439F1D 59882A98EAA0170F + 1368BB445C7C2D20 9703F239689CE34C 0378A68E72A6B3B2 16DA0E22A5031B54 DDFF57309396B38C 881C4C849EC23E87 + 193502B86EDB8857 C273FA075A505129 37E0794E1E65A761 7C90D8BD66065B1F FFE51D7A579973B1 315021EC3C19934F + 01B2F522473D1713 91125BA84DC4007C FBF2F8DA752F7C74 185203FCCA589AC7 19C34DFFBBAAD843 1DAD1C1FB597AAA5 + 018107154F25A764 BD3C79937A45B845 46DA634B8F6BE14A 8061E55CCEBA478B 23F7DACAA35C8CA7 8BEAE9624045B4B6 + 19F26337D205FB46 9CD6BD15C3D5A04D C88784FBB3D0B2DB DEA54D43B2B73F2C BB12D58386A8703E 0F948226E47EE89D + 06FBA23EB7C5AF0D 9F80940CA771B6FF D5857BAAF222EB95 A7D2809D61BFE02E 1BFD1B68FF02F0B8 102AE1C2D5D5AB1A + 11B8B424CD48BF38 FCEF68083B0B0EC5 C81A93B330EE1A67 7D0D15FF7B984E89 78EF48881E32FAC9 1B93B47333E2BA57 + 03350F55A7AEFCD3 C31B4FCB6CE5771C C6A0E9786AB59733 20C806AD36082910 7BA810C5A09FFDD9 BE2291A0C25A99A2 + 04C581234D086A99 02249B64728FFD21 A189E87935A95405 1C7CDBA7B3872629 A4FAFC05066245CB 9108F0242D0FE3EF + 0F41E58663BF08CF 068672CBD01A7EC7 3BACA4D72CA93544 DEFF686BFD6DF543 D48EAA24AFE47E1E FDE449383B676631 + */ + + let a = G1Affine::generator(); + let b = G2Affine::generator(); + + use super::fp::Fp; + use super::fp12::Fp12; + use super::fp2::Fp2; + use super::fp6::Fp6; + + let res = pairing(&a, &b); + + let prep = G2Prepared::from(b); + + assert_eq!( + res, + multi_miller_loop(&[(&a, &prep)]).final_exponentiation() + ); + + assert_eq!( + res.0, + Fp12 { + c0: Fp6 { + c0: Fp2 { + c0: Fp::from_raw_unchecked([ + 0x1972e433a01f85c5, + 0x97d32b76fd772538, + 0xc8ce546fc96bcdf9, + 0xcef63e7366d40614, + 0xa611342781843780, + 0x13f3448a3fc6d825 + ]), + c1: Fp::from_raw_unchecked([ + 0xd26331b02e9d6995, + 0x9d68a482f7797e7d, + 0x9c9b29248d39ea92, + 0xf4801ca2e13107aa, + 0xa16c0732bdbcb066, + 0x83ca4afba360478 + ]) + }, + c1: Fp2 { + c0: Fp::from_raw_unchecked([ + 0x59e261db0916b641, + 0x2716b6f4b23e960d, + 0xc8e55b10a0bd9c45, + 0xbdb0bd99c4deda8, + 0x8cf89ebf57fdaac5, + 0x12d6b7929e777a5e + ]), + c1: Fp::from_raw_unchecked([ + 0x5fc85188b0e15f35, + 0x34a06e3a8f096365, + 0xdb3126a6e02ad62c, + 0xfc6f5aa97d9a990b, + 0xa12f55f5eb89c210, + 0x1723703a926f8889 + ]) + }, + c2: Fp2 { + c0: Fp::from_raw_unchecked([ + 0x93588f2971828778, + 0x43f65b8611ab7585, + 0x3183aaf5ec279fdf, + 0xfa73d7e18ac99df6, + 0x64e176a6a64c99b0, + 0x179fa78c58388f1f + ]), + c1: Fp::from_raw_unchecked([ + 0x672a0a11ca2aef12, + 0xd11b9b52aa3f16b, + 0xa44412d0699d056e, + 0xc01d0177221a5ba5, + 0x66e0cede6c735529, + 0x5f5a71e9fddc339 + ]) + } + }, + c1: Fp6 { + c0: Fp2 { + c0: Fp::from_raw_unchecked([ + 0xd30a88a1b062c679, + 0x5ac56a5d35fc8304, + 0xd0c834a6a81f290d, + 0xcd5430c2da3707c7, + 0xf0c27ff780500af0, + 0x9245da6e2d72eae + ]), + c1: Fp::from_raw_unchecked([ + 0x9f2e0676791b5156, + 0xe2d1c8234918fe13, + 0x4c9e459f3c561bf4, + 0xa3e85e53b9d3e3c1, + 0x820a121e21a70020, + 0x15af618341c59acc + ]) + }, + c1: Fp2 { + c0: Fp::from_raw_unchecked([ + 0x7c95658c24993ab1, + 0x73eb38721ca886b9, + 0x5256d749477434bc, + 0x8ba41902ea504a8b, + 0x4a3d3f80c86ce6d, + 0x18a64a87fb686eaa + ]), + c1: Fp::from_raw_unchecked([ + 0xbb83e71bb920cf26, + 0x2a5277ac92a73945, + 0xfc0ee59f94f046a0, + 0x7158cdf3786058f7, + 0x7cc1061b82f945f6, + 0x3f847aa9fdbe567 + ]) + }, + c2: Fp2 { + c0: Fp::from_raw_unchecked([ + 0x8078dba56134e657, + 0x1cd7ec9a43998a6e, + 0xb1aa599a1a993766, + 0xc9a0f62f0842ee44, + 0x8e159be3b605dffa, + 0xc86ba0d4af13fc2 + ]), + c1: Fp::from_raw_unchecked([ + 0xe80ff2a06a52ffb1, + 0x7694ca48721a906c, + 0x7583183e03b08514, + 0xf567afdd40cee4e2, + 0x9a6d96d2e526a5fc, + 0x197e9f49861f2242 + ]) + } + } + } + ); +} diff --git a/bls12_381/src/util.rs b/bls12_381/src/util.rs new file mode 100644 index 0000000..bd25dd0 --- /dev/null +++ b/bls12_381/src/util.rs @@ -0,0 +1,174 @@ +/// Compute a + b + carry, returning the result and the new carry over. +#[inline(always)] +pub const fn adc(a: u64, b: u64, carry: u64) -> (u64, u64) { + let ret = (a as u128) + (b as u128) + (carry as u128); + (ret as u64, (ret >> 64) as u64) +} + +/// Compute a - (b + borrow), returning the result and the new borrow. +#[inline(always)] +pub const fn sbb(a: u64, b: u64, borrow: u64) -> (u64, u64) { + let ret = (a as u128).wrapping_sub((b as u128) + ((borrow >> 63) as u128)); + (ret as u64, (ret >> 64) as u64) +} + +/// Compute a + (b * c) + carry, returning the result and the new carry over. +#[inline(always)] +pub const fn mac(a: u64, b: u64, c: u64, carry: u64) -> (u64, u64) { + let ret = (a as u128) + ((b as u128) * (c as u128)) + (carry as u128); + (ret as u64, (ret >> 64) as u64) +} + +macro_rules! impl_add_binop_specify_output { + ($lhs:ident, $rhs:ident, $output:ident) => { + impl<'b> Add<&'b $rhs> for $lhs { + type Output = $output; + + #[inline] + fn add(self, rhs: &'b $rhs) -> $output { + &self + rhs + } + } + + impl<'a> Add<$rhs> for &'a $lhs { + type Output = $output; + + #[inline] + fn add(self, rhs: $rhs) -> $output { + self + &rhs + } + } + + impl Add<$rhs> for $lhs { + type Output = $output; + + #[inline] + fn add(self, rhs: $rhs) -> $output { + &self + &rhs + } + } + }; +} + +macro_rules! impl_sub_binop_specify_output { + ($lhs:ident, $rhs:ident, $output:ident) => { + impl<'b> Sub<&'b $rhs> for $lhs { + type Output = $output; + + #[inline] + fn sub(self, rhs: &'b $rhs) -> $output { + &self - rhs + } + } + + impl<'a> Sub<$rhs> for &'a $lhs { + type Output = $output; + + #[inline] + fn sub(self, rhs: $rhs) -> $output { + self - &rhs + } + } + + impl Sub<$rhs> for $lhs { + type Output = $output; + + #[inline] + fn sub(self, rhs: $rhs) -> $output { + &self - &rhs + } + } + }; +} + +macro_rules! impl_binops_additive_specify_output { + ($lhs:ident, $rhs:ident, $output:ident) => { + impl_add_binop_specify_output!($lhs, $rhs, $output); + impl_sub_binop_specify_output!($lhs, $rhs, $output); + }; +} + +macro_rules! impl_binops_multiplicative_mixed { + ($lhs:ident, $rhs:ident, $output:ident) => { + impl<'b> Mul<&'b $rhs> for $lhs { + type Output = $output; + + #[inline] + fn mul(self, rhs: &'b $rhs) -> $output { + &self * rhs + } + } + + impl<'a> Mul<$rhs> for &'a $lhs { + type Output = $output; + + #[inline] + fn mul(self, rhs: $rhs) -> $output { + self * &rhs + } + } + + impl Mul<$rhs> for $lhs { + type Output = $output; + + #[inline] + fn mul(self, rhs: $rhs) -> $output { + &self * &rhs + } + } + }; +} + +macro_rules! impl_binops_additive { + ($lhs:ident, $rhs:ident) => { + impl_binops_additive_specify_output!($lhs, $rhs, $lhs); + + impl SubAssign<$rhs> for $lhs { + #[inline] + fn sub_assign(&mut self, rhs: $rhs) { + *self = &*self - &rhs; + } + } + + impl AddAssign<$rhs> for $lhs { + #[inline] + fn add_assign(&mut self, rhs: $rhs) { + *self = &*self + &rhs; + } + } + + impl<'b> SubAssign<&'b $rhs> for $lhs { + #[inline] + fn sub_assign(&mut self, rhs: &'b $rhs) { + *self = &*self - rhs; + } + } + + impl<'b> AddAssign<&'b $rhs> for $lhs { + #[inline] + fn add_assign(&mut self, rhs: &'b $rhs) { + *self = &*self + rhs; + } + } + }; +} + +macro_rules! impl_binops_multiplicative { + ($lhs:ident, $rhs:ident) => { + impl_binops_multiplicative_mixed!($lhs, $rhs, $lhs); + + impl MulAssign<$rhs> for $lhs { + #[inline] + fn mul_assign(&mut self, rhs: $rhs) { + *self = &*self * &rhs; + } + } + + impl<'b> MulAssign<&'b $rhs> for $lhs { + #[inline] + fn mul_assign(&mut self, rhs: &'b $rhs) { + *self = &*self * rhs; + } + } + }; +}