mirror of
https://github.com/Qortal/pirate-librustzcash.git
synced 2025-01-30 15:32:14 +00:00
Merge commit 'dbd9bd1b9b43038e60bda8f14576580e51924ea0' as 'bls12_381'
This commit is contained in:
commit
f5217b56d7
95
bls12_381/.github/workflows/ci.yml
vendored
Normal file
95
bls12_381/.github/workflows/ci.yml
vendored
Normal file
@ -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
|
3
bls12_381/.gitignore
vendored
Normal file
3
bls12_381/.gitignore
vendored
Normal file
@ -0,0 +1,3 @@
|
||||
/target
|
||||
**/*.rs.bk
|
||||
Cargo.lock
|
14
bls12_381/COPYRIGHT
Normal file
14
bls12_381/COPYRIGHT
Normal file
@ -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.
|
32
bls12_381/Cargo.toml
Normal file
32
bls12_381/Cargo.toml
Normal file
@ -0,0 +1,32 @@
|
||||
[package]
|
||||
authors = ["Sean Bowe <ewillbefull@gmail.com>"]
|
||||
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"]
|
201
bls12_381/LICENSE-APACHE
Normal file
201
bls12_381/LICENSE-APACHE
Normal file
@ -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.
|
23
bls12_381/LICENSE-MIT
Normal file
23
bls12_381/LICENSE-MIT
Normal file
@ -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.
|
63
bls12_381/README.md
Normal file
63
bls12_381/README.md
Normal file
@ -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)<sup>2</sup>(z<sup>4</sup> - z<sup>2</sup> + 1) / 3 + z
|
||||
* = `0x1a0111ea397fe69a4b1ba7b6434bacd764774b84f38512bf6730d2a0f6b0f6241eabfffeb153ffffb9feffffffffaaab`
|
||||
* q = z<sup>4</sup> - z<sup>2</sup> + 1
|
||||
* = `0x73eda753299d7d483339d80809a1d80553bda402fffe5bfeffffffff00000001`
|
||||
|
||||
... yielding two **source groups** G<sub>1</sub> and G<sub>2</sub>, each of 255-bit prime order `q`, such that an efficiently computable non-degenerate bilinear pairing function `e` exists into a third **target group** G<sub>T</sub>. Specifically, G<sub>1</sub> is the `q`-order subgroup of E(F<sub>p</sub>) : y<sup>2</sup> = x<sup>3</sup> + 4 and G<sub>2</sub> is the `q`-order subgroup of E'(F<sub>p<sup>2</sup></sub>) : y<sup>2</sup> = x<sup>3</sup> + 4(u + 1) where the extention field F<sub>p<sup>2</sup></sub> is defined as F<sub>p</sub>(u) / (u<sup>2</sup> + 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 2<sup>32</sup> 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 G<sub>2</sub> 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.
|
3
bls12_381/RELEASES.md
Normal file
3
bls12_381/RELEASES.md
Normal file
@ -0,0 +1,3 @@
|
||||
# 0.1.0
|
||||
|
||||
Initial release.
|
170
bls12_381/benches/groups.rs
Normal file
170
bls12_381/benches/groups.rs
Normal file
@ -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);
|
15
bls12_381/katex-header.html
Normal file
15
bls12_381/katex-header.html
Normal file
@ -0,0 +1,15 @@
|
||||
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/katex@0.10.0/dist/katex.min.css" integrity="sha384-9eLZqc9ds8eNjO3TmqPeYcDj8n+Qfa4nuSiGYa6DjLNcv9BtN69ZIulL9+8CqC9Y" crossorigin="anonymous">
|
||||
<script src="https://cdn.jsdelivr.net/npm/katex@0.10.0/dist/katex.min.js" integrity="sha384-K3vbOmF2BtaVai+Qk37uypf7VrgBubhQreNQe9aGsz9lB63dIFiQVlJbr92dw2Lx" crossorigin="anonymous"></script>
|
||||
<script src="https://cdn.jsdelivr.net/npm/katex@0.10.0/dist/contrib/auto-render.min.js" integrity="sha384-kmZOZB5ObwgQnS/DuDg6TScgOiWWBiVt0plIRkZCmE6rDZGrEOQeHM5PcHi+nyqe" crossorigin="anonymous"></script>
|
||||
<script>
|
||||
document.addEventListener("DOMContentLoaded", function() {
|
||||
renderMathInElement(document.body, {
|
||||
delimiters: [
|
||||
{left: "$$", right: "$$", display: true},
|
||||
{left: "\\(", right: "\\)", display: false},
|
||||
{left: "$", right: "$", display: false},
|
||||
{left: "\\[", right: "\\]", display: true}
|
||||
]
|
||||
});
|
||||
});
|
||||
</script>
|
1
bls12_381/rust-toolchain
Normal file
1
bls12_381/rust-toolchain
Normal file
@ -0,0 +1 @@
|
||||
1.36.0
|
859
bls12_381/src/fp.rs
Normal file
859
bls12_381/src/fp.rs
Normal file
@ -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<Fp> {
|
||||
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<Self> {
|
||||
// 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<Self> {
|
||||
// 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
|
||||
// <http://cacr.uwaterloo.ca/hac/about/chap14.pdf>.
|
||||
|
||||
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()
|
||||
));
|
||||
}
|
638
bls12_381/src/fp12.rs
Normal file
638
bls12_381/src/fp12.rs
Normal file
@ -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<Fp> for Fp12 {
|
||||
fn from(f: Fp) -> Fp12 {
|
||||
Fp12 {
|
||||
c0: Fp6::from(f),
|
||||
c1: Fp6::zero(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl From<Fp2> for Fp12 {
|
||||
fn from(f: Fp2) -> Fp12 {
|
||||
Fp12 {
|
||||
c0: Fp6::from(f),
|
||||
c1: Fp6::zero(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl From<Fp6> 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> {
|
||||
(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()
|
||||
);
|
||||
}
|
868
bls12_381/src/fp2.rs
Normal file
868
bls12_381/src/fp2.rs
Normal file
@ -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<Fp> 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<Self> {
|
||||
// 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<Self> {
|
||||
// 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()
|
||||
));
|
||||
}
|
507
bls12_381/src/fp6.rs
Normal file
507
bls12_381/src/fp6.rs
Normal file
@ -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<Fp> for Fp6 {
|
||||
fn from(f: Fp) -> Fp6 {
|
||||
Fp6 {
|
||||
c0: Fp2::from(f),
|
||||
c1: Fp2::zero(),
|
||||
c2: Fp2::zero(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl From<Fp2> 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<Self> {
|
||||
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());
|
||||
}
|
1343
bls12_381/src/g1.rs
Normal file
1343
bls12_381/src/g1.rs
Normal file
File diff suppressed because it is too large
Load Diff
1591
bls12_381/src/g2.rs
Normal file
1591
bls12_381/src/g2.rs
Normal file
File diff suppressed because it is too large
Load Diff
81
bls12_381/src/lib.rs
Normal file
81
bls12_381/src/lib.rs
Normal file
@ -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};
|
63
bls12_381/src/notes/design.rs
Normal file
63
bls12_381/src/notes/design.rs
Normal file
@ -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.<j> = PolynomialRing(Fq, 'j')
|
||||
//! Fq2.<i> = 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
|
||||
//! ```
|
29
bls12_381/src/notes/serialization.rs
Normal file
29
bls12_381/src/notes/serialization.rs
Normal file
@ -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.
|
654
bls12_381/src/pairings.rs
Normal file
654
bls12_381/src/pairings.rs
Normal file
@ -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<G2Affine> 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<D: MillerLoopDriver>(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);
|
||||
}
|
1076
bls12_381/src/scalar.rs
Normal file
1076
bls12_381/src/scalar.rs
Normal file
File diff suppressed because it is too large
Load Diff
BIN
bls12_381/src/tests/g1_compressed_valid_test_vectors.dat
Normal file
BIN
bls12_381/src/tests/g1_compressed_valid_test_vectors.dat
Normal file
Binary file not shown.
BIN
bls12_381/src/tests/g1_uncompressed_valid_test_vectors.dat
Normal file
BIN
bls12_381/src/tests/g1_uncompressed_valid_test_vectors.dat
Normal file
Binary file not shown.
BIN
bls12_381/src/tests/g2_compressed_valid_test_vectors.dat
Normal file
BIN
bls12_381/src/tests/g2_compressed_valid_test_vectors.dat
Normal file
Binary file not shown.
BIN
bls12_381/src/tests/g2_uncompressed_valid_test_vectors.dat
Normal file
BIN
bls12_381/src/tests/g2_uncompressed_valid_test_vectors.dat
Normal file
Binary file not shown.
230
bls12_381/src/tests/mod.rs
Normal file
230
bls12_381/src/tests/mod.rs
Normal file
@ -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
|
||||
])
|
||||
}
|
||||
}
|
||||
}
|
||||
);
|
||||
}
|
174
bls12_381/src/util.rs
Normal file
174
bls12_381/src/util.rs
Normal file
@ -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;
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
Loading…
Reference in New Issue
Block a user