Merge commit 'dbd9bd1b9b43038e60bda8f14576580e51924ea0' as 'bls12_381'

This commit is contained in:
Sean Bowe 2019-12-12 11:32:35 -07:00
commit f5217b56d7
28 changed files with 8733 additions and 0 deletions

95
bls12_381/.github/workflows/ci.yml vendored Normal file
View 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
View File

@ -0,0 +1,3 @@
/target
**/*.rs.bk
Cargo.lock

14
bls12_381/COPYRIGHT Normal file
View 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
View 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
View 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
View 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
View 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
View File

@ -0,0 +1,3 @@
# 0.1.0
Initial release.

170
bls12_381/benches/groups.rs Normal file
View 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);

View 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
View File

@ -0,0 +1 @@
1.36.0

859
bls12_381/src/fp.rs Normal file
View 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
View 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
View 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
View 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

File diff suppressed because it is too large Load Diff

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
View 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};

View 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
//! ```

View 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
View 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

File diff suppressed because it is too large Load Diff

230
bls12_381/src/tests/mod.rs Normal file
View 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
View 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;
}
}
};
}