Add 'pairing/' from commit '09b6e6f9212020f385218e5cf5287e381ccd312b'
git-subtree-dir: pairing git-subtree-mainline:ad16ba6a35
git-subtree-split:09b6e6f921
This commit is contained in:
commit
e924247e73
|
@ -0,0 +1,3 @@
|
|||
target/
|
||||
**/*.rs.bk
|
||||
Cargo.lock
|
|
@ -0,0 +1,14 @@
|
|||
Copyrights in the "pairing" library are retained by their contributors. No
|
||||
copyright assignment is required to contribute to the "pairing" library.
|
||||
|
||||
The "pairing" library is licensed under either of
|
||||
|
||||
* Apache License, Version 2.0, (see ./LICENSE-APACHE or http://www.apache.org/licenses/LICENSE-2.0)
|
||||
* MIT license (see ./LICENSE-MIT or http://opensource.org/licenses/MIT)
|
||||
|
||||
at your option.
|
||||
|
||||
Unless you explicitly state otherwise, any contribution intentionally
|
||||
submitted for inclusion in the work by you, as defined in the Apache-2.0
|
||||
license, shall be dual licensed as above, without any additional terms or
|
||||
conditions.
|
|
@ -0,0 +1,23 @@
|
|||
[package]
|
||||
name = "pairing"
|
||||
|
||||
# Remember to change version string in README.md.
|
||||
version = "0.14.2"
|
||||
authors = ["Sean Bowe <ewillbefull@gmail.com>"]
|
||||
license = "MIT/Apache-2.0"
|
||||
|
||||
description = "Pairing-friendly elliptic curve library"
|
||||
documentation = "https://docs.rs/pairing/"
|
||||
homepage = "https://github.com/ebfull/pairing"
|
||||
repository = "https://github.com/ebfull/pairing"
|
||||
|
||||
[dependencies]
|
||||
rand = "0.4"
|
||||
byteorder = "1"
|
||||
clippy = { version = "0.0.200", optional = true }
|
||||
|
||||
[features]
|
||||
unstable-features = ["expose-arith"]
|
||||
expose-arith = []
|
||||
u128-support = []
|
||||
default = []
|
|
@ -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.
|
|
@ -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.
|
|
@ -0,0 +1,35 @@
|
|||
# pairing [![Crates.io](https://img.shields.io/crates/v/pairing.svg)](https://crates.io/crates/pairing) #
|
||||
|
||||
This is a Rust crate for using pairing-friendly elliptic curves. Currently, only the [BLS12-381](https://z.cash/blog/new-snark-curve.html) construction is implemented.
|
||||
|
||||
## [Documentation](https://docs.rs/pairing/)
|
||||
|
||||
Bring the `pairing` crate into your project just as you normally would.
|
||||
|
||||
If you're using a supported platform and the nightly Rust compiler, you can enable the `u128-support` feature for faster arithmetic.
|
||||
|
||||
```toml
|
||||
[dependencies.pairing]
|
||||
version = "0.14"
|
||||
features = ["u128-support"]
|
||||
```
|
||||
|
||||
## Security Warnings
|
||||
|
||||
This library does not make any guarantees about constant-time operations, memory access patterns, or resistance to side-channel attacks.
|
||||
|
||||
## License
|
||||
|
||||
Licensed under either of
|
||||
|
||||
* Apache License, Version 2.0, ([LICENSE-APACHE](LICENSE-APACHE) or http://www.apache.org/licenses/LICENSE-2.0)
|
||||
* MIT license ([LICENSE-MIT](LICENSE-MIT) or http://opensource.org/licenses/MIT)
|
||||
|
||||
at your option.
|
||||
|
||||
### Contribution
|
||||
|
||||
Unless you explicitly state otherwise, any contribution intentionally
|
||||
submitted for inclusion in the work by you, as defined in the Apache-2.0
|
||||
license, shall be dual licensed as above, without any additional terms or
|
||||
conditions.
|
|
@ -0,0 +1,127 @@
|
|||
mod g1 {
|
||||
use rand::{Rand, SeedableRng, XorShiftRng};
|
||||
|
||||
use pairing::bls12_381::*;
|
||||
use pairing::CurveProjective;
|
||||
|
||||
#[bench]
|
||||
fn bench_g1_mul_assign(b: &mut ::test::Bencher) {
|
||||
const SAMPLES: usize = 1000;
|
||||
|
||||
let mut rng = XorShiftRng::from_seed([0x5dbe6259, 0x8d313d76, 0x3237db17, 0xe5bc0654]);
|
||||
|
||||
let v: Vec<(G1, Fr)> = (0..SAMPLES)
|
||||
.map(|_| (G1::rand(&mut rng), Fr::rand(&mut rng)))
|
||||
.collect();
|
||||
|
||||
let mut count = 0;
|
||||
b.iter(|| {
|
||||
let mut tmp = v[count].0;
|
||||
tmp.mul_assign(v[count].1);
|
||||
count = (count + 1) % SAMPLES;
|
||||
tmp
|
||||
});
|
||||
}
|
||||
|
||||
#[bench]
|
||||
fn bench_g1_add_assign(b: &mut ::test::Bencher) {
|
||||
const SAMPLES: usize = 1000;
|
||||
|
||||
let mut rng = XorShiftRng::from_seed([0x5dbe6259, 0x8d313d76, 0x3237db17, 0xe5bc0654]);
|
||||
|
||||
let v: Vec<(G1, G1)> = (0..SAMPLES)
|
||||
.map(|_| (G1::rand(&mut rng), G1::rand(&mut rng)))
|
||||
.collect();
|
||||
|
||||
let mut count = 0;
|
||||
b.iter(|| {
|
||||
let mut tmp = v[count].0;
|
||||
tmp.add_assign(&v[count].1);
|
||||
count = (count + 1) % SAMPLES;
|
||||
tmp
|
||||
});
|
||||
}
|
||||
|
||||
#[bench]
|
||||
fn bench_g1_add_assign_mixed(b: &mut ::test::Bencher) {
|
||||
const SAMPLES: usize = 1000;
|
||||
|
||||
let mut rng = XorShiftRng::from_seed([0x5dbe6259, 0x8d313d76, 0x3237db17, 0xe5bc0654]);
|
||||
|
||||
let v: Vec<(G1, G1Affine)> = (0..SAMPLES)
|
||||
.map(|_| (G1::rand(&mut rng), G1::rand(&mut rng).into()))
|
||||
.collect();
|
||||
|
||||
let mut count = 0;
|
||||
b.iter(|| {
|
||||
let mut tmp = v[count].0;
|
||||
tmp.add_assign_mixed(&v[count].1);
|
||||
count = (count + 1) % SAMPLES;
|
||||
tmp
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
mod g2 {
|
||||
use rand::{Rand, SeedableRng, XorShiftRng};
|
||||
|
||||
use pairing::bls12_381::*;
|
||||
use pairing::CurveProjective;
|
||||
|
||||
#[bench]
|
||||
fn bench_g2_mul_assign(b: &mut ::test::Bencher) {
|
||||
const SAMPLES: usize = 1000;
|
||||
|
||||
let mut rng = XorShiftRng::from_seed([0x5dbe6259, 0x8d313d76, 0x3237db17, 0xe5bc0654]);
|
||||
|
||||
let v: Vec<(G2, Fr)> = (0..SAMPLES)
|
||||
.map(|_| (G2::rand(&mut rng), Fr::rand(&mut rng)))
|
||||
.collect();
|
||||
|
||||
let mut count = 0;
|
||||
b.iter(|| {
|
||||
let mut tmp = v[count].0;
|
||||
tmp.mul_assign(v[count].1);
|
||||
count = (count + 1) % SAMPLES;
|
||||
tmp
|
||||
});
|
||||
}
|
||||
|
||||
#[bench]
|
||||
fn bench_g2_add_assign(b: &mut ::test::Bencher) {
|
||||
const SAMPLES: usize = 1000;
|
||||
|
||||
let mut rng = XorShiftRng::from_seed([0x5dbe6259, 0x8d313d76, 0x3237db17, 0xe5bc0654]);
|
||||
|
||||
let v: Vec<(G2, G2)> = (0..SAMPLES)
|
||||
.map(|_| (G2::rand(&mut rng), G2::rand(&mut rng)))
|
||||
.collect();
|
||||
|
||||
let mut count = 0;
|
||||
b.iter(|| {
|
||||
let mut tmp = v[count].0;
|
||||
tmp.add_assign(&v[count].1);
|
||||
count = (count + 1) % SAMPLES;
|
||||
tmp
|
||||
});
|
||||
}
|
||||
|
||||
#[bench]
|
||||
fn bench_g2_add_assign_mixed(b: &mut ::test::Bencher) {
|
||||
const SAMPLES: usize = 1000;
|
||||
|
||||
let mut rng = XorShiftRng::from_seed([0x5dbe6259, 0x8d313d76, 0x3237db17, 0xe5bc0654]);
|
||||
|
||||
let v: Vec<(G2, G2Affine)> = (0..SAMPLES)
|
||||
.map(|_| (G2::rand(&mut rng), G2::rand(&mut rng).into()))
|
||||
.collect();
|
||||
|
||||
let mut count = 0;
|
||||
b.iter(|| {
|
||||
let mut tmp = v[count].0;
|
||||
tmp.add_assign_mixed(&v[count].1);
|
||||
count = (count + 1) % SAMPLES;
|
||||
tmp
|
||||
});
|
||||
}
|
||||
}
|
|
@ -0,0 +1,268 @@
|
|||
use rand::{Rand, SeedableRng, XorShiftRng};
|
||||
|
||||
use pairing::bls12_381::*;
|
||||
use pairing::{Field, PrimeField, PrimeFieldRepr, SqrtField};
|
||||
|
||||
#[bench]
|
||||
fn bench_fq_repr_add_nocarry(b: &mut ::test::Bencher) {
|
||||
const SAMPLES: usize = 1000;
|
||||
|
||||
let mut rng = XorShiftRng::from_seed([0x5dbe6259, 0x8d313d76, 0x3237db17, 0xe5bc0654]);
|
||||
|
||||
let v: Vec<(FqRepr, FqRepr)> = (0..SAMPLES)
|
||||
.map(|_| {
|
||||
let mut tmp1 = FqRepr::rand(&mut rng);
|
||||
let mut tmp2 = FqRepr::rand(&mut rng);
|
||||
// Shave a few bits off to avoid overflow.
|
||||
for _ in 0..3 {
|
||||
tmp1.div2();
|
||||
tmp2.div2();
|
||||
}
|
||||
(tmp1, tmp2)
|
||||
})
|
||||
.collect();
|
||||
|
||||
let mut count = 0;
|
||||
b.iter(|| {
|
||||
let mut tmp = v[count].0;
|
||||
tmp.add_nocarry(&v[count].1);
|
||||
count = (count + 1) % SAMPLES;
|
||||
tmp
|
||||
});
|
||||
}
|
||||
|
||||
#[bench]
|
||||
fn bench_fq_repr_sub_noborrow(b: &mut ::test::Bencher) {
|
||||
const SAMPLES: usize = 1000;
|
||||
|
||||
let mut rng = XorShiftRng::from_seed([0x5dbe6259, 0x8d313d76, 0x3237db17, 0xe5bc0654]);
|
||||
|
||||
let v: Vec<(FqRepr, FqRepr)> = (0..SAMPLES)
|
||||
.map(|_| {
|
||||
let tmp1 = FqRepr::rand(&mut rng);
|
||||
let mut tmp2 = tmp1;
|
||||
// Ensure tmp2 is smaller than tmp1.
|
||||
for _ in 0..10 {
|
||||
tmp2.div2();
|
||||
}
|
||||
(tmp1, tmp2)
|
||||
})
|
||||
.collect();
|
||||
|
||||
let mut count = 0;
|
||||
b.iter(|| {
|
||||
let mut tmp = v[count].0;
|
||||
tmp.sub_noborrow(&v[count].1);
|
||||
count = (count + 1) % SAMPLES;
|
||||
tmp
|
||||
});
|
||||
}
|
||||
|
||||
#[bench]
|
||||
fn bench_fq_repr_num_bits(b: &mut ::test::Bencher) {
|
||||
const SAMPLES: usize = 1000;
|
||||
|
||||
let mut rng = XorShiftRng::from_seed([0x5dbe6259, 0x8d313d76, 0x3237db17, 0xe5bc0654]);
|
||||
|
||||
let v: Vec<FqRepr> = (0..SAMPLES).map(|_| FqRepr::rand(&mut rng)).collect();
|
||||
|
||||
let mut count = 0;
|
||||
b.iter(|| {
|
||||
let tmp = v[count].num_bits();
|
||||
count = (count + 1) % SAMPLES;
|
||||
tmp
|
||||
});
|
||||
}
|
||||
|
||||
#[bench]
|
||||
fn bench_fq_repr_mul2(b: &mut ::test::Bencher) {
|
||||
const SAMPLES: usize = 1000;
|
||||
|
||||
let mut rng = XorShiftRng::from_seed([0x5dbe6259, 0x8d313d76, 0x3237db17, 0xe5bc0654]);
|
||||
|
||||
let v: Vec<FqRepr> = (0..SAMPLES).map(|_| FqRepr::rand(&mut rng)).collect();
|
||||
|
||||
let mut count = 0;
|
||||
b.iter(|| {
|
||||
let mut tmp = v[count];
|
||||
tmp.mul2();
|
||||
count = (count + 1) % SAMPLES;
|
||||
tmp
|
||||
});
|
||||
}
|
||||
|
||||
#[bench]
|
||||
fn bench_fq_repr_div2(b: &mut ::test::Bencher) {
|
||||
const SAMPLES: usize = 1000;
|
||||
|
||||
let mut rng = XorShiftRng::from_seed([0x5dbe6259, 0x8d313d76, 0x3237db17, 0xe5bc0654]);
|
||||
|
||||
let v: Vec<FqRepr> = (0..SAMPLES).map(|_| FqRepr::rand(&mut rng)).collect();
|
||||
|
||||
let mut count = 0;
|
||||
b.iter(|| {
|
||||
let mut tmp = v[count];
|
||||
tmp.div2();
|
||||
count = (count + 1) % SAMPLES;
|
||||
tmp
|
||||
});
|
||||
}
|
||||
|
||||
#[bench]
|
||||
fn bench_fq_add_assign(b: &mut ::test::Bencher) {
|
||||
const SAMPLES: usize = 1000;
|
||||
|
||||
let mut rng = XorShiftRng::from_seed([0x5dbe6259, 0x8d313d76, 0x3237db17, 0xe5bc0654]);
|
||||
|
||||
let v: Vec<(Fq, Fq)> = (0..SAMPLES)
|
||||
.map(|_| (Fq::rand(&mut rng), Fq::rand(&mut rng)))
|
||||
.collect();
|
||||
|
||||
let mut count = 0;
|
||||
b.iter(|| {
|
||||
let mut tmp = v[count].0;
|
||||
tmp.add_assign(&v[count].1);
|
||||
count = (count + 1) % SAMPLES;
|
||||
tmp
|
||||
});
|
||||
}
|
||||
|
||||
#[bench]
|
||||
fn bench_fq_sub_assign(b: &mut ::test::Bencher) {
|
||||
const SAMPLES: usize = 1000;
|
||||
|
||||
let mut rng = XorShiftRng::from_seed([0x5dbe6259, 0x8d313d76, 0x3237db17, 0xe5bc0654]);
|
||||
|
||||
let v: Vec<(Fq, Fq)> = (0..SAMPLES)
|
||||
.map(|_| (Fq::rand(&mut rng), Fq::rand(&mut rng)))
|
||||
.collect();
|
||||
|
||||
let mut count = 0;
|
||||
b.iter(|| {
|
||||
let mut tmp = v[count].0;
|
||||
tmp.sub_assign(&v[count].1);
|
||||
count = (count + 1) % SAMPLES;
|
||||
tmp
|
||||
});
|
||||
}
|
||||
|
||||
#[bench]
|
||||
fn bench_fq_mul_assign(b: &mut ::test::Bencher) {
|
||||
const SAMPLES: usize = 1000;
|
||||
|
||||
let mut rng = XorShiftRng::from_seed([0x5dbe6259, 0x8d313d76, 0x3237db17, 0xe5bc0654]);
|
||||
|
||||
let v: Vec<(Fq, Fq)> = (0..SAMPLES)
|
||||
.map(|_| (Fq::rand(&mut rng), Fq::rand(&mut rng)))
|
||||
.collect();
|
||||
|
||||
let mut count = 0;
|
||||
b.iter(|| {
|
||||
let mut tmp = v[count].0;
|
||||
tmp.mul_assign(&v[count].1);
|
||||
count = (count + 1) % SAMPLES;
|
||||
tmp
|
||||
});
|
||||
}
|
||||
|
||||
#[bench]
|
||||
fn bench_fq_square(b: &mut ::test::Bencher) {
|
||||
const SAMPLES: usize = 1000;
|
||||
|
||||
let mut rng = XorShiftRng::from_seed([0x5dbe6259, 0x8d313d76, 0x3237db17, 0xe5bc0654]);
|
||||
|
||||
let v: Vec<Fq> = (0..SAMPLES).map(|_| Fq::rand(&mut rng)).collect();
|
||||
|
||||
let mut count = 0;
|
||||
b.iter(|| {
|
||||
let mut tmp = v[count];
|
||||
tmp.square();
|
||||
count = (count + 1) % SAMPLES;
|
||||
tmp
|
||||
});
|
||||
}
|
||||
|
||||
#[bench]
|
||||
fn bench_fq_inverse(b: &mut ::test::Bencher) {
|
||||
const SAMPLES: usize = 1000;
|
||||
|
||||
let mut rng = XorShiftRng::from_seed([0x5dbe6259, 0x8d313d76, 0x3237db17, 0xe5bc0654]);
|
||||
|
||||
let v: Vec<Fq> = (0..SAMPLES).map(|_| Fq::rand(&mut rng)).collect();
|
||||
|
||||
let mut count = 0;
|
||||
b.iter(|| {
|
||||
count = (count + 1) % SAMPLES;
|
||||
v[count].inverse()
|
||||
});
|
||||
}
|
||||
|
||||
#[bench]
|
||||
fn bench_fq_negate(b: &mut ::test::Bencher) {
|
||||
const SAMPLES: usize = 1000;
|
||||
|
||||
let mut rng = XorShiftRng::from_seed([0x5dbe6259, 0x8d313d76, 0x3237db17, 0xe5bc0654]);
|
||||
|
||||
let v: Vec<Fq> = (0..SAMPLES).map(|_| Fq::rand(&mut rng)).collect();
|
||||
|
||||
let mut count = 0;
|
||||
b.iter(|| {
|
||||
let mut tmp = v[count];
|
||||
tmp.negate();
|
||||
count = (count + 1) % SAMPLES;
|
||||
tmp
|
||||
});
|
||||
}
|
||||
|
||||
#[bench]
|
||||
fn bench_fq_sqrt(b: &mut ::test::Bencher) {
|
||||
const SAMPLES: usize = 1000;
|
||||
|
||||
let mut rng = XorShiftRng::from_seed([0x5dbe6259, 0x8d313d76, 0x3237db17, 0xe5bc0654]);
|
||||
|
||||
let v: Vec<Fq> = (0..SAMPLES)
|
||||
.map(|_| {
|
||||
let mut tmp = Fq::rand(&mut rng);
|
||||
tmp.square();
|
||||
tmp
|
||||
})
|
||||
.collect();
|
||||
|
||||
let mut count = 0;
|
||||
b.iter(|| {
|
||||
count = (count + 1) % SAMPLES;
|
||||
v[count].sqrt()
|
||||
});
|
||||
}
|
||||
|
||||
#[bench]
|
||||
fn bench_fq_into_repr(b: &mut ::test::Bencher) {
|
||||
const SAMPLES: usize = 1000;
|
||||
|
||||
let mut rng = XorShiftRng::from_seed([0x5dbe6259, 0x8d313d76, 0x3237db17, 0xe5bc0654]);
|
||||
|
||||
let v: Vec<Fq> = (0..SAMPLES).map(|_| Fq::rand(&mut rng)).collect();
|
||||
|
||||
let mut count = 0;
|
||||
b.iter(|| {
|
||||
count = (count + 1) % SAMPLES;
|
||||
v[count].into_repr()
|
||||
});
|
||||
}
|
||||
|
||||
#[bench]
|
||||
fn bench_fq_from_repr(b: &mut ::test::Bencher) {
|
||||
const SAMPLES: usize = 1000;
|
||||
|
||||
let mut rng = XorShiftRng::from_seed([0x5dbe6259, 0x8d313d76, 0x3237db17, 0xe5bc0654]);
|
||||
|
||||
let v: Vec<FqRepr> = (0..SAMPLES)
|
||||
.map(|_| Fq::rand(&mut rng).into_repr())
|
||||
.collect();
|
||||
|
||||
let mut count = 0;
|
||||
b.iter(|| {
|
||||
count = (count + 1) % SAMPLES;
|
||||
Fq::from_repr(v[count])
|
||||
});
|
||||
}
|
|
@ -0,0 +1,94 @@
|
|||
use rand::{Rand, SeedableRng, XorShiftRng};
|
||||
|
||||
use pairing::bls12_381::*;
|
||||
use pairing::Field;
|
||||
|
||||
#[bench]
|
||||
fn bench_fq12_add_assign(b: &mut ::test::Bencher) {
|
||||
const SAMPLES: usize = 1000;
|
||||
|
||||
let mut rng = XorShiftRng::from_seed([0x5dbe6259, 0x8d313d76, 0x3237db17, 0xe5bc0654]);
|
||||
|
||||
let v: Vec<(Fq12, Fq12)> = (0..SAMPLES)
|
||||
.map(|_| (Fq12::rand(&mut rng), Fq12::rand(&mut rng)))
|
||||
.collect();
|
||||
|
||||
let mut count = 0;
|
||||
b.iter(|| {
|
||||
let mut tmp = v[count].0;
|
||||
tmp.add_assign(&v[count].1);
|
||||
count = (count + 1) % SAMPLES;
|
||||
tmp
|
||||
});
|
||||
}
|
||||
|
||||
#[bench]
|
||||
fn bench_fq12_sub_assign(b: &mut ::test::Bencher) {
|
||||
const SAMPLES: usize = 1000;
|
||||
|
||||
let mut rng = XorShiftRng::from_seed([0x5dbe6259, 0x8d313d76, 0x3237db17, 0xe5bc0654]);
|
||||
|
||||
let v: Vec<(Fq12, Fq12)> = (0..SAMPLES)
|
||||
.map(|_| (Fq12::rand(&mut rng), Fq12::rand(&mut rng)))
|
||||
.collect();
|
||||
|
||||
let mut count = 0;
|
||||
b.iter(|| {
|
||||
let mut tmp = v[count].0;
|
||||
tmp.sub_assign(&v[count].1);
|
||||
count = (count + 1) % SAMPLES;
|
||||
tmp
|
||||
});
|
||||
}
|
||||
|
||||
#[bench]
|
||||
fn bench_fq12_mul_assign(b: &mut ::test::Bencher) {
|
||||
const SAMPLES: usize = 1000;
|
||||
|
||||
let mut rng = XorShiftRng::from_seed([0x5dbe6259, 0x8d313d76, 0x3237db17, 0xe5bc0654]);
|
||||
|
||||
let v: Vec<(Fq12, Fq12)> = (0..SAMPLES)
|
||||
.map(|_| (Fq12::rand(&mut rng), Fq12::rand(&mut rng)))
|
||||
.collect();
|
||||
|
||||
let mut count = 0;
|
||||
b.iter(|| {
|
||||
let mut tmp = v[count].0;
|
||||
tmp.mul_assign(&v[count].1);
|
||||
count = (count + 1) % SAMPLES;
|
||||
tmp
|
||||
});
|
||||
}
|
||||
|
||||
#[bench]
|
||||
fn bench_fq12_squaring(b: &mut ::test::Bencher) {
|
||||
const SAMPLES: usize = 1000;
|
||||
|
||||
let mut rng = XorShiftRng::from_seed([0x5dbe6259, 0x8d313d76, 0x3237db17, 0xe5bc0654]);
|
||||
|
||||
let v: Vec<Fq12> = (0..SAMPLES).map(|_| Fq12::rand(&mut rng)).collect();
|
||||
|
||||
let mut count = 0;
|
||||
b.iter(|| {
|
||||
let mut tmp = v[count];
|
||||
tmp.square();
|
||||
count = (count + 1) % SAMPLES;
|
||||
tmp
|
||||
});
|
||||
}
|
||||
|
||||
#[bench]
|
||||
fn bench_fq12_inverse(b: &mut ::test::Bencher) {
|
||||
const SAMPLES: usize = 1000;
|
||||
|
||||
let mut rng = XorShiftRng::from_seed([0x5dbe6259, 0x8d313d76, 0x3237db17, 0xe5bc0654]);
|
||||
|
||||
let v: Vec<Fq12> = (0..SAMPLES).map(|_| Fq12::rand(&mut rng)).collect();
|
||||
|
||||
let mut count = 0;
|
||||
b.iter(|| {
|
||||
let tmp = v[count].inverse();
|
||||
count = (count + 1) % SAMPLES;
|
||||
tmp
|
||||
});
|
||||
}
|
|
@ -0,0 +1,110 @@
|
|||
use rand::{Rand, SeedableRng, XorShiftRng};
|
||||
|
||||
use pairing::bls12_381::*;
|
||||
use pairing::{Field, SqrtField};
|
||||
|
||||
#[bench]
|
||||
fn bench_fq2_add_assign(b: &mut ::test::Bencher) {
|
||||
const SAMPLES: usize = 1000;
|
||||
|
||||
let mut rng = XorShiftRng::from_seed([0x5dbe6259, 0x8d313d76, 0x3237db17, 0xe5bc0654]);
|
||||
|
||||
let v: Vec<(Fq2, Fq2)> = (0..SAMPLES)
|
||||
.map(|_| (Fq2::rand(&mut rng), Fq2::rand(&mut rng)))
|
||||
.collect();
|
||||
|
||||
let mut count = 0;
|
||||
b.iter(|| {
|
||||
let mut tmp = v[count].0;
|
||||
tmp.add_assign(&v[count].1);
|
||||
count = (count + 1) % SAMPLES;
|
||||
tmp
|
||||
});
|
||||
}
|
||||
|
||||
#[bench]
|
||||
fn bench_fq2_sub_assign(b: &mut ::test::Bencher) {
|
||||
const SAMPLES: usize = 1000;
|
||||
|
||||
let mut rng = XorShiftRng::from_seed([0x5dbe6259, 0x8d313d76, 0x3237db17, 0xe5bc0654]);
|
||||
|
||||
let v: Vec<(Fq2, Fq2)> = (0..SAMPLES)
|
||||
.map(|_| (Fq2::rand(&mut rng), Fq2::rand(&mut rng)))
|
||||
.collect();
|
||||
|
||||
let mut count = 0;
|
||||
b.iter(|| {
|
||||
let mut tmp = v[count].0;
|
||||
tmp.sub_assign(&v[count].1);
|
||||
count = (count + 1) % SAMPLES;
|
||||
tmp
|
||||
});
|
||||
}
|
||||
|
||||
#[bench]
|
||||
fn bench_fq2_mul_assign(b: &mut ::test::Bencher) {
|
||||
const SAMPLES: usize = 1000;
|
||||
|
||||
let mut rng = XorShiftRng::from_seed([0x5dbe6259, 0x8d313d76, 0x3237db17, 0xe5bc0654]);
|
||||
|
||||
let v: Vec<(Fq2, Fq2)> = (0..SAMPLES)
|
||||
.map(|_| (Fq2::rand(&mut rng), Fq2::rand(&mut rng)))
|
||||
.collect();
|
||||
|
||||
let mut count = 0;
|
||||
b.iter(|| {
|
||||
let mut tmp = v[count].0;
|
||||
tmp.mul_assign(&v[count].1);
|
||||
count = (count + 1) % SAMPLES;
|
||||
tmp
|
||||
});
|
||||
}
|
||||
|
||||
#[bench]
|
||||
fn bench_fq2_squaring(b: &mut ::test::Bencher) {
|
||||
const SAMPLES: usize = 1000;
|
||||
|
||||
let mut rng = XorShiftRng::from_seed([0x5dbe6259, 0x8d313d76, 0x3237db17, 0xe5bc0654]);
|
||||
|
||||
let v: Vec<Fq2> = (0..SAMPLES).map(|_| Fq2::rand(&mut rng)).collect();
|
||||
|
||||
let mut count = 0;
|
||||
b.iter(|| {
|
||||
let mut tmp = v[count];
|
||||
tmp.square();
|
||||
count = (count + 1) % SAMPLES;
|
||||
tmp
|
||||
});
|
||||
}
|
||||
|
||||
#[bench]
|
||||
fn bench_fq2_inverse(b: &mut ::test::Bencher) {
|
||||
const SAMPLES: usize = 1000;
|
||||
|
||||
let mut rng = XorShiftRng::from_seed([0x5dbe6259, 0x8d313d76, 0x3237db17, 0xe5bc0654]);
|
||||
|
||||
let v: Vec<Fq2> = (0..SAMPLES).map(|_| Fq2::rand(&mut rng)).collect();
|
||||
|
||||
let mut count = 0;
|
||||
b.iter(|| {
|
||||
let tmp = v[count].inverse();
|
||||
count = (count + 1) % SAMPLES;
|
||||
tmp
|
||||
});
|
||||
}
|
||||
|
||||
#[bench]
|
||||
fn bench_fq2_sqrt(b: &mut ::test::Bencher) {
|
||||
const SAMPLES: usize = 1000;
|
||||
|
||||
let mut rng = XorShiftRng::from_seed([0x5dbe6259, 0x8d313d76, 0x3237db17, 0xe5bc0654]);
|
||||
|
||||
let v: Vec<Fq2> = (0..SAMPLES).map(|_| Fq2::rand(&mut rng)).collect();
|
||||
|
||||
let mut count = 0;
|
||||
b.iter(|| {
|
||||
let tmp = v[count].sqrt();
|
||||
count = (count + 1) % SAMPLES;
|
||||
tmp
|
||||
});
|
||||
}
|
|
@ -0,0 +1,268 @@
|
|||
use rand::{Rand, SeedableRng, XorShiftRng};
|
||||
|
||||
use pairing::bls12_381::*;
|
||||
use pairing::{Field, PrimeField, PrimeFieldRepr, SqrtField};
|
||||
|
||||
#[bench]
|
||||
fn bench_fr_repr_add_nocarry(b: &mut ::test::Bencher) {
|
||||
const SAMPLES: usize = 1000;
|
||||
|
||||
let mut rng = XorShiftRng::from_seed([0x5dbe6259, 0x8d313d76, 0x3237db17, 0xe5bc0654]);
|
||||
|
||||
let v: Vec<(FrRepr, FrRepr)> = (0..SAMPLES)
|
||||
.map(|_| {
|
||||
let mut tmp1 = FrRepr::rand(&mut rng);
|
||||
let mut tmp2 = FrRepr::rand(&mut rng);
|
||||
// Shave a few bits off to avoid overflow.
|
||||
for _ in 0..3 {
|
||||
tmp1.div2();
|
||||
tmp2.div2();
|
||||
}
|
||||
(tmp1, tmp2)
|
||||
})
|
||||
.collect();
|
||||
|
||||
let mut count = 0;
|
||||
b.iter(|| {
|
||||
let mut tmp = v[count].0;
|
||||
tmp.add_nocarry(&v[count].1);
|
||||
count = (count + 1) % SAMPLES;
|
||||
tmp
|
||||
});
|
||||
}
|
||||
|
||||
#[bench]
|
||||
fn bench_fr_repr_sub_noborrow(b: &mut ::test::Bencher) {
|
||||
const SAMPLES: usize = 1000;
|
||||
|
||||
let mut rng = XorShiftRng::from_seed([0x5dbe6259, 0x8d313d76, 0x3237db17, 0xe5bc0654]);
|
||||
|
||||
let v: Vec<(FrRepr, FrRepr)> = (0..SAMPLES)
|
||||
.map(|_| {
|
||||
let tmp1 = FrRepr::rand(&mut rng);
|
||||
let mut tmp2 = tmp1;
|
||||
// Ensure tmp2 is smaller than tmp1.
|
||||
for _ in 0..10 {
|
||||
tmp2.div2();
|
||||
}
|
||||
(tmp1, tmp2)
|
||||
})
|
||||
.collect();
|
||||
|
||||
let mut count = 0;
|
||||
b.iter(|| {
|
||||
let mut tmp = v[count].0;
|
||||
tmp.sub_noborrow(&v[count].1);
|
||||
count = (count + 1) % SAMPLES;
|
||||
tmp
|
||||
});
|
||||
}
|
||||
|
||||
#[bench]
|
||||
fn bench_fr_repr_num_bits(b: &mut ::test::Bencher) {
|
||||
const SAMPLES: usize = 1000;
|
||||
|
||||
let mut rng = XorShiftRng::from_seed([0x5dbe6259, 0x8d313d76, 0x3237db17, 0xe5bc0654]);
|
||||
|
||||
let v: Vec<FrRepr> = (0..SAMPLES).map(|_| FrRepr::rand(&mut rng)).collect();
|
||||
|
||||
let mut count = 0;
|
||||
b.iter(|| {
|
||||
let tmp = v[count].num_bits();
|
||||
count = (count + 1) % SAMPLES;
|
||||
tmp
|
||||
});
|
||||
}
|
||||
|
||||
#[bench]
|
||||
fn bench_fr_repr_mul2(b: &mut ::test::Bencher) {
|
||||
const SAMPLES: usize = 1000;
|
||||
|
||||
let mut rng = XorShiftRng::from_seed([0x5dbe6259, 0x8d313d76, 0x3237db17, 0xe5bc0654]);
|
||||
|
||||
let v: Vec<FrRepr> = (0..SAMPLES).map(|_| FrRepr::rand(&mut rng)).collect();
|
||||
|
||||
let mut count = 0;
|
||||
b.iter(|| {
|
||||
let mut tmp = v[count];
|
||||
tmp.mul2();
|
||||
count = (count + 1) % SAMPLES;
|
||||
tmp
|
||||
});
|
||||
}
|
||||
|
||||
#[bench]
|
||||
fn bench_fr_repr_div2(b: &mut ::test::Bencher) {
|
||||
const SAMPLES: usize = 1000;
|
||||
|
||||
let mut rng = XorShiftRng::from_seed([0x5dbe6259, 0x8d313d76, 0x3237db17, 0xe5bc0654]);
|
||||
|
||||
let v: Vec<FrRepr> = (0..SAMPLES).map(|_| FrRepr::rand(&mut rng)).collect();
|
||||
|
||||
let mut count = 0;
|
||||
b.iter(|| {
|
||||
let mut tmp = v[count];
|
||||
tmp.div2();
|
||||
count = (count + 1) % SAMPLES;
|
||||
tmp
|
||||
});
|
||||
}
|
||||
|
||||
#[bench]
|
||||
fn bench_fr_add_assign(b: &mut ::test::Bencher) {
|
||||
const SAMPLES: usize = 1000;
|
||||
|
||||
let mut rng = XorShiftRng::from_seed([0x5dbe6259, 0x8d313d76, 0x3237db17, 0xe5bc0654]);
|
||||
|
||||
let v: Vec<(Fr, Fr)> = (0..SAMPLES)
|
||||
.map(|_| (Fr::rand(&mut rng), Fr::rand(&mut rng)))
|
||||
.collect();
|
||||
|
||||
let mut count = 0;
|
||||
b.iter(|| {
|
||||
let mut tmp = v[count].0;
|
||||
tmp.add_assign(&v[count].1);
|
||||
count = (count + 1) % SAMPLES;
|
||||
tmp
|
||||
});
|
||||
}
|
||||
|
||||
#[bench]
|
||||
fn bench_fr_sub_assign(b: &mut ::test::Bencher) {
|
||||
const SAMPLES: usize = 1000;
|
||||
|
||||
let mut rng = XorShiftRng::from_seed([0x5dbe6259, 0x8d313d76, 0x3237db17, 0xe5bc0654]);
|
||||
|
||||
let v: Vec<(Fr, Fr)> = (0..SAMPLES)
|
||||
.map(|_| (Fr::rand(&mut rng), Fr::rand(&mut rng)))
|
||||
.collect();
|
||||
|
||||
let mut count = 0;
|
||||
b.iter(|| {
|
||||
let mut tmp = v[count].0;
|
||||
tmp.sub_assign(&v[count].1);
|
||||
count = (count + 1) % SAMPLES;
|
||||
tmp
|
||||
});
|
||||
}
|
||||
|
||||
#[bench]
|
||||
fn bench_fr_mul_assign(b: &mut ::test::Bencher) {
|
||||
const SAMPLES: usize = 1000;
|
||||
|
||||
let mut rng = XorShiftRng::from_seed([0x5dbe6259, 0x8d313d76, 0x3237db17, 0xe5bc0654]);
|
||||
|
||||
let v: Vec<(Fr, Fr)> = (0..SAMPLES)
|
||||
.map(|_| (Fr::rand(&mut rng), Fr::rand(&mut rng)))
|
||||
.collect();
|
||||
|
||||
let mut count = 0;
|
||||
b.iter(|| {
|
||||
let mut tmp = v[count].0;
|
||||
tmp.mul_assign(&v[count].1);
|
||||
count = (count + 1) % SAMPLES;
|
||||
tmp
|
||||
});
|
||||
}
|
||||
|
||||
#[bench]
|
||||
fn bench_fr_square(b: &mut ::test::Bencher) {
|
||||
const SAMPLES: usize = 1000;
|
||||
|
||||
let mut rng = XorShiftRng::from_seed([0x5dbe6259, 0x8d313d76, 0x3237db17, 0xe5bc0654]);
|
||||
|
||||
let v: Vec<Fr> = (0..SAMPLES).map(|_| Fr::rand(&mut rng)).collect();
|
||||
|
||||
let mut count = 0;
|
||||
b.iter(|| {
|
||||
let mut tmp = v[count];
|
||||
tmp.square();
|
||||
count = (count + 1) % SAMPLES;
|
||||
tmp
|
||||
});
|
||||
}
|
||||
|
||||
#[bench]
|
||||
fn bench_fr_inverse(b: &mut ::test::Bencher) {
|
||||
const SAMPLES: usize = 1000;
|
||||
|
||||
let mut rng = XorShiftRng::from_seed([0x5dbe6259, 0x8d313d76, 0x3237db17, 0xe5bc0654]);
|
||||
|
||||
let v: Vec<Fr> = (0..SAMPLES).map(|_| Fr::rand(&mut rng)).collect();
|
||||
|
||||
let mut count = 0;
|
||||
b.iter(|| {
|
||||
count = (count + 1) % SAMPLES;
|
||||
v[count].inverse()
|
||||
});
|
||||
}
|
||||
|
||||
#[bench]
|
||||
fn bench_fr_negate(b: &mut ::test::Bencher) {
|
||||
const SAMPLES: usize = 1000;
|
||||
|
||||
let mut rng = XorShiftRng::from_seed([0x5dbe6259, 0x8d313d76, 0x3237db17, 0xe5bc0654]);
|
||||
|
||||
let v: Vec<Fr> = (0..SAMPLES).map(|_| Fr::rand(&mut rng)).collect();
|
||||
|
||||
let mut count = 0;
|
||||
b.iter(|| {
|
||||
let mut tmp = v[count];
|
||||
tmp.negate();
|
||||
count = (count + 1) % SAMPLES;
|
||||
tmp
|
||||
});
|
||||
}
|
||||
|
||||
#[bench]
|
||||
fn bench_fr_sqrt(b: &mut ::test::Bencher) {
|
||||
const SAMPLES: usize = 1000;
|
||||
|
||||
let mut rng = XorShiftRng::from_seed([0x5dbe6259, 0x8d313d76, 0x3237db17, 0xe5bc0654]);
|
||||
|
||||
let v: Vec<Fr> = (0..SAMPLES)
|
||||
.map(|_| {
|
||||
let mut tmp = Fr::rand(&mut rng);
|
||||
tmp.square();
|
||||
tmp
|
||||
})
|
||||
.collect();
|
||||
|
||||
let mut count = 0;
|
||||
b.iter(|| {
|
||||
count = (count + 1) % SAMPLES;
|
||||
v[count].sqrt()
|
||||
});
|
||||
}
|
||||
|
||||
#[bench]
|
||||
fn bench_fr_into_repr(b: &mut ::test::Bencher) {
|
||||
const SAMPLES: usize = 1000;
|
||||
|
||||
let mut rng = XorShiftRng::from_seed([0x5dbe6259, 0x8d313d76, 0x3237db17, 0xe5bc0654]);
|
||||
|
||||
let v: Vec<Fr> = (0..SAMPLES).map(|_| Fr::rand(&mut rng)).collect();
|
||||
|
||||
let mut count = 0;
|
||||
b.iter(|| {
|
||||
count = (count + 1) % SAMPLES;
|
||||
v[count].into_repr()
|
||||
});
|
||||
}
|
||||
|
||||
#[bench]
|
||||
fn bench_fr_from_repr(b: &mut ::test::Bencher) {
|
||||
const SAMPLES: usize = 1000;
|
||||
|
||||
let mut rng = XorShiftRng::from_seed([0x5dbe6259, 0x8d313d76, 0x3237db17, 0xe5bc0654]);
|
||||
|
||||
let v: Vec<FrRepr> = (0..SAMPLES)
|
||||
.map(|_| Fr::rand(&mut rng).into_repr())
|
||||
.collect();
|
||||
|
||||
let mut count = 0;
|
||||
b.iter(|| {
|
||||
count = (count + 1) % SAMPLES;
|
||||
Fr::from_repr(v[count])
|
||||
});
|
||||
}
|
|
@ -0,0 +1,107 @@
|
|||
mod ec;
|
||||
mod fq;
|
||||
mod fq12;
|
||||
mod fq2;
|
||||
mod fr;
|
||||
|
||||
use rand::{Rand, SeedableRng, XorShiftRng};
|
||||
|
||||
use pairing::bls12_381::*;
|
||||
use pairing::{CurveAffine, Engine};
|
||||
|
||||
#[bench]
|
||||
fn bench_pairing_g1_preparation(b: &mut ::test::Bencher) {
|
||||
const SAMPLES: usize = 1000;
|
||||
|
||||
let mut rng = XorShiftRng::from_seed([0x5dbe6259, 0x8d313d76, 0x3237db17, 0xe5bc0654]);
|
||||
|
||||
let v: Vec<G1> = (0..SAMPLES).map(|_| G1::rand(&mut rng)).collect();
|
||||
|
||||
let mut count = 0;
|
||||
b.iter(|| {
|
||||
let tmp = G1Affine::from(v[count]).prepare();
|
||||
count = (count + 1) % SAMPLES;
|
||||
tmp
|
||||
});
|
||||
}
|
||||
|
||||
#[bench]
|
||||
fn bench_pairing_g2_preparation(b: &mut ::test::Bencher) {
|
||||
const SAMPLES: usize = 1000;
|
||||
|
||||
let mut rng = XorShiftRng::from_seed([0x5dbe6259, 0x8d313d76, 0x3237db17, 0xe5bc0654]);
|
||||
|
||||
let v: Vec<G2> = (0..SAMPLES).map(|_| G2::rand(&mut rng)).collect();
|
||||
|
||||
let mut count = 0;
|
||||
b.iter(|| {
|
||||
let tmp = G2Affine::from(v[count]).prepare();
|
||||
count = (count + 1) % SAMPLES;
|
||||
tmp
|
||||
});
|
||||
}
|
||||
|
||||
#[bench]
|
||||
fn bench_pairing_miller_loop(b: &mut ::test::Bencher) {
|
||||
const SAMPLES: usize = 1000;
|
||||
|
||||
let mut rng = XorShiftRng::from_seed([0x5dbe6259, 0x8d313d76, 0x3237db17, 0xe5bc0654]);
|
||||
|
||||
let v: Vec<(G1Prepared, G2Prepared)> = (0..SAMPLES)
|
||||
.map(|_| {
|
||||
(
|
||||
G1Affine::from(G1::rand(&mut rng)).prepare(),
|
||||
G2Affine::from(G2::rand(&mut rng)).prepare(),
|
||||
)
|
||||
})
|
||||
.collect();
|
||||
|
||||
let mut count = 0;
|
||||
b.iter(|| {
|
||||
let tmp = Bls12::miller_loop(&[(&v[count].0, &v[count].1)]);
|
||||
count = (count + 1) % SAMPLES;
|
||||
tmp
|
||||
});
|
||||
}
|
||||
|
||||
#[bench]
|
||||
fn bench_pairing_final_exponentiation(b: &mut ::test::Bencher) {
|
||||
const SAMPLES: usize = 1000;
|
||||
|
||||
let mut rng = XorShiftRng::from_seed([0x5dbe6259, 0x8d313d76, 0x3237db17, 0xe5bc0654]);
|
||||
|
||||
let v: Vec<Fq12> = (0..SAMPLES)
|
||||
.map(|_| {
|
||||
(
|
||||
G1Affine::from(G1::rand(&mut rng)).prepare(),
|
||||
G2Affine::from(G2::rand(&mut rng)).prepare(),
|
||||
)
|
||||
})
|
||||
.map(|(ref p, ref q)| Bls12::miller_loop(&[(p, q)]))
|
||||
.collect();
|
||||
|
||||
let mut count = 0;
|
||||
b.iter(|| {
|
||||
let tmp = Bls12::final_exponentiation(&v[count]);
|
||||
count = (count + 1) % SAMPLES;
|
||||
tmp
|
||||
});
|
||||
}
|
||||
|
||||
#[bench]
|
||||
fn bench_pairing_full(b: &mut ::test::Bencher) {
|
||||
const SAMPLES: usize = 1000;
|
||||
|
||||
let mut rng = XorShiftRng::from_seed([0x5dbe6259, 0x8d313d76, 0x3237db17, 0xe5bc0654]);
|
||||
|
||||
let v: Vec<(G1, G2)> = (0..SAMPLES)
|
||||
.map(|_| (G1::rand(&mut rng), G2::rand(&mut rng)))
|
||||
.collect();
|
||||
|
||||
let mut count = 0;
|
||||
b.iter(|| {
|
||||
let tmp = Bls12::pairing(v[count].0, v[count].1);
|
||||
count = (count + 1) % SAMPLES;
|
||||
tmp
|
||||
});
|
||||
}
|
|
@ -0,0 +1,7 @@
|
|||
#![feature(test)]
|
||||
|
||||
extern crate pairing;
|
||||
extern crate rand;
|
||||
extern crate test;
|
||||
|
||||
mod bls12_381;
|
|
@ -0,0 +1,71 @@
|
|||
# BLS12-381
|
||||
|
||||
This is an implementation of the BLS12-381 pairing-friendly elliptic curve construction.
|
||||
|
||||
## BLS12 Parameterization
|
||||
|
||||
BLS12 curves are parameterized by a value *x* such that the base field modulus *q* and subgroup *r* can be computed by:
|
||||
|
||||
* q = (x - 1)<sup>2</sup> ((x<sup>4</sup> - x<sup>2</sup> + 1) / 3) + x
|
||||
* r = (x<sup>4</sup> - x<sup>2</sup> + 1)
|
||||
|
||||
Given primes *q* and *r* parameterized as above, we can easily construct an elliptic curve over the prime field F<sub>*q*</sub> which contains a subgroup of order *r* such that *r* | (*q*<sup>12</sup> - 1), giving it an embedding degree of 12. Instantiating its sextic twist over an extension field F<sub>q<sup>2</sup></sub> gives rise to an efficient bilinear pairing function between elements of the order *r* subgroups of either curves, into an order *r* multiplicative subgroup of F<sub>q<sup>12</sup></sub>.
|
||||
|
||||
In zk-SNARK schemes, we require F<sub>r</sub> with large 2<sup>n</sup> roots of unity for performing efficient fast-fourier transforms. As such, guaranteeing that large 2<sup>n</sup> | (r - 1), or equivalently that *x* has a large 2<sup>n</sup> factor, gives rise to BLS12 curves suitable for zk-SNARKs.
|
||||
|
||||
Due to recent research, it is estimated by many that *q* should be approximately 384 bits to target 128-bit security. Conveniently, *r* is approximately 256 bits when *q* is approximately 384 bits, making BLS12 curves ideal for 128-bit security. It also makes them ideal for many zk-SNARK applications, as the scalar field can be used for keying material such as embedded curve constructions.
|
||||
|
||||
Many curves match our descriptions, but we require some extra properties for efficiency purposes:
|
||||
|
||||
* *q* should be smaller than 2<sup>383</sup>, and *r* should be smaller than 2<sup>255</sup>, so that the most significant bit is unset when using 64-bit or 32-bit limbs. This allows for cheap reductions.
|
||||
* F<sub>q<sup>12</sup></sub> is typically constructed using towers of extension fields. As a byproduct of [research](https://eprint.iacr.org/2011/465.pdf) for BLS curves of embedding degree 24, we can identify subfamilies of BLS12 curves (for our purposes, where x mod 72 = {16, 64}) that produce efficient extension field towers and twisting isomorphisms.
|
||||
* We desire *x* of small Hamming weight, to increase the performance of the pairing function.
|
||||
|
||||
## BLS12-381 Instantiation
|
||||
|
||||
The BLS12-381 construction is instantiated by `x = -0xd201000000010000`, which produces the largest `q` and smallest Hamming weight of `x` that meets the above requirements. This produces:
|
||||
|
||||
* q = `0x1a0111ea397fe69a4b1ba7b6434bacd764774b84f38512bf6730d2a0f6b0f6241eabfffeb153ffffb9feffffffffaaab` (381 bits)
|
||||
* r = `0x73eda753299d7d483339d80809a1d80553bda402fffe5bfeffffffff00000001` (255 bits)
|
||||
|
||||
Our extension field tower is constructed as follows:
|
||||
|
||||
1. F<sub>q<sup>2</sup></sub> is constructed as F<sub>q</sub>(u) / (u<sup>2</sup> - β) where β = -1.
|
||||
2. F<sub>q<sup>6</sup></sub> is constructed as F<sub>q<sup>2</sup></sub>(v) / (v<sup>3</sup> - ξ) where ξ = u + 1
|
||||
3. F<sub>q<sup>12</sup></sub> is constructed as F<sub>q<sup>6</sup></sub>(w) / (w<sup>2</sup> - γ) where γ = v
|
||||
|
||||
Now, we instantiate the elliptic curve E(F<sub>q</sub>) : y<sup>2</sup> = x<sup>3</sup> + 4, and the elliptic curve E'(F<sub>q<sup>2</sup></sub>) : y<sup>2</sup> = x<sup>3</sup> + 4(u + 1).
|
||||
|
||||
The group G<sub>1</sub> is the *r* order subgroup of E, which has cofactor (x - 1)<sup>2</sup> / 3. The group G<sub>2</sub> is the *r* order subgroup of E', which has cofactor (x<sup>8</sup> - 4x<sup>7</sup> + 5x<sup>6</sup> - 4x<sup>4</sup> + 6x<sup>3</sup> - 4x<sup>2</sup> - 4x + 13) / 9.
|
||||
|
||||
### Generators
|
||||
|
||||
The generators of G<sub>1</sub> and G<sub>2</sub> are computed by finding the lexicographically smallest valid `x`-coordinate, and its lexicographically smallest `y`-coordinate and scaling it by the cofactor such that the result is not the point at infinity.
|
||||
|
||||
#### G1
|
||||
|
||||
```
|
||||
x = 3685416753713387016781088315183077757961620795782546409894578378688607592378376318836054947676345821548104185464507
|
||||
y = 1339506544944476473020471379941921221584933875938349620426543736416511423956333506472724655353366534992391756441569
|
||||
```
|
||||
|
||||
#### G2
|
||||
|
||||
```
|
||||
x = 3059144344244213709971259814753781636986470325476647558659373206291635324768958432433509563104347017837885763365758*u + 352701069587466618187139116011060144890029952792775240219908644239793785735715026873347600343865175952761926303160
|
||||
y = 927553665492332455747201965776037880757740193453592970025027978793976877002675564980949289727957565575433344219582*u + 1985150602287291935568054521177171638300868978215655730859378665066344726373823718423869104263333984641494340347905
|
||||
```
|
||||
|
||||
### Serialization
|
||||
|
||||
* Fq elements are encoded in big-endian form. They occupy 48 bytes in this form.
|
||||
* Fq2 elements are encoded in big-endian form, meaning that the Fq element c0 + c1 * u is represented by the Fq element c1 followed by the Fq element c0. This means Fq2 elements occupy 96 bytes in this form.
|
||||
* The group G1 uses Fq elements for coordinates. The group G2 uses Fq2 elements for coordinates.
|
||||
* G1 and G2 elements can be encoded in uncompressed form (the x-coordinate followed by the y-coordinate) or in compressed form (just the x-coordinate). G1 elements occupy 96 bytes in uncompressed form, and 48 bytes in compressed form. G2 elements occupy 192 bytes in uncompressed form, and 96 bytes in compressed form.
|
||||
|
||||
The most-significant three bits of a G1 or G2 encoding should be masked away before the coordinate(s) are interpreted. These bits are used to unambiguously represent the underlying element:
|
||||
|
||||
* The most significant bit, when set, indicates that the point is in compressed form. Otherwise, the point is in uncompressed form.
|
||||
* The second-most significant bit indicates that the point is at infinity. If this bit is set, the remaining bits of the group element's encoding should be set to zero.
|
||||
* The third-most significant bit is set if (and only if) this point is in compressed form _and_ it is not the point at infinity _and_ its y-coordinate is the lexicographically largest of the two associated with the encoded x-coordinate.
|
||||
|
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
|
@ -0,0 +1,189 @@
|
|||
use super::fq::FROBENIUS_COEFF_FQ12_C1;
|
||||
use super::fq2::Fq2;
|
||||
use super::fq6::Fq6;
|
||||
use rand::{Rand, Rng};
|
||||
use Field;
|
||||
|
||||
/// An element of Fq12, represented by c0 + c1 * w.
|
||||
#[derive(Copy, Clone, Debug, Eq, PartialEq)]
|
||||
pub struct Fq12 {
|
||||
pub c0: Fq6,
|
||||
pub c1: Fq6,
|
||||
}
|
||||
|
||||
impl ::std::fmt::Display for Fq12 {
|
||||
fn fmt(&self, f: &mut ::std::fmt::Formatter) -> ::std::fmt::Result {
|
||||
write!(f, "Fq12({} + {} * w)", self.c0, self.c1)
|
||||
}
|
||||
}
|
||||
|
||||
impl Rand for Fq12 {
|
||||
fn rand<R: Rng>(rng: &mut R) -> Self {
|
||||
Fq12 {
|
||||
c0: rng.gen(),
|
||||
c1: rng.gen(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Fq12 {
|
||||
pub fn conjugate(&mut self) {
|
||||
self.c1.negate();
|
||||
}
|
||||
|
||||
pub fn mul_by_014(&mut self, c0: &Fq2, c1: &Fq2, c4: &Fq2) {
|
||||
let mut aa = self.c0;
|
||||
aa.mul_by_01(c0, c1);
|
||||
let mut bb = self.c1;
|
||||
bb.mul_by_1(c4);
|
||||
let mut o = *c1;
|
||||
o.add_assign(c4);
|
||||
self.c1.add_assign(&self.c0);
|
||||
self.c1.mul_by_01(c0, &o);
|
||||
self.c1.sub_assign(&aa);
|
||||
self.c1.sub_assign(&bb);
|
||||
self.c0 = bb;
|
||||
self.c0.mul_by_nonresidue();
|
||||
self.c0.add_assign(&aa);
|
||||
}
|
||||
}
|
||||
|
||||
impl Field for Fq12 {
|
||||
fn zero() -> Self {
|
||||
Fq12 {
|
||||
c0: Fq6::zero(),
|
||||
c1: Fq6::zero(),
|
||||
}
|
||||
}
|
||||
|
||||
fn one() -> Self {
|
||||
Fq12 {
|
||||
c0: Fq6::one(),
|
||||
c1: Fq6::zero(),
|
||||
}
|
||||
}
|
||||
|
||||
fn is_zero(&self) -> bool {
|
||||
self.c0.is_zero() && self.c1.is_zero()
|
||||
}
|
||||
|
||||
fn double(&mut self) {
|
||||
self.c0.double();
|
||||
self.c1.double();
|
||||
}
|
||||
|
||||
fn negate(&mut self) {
|
||||
self.c0.negate();
|
||||
self.c1.negate();
|
||||
}
|
||||
|
||||
fn add_assign(&mut self, other: &Self) {
|
||||
self.c0.add_assign(&other.c0);
|
||||
self.c1.add_assign(&other.c1);
|
||||
}
|
||||
|
||||
fn sub_assign(&mut self, other: &Self) {
|
||||
self.c0.sub_assign(&other.c0);
|
||||
self.c1.sub_assign(&other.c1);
|
||||
}
|
||||
|
||||
fn frobenius_map(&mut self, power: usize) {
|
||||
self.c0.frobenius_map(power);
|
||||
self.c1.frobenius_map(power);
|
||||
|
||||
self.c1.c0.mul_assign(&FROBENIUS_COEFF_FQ12_C1[power % 12]);
|
||||
self.c1.c1.mul_assign(&FROBENIUS_COEFF_FQ12_C1[power % 12]);
|
||||
self.c1.c2.mul_assign(&FROBENIUS_COEFF_FQ12_C1[power % 12]);
|
||||
}
|
||||
|
||||
fn square(&mut self) {
|
||||
let mut ab = self.c0;
|
||||
ab.mul_assign(&self.c1);
|
||||
let mut c0c1 = self.c0;
|
||||
c0c1.add_assign(&self.c1);
|
||||
let mut c0 = self.c1;
|
||||
c0.mul_by_nonresidue();
|
||||
c0.add_assign(&self.c0);
|
||||
c0.mul_assign(&c0c1);
|
||||
c0.sub_assign(&ab);
|
||||
self.c1 = ab;
|
||||
self.c1.add_assign(&ab);
|
||||
ab.mul_by_nonresidue();
|
||||
c0.sub_assign(&ab);
|
||||
self.c0 = c0;
|
||||
}
|
||||
|
||||
fn mul_assign(&mut self, other: &Self) {
|
||||
let mut aa = self.c0;
|
||||
aa.mul_assign(&other.c0);
|
||||
let mut bb = self.c1;
|
||||
bb.mul_assign(&other.c1);
|
||||
let mut o = other.c0;
|
||||
o.add_assign(&other.c1);
|
||||
self.c1.add_assign(&self.c0);
|
||||
self.c1.mul_assign(&o);
|
||||
self.c1.sub_assign(&aa);
|
||||
self.c1.sub_assign(&bb);
|
||||
self.c0 = bb;
|
||||
self.c0.mul_by_nonresidue();
|
||||
self.c0.add_assign(&aa);
|
||||
}
|
||||
|
||||
fn inverse(&self) -> Option<Self> {
|
||||
let mut c0s = self.c0;
|
||||
c0s.square();
|
||||
let mut c1s = self.c1;
|
||||
c1s.square();
|
||||
c1s.mul_by_nonresidue();
|
||||
c0s.sub_assign(&c1s);
|
||||
|
||||
c0s.inverse().map(|t| {
|
||||
let mut tmp = Fq12 { c0: t, c1: t };
|
||||
tmp.c0.mul_assign(&self.c0);
|
||||
tmp.c1.mul_assign(&self.c1);
|
||||
tmp.c1.negate();
|
||||
|
||||
tmp
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
use rand::{SeedableRng, XorShiftRng};
|
||||
|
||||
#[test]
|
||||
fn test_fq12_mul_by_014() {
|
||||
let mut rng = XorShiftRng::from_seed([0x5dbe6259, 0x8d313d76, 0x3237db17, 0xe5bc0654]);
|
||||
|
||||
for _ in 0..1000 {
|
||||
let c0 = Fq2::rand(&mut rng);
|
||||
let c1 = Fq2::rand(&mut rng);
|
||||
let c5 = Fq2::rand(&mut rng);
|
||||
let mut a = Fq12::rand(&mut rng);
|
||||
let mut b = a;
|
||||
|
||||
a.mul_by_014(&c0, &c1, &c5);
|
||||
b.mul_assign(&Fq12 {
|
||||
c0: Fq6 {
|
||||
c0: c0,
|
||||
c1: c1,
|
||||
c2: Fq2::zero(),
|
||||
},
|
||||
c1: Fq6 {
|
||||
c0: Fq2::zero(),
|
||||
c1: c5,
|
||||
c2: Fq2::zero(),
|
||||
},
|
||||
});
|
||||
|
||||
assert_eq!(a, b);
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn fq12_field_tests() {
|
||||
use PrimeField;
|
||||
|
||||
::tests::field::random_field_tests::<Fq12>();
|
||||
::tests::field::random_frobenius_tests::<Fq12, _>(super::fq::Fq::char(), 13);
|
||||
}
|
|
@ -0,0 +1,908 @@
|
|||
use super::fq::{FROBENIUS_COEFF_FQ2_C1, Fq, NEGATIVE_ONE};
|
||||
use rand::{Rand, Rng};
|
||||
use {Field, SqrtField};
|
||||
|
||||
use std::cmp::Ordering;
|
||||
|
||||
/// An element of Fq2, represented by c0 + c1 * u.
|
||||
#[derive(Copy, Clone, Debug, Eq, PartialEq)]
|
||||
pub struct Fq2 {
|
||||
pub c0: Fq,
|
||||
pub c1: Fq,
|
||||
}
|
||||
|
||||
impl ::std::fmt::Display for Fq2 {
|
||||
fn fmt(&self, f: &mut ::std::fmt::Formatter) -> ::std::fmt::Result {
|
||||
write!(f, "Fq2({} + {} * u)", self.c0, self.c1)
|
||||
}
|
||||
}
|
||||
|
||||
/// `Fq2` elements are ordered lexicographically.
|
||||
impl Ord for Fq2 {
|
||||
#[inline(always)]
|
||||
fn cmp(&self, other: &Fq2) -> Ordering {
|
||||
match self.c1.cmp(&other.c1) {
|
||||
Ordering::Greater => Ordering::Greater,
|
||||
Ordering::Less => Ordering::Less,
|
||||
Ordering::Equal => self.c0.cmp(&other.c0),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl PartialOrd for Fq2 {
|
||||
#[inline(always)]
|
||||
fn partial_cmp(&self, other: &Fq2) -> Option<Ordering> {
|
||||
Some(self.cmp(other))
|
||||
}
|
||||
}
|
||||
|
||||
impl Fq2 {
|
||||
/// Multiply this element by the cubic and quadratic nonresidue 1 + u.
|
||||
pub fn mul_by_nonresidue(&mut self) {
|
||||
let t0 = self.c0;
|
||||
self.c0.sub_assign(&self.c1);
|
||||
self.c1.add_assign(&t0);
|
||||
}
|
||||
|
||||
/// Norm of Fq2 as extension field in i over Fq
|
||||
pub fn norm(&self) -> Fq {
|
||||
let mut t0 = self.c0;
|
||||
let mut t1 = self.c1;
|
||||
t0.square();
|
||||
t1.square();
|
||||
t1.add_assign(&t0);
|
||||
|
||||
t1
|
||||
}
|
||||
}
|
||||
|
||||
impl Rand for Fq2 {
|
||||
fn rand<R: Rng>(rng: &mut R) -> Self {
|
||||
Fq2 {
|
||||
c0: rng.gen(),
|
||||
c1: rng.gen(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Field for Fq2 {
|
||||
fn zero() -> Self {
|
||||
Fq2 {
|
||||
c0: Fq::zero(),
|
||||
c1: Fq::zero(),
|
||||
}
|
||||
}
|
||||
|
||||
fn one() -> Self {
|
||||
Fq2 {
|
||||
c0: Fq::one(),
|
||||
c1: Fq::zero(),
|
||||
}
|
||||
}
|
||||
|
||||
fn is_zero(&self) -> bool {
|
||||
self.c0.is_zero() && self.c1.is_zero()
|
||||
}
|
||||
|
||||
fn square(&mut self) {
|
||||
let mut ab = self.c0;
|
||||
ab.mul_assign(&self.c1);
|
||||
let mut c0c1 = self.c0;
|
||||
c0c1.add_assign(&self.c1);
|
||||
let mut c0 = self.c1;
|
||||
c0.negate();
|
||||
c0.add_assign(&self.c0);
|
||||
c0.mul_assign(&c0c1);
|
||||
c0.sub_assign(&ab);
|
||||
self.c1 = ab;
|
||||
self.c1.add_assign(&ab);
|
||||
c0.add_assign(&ab);
|
||||
self.c0 = c0;
|
||||
}
|
||||
|
||||
fn double(&mut self) {
|
||||
self.c0.double();
|
||||
self.c1.double();
|
||||
}
|
||||
|
||||
fn negate(&mut self) {
|
||||
self.c0.negate();
|
||||
self.c1.negate();
|
||||
}
|
||||
|
||||
fn add_assign(&mut self, other: &Self) {
|
||||
self.c0.add_assign(&other.c0);
|
||||
self.c1.add_assign(&other.c1);
|
||||
}
|
||||
|
||||
fn sub_assign(&mut self, other: &Self) {
|
||||
self.c0.sub_assign(&other.c0);
|
||||
self.c1.sub_assign(&other.c1);
|
||||
}
|
||||
|
||||
fn mul_assign(&mut self, other: &Self) {
|
||||
let mut aa = self.c0;
|
||||
aa.mul_assign(&other.c0);
|
||||
let mut bb = self.c1;
|
||||
bb.mul_assign(&other.c1);
|
||||
let mut o = other.c0;
|
||||
o.add_assign(&other.c1);
|
||||
self.c1.add_assign(&self.c0);
|
||||
self.c1.mul_assign(&o);
|
||||
self.c1.sub_assign(&aa);
|
||||
self.c1.sub_assign(&bb);
|
||||
self.c0 = aa;
|
||||
self.c0.sub_assign(&bb);
|
||||
}
|
||||
|
||||
fn inverse(&self) -> Option<Self> {
|
||||
let mut t1 = self.c1;
|
||||
t1.square();
|
||||
let mut t0 = self.c0;
|
||||
t0.square();
|
||||
t0.add_assign(&t1);
|
||||
t0.inverse().map(|t| {
|
||||
let mut tmp = Fq2 {
|
||||
c0: self.c0,
|
||||
c1: self.c1,
|
||||
};
|
||||
tmp.c0.mul_assign(&t);
|
||||
tmp.c1.mul_assign(&t);
|
||||
tmp.c1.negate();
|
||||
|
||||
tmp
|
||||
})
|
||||
}
|
||||
|
||||
fn frobenius_map(&mut self, power: usize) {
|
||||
self.c1.mul_assign(&FROBENIUS_COEFF_FQ2_C1[power % 2]);
|
||||
}
|
||||
}
|
||||
|
||||
impl SqrtField for Fq2 {
|
||||
fn legendre(&self) -> ::LegendreSymbol {
|
||||
self.norm().legendre()
|
||||
}
|
||||
|
||||
fn sqrt(&self) -> Option<Self> {
|
||||
// Algorithm 9, https://eprint.iacr.org/2012/685.pdf
|
||||
|
||||
if self.is_zero() {
|
||||
Some(Self::zero())
|
||||
} else {
|
||||
// a1 = self^((q - 3) / 4)
|
||||
let mut a1 = self.pow([
|
||||
0xee7fbfffffffeaaa,
|
||||
0x7aaffffac54ffff,
|
||||
0xd9cc34a83dac3d89,
|
||||
0xd91dd2e13ce144af,
|
||||
0x92c6e9ed90d2eb35,
|
||||
0x680447a8e5ff9a6,
|
||||
]);
|
||||
let mut alpha = a1;
|
||||
alpha.square();
|
||||
alpha.mul_assign(self);
|
||||
let mut a0 = alpha;
|
||||
a0.frobenius_map(1);
|
||||
a0.mul_assign(&alpha);
|
||||
|
||||
let neg1 = Fq2 {
|
||||
c0: NEGATIVE_ONE,
|
||||
c1: Fq::zero(),
|
||||
};
|
||||
|
||||
if a0 == neg1 {
|
||||
None
|
||||
} else {
|
||||
a1.mul_assign(self);
|
||||
|
||||
if alpha == neg1 {
|
||||
a1.mul_assign(&Fq2 {
|
||||
c0: Fq::zero(),
|
||||
c1: Fq::one(),
|
||||
});
|
||||
} else {
|
||||
alpha.add_assign(&Fq2::one());
|
||||
// alpha = alpha^((q - 1) / 2)
|
||||
alpha = alpha.pow([
|
||||
0xdcff7fffffffd555,
|
||||
0xf55ffff58a9ffff,
|
||||
0xb39869507b587b12,
|
||||
0xb23ba5c279c2895f,
|
||||
0x258dd3db21a5d66b,
|
||||
0xd0088f51cbff34d,
|
||||
]);
|
||||
a1.mul_assign(&alpha);
|
||||
}
|
||||
|
||||
Some(a1)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_fq2_ordering() {
|
||||
let mut a = Fq2 {
|
||||
c0: Fq::zero(),
|
||||
c1: Fq::zero(),
|
||||
};
|
||||
|
||||
let mut b = a.clone();
|
||||
|
||||
assert!(a.cmp(&b) == Ordering::Equal);
|
||||
b.c0.add_assign(&Fq::one());
|
||||
assert!(a.cmp(&b) == Ordering::Less);
|
||||
a.c0.add_assign(&Fq::one());
|
||||
assert!(a.cmp(&b) == Ordering::Equal);
|
||||
b.c1.add_assign(&Fq::one());
|
||||
assert!(a.cmp(&b) == Ordering::Less);
|
||||
a.c0.add_assign(&Fq::one());
|
||||
assert!(a.cmp(&b) == Ordering::Less);
|
||||
a.c1.add_assign(&Fq::one());
|
||||
assert!(a.cmp(&b) == Ordering::Greater);
|
||||
b.c0.add_assign(&Fq::one());
|
||||
assert!(a.cmp(&b) == Ordering::Equal);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_fq2_basics() {
|
||||
assert_eq!(
|
||||
Fq2 {
|
||||
c0: Fq::zero(),
|
||||
c1: Fq::zero(),
|
||||
},
|
||||
Fq2::zero()
|
||||
);
|
||||
assert_eq!(
|
||||
Fq2 {
|
||||
c0: Fq::one(),
|
||||
c1: Fq::zero(),
|
||||
},
|
||||
Fq2::one()
|
||||
);
|
||||
assert!(Fq2::zero().is_zero());
|
||||
assert!(!Fq2::one().is_zero());
|
||||
assert!(!Fq2 {
|
||||
c0: Fq::zero(),
|
||||
c1: Fq::one(),
|
||||
}.is_zero());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_fq2_squaring() {
|
||||
use super::fq::FqRepr;
|
||||
use PrimeField;
|
||||
|
||||
let mut a = Fq2 {
|
||||
c0: Fq::one(),
|
||||
c1: Fq::one(),
|
||||
}; // u + 1
|
||||
a.square();
|
||||
assert_eq!(
|
||||
a,
|
||||
Fq2 {
|
||||
c0: Fq::zero(),
|
||||
c1: Fq::from_repr(FqRepr::from(2)).unwrap(),
|
||||
}
|
||||
); // 2u
|
||||
|
||||
let mut a = Fq2 {
|
||||
c0: Fq::zero(),
|
||||
c1: Fq::one(),
|
||||
}; // u
|
||||
a.square();
|
||||
assert_eq!(a, {
|
||||
let mut neg1 = Fq::one();
|
||||
neg1.negate();
|
||||
Fq2 {
|
||||
c0: neg1,
|
||||
c1: Fq::zero(),
|
||||
}
|
||||
}); // -1
|
||||
|
||||
let mut a = Fq2 {
|
||||
c0: Fq::from_repr(FqRepr([
|
||||
0x9c2c6309bbf8b598,
|
||||
0x4eef5c946536f602,
|
||||
0x90e34aab6fb6a6bd,
|
||||
0xf7f295a94e58ae7c,
|
||||
0x41b76dcc1c3fbe5e,
|
||||
0x7080c5fa1d8e042,
|
||||
])).unwrap(),
|
||||
c1: Fq::from_repr(FqRepr([
|
||||
0x38f473b3c870a4ab,
|
||||
0x6ad3291177c8c7e5,
|
||||
0xdac5a4c911a4353e,
|
||||
0xbfb99020604137a0,
|
||||
0xfc58a7b7be815407,
|
||||
0x10d1615e75250a21,
|
||||
])).unwrap(),
|
||||
};
|
||||
a.square();
|
||||
assert_eq!(
|
||||
a,
|
||||
Fq2 {
|
||||
c0: Fq::from_repr(FqRepr([
|
||||
0xf262c28c538bcf68,
|
||||
0xb9f2a66eae1073ba,
|
||||
0xdc46ab8fad67ae0,
|
||||
0xcb674157618da176,
|
||||
0x4cf17b5893c3d327,
|
||||
0x7eac81369c43361
|
||||
])).unwrap(),
|
||||
c1: Fq::from_repr(FqRepr([
|
||||
0xc1579cf58e980cf8,
|
||||
0xa23eb7e12dd54d98,
|
||||
0xe75138bce4cec7aa,
|
||||
0x38d0d7275a9689e1,
|
||||
0x739c983042779a65,
|
||||
0x1542a61c8a8db994
|
||||
])).unwrap(),
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_fq2_mul() {
|
||||
use super::fq::FqRepr;
|
||||
use PrimeField;
|
||||
|
||||
let mut a = Fq2 {
|
||||
c0: Fq::from_repr(FqRepr([
|
||||
0x85c9f989e1461f03,
|
||||
0xa2e33c333449a1d6,
|
||||
0x41e461154a7354a3,
|
||||
0x9ee53e7e84d7532e,
|
||||
0x1c202d8ed97afb45,
|
||||
0x51d3f9253e2516f,
|
||||
])).unwrap(),
|
||||
c1: Fq::from_repr(FqRepr([
|
||||
0xa7348a8b511aedcf,
|
||||
0x143c215d8176b319,
|
||||
0x4cc48081c09b8903,
|
||||
0x9533e4a9a5158be,
|
||||
0x7a5e1ecb676d65f9,
|
||||
0x180c3ee46656b008,
|
||||
])).unwrap(),
|
||||
};
|
||||
a.mul_assign(&Fq2 {
|
||||
c0: Fq::from_repr(FqRepr([
|
||||
0xe21f9169805f537e,
|
||||
0xfc87e62e179c285d,
|
||||
0x27ece175be07a531,
|
||||
0xcd460f9f0c23e430,
|
||||
0x6c9110292bfa409,
|
||||
0x2c93a72eb8af83e,
|
||||
])).unwrap(),
|
||||
c1: Fq::from_repr(FqRepr([
|
||||
0x4b1c3f936d8992d4,
|
||||
0x1d2a72916dba4c8a,
|
||||
0x8871c508658d1e5f,
|
||||
0x57a06d3135a752ae,
|
||||
0x634cd3c6c565096d,
|
||||
0x19e17334d4e93558,
|
||||
])).unwrap(),
|
||||
});
|
||||
assert_eq!(
|
||||
a,
|
||||
Fq2 {
|
||||
c0: Fq::from_repr(FqRepr([
|
||||
0x95b5127e6360c7e4,
|
||||
0xde29c31a19a6937e,
|
||||
0xf61a96dacf5a39bc,
|
||||
0x5511fe4d84ee5f78,
|
||||
0x5310a202d92f9963,
|
||||
0x1751afbe166e5399
|
||||
])).unwrap(),
|
||||
c1: Fq::from_repr(FqRepr([
|
||||
0x84af0e1bd630117a,
|
||||
0x6c63cd4da2c2aa7,
|
||||
0x5ba6e5430e883d40,
|
||||
0xc975106579c275ee,
|
||||
0x33a9ac82ce4c5083,
|
||||
0x1ef1a36c201589d
|
||||
])).unwrap(),
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_fq2_inverse() {
|
||||
use super::fq::FqRepr;
|
||||
use PrimeField;
|
||||
|
||||
assert!(Fq2::zero().inverse().is_none());
|
||||
|
||||
let a = Fq2 {
|
||||
c0: Fq::from_repr(FqRepr([
|
||||
0x85c9f989e1461f03,
|
||||
0xa2e33c333449a1d6,
|
||||
0x41e461154a7354a3,
|
||||
0x9ee53e7e84d7532e,
|
||||
0x1c202d8ed97afb45,
|
||||
0x51d3f9253e2516f,
|
||||
])).unwrap(),
|
||||
c1: Fq::from_repr(FqRepr([
|
||||
0xa7348a8b511aedcf,
|
||||
0x143c215d8176b319,
|
||||
0x4cc48081c09b8903,
|
||||
0x9533e4a9a5158be,
|
||||
0x7a5e1ecb676d65f9,
|
||||
0x180c3ee46656b008,
|
||||
])).unwrap(),
|
||||
};
|
||||
let a = a.inverse().unwrap();
|
||||
assert_eq!(
|
||||
a,
|
||||
Fq2 {
|
||||
c0: Fq::from_repr(FqRepr([
|
||||
0x70300f9bcb9e594,
|
||||
0xe5ecda5fdafddbb2,
|
||||
0x64bef617d2915a8f,
|
||||
0xdfba703293941c30,
|
||||
0xa6c3d8f9586f2636,
|
||||
0x1351ef01941b70c4
|
||||
])).unwrap(),
|
||||
c1: Fq::from_repr(FqRepr([
|
||||
0x8c39fd76a8312cb4,
|
||||
0x15d7b6b95defbff0,
|
||||
0x947143f89faedee9,
|
||||
0xcbf651a0f367afb2,
|
||||
0xdf4e54f0d3ef15a6,
|
||||
0x103bdf241afb0019
|
||||
])).unwrap(),
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_fq2_addition() {
|
||||
use super::fq::FqRepr;
|
||||
use PrimeField;
|
||||
|
||||
let mut a = Fq2 {
|
||||
c0: Fq::from_repr(FqRepr([
|
||||
0x2d0078036923ffc7,
|
||||
0x11e59ea221a3b6d2,
|
||||
0x8b1a52e0a90f59ed,
|
||||
0xb966ce3bc2108b13,
|
||||
0xccc649c4b9532bf3,
|
||||
0xf8d295b2ded9dc,
|
||||
])).unwrap(),
|
||||
c1: Fq::from_repr(FqRepr([
|
||||
0x977df6efcdaee0db,
|
||||
0x946ae52d684fa7ed,
|
||||
0xbe203411c66fb3a5,
|
||||
0xb3f8afc0ee248cad,
|
||||
0x4e464dea5bcfd41e,
|
||||
0x12d1137b8a6a837,
|
||||
])).unwrap(),
|
||||
};
|
||||
a.add_assign(&Fq2 {
|
||||
c0: Fq::from_repr(FqRepr([
|
||||
0x619a02d78dc70ef2,
|
||||
0xb93adfc9119e33e8,
|
||||
0x4bf0b99a9f0dca12,
|
||||
0x3b88899a42a6318f,
|
||||
0x986a4a62fa82a49d,
|
||||
0x13ce433fa26027f5,
|
||||
])).unwrap(),
|
||||
c1: Fq::from_repr(FqRepr([
|
||||
0x66323bf80b58b9b9,
|
||||
0xa1379b6facf6e596,
|
||||
0x402aef1fb797e32f,
|
||||
0x2236f55246d0d44d,
|
||||
0x4c8c1800eb104566,
|
||||
0x11d6e20e986c2085,
|
||||
])).unwrap(),
|
||||
});
|
||||
assert_eq!(
|
||||
a,
|
||||
Fq2 {
|
||||
c0: Fq::from_repr(FqRepr([
|
||||
0x8e9a7adaf6eb0eb9,
|
||||
0xcb207e6b3341eaba,
|
||||
0xd70b0c7b481d23ff,
|
||||
0xf4ef57d604b6bca2,
|
||||
0x65309427b3d5d090,
|
||||
0x14c715d5553f01d2
|
||||
])).unwrap(),
|
||||
c1: Fq::from_repr(FqRepr([
|
||||
0xfdb032e7d9079a94,
|
||||
0x35a2809d15468d83,
|
||||
0xfe4b23317e0796d5,
|
||||
0xd62fa51334f560fa,
|
||||
0x9ad265eb46e01984,
|
||||
0x1303f3465112c8bc
|
||||
])).unwrap(),
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_fq2_subtraction() {
|
||||
use super::fq::FqRepr;
|
||||
use PrimeField;
|
||||
|
||||
let mut a = Fq2 {
|
||||
c0: Fq::from_repr(FqRepr([
|
||||
0x2d0078036923ffc7,
|
||||
0x11e59ea221a3b6d2,
|
||||
0x8b1a52e0a90f59ed,
|
||||
0xb966ce3bc2108b13,
|
||||
0xccc649c4b9532bf3,
|
||||
0xf8d295b2ded9dc,
|
||||
])).unwrap(),
|
||||
c1: Fq::from_repr(FqRepr([
|
||||
0x977df6efcdaee0db,
|
||||
0x946ae52d684fa7ed,
|
||||
0xbe203411c66fb3a5,
|
||||
0xb3f8afc0ee248cad,
|
||||
0x4e464dea5bcfd41e,
|
||||
0x12d1137b8a6a837,
|
||||
])).unwrap(),
|
||||
};
|
||||
a.sub_assign(&Fq2 {
|
||||
c0: Fq::from_repr(FqRepr([
|
||||
0x619a02d78dc70ef2,
|
||||
0xb93adfc9119e33e8,
|
||||
0x4bf0b99a9f0dca12,
|
||||
0x3b88899a42a6318f,
|
||||
0x986a4a62fa82a49d,
|
||||
0x13ce433fa26027f5,
|
||||
])).unwrap(),
|
||||
c1: Fq::from_repr(FqRepr([
|
||||
0x66323bf80b58b9b9,
|
||||
0xa1379b6facf6e596,
|
||||
0x402aef1fb797e32f,
|
||||
0x2236f55246d0d44d,
|
||||
0x4c8c1800eb104566,
|
||||
0x11d6e20e986c2085,
|
||||
])).unwrap(),
|
||||
});
|
||||
assert_eq!(
|
||||
a,
|
||||
Fq2 {
|
||||
c0: Fq::from_repr(FqRepr([
|
||||
0x8565752bdb5c9b80,
|
||||
0x7756bed7c15982e9,
|
||||
0xa65a6be700b285fe,
|
||||
0xe255902672ef6c43,
|
||||
0x7f77a718021c342d,
|
||||
0x72ba14049fe9881
|
||||
])).unwrap(),
|
||||
c1: Fq::from_repr(FqRepr([
|
||||
0xeb4abaf7c255d1cd,
|
||||
0x11df49bc6cacc256,
|
||||
0xe52617930588c69a,
|
||||
0xf63905f39ad8cb1f,
|
||||
0x4cd5dd9fb40b3b8f,
|
||||
0x957411359ba6e4c
|
||||
])).unwrap(),
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_fq2_negation() {
|
||||
use super::fq::FqRepr;
|
||||
use PrimeField;
|
||||
|
||||
let mut a = Fq2 {
|
||||
c0: Fq::from_repr(FqRepr([
|
||||
0x2d0078036923ffc7,
|
||||
0x11e59ea221a3b6d2,
|
||||
0x8b1a52e0a90f59ed,
|
||||
0xb966ce3bc2108b13,
|
||||
0xccc649c4b9532bf3,
|
||||
0xf8d295b2ded9dc,
|
||||
])).unwrap(),
|
||||
c1: Fq::from_repr(FqRepr([
|
||||
0x977df6efcdaee0db,
|
||||
0x946ae52d684fa7ed,
|
||||
0xbe203411c66fb3a5,
|
||||
0xb3f8afc0ee248cad,
|
||||
0x4e464dea5bcfd41e,
|
||||
0x12d1137b8a6a837,
|
||||
])).unwrap(),
|
||||
};
|
||||
a.negate();
|
||||
assert_eq!(
|
||||
a,
|
||||
Fq2 {
|
||||
c0: Fq::from_repr(FqRepr([
|
||||
0x8cfe87fc96dbaae4,
|
||||
0xcc6615c8fb0492d,
|
||||
0xdc167fc04da19c37,
|
||||
0xab107d49317487ab,
|
||||
0x7e555df189f880e3,
|
||||
0x19083f5486a10cbd
|
||||
])).unwrap(),
|
||||
c1: Fq::from_repr(FqRepr([
|
||||
0x228109103250c9d0,
|
||||
0x8a411ad149045812,
|
||||
0xa9109e8f3041427e,
|
||||
0xb07e9bc405608611,
|
||||
0xfcd559cbe77bd8b8,
|
||||
0x18d400b280d93e62
|
||||
])).unwrap(),
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_fq2_doubling() {
|
||||
use super::fq::FqRepr;
|
||||
use PrimeField;
|
||||
|
||||
let mut a = Fq2 {
|
||||
c0: Fq::from_repr(FqRepr([
|
||||
0x2d0078036923ffc7,
|
||||
0x11e59ea221a3b6d2,
|
||||
0x8b1a52e0a90f59ed,
|
||||
0xb966ce3bc2108b13,
|
||||
0xccc649c4b9532bf3,
|
||||
0xf8d295b2ded9dc,
|
||||
])).unwrap(),
|
||||
c1: Fq::from_repr(FqRepr([
|
||||
0x977df6efcdaee0db,
|
||||
0x946ae52d684fa7ed,
|
||||
0xbe203411c66fb3a5,
|
||||
0xb3f8afc0ee248cad,
|
||||
0x4e464dea5bcfd41e,
|
||||
0x12d1137b8a6a837,
|
||||
])).unwrap(),
|
||||
};
|
||||
a.double();
|
||||
assert_eq!(
|
||||
a,
|
||||
Fq2 {
|
||||
c0: Fq::from_repr(FqRepr([
|
||||
0x5a00f006d247ff8e,
|
||||
0x23cb3d4443476da4,
|
||||
0x1634a5c1521eb3da,
|
||||
0x72cd9c7784211627,
|
||||
0x998c938972a657e7,
|
||||
0x1f1a52b65bdb3b9
|
||||
])).unwrap(),
|
||||
c1: Fq::from_repr(FqRepr([
|
||||
0x2efbeddf9b5dc1b6,
|
||||
0x28d5ca5ad09f4fdb,
|
||||
0x7c4068238cdf674b,
|
||||
0x67f15f81dc49195b,
|
||||
0x9c8c9bd4b79fa83d,
|
||||
0x25a226f714d506e
|
||||
])).unwrap(),
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_fq2_frobenius_map() {
|
||||
use super::fq::FqRepr;
|
||||
use PrimeField;
|
||||
|
||||
let mut a = Fq2 {
|
||||
c0: Fq::from_repr(FqRepr([
|
||||
0x2d0078036923ffc7,
|
||||
0x11e59ea221a3b6d2,
|
||||
0x8b1a52e0a90f59ed,
|
||||
0xb966ce3bc2108b13,
|
||||
0xccc649c4b9532bf3,
|
||||
0xf8d295b2ded9dc,
|
||||
])).unwrap(),
|
||||
c1: Fq::from_repr(FqRepr([
|
||||
0x977df6efcdaee0db,
|
||||
0x946ae52d684fa7ed,
|
||||
0xbe203411c66fb3a5,
|
||||
0xb3f8afc0ee248cad,
|
||||
0x4e464dea5bcfd41e,
|
||||
0x12d1137b8a6a837,
|
||||
])).unwrap(),
|
||||
};
|
||||
a.frobenius_map(0);
|
||||
assert_eq!(
|
||||
a,
|
||||
Fq2 {
|
||||
c0: Fq::from_repr(FqRepr([
|
||||
0x2d0078036923ffc7,
|
||||
0x11e59ea221a3b6d2,
|
||||
0x8b1a52e0a90f59ed,
|
||||
0xb966ce3bc2108b13,
|
||||
0xccc649c4b9532bf3,
|
||||
0xf8d295b2ded9dc
|
||||
])).unwrap(),
|
||||
c1: Fq::from_repr(FqRepr([
|
||||
0x977df6efcdaee0db,
|
||||
0x946ae52d684fa7ed,
|
||||
0xbe203411c66fb3a5,
|
||||
0xb3f8afc0ee248cad,
|
||||
0x4e464dea5bcfd41e,
|
||||
0x12d1137b8a6a837
|
||||
])).unwrap(),
|
||||
}
|
||||
);
|
||||
a.frobenius_map(1);
|
||||
assert_eq!(
|
||||
a,
|
||||
Fq2 {
|
||||
c0: Fq::from_repr(FqRepr([
|
||||
0x2d0078036923ffc7,
|
||||
0x11e59ea221a3b6d2,
|
||||
0x8b1a52e0a90f59ed,
|
||||
0xb966ce3bc2108b13,
|
||||
0xccc649c4b9532bf3,
|
||||
0xf8d295b2ded9dc
|
||||
])).unwrap(),
|
||||
c1: Fq::from_repr(FqRepr([
|
||||
0x228109103250c9d0,
|
||||
0x8a411ad149045812,
|
||||
0xa9109e8f3041427e,
|
||||
0xb07e9bc405608611,
|
||||
0xfcd559cbe77bd8b8,
|
||||
0x18d400b280d93e62
|
||||
])).unwrap(),
|
||||
}
|
||||
);
|
||||
a.frobenius_map(1);
|
||||
assert_eq!(
|
||||
a,
|
||||
Fq2 {
|
||||
c0: Fq::from_repr(FqRepr([
|
||||
0x2d0078036923ffc7,
|
||||
0x11e59ea221a3b6d2,
|
||||
0x8b1a52e0a90f59ed,
|
||||
0xb966ce3bc2108b13,
|
||||
0xccc649c4b9532bf3,
|
||||
0xf8d295b2ded9dc
|
||||
])).unwrap(),
|
||||
c1: Fq::from_repr(FqRepr([
|
||||
0x977df6efcdaee0db,
|
||||
0x946ae52d684fa7ed,
|
||||
0xbe203411c66fb3a5,
|
||||
0xb3f8afc0ee248cad,
|
||||
0x4e464dea5bcfd41e,
|
||||
0x12d1137b8a6a837
|
||||
])).unwrap(),
|
||||
}
|
||||
);
|
||||
a.frobenius_map(2);
|
||||
assert_eq!(
|
||||
a,
|
||||
Fq2 {
|
||||
c0: Fq::from_repr(FqRepr([
|
||||
0x2d0078036923ffc7,
|
||||
0x11e59ea221a3b6d2,
|
||||
0x8b1a52e0a90f59ed,
|
||||
0xb966ce3bc2108b13,
|
||||
0xccc649c4b9532bf3,
|
||||
0xf8d295b2ded9dc
|
||||
])).unwrap(),
|
||||
c1: Fq::from_repr(FqRepr([
|
||||
0x977df6efcdaee0db,
|
||||
0x946ae52d684fa7ed,
|
||||
0xbe203411c66fb3a5,
|
||||
0xb3f8afc0ee248cad,
|
||||
0x4e464dea5bcfd41e,
|
||||
0x12d1137b8a6a837
|
||||
])).unwrap(),
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_fq2_sqrt() {
|
||||
use super::fq::FqRepr;
|
||||
use PrimeField;
|
||||
|
||||
assert_eq!(
|
||||
Fq2 {
|
||||
c0: Fq::from_repr(FqRepr([
|
||||
0x476b4c309720e227,
|
||||
0x34c2d04faffdab6,
|
||||
0xa57e6fc1bab51fd9,
|
||||
0xdb4a116b5bf74aa1,
|
||||
0x1e58b2159dfe10e2,
|
||||
0x7ca7da1f13606ac
|
||||
])).unwrap(),
|
||||
c1: Fq::from_repr(FqRepr([
|
||||
0xfa8de88b7516d2c3,
|
||||
0x371a75ed14f41629,
|
||||
0x4cec2dca577a3eb6,
|
||||
0x212611bca4e99121,
|
||||
0x8ee5394d77afb3d,
|
||||
0xec92336650e49d5
|
||||
])).unwrap(),
|
||||
}.sqrt()
|
||||
.unwrap(),
|
||||
Fq2 {
|
||||
c0: Fq::from_repr(FqRepr([
|
||||
0x40b299b2704258c5,
|
||||
0x6ef7de92e8c68b63,
|
||||
0x6d2ddbe552203e82,
|
||||
0x8d7f1f723d02c1d3,
|
||||
0x881b3e01b611c070,
|
||||
0x10f6963bbad2ebc5
|
||||
])).unwrap(),
|
||||
c1: Fq::from_repr(FqRepr([
|
||||
0xc099534fc209e752,
|
||||
0x7670594665676447,
|
||||
0x28a20faed211efe7,
|
||||
0x6b852aeaf2afcb1b,
|
||||
0xa4c93b08105d71a9,
|
||||
0x8d7cfff94216330
|
||||
])).unwrap(),
|
||||
}
|
||||
);
|
||||
|
||||
assert_eq!(
|
||||
Fq2 {
|
||||
c0: Fq::from_repr(FqRepr([
|
||||
0xb9f78429d1517a6b,
|
||||
0x1eabfffeb153ffff,
|
||||
0x6730d2a0f6b0f624,
|
||||
0x64774b84f38512bf,
|
||||
0x4b1ba7b6434bacd7,
|
||||
0x1a0111ea397fe69a
|
||||
])).unwrap(),
|
||||
c1: Fq::zero(),
|
||||
}.sqrt()
|
||||
.unwrap(),
|
||||
Fq2 {
|
||||
c0: Fq::zero(),
|
||||
c1: Fq::from_repr(FqRepr([
|
||||
0xb9fefffffd4357a3,
|
||||
0x1eabfffeb153ffff,
|
||||
0x6730d2a0f6b0f624,
|
||||
0x64774b84f38512bf,
|
||||
0x4b1ba7b6434bacd7,
|
||||
0x1a0111ea397fe69a
|
||||
])).unwrap(),
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_fq2_legendre() {
|
||||
use LegendreSymbol::*;
|
||||
|
||||
assert_eq!(Zero, Fq2::zero().legendre());
|
||||
// i^2 = -1
|
||||
let mut m1 = Fq2::one();
|
||||
m1.negate();
|
||||
assert_eq!(QuadraticResidue, m1.legendre());
|
||||
m1.mul_by_nonresidue();
|
||||
assert_eq!(QuadraticNonResidue, m1.legendre());
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
use rand::{SeedableRng, XorShiftRng};
|
||||
|
||||
#[test]
|
||||
fn test_fq2_mul_nonresidue() {
|
||||
let mut rng = XorShiftRng::from_seed([0x5dbe6259, 0x8d313d76, 0x3237db17, 0xe5bc0654]);
|
||||
|
||||
let nqr = Fq2 {
|
||||
c0: Fq::one(),
|
||||
c1: Fq::one(),
|
||||
};
|
||||
|
||||
for _ in 0..1000 {
|
||||
let mut a = Fq2::rand(&mut rng);
|
||||
let mut b = a;
|
||||
a.mul_by_nonresidue();
|
||||
b.mul_assign(&nqr);
|
||||
|
||||
assert_eq!(a, b);
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn fq2_field_tests() {
|
||||
use PrimeField;
|
||||
|
||||
::tests::field::random_field_tests::<Fq2>();
|
||||
::tests::field::random_sqrt_tests::<Fq2>();
|
||||
::tests::field::random_frobenius_tests::<Fq2, _>(super::fq::Fq::char(), 13);
|
||||
}
|
|
@ -0,0 +1,374 @@
|
|||
use super::fq::{FROBENIUS_COEFF_FQ6_C1, FROBENIUS_COEFF_FQ6_C2};
|
||||
use super::fq2::Fq2;
|
||||
use rand::{Rand, Rng};
|
||||
use Field;
|
||||
|
||||
/// An element of Fq6, represented by c0 + c1 * v + c2 * v^(2).
|
||||
#[derive(Copy, Clone, Debug, Eq, PartialEq)]
|
||||
pub struct Fq6 {
|
||||
pub c0: Fq2,
|
||||
pub c1: Fq2,
|
||||
pub c2: Fq2,
|
||||
}
|
||||
|
||||
impl ::std::fmt::Display for Fq6 {
|
||||
fn fmt(&self, f: &mut ::std::fmt::Formatter) -> ::std::fmt::Result {
|
||||
write!(f, "Fq6({} + {} * v, {} * v^2)", self.c0, self.c1, self.c2)
|
||||
}
|
||||
}
|
||||
|
||||
impl Rand for Fq6 {
|
||||
fn rand<R: Rng>(rng: &mut R) -> Self {
|
||||
Fq6 {
|
||||
c0: rng.gen(),
|
||||
c1: rng.gen(),
|
||||
c2: rng.gen(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Fq6 {
|
||||
/// Multiply by quadratic nonresidue v.
|
||||
pub fn mul_by_nonresidue(&mut self) {
|
||||
use std::mem::swap;
|
||||
swap(&mut self.c0, &mut self.c1);
|
||||
swap(&mut self.c0, &mut self.c2);
|
||||
|
||||
self.c0.mul_by_nonresidue();
|
||||
}
|
||||
|
||||
pub fn mul_by_1(&mut self, c1: &Fq2) {
|
||||
let mut b_b = self.c1;
|
||||
b_b.mul_assign(c1);
|
||||
|
||||
let mut t1 = *c1;
|
||||
{
|
||||
let mut tmp = self.c1;
|
||||
tmp.add_assign(&self.c2);
|
||||
|
||||
t1.mul_assign(&tmp);
|
||||
t1.sub_assign(&b_b);
|
||||
t1.mul_by_nonresidue();
|
||||
}
|
||||
|
||||
let mut t2 = *c1;
|
||||
{
|
||||
let mut tmp = self.c0;
|
||||
tmp.add_assign(&self.c1);
|
||||
|
||||
t2.mul_assign(&tmp);
|
||||
t2.sub_assign(&b_b);
|
||||
}
|
||||
|
||||
self.c0 = t1;
|
||||
self.c1 = t2;
|
||||
self.c2 = b_b;
|
||||
}
|
||||
|
||||
pub fn mul_by_01(&mut self, c0: &Fq2, c1: &Fq2) {
|
||||
let mut a_a = self.c0;
|
||||
let mut b_b = self.c1;
|
||||
a_a.mul_assign(c0);
|
||||
b_b.mul_assign(c1);
|
||||
|
||||
let mut t1 = *c1;
|
||||
{
|
||||
let mut tmp = self.c1;
|
||||
tmp.add_assign(&self.c2);
|
||||
|
||||
t1.mul_assign(&tmp);
|
||||
t1.sub_assign(&b_b);
|
||||
t1.mul_by_nonresidue();
|
||||
t1.add_assign(&a_a);
|
||||
}
|
||||
|
||||
let mut t3 = *c0;
|
||||
{
|
||||
let mut tmp = self.c0;
|
||||
tmp.add_assign(&self.c2);
|
||||
|
||||
t3.mul_assign(&tmp);
|
||||
t3.sub_assign(&a_a);
|
||||
t3.add_assign(&b_b);
|
||||
}
|
||||
|
||||
let mut t2 = *c0;
|
||||
t2.add_assign(c1);
|
||||
{
|
||||
let mut tmp = self.c0;
|
||||
tmp.add_assign(&self.c1);
|
||||
|
||||
t2.mul_assign(&tmp);
|
||||
t2.sub_assign(&a_a);
|
||||
t2.sub_assign(&b_b);
|
||||
}
|
||||
|
||||
self.c0 = t1;
|
||||
self.c1 = t2;
|
||||
self.c2 = t3;
|
||||
}
|
||||
}
|
||||
|
||||
impl Field for Fq6 {
|
||||
fn zero() -> Self {
|
||||
Fq6 {
|
||||
c0: Fq2::zero(),
|
||||
c1: Fq2::zero(),
|
||||
c2: Fq2::zero(),
|
||||
}
|
||||
}
|
||||
|
||||
fn one() -> Self {
|
||||
Fq6 {
|
||||
c0: Fq2::one(),
|
||||
c1: Fq2::zero(),
|
||||
c2: Fq2::zero(),
|
||||
}
|
||||
}
|
||||
|
||||
fn is_zero(&self) -> bool {
|
||||
self.c0.is_zero() && self.c1.is_zero() && self.c2.is_zero()
|
||||
}
|
||||
|
||||
fn double(&mut self) {
|
||||
self.c0.double();
|
||||
self.c1.double();
|
||||
self.c2.double();
|
||||
}
|
||||
|
||||
fn negate(&mut self) {
|
||||
self.c0.negate();
|
||||
self.c1.negate();
|
||||
self.c2.negate();
|
||||
}
|
||||
|
||||
fn add_assign(&mut self, other: &Self) {
|
||||
self.c0.add_assign(&other.c0);
|
||||
self.c1.add_assign(&other.c1);
|
||||
self.c2.add_assign(&other.c2);
|
||||
}
|
||||
|
||||
fn sub_assign(&mut self, other: &Self) {
|
||||
self.c0.sub_assign(&other.c0);
|
||||
self.c1.sub_assign(&other.c1);
|
||||
self.c2.sub_assign(&other.c2);
|
||||
}
|
||||
|
||||
fn frobenius_map(&mut self, power: usize) {
|
||||
self.c0.frobenius_map(power);
|
||||
self.c1.frobenius_map(power);
|
||||
self.c2.frobenius_map(power);
|
||||
|
||||
self.c1.mul_assign(&FROBENIUS_COEFF_FQ6_C1[power % 6]);
|
||||
self.c2.mul_assign(&FROBENIUS_COEFF_FQ6_C2[power % 6]);
|
||||
}
|
||||
|
||||
fn square(&mut self) {
|
||||
let mut s0 = self.c0;
|
||||
s0.square();
|
||||
let mut ab = self.c0;
|
||||
ab.mul_assign(&self.c1);
|
||||
let mut s1 = ab;
|
||||
s1.double();
|
||||
let mut s2 = self.c0;
|
||||
s2.sub_assign(&self.c1);
|
||||
s2.add_assign(&self.c2);
|
||||
s2.square();
|
||||
let mut bc = self.c1;
|
||||
bc.mul_assign(&self.c2);
|
||||
let mut s3 = bc;
|
||||
s3.double();
|
||||
let mut s4 = self.c2;
|
||||
s4.square();
|
||||
|
||||
self.c0 = s3;
|
||||
self.c0.mul_by_nonresidue();
|
||||
self.c0.add_assign(&s0);
|
||||
|
||||
self.c1 = s4;
|
||||
self.c1.mul_by_nonresidue();
|
||||
self.c1.add_assign(&s1);
|
||||
|
||||
self.c2 = s1;
|
||||
self.c2.add_assign(&s2);
|
||||
self.c2.add_assign(&s3);
|
||||
self.c2.sub_assign(&s0);
|
||||
self.c2.sub_assign(&s4);
|
||||
}
|
||||
|
||||
fn mul_assign(&mut self, other: &Self) {
|
||||
let mut a_a = self.c0;
|
||||
let mut b_b = self.c1;
|
||||
let mut c_c = self.c2;
|
||||
a_a.mul_assign(&other.c0);
|
||||
b_b.mul_assign(&other.c1);
|
||||
c_c.mul_assign(&other.c2);
|
||||
|
||||
let mut t1 = other.c1;
|
||||
t1.add_assign(&other.c2);
|
||||
{
|
||||
let mut tmp = self.c1;
|
||||
tmp.add_assign(&self.c2);
|
||||
|
||||
t1.mul_assign(&tmp);
|
||||
t1.sub_assign(&b_b);
|
||||
t1.sub_assign(&c_c);
|
||||
t1.mul_by_nonresidue();
|
||||
t1.add_assign(&a_a);
|
||||
}
|
||||
|
||||
let mut t3 = other.c0;
|
||||
t3.add_assign(&other.c2);
|
||||
{
|
||||
let mut tmp = self.c0;
|
||||
tmp.add_assign(&self.c2);
|
||||
|
||||
t3.mul_assign(&tmp);
|
||||
t3.sub_assign(&a_a);
|
||||
t3.add_assign(&b_b);
|
||||
t3.sub_assign(&c_c);
|
||||
}
|
||||
|
||||
let mut t2 = other.c0;
|
||||
t2.add_assign(&other.c1);
|
||||
{
|
||||
let mut tmp = self.c0;
|
||||
tmp.add_assign(&self.c1);
|
||||
|
||||
t2.mul_assign(&tmp);
|
||||
t2.sub_assign(&a_a);
|
||||
t2.sub_assign(&b_b);
|
||||
c_c.mul_by_nonresidue();
|
||||
t2.add_assign(&c_c);
|
||||
}
|
||||
|
||||
self.c0 = t1;
|
||||
self.c1 = t2;
|
||||
self.c2 = t3;
|
||||
}
|
||||
|
||||
fn inverse(&self) -> Option<Self> {
|
||||
let mut c0 = self.c2;
|
||||
c0.mul_by_nonresidue();
|
||||
c0.mul_assign(&self.c1);
|
||||
c0.negate();
|
||||
{
|
||||
let mut c0s = self.c0;
|
||||
c0s.square();
|
||||
c0.add_assign(&c0s);
|
||||
}
|
||||
let mut c1 = self.c2;
|
||||
c1.square();
|
||||
c1.mul_by_nonresidue();
|
||||
{
|
||||
let mut c01 = self.c0;
|
||||
c01.mul_assign(&self.c1);
|
||||
c1.sub_assign(&c01);
|
||||
}
|
||||
let mut c2 = self.c1;
|
||||
c2.square();
|
||||
{
|
||||
let mut c02 = self.c0;
|
||||
c02.mul_assign(&self.c2);
|
||||
c2.sub_assign(&c02);
|
||||
}
|
||||
|
||||
let mut tmp1 = self.c2;
|
||||
tmp1.mul_assign(&c1);
|
||||
let mut tmp2 = self.c1;
|
||||
tmp2.mul_assign(&c2);
|
||||
tmp1.add_assign(&tmp2);
|
||||
tmp1.mul_by_nonresidue();
|
||||
tmp2 = self.c0;
|
||||
tmp2.mul_assign(&c0);
|
||||
tmp1.add_assign(&tmp2);
|
||||
|
||||
match tmp1.inverse() {
|
||||
Some(t) => {
|
||||
let mut tmp = Fq6 {
|
||||
c0: t,
|
||||
c1: t,
|
||||
c2: t,
|
||||
};
|
||||
tmp.c0.mul_assign(&c0);
|
||||
tmp.c1.mul_assign(&c1);
|
||||
tmp.c2.mul_assign(&c2);
|
||||
|
||||
Some(tmp)
|
||||
}
|
||||
None => None,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
use rand::{SeedableRng, XorShiftRng};
|
||||
|
||||
#[test]
|
||||
fn test_fq6_mul_nonresidue() {
|
||||
let mut rng = XorShiftRng::from_seed([0x5dbe6259, 0x8d313d76, 0x3237db17, 0xe5bc0654]);
|
||||
|
||||
let nqr = Fq6 {
|
||||
c0: Fq2::zero(),
|
||||
c1: Fq2::one(),
|
||||
c2: Fq2::zero(),
|
||||
};
|
||||
|
||||
for _ in 0..1000 {
|
||||
let mut a = Fq6::rand(&mut rng);
|
||||
let mut b = a;
|
||||
a.mul_by_nonresidue();
|
||||
b.mul_assign(&nqr);
|
||||
|
||||
assert_eq!(a, b);
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_fq6_mul_by_1() {
|
||||
let mut rng = XorShiftRng::from_seed([0x5dbe6259, 0x8d313d76, 0x3237db17, 0xe5bc0654]);
|
||||
|
||||
for _ in 0..1000 {
|
||||
let c1 = Fq2::rand(&mut rng);
|
||||
let mut a = Fq6::rand(&mut rng);
|
||||
let mut b = a;
|
||||
|
||||
a.mul_by_1(&c1);
|
||||
b.mul_assign(&Fq6 {
|
||||
c0: Fq2::zero(),
|
||||
c1: c1,
|
||||
c2: Fq2::zero(),
|
||||
});
|
||||
|
||||
assert_eq!(a, b);
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_fq6_mul_by_01() {
|
||||
let mut rng = XorShiftRng::from_seed([0x5dbe6259, 0x8d313d76, 0x3237db17, 0xe5bc0654]);
|
||||
|
||||
for _ in 0..1000 {
|
||||
let c0 = Fq2::rand(&mut rng);
|
||||
let c1 = Fq2::rand(&mut rng);
|
||||
let mut a = Fq6::rand(&mut rng);
|
||||
let mut b = a;
|
||||
|
||||
a.mul_by_01(&c0, &c1);
|
||||
b.mul_assign(&Fq6 {
|
||||
c0: c0,
|
||||
c1: c1,
|
||||
c2: Fq2::zero(),
|
||||
});
|
||||
|
||||
assert_eq!(a, b);
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn fq6_field_tests() {
|
||||
use PrimeField;
|
||||
|
||||
::tests::field::random_field_tests::<Fq6>();
|
||||
::tests::field::random_frobenius_tests::<Fq6, _>(super::fq::Fq::char(), 13);
|
||||
}
|
File diff suppressed because it is too large
Load Diff
|
@ -0,0 +1,364 @@
|
|||
mod ec;
|
||||
mod fq;
|
||||
mod fq12;
|
||||
mod fq2;
|
||||
mod fq6;
|
||||
mod fr;
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests;
|
||||
|
||||
pub use self::ec::{
|
||||
G1, G1Affine, G1Compressed, G1Prepared, G1Uncompressed, G2, G2Affine, G2Compressed, G2Prepared,
|
||||
G2Uncompressed,
|
||||
};
|
||||
pub use self::fq::{Fq, FqRepr};
|
||||
pub use self::fq12::Fq12;
|
||||
pub use self::fq2::Fq2;
|
||||
pub use self::fq6::Fq6;
|
||||
pub use self::fr::{Fr, FrRepr};
|
||||
|
||||
use super::{BitIterator, CurveAffine, Engine, Field};
|
||||
|
||||
// The BLS parameter x for BLS12-381 is -0xd201000000010000
|
||||
const BLS_X: u64 = 0xd201000000010000;
|
||||
const BLS_X_IS_NEGATIVE: bool = true;
|
||||
|
||||
#[derive(Clone, Debug)]
|
||||
pub struct Bls12;
|
||||
|
||||
impl Engine for Bls12 {
|
||||
type Fr = Fr;
|
||||
type G1 = G1;
|
||||
type G1Affine = G1Affine;
|
||||
type G2 = G2;
|
||||
type G2Affine = G2Affine;
|
||||
type Fq = Fq;
|
||||
type Fqe = Fq2;
|
||||
type Fqk = Fq12;
|
||||
|
||||
fn miller_loop<'a, I>(i: I) -> Self::Fqk
|
||||
where
|
||||
I: IntoIterator<
|
||||
Item = &'a (
|
||||
&'a <Self::G1Affine as CurveAffine>::Prepared,
|
||||
&'a <Self::G2Affine as CurveAffine>::Prepared,
|
||||
),
|
||||
>,
|
||||
{
|
||||
let mut pairs = vec![];
|
||||
for &(p, q) in i {
|
||||
if !p.is_zero() && !q.is_zero() {
|
||||
pairs.push((p, q.coeffs.iter()));
|
||||
}
|
||||
}
|
||||
|
||||
// Twisting isomorphism from E to E'
|
||||
fn ell(f: &mut Fq12, coeffs: &(Fq2, Fq2, Fq2), p: &G1Affine) {
|
||||
let mut c0 = coeffs.0;
|
||||
let mut c1 = coeffs.1;
|
||||
|
||||
c0.c0.mul_assign(&p.y);
|
||||
c0.c1.mul_assign(&p.y);
|
||||
|
||||
c1.c0.mul_assign(&p.x);
|
||||
c1.c1.mul_assign(&p.x);
|
||||
|
||||
// Sparse multiplication in Fq12
|
||||
f.mul_by_014(&coeffs.2, &c1, &c0);
|
||||
}
|
||||
|
||||
let mut f = Fq12::one();
|
||||
|
||||
let mut found_one = false;
|
||||
for i in BitIterator::new(&[BLS_X >> 1]) {
|
||||
if !found_one {
|
||||
found_one = i;
|
||||
continue;
|
||||
}
|
||||
|
||||
for &mut (p, ref mut coeffs) in &mut pairs {
|
||||
ell(&mut f, coeffs.next().unwrap(), &p.0);
|
||||
}
|
||||
|
||||
if i {
|
||||
for &mut (p, ref mut coeffs) in &mut pairs {
|
||||
ell(&mut f, coeffs.next().unwrap(), &p.0);
|
||||
}
|
||||
}
|
||||
|
||||
f.square();
|
||||
}
|
||||
|
||||
for &mut (p, ref mut coeffs) in &mut pairs {
|
||||
ell(&mut f, coeffs.next().unwrap(), &p.0);
|
||||
}
|
||||
|
||||
if BLS_X_IS_NEGATIVE {
|
||||
f.conjugate();
|
||||
}
|
||||
|
||||
f
|
||||
}
|
||||
|
||||
fn final_exponentiation(r: &Fq12) -> Option<Fq12> {
|
||||
let mut f1 = *r;
|
||||
f1.conjugate();
|
||||
|
||||
match r.inverse() {
|
||||
Some(mut f2) => {
|
||||
let mut r = f1;
|
||||
r.mul_assign(&f2);
|
||||
f2 = r;
|
||||
r.frobenius_map(2);
|
||||
r.mul_assign(&f2);
|
||||
|
||||
fn exp_by_x(f: &mut Fq12, x: u64) {
|
||||
*f = f.pow(&[x]);
|
||||
if BLS_X_IS_NEGATIVE {
|
||||
f.conjugate();
|
||||
}
|
||||
}
|
||||
|
||||
let mut x = BLS_X;
|
||||
let mut y0 = r;
|
||||
y0.square();
|
||||
let mut y1 = y0;
|
||||
exp_by_x(&mut y1, x);
|
||||
x >>= 1;
|
||||
let mut y2 = y1;
|
||||
exp_by_x(&mut y2, x);
|
||||
x <<= 1;
|
||||
let mut y3 = r;
|
||||
y3.conjugate();
|
||||
y1.mul_assign(&y3);
|
||||
y1.conjugate();
|
||||
y1.mul_assign(&y2);
|
||||
y2 = y1;
|
||||
exp_by_x(&mut y2, x);
|
||||
y3 = y2;
|
||||
exp_by_x(&mut y3, x);
|
||||
y1.conjugate();
|
||||
y3.mul_assign(&y1);
|
||||
y1.conjugate();
|
||||
y1.frobenius_map(3);
|
||||
y2.frobenius_map(2);
|
||||
y1.mul_assign(&y2);
|
||||
y2 = y3;
|
||||
exp_by_x(&mut y2, x);
|
||||
y2.mul_assign(&y0);
|
||||
y2.mul_assign(&r);
|
||||
y1.mul_assign(&y2);
|
||||
y2 = y3;
|
||||
y2.frobenius_map(1);
|
||||
y1.mul_assign(&y2);
|
||||
|
||||
Some(y1)
|
||||
}
|
||||
None => None,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl G2Prepared {
|
||||
pub fn is_zero(&self) -> bool {
|
||||
self.infinity
|
||||
}
|
||||
|
||||
pub fn from_affine(q: G2Affine) -> Self {
|
||||
if q.is_zero() {
|
||||
return G2Prepared {
|
||||
coeffs: vec![],
|
||||
infinity: true,
|
||||
};
|
||||
}
|
||||
|
||||
fn doubling_step(r: &mut G2) -> (Fq2, Fq2, Fq2) {
|
||||
// Adaptation of Algorithm 26, https://eprint.iacr.org/2010/354.pdf
|
||||
let mut tmp0 = r.x;
|
||||
tmp0.square();
|
||||
|
||||
let mut tmp1 = r.y;
|
||||
tmp1.square();
|
||||
|
||||
let mut tmp2 = tmp1;
|
||||
tmp2.square();
|
||||
|
||||
let mut tmp3 = tmp1;
|
||||
tmp3.add_assign(&r.x);
|
||||
tmp3.square();
|
||||
tmp3.sub_assign(&tmp0);
|
||||
tmp3.sub_assign(&tmp2);
|
||||
tmp3.double();
|
||||
|
||||
let mut tmp4 = tmp0;
|
||||
tmp4.double();
|
||||
tmp4.add_assign(&tmp0);
|
||||
|
||||
let mut tmp6 = r.x;
|
||||
tmp6.add_assign(&tmp4);
|
||||
|
||||
let mut tmp5 = tmp4;
|
||||
tmp5.square();
|
||||
|
||||
let mut zsquared = r.z;
|
||||
zsquared.square();
|
||||
|
||||
r.x = tmp5;
|
||||
r.x.sub_assign(&tmp3);
|
||||
r.x.sub_assign(&tmp3);
|
||||
|
||||
r.z.add_assign(&r.y);
|
||||
r.z.square();
|
||||
r.z.sub_assign(&tmp1);
|
||||
r.z.sub_assign(&zsquared);
|
||||
|
||||
r.y = tmp3;
|
||||
r.y.sub_assign(&r.x);
|
||||
r.y.mul_assign(&tmp4);
|
||||
|
||||
tmp2.double();
|
||||
tmp2.double();
|
||||
tmp2.double();
|
||||
|
||||
r.y.sub_assign(&tmp2);
|
||||
|
||||
tmp3 = tmp4;
|
||||
tmp3.mul_assign(&zsquared);
|
||||
tmp3.double();
|
||||
tmp3.negate();
|
||||
|
||||
tmp6.square();
|
||||
tmp6.sub_assign(&tmp0);
|
||||
tmp6.sub_assign(&tmp5);
|
||||
|
||||
tmp1.double();
|
||||
tmp1.double();
|
||||
|
||||
tmp6.sub_assign(&tmp1);
|
||||
|
||||
tmp0 = r.z;
|
||||
tmp0.mul_assign(&zsquared);
|
||||
tmp0.double();
|
||||
|
||||
(tmp0, tmp3, tmp6)
|
||||
}
|
||||
|
||||
fn addition_step(r: &mut G2, q: &G2Affine) -> (Fq2, Fq2, Fq2) {
|
||||
// Adaptation of Algorithm 27, https://eprint.iacr.org/2010/354.pdf
|
||||
let mut zsquared = r.z;
|
||||
zsquared.square();
|
||||
|
||||
let mut ysquared = q.y;
|
||||
ysquared.square();
|
||||
|
||||
let mut t0 = zsquared;
|
||||
t0.mul_assign(&q.x);
|
||||
|
||||
let mut t1 = q.y;
|
||||
t1.add_assign(&r.z);
|
||||
t1.square();
|
||||
t1.sub_assign(&ysquared);
|
||||
t1.sub_assign(&zsquared);
|
||||
t1.mul_assign(&zsquared);
|
||||
|
||||
let mut t2 = t0;
|
||||
t2.sub_assign(&r.x);
|
||||
|
||||
let mut t3 = t2;
|
||||
t3.square();
|
||||
|
||||
let mut t4 = t3;
|
||||
t4.double();
|
||||
t4.double();
|
||||
|
||||
let mut t5 = t4;
|
||||
t5.mul_assign(&t2);
|
||||
|
||||
let mut t6 = t1;
|
||||
t6.sub_assign(&r.y);
|
||||
t6.sub_assign(&r.y);
|
||||
|
||||
let mut t9 = t6;
|
||||
t9.mul_assign(&q.x);
|
||||
|
||||
let mut t7 = t4;
|
||||
t7.mul_assign(&r.x);
|
||||
|
||||
r.x = t6;
|
||||
r.x.square();
|
||||
r.x.sub_assign(&t5);
|
||||
r.x.sub_assign(&t7);
|
||||
r.x.sub_assign(&t7);
|
||||
|
||||
r.z.add_assign(&t2);
|
||||
r.z.square();
|
||||
r.z.sub_assign(&zsquared);
|
||||
r.z.sub_assign(&t3);
|
||||
|
||||
let mut t10 = q.y;
|
||||
t10.add_assign(&r.z);
|
||||
|
||||
let mut t8 = t7;
|
||||
t8.sub_assign(&r.x);
|
||||
t8.mul_assign(&t6);
|
||||
|
||||
t0 = r.y;
|
||||
t0.mul_assign(&t5);
|
||||
t0.double();
|
||||
|
||||
r.y = t8;
|
||||
r.y.sub_assign(&t0);
|
||||
|
||||
t10.square();
|
||||
t10.sub_assign(&ysquared);
|
||||
|
||||
let mut ztsquared = r.z;
|
||||
ztsquared.square();
|
||||
|
||||
t10.sub_assign(&ztsquared);
|
||||
|
||||
t9.double();
|
||||
t9.sub_assign(&t10);
|
||||
|
||||
t10 = r.z;
|
||||
t10.double();
|
||||
|
||||
t6.negate();
|
||||
|
||||
t1 = t6;
|
||||
t1.double();
|
||||
|
||||
(t10, t1, t9)
|
||||
}
|
||||
|
||||
let mut coeffs = vec![];
|
||||
let mut r: G2 = q.into();
|
||||
|
||||
let mut found_one = false;
|
||||
for i in BitIterator::new([BLS_X >> 1]) {
|
||||
if !found_one {
|
||||
found_one = i;
|
||||
continue;
|
||||
}
|
||||
|
||||
coeffs.push(doubling_step(&mut r));
|
||||
|
||||
if i {
|
||||
coeffs.push(addition_step(&mut r, &q));
|
||||
}
|
||||
}
|
||||
|
||||
coeffs.push(doubling_step(&mut r));
|
||||
|
||||
G2Prepared {
|
||||
coeffs,
|
||||
infinity: false,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn bls12_engine_tests() {
|
||||
::tests::engine::engine_tests::<Bls12>();
|
||||
}
|
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
|
@ -0,0 +1,611 @@
|
|||
use super::*;
|
||||
use *;
|
||||
|
||||
#[test]
|
||||
fn test_pairing_result_against_relic() {
|
||||
/*
|
||||
Sent to me from Diego Aranha (author of RELIC library):
|
||||
|
||||
1250EBD871FC0A92 A7B2D83168D0D727 272D441BEFA15C50 3DD8E90CE98DB3E7 B6D194F60839C508 A84305AACA1789B6
|
||||
089A1C5B46E5110B 86750EC6A5323488 68A84045483C92B7 AF5AF689452EAFAB F1A8943E50439F1D 59882A98EAA0170F
|
||||
1368BB445C7C2D20 9703F239689CE34C 0378A68E72A6B3B2 16DA0E22A5031B54 DDFF57309396B38C 881C4C849EC23E87
|
||||
193502B86EDB8857 C273FA075A505129 37E0794E1E65A761 7C90D8BD66065B1F FFE51D7A579973B1 315021EC3C19934F
|
||||
01B2F522473D1713 91125BA84DC4007C FBF2F8DA752F7C74 185203FCCA589AC7 19C34DFFBBAAD843 1DAD1C1FB597AAA5
|
||||
018107154F25A764 BD3C79937A45B845 46DA634B8F6BE14A 8061E55CCEBA478B 23F7DACAA35C8CA7 8BEAE9624045B4B6
|
||||
19F26337D205FB46 9CD6BD15C3D5A04D C88784FBB3D0B2DB DEA54D43B2B73F2C BB12D58386A8703E 0F948226E47EE89D
|
||||
06FBA23EB7C5AF0D 9F80940CA771B6FF D5857BAAF222EB95 A7D2809D61BFE02E 1BFD1B68FF02F0B8 102AE1C2D5D5AB1A
|
||||
11B8B424CD48BF38 FCEF68083B0B0EC5 C81A93B330EE1A67 7D0D15FF7B984E89 78EF48881E32FAC9 1B93B47333E2BA57
|
||||
03350F55A7AEFCD3 C31B4FCB6CE5771C C6A0E9786AB59733 20C806AD36082910 7BA810C5A09FFDD9 BE2291A0C25A99A2
|
||||
04C581234D086A99 02249B64728FFD21 A189E87935A95405 1C7CDBA7B3872629 A4FAFC05066245CB 9108F0242D0FE3EF
|
||||
0F41E58663BF08CF 068672CBD01A7EC7 3BACA4D72CA93544 DEFF686BFD6DF543 D48EAA24AFE47E1E FDE449383B676631
|
||||
*/
|
||||
|
||||
assert_eq!(Bls12::pairing(G1::one(), G2::one()), Fq12 {
|
||||
c0: Fq6 {
|
||||
c0: Fq2 {
|
||||
c0: Fq::from_str("2819105605953691245277803056322684086884703000473961065716485506033588504203831029066448642358042597501014294104502").unwrap(),
|
||||
c1: Fq::from_str("1323968232986996742571315206151405965104242542339680722164220900812303524334628370163366153839984196298685227734799").unwrap()
|
||||
},
|
||||
c1: Fq2 {
|
||||
c0: Fq::from_str("2987335049721312504428602988447616328830341722376962214011674875969052835043875658579425548512925634040144704192135").unwrap(),
|
||||
c1: Fq::from_str("3879723582452552452538684314479081967502111497413076598816163759028842927668327542875108457755966417881797966271311").unwrap()
|
||||
},
|
||||
c2: Fq2 {
|
||||
c0: Fq::from_str("261508182517997003171385743374653339186059518494239543139839025878870012614975302676296704930880982238308326681253").unwrap(),
|
||||
c1: Fq::from_str("231488992246460459663813598342448669854473942105054381511346786719005883340876032043606739070883099647773793170614").unwrap()
|
||||
}
|
||||
},
|
||||
c1: Fq6 {
|
||||
c0: Fq2 {
|
||||
c0: Fq::from_str("3993582095516422658773669068931361134188738159766715576187490305611759126554796569868053818105850661142222948198557").unwrap(),
|
||||
c1: Fq::from_str("1074773511698422344502264006159859710502164045911412750831641680783012525555872467108249271286757399121183508900634").unwrap()
|
||||
},
|
||||
c1: Fq2 {
|
||||
c0: Fq::from_str("2727588299083545686739024317998512740561167011046940249988557419323068809019137624943703910267790601287073339193943").unwrap(),
|
||||
c1: Fq::from_str("493643299814437640914745677854369670041080344349607504656543355799077485536288866009245028091988146107059514546594").unwrap()
|
||||
},
|
||||
c2: Fq2 {
|
||||
c0: Fq::from_str("734401332196641441839439105942623141234148957972407782257355060229193854324927417865401895596108124443575283868655").unwrap(),
|
||||
c1: Fq::from_str("2348330098288556420918672502923664952620152483128593484301759394583320358354186482723629999370241674973832318248497").unwrap()
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
fn test_vectors<G: CurveProjective, E: EncodedPoint<Affine = G::Affine>>(expected: &[u8]) {
|
||||
let mut e = G::zero();
|
||||
|
||||
let mut v = vec![];
|
||||
{
|
||||
let mut expected = expected;
|
||||
for _ in 0..1000 {
|
||||
let e_affine = e.into_affine();
|
||||
let encoded = E::from_affine(e_affine);
|
||||
v.extend_from_slice(encoded.as_ref());
|
||||
|
||||
let mut decoded = E::empty();
|
||||
decoded.as_mut().copy_from_slice(&expected[0..E::size()]);
|
||||
expected = &expected[E::size()..];
|
||||
let decoded = decoded.into_affine().unwrap();
|
||||
assert_eq!(e_affine, decoded);
|
||||
|
||||
e.add_assign(&G::one());
|
||||
}
|
||||
}
|
||||
|
||||
assert_eq!(&v[..], expected);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_g1_uncompressed_valid_vectors() {
|
||||
test_vectors::<G1, G1Uncompressed>(include_bytes!("g1_uncompressed_valid_test_vectors.dat"));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_g1_compressed_valid_vectors() {
|
||||
test_vectors::<G1, G1Compressed>(include_bytes!("g1_compressed_valid_test_vectors.dat"));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_g2_uncompressed_valid_vectors() {
|
||||
test_vectors::<G2, G2Uncompressed>(include_bytes!("g2_uncompressed_valid_test_vectors.dat"));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_g2_compressed_valid_vectors() {
|
||||
test_vectors::<G2, G2Compressed>(include_bytes!("g2_compressed_valid_test_vectors.dat"));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_g1_uncompressed_invalid_vectors() {
|
||||
{
|
||||
let z = G1Affine::zero().into_uncompressed();
|
||||
|
||||
{
|
||||
let mut z = z;
|
||||
z.as_mut()[0] |= 0b1000_0000;
|
||||
if let Err(GroupDecodingError::UnexpectedCompressionMode) = z.into_affine() {
|
||||
// :)
|
||||
} else {
|
||||
panic!("should have rejected the point because we expected an uncompressed point");
|
||||
}
|
||||
}
|
||||
|
||||
{
|
||||
let mut z = z;
|
||||
z.as_mut()[0] |= 0b0010_0000;
|
||||
if let Err(GroupDecodingError::UnexpectedInformation) = z.into_affine() {
|
||||
// :)
|
||||
} else {
|
||||
panic!("should have rejected the point because the parity bit should not be set if the point is at infinity");
|
||||
}
|
||||
}
|
||||
|
||||
for i in 0..G1Uncompressed::size() {
|
||||
let mut z = z;
|
||||
z.as_mut()[i] |= 0b0000_0001;
|
||||
if let Err(GroupDecodingError::UnexpectedInformation) = z.into_affine() {
|
||||
// :)
|
||||
} else {
|
||||
panic!("should have rejected the point because the coordinates should be zeroes at the point at infinity");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
let o = G1Affine::one().into_uncompressed();
|
||||
|
||||
{
|
||||
let mut o = o;
|
||||
o.as_mut()[0] |= 0b1000_0000;
|
||||
if let Err(GroupDecodingError::UnexpectedCompressionMode) = o.into_affine() {
|
||||
// :)
|
||||
} else {
|
||||
panic!("should have rejected the point because we expected an uncompressed point");
|
||||
}
|
||||
}
|
||||
|
||||
let m = Fq::char();
|
||||
|
||||
{
|
||||
let mut o = o;
|
||||
m.write_be(&mut o.as_mut()[0..]).unwrap();
|
||||
|
||||
if let Err(GroupDecodingError::CoordinateDecodingError(coordinate, _)) = o.into_affine() {
|
||||
assert_eq!(coordinate, "x coordinate");
|
||||
} else {
|
||||
panic!("should have rejected the point")
|
||||
}
|
||||
}
|
||||
|
||||
{
|
||||
let mut o = o;
|
||||
m.write_be(&mut o.as_mut()[48..]).unwrap();
|
||||
|
||||
if let Err(GroupDecodingError::CoordinateDecodingError(coordinate, _)) = o.into_affine() {
|
||||
assert_eq!(coordinate, "y coordinate");
|
||||
} else {
|
||||
panic!("should have rejected the point")
|
||||
}
|
||||
}
|
||||
|
||||
{
|
||||
let m = Fq::zero().into_repr();
|
||||
|
||||
let mut o = o;
|
||||
m.write_be(&mut o.as_mut()[0..]).unwrap();
|
||||
|
||||
if let Err(GroupDecodingError::NotOnCurve) = o.into_affine() {
|
||||
// :)
|
||||
} else {
|
||||
panic!("should have rejected the point because it isn't on the curve")
|
||||
}
|
||||
}
|
||||
|
||||
{
|
||||
let mut o = o;
|
||||
let mut x = Fq::one();
|
||||
|
||||
loop {
|
||||
let mut x3b = x;
|
||||
x3b.square();
|
||||
x3b.mul_assign(&x);
|
||||
x3b.add_assign(&Fq::from_repr(FqRepr::from(4)).unwrap()); // TODO: perhaps expose coeff_b through API?
|
||||
|
||||
if let Some(y) = x3b.sqrt() {
|
||||
// We know this is on the curve, but it's likely not going to be in the correct subgroup.
|
||||
x.into_repr().write_be(&mut o.as_mut()[0..]).unwrap();
|
||||
y.into_repr().write_be(&mut o.as_mut()[48..]).unwrap();
|
||||
|
||||
if let Err(GroupDecodingError::NotInSubgroup) = o.into_affine() {
|
||||
break;
|
||||
} else {
|
||||
panic!(
|
||||
"should have rejected the point because it isn't in the correct subgroup"
|
||||
)
|
||||
}
|
||||
} else {
|
||||
x.add_assign(&Fq::one());
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_g2_uncompressed_invalid_vectors() {
|
||||
{
|
||||
let z = G2Affine::zero().into_uncompressed();
|
||||
|
||||
{
|
||||
let mut z = z;
|
||||
z.as_mut()[0] |= 0b1000_0000;
|
||||
if let Err(GroupDecodingError::UnexpectedCompressionMode) = z.into_affine() {
|
||||
// :)
|
||||
} else {
|
||||
panic!("should have rejected the point because we expected an uncompressed point");
|
||||
}
|
||||
}
|
||||
|
||||
{
|
||||
let mut z = z;
|
||||
z.as_mut()[0] |= 0b0010_0000;
|
||||
if let Err(GroupDecodingError::UnexpectedInformation) = z.into_affine() {
|
||||
// :)
|
||||
} else {
|
||||
panic!("should have rejected the point because the parity bit should not be set if the point is at infinity");
|
||||
}
|
||||
}
|
||||
|
||||
for i in 0..G2Uncompressed::size() {
|
||||
let mut z = z;
|
||||
z.as_mut()[i] |= 0b0000_0001;
|
||||
if let Err(GroupDecodingError::UnexpectedInformation) = z.into_affine() {
|
||||
// :)
|
||||
} else {
|
||||
panic!("should have rejected the point because the coordinates should be zeroes at the point at infinity");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
let o = G2Affine::one().into_uncompressed();
|
||||
|
||||
{
|
||||
let mut o = o;
|
||||
o.as_mut()[0] |= 0b1000_0000;
|
||||
if let Err(GroupDecodingError::UnexpectedCompressionMode) = o.into_affine() {
|
||||
// :)
|
||||
} else {
|
||||
panic!("should have rejected the point because we expected an uncompressed point");
|
||||
}
|
||||
}
|
||||
|
||||
let m = Fq::char();
|
||||
|
||||
{
|
||||
let mut o = o;
|
||||
m.write_be(&mut o.as_mut()[0..]).unwrap();
|
||||
|
||||
if let Err(GroupDecodingError::CoordinateDecodingError(coordinate, _)) = o.into_affine() {
|
||||
assert_eq!(coordinate, "x coordinate (c1)");
|
||||
} else {
|
||||
panic!("should have rejected the point")
|
||||
}
|
||||
}
|
||||
|
||||
{
|
||||
let mut o = o;
|
||||
m.write_be(&mut o.as_mut()[48..]).unwrap();
|
||||
|
||||
if let Err(GroupDecodingError::CoordinateDecodingError(coordinate, _)) = o.into_affine() {
|
||||
assert_eq!(coordinate, "x coordinate (c0)");
|
||||
} else {
|
||||
panic!("should have rejected the point")
|
||||
}
|
||||
}
|
||||
|
||||
{
|
||||
let mut o = o;
|
||||
m.write_be(&mut o.as_mut()[96..]).unwrap();
|
||||
|
||||
if let Err(GroupDecodingError::CoordinateDecodingError(coordinate, _)) = o.into_affine() {
|
||||
assert_eq!(coordinate, "y coordinate (c1)");
|
||||
} else {
|
||||
panic!("should have rejected the point")
|
||||
}
|
||||
}
|
||||
|
||||
{
|
||||
let mut o = o;
|
||||
m.write_be(&mut o.as_mut()[144..]).unwrap();
|
||||
|
||||
if let Err(GroupDecodingError::CoordinateDecodingError(coordinate, _)) = o.into_affine() {
|
||||
assert_eq!(coordinate, "y coordinate (c0)");
|
||||
} else {
|
||||
panic!("should have rejected the point")
|
||||
}
|
||||
}
|
||||
|
||||
{
|
||||
let m = Fq::zero().into_repr();
|
||||
|
||||
let mut o = o;
|
||||
m.write_be(&mut o.as_mut()[0..]).unwrap();
|
||||
m.write_be(&mut o.as_mut()[48..]).unwrap();
|
||||
|
||||
if let Err(GroupDecodingError::NotOnCurve) = o.into_affine() {
|
||||
// :)
|
||||
} else {
|
||||
panic!("should have rejected the point because it isn't on the curve")
|
||||
}
|
||||
}
|
||||
|
||||
{
|
||||
let mut o = o;
|
||||
let mut x = Fq2::one();
|
||||
|
||||
loop {
|
||||
let mut x3b = x;
|
||||
x3b.square();
|
||||
x3b.mul_assign(&x);
|
||||
x3b.add_assign(&Fq2 {
|
||||
c0: Fq::from_repr(FqRepr::from(4)).unwrap(),
|
||||
c1: Fq::from_repr(FqRepr::from(4)).unwrap(),
|
||||
}); // TODO: perhaps expose coeff_b through API?
|
||||
|
||||
if let Some(y) = x3b.sqrt() {
|
||||
// We know this is on the curve, but it's likely not going to be in the correct subgroup.
|
||||
x.c1.into_repr().write_be(&mut o.as_mut()[0..]).unwrap();
|
||||
x.c0.into_repr().write_be(&mut o.as_mut()[48..]).unwrap();
|
||||
y.c1.into_repr().write_be(&mut o.as_mut()[96..]).unwrap();
|
||||
y.c0.into_repr().write_be(&mut o.as_mut()[144..]).unwrap();
|
||||
|
||||
if let Err(GroupDecodingError::NotInSubgroup) = o.into_affine() {
|
||||
break;
|
||||
} else {
|
||||
panic!(
|
||||
"should have rejected the point because it isn't in the correct subgroup"
|
||||
)
|
||||
}
|
||||
} else {
|
||||
x.add_assign(&Fq2::one());
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_g1_compressed_invalid_vectors() {
|
||||
{
|
||||
let z = G1Affine::zero().into_compressed();
|
||||
|
||||
{
|
||||
let mut z = z;
|
||||
z.as_mut()[0] &= 0b0111_1111;
|
||||
if let Err(GroupDecodingError::UnexpectedCompressionMode) = z.into_affine() {
|
||||
// :)
|
||||
} else {
|
||||
panic!("should have rejected the point because we expected a compressed point");
|
||||
}
|
||||
}
|
||||
|
||||
{
|
||||
let mut z = z;
|
||||
z.as_mut()[0] |= 0b0010_0000;
|
||||
if let Err(GroupDecodingError::UnexpectedInformation) = z.into_affine() {
|
||||
// :)
|
||||
} else {
|
||||
panic!("should have rejected the point because the parity bit should not be set if the point is at infinity");
|
||||
}
|
||||
}
|
||||
|
||||
for i in 0..G1Compressed::size() {
|
||||
let mut z = z;
|
||||
z.as_mut()[i] |= 0b0000_0001;
|
||||
if let Err(GroupDecodingError::UnexpectedInformation) = z.into_affine() {
|
||||
// :)
|
||||
} else {
|
||||
panic!("should have rejected the point because the coordinates should be zeroes at the point at infinity");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
let o = G1Affine::one().into_compressed();
|
||||
|
||||
{
|
||||
let mut o = o;
|
||||
o.as_mut()[0] &= 0b0111_1111;
|
||||
if let Err(GroupDecodingError::UnexpectedCompressionMode) = o.into_affine() {
|
||||
// :)
|
||||
} else {
|
||||
panic!("should have rejected the point because we expected a compressed point");
|
||||
}
|
||||
}
|
||||
|
||||
let m = Fq::char();
|
||||
|
||||
{
|
||||
let mut o = o;
|
||||
m.write_be(&mut o.as_mut()[0..]).unwrap();
|
||||
o.as_mut()[0] |= 0b1000_0000;
|
||||
|
||||
if let Err(GroupDecodingError::CoordinateDecodingError(coordinate, _)) = o.into_affine() {
|
||||
assert_eq!(coordinate, "x coordinate");
|
||||
} else {
|
||||
panic!("should have rejected the point")
|
||||
}
|
||||
}
|
||||
|
||||
{
|
||||
let mut o = o;
|
||||
let mut x = Fq::one();
|
||||
|
||||
loop {
|
||||
let mut x3b = x;
|
||||
x3b.square();
|
||||
x3b.mul_assign(&x);
|
||||
x3b.add_assign(&Fq::from_repr(FqRepr::from(4)).unwrap()); // TODO: perhaps expose coeff_b through API?
|
||||
|
||||
if let Some(_) = x3b.sqrt() {
|
||||
x.add_assign(&Fq::one());
|
||||
} else {
|
||||
x.into_repr().write_be(&mut o.as_mut()[0..]).unwrap();
|
||||
o.as_mut()[0] |= 0b1000_0000;
|
||||
|
||||
if let Err(GroupDecodingError::NotOnCurve) = o.into_affine() {
|
||||
break;
|
||||
} else {
|
||||
panic!("should have rejected the point because it isn't on the curve")
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
{
|
||||
let mut o = o;
|
||||
let mut x = Fq::one();
|
||||
|
||||
loop {
|
||||
let mut x3b = x;
|
||||
x3b.square();
|
||||
x3b.mul_assign(&x);
|
||||
x3b.add_assign(&Fq::from_repr(FqRepr::from(4)).unwrap()); // TODO: perhaps expose coeff_b through API?
|
||||
|
||||
if let Some(_) = x3b.sqrt() {
|
||||
// We know this is on the curve, but it's likely not going to be in the correct subgroup.
|
||||
x.into_repr().write_be(&mut o.as_mut()[0..]).unwrap();
|
||||
o.as_mut()[0] |= 0b1000_0000;
|
||||
|
||||
if let Err(GroupDecodingError::NotInSubgroup) = o.into_affine() {
|
||||
break;
|
||||
} else {
|
||||
panic!(
|
||||
"should have rejected the point because it isn't in the correct subgroup"
|
||||
)
|
||||
}
|
||||
} else {
|
||||
x.add_assign(&Fq::one());
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_g2_compressed_invalid_vectors() {
|
||||
{
|
||||
let z = G2Affine::zero().into_compressed();
|
||||
|
||||
{
|
||||
let mut z = z;
|
||||
z.as_mut()[0] &= 0b0111_1111;
|
||||
if let Err(GroupDecodingError::UnexpectedCompressionMode) = z.into_affine() {
|
||||
// :)
|
||||
} else {
|
||||
panic!("should have rejected the point because we expected a compressed point");
|
||||
}
|
||||
}
|
||||
|
||||
{
|
||||
let mut z = z;
|
||||
z.as_mut()[0] |= 0b0010_0000;
|
||||
if let Err(GroupDecodingError::UnexpectedInformation) = z.into_affine() {
|
||||
// :)
|
||||
} else {
|
||||
panic!("should have rejected the point because the parity bit should not be set if the point is at infinity");
|
||||
}
|
||||
}
|
||||
|
||||
for i in 0..G2Compressed::size() {
|
||||
let mut z = z;
|
||||
z.as_mut()[i] |= 0b0000_0001;
|
||||
if let Err(GroupDecodingError::UnexpectedInformation) = z.into_affine() {
|
||||
// :)
|
||||
} else {
|
||||
panic!("should have rejected the point because the coordinates should be zeroes at the point at infinity");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
let o = G2Affine::one().into_compressed();
|
||||
|
||||
{
|
||||
let mut o = o;
|
||||
o.as_mut()[0] &= 0b0111_1111;
|
||||
if let Err(GroupDecodingError::UnexpectedCompressionMode) = o.into_affine() {
|
||||
// :)
|
||||
} else {
|
||||
panic!("should have rejected the point because we expected a compressed point");
|
||||
}
|
||||
}
|
||||
|
||||
let m = Fq::char();
|
||||
|
||||
{
|
||||
let mut o = o;
|
||||
m.write_be(&mut o.as_mut()[0..]).unwrap();
|
||||
o.as_mut()[0] |= 0b1000_0000;
|
||||
|
||||
if let Err(GroupDecodingError::CoordinateDecodingError(coordinate, _)) = o.into_affine() {
|
||||
assert_eq!(coordinate, "x coordinate (c1)");
|
||||
} else {
|
||||
panic!("should have rejected the point")
|
||||
}
|
||||
}
|
||||
|
||||
{
|
||||
let mut o = o;
|
||||
m.write_be(&mut o.as_mut()[48..]).unwrap();
|
||||
o.as_mut()[0] |= 0b1000_0000;
|
||||
|
||||
if let Err(GroupDecodingError::CoordinateDecodingError(coordinate, _)) = o.into_affine() {
|
||||
assert_eq!(coordinate, "x coordinate (c0)");
|
||||
} else {
|
||||
panic!("should have rejected the point")
|
||||
}
|
||||
}
|
||||
|
||||
{
|
||||
let mut o = o;
|
||||
let mut x = Fq2 {
|
||||
c0: Fq::one(),
|
||||
c1: Fq::one(),
|
||||
};
|
||||
|
||||
loop {
|
||||
let mut x3b = x;
|
||||
x3b.square();
|
||||
x3b.mul_assign(&x);
|
||||
x3b.add_assign(&Fq2 {
|
||||
c0: Fq::from_repr(FqRepr::from(4)).unwrap(),
|
||||
c1: Fq::from_repr(FqRepr::from(4)).unwrap(),
|
||||
}); // TODO: perhaps expose coeff_b through API?
|
||||
|
||||
if let Some(_) = x3b.sqrt() {
|
||||
x.add_assign(&Fq2::one());
|
||||
} else {
|
||||
x.c1.into_repr().write_be(&mut o.as_mut()[0..]).unwrap();
|
||||
x.c0.into_repr().write_be(&mut o.as_mut()[48..]).unwrap();
|
||||
o.as_mut()[0] |= 0b1000_0000;
|
||||
|
||||
if let Err(GroupDecodingError::NotOnCurve) = o.into_affine() {
|
||||
break;
|
||||
} else {
|
||||
panic!("should have rejected the point because it isn't on the curve")
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
{
|
||||
let mut o = o;
|
||||
let mut x = Fq2 {
|
||||
c0: Fq::one(),
|
||||
c1: Fq::one(),
|
||||
};
|
||||
|
||||
loop {
|
||||
let mut x3b = x;
|
||||
x3b.square();
|
||||
x3b.mul_assign(&x);
|
||||
x3b.add_assign(&Fq2 {
|
||||
c0: Fq::from_repr(FqRepr::from(4)).unwrap(),
|
||||
c1: Fq::from_repr(FqRepr::from(4)).unwrap(),
|
||||
}); // TODO: perhaps expose coeff_b through API?
|
||||
|
||||
if let Some(_) = x3b.sqrt() {
|
||||
// We know this is on the curve, but it's likely not going to be in the correct subgroup.
|
||||
x.c1.into_repr().write_be(&mut o.as_mut()[0..]).unwrap();
|
||||
x.c0.into_repr().write_be(&mut o.as_mut()[48..]).unwrap();
|
||||
o.as_mut()[0] |= 0b1000_0000;
|
||||
|
||||
if let Err(GroupDecodingError::NotInSubgroup) = o.into_affine() {
|
||||
break;
|
||||
} else {
|
||||
panic!(
|
||||
"should have rejected the point because it isn't in the correct subgroup"
|
||||
)
|
||||
}
|
||||
} else {
|
||||
x.add_assign(&Fq2::one());
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,758 @@
|
|||
// `clippy` is a code linting tool for improving code quality by catching
|
||||
// common mistakes or strange code patterns. If the `clippy` feature is
|
||||
// provided, it is enabled and all compiler warnings are prohibited.
|
||||
#![cfg_attr(feature = "clippy", deny(warnings))]
|
||||
#![cfg_attr(feature = "clippy", feature(plugin))]
|
||||
#![cfg_attr(feature = "clippy", plugin(clippy))]
|
||||
#![cfg_attr(feature = "clippy", allow(inline_always))]
|
||||
#![cfg_attr(feature = "clippy", allow(too_many_arguments))]
|
||||
#![cfg_attr(feature = "clippy", allow(unreadable_literal))]
|
||||
#![cfg_attr(feature = "clippy", allow(many_single_char_names))]
|
||||
#![cfg_attr(feature = "clippy", allow(new_without_default_derive))]
|
||||
#![cfg_attr(feature = "clippy", allow(write_literal))]
|
||||
// Force public structures to implement Debug
|
||||
#![deny(missing_debug_implementations)]
|
||||
|
||||
extern crate byteorder;
|
||||
extern crate rand;
|
||||
|
||||
#[cfg(test)]
|
||||
pub mod tests;
|
||||
|
||||
pub mod bls12_381;
|
||||
|
||||
mod wnaf;
|
||||
pub use self::wnaf::Wnaf;
|
||||
|
||||
use std::error::Error;
|
||||
use std::fmt;
|
||||
use std::io::{self, Read, Write};
|
||||
|
||||
/// An "engine" is a collection of types (fields, elliptic curve groups, etc.)
|
||||
/// with well-defined relationships. In particular, the G1/G2 curve groups are
|
||||
/// of prime order `r`, and are equipped with a bilinear pairing function.
|
||||
pub trait Engine: Sized + 'static + Clone {
|
||||
/// This is the scalar field of the G1/G2 groups.
|
||||
type Fr: PrimeField + SqrtField;
|
||||
|
||||
/// The projective representation of an element in G1.
|
||||
type G1: CurveProjective<
|
||||
Engine = Self,
|
||||
Base = Self::Fq,
|
||||
Scalar = Self::Fr,
|
||||
Affine = Self::G1Affine,
|
||||
>
|
||||
+ From<Self::G1Affine>;
|
||||
|
||||
/// The affine representation of an element in G1.
|
||||
type G1Affine: CurveAffine<
|
||||
Engine = Self,
|
||||
Base = Self::Fq,
|
||||
Scalar = Self::Fr,
|
||||
Projective = Self::G1,
|
||||
Pair = Self::G2Affine,
|
||||
PairingResult = Self::Fqk,
|
||||
>
|
||||
+ From<Self::G1>;
|
||||
|
||||
/// The projective representation of an element in G2.
|
||||
type G2: CurveProjective<
|
||||
Engine = Self,
|
||||
Base = Self::Fqe,
|
||||
Scalar = Self::Fr,
|
||||
Affine = Self::G2Affine,
|
||||
>
|
||||
+ From<Self::G2Affine>;
|
||||
|
||||
/// The affine representation of an element in G2.
|
||||
type G2Affine: CurveAffine<
|
||||
Engine = Self,
|
||||
Base = Self::Fqe,
|
||||
Scalar = Self::Fr,
|
||||
Projective = Self::G2,
|
||||
Pair = Self::G1Affine,
|
||||
PairingResult = Self::Fqk,
|
||||
>
|
||||
+ From<Self::G2>;
|
||||
|
||||
/// The base field that hosts G1.
|
||||
type Fq: PrimeField + SqrtField;
|
||||
|
||||
/// The extension field that hosts G2.
|
||||
type Fqe: SqrtField;
|
||||
|
||||
/// The extension field that hosts the target group of the pairing.
|
||||
type Fqk: Field;
|
||||
|
||||
/// Perform a miller loop with some number of (G1, G2) pairs.
|
||||
fn miller_loop<'a, I>(i: I) -> Self::Fqk
|
||||
where
|
||||
I: IntoIterator<
|
||||
Item = &'a (
|
||||
&'a <Self::G1Affine as CurveAffine>::Prepared,
|
||||
&'a <Self::G2Affine as CurveAffine>::Prepared,
|
||||
),
|
||||
>;
|
||||
|
||||
/// Perform final exponentiation of the result of a miller loop.
|
||||
fn final_exponentiation(&Self::Fqk) -> Option<Self::Fqk>;
|
||||
|
||||
/// Performs a complete pairing operation `(p, q)`.
|
||||
fn pairing<G1, G2>(p: G1, q: G2) -> Self::Fqk
|
||||
where
|
||||
G1: Into<Self::G1Affine>,
|
||||
G2: Into<Self::G2Affine>,
|
||||
{
|
||||
Self::final_exponentiation(&Self::miller_loop(
|
||||
[(&(p.into().prepare()), &(q.into().prepare()))].into_iter(),
|
||||
)).unwrap()
|
||||
}
|
||||
}
|
||||
|
||||
/// Projective representation of an elliptic curve point guaranteed to be
|
||||
/// in the correct prime order subgroup.
|
||||
pub trait CurveProjective:
|
||||
PartialEq
|
||||
+ Eq
|
||||
+ Sized
|
||||
+ Copy
|
||||
+ Clone
|
||||
+ Send
|
||||
+ Sync
|
||||
+ fmt::Debug
|
||||
+ fmt::Display
|
||||
+ rand::Rand
|
||||
+ 'static
|
||||
{
|
||||
type Engine: Engine<Fr = Self::Scalar>;
|
||||
type Scalar: PrimeField + SqrtField;
|
||||
type Base: SqrtField;
|
||||
type Affine: CurveAffine<Projective = Self, Scalar = Self::Scalar>;
|
||||
|
||||
/// Returns the additive identity.
|
||||
fn zero() -> Self;
|
||||
|
||||
/// Returns a fixed generator of unknown exponent.
|
||||
fn one() -> Self;
|
||||
|
||||
/// Determines if this point is the point at infinity.
|
||||
fn is_zero(&self) -> bool;
|
||||
|
||||
/// Normalizes a slice of projective elements so that
|
||||
/// conversion to affine is cheap.
|
||||
fn batch_normalization(v: &mut [Self]);
|
||||
|
||||
/// Checks if the point is already "normalized" so that
|
||||
/// cheap affine conversion is possible.
|
||||
fn is_normalized(&self) -> bool;
|
||||
|
||||
/// Doubles this element.
|
||||
fn double(&mut self);
|
||||
|
||||
/// Adds another element to this element.
|
||||
fn add_assign(&mut self, other: &Self);
|
||||
|
||||
/// Subtracts another element from this element.
|
||||
fn sub_assign(&mut self, other: &Self) {
|
||||
let mut tmp = *other;
|
||||
tmp.negate();
|
||||
self.add_assign(&tmp);
|
||||
}
|
||||
|
||||
/// Adds an affine element to this element.
|
||||
fn add_assign_mixed(&mut self, other: &Self::Affine);
|
||||
|
||||
/// Negates this element.
|
||||
fn negate(&mut self);
|
||||
|
||||
/// Performs scalar multiplication of this element.
|
||||
fn mul_assign<S: Into<<Self::Scalar as PrimeField>::Repr>>(&mut self, other: S);
|
||||
|
||||
/// Converts this element into its affine representation.
|
||||
fn into_affine(&self) -> Self::Affine;
|
||||
|
||||
/// Recommends a wNAF window table size given a scalar. Always returns a number
|
||||
/// between 2 and 22, inclusive.
|
||||
fn recommended_wnaf_for_scalar(scalar: <Self::Scalar as PrimeField>::Repr) -> usize;
|
||||
|
||||
/// Recommends a wNAF window size given the number of scalars you intend to multiply
|
||||
/// a base by. Always returns a number between 2 and 22, inclusive.
|
||||
fn recommended_wnaf_for_num_scalars(num_scalars: usize) -> usize;
|
||||
}
|
||||
|
||||
/// Affine representation of an elliptic curve point guaranteed to be
|
||||
/// in the correct prime order subgroup.
|
||||
pub trait CurveAffine:
|
||||
Copy + Clone + Sized + Send + Sync + fmt::Debug + fmt::Display + PartialEq + Eq + 'static
|
||||
{
|
||||
type Engine: Engine<Fr = Self::Scalar>;
|
||||
type Scalar: PrimeField + SqrtField;
|
||||
type Base: SqrtField;
|
||||
type Projective: CurveProjective<Affine = Self, Scalar = Self::Scalar>;
|
||||
type Prepared: Clone + Send + Sync + 'static;
|
||||
type Uncompressed: EncodedPoint<Affine = Self>;
|
||||
type Compressed: EncodedPoint<Affine = Self>;
|
||||
type Pair: CurveAffine<Pair = Self>;
|
||||
type PairingResult: Field;
|
||||
|
||||
/// Returns the additive identity.
|
||||
fn zero() -> Self;
|
||||
|
||||
/// Returns a fixed generator of unknown exponent.
|
||||
fn one() -> Self;
|
||||
|
||||
/// Determines if this point represents the point at infinity; the
|
||||
/// additive identity.
|
||||
fn is_zero(&self) -> bool;
|
||||
|
||||
/// Negates this element.
|
||||
fn negate(&mut self);
|
||||
|
||||
/// Performs scalar multiplication of this element with mixed addition.
|
||||
fn mul<S: Into<<Self::Scalar as PrimeField>::Repr>>(&self, other: S) -> Self::Projective;
|
||||
|
||||
/// Prepares this element for pairing purposes.
|
||||
fn prepare(&self) -> Self::Prepared;
|
||||
|
||||
/// Perform a pairing
|
||||
fn pairing_with(&self, other: &Self::Pair) -> Self::PairingResult;
|
||||
|
||||
/// Converts this element into its affine representation.
|
||||
fn into_projective(&self) -> Self::Projective;
|
||||
|
||||
/// Converts this element into its compressed encoding, so long as it's not
|
||||
/// the point at infinity.
|
||||
fn into_compressed(&self) -> Self::Compressed {
|
||||
<Self::Compressed as EncodedPoint>::from_affine(*self)
|
||||
}
|
||||
|
||||
/// Converts this element into its uncompressed encoding, so long as it's not
|
||||
/// the point at infinity.
|
||||
fn into_uncompressed(&self) -> Self::Uncompressed {
|
||||
<Self::Uncompressed as EncodedPoint>::from_affine(*self)
|
||||
}
|
||||
}
|
||||
|
||||
/// An encoded elliptic curve point, which should essentially wrap a `[u8; N]`.
|
||||
pub trait EncodedPoint:
|
||||
Sized + Send + Sync + AsRef<[u8]> + AsMut<[u8]> + Clone + Copy + 'static
|
||||
{
|
||||
type Affine: CurveAffine;
|
||||
|
||||
/// Creates an empty representation.
|
||||
fn empty() -> Self;
|
||||
|
||||
/// Returns the number of bytes consumed by this representation.
|
||||
fn size() -> usize;
|
||||
|
||||
/// Converts an `EncodedPoint` into a `CurveAffine` element,
|
||||
/// if the encoding represents a valid element.
|
||||
fn into_affine(&self) -> Result<Self::Affine, GroupDecodingError>;
|
||||
|
||||
/// Converts an `EncodedPoint` into a `CurveAffine` element,
|
||||
/// without guaranteeing that the encoding represents a valid
|
||||
/// element. This is useful when the caller knows the encoding is
|
||||
/// valid already.
|
||||
///
|
||||
/// If the encoding is invalid, this can break API invariants,
|
||||
/// so caution is strongly encouraged.
|
||||
fn into_affine_unchecked(&self) -> Result<Self::Affine, GroupDecodingError>;
|
||||
|
||||
/// Creates an `EncodedPoint` from an affine point, as long as the
|
||||
/// point is not the point at infinity.
|
||||
fn from_affine(affine: Self::Affine) -> Self;
|
||||
}
|
||||
|
||||
/// This trait represents an element of a field.
|
||||
pub trait Field:
|
||||
Sized + Eq + Copy + Clone + Send + Sync + fmt::Debug + fmt::Display + 'static + rand::Rand
|
||||
{
|
||||
/// Returns the zero element of the field, the additive identity.
|
||||
fn zero() -> Self;
|
||||
|
||||
/// Returns the one element of the field, the multiplicative identity.
|
||||
fn one() -> Self;
|
||||
|
||||
/// Returns true iff this element is zero.
|
||||
fn is_zero(&self) -> bool;
|
||||
|
||||
/// Squares this element.
|
||||
fn square(&mut self);
|
||||
|
||||
/// Doubles this element.
|
||||
fn double(&mut self);
|
||||
|
||||
/// Negates this element.
|
||||
fn negate(&mut self);
|
||||
|
||||
/// Adds another element to this element.
|
||||
fn add_assign(&mut self, other: &Self);
|
||||
|
||||
/// Subtracts another element from this element.
|
||||
fn sub_assign(&mut self, other: &Self);
|
||||
|
||||
/// Multiplies another element by this element.
|
||||
fn mul_assign(&mut self, other: &Self);
|
||||
|
||||
/// Computes the multiplicative inverse of this element, if nonzero.
|
||||
fn inverse(&self) -> Option<Self>;
|
||||
|
||||
/// Exponentiates this element by a power of the base prime modulus via
|
||||
/// the Frobenius automorphism.
|
||||
fn frobenius_map(&mut self, power: usize);
|
||||
|
||||
/// Exponentiates this element by a number represented with `u64` limbs,
|
||||
/// least significant digit first.
|
||||
fn pow<S: AsRef<[u64]>>(&self, exp: S) -> Self {
|
||||
let mut res = Self::one();
|
||||
|
||||
let mut found_one = false;
|
||||
|
||||
for i in BitIterator::new(exp) {
|
||||
if found_one {
|
||||
res.square();
|
||||
} else {
|
||||
found_one = i;
|
||||
}
|
||||
|
||||
if i {
|
||||
res.mul_assign(self);
|
||||
}
|
||||
}
|
||||
|
||||
res
|
||||
}
|
||||
}
|
||||
|
||||
/// This trait represents an element of a field that has a square root operation described for it.
|
||||
pub trait SqrtField: Field {
|
||||
/// Returns the Legendre symbol of the field element.
|
||||
fn legendre(&self) -> LegendreSymbol;
|
||||
|
||||
/// Returns the square root of the field element, if it is
|
||||
/// quadratic residue.
|
||||
fn sqrt(&self) -> Option<Self>;
|
||||
}
|
||||
|
||||
/// This trait represents a wrapper around a biginteger which can encode any element of a particular
|
||||
/// prime field. It is a smart wrapper around a sequence of `u64` limbs, least-significant digit
|
||||
/// first.
|
||||
pub trait PrimeFieldRepr:
|
||||
Sized
|
||||
+ Copy
|
||||
+ Clone
|
||||
+ Eq
|
||||
+ Ord
|
||||
+ Send
|
||||
+ Sync
|
||||
+ Default
|
||||
+ fmt::Debug
|
||||
+ fmt::Display
|
||||
+ 'static
|
||||
+ rand::Rand
|
||||
+ AsRef<[u64]>
|
||||
+ AsMut<[u64]>
|
||||
+ From<u64>
|
||||
{
|
||||
/// Subtract another represetation from this one.
|
||||
fn sub_noborrow(&mut self, other: &Self);
|
||||
|
||||
/// Add another representation to this one.
|
||||
fn add_nocarry(&mut self, other: &Self);
|
||||
|
||||
/// Compute the number of bits needed to encode this number. Always a
|
||||
/// multiple of 64.
|
||||
fn num_bits(&self) -> u32;
|
||||
|
||||
/// Returns true iff this number is zero.
|
||||
fn is_zero(&self) -> bool;
|
||||
|
||||
/// Returns true iff this number is odd.
|
||||
fn is_odd(&self) -> bool;
|
||||
|
||||
/// Returns true iff this number is even.
|
||||
fn is_even(&self) -> bool;
|
||||
|
||||
/// Performs a rightwise bitshift of this number, effectively dividing
|
||||
/// it by 2.
|
||||
fn div2(&mut self);
|
||||
|
||||
/// Performs a rightwise bitshift of this number by some amount.
|
||||
fn shr(&mut self, amt: u32);
|
||||
|
||||
/// Performs a leftwise bitshift of this number, effectively multiplying
|
||||
/// it by 2. Overflow is ignored.
|
||||
fn mul2(&mut self);
|
||||
|
||||
/// Performs a leftwise bitshift of this number by some amount.
|
||||
fn shl(&mut self, amt: u32);
|
||||
|
||||
/// Writes this `PrimeFieldRepr` as a big endian integer.
|
||||
fn write_be<W: Write>(&self, mut writer: W) -> io::Result<()> {
|
||||
use byteorder::{BigEndian, WriteBytesExt};
|
||||
|
||||
for digit in self.as_ref().iter().rev() {
|
||||
writer.write_u64::<BigEndian>(*digit)?;
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// Reads a big endian integer into this representation.
|
||||
fn read_be<R: Read>(&mut self, mut reader: R) -> io::Result<()> {
|
||||
use byteorder::{BigEndian, ReadBytesExt};
|
||||
|
||||
for digit in self.as_mut().iter_mut().rev() {
|
||||
*digit = reader.read_u64::<BigEndian>()?;
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// Writes this `PrimeFieldRepr` as a little endian integer.
|
||||
fn write_le<W: Write>(&self, mut writer: W) -> io::Result<()> {
|
||||
use byteorder::{LittleEndian, WriteBytesExt};
|
||||
|
||||
for digit in self.as_ref().iter() {
|
||||
writer.write_u64::<LittleEndian>(*digit)?;
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// Reads a little endian integer into this representation.
|
||||
fn read_le<R: Read>(&mut self, mut reader: R) -> io::Result<()> {
|
||||
use byteorder::{LittleEndian, ReadBytesExt};
|
||||
|
||||
for digit in self.as_mut().iter_mut() {
|
||||
*digit = reader.read_u64::<LittleEndian>()?;
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, PartialEq)]
|
||||
pub enum LegendreSymbol {
|
||||
Zero = 0,
|
||||
QuadraticResidue = 1,
|
||||
QuadraticNonResidue = -1,
|
||||
}
|
||||
|
||||
/// An error that may occur when trying to interpret a `PrimeFieldRepr` as a
|
||||
/// `PrimeField` element.
|
||||
#[derive(Debug)]
|
||||
pub enum PrimeFieldDecodingError {
|
||||
/// The encoded value is not in the field
|
||||
NotInField(String),
|
||||
}
|
||||
|
||||
impl Error for PrimeFieldDecodingError {
|
||||
fn description(&self) -> &str {
|
||||
match *self {
|
||||
PrimeFieldDecodingError::NotInField(..) => "not an element of the field",
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl fmt::Display for PrimeFieldDecodingError {
|
||||
fn fmt(&self, f: &mut fmt::Formatter) -> Result<(), fmt::Error> {
|
||||
match *self {
|
||||
PrimeFieldDecodingError::NotInField(ref repr) => {
|
||||
write!(f, "{} is not an element of the field", repr)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// An error that may occur when trying to decode an `EncodedPoint`.
|
||||
#[derive(Debug)]
|
||||
pub enum GroupDecodingError {
|
||||
/// The coordinate(s) do not lie on the curve.
|
||||
NotOnCurve,
|
||||
/// The element is not part of the r-order subgroup.
|
||||
NotInSubgroup,
|
||||
/// One of the coordinates could not be decoded
|
||||
CoordinateDecodingError(&'static str, PrimeFieldDecodingError),
|
||||
/// The compression mode of the encoded element was not as expected
|
||||
UnexpectedCompressionMode,
|
||||
/// The encoding contained bits that should not have been set
|
||||
UnexpectedInformation,
|
||||
}
|
||||
|
||||
impl Error for GroupDecodingError {
|
||||
fn description(&self) -> &str {
|
||||
match *self {
|
||||
GroupDecodingError::NotOnCurve => "coordinate(s) do not lie on the curve",
|
||||
GroupDecodingError::NotInSubgroup => "the element is not part of an r-order subgroup",
|
||||
GroupDecodingError::CoordinateDecodingError(..) => "coordinate(s) could not be decoded",
|
||||
GroupDecodingError::UnexpectedCompressionMode => {
|
||||
"encoding has unexpected compression mode"
|
||||
}
|
||||
GroupDecodingError::UnexpectedInformation => "encoding has unexpected information",
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl fmt::Display for GroupDecodingError {
|
||||
fn fmt(&self, f: &mut fmt::Formatter) -> Result<(), fmt::Error> {
|
||||
match *self {
|
||||
GroupDecodingError::CoordinateDecodingError(description, ref err) => {
|
||||
write!(f, "{} decoding error: {}", description, err)
|
||||
}
|
||||
_ => write!(f, "{}", self.description()),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// This represents an element of a prime field.
|
||||
pub trait PrimeField: Field {
|
||||
/// The prime field can be converted back and forth into this biginteger
|
||||
/// representation.
|
||||
type Repr: PrimeFieldRepr + From<Self>;
|
||||
|
||||
/// Interpret a string of numbers as a (congruent) prime field element.
|
||||
/// Does not accept unnecessary leading zeroes or a blank string.
|
||||
fn from_str(s: &str) -> Option<Self> {
|
||||
if s.is_empty() {
|
||||
return None;
|
||||
}
|
||||
|
||||
if s == "0" {
|
||||
return Some(Self::zero());
|
||||
}
|
||||
|
||||
let mut res = Self::zero();
|
||||
|
||||
let ten = Self::from_repr(Self::Repr::from(10)).unwrap();
|
||||
|
||||
let mut first_digit = true;
|
||||
|
||||
for c in s.chars() {
|
||||
match c.to_digit(10) {
|
||||
Some(c) => {
|
||||
if first_digit {
|
||||
if c == 0 {
|
||||
return None;
|
||||
}
|
||||
|
||||
first_digit = false;
|
||||
}
|
||||
|
||||
res.mul_assign(&ten);
|
||||
res.add_assign(&Self::from_repr(Self::Repr::from(u64::from(c))).unwrap());
|
||||
}
|
||||
None => {
|
||||
return None;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Some(res)
|
||||
}
|
||||
|
||||
/// Convert this prime field element into a biginteger representation.
|
||||
fn from_repr(Self::Repr) -> Result<Self, PrimeFieldDecodingError>;
|
||||
|
||||
/// Convert a biginteger representation into a prime field element, if
|
||||
/// the number is an element of the field.
|
||||
fn into_repr(&self) -> Self::Repr;
|
||||
|
||||
/// Returns the field characteristic; the modulus.
|
||||
fn char() -> Self::Repr;
|
||||
|
||||
/// How many bits are needed to represent an element of this field.
|
||||
const NUM_BITS: u32;
|
||||
|
||||
/// How many bits of information can be reliably stored in the field element.
|
||||
const CAPACITY: u32;
|
||||
|
||||
/// Returns the multiplicative generator of `char()` - 1 order. This element
|
||||
/// must also be quadratic nonresidue.
|
||||
fn multiplicative_generator() -> Self;
|
||||
|
||||
/// 2^s * t = `char()` - 1 with t odd.
|
||||
const S: u32;
|
||||
|
||||
/// Returns the 2^s root of unity computed by exponentiating the `multiplicative_generator()`
|
||||
/// by t.
|
||||
fn root_of_unity() -> Self;
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
pub struct BitIterator<E> {
|
||||
t: E,
|
||||
n: usize,
|
||||
}
|
||||
|
||||
impl<E: AsRef<[u64]>> BitIterator<E> {
|
||||
pub fn new(t: E) -> Self {
|
||||
let n = t.as_ref().len() * 64;
|
||||
|
||||
BitIterator { t, n }
|
||||
}
|
||||
}
|
||||
|
||||
impl<E: AsRef<[u64]>> Iterator for BitIterator<E> {
|
||||
type Item = bool;
|
||||
|
||||
fn next(&mut self) -> Option<bool> {
|
||||
if self.n == 0 {
|
||||
None
|
||||
} else {
|
||||
self.n -= 1;
|
||||
let part = self.n / 64;
|
||||
let bit = self.n - (64 * part);
|
||||
|
||||
Some(self.t.as_ref()[part] & (1 << bit) > 0)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_bit_iterator() {
|
||||
let mut a = BitIterator::new([0xa953d79b83f6ab59, 0x6dea2059e200bd39]);
|
||||
let expected = "01101101111010100010000001011001111000100000000010111101001110011010100101010011110101111001101110000011111101101010101101011001";
|
||||
|
||||
for e in expected.chars() {
|
||||
assert!(a.next().unwrap() == (e == '1'));
|
||||
}
|
||||
|
||||
assert!(a.next().is_none());
|
||||
|
||||
let expected = "1010010101111110101010000101101011101000011101110101001000011001100100100011011010001011011011010001011011101100110100111011010010110001000011110100110001100110011101101000101100011100100100100100001010011101010111110011101011000011101000111011011101011001";
|
||||
|
||||
let mut a = BitIterator::new([
|
||||
0x429d5f3ac3a3b759,
|
||||
0xb10f4c66768b1c92,
|
||||
0x92368b6d16ecd3b4,
|
||||
0xa57ea85ae8775219,
|
||||
]);
|
||||
|
||||
for e in expected.chars() {
|
||||
assert!(a.next().unwrap() == (e == '1'));
|
||||
}
|
||||
|
||||
assert!(a.next().is_none());
|
||||
}
|
||||
|
||||
#[cfg(not(feature = "expose-arith"))]
|
||||
use self::arith_impl::*;
|
||||
|
||||
#[cfg(feature = "expose-arith")]
|
||||
pub use self::arith_impl::*;
|
||||
|
||||
#[cfg(feature = "u128-support")]
|
||||
mod arith_impl {
|
||||
/// Calculate a - b - borrow, returning the result and modifying
|
||||
/// the borrow value.
|
||||
#[inline(always)]
|
||||
pub fn sbb(a: u64, b: u64, borrow: &mut u64) -> u64 {
|
||||
let tmp = (1u128 << 64) + u128::from(a) - u128::from(b) - u128::from(*borrow);
|
||||
|
||||
*borrow = if tmp >> 64 == 0 { 1 } else { 0 };
|
||||
|
||||
tmp as u64
|
||||
}
|
||||
|
||||
/// Calculate a + b + carry, returning the sum and modifying the
|
||||
/// carry value.
|
||||
#[inline(always)]
|
||||
pub fn adc(a: u64, b: u64, carry: &mut u64) -> u64 {
|
||||
let tmp = u128::from(a) + u128::from(b) + u128::from(*carry);
|
||||
|
||||
*carry = (tmp >> 64) as u64;
|
||||
|
||||
tmp as u64
|
||||
}
|
||||
|
||||
/// Calculate a + (b * c) + carry, returning the least significant digit
|
||||
/// and setting carry to the most significant digit.
|
||||
#[inline(always)]
|
||||
pub fn mac_with_carry(a: u64, b: u64, c: u64, carry: &mut u64) -> u64 {
|
||||
let tmp = (u128::from(a)) + u128::from(b) * u128::from(c) + u128::from(*carry);
|
||||
|
||||
*carry = (tmp >> 64) as u64;
|
||||
|
||||
tmp as u64
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(not(feature = "u128-support"))]
|
||||
mod arith_impl {
|
||||
#[inline(always)]
|
||||
fn split_u64(i: u64) -> (u64, u64) {
|
||||
(i >> 32, i & 0xFFFFFFFF)
|
||||
}
|
||||
|
||||
#[inline(always)]
|
||||
fn combine_u64(hi: u64, lo: u64) -> u64 {
|
||||
(hi << 32) | lo
|
||||
}
|
||||
|
||||
/// Calculate a - b - borrow, returning the result and modifying
|
||||
/// the borrow value.
|
||||
#[inline(always)]
|
||||
pub fn sbb(a: u64, b: u64, borrow: &mut u64) -> u64 {
|
||||
let (a_hi, a_lo) = split_u64(a);
|
||||
let (b_hi, b_lo) = split_u64(b);
|
||||
let (b, r0) = split_u64((1 << 32) + a_lo - b_lo - *borrow);
|
||||
let (b, r1) = split_u64((1 << 32) + a_hi - b_hi - ((b == 0) as u64));
|
||||
|
||||
*borrow = (b == 0) as u64;
|
||||
|
||||
combine_u64(r1, r0)
|
||||
}
|
||||
|
||||
/// Calculate a + b + carry, returning the sum and modifying the
|
||||
/// carry value.
|
||||
#[inline(always)]
|
||||
pub fn adc(a: u64, b: u64, carry: &mut u64) -> u64 {
|
||||
let (a_hi, a_lo) = split_u64(a);
|
||||
let (b_hi, b_lo) = split_u64(b);
|
||||
let (carry_hi, carry_lo) = split_u64(*carry);
|
||||
|
||||
let (t, r0) = split_u64(a_lo + b_lo + carry_lo);
|
||||
let (t, r1) = split_u64(t + a_hi + b_hi + carry_hi);
|
||||
|
||||
*carry = t;
|
||||
|
||||
combine_u64(r1, r0)
|
||||
}
|
||||
|
||||
/// Calculate a + (b * c) + carry, returning the least significant digit
|
||||
/// and setting carry to the most significant digit.
|
||||
#[inline(always)]
|
||||
pub fn mac_with_carry(a: u64, b: u64, c: u64, carry: &mut u64) -> u64 {
|
||||
/*
|
||||
[ b_hi | b_lo ]
|
||||
[ c_hi | c_lo ] *
|
||||
-------------------------------------------
|
||||
[ b_lo * c_lo ] <-- w
|
||||
[ b_hi * c_lo ] <-- x
|
||||
[ b_lo * c_hi ] <-- y
|
||||
[ b_hi * c_lo ] <-- z
|
||||
[ a_hi | a_lo ]
|
||||
[ C_hi | C_lo ]
|
||||
*/
|
||||
|
||||
let (a_hi, a_lo) = split_u64(a);
|
||||
let (b_hi, b_lo) = split_u64(b);
|
||||
let (c_hi, c_lo) = split_u64(c);
|
||||
let (carry_hi, carry_lo) = split_u64(*carry);
|
||||
|
||||
let (w_hi, w_lo) = split_u64(b_lo * c_lo);
|
||||
let (x_hi, x_lo) = split_u64(b_hi * c_lo);
|
||||
let (y_hi, y_lo) = split_u64(b_lo * c_hi);
|
||||
let (z_hi, z_lo) = split_u64(b_hi * c_hi);
|
||||
|
||||
let (t, r0) = split_u64(w_lo + a_lo + carry_lo);
|
||||
let (t, r1) = split_u64(t + w_hi + x_lo + y_lo + a_hi + carry_hi);
|
||||
let (t, r2) = split_u64(t + x_hi + y_hi + z_lo);
|
||||
let (_, r3) = split_u64(t + z_hi);
|
||||
|
||||
*carry = combine_u64(r3, r2);
|
||||
|
||||
combine_u64(r1, r0)
|
||||
}
|
||||
}
|
|
@ -0,0 +1,420 @@
|
|||
use rand::{Rand, Rng, SeedableRng, XorShiftRng};
|
||||
|
||||
use {CurveAffine, CurveProjective, EncodedPoint, Field};
|
||||
|
||||
pub fn curve_tests<G: CurveProjective>() {
|
||||
let mut rng = XorShiftRng::from_seed([0x5dbe6259, 0x8d313d76, 0x3237db17, 0xe5bc0654]);
|
||||
|
||||
// Negation edge case with zero.
|
||||
{
|
||||
let mut z = G::zero();
|
||||
z.negate();
|
||||
assert!(z.is_zero());
|
||||
}
|
||||
|
||||
// Doubling edge case with zero.
|
||||
{
|
||||
let mut z = G::zero();
|
||||
z.double();
|
||||
assert!(z.is_zero());
|
||||
}
|
||||
|
||||
// Addition edge cases with zero
|
||||
{
|
||||
let mut r = G::rand(&mut rng);
|
||||
let rcopy = r;
|
||||
r.add_assign(&G::zero());
|
||||
assert_eq!(r, rcopy);
|
||||
r.add_assign_mixed(&G::Affine::zero());
|
||||
assert_eq!(r, rcopy);
|
||||
|
||||
let mut z = G::zero();
|
||||
z.add_assign(&G::zero());
|
||||
assert!(z.is_zero());
|
||||
z.add_assign_mixed(&G::Affine::zero());
|
||||
assert!(z.is_zero());
|
||||
|
||||
let mut z2 = z;
|
||||
z2.add_assign(&r);
|
||||
|
||||
z.add_assign_mixed(&r.into_affine());
|
||||
|
||||
assert_eq!(z, z2);
|
||||
assert_eq!(z, r);
|
||||
}
|
||||
|
||||
// Transformations
|
||||
{
|
||||
let a = G::rand(&mut rng);
|
||||
let b = a.into_affine().into_projective();
|
||||
let c = a
|
||||
.into_affine()
|
||||
.into_projective()
|
||||
.into_affine()
|
||||
.into_projective();
|
||||
assert_eq!(a, b);
|
||||
assert_eq!(b, c);
|
||||
}
|
||||
|
||||
random_addition_tests::<G>();
|
||||
random_multiplication_tests::<G>();
|
||||
random_doubling_tests::<G>();
|
||||
random_negation_tests::<G>();
|
||||
random_transformation_tests::<G>();
|
||||
random_wnaf_tests::<G>();
|
||||
random_encoding_tests::<G::Affine>();
|
||||
}
|
||||
|
||||
fn random_wnaf_tests<G: CurveProjective>() {
|
||||
use wnaf::*;
|
||||
use PrimeField;
|
||||
|
||||
let mut rng = XorShiftRng::from_seed([0x5dbe6259, 0x8d313d76, 0x3237db17, 0xe5bc0654]);
|
||||
|
||||
{
|
||||
let mut table = vec![];
|
||||
let mut wnaf = vec![];
|
||||
|
||||
for w in 2..14 {
|
||||
for _ in 0..100 {
|
||||
let g = G::rand(&mut rng);
|
||||
let s = G::Scalar::rand(&mut rng).into_repr();
|
||||
let mut g1 = g;
|
||||
g1.mul_assign(s);
|
||||
|
||||
wnaf_table(&mut table, g, w);
|
||||
wnaf_form(&mut wnaf, s, w);
|
||||
let g2 = wnaf_exp(&table, &wnaf);
|
||||
|
||||
assert_eq!(g1, g2);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
{
|
||||
fn only_compiles_if_send<S: Send>(_: &S) {}
|
||||
|
||||
for _ in 0..100 {
|
||||
let g = G::rand(&mut rng);
|
||||
let s = G::Scalar::rand(&mut rng).into_repr();
|
||||
let mut g1 = g;
|
||||
g1.mul_assign(s);
|
||||
|
||||
let g2 = {
|
||||
let mut wnaf = Wnaf::new();
|
||||
wnaf.base(g, 1).scalar(s)
|
||||
};
|
||||
let g3 = {
|
||||
let mut wnaf = Wnaf::new();
|
||||
wnaf.scalar(s).base(g)
|
||||
};
|
||||
let g4 = {
|
||||
let mut wnaf = Wnaf::new();
|
||||
let mut shared = wnaf.base(g, 1).shared();
|
||||
|
||||
only_compiles_if_send(&shared);
|
||||
|
||||
shared.scalar(s)
|
||||
};
|
||||
let g5 = {
|
||||
let mut wnaf = Wnaf::new();
|
||||
let mut shared = wnaf.scalar(s).shared();
|
||||
|
||||
only_compiles_if_send(&shared);
|
||||
|
||||
shared.base(g)
|
||||
};
|
||||
|
||||
let g6 = {
|
||||
let mut wnaf = Wnaf::new();
|
||||
{
|
||||
// Populate the vectors.
|
||||
wnaf.base(rng.gen(), 1).scalar(rng.gen());
|
||||
}
|
||||
wnaf.base(g, 1).scalar(s)
|
||||
};
|
||||
let g7 = {
|
||||
let mut wnaf = Wnaf::new();
|
||||
{
|
||||
// Populate the vectors.
|
||||
wnaf.base(rng.gen(), 1).scalar(rng.gen());
|
||||
}
|
||||
wnaf.scalar(s).base(g)
|
||||
};
|
||||
let g8 = {
|
||||
let mut wnaf = Wnaf::new();
|
||||
{
|
||||
// Populate the vectors.
|
||||
wnaf.base(rng.gen(), 1).scalar(rng.gen());
|
||||
}
|
||||
let mut shared = wnaf.base(g, 1).shared();
|
||||
|
||||
only_compiles_if_send(&shared);
|
||||
|
||||
shared.scalar(s)
|
||||
};
|
||||
let g9 = {
|
||||
let mut wnaf = Wnaf::new();
|
||||
{
|
||||
// Populate the vectors.
|
||||
wnaf.base(rng.gen(), 1).scalar(rng.gen());
|
||||
}
|
||||
let mut shared = wnaf.scalar(s).shared();
|
||||
|
||||
only_compiles_if_send(&shared);
|
||||
|
||||
shared.base(g)
|
||||
};
|
||||
|
||||
assert_eq!(g1, g2);
|
||||
assert_eq!(g1, g3);
|
||||
assert_eq!(g1, g4);
|
||||
assert_eq!(g1, g5);
|
||||
assert_eq!(g1, g6);
|
||||
assert_eq!(g1, g7);
|
||||
assert_eq!(g1, g8);
|
||||
assert_eq!(g1, g9);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn random_negation_tests<G: CurveProjective>() {
|
||||
let mut rng = XorShiftRng::from_seed([0x5dbe6259, 0x8d313d76, 0x3237db17, 0xe5bc0654]);
|
||||
|
||||
for _ in 0..1000 {
|
||||
let r = G::rand(&mut rng);
|
||||
|
||||
let s = G::Scalar::rand(&mut rng);
|
||||
let mut sneg = s;
|
||||
sneg.negate();
|
||||
|
||||
let mut t1 = r;
|
||||
t1.mul_assign(s);
|
||||
|
||||
let mut t2 = r;
|
||||
t2.mul_assign(sneg);
|
||||
|
||||
let mut t3 = t1;
|
||||
t3.add_assign(&t2);
|
||||
assert!(t3.is_zero());
|
||||
|
||||
let mut t4 = t1;
|
||||
t4.add_assign_mixed(&t2.into_affine());
|
||||
assert!(t4.is_zero());
|
||||
|
||||
t1.negate();
|
||||
assert_eq!(t1, t2);
|
||||
}
|
||||
}
|
||||
|
||||
fn random_doubling_tests<G: CurveProjective>() {
|
||||
let mut rng = XorShiftRng::from_seed([0x5dbe6259, 0x8d313d76, 0x3237db17, 0xe5bc0654]);
|
||||
|
||||
for _ in 0..1000 {
|
||||
let mut a = G::rand(&mut rng);
|
||||
let mut b = G::rand(&mut rng);
|
||||
|
||||
// 2(a + b)
|
||||
let mut tmp1 = a;
|
||||
tmp1.add_assign(&b);
|
||||
tmp1.double();
|
||||
|
||||
// 2a + 2b
|
||||
a.double();
|
||||
b.double();
|
||||
|
||||
let mut tmp2 = a;
|
||||
tmp2.add_assign(&b);
|
||||
|
||||
let mut tmp3 = a;
|
||||
tmp3.add_assign_mixed(&b.into_affine());
|
||||
|
||||
assert_eq!(tmp1, tmp2);
|
||||
assert_eq!(tmp1, tmp3);
|
||||
}
|
||||
}
|
||||
|
||||
fn random_multiplication_tests<G: CurveProjective>() {
|
||||
let mut rng = XorShiftRng::from_seed([0x5dbe6259, 0x8d313d76, 0x3237db17, 0xe5bc0654]);
|
||||
|
||||
for _ in 0..1000 {
|
||||
let mut a = G::rand(&mut rng);
|
||||
let mut b = G::rand(&mut rng);
|
||||
let a_affine = a.into_affine();
|
||||
let b_affine = b.into_affine();
|
||||
|
||||
let s = G::Scalar::rand(&mut rng);
|
||||
|
||||
// s ( a + b )
|
||||
let mut tmp1 = a;
|
||||
tmp1.add_assign(&b);
|
||||
tmp1.mul_assign(s);
|
||||
|
||||
// sa + sb
|
||||
a.mul_assign(s);
|
||||
b.mul_assign(s);
|
||||
|
||||
let mut tmp2 = a;
|
||||
tmp2.add_assign(&b);
|
||||
|
||||
// Affine multiplication
|
||||
let mut tmp3 = a_affine.mul(s);
|
||||
tmp3.add_assign(&b_affine.mul(s));
|
||||
|
||||
assert_eq!(tmp1, tmp2);
|
||||
assert_eq!(tmp1, tmp3);
|
||||
}
|
||||
}
|
||||
|
||||
fn random_addition_tests<G: CurveProjective>() {
|
||||
let mut rng = XorShiftRng::from_seed([0x5dbe6259, 0x8d313d76, 0x3237db17, 0xe5bc0654]);
|
||||
|
||||
for _ in 0..1000 {
|
||||
let a = G::rand(&mut rng);
|
||||
let b = G::rand(&mut rng);
|
||||
let c = G::rand(&mut rng);
|
||||
let a_affine = a.into_affine();
|
||||
let b_affine = b.into_affine();
|
||||
let c_affine = c.into_affine();
|
||||
|
||||
// a + a should equal the doubling
|
||||
{
|
||||
let mut aplusa = a;
|
||||
aplusa.add_assign(&a);
|
||||
|
||||
let mut aplusamixed = a;
|
||||
aplusamixed.add_assign_mixed(&a.into_affine());
|
||||
|
||||
let mut adouble = a;
|
||||
adouble.double();
|
||||
|
||||
assert_eq!(aplusa, adouble);
|
||||
assert_eq!(aplusa, aplusamixed);
|
||||
}
|
||||
|
||||
let mut tmp = vec![G::zero(); 6];
|
||||
|
||||
// (a + b) + c
|
||||
tmp[0] = a;
|
||||
tmp[0].add_assign(&b);
|
||||
tmp[0].add_assign(&c);
|
||||
|
||||
// a + (b + c)
|
||||
tmp[1] = b;
|
||||
tmp[1].add_assign(&c);
|
||||
tmp[1].add_assign(&a);
|
||||
|
||||
// (a + c) + b
|
||||
tmp[2] = a;
|
||||
tmp[2].add_assign(&c);
|
||||
tmp[2].add_assign(&b);
|
||||
|
||||
// Mixed addition
|
||||
|
||||
// (a + b) + c
|
||||
tmp[3] = a_affine.into_projective();
|
||||
tmp[3].add_assign_mixed(&b_affine);
|
||||
tmp[3].add_assign_mixed(&c_affine);
|
||||
|
||||
// a + (b + c)
|
||||
tmp[4] = b_affine.into_projective();
|
||||
tmp[4].add_assign_mixed(&c_affine);
|
||||
tmp[4].add_assign_mixed(&a_affine);
|
||||
|
||||
// (a + c) + b
|
||||
tmp[5] = a_affine.into_projective();
|
||||
tmp[5].add_assign_mixed(&c_affine);
|
||||
tmp[5].add_assign_mixed(&b_affine);
|
||||
|
||||
// Comparisons
|
||||
for i in 0..6 {
|
||||
for j in 0..6 {
|
||||
assert_eq!(tmp[i], tmp[j]);
|
||||
assert_eq!(tmp[i].into_affine(), tmp[j].into_affine());
|
||||
}
|
||||
|
||||
assert!(tmp[i] != a);
|
||||
assert!(tmp[i] != b);
|
||||
assert!(tmp[i] != c);
|
||||
|
||||
assert!(a != tmp[i]);
|
||||
assert!(b != tmp[i]);
|
||||
assert!(c != tmp[i]);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn random_transformation_tests<G: CurveProjective>() {
|
||||
let mut rng = XorShiftRng::from_seed([0x5dbe6259, 0x8d313d76, 0x3237db17, 0xe5bc0654]);
|
||||
|
||||
for _ in 0..1000 {
|
||||
let g = G::rand(&mut rng);
|
||||
let g_affine = g.into_affine();
|
||||
let g_projective = g_affine.into_projective();
|
||||
assert_eq!(g, g_projective);
|
||||
}
|
||||
|
||||
// Batch normalization
|
||||
for _ in 0..10 {
|
||||
let mut v = (0..1000).map(|_| G::rand(&mut rng)).collect::<Vec<_>>();
|
||||
|
||||
for i in &v {
|
||||
assert!(!i.is_normalized());
|
||||
}
|
||||
|
||||
use rand::distributions::{IndependentSample, Range};
|
||||
let between = Range::new(0, 1000);
|
||||
// Sprinkle in some normalized points
|
||||
for _ in 0..5 {
|
||||
v[between.ind_sample(&mut rng)] = G::zero();
|
||||
}
|
||||
for _ in 0..5 {
|
||||
let s = between.ind_sample(&mut rng);
|
||||
v[s] = v[s].into_affine().into_projective();
|
||||
}
|
||||
|
||||
let expected_v = v
|
||||
.iter()
|
||||
.map(|v| v.into_affine().into_projective())
|
||||
.collect::<Vec<_>>();
|
||||
G::batch_normalization(&mut v);
|
||||
|
||||
for i in &v {
|
||||
assert!(i.is_normalized());
|
||||
}
|
||||
|
||||
assert_eq!(v, expected_v);
|
||||
}
|
||||
}
|
||||
|
||||
fn random_encoding_tests<G: CurveAffine>() {
|
||||
let mut rng = XorShiftRng::from_seed([0x5dbe6259, 0x8d313d76, 0x3237db17, 0xe5bc0654]);
|
||||
|
||||
assert_eq!(
|
||||
G::zero().into_uncompressed().into_affine().unwrap(),
|
||||
G::zero()
|
||||
);
|
||||
|
||||
assert_eq!(
|
||||
G::zero().into_compressed().into_affine().unwrap(),
|
||||
G::zero()
|
||||
);
|
||||
|
||||
for _ in 0..1000 {
|
||||
let mut r = G::Projective::rand(&mut rng).into_affine();
|
||||
|
||||
let uncompressed = r.into_uncompressed();
|
||||
let de_uncompressed = uncompressed.into_affine().unwrap();
|
||||
assert_eq!(de_uncompressed, r);
|
||||
|
||||
let compressed = r.into_compressed();
|
||||
let de_compressed = compressed.into_affine().unwrap();
|
||||
assert_eq!(de_compressed, r);
|
||||
|
||||
r.negate();
|
||||
|
||||
let compressed = r.into_compressed();
|
||||
let de_compressed = compressed.into_affine().unwrap();
|
||||
assert_eq!(de_compressed, r);
|
||||
}
|
||||
}
|
|
@ -0,0 +1,126 @@
|
|||
use rand::{Rand, SeedableRng, XorShiftRng};
|
||||
|
||||
use {CurveAffine, CurveProjective, Engine, Field, PrimeField};
|
||||
|
||||
pub fn engine_tests<E: Engine>() {
|
||||
let mut rng = XorShiftRng::from_seed([0x5dbe6259, 0x8d313d76, 0x3237db17, 0xe5bc0654]);
|
||||
|
||||
for _ in 0..10 {
|
||||
let a = E::G1::rand(&mut rng).into_affine();
|
||||
let b = E::G2::rand(&mut rng).into_affine();
|
||||
|
||||
assert!(a.pairing_with(&b) == b.pairing_with(&a));
|
||||
assert!(a.pairing_with(&b) == E::pairing(a, b));
|
||||
}
|
||||
|
||||
for _ in 0..1000 {
|
||||
let z1 = E::G1Affine::zero().prepare();
|
||||
let z2 = E::G2Affine::zero().prepare();
|
||||
|
||||
let a = E::G1::rand(&mut rng).into_affine().prepare();
|
||||
let b = E::G2::rand(&mut rng).into_affine().prepare();
|
||||
let c = E::G1::rand(&mut rng).into_affine().prepare();
|
||||
let d = E::G2::rand(&mut rng).into_affine().prepare();
|
||||
|
||||
assert_eq!(
|
||||
E::Fqk::one(),
|
||||
E::final_exponentiation(&E::miller_loop(&[(&z1, &b)])).unwrap()
|
||||
);
|
||||
|
||||
assert_eq!(
|
||||
E::Fqk::one(),
|
||||
E::final_exponentiation(&E::miller_loop(&[(&a, &z2)])).unwrap()
|
||||
);
|
||||
|
||||
assert_eq!(
|
||||
E::final_exponentiation(&E::miller_loop(&[(&z1, &b), (&c, &d)])).unwrap(),
|
||||
E::final_exponentiation(&E::miller_loop(&[(&a, &z2), (&c, &d)])).unwrap()
|
||||
);
|
||||
|
||||
assert_eq!(
|
||||
E::final_exponentiation(&E::miller_loop(&[(&a, &b), (&z1, &d)])).unwrap(),
|
||||
E::final_exponentiation(&E::miller_loop(&[(&a, &b), (&c, &z2)])).unwrap()
|
||||
);
|
||||
}
|
||||
|
||||
random_bilinearity_tests::<E>();
|
||||
random_miller_loop_tests::<E>();
|
||||
}
|
||||
|
||||
fn random_miller_loop_tests<E: Engine>() {
|
||||
let mut rng = XorShiftRng::from_seed([0x5dbe6259, 0x8d313d76, 0x3237db17, 0xe5bc0654]);
|
||||
|
||||
// Exercise the miller loop for a reduced pairing
|
||||
for _ in 0..1000 {
|
||||
let a = E::G1::rand(&mut rng);
|
||||
let b = E::G2::rand(&mut rng);
|
||||
|
||||
let p2 = E::pairing(a, b);
|
||||
|
||||
let a = a.into_affine().prepare();
|
||||
let b = b.into_affine().prepare();
|
||||
|
||||
let p1 = E::final_exponentiation(&E::miller_loop(&[(&a, &b)])).unwrap();
|
||||
|
||||
assert_eq!(p1, p2);
|
||||
}
|
||||
|
||||
// Exercise a double miller loop
|
||||
for _ in 0..1000 {
|
||||
let a = E::G1::rand(&mut rng);
|
||||
let b = E::G2::rand(&mut rng);
|
||||
let c = E::G1::rand(&mut rng);
|
||||
let d = E::G2::rand(&mut rng);
|
||||
|
||||
let ab = E::pairing(a, b);
|
||||
let cd = E::pairing(c, d);
|
||||
|
||||
let mut abcd = ab;
|
||||
abcd.mul_assign(&cd);
|
||||
|
||||
let a = a.into_affine().prepare();
|
||||
let b = b.into_affine().prepare();
|
||||
let c = c.into_affine().prepare();
|
||||
let d = d.into_affine().prepare();
|
||||
|
||||
let abcd_with_double_loop =
|
||||
E::final_exponentiation(&E::miller_loop(&[(&a, &b), (&c, &d)])).unwrap();
|
||||
|
||||
assert_eq!(abcd, abcd_with_double_loop);
|
||||
}
|
||||
}
|
||||
|
||||
fn random_bilinearity_tests<E: Engine>() {
|
||||
let mut rng = XorShiftRng::from_seed([0x5dbe6259, 0x8d313d76, 0x3237db17, 0xe5bc0654]);
|
||||
|
||||
for _ in 0..1000 {
|
||||
let a = E::G1::rand(&mut rng);
|
||||
let b = E::G2::rand(&mut rng);
|
||||
|
||||
let c = E::Fr::rand(&mut rng);
|
||||
let d = E::Fr::rand(&mut rng);
|
||||
|
||||
let mut ac = a;
|
||||
ac.mul_assign(c);
|
||||
|
||||
let mut ad = a;
|
||||
ad.mul_assign(d);
|
||||
|
||||
let mut bc = b;
|
||||
bc.mul_assign(c);
|
||||
|
||||
let mut bd = b;
|
||||
bd.mul_assign(d);
|
||||
|
||||
let acbd = E::pairing(ac, bd);
|
||||
let adbc = E::pairing(ad, bc);
|
||||
|
||||
let mut cd = c;
|
||||
cd.mul_assign(&d);
|
||||
|
||||
let abcd = E::pairing(a, b).pow(cd.into_repr());
|
||||
|
||||
assert_eq!(acbd, adbc);
|
||||
assert_eq!(acbd, abcd);
|
||||
}
|
||||
}
|
|
@ -0,0 +1,266 @@
|
|||
use rand::{Rng, SeedableRng, XorShiftRng};
|
||||
use {Field, LegendreSymbol, PrimeField, SqrtField};
|
||||
|
||||
pub fn random_frobenius_tests<F: Field, C: AsRef<[u64]>>(characteristic: C, maxpower: usize) {
|
||||
let mut rng = XorShiftRng::from_seed([0x5dbe6259, 0x8d313d76, 0x3237db17, 0xe5bc0654]);
|
||||
|
||||
for _ in 0..100 {
|
||||
for i in 0..(maxpower + 1) {
|
||||
let mut a = F::rand(&mut rng);
|
||||
let mut b = a;
|
||||
|
||||
for _ in 0..i {
|
||||
a = a.pow(&characteristic);
|
||||
}
|
||||
b.frobenius_map(i);
|
||||
|
||||
assert_eq!(a, b);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub fn random_sqrt_tests<F: SqrtField>() {
|
||||
let mut rng = XorShiftRng::from_seed([0x5dbe6259, 0x8d313d76, 0x3237db17, 0xe5bc0654]);
|
||||
|
||||
for _ in 0..10000 {
|
||||
let a = F::rand(&mut rng);
|
||||
let mut b = a;
|
||||
b.square();
|
||||
assert_eq!(b.legendre(), LegendreSymbol::QuadraticResidue);
|
||||
|
||||
let b = b.sqrt().unwrap();
|
||||
let mut negb = b;
|
||||
negb.negate();
|
||||
|
||||
assert!(a == b || a == negb);
|
||||
}
|
||||
|
||||
let mut c = F::one();
|
||||
for _ in 0..10000 {
|
||||
let mut b = c;
|
||||
b.square();
|
||||
assert_eq!(b.legendre(), LegendreSymbol::QuadraticResidue);
|
||||
|
||||
b = b.sqrt().unwrap();
|
||||
|
||||
if b != c {
|
||||
b.negate();
|
||||
}
|
||||
|
||||
assert_eq!(b, c);
|
||||
|
||||
c.add_assign(&F::one());
|
||||
}
|
||||
}
|
||||
|
||||
pub fn random_field_tests<F: Field>() {
|
||||
let mut rng = XorShiftRng::from_seed([0x5dbe6259, 0x8d313d76, 0x3237db17, 0xe5bc0654]);
|
||||
|
||||
random_multiplication_tests::<F, _>(&mut rng);
|
||||
random_addition_tests::<F, _>(&mut rng);
|
||||
random_subtraction_tests::<F, _>(&mut rng);
|
||||
random_negation_tests::<F, _>(&mut rng);
|
||||
random_doubling_tests::<F, _>(&mut rng);
|
||||
random_squaring_tests::<F, _>(&mut rng);
|
||||
random_inversion_tests::<F, _>(&mut rng);
|
||||
random_expansion_tests::<F, _>(&mut rng);
|
||||
|
||||
assert!(F::zero().is_zero());
|
||||
{
|
||||
let mut z = F::zero();
|
||||
z.negate();
|
||||
assert!(z.is_zero());
|
||||
}
|
||||
|
||||
assert!(F::zero().inverse().is_none());
|
||||
|
||||
// Multiplication by zero
|
||||
{
|
||||
let mut a = F::rand(&mut rng);
|
||||
a.mul_assign(&F::zero());
|
||||
assert!(a.is_zero());
|
||||
}
|
||||
|
||||
// Addition by zero
|
||||
{
|
||||
let mut a = F::rand(&mut rng);
|
||||
let copy = a;
|
||||
a.add_assign(&F::zero());
|
||||
assert_eq!(a, copy);
|
||||
}
|
||||
}
|
||||
|
||||
pub fn from_str_tests<F: PrimeField>() {
|
||||
{
|
||||
let a = "84395729384759238745923745892374598234705297301958723458712394587103249587213984572934750213947582345792304758273458972349582734958273495872304598234";
|
||||
let b = "38495729084572938457298347502349857029384609283450692834058293405982304598230458230495820394850293845098234059823049582309485203948502938452093482039";
|
||||
let c = "3248875134290623212325429203829831876024364170316860259933542844758450336418538569901990710701240661702808867062612075657861768196242274635305077449545396068598317421057721935408562373834079015873933065667961469731886739181625866970316226171512545167081793907058686908697431878454091011239990119126";
|
||||
|
||||
let mut a = F::from_str(a).unwrap();
|
||||
let b = F::from_str(b).unwrap();
|
||||
let c = F::from_str(c).unwrap();
|
||||
|
||||
a.mul_assign(&b);
|
||||
|
||||
assert_eq!(a, c);
|
||||
}
|
||||
|
||||
{
|
||||
let mut rng = XorShiftRng::from_seed([0x5dbe6259, 0x8d313d76, 0x3237db17, 0xe5bc0654]);
|
||||
|
||||
for _ in 0..1000 {
|
||||
let n: u64 = rng.gen();
|
||||
|
||||
let a = F::from_str(&format!("{}", n)).unwrap();
|
||||
let b = F::from_repr(n.into()).unwrap();
|
||||
|
||||
assert_eq!(a, b);
|
||||
}
|
||||
}
|
||||
|
||||
assert!(F::from_str("").is_none());
|
||||
assert!(F::from_str("0").unwrap().is_zero());
|
||||
assert!(F::from_str("00").is_none());
|
||||
assert!(F::from_str("00000000000").is_none());
|
||||
}
|
||||
|
||||
fn random_multiplication_tests<F: Field, R: Rng>(rng: &mut R) {
|
||||
for _ in 0..10000 {
|
||||
let a = F::rand(rng);
|
||||
let b = F::rand(rng);
|
||||
let c = F::rand(rng);
|
||||
|
||||
let mut t0 = a; // (a * b) * c
|
||||
t0.mul_assign(&b);
|
||||
t0.mul_assign(&c);
|
||||
|
||||
let mut t1 = a; // (a * c) * b
|
||||
t1.mul_assign(&c);
|
||||
t1.mul_assign(&b);
|
||||
|
||||
let mut t2 = b; // (b * c) * a
|
||||
t2.mul_assign(&c);
|
||||
t2.mul_assign(&a);
|
||||
|
||||
assert_eq!(t0, t1);
|
||||
assert_eq!(t1, t2);
|
||||
}
|
||||
}
|
||||
|
||||
fn random_addition_tests<F: Field, R: Rng>(rng: &mut R) {
|
||||
for _ in 0..10000 {
|
||||
let a = F::rand(rng);
|
||||
let b = F::rand(rng);
|
||||
let c = F::rand(rng);
|
||||
|
||||
let mut t0 = a; // (a + b) + c
|
||||
t0.add_assign(&b);
|
||||
t0.add_assign(&c);
|
||||
|
||||
let mut t1 = a; // (a + c) + b
|
||||
t1.add_assign(&c);
|
||||
t1.add_assign(&b);
|
||||
|
||||
let mut t2 = b; // (b + c) + a
|
||||
t2.add_assign(&c);
|
||||
t2.add_assign(&a);
|
||||
|
||||
assert_eq!(t0, t1);
|
||||
assert_eq!(t1, t2);
|
||||
}
|
||||
}
|
||||
|
||||
fn random_subtraction_tests<F: Field, R: Rng>(rng: &mut R) {
|
||||
for _ in 0..10000 {
|
||||
let a = F::rand(rng);
|
||||
let b = F::rand(rng);
|
||||
|
||||
let mut t0 = a; // (a - b)
|
||||
t0.sub_assign(&b);
|
||||
|
||||
let mut t1 = b; // (b - a)
|
||||
t1.sub_assign(&a);
|
||||
|
||||
let mut t2 = t0; // (a - b) + (b - a) = 0
|
||||
t2.add_assign(&t1);
|
||||
|
||||
assert!(t2.is_zero());
|
||||
}
|
||||
}
|
||||
|
||||
fn random_negation_tests<F: Field, R: Rng>(rng: &mut R) {
|
||||
for _ in 0..10000 {
|
||||
let a = F::rand(rng);
|
||||
let mut b = a;
|
||||
b.negate();
|
||||
b.add_assign(&a);
|
||||
|
||||
assert!(b.is_zero());
|
||||
}
|
||||
}
|
||||
|
||||
fn random_doubling_tests<F: Field, R: Rng>(rng: &mut R) {
|
||||
for _ in 0..10000 {
|
||||
let mut a = F::rand(rng);
|
||||
let mut b = a;
|
||||
a.add_assign(&b);
|
||||
b.double();
|
||||
|
||||
assert_eq!(a, b);
|
||||
}
|
||||
}
|
||||
|
||||
fn random_squaring_tests<F: Field, R: Rng>(rng: &mut R) {
|
||||
for _ in 0..10000 {
|
||||
let mut a = F::rand(rng);
|
||||
let mut b = a;
|
||||
a.mul_assign(&b);
|
||||
b.square();
|
||||
|
||||
assert_eq!(a, b);
|
||||
}
|
||||
}
|
||||
|
||||
fn random_inversion_tests<F: Field, R: Rng>(rng: &mut R) {
|
||||
assert!(F::zero().inverse().is_none());
|
||||
|
||||
for _ in 0..10000 {
|
||||
let mut a = F::rand(rng);
|
||||
let b = a.inverse().unwrap(); // probablistically nonzero
|
||||
a.mul_assign(&b);
|
||||
|
||||
assert_eq!(a, F::one());
|
||||
}
|
||||
}
|
||||
|
||||
fn random_expansion_tests<F: Field, R: Rng>(rng: &mut R) {
|
||||
for _ in 0..10000 {
|
||||
// Compare (a + b)(c + d) and (a*c + b*c + a*d + b*d)
|
||||
|
||||
let a = F::rand(rng);
|
||||
let b = F::rand(rng);
|
||||
let c = F::rand(rng);
|
||||
let d = F::rand(rng);
|
||||
|
||||
let mut t0 = a;
|
||||
t0.add_assign(&b);
|
||||
let mut t1 = c;
|
||||
t1.add_assign(&d);
|
||||
t0.mul_assign(&t1);
|
||||
|
||||
let mut t2 = a;
|
||||
t2.mul_assign(&c);
|
||||
let mut t3 = b;
|
||||
t3.mul_assign(&c);
|
||||
let mut t4 = a;
|
||||
t4.mul_assign(&d);
|
||||
let mut t5 = b;
|
||||
t5.mul_assign(&d);
|
||||
|
||||
t2.add_assign(&t3);
|
||||
t2.add_assign(&t4);
|
||||
t2.add_assign(&t5);
|
||||
|
||||
assert_eq!(t0, t2);
|
||||
}
|
||||
}
|
|
@ -0,0 +1,4 @@
|
|||
pub mod curve;
|
||||
pub mod engine;
|
||||
pub mod field;
|
||||
pub mod repr;
|
|
@ -0,0 +1,98 @@
|
|||
use rand::{SeedableRng, XorShiftRng};
|
||||
use PrimeFieldRepr;
|
||||
|
||||
pub fn random_repr_tests<R: PrimeFieldRepr>() {
|
||||
random_encoding_tests::<R>();
|
||||
random_shl_tests::<R>();
|
||||
random_shr_tests::<R>();
|
||||
}
|
||||
|
||||
fn random_encoding_tests<R: PrimeFieldRepr>() {
|
||||
let mut rng = XorShiftRng::from_seed([0x5dbe6259, 0x8d313d76, 0x3237db17, 0xe5bc0654]);
|
||||
|
||||
for _ in 0..1000 {
|
||||
let r = R::rand(&mut rng);
|
||||
|
||||
// Big endian
|
||||
{
|
||||
let mut rdecoded = R::default();
|
||||
|
||||
let mut v: Vec<u8> = vec![];
|
||||
r.write_be(&mut v).unwrap();
|
||||
rdecoded.read_be(&v[0..]).unwrap();
|
||||
|
||||
assert_eq!(r, rdecoded);
|
||||
}
|
||||
|
||||
// Little endian
|
||||
{
|
||||
let mut rdecoded = R::default();
|
||||
|
||||
let mut v: Vec<u8> = vec![];
|
||||
r.write_le(&mut v).unwrap();
|
||||
rdecoded.read_le(&v[0..]).unwrap();
|
||||
|
||||
assert_eq!(r, rdecoded);
|
||||
}
|
||||
|
||||
{
|
||||
let mut rdecoded_le = R::default();
|
||||
let mut rdecoded_be_flip = R::default();
|
||||
|
||||
let mut v: Vec<u8> = vec![];
|
||||
r.write_le(&mut v).unwrap();
|
||||
|
||||
// This reads in little-endian, so we are done.
|
||||
rdecoded_le.read_le(&v[..]).unwrap();
|
||||
|
||||
// This reads in big-endian, so we perform a swap of the
|
||||
// bytes beforehand.
|
||||
let v: Vec<u8> = v.into_iter().rev().collect();
|
||||
rdecoded_be_flip.read_be(&v[..]).unwrap();
|
||||
|
||||
assert_eq!(rdecoded_le, rdecoded_be_flip);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn random_shl_tests<R: PrimeFieldRepr>() {
|
||||
let mut rng = XorShiftRng::from_seed([0x5dbe6259, 0x8d313d76, 0x3237db17, 0xe5bc0654]);
|
||||
|
||||
for _ in 0..100 {
|
||||
let r = R::rand(&mut rng);
|
||||
|
||||
for shift in 0..(r.num_bits() + 1) {
|
||||
let mut r1 = r;
|
||||
let mut r2 = r;
|
||||
|
||||
for _ in 0..shift {
|
||||
r1.mul2();
|
||||
}
|
||||
|
||||
r2.shl(shift);
|
||||
|
||||
assert_eq!(r1, r2);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn random_shr_tests<R: PrimeFieldRepr>() {
|
||||
let mut rng = XorShiftRng::from_seed([0x5dbe6259, 0x8d313d76, 0x3237db17, 0xe5bc0654]);
|
||||
|
||||
for _ in 0..100 {
|
||||
let r = R::rand(&mut rng);
|
||||
|
||||
for shift in 0..(r.num_bits() + 1) {
|
||||
let mut r1 = r;
|
||||
let mut r2 = r;
|
||||
|
||||
for _ in 0..shift {
|
||||
r1.div2();
|
||||
}
|
||||
|
||||
r2.shr(shift);
|
||||
|
||||
assert_eq!(r1, r2);
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,179 @@
|
|||
use super::{CurveProjective, PrimeField, PrimeFieldRepr};
|
||||
|
||||
/// Replaces the contents of `table` with a w-NAF window table for the given window size.
|
||||
pub(crate) fn wnaf_table<G: CurveProjective>(table: &mut Vec<G>, mut base: G, window: usize) {
|
||||
table.truncate(0);
|
||||
table.reserve(1 << (window - 1));
|
||||
|
||||
let mut dbl = base;
|
||||
dbl.double();
|
||||
|
||||
for _ in 0..(1 << (window - 1)) {
|
||||
table.push(base);
|
||||
base.add_assign(&dbl);
|
||||
}
|
||||
}
|
||||
|
||||
/// Replaces the contents of `wnaf` with the w-NAF representation of a scalar.
|
||||
pub(crate) fn wnaf_form<S: PrimeFieldRepr>(wnaf: &mut Vec<i64>, mut c: S, window: usize) {
|
||||
wnaf.truncate(0);
|
||||
|
||||
while !c.is_zero() {
|
||||
let mut u;
|
||||
if c.is_odd() {
|
||||
u = (c.as_ref()[0] % (1 << (window + 1))) as i64;
|
||||
|
||||
if u > (1 << window) {
|
||||
u -= 1 << (window + 1);
|
||||
}
|
||||
|
||||
if u > 0 {
|
||||
c.sub_noborrow(&S::from(u as u64));
|
||||
} else {
|
||||
c.add_nocarry(&S::from((-u) as u64));
|
||||
}
|
||||
} else {
|
||||
u = 0;
|
||||
}
|
||||
|
||||
wnaf.push(u);
|
||||
|
||||
c.div2();
|
||||
}
|
||||
}
|
||||
|
||||
/// Performs w-NAF exponentiation with the provided window table and w-NAF form scalar.
|
||||
///
|
||||
/// This function must be provided a `table` and `wnaf` that were constructed with
|
||||
/// the same window size; otherwise, it may panic or produce invalid results.
|
||||
pub(crate) fn wnaf_exp<G: CurveProjective>(table: &[G], wnaf: &[i64]) -> G {
|
||||
let mut result = G::zero();
|
||||
|
||||
let mut found_one = false;
|
||||
|
||||
for n in wnaf.iter().rev() {
|
||||
if found_one {
|
||||
result.double();
|
||||
}
|
||||
|
||||
if *n != 0 {
|
||||
found_one = true;
|
||||
|
||||
if *n > 0 {
|
||||
result.add_assign(&table[(n / 2) as usize]);
|
||||
} else {
|
||||
result.sub_assign(&table[((-n) / 2) as usize]);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
result
|
||||
}
|
||||
|
||||
/// A "w-ary non-adjacent form" exponentiation context.
|
||||
#[derive(Debug)]
|
||||
pub struct Wnaf<W, B, S> {
|
||||
base: B,
|
||||
scalar: S,
|
||||
window_size: W,
|
||||
}
|
||||
|
||||
impl<G: CurveProjective> Wnaf<(), Vec<G>, Vec<i64>> {
|
||||
/// Construct a new wNAF context without allocating.
|
||||
pub fn new() -> Self {
|
||||
Wnaf {
|
||||
base: vec![],
|
||||
scalar: vec![],
|
||||
window_size: (),
|
||||
}
|
||||
}
|
||||
|
||||
/// Given a base and a number of scalars, compute a window table and return a `Wnaf` object that
|
||||
/// can perform exponentiations with `.scalar(..)`.
|
||||
pub fn base(&mut self, base: G, num_scalars: usize) -> Wnaf<usize, &[G], &mut Vec<i64>> {
|
||||
// Compute the appropriate window size based on the number of scalars.
|
||||
let window_size = G::recommended_wnaf_for_num_scalars(num_scalars);
|
||||
|
||||
// Compute a wNAF table for the provided base and window size.
|
||||
wnaf_table(&mut self.base, base, window_size);
|
||||
|
||||
// Return a Wnaf object that immutably borrows the computed base storage location,
|
||||
// but mutably borrows the scalar storage location.
|
||||
Wnaf {
|
||||
base: &self.base[..],
|
||||
scalar: &mut self.scalar,
|
||||
window_size,
|
||||
}
|
||||
}
|
||||
|
||||
/// Given a scalar, compute its wNAF representation and return a `Wnaf` object that can perform
|
||||
/// exponentiations with `.base(..)`.
|
||||
pub fn scalar(
|
||||
&mut self,
|
||||
scalar: <<G as CurveProjective>::Scalar as PrimeField>::Repr,
|
||||
) -> Wnaf<usize, &mut Vec<G>, &[i64]> {
|
||||
// Compute the appropriate window size for the scalar.
|
||||
let window_size = G::recommended_wnaf_for_scalar(scalar);
|
||||
|
||||
// Compute the wNAF form of the scalar.
|
||||
wnaf_form(&mut self.scalar, scalar, window_size);
|
||||
|
||||
// Return a Wnaf object that mutably borrows the base storage location, but
|
||||
// immutably borrows the computed wNAF form scalar location.
|
||||
Wnaf {
|
||||
base: &mut self.base,
|
||||
scalar: &self.scalar[..],
|
||||
window_size,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a, G: CurveProjective> Wnaf<usize, &'a [G], &'a mut Vec<i64>> {
|
||||
/// Constructs new space for the scalar representation while borrowing
|
||||
/// the computed window table, for sending the window table across threads.
|
||||
pub fn shared(&self) -> Wnaf<usize, &'a [G], Vec<i64>> {
|
||||
Wnaf {
|
||||
base: self.base,
|
||||
scalar: vec![],
|
||||
window_size: self.window_size,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a, G: CurveProjective> Wnaf<usize, &'a mut Vec<G>, &'a [i64]> {
|
||||
/// Constructs new space for the window table while borrowing
|
||||
/// the computed scalar representation, for sending the scalar representation
|
||||
/// across threads.
|
||||
pub fn shared(&self) -> Wnaf<usize, Vec<G>, &'a [i64]> {
|
||||
Wnaf {
|
||||
base: vec![],
|
||||
scalar: self.scalar,
|
||||
window_size: self.window_size,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<B, S: AsRef<[i64]>> Wnaf<usize, B, S> {
|
||||
/// Performs exponentiation given a base.
|
||||
pub fn base<G: CurveProjective>(&mut self, base: G) -> G
|
||||
where
|
||||
B: AsMut<Vec<G>>,
|
||||
{
|
||||
wnaf_table(self.base.as_mut(), base, self.window_size);
|
||||
wnaf_exp(self.base.as_mut(), self.scalar.as_ref())
|
||||
}
|
||||
}
|
||||
|
||||
impl<B, S: AsMut<Vec<i64>>> Wnaf<usize, B, S> {
|
||||
/// Performs exponentiation given a scalar.
|
||||
pub fn scalar<G: CurveProjective>(
|
||||
&mut self,
|
||||
scalar: <<G as CurveProjective>::Scalar as PrimeField>::Repr,
|
||||
) -> G
|
||||
where
|
||||
B: AsRef<[G]>,
|
||||
{
|
||||
wnaf_form(self.scalar.as_mut(), scalar, self.window_size);
|
||||
wnaf_exp(self.base.as_ref(), self.scalar.as_mut())
|
||||
}
|
||||
}
|
Loading…
Reference in New Issue