Merge pull request #283 from str4d/remove-subtrees
Remove subtree crates
This commit is contained in:
commit
288b3dae77
|
@ -1,17 +1,11 @@
|
|||
[workspace]
|
||||
members = [
|
||||
"bellman",
|
||||
"components/equihash",
|
||||
"ff",
|
||||
"group",
|
||||
"pairing",
|
||||
"zcash_client_backend",
|
||||
"zcash_client_sqlite",
|
||||
"zcash_history",
|
||||
"zcash_primitives",
|
||||
"zcash_proofs",
|
||||
"jubjub",
|
||||
"bls12_381",
|
||||
]
|
||||
|
||||
[profile.release]
|
||||
|
|
|
@ -1,2 +0,0 @@
|
|||
target
|
||||
Cargo.lock
|
|
@ -1,14 +0,0 @@
|
|||
Copyrights in the "bellman" library are retained by their contributors. No
|
||||
copyright assignment is required to contribute to the "bellman" library.
|
||||
|
||||
The "bellman" 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.
|
|
@ -1,44 +0,0 @@
|
|||
[package]
|
||||
authors = ["Sean Bowe <ewillbefull@gmail.com>"]
|
||||
description = "zk-SNARK library"
|
||||
readme = "README.md"
|
||||
homepage = "https://github.com/ebfull/bellman"
|
||||
license = "MIT/Apache-2.0"
|
||||
name = "bellman"
|
||||
repository = "https://github.com/ebfull/bellman"
|
||||
version = "0.7.0"
|
||||
edition = "2018"
|
||||
|
||||
[dependencies]
|
||||
bit-vec = "0.6"
|
||||
blake2s_simd = "0.5"
|
||||
ff = { version = "0.7", path = "../ff" }
|
||||
futures = "0.1"
|
||||
futures-cpupool = { version = "0.1", optional = true }
|
||||
group = { version = "0.7", path = "../group" }
|
||||
num_cpus = { version = "1", optional = true }
|
||||
crossbeam = { version = "0.7", optional = true }
|
||||
pairing = { version = "0.17", path = "../pairing", optional = true }
|
||||
rand_core = "0.5"
|
||||
byteorder = "1"
|
||||
subtle = "2.2.1"
|
||||
|
||||
[dev-dependencies]
|
||||
bls12_381 = { version = "0.2", path = "../bls12_381" }
|
||||
hex-literal = "0.2"
|
||||
rand = "0.7"
|
||||
rand_xorshift = "0.2"
|
||||
sha2 = "0.9"
|
||||
|
||||
[features]
|
||||
groth16 = ["pairing"]
|
||||
multicore = ["futures-cpupool", "crossbeam", "num_cpus"]
|
||||
default = ["groth16", "multicore"]
|
||||
|
||||
[[test]]
|
||||
name = "mimc"
|
||||
path = "tests/mimc.rs"
|
||||
required-features = ["groth16"]
|
||||
|
||||
[badges]
|
||||
maintenance = { status = "actively-developed" }
|
|
@ -1,201 +0,0 @@
|
|||
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.
|
|
@ -1,23 +0,0 @@
|
|||
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.
|
|
@ -1,33 +0,0 @@
|
|||
# bellman [![Crates.io](https://img.shields.io/crates/v/bellman.svg)](https://crates.io/crates/bellman) #
|
||||
|
||||
`bellman` is a crate for building zk-SNARK circuits. It provides circuit traits
|
||||
and primitive structures, as well as basic gadget implementations such as
|
||||
booleans and number abstractions.
|
||||
|
||||
`bellman` uses the `ff` and `group` crates to build circuits generically over a
|
||||
scalar field type, which is used as the "word" of a circuit. Arithmetic
|
||||
operations modulo the scalar field's prime are efficient, while other operations
|
||||
(such as boolean logic) are implemented using these words.
|
||||
|
||||
## Roadmap
|
||||
|
||||
Currently `bellman` bundles an implementation of the Groth16 proving system.
|
||||
This will be moved into a separate crate in the future, and `bellman` will
|
||||
contain any utilities that make implementing proving systems easier.
|
||||
|
||||
## 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.
|
|
@ -1,493 +0,0 @@
|
|||
//! This module contains an [`EvaluationDomain`] abstraction for performing
|
||||
//! various kinds of polynomial arithmetic on top of the scalar field.
|
||||
//!
|
||||
//! In pairing-based SNARKs like [Groth16], we need to calculate a quotient
|
||||
//! polynomial over a target polynomial with roots at distinct points associated
|
||||
//! with each constraint of the constraint system. In order to be efficient, we
|
||||
//! choose these roots to be the powers of a 2<sup>n</sup> root of unity in the
|
||||
//! field. This allows us to perform polynomial operations in O(n) by performing
|
||||
//! an O(n log n) FFT over such a domain.
|
||||
//!
|
||||
//! [`EvaluationDomain`]: crate::domain::EvaluationDomain
|
||||
//! [Groth16]: https://eprint.iacr.org/2016/260
|
||||
|
||||
use ff::PrimeField;
|
||||
use group::cofactor::CofactorCurve;
|
||||
|
||||
use super::SynthesisError;
|
||||
|
||||
use super::multicore::Worker;
|
||||
|
||||
pub struct EvaluationDomain<S: PrimeField, G: Group<S>> {
|
||||
coeffs: Vec<G>,
|
||||
exp: u32,
|
||||
omega: S,
|
||||
omegainv: S,
|
||||
geninv: S,
|
||||
minv: S,
|
||||
}
|
||||
|
||||
impl<S: PrimeField, G: Group<S>> AsRef<[G]> for EvaluationDomain<S, G> {
|
||||
fn as_ref(&self) -> &[G] {
|
||||
&self.coeffs
|
||||
}
|
||||
}
|
||||
|
||||
impl<S: PrimeField, G: Group<S>> AsMut<[G]> for EvaluationDomain<S, G> {
|
||||
fn as_mut(&mut self) -> &mut [G] {
|
||||
&mut self.coeffs
|
||||
}
|
||||
}
|
||||
|
||||
impl<S: PrimeField, G: Group<S>> EvaluationDomain<S, G> {
|
||||
pub fn into_coeffs(self) -> Vec<G> {
|
||||
self.coeffs
|
||||
}
|
||||
|
||||
pub fn from_coeffs(mut coeffs: Vec<G>) -> Result<EvaluationDomain<S, G>, SynthesisError> {
|
||||
// Compute the size of our evaluation domain
|
||||
let mut m = 1;
|
||||
let mut exp = 0;
|
||||
while m < coeffs.len() {
|
||||
m *= 2;
|
||||
exp += 1;
|
||||
|
||||
// The pairing-friendly curve may not be able to support
|
||||
// large enough (radix2) evaluation domains.
|
||||
if exp >= S::S {
|
||||
return Err(SynthesisError::PolynomialDegreeTooLarge);
|
||||
}
|
||||
}
|
||||
|
||||
// Compute omega, the 2^exp primitive root of unity
|
||||
let mut omega = S::root_of_unity();
|
||||
for _ in exp..S::S {
|
||||
omega = omega.square();
|
||||
}
|
||||
|
||||
// Extend the coeffs vector with zeroes if necessary
|
||||
coeffs.resize(m, G::group_zero());
|
||||
|
||||
Ok(EvaluationDomain {
|
||||
coeffs,
|
||||
exp,
|
||||
omega,
|
||||
omegainv: omega.invert().unwrap(),
|
||||
geninv: S::multiplicative_generator().invert().unwrap(),
|
||||
minv: S::from_str(&format!("{}", m)).unwrap().invert().unwrap(),
|
||||
})
|
||||
}
|
||||
|
||||
pub fn fft(&mut self, worker: &Worker) {
|
||||
best_fft(&mut self.coeffs, worker, &self.omega, self.exp);
|
||||
}
|
||||
|
||||
pub fn ifft(&mut self, worker: &Worker) {
|
||||
best_fft(&mut self.coeffs, worker, &self.omegainv, self.exp);
|
||||
|
||||
worker.scope(self.coeffs.len(), |scope, chunk| {
|
||||
let minv = self.minv;
|
||||
|
||||
for v in self.coeffs.chunks_mut(chunk) {
|
||||
scope.spawn(move |_scope| {
|
||||
for v in v {
|
||||
v.group_mul_assign(&minv);
|
||||
}
|
||||
});
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
pub fn distribute_powers(&mut self, worker: &Worker, g: S) {
|
||||
worker.scope(self.coeffs.len(), |scope, chunk| {
|
||||
for (i, v) in self.coeffs.chunks_mut(chunk).enumerate() {
|
||||
scope.spawn(move |_scope| {
|
||||
let mut u = g.pow_vartime(&[(i * chunk) as u64]);
|
||||
for v in v.iter_mut() {
|
||||
v.group_mul_assign(&u);
|
||||
u.mul_assign(&g);
|
||||
}
|
||||
});
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
pub fn coset_fft(&mut self, worker: &Worker) {
|
||||
self.distribute_powers(worker, S::multiplicative_generator());
|
||||
self.fft(worker);
|
||||
}
|
||||
|
||||
pub fn icoset_fft(&mut self, worker: &Worker) {
|
||||
let geninv = self.geninv;
|
||||
|
||||
self.ifft(worker);
|
||||
self.distribute_powers(worker, geninv);
|
||||
}
|
||||
|
||||
/// This evaluates t(tau) for this domain, which is
|
||||
/// tau^m - 1 for these radix-2 domains.
|
||||
pub fn z(&self, tau: &S) -> S {
|
||||
let mut tmp = tau.pow_vartime(&[self.coeffs.len() as u64]);
|
||||
tmp.sub_assign(&S::one());
|
||||
|
||||
tmp
|
||||
}
|
||||
|
||||
/// The target polynomial is the zero polynomial in our
|
||||
/// evaluation domain, so we must perform division over
|
||||
/// a coset.
|
||||
pub fn divide_by_z_on_coset(&mut self, worker: &Worker) {
|
||||
let i = self.z(&S::multiplicative_generator()).invert().unwrap();
|
||||
|
||||
worker.scope(self.coeffs.len(), |scope, chunk| {
|
||||
for v in self.coeffs.chunks_mut(chunk) {
|
||||
scope.spawn(move |_scope| {
|
||||
for v in v {
|
||||
v.group_mul_assign(&i);
|
||||
}
|
||||
});
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
/// Perform O(n) multiplication of two polynomials in the domain.
|
||||
pub fn mul_assign(&mut self, worker: &Worker, other: &EvaluationDomain<S, Scalar<S>>) {
|
||||
assert_eq!(self.coeffs.len(), other.coeffs.len());
|
||||
|
||||
worker.scope(self.coeffs.len(), |scope, chunk| {
|
||||
for (a, b) in self
|
||||
.coeffs
|
||||
.chunks_mut(chunk)
|
||||
.zip(other.coeffs.chunks(chunk))
|
||||
{
|
||||
scope.spawn(move |_scope| {
|
||||
for (a, b) in a.iter_mut().zip(b.iter()) {
|
||||
a.group_mul_assign(&b.0);
|
||||
}
|
||||
});
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
/// Perform O(n) subtraction of one polynomial from another in the domain.
|
||||
pub fn sub_assign(&mut self, worker: &Worker, other: &EvaluationDomain<S, G>) {
|
||||
assert_eq!(self.coeffs.len(), other.coeffs.len());
|
||||
|
||||
worker.scope(self.coeffs.len(), |scope, chunk| {
|
||||
for (a, b) in self
|
||||
.coeffs
|
||||
.chunks_mut(chunk)
|
||||
.zip(other.coeffs.chunks(chunk))
|
||||
{
|
||||
scope.spawn(move |_scope| {
|
||||
for (a, b) in a.iter_mut().zip(b.iter()) {
|
||||
a.group_sub_assign(&b);
|
||||
}
|
||||
});
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
pub trait Group<Scalar: PrimeField>: Sized + Copy + Clone + Send + Sync {
|
||||
fn group_zero() -> Self;
|
||||
fn group_mul_assign(&mut self, by: &Scalar);
|
||||
fn group_add_assign(&mut self, other: &Self);
|
||||
fn group_sub_assign(&mut self, other: &Self);
|
||||
}
|
||||
|
||||
pub struct Point<G: CofactorCurve>(pub G);
|
||||
|
||||
impl<G: CofactorCurve> PartialEq for Point<G> {
|
||||
fn eq(&self, other: &Point<G>) -> bool {
|
||||
self.0 == other.0
|
||||
}
|
||||
}
|
||||
|
||||
impl<G: CofactorCurve> Copy for Point<G> {}
|
||||
|
||||
impl<G: CofactorCurve> Clone for Point<G> {
|
||||
fn clone(&self) -> Point<G> {
|
||||
*self
|
||||
}
|
||||
}
|
||||
|
||||
impl<G: CofactorCurve> Group<G::Scalar> for Point<G> {
|
||||
fn group_zero() -> Self {
|
||||
Point(G::identity())
|
||||
}
|
||||
fn group_mul_assign(&mut self, by: &G::Scalar) {
|
||||
self.0.mul_assign(by);
|
||||
}
|
||||
fn group_add_assign(&mut self, other: &Self) {
|
||||
self.0.add_assign(&other.0);
|
||||
}
|
||||
fn group_sub_assign(&mut self, other: &Self) {
|
||||
self.0.sub_assign(&other.0);
|
||||
}
|
||||
}
|
||||
|
||||
pub struct Scalar<S: PrimeField>(pub S);
|
||||
|
||||
impl<S: PrimeField> PartialEq for Scalar<S> {
|
||||
fn eq(&self, other: &Scalar<S>) -> bool {
|
||||
self.0 == other.0
|
||||
}
|
||||
}
|
||||
|
||||
impl<S: PrimeField> Copy for Scalar<S> {}
|
||||
|
||||
impl<S: PrimeField> Clone for Scalar<S> {
|
||||
fn clone(&self) -> Scalar<S> {
|
||||
*self
|
||||
}
|
||||
}
|
||||
|
||||
impl<S: PrimeField> Group<S> for Scalar<S> {
|
||||
fn group_zero() -> Self {
|
||||
Scalar(S::zero())
|
||||
}
|
||||
fn group_mul_assign(&mut self, by: &S) {
|
||||
self.0.mul_assign(by);
|
||||
}
|
||||
fn group_add_assign(&mut self, other: &Self) {
|
||||
self.0.add_assign(&other.0);
|
||||
}
|
||||
fn group_sub_assign(&mut self, other: &Self) {
|
||||
self.0.sub_assign(&other.0);
|
||||
}
|
||||
}
|
||||
|
||||
fn best_fft<S: PrimeField, T: Group<S>>(a: &mut [T], worker: &Worker, omega: &S, log_n: u32) {
|
||||
let log_cpus = worker.log_num_cpus();
|
||||
|
||||
if log_n <= log_cpus {
|
||||
serial_fft(a, omega, log_n);
|
||||
} else {
|
||||
parallel_fft(a, worker, omega, log_n, log_cpus);
|
||||
}
|
||||
}
|
||||
|
||||
fn serial_fft<S: PrimeField, T: Group<S>>(a: &mut [T], omega: &S, log_n: u32) {
|
||||
fn bitreverse(mut n: u32, l: u32) -> u32 {
|
||||
let mut r = 0;
|
||||
for _ in 0..l {
|
||||
r = (r << 1) | (n & 1);
|
||||
n >>= 1;
|
||||
}
|
||||
r
|
||||
}
|
||||
|
||||
let n = a.len() as u32;
|
||||
assert_eq!(n, 1 << log_n);
|
||||
|
||||
for k in 0..n {
|
||||
let rk = bitreverse(k, log_n);
|
||||
if k < rk {
|
||||
a.swap(rk as usize, k as usize);
|
||||
}
|
||||
}
|
||||
|
||||
let mut m = 1;
|
||||
for _ in 0..log_n {
|
||||
let w_m = omega.pow_vartime(&[u64::from(n / (2 * m))]);
|
||||
|
||||
let mut k = 0;
|
||||
while k < n {
|
||||
let mut w = S::one();
|
||||
for j in 0..m {
|
||||
let mut t = a[(k + j + m) as usize];
|
||||
t.group_mul_assign(&w);
|
||||
let mut tmp = a[(k + j) as usize];
|
||||
tmp.group_sub_assign(&t);
|
||||
a[(k + j + m) as usize] = tmp;
|
||||
a[(k + j) as usize].group_add_assign(&t);
|
||||
w.mul_assign(&w_m);
|
||||
}
|
||||
|
||||
k += 2 * m;
|
||||
}
|
||||
|
||||
m *= 2;
|
||||
}
|
||||
}
|
||||
|
||||
fn parallel_fft<S: PrimeField, T: Group<S>>(
|
||||
a: &mut [T],
|
||||
worker: &Worker,
|
||||
omega: &S,
|
||||
log_n: u32,
|
||||
log_cpus: u32,
|
||||
) {
|
||||
assert!(log_n >= log_cpus);
|
||||
|
||||
let num_cpus = 1 << log_cpus;
|
||||
let log_new_n = log_n - log_cpus;
|
||||
let mut tmp = vec![vec![T::group_zero(); 1 << log_new_n]; num_cpus];
|
||||
let new_omega = omega.pow_vartime(&[num_cpus as u64]);
|
||||
|
||||
worker.scope(0, |scope, _| {
|
||||
let a = &*a;
|
||||
|
||||
for (j, tmp) in tmp.iter_mut().enumerate() {
|
||||
scope.spawn(move |_scope| {
|
||||
// Shuffle into a sub-FFT
|
||||
let omega_j = omega.pow_vartime(&[j as u64]);
|
||||
let omega_step = omega.pow_vartime(&[(j as u64) << log_new_n]);
|
||||
|
||||
let mut elt = S::one();
|
||||
for (i, tmp) in tmp.iter_mut().enumerate() {
|
||||
for s in 0..num_cpus {
|
||||
let idx = (i + (s << log_new_n)) % (1 << log_n);
|
||||
let mut t = a[idx];
|
||||
t.group_mul_assign(&elt);
|
||||
tmp.group_add_assign(&t);
|
||||
elt.mul_assign(&omega_step);
|
||||
}
|
||||
elt.mul_assign(&omega_j);
|
||||
}
|
||||
|
||||
// Perform sub-FFT
|
||||
serial_fft(tmp, &new_omega, log_new_n);
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
// TODO: does this hurt or help?
|
||||
worker.scope(a.len(), |scope, chunk| {
|
||||
let tmp = &tmp;
|
||||
|
||||
for (idx, a) in a.chunks_mut(chunk).enumerate() {
|
||||
scope.spawn(move |_scope| {
|
||||
let mut idx = idx * chunk;
|
||||
let mask = (1 << log_cpus) - 1;
|
||||
for a in a {
|
||||
*a = tmp[idx & mask][idx >> log_cpus];
|
||||
idx += 1;
|
||||
}
|
||||
});
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
// Test multiplying various (low degree) polynomials together and
|
||||
// comparing with naive evaluations.
|
||||
#[cfg(feature = "pairing")]
|
||||
#[test]
|
||||
fn polynomial_arith() {
|
||||
use bls12_381::Scalar as Fr;
|
||||
use rand_core::RngCore;
|
||||
|
||||
fn test_mul<S: PrimeField, R: RngCore>(rng: &mut R) {
|
||||
let worker = Worker::new();
|
||||
|
||||
for coeffs_a in 0..70 {
|
||||
for coeffs_b in 0..70 {
|
||||
let mut a: Vec<_> = (0..coeffs_a).map(|_| Scalar::<S>(S::random(rng))).collect();
|
||||
let mut b: Vec<_> = (0..coeffs_b).map(|_| Scalar::<S>(S::random(rng))).collect();
|
||||
|
||||
// naive evaluation
|
||||
let mut naive = vec![Scalar(S::zero()); coeffs_a + coeffs_b];
|
||||
for (i1, a) in a.iter().enumerate() {
|
||||
for (i2, b) in b.iter().enumerate() {
|
||||
let mut prod = *a;
|
||||
prod.group_mul_assign(&b.0);
|
||||
naive[i1 + i2].group_add_assign(&prod);
|
||||
}
|
||||
}
|
||||
|
||||
a.resize(coeffs_a + coeffs_b, Scalar(S::zero()));
|
||||
b.resize(coeffs_a + coeffs_b, Scalar(S::zero()));
|
||||
|
||||
let mut a = EvaluationDomain::from_coeffs(a).unwrap();
|
||||
let mut b = EvaluationDomain::from_coeffs(b).unwrap();
|
||||
|
||||
a.fft(&worker);
|
||||
b.fft(&worker);
|
||||
a.mul_assign(&worker, &b);
|
||||
a.ifft(&worker);
|
||||
|
||||
for (naive, fft) in naive.iter().zip(a.coeffs.iter()) {
|
||||
assert!(naive == fft);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
let rng = &mut rand::thread_rng();
|
||||
|
||||
test_mul::<Fr, _>(rng);
|
||||
}
|
||||
|
||||
#[cfg(feature = "pairing")]
|
||||
#[test]
|
||||
fn fft_composition() {
|
||||
use bls12_381::Scalar as Fr;
|
||||
use rand_core::RngCore;
|
||||
|
||||
fn test_comp<S: PrimeField, R: RngCore>(rng: &mut R) {
|
||||
let worker = Worker::new();
|
||||
|
||||
for coeffs in 0..10 {
|
||||
let coeffs = 1 << coeffs;
|
||||
|
||||
let mut v = vec![];
|
||||
for _ in 0..coeffs {
|
||||
v.push(Scalar::<S>(S::random(rng)));
|
||||
}
|
||||
|
||||
let mut domain = EvaluationDomain::from_coeffs(v.clone()).unwrap();
|
||||
domain.ifft(&worker);
|
||||
domain.fft(&worker);
|
||||
assert!(v == domain.coeffs);
|
||||
domain.fft(&worker);
|
||||
domain.ifft(&worker);
|
||||
assert!(v == domain.coeffs);
|
||||
domain.icoset_fft(&worker);
|
||||
domain.coset_fft(&worker);
|
||||
assert!(v == domain.coeffs);
|
||||
domain.coset_fft(&worker);
|
||||
domain.icoset_fft(&worker);
|
||||
assert!(v == domain.coeffs);
|
||||
}
|
||||
}
|
||||
|
||||
let rng = &mut rand::thread_rng();
|
||||
|
||||
test_comp::<Fr, _>(rng);
|
||||
}
|
||||
|
||||
#[cfg(feature = "pairing")]
|
||||
#[test]
|
||||
fn parallel_fft_consistency() {
|
||||
use bls12_381::Scalar as Fr;
|
||||
use rand_core::RngCore;
|
||||
use std::cmp::min;
|
||||
|
||||
fn test_consistency<S: PrimeField, R: RngCore>(rng: &mut R) {
|
||||
let worker = Worker::new();
|
||||
|
||||
for _ in 0..5 {
|
||||
for log_d in 0..10 {
|
||||
let d = 1 << log_d;
|
||||
|
||||
let v1 = (0..d)
|
||||
.map(|_| Scalar::<S>(S::random(rng)))
|
||||
.collect::<Vec<_>>();
|
||||
let mut v1 = EvaluationDomain::from_coeffs(v1).unwrap();
|
||||
let mut v2 = EvaluationDomain::from_coeffs(v1.coeffs.clone()).unwrap();
|
||||
|
||||
for log_cpus in log_d..min(log_d + 1, 3) {
|
||||
parallel_fft(&mut v1.coeffs, &worker, &v1.omega, log_d, log_cpus);
|
||||
serial_fft(&mut v2.coeffs, &v2.omega, log_d);
|
||||
|
||||
assert!(v1.coeffs == v2.coeffs);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
let rng = &mut rand::thread_rng();
|
||||
|
||||
test_consistency::<Fr, _>(rng);
|
||||
}
|
|
@ -1,33 +0,0 @@
|
|||
//! Self-contained sub-circuit implementations for various primitives.
|
||||
|
||||
pub mod test;
|
||||
|
||||
pub mod blake2s;
|
||||
pub mod boolean;
|
||||
pub mod lookup;
|
||||
pub mod multieq;
|
||||
pub mod multipack;
|
||||
pub mod num;
|
||||
pub mod sha256;
|
||||
pub mod uint32;
|
||||
|
||||
use crate::SynthesisError;
|
||||
|
||||
// TODO: This should probably be removed and we
|
||||
// should use existing helper methods on `Option`
|
||||
// for mapping with an error.
|
||||
/// This basically is just an extension to `Option`
|
||||
/// which allows for a convenient mapping to an
|
||||
/// error on `None`.
|
||||
pub trait Assignment<T> {
|
||||
fn get(&self) -> Result<&T, SynthesisError>;
|
||||
}
|
||||
|
||||
impl<T> Assignment<T> for Option<T> {
|
||||
fn get(&self) -> Result<&T, SynthesisError> {
|
||||
match *self {
|
||||
Some(ref v) => Ok(v),
|
||||
None => Err(SynthesisError::AssignmentMissing),
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1,697 +0,0 @@
|
|||
//! The [BLAKE2s] hash function with personalization support.
|
||||
//!
|
||||
//! [BLAKE2s]: https://tools.ietf.org/html/rfc7693
|
||||
|
||||
use super::{boolean::Boolean, multieq::MultiEq, uint32::UInt32};
|
||||
use crate::{ConstraintSystem, SynthesisError};
|
||||
use ff::PrimeField;
|
||||
|
||||
/*
|
||||
2.1. Parameters
|
||||
The following table summarizes various parameters and their ranges:
|
||||
| BLAKE2b | BLAKE2s |
|
||||
--------------+------------------+------------------+
|
||||
Bits in word | w = 64 | w = 32 |
|
||||
Rounds in F | r = 12 | r = 10 |
|
||||
Block bytes | bb = 128 | bb = 64 |
|
||||
Hash bytes | 1 <= nn <= 64 | 1 <= nn <= 32 |
|
||||
Key bytes | 0 <= kk <= 64 | 0 <= kk <= 32 |
|
||||
Input bytes | 0 <= ll < 2**128 | 0 <= ll < 2**64 |
|
||||
--------------+------------------+------------------+
|
||||
G Rotation | (R1, R2, R3, R4) | (R1, R2, R3, R4) |
|
||||
constants = | (32, 24, 16, 63) | (16, 12, 8, 7) |
|
||||
--------------+------------------+------------------+
|
||||
*/
|
||||
|
||||
const R1: usize = 16;
|
||||
const R2: usize = 12;
|
||||
const R3: usize = 8;
|
||||
const R4: usize = 7;
|
||||
|
||||
/*
|
||||
Round | 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 |
|
||||
----------+-------------------------------------------------+
|
||||
SIGMA[0] | 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 |
|
||||
SIGMA[1] | 14 10 4 8 9 15 13 6 1 12 0 2 11 7 5 3 |
|
||||
SIGMA[2] | 11 8 12 0 5 2 15 13 10 14 3 6 7 1 9 4 |
|
||||
SIGMA[3] | 7 9 3 1 13 12 11 14 2 6 5 10 4 0 15 8 |
|
||||
SIGMA[4] | 9 0 5 7 2 4 10 15 14 1 11 12 6 8 3 13 |
|
||||
SIGMA[5] | 2 12 6 10 0 11 8 3 4 13 7 5 15 14 1 9 |
|
||||
SIGMA[6] | 12 5 1 15 14 13 4 10 0 7 6 3 9 2 8 11 |
|
||||
SIGMA[7] | 13 11 7 14 12 1 3 9 5 0 15 4 8 6 2 10 |
|
||||
SIGMA[8] | 6 15 14 9 11 3 0 8 12 2 13 7 1 4 10 5 |
|
||||
SIGMA[9] | 10 2 8 4 7 6 1 5 15 11 9 14 3 12 13 0 |
|
||||
----------+-------------------------------------------------+
|
||||
*/
|
||||
|
||||
const SIGMA: [[usize; 16]; 10] = [
|
||||
[0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15],
|
||||
[14, 10, 4, 8, 9, 15, 13, 6, 1, 12, 0, 2, 11, 7, 5, 3],
|
||||
[11, 8, 12, 0, 5, 2, 15, 13, 10, 14, 3, 6, 7, 1, 9, 4],
|
||||
[7, 9, 3, 1, 13, 12, 11, 14, 2, 6, 5, 10, 4, 0, 15, 8],
|
||||
[9, 0, 5, 7, 2, 4, 10, 15, 14, 1, 11, 12, 6, 8, 3, 13],
|
||||
[2, 12, 6, 10, 0, 11, 8, 3, 4, 13, 7, 5, 15, 14, 1, 9],
|
||||
[12, 5, 1, 15, 14, 13, 4, 10, 0, 7, 6, 3, 9, 2, 8, 11],
|
||||
[13, 11, 7, 14, 12, 1, 3, 9, 5, 0, 15, 4, 8, 6, 2, 10],
|
||||
[6, 15, 14, 9, 11, 3, 0, 8, 12, 2, 13, 7, 1, 4, 10, 5],
|
||||
[10, 2, 8, 4, 7, 6, 1, 5, 15, 11, 9, 14, 3, 12, 13, 0],
|
||||
];
|
||||
|
||||
/*
|
||||
3.1. Mixing Function G
|
||||
The G primitive function mixes two input words, "x" and "y", into
|
||||
four words indexed by "a", "b", "c", and "d" in the working vector
|
||||
v[0..15]. The full modified vector is returned. The rotation
|
||||
constants (R1, R2, R3, R4) are given in Section 2.1.
|
||||
FUNCTION G( v[0..15], a, b, c, d, x, y )
|
||||
|
|
||||
| v[a] := (v[a] + v[b] + x) mod 2**w
|
||||
| v[d] := (v[d] ^ v[a]) >>> R1
|
||||
| v[c] := (v[c] + v[d]) mod 2**w
|
||||
| v[b] := (v[b] ^ v[c]) >>> R2
|
||||
| v[a] := (v[a] + v[b] + y) mod 2**w
|
||||
| v[d] := (v[d] ^ v[a]) >>> R3
|
||||
| v[c] := (v[c] + v[d]) mod 2**w
|
||||
| v[b] := (v[b] ^ v[c]) >>> R4
|
||||
|
|
||||
| RETURN v[0..15]
|
||||
|
|
||||
END FUNCTION.
|
||||
*/
|
||||
|
||||
fn mixing_g<Scalar: PrimeField, CS: ConstraintSystem<Scalar>, M>(
|
||||
mut cs: M,
|
||||
v: &mut [UInt32],
|
||||
a: usize,
|
||||
b: usize,
|
||||
c: usize,
|
||||
d: usize,
|
||||
x: &UInt32,
|
||||
y: &UInt32,
|
||||
) -> Result<(), SynthesisError>
|
||||
where
|
||||
M: ConstraintSystem<Scalar, Root = MultiEq<Scalar, CS>>,
|
||||
{
|
||||
v[a] = UInt32::addmany(
|
||||
cs.namespace(|| "mixing step 1"),
|
||||
&[v[a].clone(), v[b].clone(), x.clone()],
|
||||
)?;
|
||||
v[d] = v[d].xor(cs.namespace(|| "mixing step 2"), &v[a])?.rotr(R1);
|
||||
v[c] = UInt32::addmany(
|
||||
cs.namespace(|| "mixing step 3"),
|
||||
&[v[c].clone(), v[d].clone()],
|
||||
)?;
|
||||
v[b] = v[b].xor(cs.namespace(|| "mixing step 4"), &v[c])?.rotr(R2);
|
||||
v[a] = UInt32::addmany(
|
||||
cs.namespace(|| "mixing step 5"),
|
||||
&[v[a].clone(), v[b].clone(), y.clone()],
|
||||
)?;
|
||||
v[d] = v[d].xor(cs.namespace(|| "mixing step 6"), &v[a])?.rotr(R3);
|
||||
v[c] = UInt32::addmany(
|
||||
cs.namespace(|| "mixing step 7"),
|
||||
&[v[c].clone(), v[d].clone()],
|
||||
)?;
|
||||
v[b] = v[b].xor(cs.namespace(|| "mixing step 8"), &v[c])?.rotr(R4);
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/*
|
||||
3.2. Compression Function F
|
||||
Compression function F takes as an argument the state vector "h",
|
||||
message block vector "m" (last block is padded with zeros to full
|
||||
block size, if required), 2w-bit offset counter "t", and final block
|
||||
indicator flag "f". Local vector v[0..15] is used in processing. F
|
||||
returns a new state vector. The number of rounds, "r", is 12 for
|
||||
BLAKE2b and 10 for BLAKE2s. Rounds are numbered from 0 to r - 1.
|
||||
FUNCTION F( h[0..7], m[0..15], t, f )
|
||||
|
|
||||
| // Initialize local work vector v[0..15]
|
||||
| v[0..7] := h[0..7] // First half from state.
|
||||
| v[8..15] := IV[0..7] // Second half from IV.
|
||||
|
|
||||
| v[12] := v[12] ^ (t mod 2**w) // Low word of the offset.
|
||||
| v[13] := v[13] ^ (t >> w) // High word.
|
||||
|
|
||||
| IF f = TRUE THEN // last block flag?
|
||||
| | v[14] := v[14] ^ 0xFF..FF // Invert all bits.
|
||||
| END IF.
|
||||
|
|
||||
| // Cryptographic mixing
|
||||
| FOR i = 0 TO r - 1 DO // Ten or twelve rounds.
|
||||
| |
|
||||
| | // Message word selection permutation for this round.
|
||||
| | s[0..15] := SIGMA[i mod 10][0..15]
|
||||
| |
|
||||
| | v := G( v, 0, 4, 8, 12, m[s[ 0]], m[s[ 1]] )
|
||||
| | v := G( v, 1, 5, 9, 13, m[s[ 2]], m[s[ 3]] )
|
||||
| | v := G( v, 2, 6, 10, 14, m[s[ 4]], m[s[ 5]] )
|
||||
| | v := G( v, 3, 7, 11, 15, m[s[ 6]], m[s[ 7]] )
|
||||
| |
|
||||
| | v := G( v, 0, 5, 10, 15, m[s[ 8]], m[s[ 9]] )
|
||||
| | v := G( v, 1, 6, 11, 12, m[s[10]], m[s[11]] )
|
||||
| | v := G( v, 2, 7, 8, 13, m[s[12]], m[s[13]] )
|
||||
| | v := G( v, 3, 4, 9, 14, m[s[14]], m[s[15]] )
|
||||
| |
|
||||
| END FOR
|
||||
|
|
||||
| FOR i = 0 TO 7 DO // XOR the two halves.
|
||||
| | h[i] := h[i] ^ v[i] ^ v[i + 8]
|
||||
| END FOR.
|
||||
|
|
||||
| RETURN h[0..7] // New state.
|
||||
|
|
||||
END FUNCTION.
|
||||
*/
|
||||
|
||||
fn blake2s_compression<Scalar: PrimeField, CS: ConstraintSystem<Scalar>>(
|
||||
mut cs: CS,
|
||||
h: &mut [UInt32],
|
||||
m: &[UInt32],
|
||||
t: u64,
|
||||
f: bool,
|
||||
) -> Result<(), SynthesisError> {
|
||||
assert_eq!(h.len(), 8);
|
||||
assert_eq!(m.len(), 16);
|
||||
|
||||
/*
|
||||
static const uint32_t blake2s_iv[8] =
|
||||
{
|
||||
0x6A09E667, 0xBB67AE85, 0x3C6EF372, 0xA54FF53A,
|
||||
0x510E527F, 0x9B05688C, 0x1F83D9AB, 0x5BE0CD19
|
||||
};
|
||||
*/
|
||||
|
||||
let mut v = Vec::with_capacity(16);
|
||||
v.extend_from_slice(h);
|
||||
v.push(UInt32::constant(0x6A09E667));
|
||||
v.push(UInt32::constant(0xBB67AE85));
|
||||
v.push(UInt32::constant(0x3C6EF372));
|
||||
v.push(UInt32::constant(0xA54FF53A));
|
||||
v.push(UInt32::constant(0x510E527F));
|
||||
v.push(UInt32::constant(0x9B05688C));
|
||||
v.push(UInt32::constant(0x1F83D9AB));
|
||||
v.push(UInt32::constant(0x5BE0CD19));
|
||||
|
||||
assert_eq!(v.len(), 16);
|
||||
|
||||
v[12] = v[12].xor(cs.namespace(|| "first xor"), &UInt32::constant(t as u32))?;
|
||||
v[13] = v[13].xor(
|
||||
cs.namespace(|| "second xor"),
|
||||
&UInt32::constant((t >> 32) as u32),
|
||||
)?;
|
||||
|
||||
if f {
|
||||
v[14] = v[14].xor(
|
||||
cs.namespace(|| "third xor"),
|
||||
&UInt32::constant(u32::max_value()),
|
||||
)?;
|
||||
}
|
||||
|
||||
{
|
||||
let mut cs = MultiEq::new(&mut cs);
|
||||
|
||||
for i in 0..10 {
|
||||
let mut cs = cs.namespace(|| format!("round {}", i));
|
||||
|
||||
let s = SIGMA[i % 10];
|
||||
|
||||
mixing_g(
|
||||
cs.namespace(|| "mixing invocation 1"),
|
||||
&mut v,
|
||||
0,
|
||||
4,
|
||||
8,
|
||||
12,
|
||||
&m[s[0]],
|
||||
&m[s[1]],
|
||||
)?;
|
||||
mixing_g(
|
||||
cs.namespace(|| "mixing invocation 2"),
|
||||
&mut v,
|
||||
1,
|
||||
5,
|
||||
9,
|
||||
13,
|
||||
&m[s[2]],
|
||||
&m[s[3]],
|
||||
)?;
|
||||
mixing_g(
|
||||
cs.namespace(|| "mixing invocation 3"),
|
||||
&mut v,
|
||||
2,
|
||||
6,
|
||||
10,
|
||||
14,
|
||||
&m[s[4]],
|
||||
&m[s[5]],
|
||||
)?;
|
||||
mixing_g(
|
||||
cs.namespace(|| "mixing invocation 4"),
|
||||
&mut v,
|
||||
3,
|
||||
7,
|
||||
11,
|
||||
15,
|
||||
&m[s[6]],
|
||||
&m[s[7]],
|
||||
)?;
|
||||
|
||||
mixing_g(
|
||||
cs.namespace(|| "mixing invocation 5"),
|
||||
&mut v,
|
||||
0,
|
||||
5,
|
||||
10,
|
||||
15,
|
||||
&m[s[8]],
|
||||
&m[s[9]],
|
||||
)?;
|
||||
mixing_g(
|
||||
cs.namespace(|| "mixing invocation 6"),
|
||||
&mut v,
|
||||
1,
|
||||
6,
|
||||
11,
|
||||
12,
|
||||
&m[s[10]],
|
||||
&m[s[11]],
|
||||
)?;
|
||||
mixing_g(
|
||||
cs.namespace(|| "mixing invocation 7"),
|
||||
&mut v,
|
||||
2,
|
||||
7,
|
||||
8,
|
||||
13,
|
||||
&m[s[12]],
|
||||
&m[s[13]],
|
||||
)?;
|
||||
mixing_g(
|
||||
cs.namespace(|| "mixing invocation 8"),
|
||||
&mut v,
|
||||
3,
|
||||
4,
|
||||
9,
|
||||
14,
|
||||
&m[s[14]],
|
||||
&m[s[15]],
|
||||
)?;
|
||||
}
|
||||
}
|
||||
|
||||
for i in 0..8 {
|
||||
let mut cs = cs.namespace(|| format!("h[{i}] ^ v[{i}] ^ v[{i} + 8]", i = i));
|
||||
|
||||
h[i] = h[i].xor(cs.namespace(|| "first xor"), &v[i])?;
|
||||
h[i] = h[i].xor(cs.namespace(|| "second xor"), &v[i + 8])?;
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/*
|
||||
FUNCTION BLAKE2( d[0..dd-1], ll, kk, nn )
|
||||
|
|
||||
| h[0..7] := IV[0..7] // Initialization Vector.
|
||||
|
|
||||
| // Parameter block p[0]
|
||||
| h[0] := h[0] ^ 0x01010000 ^ (kk << 8) ^ nn
|
||||
|
|
||||
| // Process padded key and data blocks
|
||||
| IF dd > 1 THEN
|
||||
| | FOR i = 0 TO dd - 2 DO
|
||||
| | | h := F( h, d[i], (i + 1) * bb, FALSE )
|
||||
| | END FOR.
|
||||
| END IF.
|
||||
|
|
||||
| // Final block.
|
||||
| IF kk = 0 THEN
|
||||
| | h := F( h, d[dd - 1], ll, TRUE )
|
||||
| ELSE
|
||||
| | h := F( h, d[dd - 1], ll + bb, TRUE )
|
||||
| END IF.
|
||||
|
|
||||
| RETURN first "nn" bytes from little-endian word array h[].
|
||||
|
|
||||
END FUNCTION.
|
||||
*/
|
||||
|
||||
pub fn blake2s<Scalar: PrimeField, CS: ConstraintSystem<Scalar>>(
|
||||
mut cs: CS,
|
||||
input: &[Boolean],
|
||||
personalization: &[u8],
|
||||
) -> Result<Vec<Boolean>, SynthesisError> {
|
||||
use byteorder::{ByteOrder, LittleEndian};
|
||||
|
||||
assert_eq!(personalization.len(), 8);
|
||||
assert!(input.len() % 8 == 0);
|
||||
|
||||
let mut h = Vec::with_capacity(8);
|
||||
h.push(UInt32::constant(0x6A09E667 ^ 0x01010000 ^ 32));
|
||||
h.push(UInt32::constant(0xBB67AE85));
|
||||
h.push(UInt32::constant(0x3C6EF372));
|
||||
h.push(UInt32::constant(0xA54FF53A));
|
||||
h.push(UInt32::constant(0x510E527F));
|
||||
h.push(UInt32::constant(0x9B05688C));
|
||||
|
||||
// Personalization is stored here
|
||||
h.push(UInt32::constant(
|
||||
0x1F83D9AB ^ LittleEndian::read_u32(&personalization[0..4]),
|
||||
));
|
||||
h.push(UInt32::constant(
|
||||
0x5BE0CD19 ^ LittleEndian::read_u32(&personalization[4..8]),
|
||||
));
|
||||
|
||||
let mut blocks: Vec<Vec<UInt32>> = vec![];
|
||||
|
||||
for block in input.chunks(512) {
|
||||
let mut this_block = Vec::with_capacity(16);
|
||||
for word in block.chunks(32) {
|
||||
let mut tmp = word.to_vec();
|
||||
while tmp.len() < 32 {
|
||||
tmp.push(Boolean::constant(false));
|
||||
}
|
||||
this_block.push(UInt32::from_bits(&tmp));
|
||||
}
|
||||
while this_block.len() < 16 {
|
||||
this_block.push(UInt32::constant(0));
|
||||
}
|
||||
blocks.push(this_block);
|
||||
}
|
||||
|
||||
if blocks.is_empty() {
|
||||
blocks.push((0..16).map(|_| UInt32::constant(0)).collect());
|
||||
}
|
||||
|
||||
for (i, block) in blocks[0..blocks.len() - 1].iter().enumerate() {
|
||||
let cs = cs.namespace(|| format!("block {}", i));
|
||||
|
||||
blake2s_compression(cs, &mut h, block, ((i as u64) + 1) * 64, false)?;
|
||||
}
|
||||
|
||||
{
|
||||
let cs = cs.namespace(|| "final block");
|
||||
|
||||
blake2s_compression(
|
||||
cs,
|
||||
&mut h,
|
||||
&blocks[blocks.len() - 1],
|
||||
(input.len() / 8) as u64,
|
||||
true,
|
||||
)?;
|
||||
}
|
||||
|
||||
Ok(h.into_iter().flat_map(|b| b.into_bits()).collect())
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod test {
|
||||
use blake2s_simd::Params as Blake2sParams;
|
||||
use bls12_381::Scalar;
|
||||
use hex_literal::hex;
|
||||
use rand_core::{RngCore, SeedableRng};
|
||||
use rand_xorshift::XorShiftRng;
|
||||
|
||||
use super::blake2s;
|
||||
use crate::gadgets::boolean::{AllocatedBit, Boolean};
|
||||
use crate::gadgets::test::TestConstraintSystem;
|
||||
use crate::ConstraintSystem;
|
||||
|
||||
#[test]
|
||||
fn test_blank_hash() {
|
||||
let mut cs = TestConstraintSystem::<Scalar>::new();
|
||||
let input_bits = vec![];
|
||||
let out = blake2s(&mut cs, &input_bits, b"12345678").unwrap();
|
||||
assert!(cs.is_satisfied());
|
||||
assert_eq!(cs.num_constraints(), 0);
|
||||
|
||||
// >>> import blake2s from hashlib
|
||||
// >>> h = blake2s(digest_size=32, person=b'12345678')
|
||||
// >>> h.hexdigest()
|
||||
let expected = hex!("c59f682376d137f3f255e671e207d1f2374ebe504e9314208a52d9f88d69e8c8");
|
||||
|
||||
let mut out = out.into_iter();
|
||||
for b in expected.iter() {
|
||||
for i in 0..8 {
|
||||
let c = out.next().unwrap().get_value().unwrap();
|
||||
|
||||
assert_eq!(c, (b >> i) & 1u8 == 1u8);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_blake2s_constraints() {
|
||||
let mut cs = TestConstraintSystem::<Scalar>::new();
|
||||
let input_bits: Vec<_> = (0..512)
|
||||
.map(|i| {
|
||||
AllocatedBit::alloc(cs.namespace(|| format!("input bit {}", i)), Some(true))
|
||||
.unwrap()
|
||||
.into()
|
||||
})
|
||||
.collect();
|
||||
blake2s(&mut cs, &input_bits, b"12345678").unwrap();
|
||||
assert!(cs.is_satisfied());
|
||||
assert_eq!(cs.num_constraints(), 21518);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_blake2s_precomp_constraints() {
|
||||
// Test that 512 fixed leading bits (constants)
|
||||
// doesn't result in more constraints.
|
||||
|
||||
let mut cs = TestConstraintSystem::<Scalar>::new();
|
||||
let mut rng = XorShiftRng::from_seed([
|
||||
0x59, 0x62, 0xbe, 0x5d, 0x76, 0x3d, 0x31, 0x8d, 0x17, 0xdb, 0x37, 0x32, 0x54, 0x06,
|
||||
0xbc, 0xe5,
|
||||
]);
|
||||
let input_bits: Vec<_> = (0..512)
|
||||
.map(|_| Boolean::constant(rng.next_u32() % 2 != 0))
|
||||
.chain((0..512).map(|i| {
|
||||
AllocatedBit::alloc(cs.namespace(|| format!("input bit {}", i)), Some(true))
|
||||
.unwrap()
|
||||
.into()
|
||||
}))
|
||||
.collect();
|
||||
blake2s(&mut cs, &input_bits, b"12345678").unwrap();
|
||||
assert!(cs.is_satisfied());
|
||||
assert_eq!(cs.num_constraints(), 21518);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_blake2s_constant_constraints() {
|
||||
let mut cs = TestConstraintSystem::<Scalar>::new();
|
||||
let mut rng = XorShiftRng::from_seed([
|
||||
0x59, 0x62, 0xbe, 0x5d, 0x76, 0x3d, 0x31, 0x8d, 0x17, 0xdb, 0x37, 0x32, 0x54, 0x06,
|
||||
0xbc, 0xe5,
|
||||
]);
|
||||
let input_bits: Vec<_> = (0..512)
|
||||
.map(|_| Boolean::constant(rng.next_u32() % 2 != 0))
|
||||
.collect();
|
||||
blake2s(&mut cs, &input_bits, b"12345678").unwrap();
|
||||
assert_eq!(cs.num_constraints(), 0);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_blake2s() {
|
||||
let mut rng = XorShiftRng::from_seed([
|
||||
0x59, 0x62, 0xbe, 0x5d, 0x76, 0x3d, 0x31, 0x8d, 0x17, 0xdb, 0x37, 0x32, 0x54, 0x06,
|
||||
0xbc, 0xe5,
|
||||
]);
|
||||
|
||||
for input_len in (0..32).chain((32..256).filter(|a| a % 8 == 0)) {
|
||||
let mut h = Blake2sParams::new()
|
||||
.hash_length(32)
|
||||
.personal(b"12345678")
|
||||
.to_state();
|
||||
|
||||
let data: Vec<u8> = (0..input_len).map(|_| rng.next_u32() as u8).collect();
|
||||
|
||||
h.update(&data);
|
||||
|
||||
let hash_result = h.finalize();
|
||||
|
||||
let mut cs = TestConstraintSystem::<Scalar>::new();
|
||||
|
||||
let mut input_bits = vec![];
|
||||
|
||||
for (byte_i, input_byte) in data.into_iter().enumerate() {
|
||||
for bit_i in 0..8 {
|
||||
let cs = cs.namespace(|| format!("input bit {} {}", byte_i, bit_i));
|
||||
|
||||
input_bits.push(
|
||||
AllocatedBit::alloc(cs, Some((input_byte >> bit_i) & 1u8 == 1u8))
|
||||
.unwrap()
|
||||
.into(),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
let r = blake2s(&mut cs, &input_bits, b"12345678").unwrap();
|
||||
|
||||
assert!(cs.is_satisfied());
|
||||
|
||||
let mut s = hash_result
|
||||
.as_ref()
|
||||
.iter()
|
||||
.flat_map(|&byte| (0..8).map(move |i| (byte >> i) & 1u8 == 1u8));
|
||||
|
||||
for b in r {
|
||||
match b {
|
||||
Boolean::Is(b) => {
|
||||
assert!(s.next().unwrap() == b.get_value().unwrap());
|
||||
}
|
||||
Boolean::Not(b) => {
|
||||
assert!(s.next().unwrap() != b.get_value().unwrap());
|
||||
}
|
||||
Boolean::Constant(b) => {
|
||||
assert!(input_len == 0);
|
||||
assert!(s.next().unwrap() == b);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_blake2s_256_vars() {
|
||||
let data: Vec<u8> = hex!("be9f9c485e670acce8b1516a378176161b20583637b6f1c536fbc1158a0a3296831df2920e57a442d5738f4be4dd6be89dd7913fc8b4d1c0a815646a4d674b77f7caf313bd880bf759fcac27037c48c2b2a20acd2fd5248e3be426c84a341c0a3c63eaf36e0d537d10b8db5c6e4c801832c41eb1a3ed602177acded8b4b803bd34339d99a18b71df399641cc8dfae2ad193fcd74b5913e704551777160d14c78f2e8d5c32716a8599c1080cb89a40ccd6ba596694a8b4a065d9f2d0667ef423ed2e418093caff884540858b4f4b62acd47edcea880523e1b1cda8eb225c128c2e9e83f14f6e7448c5733a195cac7d79a53dde5083172462c45b2f799e42af1c9").to_vec();
|
||||
assert_eq!(data.len(), 256);
|
||||
|
||||
let mut cs = TestConstraintSystem::<Scalar>::new();
|
||||
|
||||
let mut input_bits = vec![];
|
||||
|
||||
for (byte_i, input_byte) in data.into_iter().enumerate() {
|
||||
for bit_i in 0..8 {
|
||||
let cs = cs.namespace(|| format!("input bit {} {}", byte_i, bit_i));
|
||||
|
||||
input_bits.push(
|
||||
AllocatedBit::alloc(cs, Some((input_byte >> bit_i) & 1u8 == 1u8))
|
||||
.unwrap()
|
||||
.into(),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
let r = blake2s(&mut cs, &input_bits, b"12345678").unwrap();
|
||||
|
||||
assert!(cs.is_satisfied());
|
||||
|
||||
let expected = hex!("0af5695115ced92c8a0341e43869209636e9aa6472e4576f0f2b996cf812b30e");
|
||||
|
||||
let mut out = r.into_iter();
|
||||
for b in expected.iter() {
|
||||
for i in 0..8 {
|
||||
let c = out.next().unwrap().get_value().unwrap();
|
||||
|
||||
assert_eq!(c, (b >> i) & 1u8 == 1u8);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_blake2s_700_vars() {
|
||||
let data: Vec<u8> = hex!("5dcfe8bab4c758d2eb1ddb7ef337583e0df3e2c358e1755b7cd303a658de9a1227eed1d1114179a5c3c38d692ff2cf2d4e5c92a9516de750106774bbf9f7d063f707f4c9b6a02c0a77e4feb99e036c3ccaee7d1a31cb144093aa074bc9da608f8ff30b39c3c60e4a243cc0bbd406d1262a7d6607b31c60275c6bcc8b0ac49a06a4b629a98693c5f7640f3bca45e4977cfabc5b17f52838af3433b1fd407dbbdc131e8e4bd58bcee85bbab4b57b656c6a2ec6cf852525bc8423675e2bf29159139cd5df99db94719f3f7167230e0d5bd76f6d7891b656732cef9c3c0d48a5fa3d7a879988157b39015a85451b25af0301ca5e759ac35fea79dca38c673ec6db9f3885d9103e2dcb3304bd3d59b0b1d01babc97ef8a74d91b6ab6bf50f29eb5adf7250a28fd85db37bff0133193635da69caeefc72979cf3bef1d2896d847eea7e8a81e0927893dbd010feb6fb845d0399007d9a148a0596d86cd8f4192631f975c560f4de8da5f712c161342063af3c11029d93d6df7ff46db48343499de9ec4786cac059c4025ef418c9fe40132428ff8b91259d71d1709ff066add84ae944b45a817f60b4c1bf719e39ae23e9b413469db2310793e9137cf38741e5dd2a3c138a566dbde1950c00071b20ac457b46ba9b0a7ebdddcc212bd228d2a4c4146a970e54158477247c27871af1564b176576e9fd43bf63740bf77434bc4ea3b1a4b430e1a11714bf43160145578a575c3f78ddeaa48de97f73460f26f8df2b5d63e31800100d16bc27160fea5ced5a977ef541cfe8dadc7b3991ed1c0d4f16a3076bbfed96ba3e155113e794987af8abb133f06feefabc2ac32eb4d4d4ba1541ca08b9e518d2e74b7f946b0cbd2663d58c689359b9a565821acc619011233d1011963fa302cde34fc9c5ba2e03eeb2512f547391e940d56218e22ae325f2dfa38d4bae35744ee707aa5dc9c17674025d15390a08f5c452343546ef6da0f7").to_vec();
|
||||
assert_eq!(data.len(), 700);
|
||||
|
||||
let mut cs = TestConstraintSystem::<Scalar>::new();
|
||||
|
||||
let mut input_bits = vec![];
|
||||
|
||||
for (byte_i, input_byte) in data.into_iter().enumerate() {
|
||||
for bit_i in 0..8 {
|
||||
let cs = cs.namespace(|| format!("input bit {} {}", byte_i, bit_i));
|
||||
|
||||
input_bits.push(
|
||||
AllocatedBit::alloc(cs, Some((input_byte >> bit_i) & 1u8 == 1u8))
|
||||
.unwrap()
|
||||
.into(),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
let r = blake2s(&mut cs, &input_bits, b"12345678").unwrap();
|
||||
|
||||
assert!(cs.is_satisfied());
|
||||
|
||||
let expected = hex!("2ab8f0683167ba220eef19dccf4f9b1a8193cc09b35e0235842323950530f18a");
|
||||
|
||||
let mut out = r.into_iter();
|
||||
for b in expected.iter() {
|
||||
for i in 0..8 {
|
||||
let c = out.next().unwrap().get_value().unwrap();
|
||||
|
||||
assert_eq!(c, (b >> i) & 1u8 == 1u8);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_blake2s_test_vectors() {
|
||||
let mut rng = XorShiftRng::from_seed([
|
||||
0x59, 0x62, 0xbe, 0x5d, 0x76, 0x3d, 0x31, 0x8d, 0x17, 0xdb, 0x37, 0x32, 0x54, 0x06,
|
||||
0xbc, 0xe5,
|
||||
]);
|
||||
|
||||
let expecteds = [
|
||||
hex!("a1309e334376c8f36a736a4ab0e691ef931ee3ebdb9ea96187127136fea622a1"),
|
||||
hex!("82fefff60f265cea255252f7c194a7f93965dffee0609ef74eb67f0d76cd41c6"),
|
||||
];
|
||||
for i in 0..2 {
|
||||
let mut h = Blake2sParams::new()
|
||||
.hash_length(32)
|
||||
.personal(b"12345678")
|
||||
.to_state();
|
||||
let input_len = 1024;
|
||||
let data: Vec<u8> = (0..input_len).map(|_| rng.next_u32() as u8).collect();
|
||||
|
||||
h.update(&data);
|
||||
|
||||
let hash_result = h.finalize();
|
||||
|
||||
let mut cs = TestConstraintSystem::<Scalar>::new();
|
||||
|
||||
let mut input_bits = vec![];
|
||||
|
||||
for (byte_i, input_byte) in data.into_iter().enumerate() {
|
||||
for bit_i in 0..8 {
|
||||
let cs = cs.namespace(|| format!("input bit {} {}", byte_i, bit_i));
|
||||
|
||||
input_bits.push(
|
||||
AllocatedBit::alloc(cs, Some((input_byte >> bit_i) & 1u8 == 1u8))
|
||||
.unwrap()
|
||||
.into(),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
let r = blake2s(&mut cs, &input_bits, b"12345678").unwrap();
|
||||
|
||||
assert!(cs.is_satisfied());
|
||||
|
||||
let mut s = hash_result
|
||||
.as_ref()
|
||||
.iter()
|
||||
.flat_map(|&byte| (0..8).map(move |i| (byte >> i) & 1u8 == 1u8));
|
||||
|
||||
for b in r {
|
||||
match b {
|
||||
Boolean::Is(b) => {
|
||||
assert!(s.next().unwrap() == b.get_value().unwrap());
|
||||
}
|
||||
Boolean::Not(b) => {
|
||||
assert!(s.next().unwrap() != b.get_value().unwrap());
|
||||
}
|
||||
Boolean::Constant(b) => {
|
||||
assert!(input_len == 0);
|
||||
assert!(s.next().unwrap() == b);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
assert_eq!(expecteds[i], hash_result.as_bytes());
|
||||
}
|
||||
}
|
||||
}
|
File diff suppressed because it is too large
Load Diff
|
@ -1,319 +0,0 @@
|
|||
//! Window table lookup gadgets.
|
||||
|
||||
use ff::PrimeField;
|
||||
|
||||
use super::boolean::Boolean;
|
||||
use super::num::{AllocatedNum, Num};
|
||||
use super::*;
|
||||
use crate::ConstraintSystem;
|
||||
|
||||
// Synthesize the constants for each base pattern.
|
||||
fn synth<'a, Scalar: PrimeField, I>(window_size: usize, constants: I, assignment: &mut [Scalar])
|
||||
where
|
||||
I: IntoIterator<Item = &'a Scalar>,
|
||||
{
|
||||
assert_eq!(assignment.len(), 1 << window_size);
|
||||
|
||||
for (i, constant) in constants.into_iter().enumerate() {
|
||||
let mut cur = assignment[i].neg();
|
||||
cur.add_assign(constant);
|
||||
assignment[i] = cur;
|
||||
for (j, eval) in assignment.iter_mut().enumerate().skip(i + 1) {
|
||||
if j & i == i {
|
||||
eval.add_assign(&cur);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Performs a 3-bit window table lookup. `bits` is in
|
||||
/// little-endian order.
|
||||
pub fn lookup3_xy<Scalar: PrimeField, CS>(
|
||||
mut cs: CS,
|
||||
bits: &[Boolean],
|
||||
coords: &[(Scalar, Scalar)],
|
||||
) -> Result<(AllocatedNum<Scalar>, AllocatedNum<Scalar>), SynthesisError>
|
||||
where
|
||||
CS: ConstraintSystem<Scalar>,
|
||||
{
|
||||
assert_eq!(bits.len(), 3);
|
||||
assert_eq!(coords.len(), 8);
|
||||
|
||||
// Calculate the index into `coords`
|
||||
let i = match (
|
||||
bits[0].get_value(),
|
||||
bits[1].get_value(),
|
||||
bits[2].get_value(),
|
||||
) {
|
||||
(Some(a_value), Some(b_value), Some(c_value)) => {
|
||||
let mut tmp = 0;
|
||||
if a_value {
|
||||
tmp += 1;
|
||||
}
|
||||
if b_value {
|
||||
tmp += 2;
|
||||
}
|
||||
if c_value {
|
||||
tmp += 4;
|
||||
}
|
||||
Some(tmp)
|
||||
}
|
||||
_ => None,
|
||||
};
|
||||
|
||||
// Allocate the x-coordinate resulting from the lookup
|
||||
let res_x = AllocatedNum::alloc(cs.namespace(|| "x"), || Ok(coords[*i.get()?].0))?;
|
||||
|
||||
// Allocate the y-coordinate resulting from the lookup
|
||||
let res_y = AllocatedNum::alloc(cs.namespace(|| "y"), || Ok(coords[*i.get()?].1))?;
|
||||
|
||||
// Compute the coefficients for the lookup constraints
|
||||
let mut x_coeffs = [Scalar::zero(); 8];
|
||||
let mut y_coeffs = [Scalar::zero(); 8];
|
||||
synth::<Scalar, _>(3, coords.iter().map(|c| &c.0), &mut x_coeffs);
|
||||
synth::<Scalar, _>(3, coords.iter().map(|c| &c.1), &mut y_coeffs);
|
||||
|
||||
let precomp = Boolean::and(cs.namespace(|| "precomp"), &bits[1], &bits[2])?;
|
||||
|
||||
let one = CS::one();
|
||||
|
||||
cs.enforce(
|
||||
|| "x-coordinate lookup",
|
||||
|lc| {
|
||||
lc + (x_coeffs[0b001], one)
|
||||
+ &bits[1].lc::<Scalar>(one, x_coeffs[0b011])
|
||||
+ &bits[2].lc::<Scalar>(one, x_coeffs[0b101])
|
||||
+ &precomp.lc::<Scalar>(one, x_coeffs[0b111])
|
||||
},
|
||||
|lc| lc + &bits[0].lc::<Scalar>(one, Scalar::one()),
|
||||
|lc| {
|
||||
lc + res_x.get_variable()
|
||||
- (x_coeffs[0b000], one)
|
||||
- &bits[1].lc::<Scalar>(one, x_coeffs[0b010])
|
||||
- &bits[2].lc::<Scalar>(one, x_coeffs[0b100])
|
||||
- &precomp.lc::<Scalar>(one, x_coeffs[0b110])
|
||||
},
|
||||
);
|
||||
|
||||
cs.enforce(
|
||||
|| "y-coordinate lookup",
|
||||
|lc| {
|
||||
lc + (y_coeffs[0b001], one)
|
||||
+ &bits[1].lc::<Scalar>(one, y_coeffs[0b011])
|
||||
+ &bits[2].lc::<Scalar>(one, y_coeffs[0b101])
|
||||
+ &precomp.lc::<Scalar>(one, y_coeffs[0b111])
|
||||
},
|
||||
|lc| lc + &bits[0].lc::<Scalar>(one, Scalar::one()),
|
||||
|lc| {
|
||||
lc + res_y.get_variable()
|
||||
- (y_coeffs[0b000], one)
|
||||
- &bits[1].lc::<Scalar>(one, y_coeffs[0b010])
|
||||
- &bits[2].lc::<Scalar>(one, y_coeffs[0b100])
|
||||
- &precomp.lc::<Scalar>(one, y_coeffs[0b110])
|
||||
},
|
||||
);
|
||||
|
||||
Ok((res_x, res_y))
|
||||
}
|
||||
|
||||
/// Performs a 3-bit window table lookup, where
|
||||
/// one of the bits is a sign bit.
|
||||
pub fn lookup3_xy_with_conditional_negation<Scalar: PrimeField, CS>(
|
||||
mut cs: CS,
|
||||
bits: &[Boolean],
|
||||
coords: &[(Scalar, Scalar)],
|
||||
) -> Result<(Num<Scalar>, Num<Scalar>), SynthesisError>
|
||||
where
|
||||
CS: ConstraintSystem<Scalar>,
|
||||
{
|
||||
assert_eq!(bits.len(), 3);
|
||||
assert_eq!(coords.len(), 4);
|
||||
|
||||
// Calculate the index into `coords`
|
||||
let i = match (bits[0].get_value(), bits[1].get_value()) {
|
||||
(Some(a_value), Some(b_value)) => {
|
||||
let mut tmp = 0;
|
||||
if a_value {
|
||||
tmp += 1;
|
||||
}
|
||||
if b_value {
|
||||
tmp += 2;
|
||||
}
|
||||
Some(tmp)
|
||||
}
|
||||
_ => None,
|
||||
};
|
||||
|
||||
// Allocate the y-coordinate resulting from the lookup
|
||||
// and conditional negation
|
||||
let y = AllocatedNum::alloc(cs.namespace(|| "y"), || {
|
||||
let mut tmp = coords[*i.get()?].1;
|
||||
if *bits[2].get_value().get()? {
|
||||
tmp = tmp.neg();
|
||||
}
|
||||
Ok(tmp)
|
||||
})?;
|
||||
|
||||
let one = CS::one();
|
||||
|
||||
// Compute the coefficients for the lookup constraints
|
||||
let mut x_coeffs = [Scalar::zero(); 4];
|
||||
let mut y_coeffs = [Scalar::zero(); 4];
|
||||
synth::<Scalar, _>(2, coords.iter().map(|c| &c.0), &mut x_coeffs);
|
||||
synth::<Scalar, _>(2, coords.iter().map(|c| &c.1), &mut y_coeffs);
|
||||
|
||||
let precomp = Boolean::and(cs.namespace(|| "precomp"), &bits[0], &bits[1])?;
|
||||
|
||||
let x = Num::zero()
|
||||
.add_bool_with_coeff(one, &Boolean::constant(true), x_coeffs[0b00])
|
||||
.add_bool_with_coeff(one, &bits[0], x_coeffs[0b01])
|
||||
.add_bool_with_coeff(one, &bits[1], x_coeffs[0b10])
|
||||
.add_bool_with_coeff(one, &precomp, x_coeffs[0b11]);
|
||||
|
||||
let y_lc = precomp.lc::<Scalar>(one, y_coeffs[0b11])
|
||||
+ &bits[1].lc::<Scalar>(one, y_coeffs[0b10])
|
||||
+ &bits[0].lc::<Scalar>(one, y_coeffs[0b01])
|
||||
+ (y_coeffs[0b00], one);
|
||||
|
||||
cs.enforce(
|
||||
|| "y-coordinate lookup",
|
||||
|lc| lc + &y_lc + &y_lc,
|
||||
|lc| lc + &bits[2].lc::<Scalar>(one, Scalar::one()),
|
||||
|lc| lc + &y_lc - y.get_variable(),
|
||||
);
|
||||
|
||||
Ok((x, y.into()))
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod test {
|
||||
use super::*;
|
||||
use crate::gadgets::boolean::{AllocatedBit, Boolean};
|
||||
use crate::gadgets::test::*;
|
||||
|
||||
use bls12_381::Scalar;
|
||||
use ff::Field;
|
||||
use rand_core::{RngCore, SeedableRng};
|
||||
use rand_xorshift::XorShiftRng;
|
||||
use std::ops::{AddAssign, Neg};
|
||||
|
||||
#[test]
|
||||
fn test_lookup3_xy() {
|
||||
let mut rng = XorShiftRng::from_seed([
|
||||
0x59, 0x62, 0xbe, 0x3d, 0x76, 0x3d, 0x31, 0x8d, 0x17, 0xdb, 0x37, 0x32, 0x54, 0x06,
|
||||
0xbc, 0xe5,
|
||||
]);
|
||||
|
||||
for _ in 0..100 {
|
||||
let mut cs = TestConstraintSystem::new();
|
||||
|
||||
let a_val = rng.next_u32() % 2 != 0;
|
||||
let a = Boolean::from(AllocatedBit::alloc(cs.namespace(|| "a"), Some(a_val)).unwrap());
|
||||
|
||||
let b_val = rng.next_u32() % 2 != 0;
|
||||
let b = Boolean::from(AllocatedBit::alloc(cs.namespace(|| "b"), Some(b_val)).unwrap());
|
||||
|
||||
let c_val = rng.next_u32() % 2 != 0;
|
||||
let c = Boolean::from(AllocatedBit::alloc(cs.namespace(|| "c"), Some(c_val)).unwrap());
|
||||
|
||||
let bits = vec![a, b, c];
|
||||
|
||||
let points: Vec<(Scalar, Scalar)> = (0..8)
|
||||
.map(|_| (Scalar::random(&mut rng), Scalar::random(&mut rng)))
|
||||
.collect();
|
||||
|
||||
let res = lookup3_xy(&mut cs, &bits, &points).unwrap();
|
||||
|
||||
assert!(cs.is_satisfied());
|
||||
|
||||
let mut index = 0;
|
||||
if a_val {
|
||||
index += 1
|
||||
}
|
||||
if b_val {
|
||||
index += 2
|
||||
}
|
||||
if c_val {
|
||||
index += 4
|
||||
}
|
||||
|
||||
assert_eq!(res.0.get_value().unwrap(), points[index].0);
|
||||
assert_eq!(res.1.get_value().unwrap(), points[index].1);
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_lookup3_xy_with_conditional_negation() {
|
||||
let mut rng = XorShiftRng::from_seed([
|
||||
0x59, 0x62, 0xbe, 0x3d, 0x76, 0x3d, 0x31, 0x8d, 0x17, 0xdb, 0x37, 0x32, 0x54, 0x06,
|
||||
0xbc, 0xe5,
|
||||
]);
|
||||
|
||||
for _ in 0..100 {
|
||||
let mut cs = TestConstraintSystem::new();
|
||||
|
||||
let a_val = rng.next_u32() % 2 != 0;
|
||||
let a = Boolean::from(AllocatedBit::alloc(cs.namespace(|| "a"), Some(a_val)).unwrap());
|
||||
|
||||
let b_val = rng.next_u32() % 2 != 0;
|
||||
let b = Boolean::from(AllocatedBit::alloc(cs.namespace(|| "b"), Some(b_val)).unwrap());
|
||||
|
||||
let c_val = rng.next_u32() % 2 != 0;
|
||||
let c = Boolean::from(AllocatedBit::alloc(cs.namespace(|| "c"), Some(c_val)).unwrap());
|
||||
|
||||
let bits = vec![a, b, c];
|
||||
|
||||
let points: Vec<(Scalar, Scalar)> = (0..4)
|
||||
.map(|_| (Scalar::random(&mut rng), Scalar::random(&mut rng)))
|
||||
.collect();
|
||||
|
||||
let res = lookup3_xy_with_conditional_negation(&mut cs, &bits, &points).unwrap();
|
||||
|
||||
assert!(cs.is_satisfied());
|
||||
|
||||
let mut index = 0;
|
||||
if a_val {
|
||||
index += 1
|
||||
}
|
||||
if b_val {
|
||||
index += 2
|
||||
}
|
||||
|
||||
assert_eq!(res.0.get_value().unwrap(), points[index].0);
|
||||
let mut tmp = points[index].1;
|
||||
if c_val {
|
||||
tmp = tmp.neg()
|
||||
}
|
||||
assert_eq!(res.1.get_value().unwrap(), tmp);
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_synth() {
|
||||
let mut rng = XorShiftRng::from_seed([
|
||||
0x59, 0x62, 0xbe, 0x3d, 0x76, 0x3d, 0x31, 0x8d, 0x17, 0xdb, 0x37, 0x32, 0x54, 0x06,
|
||||
0xbc, 0xe5,
|
||||
]);
|
||||
|
||||
let window_size = 4;
|
||||
|
||||
let mut assignment = vec![Scalar::zero(); 1 << window_size];
|
||||
let constants: Vec<_> = (0..(1 << window_size))
|
||||
.map(|_| Scalar::random(&mut rng))
|
||||
.collect();
|
||||
|
||||
synth(window_size, &constants, &mut assignment);
|
||||
|
||||
for b in 0..(1 << window_size) {
|
||||
let mut acc = Scalar::zero();
|
||||
|
||||
for j in 0..(1 << window_size) {
|
||||
if j & b == j {
|
||||
acc.add_assign(&assignment[j]);
|
||||
}
|
||||
}
|
||||
|
||||
assert_eq!(acc, constants[b]);
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1,123 +0,0 @@
|
|||
use ff::PrimeField;
|
||||
|
||||
use crate::{ConstraintSystem, LinearCombination, SynthesisError, Variable};
|
||||
|
||||
pub struct MultiEq<Scalar: PrimeField, CS: ConstraintSystem<Scalar>> {
|
||||
cs: CS,
|
||||
ops: usize,
|
||||
bits_used: usize,
|
||||
lhs: LinearCombination<Scalar>,
|
||||
rhs: LinearCombination<Scalar>,
|
||||
}
|
||||
|
||||
impl<Scalar: PrimeField, CS: ConstraintSystem<Scalar>> MultiEq<Scalar, CS> {
|
||||
pub fn new(cs: CS) -> Self {
|
||||
MultiEq {
|
||||
cs,
|
||||
ops: 0,
|
||||
bits_used: 0,
|
||||
lhs: LinearCombination::zero(),
|
||||
rhs: LinearCombination::zero(),
|
||||
}
|
||||
}
|
||||
|
||||
fn accumulate(&mut self) {
|
||||
let ops = self.ops;
|
||||
let lhs = self.lhs.clone();
|
||||
let rhs = self.rhs.clone();
|
||||
self.cs.enforce(
|
||||
|| format!("multieq {}", ops),
|
||||
|_| lhs,
|
||||
|lc| lc + CS::one(),
|
||||
|_| rhs,
|
||||
);
|
||||
self.lhs = LinearCombination::zero();
|
||||
self.rhs = LinearCombination::zero();
|
||||
self.bits_used = 0;
|
||||
self.ops += 1;
|
||||
}
|
||||
|
||||
pub fn enforce_equal(
|
||||
&mut self,
|
||||
num_bits: usize,
|
||||
lhs: &LinearCombination<Scalar>,
|
||||
rhs: &LinearCombination<Scalar>,
|
||||
) {
|
||||
// Check if we will exceed the capacity
|
||||
if (Scalar::CAPACITY as usize) <= (self.bits_used + num_bits) {
|
||||
self.accumulate();
|
||||
}
|
||||
|
||||
assert!((Scalar::CAPACITY as usize) > (self.bits_used + num_bits));
|
||||
|
||||
let coeff = Scalar::from_str("2")
|
||||
.unwrap()
|
||||
.pow_vartime(&[self.bits_used as u64]);
|
||||
self.lhs = self.lhs.clone() + (coeff, lhs);
|
||||
self.rhs = self.rhs.clone() + (coeff, rhs);
|
||||
self.bits_used += num_bits;
|
||||
}
|
||||
}
|
||||
|
||||
impl<Scalar: PrimeField, CS: ConstraintSystem<Scalar>> Drop for MultiEq<Scalar, CS> {
|
||||
fn drop(&mut self) {
|
||||
if self.bits_used > 0 {
|
||||
self.accumulate();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<Scalar: PrimeField, CS: ConstraintSystem<Scalar>> ConstraintSystem<Scalar>
|
||||
for MultiEq<Scalar, CS>
|
||||
{
|
||||
type Root = Self;
|
||||
|
||||
fn one() -> Variable {
|
||||
CS::one()
|
||||
}
|
||||
|
||||
fn alloc<F, A, AR>(&mut self, annotation: A, f: F) -> Result<Variable, SynthesisError>
|
||||
where
|
||||
F: FnOnce() -> Result<Scalar, SynthesisError>,
|
||||
A: FnOnce() -> AR,
|
||||
AR: Into<String>,
|
||||
{
|
||||
self.cs.alloc(annotation, f)
|
||||
}
|
||||
|
||||
fn alloc_input<F, A, AR>(&mut self, annotation: A, f: F) -> Result<Variable, SynthesisError>
|
||||
where
|
||||
F: FnOnce() -> Result<Scalar, SynthesisError>,
|
||||
A: FnOnce() -> AR,
|
||||
AR: Into<String>,
|
||||
{
|
||||
self.cs.alloc_input(annotation, f)
|
||||
}
|
||||
|
||||
fn enforce<A, AR, LA, LB, LC>(&mut self, annotation: A, a: LA, b: LB, c: LC)
|
||||
where
|
||||
A: FnOnce() -> AR,
|
||||
AR: Into<String>,
|
||||
LA: FnOnce(LinearCombination<Scalar>) -> LinearCombination<Scalar>,
|
||||
LB: FnOnce(LinearCombination<Scalar>) -> LinearCombination<Scalar>,
|
||||
LC: FnOnce(LinearCombination<Scalar>) -> LinearCombination<Scalar>,
|
||||
{
|
||||
self.cs.enforce(annotation, a, b, c)
|
||||
}
|
||||
|
||||
fn push_namespace<NR, N>(&mut self, name_fn: N)
|
||||
where
|
||||
NR: Into<String>,
|
||||
N: FnOnce() -> NR,
|
||||
{
|
||||
self.cs.get_root().push_namespace(name_fn)
|
||||
}
|
||||
|
||||
fn pop_namespace(&mut self) {
|
||||
self.cs.get_root().pop_namespace()
|
||||
}
|
||||
|
||||
fn get_root(&mut self) -> &mut Self::Root {
|
||||
self
|
||||
}
|
||||
}
|
|
@ -1,111 +0,0 @@
|
|||
//! Helpers for packing vectors of bits into scalar field elements.
|
||||
|
||||
use super::boolean::Boolean;
|
||||
use super::num::Num;
|
||||
use super::Assignment;
|
||||
use crate::{ConstraintSystem, SynthesisError};
|
||||
use ff::PrimeField;
|
||||
|
||||
/// Takes a sequence of booleans and exposes them as compact
|
||||
/// public inputs
|
||||
pub fn pack_into_inputs<Scalar, CS>(mut cs: CS, bits: &[Boolean]) -> Result<(), SynthesisError>
|
||||
where
|
||||
Scalar: PrimeField,
|
||||
CS: ConstraintSystem<Scalar>,
|
||||
{
|
||||
for (i, bits) in bits.chunks(Scalar::CAPACITY as usize).enumerate() {
|
||||
let mut num = Num::<Scalar>::zero();
|
||||
let mut coeff = Scalar::one();
|
||||
for bit in bits {
|
||||
num = num.add_bool_with_coeff(CS::one(), bit, coeff);
|
||||
|
||||
coeff = coeff.double();
|
||||
}
|
||||
|
||||
let input = cs.alloc_input(|| format!("input {}", i), || Ok(*num.get_value().get()?))?;
|
||||
|
||||
// num * 1 = input
|
||||
cs.enforce(
|
||||
|| format!("packing constraint {}", i),
|
||||
|_| num.lc(Scalar::one()),
|
||||
|lc| lc + CS::one(),
|
||||
|lc| lc + input,
|
||||
);
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub fn bytes_to_bits(bytes: &[u8]) -> Vec<bool> {
|
||||
bytes
|
||||
.iter()
|
||||
.flat_map(|&v| (0..8).rev().map(move |i| (v >> i) & 1 == 1))
|
||||
.collect()
|
||||
}
|
||||
|
||||
pub fn bytes_to_bits_le(bytes: &[u8]) -> Vec<bool> {
|
||||
bytes
|
||||
.iter()
|
||||
.flat_map(|&v| (0..8).map(move |i| (v >> i) & 1 == 1))
|
||||
.collect()
|
||||
}
|
||||
|
||||
pub fn compute_multipacking<Scalar: PrimeField>(bits: &[bool]) -> Vec<Scalar> {
|
||||
let mut result = vec![];
|
||||
|
||||
for bits in bits.chunks(Scalar::CAPACITY as usize) {
|
||||
let mut cur = Scalar::zero();
|
||||
let mut coeff = Scalar::one();
|
||||
|
||||
for bit in bits {
|
||||
if *bit {
|
||||
cur.add_assign(&coeff);
|
||||
}
|
||||
|
||||
coeff = coeff.double();
|
||||
}
|
||||
|
||||
result.push(cur);
|
||||
}
|
||||
|
||||
result
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_multipacking() {
|
||||
use crate::ConstraintSystem;
|
||||
use bls12_381::Scalar;
|
||||
use rand_core::{RngCore, SeedableRng};
|
||||
use rand_xorshift::XorShiftRng;
|
||||
|
||||
use super::boolean::{AllocatedBit, Boolean};
|
||||
use crate::gadgets::test::*;
|
||||
|
||||
let mut rng = XorShiftRng::from_seed([
|
||||
0x59, 0x62, 0xbe, 0x3d, 0x76, 0x3d, 0x31, 0x8d, 0x17, 0xdb, 0x37, 0x32, 0x54, 0x06, 0xbc,
|
||||
0xe5,
|
||||
]);
|
||||
|
||||
for num_bits in 0..1500 {
|
||||
let mut cs = TestConstraintSystem::<Scalar>::new();
|
||||
|
||||
let bits: Vec<bool> = (0..num_bits).map(|_| rng.next_u32() % 2 != 0).collect();
|
||||
|
||||
let circuit_bits = bits
|
||||
.iter()
|
||||
.enumerate()
|
||||
.map(|(i, &b)| {
|
||||
Boolean::from(
|
||||
AllocatedBit::alloc(cs.namespace(|| format!("bit {}", i)), Some(b)).unwrap(),
|
||||
)
|
||||
})
|
||||
.collect::<Vec<_>>();
|
||||
|
||||
let expected_inputs = compute_multipacking(&bits);
|
||||
|
||||
pack_into_inputs(cs.namespace(|| "pack"), &circuit_bits).unwrap();
|
||||
|
||||
assert!(cs.is_satisfied());
|
||||
assert!(cs.verify(&expected_inputs));
|
||||
}
|
||||
}
|
|
@ -1,591 +0,0 @@
|
|||
//! Gadgets representing numbers in the scalar field of the underlying curve.
|
||||
|
||||
use ff::{BitIterator, PrimeField};
|
||||
|
||||
use crate::{ConstraintSystem, LinearCombination, SynthesisError, Variable};
|
||||
|
||||
use super::Assignment;
|
||||
|
||||
use super::boolean::{self, AllocatedBit, Boolean};
|
||||
|
||||
pub struct AllocatedNum<Scalar: PrimeField> {
|
||||
value: Option<Scalar>,
|
||||
variable: Variable,
|
||||
}
|
||||
|
||||
impl<Scalar: PrimeField> Clone for AllocatedNum<Scalar> {
|
||||
fn clone(&self) -> Self {
|
||||
AllocatedNum {
|
||||
value: self.value,
|
||||
variable: self.variable,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<Scalar: PrimeField> AllocatedNum<Scalar> {
|
||||
pub fn alloc<CS, F>(mut cs: CS, value: F) -> Result<Self, SynthesisError>
|
||||
where
|
||||
CS: ConstraintSystem<Scalar>,
|
||||
F: FnOnce() -> Result<Scalar, SynthesisError>,
|
||||
{
|
||||
let mut new_value = None;
|
||||
let var = cs.alloc(
|
||||
|| "num",
|
||||
|| {
|
||||
let tmp = value()?;
|
||||
|
||||
new_value = Some(tmp);
|
||||
|
||||
Ok(tmp)
|
||||
},
|
||||
)?;
|
||||
|
||||
Ok(AllocatedNum {
|
||||
value: new_value,
|
||||
variable: var,
|
||||
})
|
||||
}
|
||||
|
||||
pub fn inputize<CS>(&self, mut cs: CS) -> Result<(), SynthesisError>
|
||||
where
|
||||
CS: ConstraintSystem<Scalar>,
|
||||
{
|
||||
let input = cs.alloc_input(|| "input variable", || Ok(*self.value.get()?))?;
|
||||
|
||||
cs.enforce(
|
||||
|| "enforce input is correct",
|
||||
|lc| lc + input,
|
||||
|lc| lc + CS::one(),
|
||||
|lc| lc + self.variable,
|
||||
);
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// Deconstructs this allocated number into its
|
||||
/// boolean representation in little-endian bit
|
||||
/// order, requiring that the representation
|
||||
/// strictly exists "in the field" (i.e., a
|
||||
/// congruency is not allowed.)
|
||||
pub fn to_bits_le_strict<CS>(&self, mut cs: CS) -> Result<Vec<Boolean>, SynthesisError>
|
||||
where
|
||||
CS: ConstraintSystem<Scalar>,
|
||||
{
|
||||
pub fn kary_and<Scalar, CS>(
|
||||
mut cs: CS,
|
||||
v: &[AllocatedBit],
|
||||
) -> Result<AllocatedBit, SynthesisError>
|
||||
where
|
||||
Scalar: PrimeField,
|
||||
CS: ConstraintSystem<Scalar>,
|
||||
{
|
||||
assert!(!v.is_empty());
|
||||
|
||||
// Let's keep this simple for now and just AND them all
|
||||
// manually
|
||||
let mut cur = None;
|
||||
|
||||
for (i, v) in v.iter().enumerate() {
|
||||
if cur.is_none() {
|
||||
cur = Some(v.clone());
|
||||
} else {
|
||||
cur = Some(AllocatedBit::and(
|
||||
cs.namespace(|| format!("and {}", i)),
|
||||
cur.as_ref().unwrap(),
|
||||
v,
|
||||
)?);
|
||||
}
|
||||
}
|
||||
|
||||
Ok(cur.expect("v.len() > 0"))
|
||||
}
|
||||
|
||||
// We want to ensure that the bit representation of a is
|
||||
// less than or equal to r - 1.
|
||||
let mut a = self.value.map(|e| BitIterator::<u8, _>::new(e.to_repr()));
|
||||
let b = (-Scalar::one()).to_repr();
|
||||
|
||||
let mut result = vec![];
|
||||
|
||||
// Runs of ones in r
|
||||
let mut last_run = None;
|
||||
let mut current_run = vec![];
|
||||
|
||||
let mut found_one = false;
|
||||
let mut i = 0;
|
||||
for b in BitIterator::<u8, _>::new(b) {
|
||||
let a_bit = a.as_mut().map(|e| e.next().unwrap());
|
||||
|
||||
// Skip over unset bits at the beginning
|
||||
found_one |= b;
|
||||
if !found_one {
|
||||
// a_bit should also be false
|
||||
a_bit.map(|e| assert!(!e));
|
||||
continue;
|
||||
}
|
||||
|
||||
if b {
|
||||
// This is part of a run of ones. Let's just
|
||||
// allocate the boolean with the expected value.
|
||||
let a_bit = AllocatedBit::alloc(cs.namespace(|| format!("bit {}", i)), a_bit)?;
|
||||
// ... and add it to the current run of ones.
|
||||
current_run.push(a_bit.clone());
|
||||
result.push(a_bit);
|
||||
} else {
|
||||
if !current_run.is_empty() {
|
||||
// This is the start of a run of zeros, but we need
|
||||
// to k-ary AND against `last_run` first.
|
||||
|
||||
if last_run.is_some() {
|
||||
current_run.push(last_run.clone().unwrap());
|
||||
}
|
||||
last_run = Some(kary_and(
|
||||
cs.namespace(|| format!("run ending at {}", i)),
|
||||
¤t_run,
|
||||
)?);
|
||||
current_run.truncate(0);
|
||||
}
|
||||
|
||||
// If `last_run` is true, `a` must be false, or it would
|
||||
// not be in the field.
|
||||
//
|
||||
// If `last_run` is false, `a` can be true or false.
|
||||
|
||||
let a_bit = AllocatedBit::alloc_conditionally(
|
||||
cs.namespace(|| format!("bit {}", i)),
|
||||
a_bit,
|
||||
&last_run.as_ref().expect("char always starts with a one"),
|
||||
)?;
|
||||
result.push(a_bit);
|
||||
}
|
||||
|
||||
i += 1;
|
||||
}
|
||||
|
||||
// char is prime, so we'll always end on
|
||||
// a run of zeros.
|
||||
assert_eq!(current_run.len(), 0);
|
||||
|
||||
// Now, we have `result` in big-endian order.
|
||||
// However, now we have to unpack self!
|
||||
|
||||
let mut lc = LinearCombination::zero();
|
||||
let mut coeff = Scalar::one();
|
||||
|
||||
for bit in result.iter().rev() {
|
||||
lc = lc + (coeff, bit.get_variable());
|
||||
|
||||
coeff = coeff.double();
|
||||
}
|
||||
|
||||
lc = lc - self.variable;
|
||||
|
||||
cs.enforce(|| "unpacking constraint", |lc| lc, |lc| lc, |_| lc);
|
||||
|
||||
// Convert into booleans, and reverse for little-endian bit order
|
||||
Ok(result.into_iter().map(Boolean::from).rev().collect())
|
||||
}
|
||||
|
||||
/// Convert the allocated number into its little-endian representation.
|
||||
/// Note that this does not strongly enforce that the commitment is
|
||||
/// "in the field."
|
||||
pub fn to_bits_le<CS>(&self, mut cs: CS) -> Result<Vec<Boolean>, SynthesisError>
|
||||
where
|
||||
CS: ConstraintSystem<Scalar>,
|
||||
{
|
||||
let bits = boolean::field_into_allocated_bits_le(&mut cs, self.value)?;
|
||||
|
||||
let mut lc = LinearCombination::zero();
|
||||
let mut coeff = Scalar::one();
|
||||
|
||||
for bit in bits.iter() {
|
||||
lc = lc + (coeff, bit.get_variable());
|
||||
|
||||
coeff = coeff.double();
|
||||
}
|
||||
|
||||
lc = lc - self.variable;
|
||||
|
||||
cs.enforce(|| "unpacking constraint", |lc| lc, |lc| lc, |_| lc);
|
||||
|
||||
Ok(bits.into_iter().map(Boolean::from).collect())
|
||||
}
|
||||
|
||||
pub fn mul<CS>(&self, mut cs: CS, other: &Self) -> Result<Self, SynthesisError>
|
||||
where
|
||||
CS: ConstraintSystem<Scalar>,
|
||||
{
|
||||
let mut value = None;
|
||||
|
||||
let var = cs.alloc(
|
||||
|| "product num",
|
||||
|| {
|
||||
let mut tmp = *self.value.get()?;
|
||||
tmp.mul_assign(other.value.get()?);
|
||||
|
||||
value = Some(tmp);
|
||||
|
||||
Ok(tmp)
|
||||
},
|
||||
)?;
|
||||
|
||||
// Constrain: a * b = ab
|
||||
cs.enforce(
|
||||
|| "multiplication constraint",
|
||||
|lc| lc + self.variable,
|
||||
|lc| lc + other.variable,
|
||||
|lc| lc + var,
|
||||
);
|
||||
|
||||
Ok(AllocatedNum {
|
||||
value,
|
||||
variable: var,
|
||||
})
|
||||
}
|
||||
|
||||
pub fn square<CS>(&self, mut cs: CS) -> Result<Self, SynthesisError>
|
||||
where
|
||||
CS: ConstraintSystem<Scalar>,
|
||||
{
|
||||
let mut value = None;
|
||||
|
||||
let var = cs.alloc(
|
||||
|| "squared num",
|
||||
|| {
|
||||
let tmp = self.value.get()?.square();
|
||||
|
||||
value = Some(tmp);
|
||||
|
||||
Ok(tmp)
|
||||
},
|
||||
)?;
|
||||
|
||||
// Constrain: a * a = aa
|
||||
cs.enforce(
|
||||
|| "squaring constraint",
|
||||
|lc| lc + self.variable,
|
||||
|lc| lc + self.variable,
|
||||
|lc| lc + var,
|
||||
);
|
||||
|
||||
Ok(AllocatedNum {
|
||||
value,
|
||||
variable: var,
|
||||
})
|
||||
}
|
||||
|
||||
pub fn assert_nonzero<CS>(&self, mut cs: CS) -> Result<(), SynthesisError>
|
||||
where
|
||||
CS: ConstraintSystem<Scalar>,
|
||||
{
|
||||
let inv = cs.alloc(
|
||||
|| "ephemeral inverse",
|
||||
|| {
|
||||
let tmp = *self.value.get()?;
|
||||
|
||||
if tmp.is_zero() {
|
||||
Err(SynthesisError::DivisionByZero)
|
||||
} else {
|
||||
Ok(tmp.invert().unwrap())
|
||||
}
|
||||
},
|
||||
)?;
|
||||
|
||||
// Constrain a * inv = 1, which is only valid
|
||||
// iff a has a multiplicative inverse, untrue
|
||||
// for zero.
|
||||
cs.enforce(
|
||||
|| "nonzero assertion constraint",
|
||||
|lc| lc + self.variable,
|
||||
|lc| lc + inv,
|
||||
|lc| lc + CS::one(),
|
||||
);
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// Takes two allocated numbers (a, b) and returns
|
||||
/// (b, a) if the condition is true, and (a, b)
|
||||
/// otherwise.
|
||||
pub fn conditionally_reverse<CS>(
|
||||
mut cs: CS,
|
||||
a: &Self,
|
||||
b: &Self,
|
||||
condition: &Boolean,
|
||||
) -> Result<(Self, Self), SynthesisError>
|
||||
where
|
||||
CS: ConstraintSystem<Scalar>,
|
||||
{
|
||||
let c = Self::alloc(cs.namespace(|| "conditional reversal result 1"), || {
|
||||
if *condition.get_value().get()? {
|
||||
Ok(*b.value.get()?)
|
||||
} else {
|
||||
Ok(*a.value.get()?)
|
||||
}
|
||||
})?;
|
||||
|
||||
cs.enforce(
|
||||
|| "first conditional reversal",
|
||||
|lc| lc + a.variable - b.variable,
|
||||
|_| condition.lc(CS::one(), Scalar::one()),
|
||||
|lc| lc + a.variable - c.variable,
|
||||
);
|
||||
|
||||
let d = Self::alloc(cs.namespace(|| "conditional reversal result 2"), || {
|
||||
if *condition.get_value().get()? {
|
||||
Ok(*a.value.get()?)
|
||||
} else {
|
||||
Ok(*b.value.get()?)
|
||||
}
|
||||
})?;
|
||||
|
||||
cs.enforce(
|
||||
|| "second conditional reversal",
|
||||
|lc| lc + b.variable - a.variable,
|
||||
|_| condition.lc(CS::one(), Scalar::one()),
|
||||
|lc| lc + b.variable - d.variable,
|
||||
);
|
||||
|
||||
Ok((c, d))
|
||||
}
|
||||
|
||||
pub fn get_value(&self) -> Option<Scalar> {
|
||||
self.value
|
||||
}
|
||||
|
||||
pub fn get_variable(&self) -> Variable {
|
||||
self.variable
|
||||
}
|
||||
}
|
||||
|
||||
pub struct Num<Scalar: PrimeField> {
|
||||
value: Option<Scalar>,
|
||||
lc: LinearCombination<Scalar>,
|
||||
}
|
||||
|
||||
impl<Scalar: PrimeField> From<AllocatedNum<Scalar>> for Num<Scalar> {
|
||||
fn from(num: AllocatedNum<Scalar>) -> Num<Scalar> {
|
||||
Num {
|
||||
value: num.value,
|
||||
lc: LinearCombination::<Scalar>::zero() + num.variable,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<Scalar: PrimeField> Num<Scalar> {
|
||||
pub fn zero() -> Self {
|
||||
Num {
|
||||
value: Some(Scalar::zero()),
|
||||
lc: LinearCombination::zero(),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn get_value(&self) -> Option<Scalar> {
|
||||
self.value
|
||||
}
|
||||
|
||||
pub fn lc(&self, coeff: Scalar) -> LinearCombination<Scalar> {
|
||||
LinearCombination::zero() + (coeff, &self.lc)
|
||||
}
|
||||
|
||||
pub fn add_bool_with_coeff(self, one: Variable, bit: &Boolean, coeff: Scalar) -> Self {
|
||||
let newval = match (self.value, bit.get_value()) {
|
||||
(Some(mut curval), Some(bval)) => {
|
||||
if bval {
|
||||
curval.add_assign(&coeff);
|
||||
}
|
||||
|
||||
Some(curval)
|
||||
}
|
||||
_ => None,
|
||||
};
|
||||
|
||||
Num {
|
||||
value: newval,
|
||||
lc: self.lc + &bit.lc(one, coeff),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod test {
|
||||
use crate::ConstraintSystem;
|
||||
use bls12_381::Scalar;
|
||||
use ff::{BitIterator, Field, PrimeField};
|
||||
use rand_core::SeedableRng;
|
||||
use rand_xorshift::XorShiftRng;
|
||||
use std::ops::{Neg, SubAssign};
|
||||
|
||||
use super::{AllocatedNum, Boolean};
|
||||
use crate::gadgets::test::*;
|
||||
|
||||
#[test]
|
||||
fn test_allocated_num() {
|
||||
let mut cs = TestConstraintSystem::new();
|
||||
|
||||
AllocatedNum::alloc(&mut cs, || Ok(Scalar::one())).unwrap();
|
||||
|
||||
assert!(cs.get("num") == Scalar::one());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_num_squaring() {
|
||||
let mut cs = TestConstraintSystem::new();
|
||||
|
||||
let n = AllocatedNum::alloc(&mut cs, || Ok(Scalar::from_str("3").unwrap())).unwrap();
|
||||
let n2 = n.square(&mut cs).unwrap();
|
||||
|
||||
assert!(cs.is_satisfied());
|
||||
assert!(cs.get("squared num") == Scalar::from_str("9").unwrap());
|
||||
assert!(n2.value.unwrap() == Scalar::from_str("9").unwrap());
|
||||
cs.set("squared num", Scalar::from_str("10").unwrap());
|
||||
assert!(!cs.is_satisfied());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_num_multiplication() {
|
||||
let mut cs = TestConstraintSystem::new();
|
||||
|
||||
let n = AllocatedNum::alloc(cs.namespace(|| "a"), || Ok(Scalar::from_str("12").unwrap()))
|
||||
.unwrap();
|
||||
let n2 = AllocatedNum::alloc(cs.namespace(|| "b"), || Ok(Scalar::from_str("10").unwrap()))
|
||||
.unwrap();
|
||||
let n3 = n.mul(&mut cs, &n2).unwrap();
|
||||
|
||||
assert!(cs.is_satisfied());
|
||||
assert!(cs.get("product num") == Scalar::from_str("120").unwrap());
|
||||
assert!(n3.value.unwrap() == Scalar::from_str("120").unwrap());
|
||||
cs.set("product num", Scalar::from_str("121").unwrap());
|
||||
assert!(!cs.is_satisfied());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_num_conditional_reversal() {
|
||||
let mut rng = XorShiftRng::from_seed([
|
||||
0x59, 0x62, 0xbe, 0x3d, 0x76, 0x3d, 0x31, 0x8d, 0x17, 0xdb, 0x37, 0x32, 0x54, 0x06,
|
||||
0xbc, 0xe5,
|
||||
]);
|
||||
{
|
||||
let mut cs = TestConstraintSystem::new();
|
||||
|
||||
let a =
|
||||
AllocatedNum::alloc(cs.namespace(|| "a"), || Ok(Scalar::random(&mut rng))).unwrap();
|
||||
let b =
|
||||
AllocatedNum::alloc(cs.namespace(|| "b"), || Ok(Scalar::random(&mut rng))).unwrap();
|
||||
let condition = Boolean::constant(false);
|
||||
let (c, d) = AllocatedNum::conditionally_reverse(&mut cs, &a, &b, &condition).unwrap();
|
||||
|
||||
assert!(cs.is_satisfied());
|
||||
|
||||
assert_eq!(a.value.unwrap(), c.value.unwrap());
|
||||
assert_eq!(b.value.unwrap(), d.value.unwrap());
|
||||
}
|
||||
|
||||
{
|
||||
let mut cs = TestConstraintSystem::new();
|
||||
|
||||
let a =
|
||||
AllocatedNum::alloc(cs.namespace(|| "a"), || Ok(Scalar::random(&mut rng))).unwrap();
|
||||
let b =
|
||||
AllocatedNum::alloc(cs.namespace(|| "b"), || Ok(Scalar::random(&mut rng))).unwrap();
|
||||
let condition = Boolean::constant(true);
|
||||
let (c, d) = AllocatedNum::conditionally_reverse(&mut cs, &a, &b, &condition).unwrap();
|
||||
|
||||
assert!(cs.is_satisfied());
|
||||
|
||||
assert_eq!(a.value.unwrap(), d.value.unwrap());
|
||||
assert_eq!(b.value.unwrap(), c.value.unwrap());
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_num_nonzero() {
|
||||
{
|
||||
let mut cs = TestConstraintSystem::new();
|
||||
|
||||
let n = AllocatedNum::alloc(&mut cs, || Ok(Scalar::from_str("3").unwrap())).unwrap();
|
||||
n.assert_nonzero(&mut cs).unwrap();
|
||||
|
||||
assert!(cs.is_satisfied());
|
||||
cs.set("ephemeral inverse", Scalar::from_str("3").unwrap());
|
||||
assert!(cs.which_is_unsatisfied() == Some("nonzero assertion constraint"));
|
||||
}
|
||||
{
|
||||
let mut cs = TestConstraintSystem::new();
|
||||
|
||||
let n = AllocatedNum::alloc(&mut cs, || Ok(Scalar::zero())).unwrap();
|
||||
assert!(n.assert_nonzero(&mut cs).is_err());
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_into_bits_strict() {
|
||||
let negone = Scalar::one().neg();
|
||||
|
||||
let mut cs = TestConstraintSystem::new();
|
||||
|
||||
let n = AllocatedNum::alloc(&mut cs, || Ok(negone)).unwrap();
|
||||
n.to_bits_le_strict(&mut cs).unwrap();
|
||||
|
||||
assert!(cs.is_satisfied());
|
||||
|
||||
// make the bit representation the characteristic
|
||||
cs.set("bit 254/boolean", Scalar::one());
|
||||
|
||||
// this makes the conditional boolean constraint fail
|
||||
assert_eq!(
|
||||
cs.which_is_unsatisfied().unwrap(),
|
||||
"bit 254/boolean constraint"
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_into_bits() {
|
||||
let mut rng = XorShiftRng::from_seed([
|
||||
0x59, 0x62, 0xbe, 0x3d, 0x76, 0x3d, 0x31, 0x8d, 0x17, 0xdb, 0x37, 0x32, 0x54, 0x06,
|
||||
0xbc, 0xe5,
|
||||
]);
|
||||
|
||||
for i in 0..200 {
|
||||
let r = Scalar::random(&mut rng);
|
||||
let mut cs = TestConstraintSystem::new();
|
||||
|
||||
let n = AllocatedNum::alloc(&mut cs, || Ok(r)).unwrap();
|
||||
|
||||
let bits = if i % 2 == 0 {
|
||||
n.to_bits_le(&mut cs).unwrap()
|
||||
} else {
|
||||
n.to_bits_le_strict(&mut cs).unwrap()
|
||||
};
|
||||
|
||||
assert!(cs.is_satisfied());
|
||||
|
||||
for (b, a) in BitIterator::<u8, _>::new(r.to_repr())
|
||||
.skip(1)
|
||||
.zip(bits.iter().rev())
|
||||
{
|
||||
if let &Boolean::Is(ref a) = a {
|
||||
assert_eq!(b, a.get_value().unwrap());
|
||||
} else {
|
||||
unreachable!()
|
||||
}
|
||||
}
|
||||
|
||||
cs.set("num", Scalar::random(&mut rng));
|
||||
assert!(!cs.is_satisfied());
|
||||
cs.set("num", r);
|
||||
assert!(cs.is_satisfied());
|
||||
|
||||
for i in 0..Scalar::NUM_BITS {
|
||||
let name = format!("bit {}/boolean", i);
|
||||
let cur = cs.get(&name);
|
||||
let mut tmp = Scalar::one();
|
||||
tmp.sub_assign(&cur);
|
||||
cs.set(&name, tmp);
|
||||
assert!(!cs.is_satisfied());
|
||||
cs.set(&name, cur);
|
||||
assert!(cs.is_satisfied());
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1,388 +0,0 @@
|
|||
//! Circuits for the [SHA-256] hash function and its internal compression
|
||||
//! function.
|
||||
//!
|
||||
//! [SHA-256]: https://tools.ietf.org/html/rfc6234
|
||||
|
||||
use super::boolean::Boolean;
|
||||
use super::multieq::MultiEq;
|
||||
use super::uint32::UInt32;
|
||||
use crate::{ConstraintSystem, SynthesisError};
|
||||
use ff::PrimeField;
|
||||
|
||||
#[allow(clippy::unreadable_literal)]
|
||||
const ROUND_CONSTANTS: [u32; 64] = [
|
||||
0x428a2f98, 0x71374491, 0xb5c0fbcf, 0xe9b5dba5, 0x3956c25b, 0x59f111f1, 0x923f82a4, 0xab1c5ed5,
|
||||
0xd807aa98, 0x12835b01, 0x243185be, 0x550c7dc3, 0x72be5d74, 0x80deb1fe, 0x9bdc06a7, 0xc19bf174,
|
||||
0xe49b69c1, 0xefbe4786, 0x0fc19dc6, 0x240ca1cc, 0x2de92c6f, 0x4a7484aa, 0x5cb0a9dc, 0x76f988da,
|
||||
0x983e5152, 0xa831c66d, 0xb00327c8, 0xbf597fc7, 0xc6e00bf3, 0xd5a79147, 0x06ca6351, 0x14292967,
|
||||
0x27b70a85, 0x2e1b2138, 0x4d2c6dfc, 0x53380d13, 0x650a7354, 0x766a0abb, 0x81c2c92e, 0x92722c85,
|
||||
0xa2bfe8a1, 0xa81a664b, 0xc24b8b70, 0xc76c51a3, 0xd192e819, 0xd6990624, 0xf40e3585, 0x106aa070,
|
||||
0x19a4c116, 0x1e376c08, 0x2748774c, 0x34b0bcb5, 0x391c0cb3, 0x4ed8aa4a, 0x5b9cca4f, 0x682e6ff3,
|
||||
0x748f82ee, 0x78a5636f, 0x84c87814, 0x8cc70208, 0x90befffa, 0xa4506ceb, 0xbef9a3f7, 0xc67178f2,
|
||||
];
|
||||
|
||||
#[allow(clippy::unreadable_literal)]
|
||||
const IV: [u32; 8] = [
|
||||
0x6a09e667, 0xbb67ae85, 0x3c6ef372, 0xa54ff53a, 0x510e527f, 0x9b05688c, 0x1f83d9ab, 0x5be0cd19,
|
||||
];
|
||||
|
||||
pub fn sha256_block_no_padding<Scalar, CS>(
|
||||
mut cs: CS,
|
||||
input: &[Boolean],
|
||||
) -> Result<Vec<Boolean>, SynthesisError>
|
||||
where
|
||||
Scalar: PrimeField,
|
||||
CS: ConstraintSystem<Scalar>,
|
||||
{
|
||||
assert_eq!(input.len(), 512);
|
||||
|
||||
Ok(
|
||||
sha256_compression_function(&mut cs, &input, &get_sha256_iv())?
|
||||
.into_iter()
|
||||
.flat_map(|e| e.into_bits_be())
|
||||
.collect(),
|
||||
)
|
||||
}
|
||||
|
||||
pub fn sha256<Scalar, CS>(mut cs: CS, input: &[Boolean]) -> Result<Vec<Boolean>, SynthesisError>
|
||||
where
|
||||
Scalar: PrimeField,
|
||||
CS: ConstraintSystem<Scalar>,
|
||||
{
|
||||
assert!(input.len() % 8 == 0);
|
||||
|
||||
let mut padded = input.to_vec();
|
||||
let plen = padded.len() as u64;
|
||||
// append a single '1' bit
|
||||
padded.push(Boolean::constant(true));
|
||||
// append K '0' bits, where K is the minimum number >= 0 such that L + 1 + K + 64 is a multiple of 512
|
||||
while (padded.len() + 64) % 512 != 0 {
|
||||
padded.push(Boolean::constant(false));
|
||||
}
|
||||
// append L as a 64-bit big-endian integer, making the total post-processed length a multiple of 512 bits
|
||||
for b in (0..64).rev().map(|i| (plen >> i) & 1 == 1) {
|
||||
padded.push(Boolean::constant(b));
|
||||
}
|
||||
assert!(padded.len() % 512 == 0);
|
||||
|
||||
let mut cur = get_sha256_iv();
|
||||
for (i, block) in padded.chunks(512).enumerate() {
|
||||
cur = sha256_compression_function(cs.namespace(|| format!("block {}", i)), block, &cur)?;
|
||||
}
|
||||
|
||||
Ok(cur.into_iter().flat_map(|e| e.into_bits_be()).collect())
|
||||
}
|
||||
|
||||
fn get_sha256_iv() -> Vec<UInt32> {
|
||||
IV.iter().map(|&v| UInt32::constant(v)).collect()
|
||||
}
|
||||
|
||||
fn sha256_compression_function<Scalar, CS>(
|
||||
cs: CS,
|
||||
input: &[Boolean],
|
||||
current_hash_value: &[UInt32],
|
||||
) -> Result<Vec<UInt32>, SynthesisError>
|
||||
where
|
||||
Scalar: PrimeField,
|
||||
CS: ConstraintSystem<Scalar>,
|
||||
{
|
||||
assert_eq!(input.len(), 512);
|
||||
assert_eq!(current_hash_value.len(), 8);
|
||||
|
||||
let mut w = input
|
||||
.chunks(32)
|
||||
.map(|e| UInt32::from_bits_be(e))
|
||||
.collect::<Vec<_>>();
|
||||
|
||||
// We can save some constraints by combining some of
|
||||
// the constraints in different u32 additions
|
||||
let mut cs = MultiEq::new(cs);
|
||||
|
||||
for i in 16..64 {
|
||||
let cs = &mut cs.namespace(|| format!("w extension {}", i));
|
||||
|
||||
// s0 := (w[i-15] rightrotate 7) xor (w[i-15] rightrotate 18) xor (w[i-15] rightshift 3)
|
||||
let mut s0 = w[i - 15].rotr(7);
|
||||
s0 = s0.xor(cs.namespace(|| "first xor for s0"), &w[i - 15].rotr(18))?;
|
||||
s0 = s0.xor(cs.namespace(|| "second xor for s0"), &w[i - 15].shr(3))?;
|
||||
|
||||
// s1 := (w[i-2] rightrotate 17) xor (w[i-2] rightrotate 19) xor (w[i-2] rightshift 10)
|
||||
let mut s1 = w[i - 2].rotr(17);
|
||||
s1 = s1.xor(cs.namespace(|| "first xor for s1"), &w[i - 2].rotr(19))?;
|
||||
s1 = s1.xor(cs.namespace(|| "second xor for s1"), &w[i - 2].shr(10))?;
|
||||
|
||||
let tmp = UInt32::addmany(
|
||||
cs.namespace(|| "computation of w[i]"),
|
||||
&[w[i - 16].clone(), s0, w[i - 7].clone(), s1],
|
||||
)?;
|
||||
|
||||
// w[i] := w[i-16] + s0 + w[i-7] + s1
|
||||
w.push(tmp);
|
||||
}
|
||||
|
||||
assert_eq!(w.len(), 64);
|
||||
|
||||
enum Maybe {
|
||||
Deferred(Vec<UInt32>),
|
||||
Concrete(UInt32),
|
||||
}
|
||||
|
||||
impl Maybe {
|
||||
fn compute<Scalar, CS, M>(self, cs: M, others: &[UInt32]) -> Result<UInt32, SynthesisError>
|
||||
where
|
||||
Scalar: PrimeField,
|
||||
CS: ConstraintSystem<Scalar>,
|
||||
M: ConstraintSystem<Scalar, Root = MultiEq<Scalar, CS>>,
|
||||
{
|
||||
Ok(match self {
|
||||
Maybe::Concrete(ref v) => return Ok(v.clone()),
|
||||
Maybe::Deferred(mut v) => {
|
||||
v.extend(others.iter().cloned());
|
||||
UInt32::addmany(cs, &v)?
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
let mut a = Maybe::Concrete(current_hash_value[0].clone());
|
||||
let mut b = current_hash_value[1].clone();
|
||||
let mut c = current_hash_value[2].clone();
|
||||
let mut d = current_hash_value[3].clone();
|
||||
let mut e = Maybe::Concrete(current_hash_value[4].clone());
|
||||
let mut f = current_hash_value[5].clone();
|
||||
let mut g = current_hash_value[6].clone();
|
||||
let mut h = current_hash_value[7].clone();
|
||||
|
||||
for i in 0..64 {
|
||||
let cs = &mut cs.namespace(|| format!("compression round {}", i));
|
||||
|
||||
// S1 := (e rightrotate 6) xor (e rightrotate 11) xor (e rightrotate 25)
|
||||
let new_e = e.compute(cs.namespace(|| "deferred e computation"), &[])?;
|
||||
let mut s1 = new_e.rotr(6);
|
||||
s1 = s1.xor(cs.namespace(|| "first xor for s1"), &new_e.rotr(11))?;
|
||||
s1 = s1.xor(cs.namespace(|| "second xor for s1"), &new_e.rotr(25))?;
|
||||
|
||||
// ch := (e and f) xor ((not e) and g)
|
||||
let ch = UInt32::sha256_ch(cs.namespace(|| "ch"), &new_e, &f, &g)?;
|
||||
|
||||
// temp1 := h + S1 + ch + k[i] + w[i]
|
||||
let temp1 = vec![
|
||||
h.clone(),
|
||||
s1,
|
||||
ch,
|
||||
UInt32::constant(ROUND_CONSTANTS[i]),
|
||||
w[i].clone(),
|
||||
];
|
||||
|
||||
// S0 := (a rightrotate 2) xor (a rightrotate 13) xor (a rightrotate 22)
|
||||
let new_a = a.compute(cs.namespace(|| "deferred a computation"), &[])?;
|
||||
let mut s0 = new_a.rotr(2);
|
||||
s0 = s0.xor(cs.namespace(|| "first xor for s0"), &new_a.rotr(13))?;
|
||||
s0 = s0.xor(cs.namespace(|| "second xor for s0"), &new_a.rotr(22))?;
|
||||
|
||||
// maj := (a and b) xor (a and c) xor (b and c)
|
||||
let maj = UInt32::sha256_maj(cs.namespace(|| "maj"), &new_a, &b, &c)?;
|
||||
|
||||
// temp2 := S0 + maj
|
||||
let temp2 = vec![s0, maj];
|
||||
|
||||
/*
|
||||
h := g
|
||||
g := f
|
||||
f := e
|
||||
e := d + temp1
|
||||
d := c
|
||||
c := b
|
||||
b := a
|
||||
a := temp1 + temp2
|
||||
*/
|
||||
|
||||
h = g;
|
||||
g = f;
|
||||
f = new_e;
|
||||
e = Maybe::Deferred(temp1.iter().cloned().chain(Some(d)).collect::<Vec<_>>());
|
||||
d = c;
|
||||
c = b;
|
||||
b = new_a;
|
||||
a = Maybe::Deferred(
|
||||
temp1
|
||||
.iter()
|
||||
.cloned()
|
||||
.chain(temp2.iter().cloned())
|
||||
.collect::<Vec<_>>(),
|
||||
);
|
||||
}
|
||||
|
||||
/*
|
||||
Add the compressed chunk to the current hash value:
|
||||
h0 := h0 + a
|
||||
h1 := h1 + b
|
||||
h2 := h2 + c
|
||||
h3 := h3 + d
|
||||
h4 := h4 + e
|
||||
h5 := h5 + f
|
||||
h6 := h6 + g
|
||||
h7 := h7 + h
|
||||
*/
|
||||
|
||||
let h0 = a.compute(
|
||||
cs.namespace(|| "deferred h0 computation"),
|
||||
&[current_hash_value[0].clone()],
|
||||
)?;
|
||||
|
||||
let h1 = UInt32::addmany(
|
||||
cs.namespace(|| "new h1"),
|
||||
&[current_hash_value[1].clone(), b],
|
||||
)?;
|
||||
|
||||
let h2 = UInt32::addmany(
|
||||
cs.namespace(|| "new h2"),
|
||||
&[current_hash_value[2].clone(), c],
|
||||
)?;
|
||||
|
||||
let h3 = UInt32::addmany(
|
||||
cs.namespace(|| "new h3"),
|
||||
&[current_hash_value[3].clone(), d],
|
||||
)?;
|
||||
|
||||
let h4 = e.compute(
|
||||
cs.namespace(|| "deferred h4 computation"),
|
||||
&[current_hash_value[4].clone()],
|
||||
)?;
|
||||
|
||||
let h5 = UInt32::addmany(
|
||||
cs.namespace(|| "new h5"),
|
||||
&[current_hash_value[5].clone(), f],
|
||||
)?;
|
||||
|
||||
let h6 = UInt32::addmany(
|
||||
cs.namespace(|| "new h6"),
|
||||
&[current_hash_value[6].clone(), g],
|
||||
)?;
|
||||
|
||||
let h7 = UInt32::addmany(
|
||||
cs.namespace(|| "new h7"),
|
||||
&[current_hash_value[7].clone(), h],
|
||||
)?;
|
||||
|
||||
Ok(vec![h0, h1, h2, h3, h4, h5, h6, h7])
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod test {
|
||||
use super::*;
|
||||
use crate::gadgets::boolean::AllocatedBit;
|
||||
use crate::gadgets::test::TestConstraintSystem;
|
||||
use bls12_381::Scalar;
|
||||
use hex_literal::hex;
|
||||
use rand_core::{RngCore, SeedableRng};
|
||||
use rand_xorshift::XorShiftRng;
|
||||
|
||||
#[test]
|
||||
fn test_blank_hash() {
|
||||
let iv = get_sha256_iv();
|
||||
|
||||
let mut cs = TestConstraintSystem::<Scalar>::new();
|
||||
let mut input_bits: Vec<_> = (0..512).map(|_| Boolean::Constant(false)).collect();
|
||||
input_bits[0] = Boolean::Constant(true);
|
||||
let out = sha256_compression_function(&mut cs, &input_bits, &iv).unwrap();
|
||||
let out_bits: Vec<_> = out.into_iter().flat_map(|e| e.into_bits_be()).collect();
|
||||
|
||||
assert!(cs.is_satisfied());
|
||||
assert_eq!(cs.num_constraints(), 0);
|
||||
|
||||
let expected = hex!("e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855");
|
||||
|
||||
let mut out = out_bits.into_iter();
|
||||
for b in expected.iter() {
|
||||
for i in (0..8).rev() {
|
||||
let c = out.next().unwrap().get_value().unwrap();
|
||||
|
||||
assert_eq!(c, (b >> i) & 1u8 == 1u8);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_full_block() {
|
||||
let mut rng = XorShiftRng::from_seed([
|
||||
0x59, 0x62, 0xbe, 0x3d, 0x76, 0x3d, 0x31, 0x8d, 0x17, 0xdb, 0x37, 0x32, 0x54, 0x06,
|
||||
0xbc, 0xe5,
|
||||
]);
|
||||
|
||||
let iv = get_sha256_iv();
|
||||
|
||||
let mut cs = TestConstraintSystem::<Scalar>::new();
|
||||
let input_bits: Vec<_> = (0..512)
|
||||
.map(|i| {
|
||||
Boolean::from(
|
||||
AllocatedBit::alloc(
|
||||
cs.namespace(|| format!("input bit {}", i)),
|
||||
Some(rng.next_u32() % 2 != 0),
|
||||
)
|
||||
.unwrap(),
|
||||
)
|
||||
})
|
||||
.collect();
|
||||
|
||||
sha256_compression_function(cs.namespace(|| "sha256"), &input_bits, &iv).unwrap();
|
||||
|
||||
assert!(cs.is_satisfied());
|
||||
assert_eq!(cs.num_constraints() - 512, 25840);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_against_vectors() {
|
||||
use sha2::{Digest, Sha256};
|
||||
|
||||
let mut rng = XorShiftRng::from_seed([
|
||||
0x59, 0x62, 0xbe, 0x3d, 0x76, 0x3d, 0x31, 0x8d, 0x17, 0xdb, 0x37, 0x32, 0x54, 0x06,
|
||||
0xbc, 0xe5,
|
||||
]);
|
||||
|
||||
for input_len in (0..32).chain((32..256).filter(|a| a % 8 == 0)) {
|
||||
let mut h = Sha256::new();
|
||||
let data: Vec<u8> = (0..input_len).map(|_| rng.next_u32() as u8).collect();
|
||||
h.update(&data);
|
||||
let hash_result = h.finalize();
|
||||
|
||||
let mut cs = TestConstraintSystem::<Scalar>::new();
|
||||
let mut input_bits = vec![];
|
||||
|
||||
for (byte_i, input_byte) in data.into_iter().enumerate() {
|
||||
for bit_i in (0..8).rev() {
|
||||
let cs = cs.namespace(|| format!("input bit {} {}", byte_i, bit_i));
|
||||
|
||||
input_bits.push(
|
||||
AllocatedBit::alloc(cs, Some((input_byte >> bit_i) & 1u8 == 1u8))
|
||||
.unwrap()
|
||||
.into(),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
let r = sha256(&mut cs, &input_bits).unwrap();
|
||||
|
||||
assert!(cs.is_satisfied());
|
||||
|
||||
let mut s = hash_result
|
||||
.iter()
|
||||
.flat_map(|&byte| (0..8).rev().map(move |i| (byte >> i) & 1u8 == 1u8));
|
||||
|
||||
for b in r {
|
||||
match b {
|
||||
Boolean::Is(b) => {
|
||||
assert!(s.next().unwrap() == b.get_value().unwrap());
|
||||
}
|
||||
Boolean::Not(b) => {
|
||||
assert!(s.next().unwrap() != b.get_value().unwrap());
|
||||
}
|
||||
Boolean::Constant(b) => {
|
||||
assert!(input_len == 0);
|
||||
assert!(s.next().unwrap() == b);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1,463 +0,0 @@
|
|||
//! Helpers for testing circuit implementations.
|
||||
|
||||
use ff::{Endianness, PrimeField};
|
||||
|
||||
use crate::{ConstraintSystem, Index, LinearCombination, SynthesisError, Variable};
|
||||
|
||||
use std::collections::HashMap;
|
||||
use std::fmt::Write;
|
||||
|
||||
use byteorder::{BigEndian, ByteOrder};
|
||||
use std::cmp::Ordering;
|
||||
use std::collections::BTreeMap;
|
||||
|
||||
use blake2s_simd::{Params as Blake2sParams, State as Blake2sState};
|
||||
|
||||
#[derive(Debug)]
|
||||
enum NamedObject {
|
||||
Constraint(usize),
|
||||
Var(Variable),
|
||||
Namespace,
|
||||
}
|
||||
|
||||
/// Constraint system for testing purposes.
|
||||
pub struct TestConstraintSystem<Scalar: PrimeField> {
|
||||
named_objects: HashMap<String, NamedObject>,
|
||||
current_namespace: Vec<String>,
|
||||
constraints: Vec<(
|
||||
LinearCombination<Scalar>,
|
||||
LinearCombination<Scalar>,
|
||||
LinearCombination<Scalar>,
|
||||
String,
|
||||
)>,
|
||||
inputs: Vec<(Scalar, String)>,
|
||||
aux: Vec<(Scalar, String)>,
|
||||
}
|
||||
|
||||
#[derive(Clone, Copy)]
|
||||
struct OrderedVariable(Variable);
|
||||
|
||||
impl Eq for OrderedVariable {}
|
||||
impl PartialEq for OrderedVariable {
|
||||
fn eq(&self, other: &OrderedVariable) -> bool {
|
||||
match (self.0.get_unchecked(), other.0.get_unchecked()) {
|
||||
(Index::Input(ref a), Index::Input(ref b)) => a == b,
|
||||
(Index::Aux(ref a), Index::Aux(ref b)) => a == b,
|
||||
_ => false,
|
||||
}
|
||||
}
|
||||
}
|
||||
impl PartialOrd for OrderedVariable {
|
||||
fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
|
||||
Some(self.cmp(other))
|
||||
}
|
||||
}
|
||||
impl Ord for OrderedVariable {
|
||||
fn cmp(&self, other: &Self) -> Ordering {
|
||||
match (self.0.get_unchecked(), other.0.get_unchecked()) {
|
||||
(Index::Input(ref a), Index::Input(ref b)) => a.cmp(b),
|
||||
(Index::Aux(ref a), Index::Aux(ref b)) => a.cmp(b),
|
||||
(Index::Input(_), Index::Aux(_)) => Ordering::Less,
|
||||
(Index::Aux(_), Index::Input(_)) => Ordering::Greater,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn proc_lc<Scalar: PrimeField>(terms: &[(Variable, Scalar)]) -> BTreeMap<OrderedVariable, Scalar> {
|
||||
let mut map = BTreeMap::new();
|
||||
for &(var, coeff) in terms {
|
||||
map.entry(OrderedVariable(var))
|
||||
.or_insert_with(Scalar::zero)
|
||||
.add_assign(&coeff);
|
||||
}
|
||||
|
||||
// Remove terms that have a zero coefficient to normalize
|
||||
let mut to_remove = vec![];
|
||||
for (var, coeff) in map.iter() {
|
||||
if coeff.is_zero() {
|
||||
to_remove.push(var.clone())
|
||||
}
|
||||
}
|
||||
|
||||
for var in to_remove {
|
||||
map.remove(&var);
|
||||
}
|
||||
|
||||
map
|
||||
}
|
||||
|
||||
fn hash_lc<Scalar: PrimeField>(terms: &[(Variable, Scalar)], h: &mut Blake2sState) {
|
||||
let map = proc_lc::<Scalar>(terms);
|
||||
|
||||
let mut buf = [0u8; 9 + 32];
|
||||
BigEndian::write_u64(&mut buf[0..8], map.len() as u64);
|
||||
h.update(&buf[0..8]);
|
||||
|
||||
for (var, coeff) in map {
|
||||
match var.0.get_unchecked() {
|
||||
Index::Input(i) => {
|
||||
buf[0] = b'I';
|
||||
BigEndian::write_u64(&mut buf[1..9], i as u64);
|
||||
}
|
||||
Index::Aux(i) => {
|
||||
buf[0] = b'A';
|
||||
BigEndian::write_u64(&mut buf[1..9], i as u64);
|
||||
}
|
||||
}
|
||||
|
||||
let mut coeff_repr = coeff.to_repr();
|
||||
<Scalar as PrimeField>::ReprEndianness::toggle_little_endian(&mut coeff_repr);
|
||||
let coeff_be: Vec<_> = coeff_repr.as_ref().iter().cloned().rev().collect();
|
||||
buf[9..].copy_from_slice(&coeff_be[..]);
|
||||
|
||||
h.update(&buf);
|
||||
}
|
||||
}
|
||||
|
||||
fn eval_lc<Scalar: PrimeField>(
|
||||
terms: &[(Variable, Scalar)],
|
||||
inputs: &[(Scalar, String)],
|
||||
aux: &[(Scalar, String)],
|
||||
) -> Scalar {
|
||||
let mut acc = Scalar::zero();
|
||||
|
||||
for &(var, ref coeff) in terms {
|
||||
let mut tmp = match var.get_unchecked() {
|
||||
Index::Input(index) => inputs[index].0,
|
||||
Index::Aux(index) => aux[index].0,
|
||||
};
|
||||
|
||||
tmp.mul_assign(coeff);
|
||||
acc.add_assign(&tmp);
|
||||
}
|
||||
|
||||
acc
|
||||
}
|
||||
|
||||
impl<Scalar: PrimeField> TestConstraintSystem<Scalar> {
|
||||
pub fn new() -> TestConstraintSystem<Scalar> {
|
||||
let mut map = HashMap::new();
|
||||
map.insert(
|
||||
"ONE".into(),
|
||||
NamedObject::Var(TestConstraintSystem::<Scalar>::one()),
|
||||
);
|
||||
|
||||
TestConstraintSystem {
|
||||
named_objects: map,
|
||||
current_namespace: vec![],
|
||||
constraints: vec![],
|
||||
inputs: vec![(Scalar::one(), "ONE".into())],
|
||||
aux: vec![],
|
||||
}
|
||||
}
|
||||
|
||||
pub fn pretty_print(&self) -> String {
|
||||
let mut s = String::new();
|
||||
|
||||
let negone = Scalar::one().neg();
|
||||
|
||||
let powers_of_two = (0..Scalar::NUM_BITS)
|
||||
.map(|i| Scalar::from_str("2").unwrap().pow_vartime(&[u64::from(i)]))
|
||||
.collect::<Vec<_>>();
|
||||
|
||||
let pp = |s: &mut String, lc: &LinearCombination<Scalar>| {
|
||||
write!(s, "(").unwrap();
|
||||
let mut is_first = true;
|
||||
for (var, coeff) in proc_lc::<Scalar>(lc.as_ref()) {
|
||||
if coeff == negone {
|
||||
write!(s, " - ").unwrap();
|
||||
} else if !is_first {
|
||||
write!(s, " + ").unwrap();
|
||||
}
|
||||
is_first = false;
|
||||
|
||||
if coeff != Scalar::one() && coeff != negone {
|
||||
for (i, x) in powers_of_two.iter().enumerate() {
|
||||
if x == &coeff {
|
||||
write!(s, "2^{} . ", i).unwrap();
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
write!(s, "{} . ", coeff).unwrap();
|
||||
}
|
||||
|
||||
match var.0.get_unchecked() {
|
||||
Index::Input(i) => {
|
||||
write!(s, "`{}`", &self.inputs[i].1).unwrap();
|
||||
}
|
||||
Index::Aux(i) => {
|
||||
write!(s, "`{}`", &self.aux[i].1).unwrap();
|
||||
}
|
||||
}
|
||||
}
|
||||
if is_first {
|
||||
// Nothing was visited, print 0.
|
||||
write!(s, "0").unwrap();
|
||||
}
|
||||
write!(s, ")").unwrap();
|
||||
};
|
||||
|
||||
for &(ref a, ref b, ref c, ref name) in &self.constraints {
|
||||
write!(&mut s, "\n").unwrap();
|
||||
|
||||
write!(&mut s, "{}: ", name).unwrap();
|
||||
pp(&mut s, a);
|
||||
write!(&mut s, " * ").unwrap();
|
||||
pp(&mut s, b);
|
||||
write!(&mut s, " = ").unwrap();
|
||||
pp(&mut s, c);
|
||||
}
|
||||
|
||||
write!(&mut s, "\n").unwrap();
|
||||
|
||||
s
|
||||
}
|
||||
|
||||
pub fn hash(&self) -> String {
|
||||
let mut h = Blake2sParams::new().hash_length(32).to_state();
|
||||
{
|
||||
let mut buf = [0u8; 24];
|
||||
|
||||
BigEndian::write_u64(&mut buf[0..8], self.inputs.len() as u64);
|
||||
BigEndian::write_u64(&mut buf[8..16], self.aux.len() as u64);
|
||||
BigEndian::write_u64(&mut buf[16..24], self.constraints.len() as u64);
|
||||
h.update(&buf);
|
||||
}
|
||||
|
||||
for constraint in &self.constraints {
|
||||
hash_lc::<Scalar>(constraint.0.as_ref(), &mut h);
|
||||
hash_lc::<Scalar>(constraint.1.as_ref(), &mut h);
|
||||
hash_lc::<Scalar>(constraint.2.as_ref(), &mut h);
|
||||
}
|
||||
|
||||
let mut s = String::new();
|
||||
for b in h.finalize().as_ref() {
|
||||
s += &format!("{:02x}", b);
|
||||
}
|
||||
|
||||
s
|
||||
}
|
||||
|
||||
pub fn which_is_unsatisfied(&self) -> Option<&str> {
|
||||
for &(ref a, ref b, ref c, ref path) in &self.constraints {
|
||||
let mut a = eval_lc::<Scalar>(a.as_ref(), &self.inputs, &self.aux);
|
||||
let b = eval_lc::<Scalar>(b.as_ref(), &self.inputs, &self.aux);
|
||||
let c = eval_lc::<Scalar>(c.as_ref(), &self.inputs, &self.aux);
|
||||
|
||||
a.mul_assign(&b);
|
||||
|
||||
if a != c {
|
||||
return Some(&*path);
|
||||
}
|
||||
}
|
||||
|
||||
None
|
||||
}
|
||||
|
||||
pub fn is_satisfied(&self) -> bool {
|
||||
self.which_is_unsatisfied().is_none()
|
||||
}
|
||||
|
||||
pub fn num_constraints(&self) -> usize {
|
||||
self.constraints.len()
|
||||
}
|
||||
|
||||
pub fn set(&mut self, path: &str, to: Scalar) {
|
||||
match self.named_objects.get(path) {
|
||||
Some(&NamedObject::Var(ref v)) => match v.get_unchecked() {
|
||||
Index::Input(index) => self.inputs[index].0 = to,
|
||||
Index::Aux(index) => self.aux[index].0 = to,
|
||||
},
|
||||
Some(e) => panic!(
|
||||
"tried to set path `{}` to value, but `{:?}` already exists there.",
|
||||
path, e
|
||||
),
|
||||
_ => panic!("no variable exists at path: {}", path),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn verify(&self, expected: &[Scalar]) -> bool {
|
||||
assert_eq!(expected.len() + 1, self.inputs.len());
|
||||
|
||||
for (a, b) in self.inputs.iter().skip(1).zip(expected.iter()) {
|
||||
if &a.0 != b {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
true
|
||||
}
|
||||
|
||||
pub fn num_inputs(&self) -> usize {
|
||||
self.inputs.len()
|
||||
}
|
||||
|
||||
pub fn get_input(&mut self, index: usize, path: &str) -> Scalar {
|
||||
let (assignment, name) = self.inputs[index].clone();
|
||||
|
||||
assert_eq!(path, name);
|
||||
|
||||
assignment
|
||||
}
|
||||
|
||||
pub fn get(&mut self, path: &str) -> Scalar {
|
||||
match self.named_objects.get(path) {
|
||||
Some(&NamedObject::Var(ref v)) => match v.get_unchecked() {
|
||||
Index::Input(index) => self.inputs[index].0,
|
||||
Index::Aux(index) => self.aux[index].0,
|
||||
},
|
||||
Some(e) => panic!(
|
||||
"tried to get value of path `{}`, but `{:?}` exists there (not a variable)",
|
||||
path, e
|
||||
),
|
||||
_ => panic!("no variable exists at path: {}", path),
|
||||
}
|
||||
}
|
||||
|
||||
fn set_named_obj(&mut self, path: String, to: NamedObject) {
|
||||
if self.named_objects.contains_key(&path) {
|
||||
panic!("tried to create object at existing path: {}", path);
|
||||
}
|
||||
|
||||
self.named_objects.insert(path, to);
|
||||
}
|
||||
}
|
||||
|
||||
fn compute_path(ns: &[String], this: String) -> String {
|
||||
if this.chars().any(|a| a == '/') {
|
||||
panic!("'/' is not allowed in names");
|
||||
}
|
||||
|
||||
let mut name = String::new();
|
||||
|
||||
let mut needs_separation = false;
|
||||
for ns in ns.iter().chain(Some(&this).into_iter()) {
|
||||
if needs_separation {
|
||||
name += "/";
|
||||
}
|
||||
|
||||
name += ns;
|
||||
needs_separation = true;
|
||||
}
|
||||
|
||||
name
|
||||
}
|
||||
|
||||
impl<Scalar: PrimeField> ConstraintSystem<Scalar> for TestConstraintSystem<Scalar> {
|
||||
type Root = Self;
|
||||
|
||||
fn alloc<F, A, AR>(&mut self, annotation: A, f: F) -> Result<Variable, SynthesisError>
|
||||
where
|
||||
F: FnOnce() -> Result<Scalar, SynthesisError>,
|
||||
A: FnOnce() -> AR,
|
||||
AR: Into<String>,
|
||||
{
|
||||
let index = self.aux.len();
|
||||
let path = compute_path(&self.current_namespace, annotation().into());
|
||||
self.aux.push((f()?, path.clone()));
|
||||
let var = Variable::new_unchecked(Index::Aux(index));
|
||||
self.set_named_obj(path, NamedObject::Var(var));
|
||||
|
||||
Ok(var)
|
||||
}
|
||||
|
||||
fn alloc_input<F, A, AR>(&mut self, annotation: A, f: F) -> Result<Variable, SynthesisError>
|
||||
where
|
||||
F: FnOnce() -> Result<Scalar, SynthesisError>,
|
||||
A: FnOnce() -> AR,
|
||||
AR: Into<String>,
|
||||
{
|
||||
let index = self.inputs.len();
|
||||
let path = compute_path(&self.current_namespace, annotation().into());
|
||||
self.inputs.push((f()?, path.clone()));
|
||||
let var = Variable::new_unchecked(Index::Input(index));
|
||||
self.set_named_obj(path, NamedObject::Var(var));
|
||||
|
||||
Ok(var)
|
||||
}
|
||||
|
||||
fn enforce<A, AR, LA, LB, LC>(&mut self, annotation: A, a: LA, b: LB, c: LC)
|
||||
where
|
||||
A: FnOnce() -> AR,
|
||||
AR: Into<String>,
|
||||
LA: FnOnce(LinearCombination<Scalar>) -> LinearCombination<Scalar>,
|
||||
LB: FnOnce(LinearCombination<Scalar>) -> LinearCombination<Scalar>,
|
||||
LC: FnOnce(LinearCombination<Scalar>) -> LinearCombination<Scalar>,
|
||||
{
|
||||
let path = compute_path(&self.current_namespace, annotation().into());
|
||||
let index = self.constraints.len();
|
||||
self.set_named_obj(path.clone(), NamedObject::Constraint(index));
|
||||
|
||||
let a = a(LinearCombination::zero());
|
||||
let b = b(LinearCombination::zero());
|
||||
let c = c(LinearCombination::zero());
|
||||
|
||||
self.constraints.push((a, b, c, path));
|
||||
}
|
||||
|
||||
fn push_namespace<NR, N>(&mut self, name_fn: N)
|
||||
where
|
||||
NR: Into<String>,
|
||||
N: FnOnce() -> NR,
|
||||
{
|
||||
let name = name_fn().into();
|
||||
let path = compute_path(&self.current_namespace, name.clone());
|
||||
self.set_named_obj(path.clone(), NamedObject::Namespace);
|
||||
self.current_namespace.push(name);
|
||||
}
|
||||
|
||||
fn pop_namespace(&mut self) {
|
||||
assert!(self.current_namespace.pop().is_some());
|
||||
}
|
||||
|
||||
fn get_root(&mut self) -> &mut Self::Root {
|
||||
self
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_cs() {
|
||||
use bls12_381::Scalar;
|
||||
use ff::PrimeField;
|
||||
|
||||
let mut cs = TestConstraintSystem::new();
|
||||
assert!(cs.is_satisfied());
|
||||
assert_eq!(cs.num_constraints(), 0);
|
||||
let a = cs
|
||||
.namespace(|| "a")
|
||||
.alloc(|| "var", || Ok(Scalar::from_str("10").unwrap()))
|
||||
.unwrap();
|
||||
let b = cs
|
||||
.namespace(|| "b")
|
||||
.alloc(|| "var", || Ok(Scalar::from_str("4").unwrap()))
|
||||
.unwrap();
|
||||
let c = cs
|
||||
.alloc(|| "product", || Ok(Scalar::from_str("40").unwrap()))
|
||||
.unwrap();
|
||||
|
||||
cs.enforce(|| "mult", |lc| lc + a, |lc| lc + b, |lc| lc + c);
|
||||
assert!(cs.is_satisfied());
|
||||
assert_eq!(cs.num_constraints(), 1);
|
||||
|
||||
cs.set("a/var", Scalar::from_str("4").unwrap());
|
||||
|
||||
let one = TestConstraintSystem::<Scalar>::one();
|
||||
cs.enforce(|| "eq", |lc| lc + a, |lc| lc + one, |lc| lc + b);
|
||||
|
||||
assert!(!cs.is_satisfied());
|
||||
assert!(cs.which_is_unsatisfied() == Some("mult"));
|
||||
|
||||
assert!(cs.get("product") == Scalar::from_str("40").unwrap());
|
||||
|
||||
cs.set("product", Scalar::from_str("16").unwrap());
|
||||
assert!(cs.is_satisfied());
|
||||
|
||||
{
|
||||
let mut cs = cs.namespace(|| "test1");
|
||||
let mut cs = cs.namespace(|| "test2");
|
||||
cs.alloc(|| "hehe", || Ok(Scalar::one())).unwrap();
|
||||
}
|
||||
|
||||
assert!(cs.get("test1/test2/hehe") == Scalar::one());
|
||||
}
|
|
@ -1,767 +0,0 @@
|
|||
//! Circuit representation of a [`u32`], with helpers for the [`sha256`]
|
||||
//! gadgets.
|
||||
//!
|
||||
//! [`sha256`]: crate::gadgets::sha256
|
||||
|
||||
use ff::PrimeField;
|
||||
|
||||
use crate::{ConstraintSystem, LinearCombination, SynthesisError};
|
||||
|
||||
use super::boolean::{AllocatedBit, Boolean};
|
||||
|
||||
use super::multieq::MultiEq;
|
||||
|
||||
/// Represents an interpretation of 32 `Boolean` objects as an
|
||||
/// unsigned integer.
|
||||
#[derive(Clone)]
|
||||
pub struct UInt32 {
|
||||
// Least significant bit first
|
||||
bits: Vec<Boolean>,
|
||||
value: Option<u32>,
|
||||
}
|
||||
|
||||
impl UInt32 {
|
||||
/// Construct a constant `UInt32` from a `u32`
|
||||
pub fn constant(value: u32) -> Self {
|
||||
let mut bits = Vec::with_capacity(32);
|
||||
|
||||
let mut tmp = value;
|
||||
for _ in 0..32 {
|
||||
if tmp & 1 == 1 {
|
||||
bits.push(Boolean::constant(true))
|
||||
} else {
|
||||
bits.push(Boolean::constant(false))
|
||||
}
|
||||
|
||||
tmp >>= 1;
|
||||
}
|
||||
|
||||
UInt32 {
|
||||
bits,
|
||||
value: Some(value),
|
||||
}
|
||||
}
|
||||
|
||||
/// Allocate a `UInt32` in the constraint system
|
||||
pub fn alloc<Scalar, CS>(mut cs: CS, value: Option<u32>) -> Result<Self, SynthesisError>
|
||||
where
|
||||
Scalar: PrimeField,
|
||||
CS: ConstraintSystem<Scalar>,
|
||||
{
|
||||
let values = match value {
|
||||
Some(mut val) => {
|
||||
let mut v = Vec::with_capacity(32);
|
||||
|
||||
for _ in 0..32 {
|
||||
v.push(Some(val & 1 == 1));
|
||||
val >>= 1;
|
||||
}
|
||||
|
||||
v
|
||||
}
|
||||
None => vec![None; 32],
|
||||
};
|
||||
|
||||
let bits = values
|
||||
.into_iter()
|
||||
.enumerate()
|
||||
.map(|(i, v)| {
|
||||
Ok(Boolean::from(AllocatedBit::alloc(
|
||||
cs.namespace(|| format!("allocated bit {}", i)),
|
||||
v,
|
||||
)?))
|
||||
})
|
||||
.collect::<Result<Vec<_>, SynthesisError>>()?;
|
||||
|
||||
Ok(UInt32 { bits, value })
|
||||
}
|
||||
|
||||
pub fn into_bits_be(self) -> Vec<Boolean> {
|
||||
let mut ret = self.bits;
|
||||
ret.reverse();
|
||||
ret
|
||||
}
|
||||
|
||||
pub fn from_bits_be(bits: &[Boolean]) -> Self {
|
||||
assert_eq!(bits.len(), 32);
|
||||
|
||||
let mut value = Some(0u32);
|
||||
for b in bits {
|
||||
value.as_mut().map(|v| *v <<= 1);
|
||||
|
||||
match b.get_value() {
|
||||
Some(true) => {
|
||||
value.as_mut().map(|v| *v |= 1);
|
||||
}
|
||||
Some(false) => {}
|
||||
None => {
|
||||
value = None;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
UInt32 {
|
||||
value,
|
||||
bits: bits.iter().rev().cloned().collect(),
|
||||
}
|
||||
}
|
||||
|
||||
/// Turns this `UInt32` into its little-endian byte order representation.
|
||||
pub fn into_bits(self) -> Vec<Boolean> {
|
||||
self.bits
|
||||
}
|
||||
|
||||
/// Converts a little-endian byte order representation of bits into a
|
||||
/// `UInt32`.
|
||||
pub fn from_bits(bits: &[Boolean]) -> Self {
|
||||
assert_eq!(bits.len(), 32);
|
||||
|
||||
let new_bits = bits.to_vec();
|
||||
|
||||
let mut value = Some(0u32);
|
||||
for b in new_bits.iter().rev() {
|
||||
value.as_mut().map(|v| *v <<= 1);
|
||||
|
||||
match *b {
|
||||
Boolean::Constant(b) => {
|
||||
if b {
|
||||
value.as_mut().map(|v| *v |= 1);
|
||||
}
|
||||
}
|
||||
Boolean::Is(ref b) => match b.get_value() {
|
||||
Some(true) => {
|
||||
value.as_mut().map(|v| *v |= 1);
|
||||
}
|
||||
Some(false) => {}
|
||||
None => value = None,
|
||||
},
|
||||
Boolean::Not(ref b) => match b.get_value() {
|
||||
Some(false) => {
|
||||
value.as_mut().map(|v| *v |= 1);
|
||||
}
|
||||
Some(true) => {}
|
||||
None => value = None,
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
UInt32 {
|
||||
value,
|
||||
bits: new_bits,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn rotr(&self, by: usize) -> Self {
|
||||
let by = by % 32;
|
||||
|
||||
let new_bits = self
|
||||
.bits
|
||||
.iter()
|
||||
.skip(by)
|
||||
.chain(self.bits.iter())
|
||||
.take(32)
|
||||
.cloned()
|
||||
.collect();
|
||||
|
||||
UInt32 {
|
||||
bits: new_bits,
|
||||
value: self.value.map(|v| v.rotate_right(by as u32)),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn shr(&self, by: usize) -> Self {
|
||||
let by = by % 32;
|
||||
|
||||
let fill = Boolean::constant(false);
|
||||
|
||||
let new_bits = self
|
||||
.bits
|
||||
.iter() // The bits are least significant first
|
||||
.skip(by) // Skip the bits that will be lost during the shift
|
||||
.chain(Some(&fill).into_iter().cycle()) // Rest will be zeros
|
||||
.take(32) // Only 32 bits needed!
|
||||
.cloned()
|
||||
.collect();
|
||||
|
||||
UInt32 {
|
||||
bits: new_bits,
|
||||
value: self.value.map(|v| v >> by as u32),
|
||||
}
|
||||
}
|
||||
|
||||
fn triop<Scalar, CS, F, U>(
|
||||
mut cs: CS,
|
||||
a: &Self,
|
||||
b: &Self,
|
||||
c: &Self,
|
||||
tri_fn: F,
|
||||
circuit_fn: U,
|
||||
) -> Result<Self, SynthesisError>
|
||||
where
|
||||
Scalar: PrimeField,
|
||||
CS: ConstraintSystem<Scalar>,
|
||||
F: Fn(u32, u32, u32) -> u32,
|
||||
U: Fn(&mut CS, usize, &Boolean, &Boolean, &Boolean) -> Result<Boolean, SynthesisError>,
|
||||
{
|
||||
let new_value = match (a.value, b.value, c.value) {
|
||||
(Some(a), Some(b), Some(c)) => Some(tri_fn(a, b, c)),
|
||||
_ => None,
|
||||
};
|
||||
|
||||
let bits = a
|
||||
.bits
|
||||
.iter()
|
||||
.zip(b.bits.iter())
|
||||
.zip(c.bits.iter())
|
||||
.enumerate()
|
||||
.map(|(i, ((a, b), c))| circuit_fn(&mut cs, i, a, b, c))
|
||||
.collect::<Result<_, _>>()?;
|
||||
|
||||
Ok(UInt32 {
|
||||
bits,
|
||||
value: new_value,
|
||||
})
|
||||
}
|
||||
|
||||
/// Compute the `maj` value (a and b) xor (a and c) xor (b and c)
|
||||
/// during SHA256.
|
||||
pub fn sha256_maj<Scalar, CS>(
|
||||
cs: CS,
|
||||
a: &Self,
|
||||
b: &Self,
|
||||
c: &Self,
|
||||
) -> Result<Self, SynthesisError>
|
||||
where
|
||||
Scalar: PrimeField,
|
||||
CS: ConstraintSystem<Scalar>,
|
||||
{
|
||||
Self::triop(
|
||||
cs,
|
||||
a,
|
||||
b,
|
||||
c,
|
||||
|a, b, c| (a & b) ^ (a & c) ^ (b & c),
|
||||
|cs, i, a, b, c| Boolean::sha256_maj(cs.namespace(|| format!("maj {}", i)), a, b, c),
|
||||
)
|
||||
}
|
||||
|
||||
/// Compute the `ch` value `(a and b) xor ((not a) and c)`
|
||||
/// during SHA256.
|
||||
pub fn sha256_ch<Scalar, CS>(
|
||||
cs: CS,
|
||||
a: &Self,
|
||||
b: &Self,
|
||||
c: &Self,
|
||||
) -> Result<Self, SynthesisError>
|
||||
where
|
||||
Scalar: PrimeField,
|
||||
CS: ConstraintSystem<Scalar>,
|
||||
{
|
||||
Self::triop(
|
||||
cs,
|
||||
a,
|
||||
b,
|
||||
c,
|
||||
|a, b, c| (a & b) ^ ((!a) & c),
|
||||
|cs, i, a, b, c| Boolean::sha256_ch(cs.namespace(|| format!("ch {}", i)), a, b, c),
|
||||
)
|
||||
}
|
||||
|
||||
/// XOR this `UInt32` with another `UInt32`
|
||||
pub fn xor<Scalar, CS>(&self, mut cs: CS, other: &Self) -> Result<Self, SynthesisError>
|
||||
where
|
||||
Scalar: PrimeField,
|
||||
CS: ConstraintSystem<Scalar>,
|
||||
{
|
||||
let new_value = match (self.value, other.value) {
|
||||
(Some(a), Some(b)) => Some(a ^ b),
|
||||
_ => None,
|
||||
};
|
||||
|
||||
let bits = self
|
||||
.bits
|
||||
.iter()
|
||||
.zip(other.bits.iter())
|
||||
.enumerate()
|
||||
.map(|(i, (a, b))| Boolean::xor(cs.namespace(|| format!("xor of bit {}", i)), a, b))
|
||||
.collect::<Result<_, _>>()?;
|
||||
|
||||
Ok(UInt32 {
|
||||
bits,
|
||||
value: new_value,
|
||||
})
|
||||
}
|
||||
|
||||
/// Perform modular addition of several `UInt32` objects.
|
||||
pub fn addmany<Scalar, CS, M>(mut cs: M, operands: &[Self]) -> Result<Self, SynthesisError>
|
||||
where
|
||||
Scalar: PrimeField,
|
||||
CS: ConstraintSystem<Scalar>,
|
||||
M: ConstraintSystem<Scalar, Root = MultiEq<Scalar, CS>>,
|
||||
{
|
||||
// Make some arbitrary bounds for ourselves to avoid overflows
|
||||
// in the scalar field
|
||||
assert!(Scalar::NUM_BITS >= 64);
|
||||
assert!(operands.len() >= 2); // Weird trivial cases that should never happen
|
||||
assert!(operands.len() <= 10);
|
||||
|
||||
// Compute the maximum value of the sum so we allocate enough bits for
|
||||
// the result
|
||||
let mut max_value = (operands.len() as u64) * (u64::from(u32::max_value()));
|
||||
|
||||
// Keep track of the resulting value
|
||||
let mut result_value = Some(0u64);
|
||||
|
||||
// This is a linear combination that we will enforce to equal the
|
||||
// output
|
||||
let mut lc = LinearCombination::zero();
|
||||
|
||||
let mut all_constants = true;
|
||||
|
||||
// Iterate over the operands
|
||||
for op in operands {
|
||||
// Accumulate the value
|
||||
match op.value {
|
||||
Some(val) => {
|
||||
result_value.as_mut().map(|v| *v += u64::from(val));
|
||||
}
|
||||
None => {
|
||||
// If any of our operands have unknown value, we won't
|
||||
// know the value of the result
|
||||
result_value = None;
|
||||
}
|
||||
}
|
||||
|
||||
// Iterate over each bit of the operand and add the operand to
|
||||
// the linear combination
|
||||
let mut coeff = Scalar::one();
|
||||
for bit in &op.bits {
|
||||
lc = lc + &bit.lc(CS::one(), coeff);
|
||||
|
||||
all_constants &= bit.is_constant();
|
||||
|
||||
coeff = coeff.double();
|
||||
}
|
||||
}
|
||||
|
||||
// The value of the actual result is modulo 2^32
|
||||
let modular_value = result_value.map(|v| v as u32);
|
||||
|
||||
if all_constants && modular_value.is_some() {
|
||||
// We can just return a constant, rather than
|
||||
// unpacking the result into allocated bits.
|
||||
|
||||
return Ok(UInt32::constant(modular_value.unwrap()));
|
||||
}
|
||||
|
||||
// Storage area for the resulting bits
|
||||
let mut result_bits = vec![];
|
||||
|
||||
// Linear combination representing the output,
|
||||
// for comparison with the sum of the operands
|
||||
let mut result_lc = LinearCombination::zero();
|
||||
|
||||
// Allocate each bit of the result
|
||||
let mut coeff = Scalar::one();
|
||||
let mut i = 0;
|
||||
while max_value != 0 {
|
||||
// Allocate the bit
|
||||
let b = AllocatedBit::alloc(
|
||||
cs.namespace(|| format!("result bit {}", i)),
|
||||
result_value.map(|v| (v >> i) & 1 == 1),
|
||||
)?;
|
||||
|
||||
// Add this bit to the result combination
|
||||
result_lc = result_lc + (coeff, b.get_variable());
|
||||
|
||||
result_bits.push(b.into());
|
||||
|
||||
max_value >>= 1;
|
||||
i += 1;
|
||||
coeff = coeff.double();
|
||||
}
|
||||
|
||||
// Enforce equality between the sum and result
|
||||
cs.get_root().enforce_equal(i, &lc, &result_lc);
|
||||
|
||||
// Discard carry bits that we don't care about
|
||||
result_bits.truncate(32);
|
||||
|
||||
Ok(UInt32 {
|
||||
bits: result_bits,
|
||||
value: modular_value,
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod test {
|
||||
use super::UInt32;
|
||||
use crate::gadgets::boolean::Boolean;
|
||||
use crate::gadgets::multieq::MultiEq;
|
||||
use crate::gadgets::test::*;
|
||||
use crate::ConstraintSystem;
|
||||
use bls12_381::Scalar;
|
||||
use ff::Field;
|
||||
use rand_core::{RngCore, SeedableRng};
|
||||
use rand_xorshift::XorShiftRng;
|
||||
|
||||
#[test]
|
||||
fn test_uint32_from_bits_be() {
|
||||
let mut rng = XorShiftRng::from_seed([
|
||||
0x59, 0x62, 0xbe, 0x5d, 0x76, 0x3d, 0x31, 0x8d, 0x17, 0xdb, 0x37, 0x32, 0x54, 0x06,
|
||||
0xbc, 0xe5,
|
||||
]);
|
||||
|
||||
for _ in 0..1000 {
|
||||
let v = (0..32)
|
||||
.map(|_| Boolean::constant(rng.next_u32() % 2 != 0))
|
||||
.collect::<Vec<_>>();
|
||||
|
||||
let b = UInt32::from_bits_be(&v);
|
||||
|
||||
for (i, bit) in b.bits.iter().enumerate() {
|
||||
match *bit {
|
||||
Boolean::Constant(bit) => {
|
||||
assert!(bit == ((b.value.unwrap() >> i) & 1 == 1));
|
||||
}
|
||||
_ => unreachable!(),
|
||||
}
|
||||
}
|
||||
|
||||
let expected_to_be_same = b.into_bits_be();
|
||||
|
||||
for x in v.iter().zip(expected_to_be_same.iter()) {
|
||||
match x {
|
||||
(&Boolean::Constant(true), &Boolean::Constant(true)) => {}
|
||||
(&Boolean::Constant(false), &Boolean::Constant(false)) => {}
|
||||
_ => unreachable!(),
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_uint32_from_bits() {
|
||||
let mut rng = XorShiftRng::from_seed([
|
||||
0x59, 0x62, 0xbe, 0x5d, 0x76, 0x3d, 0x31, 0x8d, 0x17, 0xdb, 0x37, 0x32, 0x54, 0x06,
|
||||
0xbc, 0xe5,
|
||||
]);
|
||||
|
||||
for _ in 0..1000 {
|
||||
let v = (0..32)
|
||||
.map(|_| Boolean::constant(rng.next_u32() % 2 != 0))
|
||||
.collect::<Vec<_>>();
|
||||
|
||||
let b = UInt32::from_bits(&v);
|
||||
|
||||
for (i, bit) in b.bits.iter().enumerate() {
|
||||
match *bit {
|
||||
Boolean::Constant(bit) => {
|
||||
assert!(bit == ((b.value.unwrap() >> i) & 1 == 1));
|
||||
}
|
||||
_ => unreachable!(),
|
||||
}
|
||||
}
|
||||
|
||||
let expected_to_be_same = b.into_bits();
|
||||
|
||||
for x in v.iter().zip(expected_to_be_same.iter()) {
|
||||
match x {
|
||||
(&Boolean::Constant(true), &Boolean::Constant(true)) => {}
|
||||
(&Boolean::Constant(false), &Boolean::Constant(false)) => {}
|
||||
_ => unreachable!(),
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_uint32_xor() {
|
||||
let mut rng = XorShiftRng::from_seed([
|
||||
0x59, 0x62, 0xbe, 0x5d, 0x76, 0x3d, 0x31, 0x8d, 0x17, 0xdb, 0x37, 0x32, 0x54, 0x06,
|
||||
0xbc, 0xe5,
|
||||
]);
|
||||
|
||||
for _ in 0..1000 {
|
||||
let mut cs = TestConstraintSystem::<Scalar>::new();
|
||||
|
||||
let a = rng.next_u32();
|
||||
let b = rng.next_u32();
|
||||
let c = rng.next_u32();
|
||||
|
||||
let mut expected = a ^ b ^ c;
|
||||
|
||||
let a_bit = UInt32::alloc(cs.namespace(|| "a_bit"), Some(a)).unwrap();
|
||||
let b_bit = UInt32::constant(b);
|
||||
let c_bit = UInt32::alloc(cs.namespace(|| "c_bit"), Some(c)).unwrap();
|
||||
|
||||
let r = a_bit.xor(cs.namespace(|| "first xor"), &b_bit).unwrap();
|
||||
let r = r.xor(cs.namespace(|| "second xor"), &c_bit).unwrap();
|
||||
|
||||
assert!(cs.is_satisfied());
|
||||
|
||||
assert!(r.value == Some(expected));
|
||||
|
||||
for b in r.bits.iter() {
|
||||
match *b {
|
||||
Boolean::Is(ref b) => {
|
||||
assert!(b.get_value().unwrap() == (expected & 1 == 1));
|
||||
}
|
||||
Boolean::Not(ref b) => {
|
||||
assert!(!b.get_value().unwrap() == (expected & 1 == 1));
|
||||
}
|
||||
Boolean::Constant(b) => {
|
||||
assert!(b == (expected & 1 == 1));
|
||||
}
|
||||
}
|
||||
|
||||
expected >>= 1;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_uint32_addmany_constants() {
|
||||
let mut rng = XorShiftRng::from_seed([
|
||||
0x59, 0x62, 0xbe, 0x5d, 0x76, 0x3d, 0x31, 0x8d, 0x17, 0xdb, 0x37, 0x32, 0x54, 0x06,
|
||||
0xbc, 0xe5,
|
||||
]);
|
||||
|
||||
for _ in 0..1000 {
|
||||
let mut cs = TestConstraintSystem::<Scalar>::new();
|
||||
|
||||
let a = rng.next_u32();
|
||||
let b = rng.next_u32();
|
||||
let c = rng.next_u32();
|
||||
|
||||
let a_bit = UInt32::constant(a);
|
||||
let b_bit = UInt32::constant(b);
|
||||
let c_bit = UInt32::constant(c);
|
||||
|
||||
let mut expected = a.wrapping_add(b).wrapping_add(c);
|
||||
|
||||
let r = {
|
||||
let mut cs = MultiEq::new(&mut cs);
|
||||
let r =
|
||||
UInt32::addmany(cs.namespace(|| "addition"), &[a_bit, b_bit, c_bit]).unwrap();
|
||||
r
|
||||
};
|
||||
|
||||
assert!(r.value == Some(expected));
|
||||
|
||||
for b in r.bits.iter() {
|
||||
match *b {
|
||||
Boolean::Is(_) => panic!(),
|
||||
Boolean::Not(_) => panic!(),
|
||||
Boolean::Constant(b) => {
|
||||
assert!(b == (expected & 1 == 1));
|
||||
}
|
||||
}
|
||||
|
||||
expected >>= 1;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_uint32_addmany() {
|
||||
let mut rng = XorShiftRng::from_seed([
|
||||
0x59, 0x62, 0xbe, 0x5d, 0x76, 0x3d, 0x31, 0x8d, 0x17, 0xdb, 0x37, 0x32, 0x54, 0x06,
|
||||
0xbc, 0xe5,
|
||||
]);
|
||||
|
||||
for _ in 0..1000 {
|
||||
let mut cs = TestConstraintSystem::<Scalar>::new();
|
||||
|
||||
let a = rng.next_u32();
|
||||
let b = rng.next_u32();
|
||||
let c = rng.next_u32();
|
||||
let d = rng.next_u32();
|
||||
|
||||
let mut expected = (a ^ b).wrapping_add(c).wrapping_add(d);
|
||||
|
||||
let a_bit = UInt32::alloc(cs.namespace(|| "a_bit"), Some(a)).unwrap();
|
||||
let b_bit = UInt32::constant(b);
|
||||
let c_bit = UInt32::constant(c);
|
||||
let d_bit = UInt32::alloc(cs.namespace(|| "d_bit"), Some(d)).unwrap();
|
||||
|
||||
let r = a_bit.xor(cs.namespace(|| "xor"), &b_bit).unwrap();
|
||||
let r = {
|
||||
let mut cs = MultiEq::new(&mut cs);
|
||||
UInt32::addmany(cs.namespace(|| "addition"), &[r, c_bit, d_bit]).unwrap()
|
||||
};
|
||||
|
||||
assert!(cs.is_satisfied());
|
||||
|
||||
assert!(r.value == Some(expected));
|
||||
|
||||
for b in r.bits.iter() {
|
||||
match *b {
|
||||
Boolean::Is(ref b) => {
|
||||
assert!(b.get_value().unwrap() == (expected & 1 == 1));
|
||||
}
|
||||
Boolean::Not(ref b) => {
|
||||
assert!(!b.get_value().unwrap() == (expected & 1 == 1));
|
||||
}
|
||||
Boolean::Constant(_) => unreachable!(),
|
||||
}
|
||||
|
||||
expected >>= 1;
|
||||
}
|
||||
|
||||
// Flip a bit and see if the addition constraint still works
|
||||
if cs.get("addition/result bit 0/boolean").is_zero() {
|
||||
cs.set("addition/result bit 0/boolean", Field::one());
|
||||
} else {
|
||||
cs.set("addition/result bit 0/boolean", Field::zero());
|
||||
}
|
||||
|
||||
assert!(!cs.is_satisfied());
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_uint32_rotr() {
|
||||
let mut rng = XorShiftRng::from_seed([
|
||||
0x59, 0x62, 0xbe, 0x5d, 0x76, 0x3d, 0x31, 0x8d, 0x17, 0xdb, 0x37, 0x32, 0x54, 0x06,
|
||||
0xbc, 0xe5,
|
||||
]);
|
||||
|
||||
let mut num = rng.next_u32();
|
||||
|
||||
let a = UInt32::constant(num);
|
||||
|
||||
for i in 0..32 {
|
||||
let b = a.rotr(i);
|
||||
assert_eq!(a.bits.len(), b.bits.len());
|
||||
|
||||
assert!(b.value.unwrap() == num);
|
||||
|
||||
let mut tmp = num;
|
||||
for b in &b.bits {
|
||||
match *b {
|
||||
Boolean::Constant(b) => {
|
||||
assert_eq!(b, tmp & 1 == 1);
|
||||
}
|
||||
_ => unreachable!(),
|
||||
}
|
||||
|
||||
tmp >>= 1;
|
||||
}
|
||||
|
||||
num = num.rotate_right(1);
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_uint32_shr() {
|
||||
let mut rng = XorShiftRng::from_seed([
|
||||
0x59, 0x62, 0xbe, 0x5d, 0x76, 0x3d, 0x31, 0x8d, 0x17, 0xdb, 0x37, 0x32, 0x54, 0x06,
|
||||
0xbc, 0xe5,
|
||||
]);
|
||||
|
||||
for _ in 0..50 {
|
||||
for i in 0..60 {
|
||||
let num = rng.next_u32();
|
||||
let a = UInt32::constant(num).shr(i);
|
||||
let b = UInt32::constant(num.wrapping_shr(i as u32));
|
||||
|
||||
assert_eq!(a.value.unwrap(), num.wrapping_shr(i as u32));
|
||||
|
||||
assert_eq!(a.bits.len(), b.bits.len());
|
||||
for (a, b) in a.bits.iter().zip(b.bits.iter()) {
|
||||
assert_eq!(a.get_value().unwrap(), b.get_value().unwrap());
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_uint32_sha256_maj() {
|
||||
let mut rng = XorShiftRng::from_seed([
|
||||
0x59, 0x62, 0xbe, 0x5d, 0x76, 0x3d, 0x31, 0x8d, 0x17, 0xdb, 0x37, 0x32, 0x54, 0x06,
|
||||
0xbc, 0xe5,
|
||||
]);
|
||||
|
||||
for _ in 0..1000 {
|
||||
let mut cs = TestConstraintSystem::<Scalar>::new();
|
||||
|
||||
let a = rng.next_u32();
|
||||
let b = rng.next_u32();
|
||||
let c = rng.next_u32();
|
||||
|
||||
let mut expected = (a & b) ^ (a & c) ^ (b & c);
|
||||
|
||||
let a_bit = UInt32::alloc(cs.namespace(|| "a_bit"), Some(a)).unwrap();
|
||||
let b_bit = UInt32::constant(b);
|
||||
let c_bit = UInt32::alloc(cs.namespace(|| "c_bit"), Some(c)).unwrap();
|
||||
|
||||
let r = UInt32::sha256_maj(&mut cs, &a_bit, &b_bit, &c_bit).unwrap();
|
||||
|
||||
assert!(cs.is_satisfied());
|
||||
|
||||
assert!(r.value == Some(expected));
|
||||
|
||||
for b in r.bits.iter() {
|
||||
match b {
|
||||
&Boolean::Is(ref b) => {
|
||||
assert!(b.get_value().unwrap() == (expected & 1 == 1));
|
||||
}
|
||||
&Boolean::Not(ref b) => {
|
||||
assert!(!b.get_value().unwrap() == (expected & 1 == 1));
|
||||
}
|
||||
&Boolean::Constant(b) => {
|
||||
assert!(b == (expected & 1 == 1));
|
||||
}
|
||||
}
|
||||
|
||||
expected >>= 1;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_uint32_sha256_ch() {
|
||||
let mut rng = XorShiftRng::from_seed([
|
||||
0x59, 0x62, 0xbe, 0x5d, 0x76, 0x3d, 0x31, 0x8d, 0x17, 0xdb, 0x37, 0x32, 0x54, 0x06,
|
||||
0xbc, 0xe5,
|
||||
]);
|
||||
|
||||
for _ in 0..1000 {
|
||||
let mut cs = TestConstraintSystem::<Scalar>::new();
|
||||
|
||||
let a = rng.next_u32();
|
||||
let b = rng.next_u32();
|
||||
let c = rng.next_u32();
|
||||
|
||||
let mut expected = (a & b) ^ ((!a) & c);
|
||||
|
||||
let a_bit = UInt32::alloc(cs.namespace(|| "a_bit"), Some(a)).unwrap();
|
||||
let b_bit = UInt32::constant(b);
|
||||
let c_bit = UInt32::alloc(cs.namespace(|| "c_bit"), Some(c)).unwrap();
|
||||
|
||||
let r = UInt32::sha256_ch(&mut cs, &a_bit, &b_bit, &c_bit).unwrap();
|
||||
|
||||
assert!(cs.is_satisfied());
|
||||
|
||||
assert!(r.value == Some(expected));
|
||||
|
||||
for b in r.bits.iter() {
|
||||
match b {
|
||||
&Boolean::Is(ref b) => {
|
||||
assert!(b.get_value().unwrap() == (expected & 1 == 1));
|
||||
}
|
||||
&Boolean::Not(ref b) => {
|
||||
assert!(!b.get_value().unwrap() == (expected & 1 == 1));
|
||||
}
|
||||
&Boolean::Constant(b) => {
|
||||
assert!(b == (expected & 1 == 1));
|
||||
}
|
||||
}
|
||||
|
||||
expected >>= 1;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1,501 +0,0 @@
|
|||
use rand_core::RngCore;
|
||||
use std::ops::{AddAssign, MulAssign};
|
||||
use std::sync::Arc;
|
||||
|
||||
use ff::{Field, PrimeField};
|
||||
use group::{prime::PrimeCurveAffine, Curve, Group, Wnaf, WnafGroup};
|
||||
use pairing::Engine;
|
||||
|
||||
use super::{Parameters, VerifyingKey};
|
||||
|
||||
use crate::{Circuit, ConstraintSystem, Index, LinearCombination, SynthesisError, Variable};
|
||||
|
||||
use crate::domain::{EvaluationDomain, Scalar};
|
||||
|
||||
use crate::multicore::Worker;
|
||||
|
||||
/// Generates a random common reference string for
|
||||
/// a circuit.
|
||||
pub fn generate_random_parameters<E, C, R>(
|
||||
circuit: C,
|
||||
rng: &mut R,
|
||||
) -> Result<Parameters<E>, SynthesisError>
|
||||
where
|
||||
E: Engine,
|
||||
E::G1: WnafGroup,
|
||||
E::G2: WnafGroup,
|
||||
C: Circuit<E::Fr>,
|
||||
R: RngCore,
|
||||
{
|
||||
let g1 = E::G1::random(rng);
|
||||
let g2 = E::G2::random(rng);
|
||||
let alpha = E::Fr::random(rng);
|
||||
let beta = E::Fr::random(rng);
|
||||
let gamma = E::Fr::random(rng);
|
||||
let delta = E::Fr::random(rng);
|
||||
let tau = E::Fr::random(rng);
|
||||
|
||||
generate_parameters::<E, C>(circuit, g1, g2, alpha, beta, gamma, delta, tau)
|
||||
}
|
||||
|
||||
/// This is our assembly structure that we'll use to synthesize the
|
||||
/// circuit into a QAP.
|
||||
struct KeypairAssembly<Scalar: PrimeField> {
|
||||
num_inputs: usize,
|
||||
num_aux: usize,
|
||||
num_constraints: usize,
|
||||
at_inputs: Vec<Vec<(Scalar, usize)>>,
|
||||
bt_inputs: Vec<Vec<(Scalar, usize)>>,
|
||||
ct_inputs: Vec<Vec<(Scalar, usize)>>,
|
||||
at_aux: Vec<Vec<(Scalar, usize)>>,
|
||||
bt_aux: Vec<Vec<(Scalar, usize)>>,
|
||||
ct_aux: Vec<Vec<(Scalar, usize)>>,
|
||||
}
|
||||
|
||||
impl<Scalar: PrimeField> ConstraintSystem<Scalar> for KeypairAssembly<Scalar> {
|
||||
type Root = Self;
|
||||
|
||||
fn alloc<F, A, AR>(&mut self, _: A, _: F) -> Result<Variable, SynthesisError>
|
||||
where
|
||||
F: FnOnce() -> Result<Scalar, SynthesisError>,
|
||||
A: FnOnce() -> AR,
|
||||
AR: Into<String>,
|
||||
{
|
||||
// There is no assignment, so we don't even invoke the
|
||||
// function for obtaining one.
|
||||
|
||||
let index = self.num_aux;
|
||||
self.num_aux += 1;
|
||||
|
||||
self.at_aux.push(vec![]);
|
||||
self.bt_aux.push(vec![]);
|
||||
self.ct_aux.push(vec![]);
|
||||
|
||||
Ok(Variable(Index::Aux(index)))
|
||||
}
|
||||
|
||||
fn alloc_input<F, A, AR>(&mut self, _: A, _: F) -> Result<Variable, SynthesisError>
|
||||
where
|
||||
F: FnOnce() -> Result<Scalar, SynthesisError>,
|
||||
A: FnOnce() -> AR,
|
||||
AR: Into<String>,
|
||||
{
|
||||
// There is no assignment, so we don't even invoke the
|
||||
// function for obtaining one.
|
||||
|
||||
let index = self.num_inputs;
|
||||
self.num_inputs += 1;
|
||||
|
||||
self.at_inputs.push(vec![]);
|
||||
self.bt_inputs.push(vec![]);
|
||||
self.ct_inputs.push(vec![]);
|
||||
|
||||
Ok(Variable(Index::Input(index)))
|
||||
}
|
||||
|
||||
fn enforce<A, AR, LA, LB, LC>(&mut self, _: A, a: LA, b: LB, c: LC)
|
||||
where
|
||||
A: FnOnce() -> AR,
|
||||
AR: Into<String>,
|
||||
LA: FnOnce(LinearCombination<Scalar>) -> LinearCombination<Scalar>,
|
||||
LB: FnOnce(LinearCombination<Scalar>) -> LinearCombination<Scalar>,
|
||||
LC: FnOnce(LinearCombination<Scalar>) -> LinearCombination<Scalar>,
|
||||
{
|
||||
fn eval<Scalar: PrimeField>(
|
||||
l: LinearCombination<Scalar>,
|
||||
inputs: &mut [Vec<(Scalar, usize)>],
|
||||
aux: &mut [Vec<(Scalar, usize)>],
|
||||
this_constraint: usize,
|
||||
) {
|
||||
for (index, coeff) in l.0 {
|
||||
match index {
|
||||
Variable(Index::Input(id)) => inputs[id].push((coeff, this_constraint)),
|
||||
Variable(Index::Aux(id)) => aux[id].push((coeff, this_constraint)),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
eval(
|
||||
a(LinearCombination::zero()),
|
||||
&mut self.at_inputs,
|
||||
&mut self.at_aux,
|
||||
self.num_constraints,
|
||||
);
|
||||
eval(
|
||||
b(LinearCombination::zero()),
|
||||
&mut self.bt_inputs,
|
||||
&mut self.bt_aux,
|
||||
self.num_constraints,
|
||||
);
|
||||
eval(
|
||||
c(LinearCombination::zero()),
|
||||
&mut self.ct_inputs,
|
||||
&mut self.ct_aux,
|
||||
self.num_constraints,
|
||||
);
|
||||
|
||||
self.num_constraints += 1;
|
||||
}
|
||||
|
||||
fn push_namespace<NR, N>(&mut self, _: N)
|
||||
where
|
||||
NR: Into<String>,
|
||||
N: FnOnce() -> NR,
|
||||
{
|
||||
// Do nothing; we don't care about namespaces in this context.
|
||||
}
|
||||
|
||||
fn pop_namespace(&mut self) {
|
||||
// Do nothing; we don't care about namespaces in this context.
|
||||
}
|
||||
|
||||
fn get_root(&mut self) -> &mut Self::Root {
|
||||
self
|
||||
}
|
||||
}
|
||||
|
||||
/// Create parameters for a circuit, given some toxic waste.
|
||||
pub fn generate_parameters<E, C>(
|
||||
circuit: C,
|
||||
g1: E::G1,
|
||||
g2: E::G2,
|
||||
alpha: E::Fr,
|
||||
beta: E::Fr,
|
||||
gamma: E::Fr,
|
||||
delta: E::Fr,
|
||||
tau: E::Fr,
|
||||
) -> Result<Parameters<E>, SynthesisError>
|
||||
where
|
||||
E: Engine,
|
||||
E::G1: WnafGroup,
|
||||
E::G2: WnafGroup,
|
||||
C: Circuit<E::Fr>,
|
||||
{
|
||||
let mut assembly = KeypairAssembly {
|
||||
num_inputs: 0,
|
||||
num_aux: 0,
|
||||
num_constraints: 0,
|
||||
at_inputs: vec![],
|
||||
bt_inputs: vec![],
|
||||
ct_inputs: vec![],
|
||||
at_aux: vec![],
|
||||
bt_aux: vec![],
|
||||
ct_aux: vec![],
|
||||
};
|
||||
|
||||
// Allocate the "one" input variable
|
||||
assembly.alloc_input(|| "", || Ok(E::Fr::one()))?;
|
||||
|
||||
// Synthesize the circuit.
|
||||
circuit.synthesize(&mut assembly)?;
|
||||
|
||||
// Input constraints to ensure full density of IC query
|
||||
// x * 0 = 0
|
||||
for i in 0..assembly.num_inputs {
|
||||
assembly.enforce(|| "", |lc| lc + Variable(Index::Input(i)), |lc| lc, |lc| lc);
|
||||
}
|
||||
|
||||
// Create bases for blind evaluation of polynomials at tau
|
||||
let powers_of_tau = vec![Scalar::<E::Fr>(E::Fr::zero()); assembly.num_constraints];
|
||||
let mut powers_of_tau = EvaluationDomain::from_coeffs(powers_of_tau)?;
|
||||
|
||||
// Compute G1 window table
|
||||
let mut g1_wnaf = Wnaf::new();
|
||||
let g1_wnaf = g1_wnaf.base(g1, {
|
||||
// H query
|
||||
(powers_of_tau.as_ref().len() - 1)
|
||||
// IC/L queries
|
||||
+ assembly.num_inputs + assembly.num_aux
|
||||
// A query
|
||||
+ assembly.num_inputs + assembly.num_aux
|
||||
// B query
|
||||
+ assembly.num_inputs + assembly.num_aux
|
||||
});
|
||||
|
||||
// Compute G2 window table
|
||||
let mut g2_wnaf = Wnaf::new();
|
||||
let g2_wnaf = g2_wnaf.base(g2, {
|
||||
// B query
|
||||
assembly.num_inputs + assembly.num_aux
|
||||
});
|
||||
|
||||
let gamma_inverse = {
|
||||
let inverse = gamma.invert();
|
||||
if bool::from(inverse.is_some()) {
|
||||
Ok(inverse.unwrap())
|
||||
} else {
|
||||
Err(SynthesisError::UnexpectedIdentity)
|
||||
}
|
||||
}?;
|
||||
let delta_inverse = {
|
||||
let inverse = delta.invert();
|
||||
if bool::from(inverse.is_some()) {
|
||||
Ok(inverse.unwrap())
|
||||
} else {
|
||||
Err(SynthesisError::UnexpectedIdentity)
|
||||
}
|
||||
}?;
|
||||
|
||||
let worker = Worker::new();
|
||||
|
||||
let mut h = vec![E::G1Affine::identity(); powers_of_tau.as_ref().len() - 1];
|
||||
{
|
||||
// Compute powers of tau
|
||||
{
|
||||
let powers_of_tau = powers_of_tau.as_mut();
|
||||
worker.scope(powers_of_tau.len(), |scope, chunk| {
|
||||
for (i, powers_of_tau) in powers_of_tau.chunks_mut(chunk).enumerate() {
|
||||
scope.spawn(move |_scope| {
|
||||
let mut current_tau_power = tau.pow_vartime(&[(i * chunk) as u64]);
|
||||
|
||||
for p in powers_of_tau {
|
||||
p.0 = current_tau_power;
|
||||
current_tau_power.mul_assign(&tau);
|
||||
}
|
||||
});
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
// coeff = t(x) / delta
|
||||
let mut coeff = powers_of_tau.z(&tau);
|
||||
coeff.mul_assign(&delta_inverse);
|
||||
|
||||
// Compute the H query with multiple threads
|
||||
worker.scope(h.len(), |scope, chunk| {
|
||||
for (h, p) in h
|
||||
.chunks_mut(chunk)
|
||||
.zip(powers_of_tau.as_ref().chunks(chunk))
|
||||
{
|
||||
let mut g1_wnaf = g1_wnaf.shared();
|
||||
|
||||
scope.spawn(move |_scope| {
|
||||
// Set values of the H query to g1^{(tau^i * t(tau)) / delta}
|
||||
let h_proj: Vec<_> = p[..h.len()]
|
||||
.iter()
|
||||
.map(|p| {
|
||||
// Compute final exponent
|
||||
let mut exp = p.0;
|
||||
exp.mul_assign(&coeff);
|
||||
|
||||
// Exponentiate
|
||||
g1_wnaf.scalar(&exp)
|
||||
})
|
||||
.collect();
|
||||
|
||||
// Batch normalize
|
||||
E::G1::batch_normalize(&h_proj, h);
|
||||
});
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
// Use inverse FFT to convert powers of tau to Lagrange coefficients
|
||||
powers_of_tau.ifft(&worker);
|
||||
let powers_of_tau = powers_of_tau.into_coeffs();
|
||||
|
||||
let mut a = vec![E::G1Affine::identity(); assembly.num_inputs + assembly.num_aux];
|
||||
let mut b_g1 = vec![E::G1Affine::identity(); assembly.num_inputs + assembly.num_aux];
|
||||
let mut b_g2 = vec![E::G2Affine::identity(); assembly.num_inputs + assembly.num_aux];
|
||||
let mut ic = vec![E::G1Affine::identity(); assembly.num_inputs];
|
||||
let mut l = vec![E::G1Affine::identity(); assembly.num_aux];
|
||||
|
||||
fn eval<E: Engine>(
|
||||
// wNAF window tables
|
||||
g1_wnaf: &Wnaf<usize, &[E::G1], &mut Vec<i64>>,
|
||||
g2_wnaf: &Wnaf<usize, &[E::G2], &mut Vec<i64>>,
|
||||
|
||||
// Lagrange coefficients for tau
|
||||
powers_of_tau: &[Scalar<E::Fr>],
|
||||
|
||||
// QAP polynomials
|
||||
at: &[Vec<(E::Fr, usize)>],
|
||||
bt: &[Vec<(E::Fr, usize)>],
|
||||
ct: &[Vec<(E::Fr, usize)>],
|
||||
|
||||
// Resulting evaluated QAP polynomials
|
||||
a: &mut [E::G1Affine],
|
||||
b_g1: &mut [E::G1Affine],
|
||||
b_g2: &mut [E::G2Affine],
|
||||
ext: &mut [E::G1Affine],
|
||||
|
||||
// Inverse coefficient for ext elements
|
||||
inv: &E::Fr,
|
||||
|
||||
// Trapdoors
|
||||
alpha: &E::Fr,
|
||||
beta: &E::Fr,
|
||||
|
||||
// Worker
|
||||
worker: &Worker,
|
||||
) {
|
||||
// Sanity check
|
||||
assert_eq!(a.len(), at.len());
|
||||
assert_eq!(a.len(), bt.len());
|
||||
assert_eq!(a.len(), ct.len());
|
||||
assert_eq!(a.len(), b_g1.len());
|
||||
assert_eq!(a.len(), b_g2.len());
|
||||
assert_eq!(a.len(), ext.len());
|
||||
|
||||
// Evaluate polynomials in multiple threads
|
||||
worker.scope(a.len(), |scope, chunk| {
|
||||
for ((((((a, b_g1), b_g2), ext), at), bt), ct) in a
|
||||
.chunks_mut(chunk)
|
||||
.zip(b_g1.chunks_mut(chunk))
|
||||
.zip(b_g2.chunks_mut(chunk))
|
||||
.zip(ext.chunks_mut(chunk))
|
||||
.zip(at.chunks(chunk))
|
||||
.zip(bt.chunks(chunk))
|
||||
.zip(ct.chunks(chunk))
|
||||
{
|
||||
let mut g1_wnaf = g1_wnaf.shared();
|
||||
let mut g2_wnaf = g2_wnaf.shared();
|
||||
|
||||
scope.spawn(move |_scope| {
|
||||
let mut a_proj = vec![E::G1::identity(); a.len()];
|
||||
let mut b_g1_proj = vec![E::G1::identity(); b_g1.len()];
|
||||
let mut b_g2_proj = vec![E::G2::identity(); b_g2.len()];
|
||||
let mut ext_proj = vec![E::G1::identity(); ext.len()];
|
||||
|
||||
for ((((((a, b_g1), b_g2), ext), at), bt), ct) in a_proj
|
||||
.iter_mut()
|
||||
.zip(b_g1_proj.iter_mut())
|
||||
.zip(b_g2_proj.iter_mut())
|
||||
.zip(ext_proj.iter_mut())
|
||||
.zip(at.iter())
|
||||
.zip(bt.iter())
|
||||
.zip(ct.iter())
|
||||
{
|
||||
fn eval_at_tau<S: PrimeField>(
|
||||
powers_of_tau: &[Scalar<S>],
|
||||
p: &[(S, usize)],
|
||||
) -> S {
|
||||
let mut acc = S::zero();
|
||||
|
||||
for &(ref coeff, index) in p {
|
||||
let mut n = powers_of_tau[index].0;
|
||||
n.mul_assign(coeff);
|
||||
acc.add_assign(&n);
|
||||
}
|
||||
|
||||
acc
|
||||
}
|
||||
|
||||
// Evaluate QAP polynomials at tau
|
||||
let mut at = eval_at_tau(powers_of_tau, at);
|
||||
let mut bt = eval_at_tau(powers_of_tau, bt);
|
||||
let ct = eval_at_tau(powers_of_tau, ct);
|
||||
|
||||
// Compute A query (in G1)
|
||||
if !at.is_zero() {
|
||||
*a = g1_wnaf.scalar(&at);
|
||||
}
|
||||
|
||||
// Compute B query (in G1/G2)
|
||||
if !bt.is_zero() {
|
||||
();
|
||||
*b_g1 = g1_wnaf.scalar(&bt);
|
||||
*b_g2 = g2_wnaf.scalar(&bt);
|
||||
}
|
||||
|
||||
at.mul_assign(&beta);
|
||||
bt.mul_assign(&alpha);
|
||||
|
||||
let mut e = at;
|
||||
e.add_assign(&bt);
|
||||
e.add_assign(&ct);
|
||||
e.mul_assign(inv);
|
||||
|
||||
*ext = g1_wnaf.scalar(&e);
|
||||
}
|
||||
|
||||
// Batch normalize
|
||||
E::G1::batch_normalize(&a_proj, a);
|
||||
E::G1::batch_normalize(&b_g1_proj, b_g1);
|
||||
E::G2::batch_normalize(&b_g2_proj, b_g2);
|
||||
E::G1::batch_normalize(&ext_proj, ext);
|
||||
});
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
// Evaluate for inputs.
|
||||
eval::<E>(
|
||||
&g1_wnaf,
|
||||
&g2_wnaf,
|
||||
&powers_of_tau,
|
||||
&assembly.at_inputs,
|
||||
&assembly.bt_inputs,
|
||||
&assembly.ct_inputs,
|
||||
&mut a[0..assembly.num_inputs],
|
||||
&mut b_g1[0..assembly.num_inputs],
|
||||
&mut b_g2[0..assembly.num_inputs],
|
||||
&mut ic,
|
||||
&gamma_inverse,
|
||||
&alpha,
|
||||
&beta,
|
||||
&worker,
|
||||
);
|
||||
|
||||
// Evaluate for auxiliary variables.
|
||||
eval::<E>(
|
||||
&g1_wnaf,
|
||||
&g2_wnaf,
|
||||
&powers_of_tau,
|
||||
&assembly.at_aux,
|
||||
&assembly.bt_aux,
|
||||
&assembly.ct_aux,
|
||||
&mut a[assembly.num_inputs..],
|
||||
&mut b_g1[assembly.num_inputs..],
|
||||
&mut b_g2[assembly.num_inputs..],
|
||||
&mut l,
|
||||
&delta_inverse,
|
||||
&alpha,
|
||||
&beta,
|
||||
&worker,
|
||||
);
|
||||
|
||||
// Don't allow any elements be unconstrained, so that
|
||||
// the L query is always fully dense.
|
||||
for e in l.iter() {
|
||||
if e.is_identity().into() {
|
||||
return Err(SynthesisError::UnconstrainedVariable);
|
||||
}
|
||||
}
|
||||
|
||||
let g1 = g1.to_affine();
|
||||
let g2 = g2.to_affine();
|
||||
|
||||
let vk = VerifyingKey::<E> {
|
||||
alpha_g1: (g1 * &alpha).to_affine(),
|
||||
beta_g1: (g1 * &beta).to_affine(),
|
||||
beta_g2: (g2 * &beta).to_affine(),
|
||||
gamma_g2: (g2 * &gamma).to_affine(),
|
||||
delta_g1: (g1 * &delta).to_affine(),
|
||||
delta_g2: (g2 * &delta).to_affine(),
|
||||
ic,
|
||||
};
|
||||
|
||||
Ok(Parameters {
|
||||
vk,
|
||||
h: Arc::new(h),
|
||||
l: Arc::new(l),
|
||||
|
||||
// Filter points at infinity away from A/B queries
|
||||
a: Arc::new(
|
||||
a.into_iter()
|
||||
.filter(|e| bool::from(!e.is_identity()))
|
||||
.collect(),
|
||||
),
|
||||
b_g1: Arc::new(
|
||||
b_g1.into_iter()
|
||||
.filter(|e| bool::from(!e.is_identity()))
|
||||
.collect(),
|
||||
),
|
||||
b_g2: Arc::new(
|
||||
b_g2.into_iter()
|
||||
.filter(|e| bool::from(!e.is_identity()))
|
||||
.collect(),
|
||||
),
|
||||
})
|
||||
}
|
|
@ -1,567 +0,0 @@
|
|||
//! The [Groth16] proving system.
|
||||
//!
|
||||
//! [Groth16]: https://eprint.iacr.org/2016/260
|
||||
|
||||
use group::{prime::PrimeCurveAffine, GroupEncoding, UncompressedEncoding};
|
||||
use pairing::{Engine, MultiMillerLoop};
|
||||
|
||||
use crate::SynthesisError;
|
||||
|
||||
use crate::multiexp::SourceBuilder;
|
||||
use byteorder::{BigEndian, ReadBytesExt, WriteBytesExt};
|
||||
use std::io::{self, Read, Write};
|
||||
use std::sync::Arc;
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests;
|
||||
|
||||
mod generator;
|
||||
mod prover;
|
||||
mod verifier;
|
||||
|
||||
pub use self::generator::*;
|
||||
pub use self::prover::*;
|
||||
pub use self::verifier::*;
|
||||
|
||||
#[derive(Clone)]
|
||||
pub struct Proof<E: Engine> {
|
||||
pub a: E::G1Affine,
|
||||
pub b: E::G2Affine,
|
||||
pub c: E::G1Affine,
|
||||
}
|
||||
|
||||
impl<E: Engine> PartialEq for Proof<E> {
|
||||
fn eq(&self, other: &Self) -> bool {
|
||||
self.a == other.a && self.b == other.b && self.c == other.c
|
||||
}
|
||||
}
|
||||
|
||||
impl<E: Engine> Proof<E> {
|
||||
pub fn write<W: Write>(&self, mut writer: W) -> io::Result<()> {
|
||||
writer.write_all(self.a.to_bytes().as_ref())?;
|
||||
writer.write_all(self.b.to_bytes().as_ref())?;
|
||||
writer.write_all(self.c.to_bytes().as_ref())?;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub fn read<R: Read>(mut reader: R) -> io::Result<Self> {
|
||||
let read_g1 = |reader: &mut R| -> io::Result<E::G1Affine> {
|
||||
let mut g1_repr = <E::G1Affine as GroupEncoding>::Repr::default();
|
||||
reader.read_exact(g1_repr.as_mut())?;
|
||||
|
||||
let affine = E::G1Affine::from_bytes(&g1_repr);
|
||||
let affine = if affine.is_some().into() {
|
||||
Ok(affine.unwrap())
|
||||
} else {
|
||||
Err(io::Error::new(io::ErrorKind::InvalidData, "invalid G1"))
|
||||
};
|
||||
|
||||
affine.and_then(|e| {
|
||||
if e.is_identity().into() {
|
||||
Err(io::Error::new(
|
||||
io::ErrorKind::InvalidData,
|
||||
"point at infinity",
|
||||
))
|
||||
} else {
|
||||
Ok(e)
|
||||
}
|
||||
})
|
||||
};
|
||||
|
||||
let read_g2 = |reader: &mut R| -> io::Result<E::G2Affine> {
|
||||
let mut g2_repr = <E::G2Affine as GroupEncoding>::Repr::default();
|
||||
reader.read_exact(g2_repr.as_mut())?;
|
||||
|
||||
let affine = E::G2Affine::from_bytes(&g2_repr);
|
||||
let affine = if affine.is_some().into() {
|
||||
Ok(affine.unwrap())
|
||||
} else {
|
||||
Err(io::Error::new(io::ErrorKind::InvalidData, "invalid G2"))
|
||||
};
|
||||
|
||||
affine.and_then(|e| {
|
||||
if e.is_identity().into() {
|
||||
Err(io::Error::new(
|
||||
io::ErrorKind::InvalidData,
|
||||
"point at infinity",
|
||||
))
|
||||
} else {
|
||||
Ok(e)
|
||||
}
|
||||
})
|
||||
};
|
||||
|
||||
let a = read_g1(&mut reader)?;
|
||||
let b = read_g2(&mut reader)?;
|
||||
let c = read_g1(&mut reader)?;
|
||||
|
||||
Ok(Proof { a, b, c })
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Clone)]
|
||||
pub struct VerifyingKey<E: Engine> {
|
||||
// alpha in g1 for verifying and for creating A/C elements of
|
||||
// proof. Never the point at infinity.
|
||||
pub alpha_g1: E::G1Affine,
|
||||
|
||||
// beta in g1 and g2 for verifying and for creating B/C elements
|
||||
// of proof. Never the point at infinity.
|
||||
pub beta_g1: E::G1Affine,
|
||||
pub beta_g2: E::G2Affine,
|
||||
|
||||
// gamma in g2 for verifying. Never the point at infinity.
|
||||
pub gamma_g2: E::G2Affine,
|
||||
|
||||
// delta in g1/g2 for verifying and proving, essentially the magic
|
||||
// trapdoor that forces the prover to evaluate the C element of the
|
||||
// proof with only components from the CRS. Never the point at
|
||||
// infinity.
|
||||
pub delta_g1: E::G1Affine,
|
||||
pub delta_g2: E::G2Affine,
|
||||
|
||||
// Elements of the form (beta * u_i(tau) + alpha v_i(tau) + w_i(tau)) / gamma
|
||||
// for all public inputs. Because all public inputs have a dummy constraint,
|
||||
// this is the same size as the number of inputs, and never contains points
|
||||
// at infinity.
|
||||
pub ic: Vec<E::G1Affine>,
|
||||
}
|
||||
|
||||
impl<E: Engine> PartialEq for VerifyingKey<E> {
|
||||
fn eq(&self, other: &Self) -> bool {
|
||||
self.alpha_g1 == other.alpha_g1
|
||||
&& self.beta_g1 == other.beta_g1
|
||||
&& self.beta_g2 == other.beta_g2
|
||||
&& self.gamma_g2 == other.gamma_g2
|
||||
&& self.delta_g1 == other.delta_g1
|
||||
&& self.delta_g2 == other.delta_g2
|
||||
&& self.ic == other.ic
|
||||
}
|
||||
}
|
||||
|
||||
impl<E: Engine> VerifyingKey<E> {
|
||||
pub fn write<W: Write>(&self, mut writer: W) -> io::Result<()> {
|
||||
writer.write_all(self.alpha_g1.to_uncompressed().as_ref())?;
|
||||
writer.write_all(self.beta_g1.to_uncompressed().as_ref())?;
|
||||
writer.write_all(self.beta_g2.to_uncompressed().as_ref())?;
|
||||
writer.write_all(self.gamma_g2.to_uncompressed().as_ref())?;
|
||||
writer.write_all(self.delta_g1.to_uncompressed().as_ref())?;
|
||||
writer.write_all(self.delta_g2.to_uncompressed().as_ref())?;
|
||||
writer.write_u32::<BigEndian>(self.ic.len() as u32)?;
|
||||
for ic in &self.ic {
|
||||
writer.write_all(ic.to_uncompressed().as_ref())?;
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub fn read<R: Read>(mut reader: R) -> io::Result<Self> {
|
||||
let read_g1 = |reader: &mut R| -> io::Result<E::G1Affine> {
|
||||
let mut g1_repr = <E::G1Affine as UncompressedEncoding>::Uncompressed::default();
|
||||
reader.read_exact(g1_repr.as_mut())?;
|
||||
|
||||
let affine = E::G1Affine::from_uncompressed(&g1_repr);
|
||||
if affine.is_some().into() {
|
||||
Ok(affine.unwrap())
|
||||
} else {
|
||||
Err(io::Error::new(io::ErrorKind::InvalidData, "invalid G1"))
|
||||
}
|
||||
};
|
||||
|
||||
let read_g2 = |reader: &mut R| -> io::Result<E::G2Affine> {
|
||||
let mut g2_repr = <E::G2Affine as UncompressedEncoding>::Uncompressed::default();
|
||||
reader.read_exact(g2_repr.as_mut())?;
|
||||
|
||||
let affine = E::G2Affine::from_uncompressed(&g2_repr);
|
||||
if affine.is_some().into() {
|
||||
Ok(affine.unwrap())
|
||||
} else {
|
||||
Err(io::Error::new(io::ErrorKind::InvalidData, "invalid G2"))
|
||||
}
|
||||
};
|
||||
|
||||
let alpha_g1 = read_g1(&mut reader)?;
|
||||
let beta_g1 = read_g1(&mut reader)?;
|
||||
let beta_g2 = read_g2(&mut reader)?;
|
||||
let gamma_g2 = read_g2(&mut reader)?;
|
||||
let delta_g1 = read_g1(&mut reader)?;
|
||||
let delta_g2 = read_g2(&mut reader)?;
|
||||
|
||||
let ic_len = reader.read_u32::<BigEndian>()? as usize;
|
||||
|
||||
let mut ic = vec![];
|
||||
|
||||
for _ in 0..ic_len {
|
||||
let g1 = read_g1(&mut reader).and_then(|e| {
|
||||
if e.is_identity().into() {
|
||||
Err(io::Error::new(
|
||||
io::ErrorKind::InvalidData,
|
||||
"point at infinity",
|
||||
))
|
||||
} else {
|
||||
Ok(e)
|
||||
}
|
||||
})?;
|
||||
|
||||
ic.push(g1);
|
||||
}
|
||||
|
||||
Ok(VerifyingKey {
|
||||
alpha_g1,
|
||||
beta_g1,
|
||||
beta_g2,
|
||||
gamma_g2,
|
||||
delta_g1,
|
||||
delta_g2,
|
||||
ic,
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Clone)]
|
||||
pub struct Parameters<E: Engine> {
|
||||
pub vk: VerifyingKey<E>,
|
||||
|
||||
// Elements of the form ((tau^i * t(tau)) / delta) for i between 0 and
|
||||
// m-2 inclusive. Never contains points at infinity.
|
||||
pub h: Arc<Vec<E::G1Affine>>,
|
||||
|
||||
// Elements of the form (beta * u_i(tau) + alpha v_i(tau) + w_i(tau)) / delta
|
||||
// for all auxiliary inputs. Variables can never be unconstrained, so this
|
||||
// never contains points at infinity.
|
||||
pub l: Arc<Vec<E::G1Affine>>,
|
||||
|
||||
// QAP "A" polynomials evaluated at tau in the Lagrange basis. Never contains
|
||||
// points at infinity: polynomials that evaluate to zero are omitted from
|
||||
// the CRS and the prover can deterministically skip their evaluation.
|
||||
pub a: Arc<Vec<E::G1Affine>>,
|
||||
|
||||
// QAP "B" polynomials evaluated at tau in the Lagrange basis. Needed in
|
||||
// G1 and G2 for C/B queries, respectively. Never contains points at
|
||||
// infinity for the same reason as the "A" polynomials.
|
||||
pub b_g1: Arc<Vec<E::G1Affine>>,
|
||||
pub b_g2: Arc<Vec<E::G2Affine>>,
|
||||
}
|
||||
|
||||
impl<E: Engine> PartialEq for Parameters<E> {
|
||||
fn eq(&self, other: &Self) -> bool {
|
||||
self.vk == other.vk
|
||||
&& self.h == other.h
|
||||
&& self.l == other.l
|
||||
&& self.a == other.a
|
||||
&& self.b_g1 == other.b_g1
|
||||
&& self.b_g2 == other.b_g2
|
||||
}
|
||||
}
|
||||
|
||||
impl<E: Engine> Parameters<E> {
|
||||
pub fn write<W: Write>(&self, mut writer: W) -> io::Result<()> {
|
||||
self.vk.write(&mut writer)?;
|
||||
|
||||
writer.write_u32::<BigEndian>(self.h.len() as u32)?;
|
||||
for g in &self.h[..] {
|
||||
writer.write_all(g.to_uncompressed().as_ref())?;
|
||||
}
|
||||
|
||||
writer.write_u32::<BigEndian>(self.l.len() as u32)?;
|
||||
for g in &self.l[..] {
|
||||
writer.write_all(g.to_uncompressed().as_ref())?;
|
||||
}
|
||||
|
||||
writer.write_u32::<BigEndian>(self.a.len() as u32)?;
|
||||
for g in &self.a[..] {
|
||||
writer.write_all(g.to_uncompressed().as_ref())?;
|
||||
}
|
||||
|
||||
writer.write_u32::<BigEndian>(self.b_g1.len() as u32)?;
|
||||
for g in &self.b_g1[..] {
|
||||
writer.write_all(g.to_uncompressed().as_ref())?;
|
||||
}
|
||||
|
||||
writer.write_u32::<BigEndian>(self.b_g2.len() as u32)?;
|
||||
for g in &self.b_g2[..] {
|
||||
writer.write_all(g.to_uncompressed().as_ref())?;
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub fn read<R: Read>(mut reader: R, checked: bool) -> io::Result<Self> {
|
||||
let read_g1 = |reader: &mut R| -> io::Result<E::G1Affine> {
|
||||
let mut repr = <E::G1Affine as UncompressedEncoding>::Uncompressed::default();
|
||||
reader.read_exact(repr.as_mut())?;
|
||||
|
||||
let affine = if checked {
|
||||
E::G1Affine::from_uncompressed(&repr)
|
||||
} else {
|
||||
E::G1Affine::from_uncompressed_unchecked(&repr)
|
||||
};
|
||||
|
||||
let affine = if affine.is_some().into() {
|
||||
Ok(affine.unwrap())
|
||||
} else {
|
||||
Err(io::Error::new(io::ErrorKind::InvalidData, "invalid G1"))
|
||||
};
|
||||
|
||||
affine.and_then(|e| {
|
||||
if e.is_identity().into() {
|
||||
Err(io::Error::new(
|
||||
io::ErrorKind::InvalidData,
|
||||
"point at infinity",
|
||||
))
|
||||
} else {
|
||||
Ok(e)
|
||||
}
|
||||
})
|
||||
};
|
||||
|
||||
let read_g2 = |reader: &mut R| -> io::Result<E::G2Affine> {
|
||||
let mut repr = <E::G2Affine as UncompressedEncoding>::Uncompressed::default();
|
||||
reader.read_exact(repr.as_mut())?;
|
||||
|
||||
let affine = if checked {
|
||||
E::G2Affine::from_uncompressed(&repr)
|
||||
} else {
|
||||
E::G2Affine::from_uncompressed_unchecked(&repr)
|
||||
};
|
||||
|
||||
let affine = if affine.is_some().into() {
|
||||
Ok(affine.unwrap())
|
||||
} else {
|
||||
Err(io::Error::new(io::ErrorKind::InvalidData, "invalid G2"))
|
||||
};
|
||||
|
||||
affine.and_then(|e| {
|
||||
if e.is_identity().into() {
|
||||
Err(io::Error::new(
|
||||
io::ErrorKind::InvalidData,
|
||||
"point at infinity",
|
||||
))
|
||||
} else {
|
||||
Ok(e)
|
||||
}
|
||||
})
|
||||
};
|
||||
|
||||
let vk = VerifyingKey::<E>::read(&mut reader)?;
|
||||
|
||||
let mut h = vec![];
|
||||
let mut l = vec![];
|
||||
let mut a = vec![];
|
||||
let mut b_g1 = vec![];
|
||||
let mut b_g2 = vec![];
|
||||
|
||||
{
|
||||
let len = reader.read_u32::<BigEndian>()? as usize;
|
||||
for _ in 0..len {
|
||||
h.push(read_g1(&mut reader)?);
|
||||
}
|
||||
}
|
||||
|
||||
{
|
||||
let len = reader.read_u32::<BigEndian>()? as usize;
|
||||
for _ in 0..len {
|
||||
l.push(read_g1(&mut reader)?);
|
||||
}
|
||||
}
|
||||
|
||||
{
|
||||
let len = reader.read_u32::<BigEndian>()? as usize;
|
||||
for _ in 0..len {
|
||||
a.push(read_g1(&mut reader)?);
|
||||
}
|
||||
}
|
||||
|
||||
{
|
||||
let len = reader.read_u32::<BigEndian>()? as usize;
|
||||
for _ in 0..len {
|
||||
b_g1.push(read_g1(&mut reader)?);
|
||||
}
|
||||
}
|
||||
|
||||
{
|
||||
let len = reader.read_u32::<BigEndian>()? as usize;
|
||||
for _ in 0..len {
|
||||
b_g2.push(read_g2(&mut reader)?);
|
||||
}
|
||||
}
|
||||
|
||||
Ok(Parameters {
|
||||
vk,
|
||||
h: Arc::new(h),
|
||||
l: Arc::new(l),
|
||||
a: Arc::new(a),
|
||||
b_g1: Arc::new(b_g1),
|
||||
b_g2: Arc::new(b_g2),
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
pub struct PreparedVerifyingKey<E: MultiMillerLoop> {
|
||||
/// Pairing result of alpha*beta
|
||||
alpha_g1_beta_g2: E::Gt,
|
||||
/// -gamma in G2
|
||||
neg_gamma_g2: E::G2Prepared,
|
||||
/// -delta in G2
|
||||
neg_delta_g2: E::G2Prepared,
|
||||
/// Copy of IC from `VerifiyingKey`.
|
||||
ic: Vec<E::G1Affine>,
|
||||
}
|
||||
|
||||
pub trait ParameterSource<E: Engine> {
|
||||
type G1Builder: SourceBuilder<E::G1Affine>;
|
||||
type G2Builder: SourceBuilder<E::G2Affine>;
|
||||
|
||||
fn get_vk(&mut self, num_ic: usize) -> Result<VerifyingKey<E>, SynthesisError>;
|
||||
fn get_h(&mut self, num_h: usize) -> Result<Self::G1Builder, SynthesisError>;
|
||||
fn get_l(&mut self, num_l: usize) -> Result<Self::G1Builder, SynthesisError>;
|
||||
fn get_a(
|
||||
&mut self,
|
||||
num_inputs: usize,
|
||||
num_aux: usize,
|
||||
) -> Result<(Self::G1Builder, Self::G1Builder), SynthesisError>;
|
||||
fn get_b_g1(
|
||||
&mut self,
|
||||
num_inputs: usize,
|
||||
num_aux: usize,
|
||||
) -> Result<(Self::G1Builder, Self::G1Builder), SynthesisError>;
|
||||
fn get_b_g2(
|
||||
&mut self,
|
||||
num_inputs: usize,
|
||||
num_aux: usize,
|
||||
) -> Result<(Self::G2Builder, Self::G2Builder), SynthesisError>;
|
||||
}
|
||||
|
||||
impl<'a, E: Engine> ParameterSource<E> for &'a Parameters<E> {
|
||||
type G1Builder = (Arc<Vec<E::G1Affine>>, usize);
|
||||
type G2Builder = (Arc<Vec<E::G2Affine>>, usize);
|
||||
|
||||
fn get_vk(&mut self, _: usize) -> Result<VerifyingKey<E>, SynthesisError> {
|
||||
Ok(self.vk.clone())
|
||||
}
|
||||
|
||||
fn get_h(&mut self, _: usize) -> Result<Self::G1Builder, SynthesisError> {
|
||||
Ok((self.h.clone(), 0))
|
||||
}
|
||||
|
||||
fn get_l(&mut self, _: usize) -> Result<Self::G1Builder, SynthesisError> {
|
||||
Ok((self.l.clone(), 0))
|
||||
}
|
||||
|
||||
fn get_a(
|
||||
&mut self,
|
||||
num_inputs: usize,
|
||||
_: usize,
|
||||
) -> Result<(Self::G1Builder, Self::G1Builder), SynthesisError> {
|
||||
Ok(((self.a.clone(), 0), (self.a.clone(), num_inputs)))
|
||||
}
|
||||
|
||||
fn get_b_g1(
|
||||
&mut self,
|
||||
num_inputs: usize,
|
||||
_: usize,
|
||||
) -> Result<(Self::G1Builder, Self::G1Builder), SynthesisError> {
|
||||
Ok(((self.b_g1.clone(), 0), (self.b_g1.clone(), num_inputs)))
|
||||
}
|
||||
|
||||
fn get_b_g2(
|
||||
&mut self,
|
||||
num_inputs: usize,
|
||||
_: usize,
|
||||
) -> Result<(Self::G2Builder, Self::G2Builder), SynthesisError> {
|
||||
Ok(((self.b_g2.clone(), 0), (self.b_g2.clone(), num_inputs)))
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod test_with_bls12_381 {
|
||||
use super::*;
|
||||
use crate::{Circuit, ConstraintSystem, SynthesisError};
|
||||
|
||||
use bls12_381::{Bls12, Scalar};
|
||||
use ff::{Field, PrimeField};
|
||||
use rand::thread_rng;
|
||||
use std::ops::MulAssign;
|
||||
|
||||
#[test]
|
||||
fn serialization() {
|
||||
struct MySillyCircuit<Scalar: PrimeField> {
|
||||
a: Option<Scalar>,
|
||||
b: Option<Scalar>,
|
||||
}
|
||||
|
||||
impl<Scalar: PrimeField> Circuit<Scalar> for MySillyCircuit<Scalar> {
|
||||
fn synthesize<CS: ConstraintSystem<Scalar>>(
|
||||
self,
|
||||
cs: &mut CS,
|
||||
) -> Result<(), SynthesisError> {
|
||||
let a = cs.alloc(|| "a", || self.a.ok_or(SynthesisError::AssignmentMissing))?;
|
||||
let b = cs.alloc(|| "b", || self.b.ok_or(SynthesisError::AssignmentMissing))?;
|
||||
let c = cs.alloc_input(
|
||||
|| "c",
|
||||
|| {
|
||||
let mut a = self.a.ok_or(SynthesisError::AssignmentMissing)?;
|
||||
let b = self.b.ok_or(SynthesisError::AssignmentMissing)?;
|
||||
|
||||
a.mul_assign(&b);
|
||||
Ok(a)
|
||||
},
|
||||
)?;
|
||||
|
||||
cs.enforce(|| "a*b=c", |lc| lc + a, |lc| lc + b, |lc| lc + c);
|
||||
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
let rng = &mut thread_rng();
|
||||
|
||||
let params =
|
||||
generate_random_parameters::<Bls12, _, _>(MySillyCircuit { a: None, b: None }, rng)
|
||||
.unwrap();
|
||||
|
||||
{
|
||||
let mut v = vec![];
|
||||
|
||||
params.write(&mut v).unwrap();
|
||||
assert_eq!(v.len(), 2136);
|
||||
|
||||
let de_params = Parameters::read(&v[..], true).unwrap();
|
||||
assert!(params == de_params);
|
||||
|
||||
let de_params = Parameters::read(&v[..], false).unwrap();
|
||||
assert!(params == de_params);
|
||||
}
|
||||
|
||||
let pvk = prepare_verifying_key::<Bls12>(¶ms.vk);
|
||||
|
||||
for _ in 0..100 {
|
||||
let a = Scalar::random(rng);
|
||||
let b = Scalar::random(rng);
|
||||
let mut c = a;
|
||||
c.mul_assign(&b);
|
||||
|
||||
let proof = create_random_proof(
|
||||
MySillyCircuit {
|
||||
a: Some(a),
|
||||
b: Some(b),
|
||||
},
|
||||
¶ms,
|
||||
rng,
|
||||
)
|
||||
.unwrap();
|
||||
|
||||
let mut v = vec![];
|
||||
proof.write(&mut v).unwrap();
|
||||
|
||||
assert_eq!(v.len(), 192);
|
||||
|
||||
let de_proof = Proof::read(&v[..]).unwrap();
|
||||
assert!(proof == de_proof);
|
||||
|
||||
assert!(verify_proof(&pvk, &proof, &[c]).is_ok());
|
||||
assert!(verify_proof(&pvk, &proof, &[a]).is_err());
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1,339 +0,0 @@
|
|||
use rand_core::RngCore;
|
||||
use std::ops::{AddAssign, MulAssign};
|
||||
use std::sync::Arc;
|
||||
|
||||
use futures::Future;
|
||||
|
||||
use ff::{Field, PrimeField};
|
||||
use group::{prime::PrimeCurveAffine, Curve};
|
||||
use pairing::Engine;
|
||||
|
||||
use super::{ParameterSource, Proof};
|
||||
|
||||
use crate::{Circuit, ConstraintSystem, Index, LinearCombination, SynthesisError, Variable};
|
||||
|
||||
use crate::domain::{EvaluationDomain, Scalar};
|
||||
|
||||
use crate::multiexp::{multiexp, DensityTracker, FullDensity};
|
||||
|
||||
use crate::multicore::Worker;
|
||||
|
||||
fn eval<S: PrimeField>(
|
||||
lc: &LinearCombination<S>,
|
||||
mut input_density: Option<&mut DensityTracker>,
|
||||
mut aux_density: Option<&mut DensityTracker>,
|
||||
input_assignment: &[S],
|
||||
aux_assignment: &[S],
|
||||
) -> S {
|
||||
let mut acc = S::zero();
|
||||
|
||||
for &(index, coeff) in lc.0.iter() {
|
||||
let mut tmp;
|
||||
|
||||
match index {
|
||||
Variable(Index::Input(i)) => {
|
||||
tmp = input_assignment[i];
|
||||
if let Some(ref mut v) = input_density {
|
||||
v.inc(i);
|
||||
}
|
||||
}
|
||||
Variable(Index::Aux(i)) => {
|
||||
tmp = aux_assignment[i];
|
||||
if let Some(ref mut v) = aux_density {
|
||||
v.inc(i);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if coeff == S::one() {
|
||||
acc.add_assign(&tmp);
|
||||
} else {
|
||||
tmp.mul_assign(&coeff);
|
||||
acc.add_assign(&tmp);
|
||||
}
|
||||
}
|
||||
|
||||
acc
|
||||
}
|
||||
|
||||
struct ProvingAssignment<S: PrimeField> {
|
||||
// Density of queries
|
||||
a_aux_density: DensityTracker,
|
||||
b_input_density: DensityTracker,
|
||||
b_aux_density: DensityTracker,
|
||||
|
||||
// Evaluations of A, B, C polynomials
|
||||
a: Vec<Scalar<S>>,
|
||||
b: Vec<Scalar<S>>,
|
||||
c: Vec<Scalar<S>>,
|
||||
|
||||
// Assignments of variables
|
||||
input_assignment: Vec<S>,
|
||||
aux_assignment: Vec<S>,
|
||||
}
|
||||
|
||||
impl<S: PrimeField> ConstraintSystem<S> for ProvingAssignment<S> {
|
||||
type Root = Self;
|
||||
|
||||
fn alloc<F, A, AR>(&mut self, _: A, f: F) -> Result<Variable, SynthesisError>
|
||||
where
|
||||
F: FnOnce() -> Result<S, SynthesisError>,
|
||||
A: FnOnce() -> AR,
|
||||
AR: Into<String>,
|
||||
{
|
||||
self.aux_assignment.push(f()?);
|
||||
self.a_aux_density.add_element();
|
||||
self.b_aux_density.add_element();
|
||||
|
||||
Ok(Variable(Index::Aux(self.aux_assignment.len() - 1)))
|
||||
}
|
||||
|
||||
fn alloc_input<F, A, AR>(&mut self, _: A, f: F) -> Result<Variable, SynthesisError>
|
||||
where
|
||||
F: FnOnce() -> Result<S, SynthesisError>,
|
||||
A: FnOnce() -> AR,
|
||||
AR: Into<String>,
|
||||
{
|
||||
self.input_assignment.push(f()?);
|
||||
self.b_input_density.add_element();
|
||||
|
||||
Ok(Variable(Index::Input(self.input_assignment.len() - 1)))
|
||||
}
|
||||
|
||||
fn enforce<A, AR, LA, LB, LC>(&mut self, _: A, a: LA, b: LB, c: LC)
|
||||
where
|
||||
A: FnOnce() -> AR,
|
||||
AR: Into<String>,
|
||||
LA: FnOnce(LinearCombination<S>) -> LinearCombination<S>,
|
||||
LB: FnOnce(LinearCombination<S>) -> LinearCombination<S>,
|
||||
LC: FnOnce(LinearCombination<S>) -> LinearCombination<S>,
|
||||
{
|
||||
let a = a(LinearCombination::zero());
|
||||
let b = b(LinearCombination::zero());
|
||||
let c = c(LinearCombination::zero());
|
||||
|
||||
self.a.push(Scalar(eval(
|
||||
&a,
|
||||
// Inputs have full density in the A query
|
||||
// because there are constraints of the
|
||||
// form x * 0 = 0 for each input.
|
||||
None,
|
||||
Some(&mut self.a_aux_density),
|
||||
&self.input_assignment,
|
||||
&self.aux_assignment,
|
||||
)));
|
||||
self.b.push(Scalar(eval(
|
||||
&b,
|
||||
Some(&mut self.b_input_density),
|
||||
Some(&mut self.b_aux_density),
|
||||
&self.input_assignment,
|
||||
&self.aux_assignment,
|
||||
)));
|
||||
self.c.push(Scalar(eval(
|
||||
&c,
|
||||
// There is no C polynomial query,
|
||||
// though there is an (beta)A + (alpha)B + C
|
||||
// query for all aux variables.
|
||||
// However, that query has full density.
|
||||
None,
|
||||
None,
|
||||
&self.input_assignment,
|
||||
&self.aux_assignment,
|
||||
)));
|
||||
}
|
||||
|
||||
fn push_namespace<NR, N>(&mut self, _: N)
|
||||
where
|
||||
NR: Into<String>,
|
||||
N: FnOnce() -> NR,
|
||||
{
|
||||
// Do nothing; we don't care about namespaces in this context.
|
||||
}
|
||||
|
||||
fn pop_namespace(&mut self) {
|
||||
// Do nothing; we don't care about namespaces in this context.
|
||||
}
|
||||
|
||||
fn get_root(&mut self) -> &mut Self::Root {
|
||||
self
|
||||
}
|
||||
}
|
||||
|
||||
pub fn create_random_proof<E, C, R, P: ParameterSource<E>>(
|
||||
circuit: C,
|
||||
params: P,
|
||||
rng: &mut R,
|
||||
) -> Result<Proof<E>, SynthesisError>
|
||||
where
|
||||
E: Engine,
|
||||
C: Circuit<E::Fr>,
|
||||
R: RngCore,
|
||||
{
|
||||
let r = E::Fr::random(rng);
|
||||
let s = E::Fr::random(rng);
|
||||
|
||||
create_proof::<E, C, P>(circuit, params, r, s)
|
||||
}
|
||||
|
||||
pub fn create_proof<E, C, P: ParameterSource<E>>(
|
||||
circuit: C,
|
||||
mut params: P,
|
||||
r: E::Fr,
|
||||
s: E::Fr,
|
||||
) -> Result<Proof<E>, SynthesisError>
|
||||
where
|
||||
E: Engine,
|
||||
C: Circuit<E::Fr>,
|
||||
{
|
||||
let mut prover = ProvingAssignment {
|
||||
a_aux_density: DensityTracker::new(),
|
||||
b_input_density: DensityTracker::new(),
|
||||
b_aux_density: DensityTracker::new(),
|
||||
a: vec![],
|
||||
b: vec![],
|
||||
c: vec![],
|
||||
input_assignment: vec![],
|
||||
aux_assignment: vec![],
|
||||
};
|
||||
|
||||
prover.alloc_input(|| "", || Ok(E::Fr::one()))?;
|
||||
|
||||
circuit.synthesize(&mut prover)?;
|
||||
|
||||
for i in 0..prover.input_assignment.len() {
|
||||
prover.enforce(|| "", |lc| lc + Variable(Index::Input(i)), |lc| lc, |lc| lc);
|
||||
}
|
||||
|
||||
let worker = Worker::new();
|
||||
|
||||
let vk = params.get_vk(prover.input_assignment.len())?;
|
||||
|
||||
let h = {
|
||||
let mut a = EvaluationDomain::from_coeffs(prover.a)?;
|
||||
let mut b = EvaluationDomain::from_coeffs(prover.b)?;
|
||||
let mut c = EvaluationDomain::from_coeffs(prover.c)?;
|
||||
a.ifft(&worker);
|
||||
a.coset_fft(&worker);
|
||||
b.ifft(&worker);
|
||||
b.coset_fft(&worker);
|
||||
c.ifft(&worker);
|
||||
c.coset_fft(&worker);
|
||||
|
||||
a.mul_assign(&worker, &b);
|
||||
drop(b);
|
||||
a.sub_assign(&worker, &c);
|
||||
drop(c);
|
||||
a.divide_by_z_on_coset(&worker);
|
||||
a.icoset_fft(&worker);
|
||||
let mut a = a.into_coeffs();
|
||||
let a_len = a.len() - 1;
|
||||
a.truncate(a_len);
|
||||
// TODO: parallelize if it's even helpful
|
||||
let a = Arc::new(a.into_iter().map(|s| s.0).collect::<Vec<_>>());
|
||||
|
||||
multiexp(&worker, params.get_h(a.len())?, FullDensity, a)
|
||||
};
|
||||
|
||||
// TODO: parallelize if it's even helpful
|
||||
let input_assignment = Arc::new(prover.input_assignment);
|
||||
let aux_assignment = Arc::new(prover.aux_assignment);
|
||||
|
||||
let l = multiexp(
|
||||
&worker,
|
||||
params.get_l(aux_assignment.len())?,
|
||||
FullDensity,
|
||||
aux_assignment.clone(),
|
||||
);
|
||||
|
||||
let a_aux_density_total = prover.a_aux_density.get_total_density();
|
||||
|
||||
let (a_inputs_source, a_aux_source) =
|
||||
params.get_a(input_assignment.len(), a_aux_density_total)?;
|
||||
|
||||
let a_inputs = multiexp(
|
||||
&worker,
|
||||
a_inputs_source,
|
||||
FullDensity,
|
||||
input_assignment.clone(),
|
||||
);
|
||||
let a_aux = multiexp(
|
||||
&worker,
|
||||
a_aux_source,
|
||||
Arc::new(prover.a_aux_density),
|
||||
aux_assignment.clone(),
|
||||
);
|
||||
|
||||
let b_input_density = Arc::new(prover.b_input_density);
|
||||
let b_input_density_total = b_input_density.get_total_density();
|
||||
let b_aux_density = Arc::new(prover.b_aux_density);
|
||||
let b_aux_density_total = b_aux_density.get_total_density();
|
||||
|
||||
let (b_g1_inputs_source, b_g1_aux_source) =
|
||||
params.get_b_g1(b_input_density_total, b_aux_density_total)?;
|
||||
|
||||
let b_g1_inputs = multiexp(
|
||||
&worker,
|
||||
b_g1_inputs_source,
|
||||
b_input_density.clone(),
|
||||
input_assignment.clone(),
|
||||
);
|
||||
let b_g1_aux = multiexp(
|
||||
&worker,
|
||||
b_g1_aux_source,
|
||||
b_aux_density.clone(),
|
||||
aux_assignment.clone(),
|
||||
);
|
||||
|
||||
let (b_g2_inputs_source, b_g2_aux_source) =
|
||||
params.get_b_g2(b_input_density_total, b_aux_density_total)?;
|
||||
|
||||
let b_g2_inputs = multiexp(
|
||||
&worker,
|
||||
b_g2_inputs_source,
|
||||
b_input_density,
|
||||
input_assignment,
|
||||
);
|
||||
let b_g2_aux = multiexp(&worker, b_g2_aux_source, b_aux_density, aux_assignment);
|
||||
|
||||
if bool::from(vk.delta_g1.is_identity() | vk.delta_g2.is_identity()) {
|
||||
// If this element is zero, someone is trying to perform a
|
||||
// subversion-CRS attack.
|
||||
return Err(SynthesisError::UnexpectedIdentity);
|
||||
}
|
||||
|
||||
let mut g_a = vk.delta_g1 * &r;
|
||||
AddAssign::<&E::G1Affine>::add_assign(&mut g_a, &vk.alpha_g1);
|
||||
let mut g_b = vk.delta_g2 * &s;
|
||||
AddAssign::<&E::G2Affine>::add_assign(&mut g_b, &vk.beta_g2);
|
||||
let mut g_c;
|
||||
{
|
||||
let mut rs = r;
|
||||
rs.mul_assign(&s);
|
||||
|
||||
g_c = vk.delta_g1 * &rs;
|
||||
AddAssign::<&E::G1>::add_assign(&mut g_c, &(vk.alpha_g1 * &s));
|
||||
AddAssign::<&E::G1>::add_assign(&mut g_c, &(vk.beta_g1 * &r));
|
||||
}
|
||||
let mut a_answer = a_inputs.wait()?;
|
||||
AddAssign::<&E::G1>::add_assign(&mut a_answer, &a_aux.wait()?);
|
||||
AddAssign::<&E::G1>::add_assign(&mut g_a, &a_answer);
|
||||
MulAssign::<E::Fr>::mul_assign(&mut a_answer, s);
|
||||
AddAssign::<&E::G1>::add_assign(&mut g_c, &a_answer);
|
||||
|
||||
let mut b1_answer: E::G1 = b_g1_inputs.wait()?;
|
||||
AddAssign::<&E::G1>::add_assign(&mut b1_answer, &b_g1_aux.wait()?);
|
||||
let mut b2_answer = b_g2_inputs.wait()?;
|
||||
AddAssign::<&E::G2>::add_assign(&mut b2_answer, &b_g2_aux.wait()?);
|
||||
|
||||
AddAssign::<&E::G2>::add_assign(&mut g_b, &b2_answer);
|
||||
MulAssign::<E::Fr>::mul_assign(&mut b1_answer, r);
|
||||
AddAssign::<&E::G1>::add_assign(&mut g_c, &b1_answer);
|
||||
AddAssign::<&E::G1>::add_assign(&mut g_c, &h.wait()?);
|
||||
AddAssign::<&E::G1>::add_assign(&mut g_c, &l.wait()?);
|
||||
|
||||
Ok(Proof {
|
||||
a: g_a.to_affine(),
|
||||
b: g_b.to_affine(),
|
||||
c: g_c.to_affine(),
|
||||
})
|
||||
}
|
|
@ -1,491 +0,0 @@
|
|||
use ff::{Field, PrimeField};
|
||||
use group::{
|
||||
prime::{PrimeCurve, PrimeCurveAffine, PrimeGroup},
|
||||
Curve, Group, GroupEncoding, UncompressedEncoding, WnafGroup,
|
||||
};
|
||||
use pairing::{Engine, MillerLoopResult, MultiMillerLoop, PairingCurveAffine};
|
||||
|
||||
use rand_core::RngCore;
|
||||
use std::fmt;
|
||||
use std::iter::Sum;
|
||||
use std::num::Wrapping;
|
||||
use std::ops::{Add, AddAssign, BitAnd, Mul, MulAssign, Neg, Shr, Sub, SubAssign};
|
||||
use subtle::{Choice, ConditionallySelectable, ConstantTimeEq, CtOption};
|
||||
|
||||
const MODULUS_R: Wrapping<u32> = Wrapping(64513);
|
||||
|
||||
#[derive(Copy, Clone, Debug, PartialEq, Eq)]
|
||||
pub struct Fr(Wrapping<u32>);
|
||||
|
||||
impl Default for Fr {
|
||||
fn default() -> Self {
|
||||
<Fr as Field>::zero()
|
||||
}
|
||||
}
|
||||
|
||||
impl ConstantTimeEq for Fr {
|
||||
fn ct_eq(&self, other: &Fr) -> Choice {
|
||||
(self.0).0.ct_eq(&(other.0).0)
|
||||
}
|
||||
}
|
||||
|
||||
impl fmt::Display for Fr {
|
||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> Result<(), fmt::Error> {
|
||||
write!(f, "{}", (self.0).0)
|
||||
}
|
||||
}
|
||||
|
||||
impl From<u64> for Fr {
|
||||
fn from(v: u64) -> Fr {
|
||||
Fr(Wrapping((v % MODULUS_R.0 as u64) as u32))
|
||||
}
|
||||
}
|
||||
|
||||
impl ConditionallySelectable for Fr {
|
||||
fn conditional_select(a: &Self, b: &Self, choice: Choice) -> Self {
|
||||
Fr(Wrapping(u32::conditional_select(
|
||||
&(a.0).0,
|
||||
&(b.0).0,
|
||||
choice,
|
||||
)))
|
||||
}
|
||||
}
|
||||
|
||||
impl Sum for Fr {
|
||||
fn sum<I: Iterator<Item = Self>>(iter: I) -> Self {
|
||||
iter.fold(Self::zero(), ::std::ops::Add::add)
|
||||
}
|
||||
}
|
||||
|
||||
impl<'r> Sum<&'r Fr> for Fr {
|
||||
fn sum<I: Iterator<Item = &'r Fr>>(iter: I) -> Self {
|
||||
iter.fold(Self::zero(), ::std::ops::Add::add)
|
||||
}
|
||||
}
|
||||
|
||||
impl Neg for Fr {
|
||||
type Output = Self;
|
||||
|
||||
fn neg(mut self) -> Self {
|
||||
if !<Fr as Field>::is_zero(&self) {
|
||||
self.0 = MODULUS_R - self.0;
|
||||
}
|
||||
self
|
||||
}
|
||||
}
|
||||
|
||||
impl<'r> Add<&'r Fr> for Fr {
|
||||
type Output = Self;
|
||||
|
||||
fn add(self, other: &Self) -> Self {
|
||||
let mut ret = self;
|
||||
AddAssign::add_assign(&mut ret, other);
|
||||
ret
|
||||
}
|
||||
}
|
||||
|
||||
impl Add for Fr {
|
||||
type Output = Self;
|
||||
|
||||
fn add(self, other: Self) -> Self {
|
||||
self + &other
|
||||
}
|
||||
}
|
||||
|
||||
impl<'r> AddAssign<&'r Fr> for Fr {
|
||||
fn add_assign(&mut self, other: &Self) {
|
||||
self.0 = (self.0 + other.0) % MODULUS_R;
|
||||
}
|
||||
}
|
||||
|
||||
impl AddAssign for Fr {
|
||||
fn add_assign(&mut self, other: Self) {
|
||||
AddAssign::add_assign(self, &other);
|
||||
}
|
||||
}
|
||||
|
||||
impl<'r> Sub<&'r Fr> for Fr {
|
||||
type Output = Self;
|
||||
|
||||
fn sub(self, other: &Self) -> Self {
|
||||
let mut ret = self;
|
||||
SubAssign::sub_assign(&mut ret, other);
|
||||
ret
|
||||
}
|
||||
}
|
||||
|
||||
impl Sub for Fr {
|
||||
type Output = Self;
|
||||
|
||||
fn sub(self, other: Self) -> Self {
|
||||
self - &other
|
||||
}
|
||||
}
|
||||
|
||||
impl<'r> SubAssign<&'r Fr> for Fr {
|
||||
fn sub_assign(&mut self, other: &Self) {
|
||||
self.0 = ((MODULUS_R + self.0) - other.0) % MODULUS_R;
|
||||
}
|
||||
}
|
||||
|
||||
impl SubAssign for Fr {
|
||||
fn sub_assign(&mut self, other: Self) {
|
||||
SubAssign::sub_assign(self, &other);
|
||||
}
|
||||
}
|
||||
|
||||
impl<'r> Mul<&'r Fr> for Fr {
|
||||
type Output = Self;
|
||||
|
||||
fn mul(self, other: &Self) -> Self {
|
||||
let mut ret = self;
|
||||
MulAssign::mul_assign(&mut ret, other);
|
||||
ret
|
||||
}
|
||||
}
|
||||
|
||||
impl Mul for Fr {
|
||||
type Output = Self;
|
||||
|
||||
fn mul(self, other: Self) -> Self {
|
||||
self * &other
|
||||
}
|
||||
}
|
||||
|
||||
impl<'r> MulAssign<&'r Fr> for Fr {
|
||||
fn mul_assign(&mut self, other: &Self) {
|
||||
self.0 = (self.0 * other.0) % MODULUS_R;
|
||||
}
|
||||
}
|
||||
|
||||
impl MulAssign for Fr {
|
||||
fn mul_assign(&mut self, other: Self) {
|
||||
MulAssign::mul_assign(self, &other);
|
||||
}
|
||||
}
|
||||
|
||||
impl BitAnd<u64> for Fr {
|
||||
type Output = u64;
|
||||
|
||||
fn bitand(self, rhs: u64) -> u64 {
|
||||
(self.0).0 as u64 & rhs
|
||||
}
|
||||
}
|
||||
|
||||
impl Shr<u32> for Fr {
|
||||
type Output = Fr;
|
||||
|
||||
fn shr(mut self, rhs: u32) -> Fr {
|
||||
self.0 = Wrapping((self.0).0 >> rhs);
|
||||
self
|
||||
}
|
||||
}
|
||||
|
||||
impl Field for Fr {
|
||||
fn random<R: RngCore + ?Sized>(rng: &mut R) -> Self {
|
||||
Fr(Wrapping(rng.next_u32()) % MODULUS_R)
|
||||
}
|
||||
|
||||
fn zero() -> Self {
|
||||
Fr(Wrapping(0))
|
||||
}
|
||||
|
||||
fn one() -> Self {
|
||||
Fr(Wrapping(1))
|
||||
}
|
||||
|
||||
fn is_zero(&self) -> bool {
|
||||
(self.0).0 == 0
|
||||
}
|
||||
|
||||
fn square(&self) -> Self {
|
||||
Fr((self.0 * self.0) % MODULUS_R)
|
||||
}
|
||||
|
||||
fn double(&self) -> Self {
|
||||
Fr((self.0 << 1) % MODULUS_R)
|
||||
}
|
||||
|
||||
fn invert(&self) -> CtOption<Self> {
|
||||
if <Fr as Field>::is_zero(self) {
|
||||
CtOption::new(<Fr as Field>::zero(), Choice::from(0))
|
||||
} else {
|
||||
CtOption::new(
|
||||
self.pow_vartime(&[(MODULUS_R.0 as u64) - 2]),
|
||||
Choice::from(1),
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
fn sqrt(&self) -> CtOption<Self> {
|
||||
// Tonelli-Shank's algorithm for q mod 16 = 1
|
||||
// https://eprint.iacr.org/2012/685.pdf (page 12, algorithm 5)
|
||||
let mut c = Fr::root_of_unity();
|
||||
// r = self^((t + 1) // 2)
|
||||
let mut r = self.pow_vartime([32u64]);
|
||||
// t = self^t
|
||||
let mut t = self.pow_vartime([63u64]);
|
||||
let mut m = Fr::S;
|
||||
|
||||
while t != <Fr as Field>::one() {
|
||||
let mut i = 1;
|
||||
{
|
||||
let mut t2i = t.square();
|
||||
loop {
|
||||
if t2i == <Fr as Field>::one() {
|
||||
break;
|
||||
}
|
||||
t2i = t2i.square();
|
||||
i += 1;
|
||||
}
|
||||
}
|
||||
|
||||
for _ in 0..(m - i - 1) {
|
||||
c = c.square();
|
||||
}
|
||||
MulAssign::mul_assign(&mut r, &c);
|
||||
c = c.square();
|
||||
MulAssign::mul_assign(&mut t, &c);
|
||||
m = i;
|
||||
}
|
||||
|
||||
CtOption::new(r, (r * r).ct_eq(self))
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Copy, Clone, Debug, Eq, PartialEq)]
|
||||
pub struct FrRepr([u8; 8]);
|
||||
|
||||
impl From<Fr> for FrRepr {
|
||||
fn from(v: Fr) -> FrRepr {
|
||||
FrRepr::from(&v)
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> From<&'a Fr> for FrRepr {
|
||||
fn from(v: &'a Fr) -> FrRepr {
|
||||
FrRepr(((v.0).0 as u64).to_le_bytes())
|
||||
}
|
||||
}
|
||||
|
||||
impl AsMut<[u8]> for FrRepr {
|
||||
fn as_mut(&mut self) -> &mut [u8] {
|
||||
&mut self.0[..]
|
||||
}
|
||||
}
|
||||
|
||||
impl AsRef<[u8]> for FrRepr {
|
||||
fn as_ref(&self) -> &[u8] {
|
||||
&self.0[..]
|
||||
}
|
||||
}
|
||||
|
||||
impl Default for FrRepr {
|
||||
fn default() -> FrRepr {
|
||||
FrRepr([0; 8])
|
||||
}
|
||||
}
|
||||
|
||||
impl PrimeField for Fr {
|
||||
type Repr = FrRepr;
|
||||
type ReprEndianness = byteorder::LittleEndian;
|
||||
|
||||
const NUM_BITS: u32 = 16;
|
||||
const CAPACITY: u32 = 15;
|
||||
const S: u32 = 10;
|
||||
|
||||
fn from_repr(repr: FrRepr) -> Option<Self> {
|
||||
let v = u64::from_le_bytes(repr.0);
|
||||
if v >= (MODULUS_R.0 as u64) {
|
||||
None
|
||||
} else {
|
||||
Some(Fr(Wrapping(v as u32)))
|
||||
}
|
||||
}
|
||||
|
||||
fn to_repr(&self) -> FrRepr {
|
||||
FrRepr::from(*self)
|
||||
}
|
||||
|
||||
fn is_odd(&self) -> bool {
|
||||
(self.0).0 % 2 != 0
|
||||
}
|
||||
|
||||
fn char() -> FrRepr {
|
||||
Fr(MODULUS_R).into()
|
||||
}
|
||||
|
||||
fn multiplicative_generator() -> Fr {
|
||||
Fr(Wrapping(5))
|
||||
}
|
||||
|
||||
fn root_of_unity() -> Fr {
|
||||
Fr(Wrapping(57751))
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Clone)]
|
||||
pub struct DummyEngine;
|
||||
|
||||
impl Engine for DummyEngine {
|
||||
type Fr = Fr;
|
||||
type G1 = Fr;
|
||||
type G1Affine = Fr;
|
||||
type G2 = Fr;
|
||||
type G2Affine = Fr;
|
||||
|
||||
// TODO: This should be F_645131 or something. Doesn't matter for now.
|
||||
type Gt = Fr;
|
||||
|
||||
fn pairing(p: &Self::G1Affine, q: &Self::G2Affine) -> Self::Gt {
|
||||
Self::multi_miller_loop(&[(p, &(*q).into())]).final_exponentiation()
|
||||
}
|
||||
}
|
||||
|
||||
impl MultiMillerLoop for DummyEngine {
|
||||
type G2Prepared = Fr;
|
||||
// TODO: This should be F_645131 or something. Doesn't matter for now.
|
||||
type Result = Fr;
|
||||
|
||||
fn multi_miller_loop(terms: &[(&Self::G1Affine, &Self::G2Prepared)]) -> Self::Result {
|
||||
let mut acc = <Fr as Field>::zero();
|
||||
|
||||
for &(a, b) in terms {
|
||||
let mut tmp = *a;
|
||||
MulAssign::mul_assign(&mut tmp, b);
|
||||
AddAssign::add_assign(&mut acc, &tmp);
|
||||
}
|
||||
|
||||
acc
|
||||
}
|
||||
}
|
||||
|
||||
impl MillerLoopResult for Fr {
|
||||
type Gt = Fr;
|
||||
|
||||
/// Perform final exponentiation of the result of a miller loop.
|
||||
fn final_exponentiation(&self) -> Self::Gt {
|
||||
*self
|
||||
}
|
||||
}
|
||||
|
||||
impl Group for Fr {
|
||||
type Scalar = Fr;
|
||||
|
||||
fn random<R: RngCore + ?Sized>(rng: &mut R) -> Self {
|
||||
<Fr as Field>::random(rng)
|
||||
}
|
||||
|
||||
fn identity() -> Self {
|
||||
<Fr as Field>::zero()
|
||||
}
|
||||
|
||||
fn generator() -> Self {
|
||||
<Fr as Field>::one()
|
||||
}
|
||||
|
||||
fn is_identity(&self) -> Choice {
|
||||
Choice::from(if <Fr as Field>::is_zero(self) { 1 } else { 0 })
|
||||
}
|
||||
|
||||
fn double(&self) -> Self {
|
||||
<Fr as Field>::double(self)
|
||||
}
|
||||
}
|
||||
|
||||
impl PrimeGroup for Fr {}
|
||||
|
||||
impl Curve for Fr {
|
||||
type AffineRepr = Fr;
|
||||
|
||||
fn to_affine(&self) -> Fr {
|
||||
*self
|
||||
}
|
||||
}
|
||||
|
||||
impl WnafGroup for Fr {
|
||||
fn recommended_wnaf_for_num_scalars(_: usize) -> usize {
|
||||
3
|
||||
}
|
||||
}
|
||||
|
||||
impl PrimeCurve for Fr {
|
||||
type Affine = Fr;
|
||||
}
|
||||
|
||||
#[derive(Copy, Clone, Default)]
|
||||
pub struct FakePoint;
|
||||
|
||||
impl AsMut<[u8]> for FakePoint {
|
||||
fn as_mut(&mut self) -> &mut [u8] {
|
||||
unimplemented!()
|
||||
}
|
||||
}
|
||||
|
||||
impl AsRef<[u8]> for FakePoint {
|
||||
fn as_ref(&self) -> &[u8] {
|
||||
unimplemented!()
|
||||
}
|
||||
}
|
||||
|
||||
impl PrimeCurveAffine for Fr {
|
||||
type Curve = Fr;
|
||||
type Scalar = Fr;
|
||||
|
||||
fn identity() -> Self {
|
||||
<Fr as Field>::zero()
|
||||
}
|
||||
|
||||
fn generator() -> Self {
|
||||
<Fr as Field>::one()
|
||||
}
|
||||
|
||||
fn is_identity(&self) -> Choice {
|
||||
Choice::from(if <Fr as Field>::is_zero(self) { 1 } else { 0 })
|
||||
}
|
||||
|
||||
fn to_curve(&self) -> Self::Curve {
|
||||
*self
|
||||
}
|
||||
}
|
||||
|
||||
impl GroupEncoding for Fr {
|
||||
type Repr = FakePoint;
|
||||
|
||||
fn from_bytes(_bytes: &Self::Repr) -> CtOption<Self> {
|
||||
unimplemented!()
|
||||
}
|
||||
|
||||
fn from_bytes_unchecked(_bytes: &Self::Repr) -> CtOption<Self> {
|
||||
unimplemented!()
|
||||
}
|
||||
|
||||
fn to_bytes(&self) -> Self::Repr {
|
||||
unimplemented!()
|
||||
}
|
||||
}
|
||||
|
||||
impl UncompressedEncoding for Fr {
|
||||
type Uncompressed = FakePoint;
|
||||
|
||||
fn from_uncompressed(_bytes: &Self::Uncompressed) -> CtOption<Self> {
|
||||
unimplemented!()
|
||||
}
|
||||
|
||||
fn from_uncompressed_unchecked(_bytes: &Self::Uncompressed) -> CtOption<Self> {
|
||||
unimplemented!()
|
||||
}
|
||||
|
||||
fn to_uncompressed(&self) -> Self::Uncompressed {
|
||||
unimplemented!()
|
||||
}
|
||||
}
|
||||
|
||||
impl PairingCurveAffine for Fr {
|
||||
type Pair = Fr;
|
||||
type PairingResult = Fr;
|
||||
|
||||
fn pairing_with(&self, other: &Self::Pair) -> Self::PairingResult {
|
||||
self.mul(*other)
|
||||
}
|
||||
}
|
|
@ -1,381 +0,0 @@
|
|||
use ff::{Field, PrimeField};
|
||||
|
||||
mod dummy_engine;
|
||||
use self::dummy_engine::*;
|
||||
|
||||
use std::marker::PhantomData;
|
||||
use std::ops::{AddAssign, MulAssign, SubAssign};
|
||||
|
||||
use crate::{Circuit, ConstraintSystem, SynthesisError};
|
||||
|
||||
use super::{create_proof, generate_parameters, prepare_verifying_key, verify_proof};
|
||||
|
||||
struct XORDemo<Scalar: PrimeField> {
|
||||
a: Option<bool>,
|
||||
b: Option<bool>,
|
||||
_marker: PhantomData<Scalar>,
|
||||
}
|
||||
|
||||
impl<Scalar: PrimeField> Circuit<Scalar> for XORDemo<Scalar> {
|
||||
fn synthesize<CS: ConstraintSystem<Scalar>>(self, cs: &mut CS) -> Result<(), SynthesisError> {
|
||||
let a_var = cs.alloc(
|
||||
|| "a",
|
||||
|| {
|
||||
if self.a.is_some() {
|
||||
if self.a.unwrap() {
|
||||
Ok(Scalar::one())
|
||||
} else {
|
||||
Ok(Scalar::zero())
|
||||
}
|
||||
} else {
|
||||
Err(SynthesisError::AssignmentMissing)
|
||||
}
|
||||
},
|
||||
)?;
|
||||
|
||||
cs.enforce(
|
||||
|| "a_boolean_constraint",
|
||||
|lc| lc + CS::one() - a_var,
|
||||
|lc| lc + a_var,
|
||||
|lc| lc,
|
||||
);
|
||||
|
||||
let b_var = cs.alloc(
|
||||
|| "b",
|
||||
|| {
|
||||
if self.b.is_some() {
|
||||
if self.b.unwrap() {
|
||||
Ok(Scalar::one())
|
||||
} else {
|
||||
Ok(Scalar::zero())
|
||||
}
|
||||
} else {
|
||||
Err(SynthesisError::AssignmentMissing)
|
||||
}
|
||||
},
|
||||
)?;
|
||||
|
||||
cs.enforce(
|
||||
|| "b_boolean_constraint",
|
||||
|lc| lc + CS::one() - b_var,
|
||||
|lc| lc + b_var,
|
||||
|lc| lc,
|
||||
);
|
||||
|
||||
let c_var = cs.alloc_input(
|
||||
|| "c",
|
||||
|| {
|
||||
if self.a.is_some() && self.b.is_some() {
|
||||
if self.a.unwrap() ^ self.b.unwrap() {
|
||||
Ok(Scalar::one())
|
||||
} else {
|
||||
Ok(Scalar::zero())
|
||||
}
|
||||
} else {
|
||||
Err(SynthesisError::AssignmentMissing)
|
||||
}
|
||||
},
|
||||
)?;
|
||||
|
||||
cs.enforce(
|
||||
|| "c_xor_constraint",
|
||||
|lc| lc + a_var + a_var,
|
||||
|lc| lc + b_var,
|
||||
|lc| lc + a_var + b_var - c_var,
|
||||
);
|
||||
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_xordemo() {
|
||||
let g1 = Fr::one();
|
||||
let g2 = Fr::one();
|
||||
let alpha = Fr::from_str("48577").unwrap();
|
||||
let beta = Fr::from_str("22580").unwrap();
|
||||
let gamma = Fr::from_str("53332").unwrap();
|
||||
let delta = Fr::from_str("5481").unwrap();
|
||||
let tau = Fr::from_str("3673").unwrap();
|
||||
|
||||
let params = {
|
||||
let c = XORDemo {
|
||||
a: None,
|
||||
b: None,
|
||||
_marker: PhantomData,
|
||||
};
|
||||
|
||||
generate_parameters::<DummyEngine, _>(c, g1, g2, alpha, beta, gamma, delta, tau).unwrap()
|
||||
};
|
||||
|
||||
// This will synthesize the constraint system:
|
||||
//
|
||||
// public inputs: a_0 = 1, a_1 = c
|
||||
// aux inputs: a_2 = a, a_3 = b
|
||||
// constraints:
|
||||
// (a_0 - a_2) * (a_2) = 0
|
||||
// (a_0 - a_3) * (a_3) = 0
|
||||
// (a_2 + a_2) * (a_3) = (a_2 + a_3 - a_1)
|
||||
// (a_0) * 0 = 0
|
||||
// (a_1) * 0 = 0
|
||||
|
||||
// The evaluation domain is 8. The H query should
|
||||
// have 7 elements (it's a quotient polynomial)
|
||||
assert_eq!(7, params.h.len());
|
||||
|
||||
let mut root_of_unity = Fr::root_of_unity();
|
||||
|
||||
// We expect this to be a 2^10 root of unity
|
||||
assert_eq!(Fr::one(), root_of_unity.pow_vartime(&[1u64 << 10]));
|
||||
|
||||
// Let's turn it into a 2^3 root of unity.
|
||||
root_of_unity = root_of_unity.pow_vartime(&[1u64 << 7]);
|
||||
assert_eq!(Fr::one(), root_of_unity.pow_vartime(&[1u64 << 3]));
|
||||
assert_eq!(Fr::from_str("20201").unwrap(), root_of_unity);
|
||||
|
||||
// Let's compute all the points in our evaluation domain.
|
||||
let mut points = Vec::with_capacity(8);
|
||||
for i in 0u64..8 {
|
||||
points.push(root_of_unity.pow_vartime(&[i]));
|
||||
}
|
||||
|
||||
// Let's compute t(tau) = (tau - p_0)(tau - p_1)...
|
||||
// = tau^8 - 1
|
||||
let mut t_at_tau = tau.pow_vartime(&[8u64]);
|
||||
t_at_tau.sub_assign(&Fr::one());
|
||||
{
|
||||
let mut tmp = Fr::one();
|
||||
for p in &points {
|
||||
let mut term = tau;
|
||||
term.sub_assign(p);
|
||||
tmp.mul_assign(&term);
|
||||
}
|
||||
assert_eq!(tmp, t_at_tau);
|
||||
}
|
||||
|
||||
// We expect our H query to be 7 elements of the form...
|
||||
// {tau^i t(tau) / delta}
|
||||
let delta_inverse = delta.invert().unwrap();
|
||||
let gamma_inverse = gamma.invert().unwrap();
|
||||
{
|
||||
let mut coeff = delta_inverse;
|
||||
coeff.mul_assign(&t_at_tau);
|
||||
|
||||
let mut cur = Fr::one();
|
||||
for h in params.h.iter() {
|
||||
let mut tmp = cur;
|
||||
tmp.mul_assign(&coeff);
|
||||
|
||||
assert_eq!(*h, tmp);
|
||||
|
||||
cur.mul_assign(&tau);
|
||||
}
|
||||
}
|
||||
|
||||
// The density of the IC query is 2 (2 inputs)
|
||||
assert_eq!(2, params.vk.ic.len());
|
||||
|
||||
// The density of the L query is 2 (2 aux variables)
|
||||
assert_eq!(2, params.l.len());
|
||||
|
||||
// The density of the A query is 4 (each variable is in at least one A term)
|
||||
assert_eq!(4, params.a.len());
|
||||
|
||||
// The density of the B query is 2 (two variables are in at least one B term)
|
||||
assert_eq!(2, params.b_g1.len());
|
||||
assert_eq!(2, params.b_g2.len());
|
||||
|
||||
/*
|
||||
Lagrange interpolation polynomials in our evaluation domain:
|
||||
|
||||
,-------------------------------. ,-------------------------------. ,-------------------------------.
|
||||
| A TERM | | B TERM | | C TERM |
|
||||
`-------------------------------. `-------------------------------' `-------------------------------'
|
||||
| a_0 | a_1 | a_2 | a_3 | | a_0 | a_1 | a_2 | a_3 | | a_0 | a_1 | a_2 | a_3 |
|
||||
| 1 | 0 | 64512 | 0 | | 0 | 0 | 1 | 0 | | 0 | 0 | 0 | 0 |
|
||||
| 1 | 0 | 0 | 64512 | | 0 | 0 | 0 | 1 | | 0 | 0 | 0 | 0 |
|
||||
| 0 | 0 | 2 | 0 | | 0 | 0 | 0 | 1 | | 0 | 64512 | 1 | 1 |
|
||||
| 1 | 0 | 0 | 0 | | 0 | 0 | 0 | 0 | | 0 | 0 | 0 | 0 |
|
||||
| 0 | 1 | 0 | 0 | | 0 | 0 | 0 | 0 | | 0 | 0 | 0 | 0 |
|
||||
`-------'-------'-------'-------' `-------'-------'-------'-------' `-------'-------'-------'-------'
|
||||
|
||||
Example for u_0:
|
||||
|
||||
sage: r = 64513
|
||||
sage: Fr = GF(r)
|
||||
sage: omega = (Fr(5)^63)^(2^7)
|
||||
sage: tau = Fr(3673)
|
||||
sage: R.<x> = PolynomialRing(Fr, 'x')
|
||||
sage: def eval(tau, c0, c1, c2, c3, c4):
|
||||
....: p = R.lagrange_polynomial([(omega^0, c0), (omega^1, c1), (omega^2, c2), (omega^3, c3), (omega^4, c4), (omega^5, 0), (omega^6, 0), (omega^7, 0)])
|
||||
....: return p.substitute(tau)
|
||||
sage: eval(tau, 1, 1, 0, 1, 0)
|
||||
59158
|
||||
*/
|
||||
|
||||
let u_i = [59158, 48317, 21767, 10402]
|
||||
.iter()
|
||||
.map(|e| Fr::from_str(&format!("{}", e)).unwrap())
|
||||
.collect::<Vec<Fr>>();
|
||||
let v_i = [0, 0, 60619, 30791]
|
||||
.iter()
|
||||
.map(|e| Fr::from_str(&format!("{}", e)).unwrap())
|
||||
.collect::<Vec<Fr>>();
|
||||
let w_i = [0, 23320, 41193, 41193]
|
||||
.iter()
|
||||
.map(|e| Fr::from_str(&format!("{}", e)).unwrap())
|
||||
.collect::<Vec<Fr>>();
|
||||
|
||||
for (u, a) in u_i.iter().zip(¶ms.a[..]) {
|
||||
assert_eq!(u, a);
|
||||
}
|
||||
|
||||
for (v, b) in v_i
|
||||
.iter()
|
||||
.filter(|&&e| e != Fr::zero())
|
||||
.zip(¶ms.b_g1[..])
|
||||
{
|
||||
assert_eq!(v, b);
|
||||
}
|
||||
|
||||
for (v, b) in v_i
|
||||
.iter()
|
||||
.filter(|&&e| e != Fr::zero())
|
||||
.zip(¶ms.b_g2[..])
|
||||
{
|
||||
assert_eq!(v, b);
|
||||
}
|
||||
|
||||
for i in 0..4 {
|
||||
let mut tmp1 = beta;
|
||||
tmp1.mul_assign(&u_i[i]);
|
||||
|
||||
let mut tmp2 = alpha;
|
||||
tmp2.mul_assign(&v_i[i]);
|
||||
|
||||
tmp1.add_assign(&tmp2);
|
||||
tmp1.add_assign(&w_i[i]);
|
||||
|
||||
if i < 2 {
|
||||
// Check the correctness of the IC query elements
|
||||
tmp1.mul_assign(&gamma_inverse);
|
||||
|
||||
assert_eq!(tmp1, params.vk.ic[i]);
|
||||
} else {
|
||||
// Check the correctness of the L query elements
|
||||
tmp1.mul_assign(&delta_inverse);
|
||||
|
||||
assert_eq!(tmp1, params.l[i - 2]);
|
||||
}
|
||||
}
|
||||
|
||||
// Check consistency of the other elements
|
||||
assert_eq!(alpha, params.vk.alpha_g1);
|
||||
assert_eq!(beta, params.vk.beta_g1);
|
||||
assert_eq!(beta, params.vk.beta_g2);
|
||||
assert_eq!(gamma, params.vk.gamma_g2);
|
||||
assert_eq!(delta, params.vk.delta_g1);
|
||||
assert_eq!(delta, params.vk.delta_g2);
|
||||
|
||||
let pvk = prepare_verifying_key(¶ms.vk);
|
||||
|
||||
let r = Fr::from_str("27134").unwrap();
|
||||
let s = Fr::from_str("17146").unwrap();
|
||||
|
||||
let proof = {
|
||||
let c = XORDemo {
|
||||
a: Some(true),
|
||||
b: Some(false),
|
||||
_marker: PhantomData,
|
||||
};
|
||||
|
||||
create_proof(c, ¶ms, r, s).unwrap()
|
||||
};
|
||||
|
||||
// A(x) =
|
||||
// a_0 * (44865*x^7 + 56449*x^6 + 44865*x^5 + 8064*x^4 + 3520*x^3 + 56449*x^2 + 3520*x + 40321) +
|
||||
// a_1 * (8064*x^7 + 56449*x^6 + 8064*x^5 + 56449*x^4 + 8064*x^3 + 56449*x^2 + 8064*x + 56449) +
|
||||
// a_2 * (16983*x^7 + 24192*x^6 + 63658*x^5 + 56449*x^4 + 16983*x^3 + 24192*x^2 + 63658*x + 56449) +
|
||||
// a_3 * (5539*x^7 + 27797*x^6 + 6045*x^5 + 56449*x^4 + 58974*x^3 + 36716*x^2 + 58468*x + 8064) +
|
||||
{
|
||||
// proof A = alpha + A(tau) + delta * r
|
||||
let mut expected_a = delta;
|
||||
expected_a.mul_assign(&r);
|
||||
expected_a.add_assign(&alpha);
|
||||
expected_a.add_assign(&u_i[0]); // a_0 = 1
|
||||
expected_a.add_assign(&u_i[1]); // a_1 = 1
|
||||
expected_a.add_assign(&u_i[2]); // a_2 = 1
|
||||
// a_3 = 0
|
||||
assert_eq!(proof.a, expected_a);
|
||||
}
|
||||
|
||||
// B(x) =
|
||||
// a_0 * (0) +
|
||||
// a_1 * (0) +
|
||||
// a_2 * (56449*x^7 + 56449*x^6 + 56449*x^5 + 56449*x^4 + 56449*x^3 + 56449*x^2 + 56449*x + 56449) +
|
||||
// a_3 * (31177*x^7 + 44780*x^6 + 21752*x^5 + 42255*x^3 + 35861*x^2 + 33842*x + 48385)
|
||||
{
|
||||
// proof B = beta + B(tau) + delta * s
|
||||
let mut expected_b = delta;
|
||||
expected_b.mul_assign(&s);
|
||||
expected_b.add_assign(&beta);
|
||||
expected_b.add_assign(&v_i[0]); // a_0 = 1
|
||||
expected_b.add_assign(&v_i[1]); // a_1 = 1
|
||||
expected_b.add_assign(&v_i[2]); // a_2 = 1
|
||||
// a_3 = 0
|
||||
assert_eq!(proof.b, expected_b);
|
||||
}
|
||||
|
||||
// C(x) =
|
||||
// a_0 * (0) +
|
||||
// a_1 * (27797*x^7 + 56449*x^6 + 36716*x^5 + 8064*x^4 + 27797*x^3 + 56449*x^2 + 36716*x + 8064) +
|
||||
// a_2 * (36716*x^7 + 8064*x^6 + 27797*x^5 + 56449*x^4 + 36716*x^3 + 8064*x^2 + 27797*x + 56449) +
|
||||
// a_3 * (36716*x^7 + 8064*x^6 + 27797*x^5 + 56449*x^4 + 36716*x^3 + 8064*x^2 + 27797*x + 56449)
|
||||
//
|
||||
// If A * B = C at each point in the domain, then the following polynomial...
|
||||
// P(x) = A(x) * B(x) - C(x)
|
||||
// = 49752*x^14 + 13914*x^13 + 29243*x^12 + 27227*x^11 + 62362*x^10 + 35703*x^9 + 4032*x^8 + 14761*x^6 + 50599*x^5 + 35270*x^4 + 37286*x^3 + 2151*x^2 + 28810*x + 60481
|
||||
//
|
||||
// ... should be divisible by t(x), producing the quotient polynomial:
|
||||
// h(x) = P(x) / t(x)
|
||||
// = 49752*x^6 + 13914*x^5 + 29243*x^4 + 27227*x^3 + 62362*x^2 + 35703*x + 4032
|
||||
{
|
||||
let mut expected_c = Fr::zero();
|
||||
|
||||
// A * s
|
||||
let mut tmp = proof.a;
|
||||
tmp.mul_assign(&s);
|
||||
expected_c.add_assign(&tmp);
|
||||
|
||||
// B * r
|
||||
let mut tmp = proof.b;
|
||||
tmp.mul_assign(&r);
|
||||
expected_c.add_assign(&tmp);
|
||||
|
||||
// delta * r * s
|
||||
let mut tmp = delta;
|
||||
tmp.mul_assign(&r);
|
||||
tmp.mul_assign(&s);
|
||||
expected_c.sub_assign(&tmp);
|
||||
|
||||
// L query answer
|
||||
// a_2 = 1, a_3 = 0
|
||||
expected_c.add_assign(¶ms.l[0]);
|
||||
|
||||
// H query answer
|
||||
for (i, coeff) in [5040, 11763, 10755, 63633, 128, 9747, 8739]
|
||||
.iter()
|
||||
.enumerate()
|
||||
{
|
||||
let coeff = Fr::from_str(&format!("{}", coeff)).unwrap();
|
||||
|
||||
let mut tmp = params.h[i];
|
||||
tmp.mul_assign(&coeff);
|
||||
expected_c.add_assign(&tmp);
|
||||
}
|
||||
|
||||
assert_eq!(expected_c, proof.c);
|
||||
}
|
||||
|
||||
assert!(verify_proof(&pvk, &proof, &[Fr::one()]).is_ok());
|
||||
}
|
|
@ -1,56 +0,0 @@
|
|||
use group::{prime::PrimeCurveAffine, Curve};
|
||||
use pairing::{MillerLoopResult, MultiMillerLoop};
|
||||
use std::ops::{AddAssign, Neg};
|
||||
|
||||
use super::{PreparedVerifyingKey, Proof, VerifyingKey};
|
||||
|
||||
use crate::VerificationError;
|
||||
|
||||
pub fn prepare_verifying_key<E: MultiMillerLoop>(vk: &VerifyingKey<E>) -> PreparedVerifyingKey<E> {
|
||||
let gamma = vk.gamma_g2.neg();
|
||||
let delta = vk.delta_g2.neg();
|
||||
|
||||
PreparedVerifyingKey {
|
||||
alpha_g1_beta_g2: E::pairing(&vk.alpha_g1, &vk.beta_g2),
|
||||
neg_gamma_g2: gamma.into(),
|
||||
neg_delta_g2: delta.into(),
|
||||
ic: vk.ic.clone(),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn verify_proof<'a, E: MultiMillerLoop>(
|
||||
pvk: &'a PreparedVerifyingKey<E>,
|
||||
proof: &Proof<E>,
|
||||
public_inputs: &[E::Fr],
|
||||
) -> Result<(), VerificationError> {
|
||||
if (public_inputs.len() + 1) != pvk.ic.len() {
|
||||
return Err(VerificationError::InvalidVerifyingKey);
|
||||
}
|
||||
|
||||
let mut acc = pvk.ic[0].to_curve();
|
||||
|
||||
for (i, b) in public_inputs.iter().zip(pvk.ic.iter().skip(1)) {
|
||||
AddAssign::<&E::G1>::add_assign(&mut acc, &(*b * i));
|
||||
}
|
||||
|
||||
// The original verification equation is:
|
||||
// A * B = alpha * beta + inputs * gamma + C * delta
|
||||
// ... however, we rearrange it so that it is:
|
||||
// A * B - inputs * gamma - C * delta = alpha * beta
|
||||
// or equivalently:
|
||||
// A * B + inputs * (-gamma) + C * (-delta) = alpha * beta
|
||||
// which allows us to do a single final exponentiation.
|
||||
|
||||
if pvk.alpha_g1_beta_g2
|
||||
== E::multi_miller_loop(&[
|
||||
(&proof.a, &proof.b.into()),
|
||||
(&acc.to_affine(), &pvk.neg_gamma_g2),
|
||||
(&proof.c, &pvk.neg_delta_g2),
|
||||
])
|
||||
.final_exponentiation()
|
||||
{
|
||||
Ok(())
|
||||
} else {
|
||||
Err(VerificationError::InvalidProof)
|
||||
}
|
||||
}
|
|
@ -1,575 +0,0 @@
|
|||
//! `bellman` is a crate for building zk-SNARK circuits. It provides circuit
|
||||
//! traits and and primitive structures, as well as basic gadget implementations
|
||||
//! such as booleans and number abstractions.
|
||||
//!
|
||||
//! # Example circuit
|
||||
//!
|
||||
//! Say we want to write a circuit that proves we know the preimage to some hash
|
||||
//! computed using SHA-256d (calling SHA-256 twice). The preimage must have a
|
||||
//! fixed length known in advance (because the circuit parameters will depend on
|
||||
//! it), but can otherwise have any value. We take the following strategy:
|
||||
//!
|
||||
//! - Witness each bit of the preimage.
|
||||
//! - Compute `hash = SHA-256d(preimage)` inside the circuit.
|
||||
//! - Expose `hash` as a public input using multiscalar packing.
|
||||
//!
|
||||
//! ```
|
||||
//! use bellman::{
|
||||
//! gadgets::{
|
||||
//! boolean::{AllocatedBit, Boolean},
|
||||
//! multipack,
|
||||
//! sha256::sha256,
|
||||
//! },
|
||||
//! groth16, Circuit, ConstraintSystem, SynthesisError,
|
||||
//! };
|
||||
//! use bls12_381::Bls12;
|
||||
//! use ff::PrimeField;
|
||||
//! use pairing::Engine;
|
||||
//! use rand::rngs::OsRng;
|
||||
//! use sha2::{Digest, Sha256};
|
||||
//!
|
||||
//! /// Our own SHA-256d gadget. Input and output are in little-endian bit order.
|
||||
//! fn sha256d<Scalar: PrimeField, CS: ConstraintSystem<Scalar>>(
|
||||
//! mut cs: CS,
|
||||
//! data: &[Boolean],
|
||||
//! ) -> Result<Vec<Boolean>, SynthesisError> {
|
||||
//! // Flip endianness of each input byte
|
||||
//! let input: Vec<_> = data
|
||||
//! .chunks(8)
|
||||
//! .map(|c| c.iter().rev())
|
||||
//! .flatten()
|
||||
//! .cloned()
|
||||
//! .collect();
|
||||
//!
|
||||
//! let mid = sha256(cs.namespace(|| "SHA-256(input)"), &input)?;
|
||||
//! let res = sha256(cs.namespace(|| "SHA-256(mid)"), &mid)?;
|
||||
//!
|
||||
//! // Flip endianness of each output byte
|
||||
//! Ok(res
|
||||
//! .chunks(8)
|
||||
//! .map(|c| c.iter().rev())
|
||||
//! .flatten()
|
||||
//! .cloned()
|
||||
//! .collect())
|
||||
//! }
|
||||
//!
|
||||
//! struct MyCircuit {
|
||||
//! /// The input to SHA-256d we are proving that we know. Set to `None` when we
|
||||
//! /// are verifying a proof (and do not have the witness data).
|
||||
//! preimage: Option<[u8; 80]>,
|
||||
//! }
|
||||
//!
|
||||
//! impl<Scalar: PrimeField> Circuit<Scalar> for MyCircuit {
|
||||
//! fn synthesize<CS: ConstraintSystem<Scalar>>(self, cs: &mut CS) -> Result<(), SynthesisError> {
|
||||
//! // Compute the values for the bits of the preimage. If we are verifying a proof,
|
||||
//! // we still need to create the same constraints, so we return an equivalent-size
|
||||
//! // Vec of None (indicating that the value of each bit is unknown).
|
||||
//! let bit_values = if let Some(preimage) = self.preimage {
|
||||
//! preimage
|
||||
//! .into_iter()
|
||||
//! .map(|byte| (0..8).map(move |i| (byte >> i) & 1u8 == 1u8))
|
||||
//! .flatten()
|
||||
//! .map(|b| Some(b))
|
||||
//! .collect()
|
||||
//! } else {
|
||||
//! vec![None; 80 * 8]
|
||||
//! };
|
||||
//! assert_eq!(bit_values.len(), 80 * 8);
|
||||
//!
|
||||
//! // Witness the bits of the preimage.
|
||||
//! let preimage_bits = bit_values
|
||||
//! .into_iter()
|
||||
//! .enumerate()
|
||||
//! // Allocate each bit.
|
||||
//! .map(|(i, b)| {
|
||||
//! AllocatedBit::alloc(cs.namespace(|| format!("preimage bit {}", i)), b)
|
||||
//! })
|
||||
//! // Convert the AllocatedBits into Booleans (required for the sha256 gadget).
|
||||
//! .map(|b| b.map(Boolean::from))
|
||||
//! .collect::<Result<Vec<_>, _>>()?;
|
||||
//!
|
||||
//! // Compute hash = SHA-256d(preimage).
|
||||
//! let hash = sha256d(cs.namespace(|| "SHA-256d(preimage)"), &preimage_bits)?;
|
||||
//!
|
||||
//! // Expose the vector of 32 boolean variables as compact public inputs.
|
||||
//! multipack::pack_into_inputs(cs.namespace(|| "pack hash"), &hash)
|
||||
//! }
|
||||
//! }
|
||||
//!
|
||||
//! // Create parameters for our circuit. In a production deployment these would
|
||||
//! // be generated securely using a multiparty computation.
|
||||
//! let params = {
|
||||
//! let c = MyCircuit { preimage: None };
|
||||
//! groth16::generate_random_parameters::<Bls12, _, _>(c, &mut OsRng).unwrap()
|
||||
//! };
|
||||
//!
|
||||
//! // Prepare the verification key (for proof verification).
|
||||
//! let pvk = groth16::prepare_verifying_key(¶ms.vk);
|
||||
//!
|
||||
//! // Pick a preimage and compute its hash.
|
||||
//! let preimage = [42; 80];
|
||||
//! let hash = Sha256::digest(&Sha256::digest(&preimage));
|
||||
//!
|
||||
//! // Create an instance of our circuit (with the preimage as a witness).
|
||||
//! let c = MyCircuit {
|
||||
//! preimage: Some(preimage),
|
||||
//! };
|
||||
//!
|
||||
//! // Create a Groth16 proof with our parameters.
|
||||
//! let proof = groth16::create_random_proof(c, ¶ms, &mut OsRng).unwrap();
|
||||
//!
|
||||
//! // Pack the hash as inputs for proof verification.
|
||||
//! let hash_bits = multipack::bytes_to_bits_le(&hash);
|
||||
//! let inputs = multipack::compute_multipacking(&hash_bits);
|
||||
//!
|
||||
//! // Check the proof!
|
||||
//! assert!(groth16::verify_proof(&pvk, &proof, &inputs).is_ok());
|
||||
//! ```
|
||||
//!
|
||||
//! # Roadmap
|
||||
//!
|
||||
//! `bellman` is being refactored into a generic proving library. Currently it
|
||||
//! is pairing-specific, and different types of proving systems need to be
|
||||
//! implemented as sub-modules. After the refactor, `bellman` will be generic
|
||||
//! using the [`ff`] and [`group`] crates, while specific proving systems will
|
||||
//! be separate crates that pull in the dependencies they require.
|
||||
|
||||
// Catch documentation errors caused by code changes.
|
||||
#![deny(intra_doc_link_resolution_failure)]
|
||||
|
||||
pub mod domain;
|
||||
pub mod gadgets;
|
||||
#[cfg(feature = "groth16")]
|
||||
pub mod groth16;
|
||||
pub mod multicore;
|
||||
mod multiexp;
|
||||
|
||||
use ff::PrimeField;
|
||||
|
||||
use std::error::Error;
|
||||
use std::fmt;
|
||||
use std::io;
|
||||
use std::marker::PhantomData;
|
||||
use std::ops::{Add, Sub};
|
||||
|
||||
/// Computations are expressed in terms of arithmetic circuits, in particular
|
||||
/// rank-1 quadratic constraint systems. The `Circuit` trait represents a
|
||||
/// circuit that can be synthesized. The `synthesize` method is called during
|
||||
/// CRS generation and during proving.
|
||||
pub trait Circuit<Scalar: PrimeField> {
|
||||
/// Synthesize the circuit into a rank-1 quadratic constraint system
|
||||
fn synthesize<CS: ConstraintSystem<Scalar>>(self, cs: &mut CS) -> Result<(), SynthesisError>;
|
||||
}
|
||||
|
||||
/// Represents a variable in our constraint system.
|
||||
#[derive(Copy, Clone, Debug)]
|
||||
pub struct Variable(Index);
|
||||
|
||||
impl Variable {
|
||||
/// This constructs a variable with an arbitrary index.
|
||||
/// Circuit implementations are not recommended to use this.
|
||||
pub fn new_unchecked(idx: Index) -> Variable {
|
||||
Variable(idx)
|
||||
}
|
||||
|
||||
/// This returns the index underlying the variable.
|
||||
/// Circuit implementations are not recommended to use this.
|
||||
pub fn get_unchecked(&self) -> Index {
|
||||
self.0
|
||||
}
|
||||
}
|
||||
|
||||
/// Represents the index of either an input variable or
|
||||
/// auxiliary variable.
|
||||
#[derive(Copy, Clone, PartialEq, Debug)]
|
||||
pub enum Index {
|
||||
Input(usize),
|
||||
Aux(usize),
|
||||
}
|
||||
|
||||
/// This represents a linear combination of some variables, with coefficients
|
||||
/// in the scalar field of a pairing-friendly elliptic curve group.
|
||||
#[derive(Clone)]
|
||||
pub struct LinearCombination<Scalar: PrimeField>(Vec<(Variable, Scalar)>);
|
||||
|
||||
impl<Scalar: PrimeField> AsRef<[(Variable, Scalar)]> for LinearCombination<Scalar> {
|
||||
fn as_ref(&self) -> &[(Variable, Scalar)] {
|
||||
&self.0
|
||||
}
|
||||
}
|
||||
|
||||
impl<Scalar: PrimeField> LinearCombination<Scalar> {
|
||||
pub fn zero() -> LinearCombination<Scalar> {
|
||||
LinearCombination(vec![])
|
||||
}
|
||||
}
|
||||
|
||||
impl<Scalar: PrimeField> Add<(Scalar, Variable)> for LinearCombination<Scalar> {
|
||||
type Output = LinearCombination<Scalar>;
|
||||
|
||||
fn add(mut self, (coeff, var): (Scalar, Variable)) -> LinearCombination<Scalar> {
|
||||
self.0.push((var, coeff));
|
||||
|
||||
self
|
||||
}
|
||||
}
|
||||
|
||||
impl<Scalar: PrimeField> Sub<(Scalar, Variable)> for LinearCombination<Scalar> {
|
||||
type Output = LinearCombination<Scalar>;
|
||||
|
||||
#[allow(clippy::suspicious_arithmetic_impl)]
|
||||
fn sub(self, (coeff, var): (Scalar, Variable)) -> LinearCombination<Scalar> {
|
||||
self + (coeff.neg(), var)
|
||||
}
|
||||
}
|
||||
|
||||
impl<Scalar: PrimeField> Add<Variable> for LinearCombination<Scalar> {
|
||||
type Output = LinearCombination<Scalar>;
|
||||
|
||||
fn add(self, other: Variable) -> LinearCombination<Scalar> {
|
||||
self + (Scalar::one(), other)
|
||||
}
|
||||
}
|
||||
|
||||
impl<Scalar: PrimeField> Sub<Variable> for LinearCombination<Scalar> {
|
||||
type Output = LinearCombination<Scalar>;
|
||||
|
||||
fn sub(self, other: Variable) -> LinearCombination<Scalar> {
|
||||
self - (Scalar::one(), other)
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a, Scalar: PrimeField> Add<&'a LinearCombination<Scalar>> for LinearCombination<Scalar> {
|
||||
type Output = LinearCombination<Scalar>;
|
||||
|
||||
fn add(mut self, other: &'a LinearCombination<Scalar>) -> LinearCombination<Scalar> {
|
||||
for s in &other.0 {
|
||||
self = self + (s.1, s.0);
|
||||
}
|
||||
|
||||
self
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a, Scalar: PrimeField> Sub<&'a LinearCombination<Scalar>> for LinearCombination<Scalar> {
|
||||
type Output = LinearCombination<Scalar>;
|
||||
|
||||
fn sub(mut self, other: &'a LinearCombination<Scalar>) -> LinearCombination<Scalar> {
|
||||
for s in &other.0 {
|
||||
self = self - (s.1, s.0);
|
||||
}
|
||||
|
||||
self
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a, Scalar: PrimeField> Add<(Scalar, &'a LinearCombination<Scalar>)>
|
||||
for LinearCombination<Scalar>
|
||||
{
|
||||
type Output = LinearCombination<Scalar>;
|
||||
|
||||
fn add(
|
||||
mut self,
|
||||
(coeff, other): (Scalar, &'a LinearCombination<Scalar>),
|
||||
) -> LinearCombination<Scalar> {
|
||||
for s in &other.0 {
|
||||
let mut tmp = s.1;
|
||||
tmp.mul_assign(&coeff);
|
||||
self = self + (tmp, s.0);
|
||||
}
|
||||
|
||||
self
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a, Scalar: PrimeField> Sub<(Scalar, &'a LinearCombination<Scalar>)>
|
||||
for LinearCombination<Scalar>
|
||||
{
|
||||
type Output = LinearCombination<Scalar>;
|
||||
|
||||
fn sub(
|
||||
mut self,
|
||||
(coeff, other): (Scalar, &'a LinearCombination<Scalar>),
|
||||
) -> LinearCombination<Scalar> {
|
||||
for s in &other.0 {
|
||||
let mut tmp = s.1;
|
||||
tmp.mul_assign(&coeff);
|
||||
self = self - (tmp, s.0);
|
||||
}
|
||||
|
||||
self
|
||||
}
|
||||
}
|
||||
|
||||
/// This is an error that could occur during circuit synthesis contexts,
|
||||
/// such as CRS generation or proving.
|
||||
#[derive(Debug)]
|
||||
pub enum SynthesisError {
|
||||
/// During synthesis, we lacked knowledge of a variable assignment.
|
||||
AssignmentMissing,
|
||||
/// During synthesis, we divided by zero.
|
||||
DivisionByZero,
|
||||
/// During synthesis, we constructed an unsatisfiable constraint system.
|
||||
Unsatisfiable,
|
||||
/// During synthesis, our polynomials ended up being too high of degree
|
||||
PolynomialDegreeTooLarge,
|
||||
/// During proof generation, we encountered an identity in the CRS
|
||||
UnexpectedIdentity,
|
||||
/// During proof generation, we encountered an I/O error with the CRS
|
||||
IoError(io::Error),
|
||||
/// During CRS generation, we observed an unconstrained auxiliary variable
|
||||
UnconstrainedVariable,
|
||||
}
|
||||
|
||||
impl From<io::Error> for SynthesisError {
|
||||
fn from(e: io::Error) -> SynthesisError {
|
||||
SynthesisError::IoError(e)
|
||||
}
|
||||
}
|
||||
|
||||
impl Error for SynthesisError {
|
||||
fn description(&self) -> &str {
|
||||
match *self {
|
||||
SynthesisError::AssignmentMissing => {
|
||||
"an assignment for a variable could not be computed"
|
||||
}
|
||||
SynthesisError::DivisionByZero => "division by zero",
|
||||
SynthesisError::Unsatisfiable => "unsatisfiable constraint system",
|
||||
SynthesisError::PolynomialDegreeTooLarge => "polynomial degree is too large",
|
||||
SynthesisError::UnexpectedIdentity => "encountered an identity element in the CRS",
|
||||
SynthesisError::IoError(_) => "encountered an I/O error",
|
||||
SynthesisError::UnconstrainedVariable => "auxiliary variable was unconstrained",
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl fmt::Display for SynthesisError {
|
||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> Result<(), fmt::Error> {
|
||||
if let SynthesisError::IoError(ref e) = *self {
|
||||
write!(f, "I/O error: ")?;
|
||||
e.fmt(f)
|
||||
} else {
|
||||
write!(f, "{}", self)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// An error during verification.
|
||||
#[derive(Debug, Clone)]
|
||||
pub enum VerificationError {
|
||||
/// Verification was attempted with a malformed verifying key.
|
||||
InvalidVerifyingKey,
|
||||
/// Proof verification failed.
|
||||
InvalidProof,
|
||||
}
|
||||
|
||||
impl Error for VerificationError {
|
||||
fn description(&self) -> &str {
|
||||
match *self {
|
||||
VerificationError::InvalidVerifyingKey => "malformed verifying key",
|
||||
VerificationError::InvalidProof => "proof verification failed",
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl fmt::Display for VerificationError {
|
||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> Result<(), fmt::Error> {
|
||||
write!(f, "{}", self)
|
||||
}
|
||||
}
|
||||
|
||||
/// Represents a constraint system which can have new variables
|
||||
/// allocated and constrains between them formed.
|
||||
pub trait ConstraintSystem<Scalar: PrimeField>: Sized {
|
||||
/// Represents the type of the "root" of this constraint system
|
||||
/// so that nested namespaces can minimize indirection.
|
||||
type Root: ConstraintSystem<Scalar>;
|
||||
|
||||
/// Return the "one" input variable
|
||||
fn one() -> Variable {
|
||||
Variable::new_unchecked(Index::Input(0))
|
||||
}
|
||||
|
||||
/// Allocate a private variable in the constraint system. The provided function is used to
|
||||
/// determine the assignment of the variable. The given `annotation` function is invoked
|
||||
/// in testing contexts in order to derive a unique name for this variable in the current
|
||||
/// namespace.
|
||||
fn alloc<F, A, AR>(&mut self, annotation: A, f: F) -> Result<Variable, SynthesisError>
|
||||
where
|
||||
F: FnOnce() -> Result<Scalar, SynthesisError>,
|
||||
A: FnOnce() -> AR,
|
||||
AR: Into<String>;
|
||||
|
||||
/// Allocate a public variable in the constraint system. The provided function is used to
|
||||
/// determine the assignment of the variable.
|
||||
fn alloc_input<F, A, AR>(&mut self, annotation: A, f: F) -> Result<Variable, SynthesisError>
|
||||
where
|
||||
F: FnOnce() -> Result<Scalar, SynthesisError>,
|
||||
A: FnOnce() -> AR,
|
||||
AR: Into<String>;
|
||||
|
||||
/// Enforce that `A` * `B` = `C`. The `annotation` function is invoked in testing contexts
|
||||
/// in order to derive a unique name for the constraint in the current namespace.
|
||||
fn enforce<A, AR, LA, LB, LC>(&mut self, annotation: A, a: LA, b: LB, c: LC)
|
||||
where
|
||||
A: FnOnce() -> AR,
|
||||
AR: Into<String>,
|
||||
LA: FnOnce(LinearCombination<Scalar>) -> LinearCombination<Scalar>,
|
||||
LB: FnOnce(LinearCombination<Scalar>) -> LinearCombination<Scalar>,
|
||||
LC: FnOnce(LinearCombination<Scalar>) -> LinearCombination<Scalar>;
|
||||
|
||||
/// Create a new (sub)namespace and enter into it. Not intended
|
||||
/// for downstream use; use `namespace` instead.
|
||||
fn push_namespace<NR, N>(&mut self, name_fn: N)
|
||||
where
|
||||
NR: Into<String>,
|
||||
N: FnOnce() -> NR;
|
||||
|
||||
/// Exit out of the existing namespace. Not intended for
|
||||
/// downstream use; use `namespace` instead.
|
||||
fn pop_namespace(&mut self);
|
||||
|
||||
/// Gets the "root" constraint system, bypassing the namespacing.
|
||||
/// Not intended for downstream use; use `namespace` instead.
|
||||
fn get_root(&mut self) -> &mut Self::Root;
|
||||
|
||||
/// Begin a namespace for this constraint system.
|
||||
fn namespace<NR, N>(&mut self, name_fn: N) -> Namespace<'_, Scalar, Self::Root>
|
||||
where
|
||||
NR: Into<String>,
|
||||
N: FnOnce() -> NR,
|
||||
{
|
||||
self.get_root().push_namespace(name_fn);
|
||||
|
||||
Namespace(self.get_root(), PhantomData)
|
||||
}
|
||||
}
|
||||
|
||||
/// This is a "namespaced" constraint system which borrows a constraint system (pushing
|
||||
/// a namespace context) and, when dropped, pops out of the namespace context.
|
||||
pub struct Namespace<'a, Scalar: PrimeField, CS: ConstraintSystem<Scalar>>(
|
||||
&'a mut CS,
|
||||
PhantomData<Scalar>,
|
||||
);
|
||||
|
||||
impl<'cs, Scalar: PrimeField, CS: ConstraintSystem<Scalar>> ConstraintSystem<Scalar>
|
||||
for Namespace<'cs, Scalar, CS>
|
||||
{
|
||||
type Root = CS::Root;
|
||||
|
||||
fn one() -> Variable {
|
||||
CS::one()
|
||||
}
|
||||
|
||||
fn alloc<F, A, AR>(&mut self, annotation: A, f: F) -> Result<Variable, SynthesisError>
|
||||
where
|
||||
F: FnOnce() -> Result<Scalar, SynthesisError>,
|
||||
A: FnOnce() -> AR,
|
||||
AR: Into<String>,
|
||||
{
|
||||
self.0.alloc(annotation, f)
|
||||
}
|
||||
|
||||
fn alloc_input<F, A, AR>(&mut self, annotation: A, f: F) -> Result<Variable, SynthesisError>
|
||||
where
|
||||
F: FnOnce() -> Result<Scalar, SynthesisError>,
|
||||
A: FnOnce() -> AR,
|
||||
AR: Into<String>,
|
||||
{
|
||||
self.0.alloc_input(annotation, f)
|
||||
}
|
||||
|
||||
fn enforce<A, AR, LA, LB, LC>(&mut self, annotation: A, a: LA, b: LB, c: LC)
|
||||
where
|
||||
A: FnOnce() -> AR,
|
||||
AR: Into<String>,
|
||||
LA: FnOnce(LinearCombination<Scalar>) -> LinearCombination<Scalar>,
|
||||
LB: FnOnce(LinearCombination<Scalar>) -> LinearCombination<Scalar>,
|
||||
LC: FnOnce(LinearCombination<Scalar>) -> LinearCombination<Scalar>,
|
||||
{
|
||||
self.0.enforce(annotation, a, b, c)
|
||||
}
|
||||
|
||||
// Downstream users who use `namespace` will never interact with these
|
||||
// functions and they will never be invoked because the namespace is
|
||||
// never a root constraint system.
|
||||
|
||||
fn push_namespace<NR, N>(&mut self, _: N)
|
||||
where
|
||||
NR: Into<String>,
|
||||
N: FnOnce() -> NR,
|
||||
{
|
||||
panic!("only the root's push_namespace should be called");
|
||||
}
|
||||
|
||||
fn pop_namespace(&mut self) {
|
||||
panic!("only the root's pop_namespace should be called");
|
||||
}
|
||||
|
||||
fn get_root(&mut self) -> &mut Self::Root {
|
||||
self.0.get_root()
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a, Scalar: PrimeField, CS: ConstraintSystem<Scalar>> Drop for Namespace<'a, Scalar, CS> {
|
||||
fn drop(&mut self) {
|
||||
self.get_root().pop_namespace()
|
||||
}
|
||||
}
|
||||
|
||||
/// Convenience implementation of ConstraintSystem<Scalar> for mutable references to
|
||||
/// constraint systems.
|
||||
impl<'cs, Scalar: PrimeField, CS: ConstraintSystem<Scalar>> ConstraintSystem<Scalar>
|
||||
for &'cs mut CS
|
||||
{
|
||||
type Root = CS::Root;
|
||||
|
||||
fn one() -> Variable {
|
||||
CS::one()
|
||||
}
|
||||
|
||||
fn alloc<F, A, AR>(&mut self, annotation: A, f: F) -> Result<Variable, SynthesisError>
|
||||
where
|
||||
F: FnOnce() -> Result<Scalar, SynthesisError>,
|
||||
A: FnOnce() -> AR,
|
||||
AR: Into<String>,
|
||||
{
|
||||
(**self).alloc(annotation, f)
|
||||
}
|
||||
|
||||
fn alloc_input<F, A, AR>(&mut self, annotation: A, f: F) -> Result<Variable, SynthesisError>
|
||||
where
|
||||
F: FnOnce() -> Result<Scalar, SynthesisError>,
|
||||
A: FnOnce() -> AR,
|
||||
AR: Into<String>,
|
||||
{
|
||||
(**self).alloc_input(annotation, f)
|
||||
}
|
||||
|
||||
fn enforce<A, AR, LA, LB, LC>(&mut self, annotation: A, a: LA, b: LB, c: LC)
|
||||
where
|
||||
A: FnOnce() -> AR,
|
||||
AR: Into<String>,
|
||||
LA: FnOnce(LinearCombination<Scalar>) -> LinearCombination<Scalar>,
|
||||
LB: FnOnce(LinearCombination<Scalar>) -> LinearCombination<Scalar>,
|
||||
LC: FnOnce(LinearCombination<Scalar>) -> LinearCombination<Scalar>,
|
||||
{
|
||||
(**self).enforce(annotation, a, b, c)
|
||||
}
|
||||
|
||||
fn push_namespace<NR, N>(&mut self, name_fn: N)
|
||||
where
|
||||
NR: Into<String>,
|
||||
N: FnOnce() -> NR,
|
||||
{
|
||||
(**self).push_namespace(name_fn)
|
||||
}
|
||||
|
||||
fn pop_namespace(&mut self) {
|
||||
(**self).pop_namespace()
|
||||
}
|
||||
|
||||
fn get_root(&mut self) -> &mut Self::Root {
|
||||
(**self).get_root()
|
||||
}
|
||||
}
|
|
@ -1,164 +0,0 @@
|
|||
//! An interface for dealing with the kinds of parallel computations involved in
|
||||
//! `bellman`. It's currently just a thin wrapper around [`CpuPool`] and
|
||||
//! [`crossbeam`] but may be extended in the future to allow for various
|
||||
//! parallelism strategies.
|
||||
//!
|
||||
//! [`CpuPool`]: futures_cpupool::CpuPool
|
||||
|
||||
#[cfg(feature = "multicore")]
|
||||
mod implementation {
|
||||
use crossbeam::{self, thread::Scope};
|
||||
use futures::{Future, IntoFuture, Poll};
|
||||
use futures_cpupool::{CpuFuture, CpuPool};
|
||||
use num_cpus;
|
||||
|
||||
#[derive(Clone)]
|
||||
pub struct Worker {
|
||||
cpus: usize,
|
||||
pool: CpuPool,
|
||||
}
|
||||
|
||||
impl Worker {
|
||||
// We don't expose this outside the library so that
|
||||
// all `Worker` instances have the same number of
|
||||
// CPUs configured.
|
||||
pub(crate) fn new_with_cpus(cpus: usize) -> Worker {
|
||||
Worker {
|
||||
cpus,
|
||||
pool: CpuPool::new(cpus),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn new() -> Worker {
|
||||
Self::new_with_cpus(num_cpus::get())
|
||||
}
|
||||
|
||||
pub fn log_num_cpus(&self) -> u32 {
|
||||
log2_floor(self.cpus)
|
||||
}
|
||||
|
||||
pub fn compute<F, R>(&self, f: F) -> WorkerFuture<R::Item, R::Error>
|
||||
where
|
||||
F: FnOnce() -> R + Send + 'static,
|
||||
R: IntoFuture + 'static,
|
||||
R::Future: Send + 'static,
|
||||
R::Item: Send + 'static,
|
||||
R::Error: Send + 'static,
|
||||
{
|
||||
WorkerFuture {
|
||||
future: self.pool.spawn_fn(f),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn scope<'a, F, R>(&self, elements: usize, f: F) -> R
|
||||
where
|
||||
F: FnOnce(&Scope<'a>, usize) -> R,
|
||||
{
|
||||
let chunk_size = if elements < self.cpus {
|
||||
1
|
||||
} else {
|
||||
elements / self.cpus
|
||||
};
|
||||
|
||||
// TODO: Handle case where threads fail
|
||||
crossbeam::scope(|scope| f(scope, chunk_size))
|
||||
.expect("Threads aren't allowed to fail yet")
|
||||
}
|
||||
}
|
||||
|
||||
pub struct WorkerFuture<T, E> {
|
||||
future: CpuFuture<T, E>,
|
||||
}
|
||||
|
||||
impl<T: Send + 'static, E: Send + 'static> Future for WorkerFuture<T, E> {
|
||||
type Item = T;
|
||||
type Error = E;
|
||||
|
||||
fn poll(&mut self) -> Poll<Self::Item, Self::Error> {
|
||||
self.future.poll()
|
||||
}
|
||||
}
|
||||
|
||||
fn log2_floor(num: usize) -> u32 {
|
||||
assert!(num > 0);
|
||||
|
||||
let mut pow = 0;
|
||||
|
||||
while (1 << (pow + 1)) <= num {
|
||||
pow += 1;
|
||||
}
|
||||
|
||||
pow
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_log2_floor() {
|
||||
assert_eq!(log2_floor(1), 0);
|
||||
assert_eq!(log2_floor(2), 1);
|
||||
assert_eq!(log2_floor(3), 1);
|
||||
assert_eq!(log2_floor(4), 2);
|
||||
assert_eq!(log2_floor(5), 2);
|
||||
assert_eq!(log2_floor(6), 2);
|
||||
assert_eq!(log2_floor(7), 2);
|
||||
assert_eq!(log2_floor(8), 3);
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(not(feature = "multicore"))]
|
||||
mod implementation {
|
||||
use futures::{future, Future, IntoFuture, Poll};
|
||||
|
||||
#[derive(Clone)]
|
||||
pub struct Worker;
|
||||
|
||||
impl Worker {
|
||||
pub fn new() -> Worker {
|
||||
Worker
|
||||
}
|
||||
|
||||
pub fn log_num_cpus(&self) -> u32 {
|
||||
0
|
||||
}
|
||||
|
||||
pub fn compute<F, R>(&self, f: F) -> R::Future
|
||||
where
|
||||
F: FnOnce() -> R + Send + 'static,
|
||||
R: IntoFuture + 'static,
|
||||
R::Future: Send + 'static,
|
||||
R::Item: Send + 'static,
|
||||
R::Error: Send + 'static,
|
||||
{
|
||||
f().into_future()
|
||||
}
|
||||
|
||||
pub fn scope<F, R>(&self, elements: usize, f: F) -> R
|
||||
where
|
||||
F: FnOnce(&DummyScope, usize) -> R,
|
||||
{
|
||||
f(&DummyScope, elements)
|
||||
}
|
||||
}
|
||||
|
||||
pub struct WorkerFuture<T, E> {
|
||||
future: future::FutureResult<T, E>,
|
||||
}
|
||||
|
||||
impl<T: Send + 'static, E: Send + 'static> Future for WorkerFuture<T, E> {
|
||||
type Item = T;
|
||||
type Error = E;
|
||||
|
||||
fn poll(&mut self) -> Poll<Self::Item, Self::Error> {
|
||||
self.future.poll()
|
||||
}
|
||||
}
|
||||
|
||||
pub struct DummyScope;
|
||||
|
||||
impl DummyScope {
|
||||
pub fn spawn<F: FnOnce(&DummyScope)>(&self, f: F) {
|
||||
f(self);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub use self::implementation::*;
|
|
@ -1,340 +0,0 @@
|
|||
use super::multicore::Worker;
|
||||
use bit_vec::{self, BitVec};
|
||||
use ff::{Endianness, Field, PrimeField};
|
||||
use futures::Future;
|
||||
use group::prime::{PrimeCurve, PrimeCurveAffine};
|
||||
use std::io;
|
||||
use std::iter;
|
||||
use std::ops::AddAssign;
|
||||
use std::sync::Arc;
|
||||
|
||||
use super::SynthesisError;
|
||||
|
||||
/// An object that builds a source of bases.
|
||||
pub trait SourceBuilder<G: PrimeCurveAffine>: Send + Sync + 'static + Clone {
|
||||
type Source: Source<G>;
|
||||
|
||||
fn new(self) -> Self::Source;
|
||||
}
|
||||
|
||||
/// A source of bases, like an iterator.
|
||||
pub trait Source<G: PrimeCurveAffine> {
|
||||
fn next(&mut self) -> Result<&G, SynthesisError>;
|
||||
|
||||
/// Skips `amt` elements from the source, avoiding deserialization.
|
||||
fn skip(&mut self, amt: usize) -> Result<(), SynthesisError>;
|
||||
}
|
||||
|
||||
pub trait AddAssignFromSource: PrimeCurve {
|
||||
/// Parses the element from the source. Fails if the point is at infinity.
|
||||
fn add_assign_from_source<S: Source<<Self as PrimeCurve>::Affine>>(
|
||||
&mut self,
|
||||
source: &mut S,
|
||||
) -> Result<(), SynthesisError> {
|
||||
AddAssign::<&<Self as PrimeCurve>::Affine>::add_assign(self, source.next()?);
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
impl<G> AddAssignFromSource for G where G: PrimeCurve {}
|
||||
|
||||
impl<G: PrimeCurveAffine> SourceBuilder<G> for (Arc<Vec<G>>, usize) {
|
||||
type Source = (Arc<Vec<G>>, usize);
|
||||
|
||||
fn new(self) -> (Arc<Vec<G>>, usize) {
|
||||
(self.0.clone(), self.1)
|
||||
}
|
||||
}
|
||||
|
||||
impl<G: PrimeCurveAffine> Source<G> for (Arc<Vec<G>>, usize) {
|
||||
fn next(&mut self) -> Result<&G, SynthesisError> {
|
||||
if self.0.len() <= self.1 {
|
||||
return Err(io::Error::new(
|
||||
io::ErrorKind::UnexpectedEof,
|
||||
"expected more bases from source",
|
||||
)
|
||||
.into());
|
||||
}
|
||||
|
||||
if self.0[self.1].is_identity().into() {
|
||||
return Err(SynthesisError::UnexpectedIdentity);
|
||||
}
|
||||
|
||||
let ret = &self.0[self.1];
|
||||
self.1 += 1;
|
||||
|
||||
Ok(ret)
|
||||
}
|
||||
|
||||
fn skip(&mut self, amt: usize) -> Result<(), SynthesisError> {
|
||||
if self.0.len() <= self.1 {
|
||||
return Err(io::Error::new(
|
||||
io::ErrorKind::UnexpectedEof,
|
||||
"expected more bases from source",
|
||||
)
|
||||
.into());
|
||||
}
|
||||
|
||||
self.1 += amt;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
pub trait QueryDensity {
|
||||
/// Returns whether the base exists.
|
||||
type Iter: Iterator<Item = bool>;
|
||||
|
||||
fn iter(self) -> Self::Iter;
|
||||
fn get_query_size(self) -> Option<usize>;
|
||||
}
|
||||
|
||||
#[derive(Clone)]
|
||||
pub struct FullDensity;
|
||||
|
||||
impl AsRef<FullDensity> for FullDensity {
|
||||
fn as_ref(&self) -> &FullDensity {
|
||||
self
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> QueryDensity for &'a FullDensity {
|
||||
type Iter = iter::Repeat<bool>;
|
||||
|
||||
fn iter(self) -> Self::Iter {
|
||||
iter::repeat(true)
|
||||
}
|
||||
|
||||
fn get_query_size(self) -> Option<usize> {
|
||||
None
|
||||
}
|
||||
}
|
||||
|
||||
pub struct DensityTracker {
|
||||
bv: BitVec,
|
||||
total_density: usize,
|
||||
}
|
||||
|
||||
impl<'a> QueryDensity for &'a DensityTracker {
|
||||
type Iter = bit_vec::Iter<'a>;
|
||||
|
||||
fn iter(self) -> Self::Iter {
|
||||
self.bv.iter()
|
||||
}
|
||||
|
||||
fn get_query_size(self) -> Option<usize> {
|
||||
Some(self.bv.len())
|
||||
}
|
||||
}
|
||||
|
||||
impl DensityTracker {
|
||||
pub fn new() -> DensityTracker {
|
||||
DensityTracker {
|
||||
bv: BitVec::new(),
|
||||
total_density: 0,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn add_element(&mut self) {
|
||||
self.bv.push(false);
|
||||
}
|
||||
|
||||
pub fn inc(&mut self, idx: usize) {
|
||||
if !self.bv.get(idx).unwrap() {
|
||||
self.bv.set(idx, true);
|
||||
self.total_density += 1;
|
||||
}
|
||||
}
|
||||
|
||||
pub fn get_total_density(&self) -> usize {
|
||||
self.total_density
|
||||
}
|
||||
}
|
||||
|
||||
fn multiexp_inner<Q, D, G, S>(
|
||||
pool: &Worker,
|
||||
bases: S,
|
||||
density_map: D,
|
||||
exponents: Arc<Vec<G::Scalar>>,
|
||||
mut skip: u32,
|
||||
c: u32,
|
||||
handle_trivial: bool,
|
||||
) -> Box<dyn Future<Item = G, Error = SynthesisError>>
|
||||
where
|
||||
for<'a> &'a Q: QueryDensity,
|
||||
D: Send + Sync + 'static + Clone + AsRef<Q>,
|
||||
G: PrimeCurve,
|
||||
S: SourceBuilder<<G as PrimeCurve>::Affine>,
|
||||
{
|
||||
// Perform this region of the multiexp
|
||||
let this = {
|
||||
let bases = bases.clone();
|
||||
let exponents = exponents.clone();
|
||||
let density_map = density_map.clone();
|
||||
|
||||
pool.compute(move || {
|
||||
// Accumulate the result
|
||||
let mut acc = G::identity();
|
||||
|
||||
// Build a source for the bases
|
||||
let mut bases = bases.new();
|
||||
|
||||
// Create space for the buckets
|
||||
let mut buckets = vec![G::identity(); (1 << c) - 1];
|
||||
|
||||
let one = G::Scalar::one();
|
||||
|
||||
// Sort the bases into buckets
|
||||
for (&exp, density) in exponents.iter().zip(density_map.as_ref().iter()) {
|
||||
if density {
|
||||
if exp.is_zero() {
|
||||
bases.skip(1)?;
|
||||
} else if exp == one {
|
||||
if handle_trivial {
|
||||
acc.add_assign_from_source(&mut bases)?;
|
||||
} else {
|
||||
bases.skip(1)?;
|
||||
}
|
||||
} else {
|
||||
let mut exp = exp.to_repr();
|
||||
<G::Scalar as PrimeField>::ReprEndianness::toggle_little_endian(&mut exp);
|
||||
|
||||
let exp = exp
|
||||
.as_ref()
|
||||
.into_iter()
|
||||
.map(|b| (0..8).map(move |i| (b >> i) & 1u8))
|
||||
.flatten()
|
||||
.skip(skip as usize)
|
||||
.take(c as usize)
|
||||
.enumerate()
|
||||
.fold(0u64, |acc, (i, b)| acc + ((b as u64) << i));
|
||||
|
||||
if exp != 0 {
|
||||
(&mut buckets[(exp - 1) as usize])
|
||||
.add_assign_from_source(&mut bases)?;
|
||||
} else {
|
||||
bases.skip(1)?;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Summation by parts
|
||||
// e.g. 3a + 2b + 1c = a +
|
||||
// (a) + b +
|
||||
// ((a) + b) + c
|
||||
let mut running_sum = G::identity();
|
||||
for exp in buckets.into_iter().rev() {
|
||||
running_sum.add_assign(&exp);
|
||||
acc.add_assign(&running_sum);
|
||||
}
|
||||
|
||||
Ok(acc)
|
||||
})
|
||||
};
|
||||
|
||||
skip += c;
|
||||
|
||||
if skip >= G::Scalar::NUM_BITS {
|
||||
// There isn't another region.
|
||||
Box::new(this)
|
||||
} else {
|
||||
// There's another region more significant. Calculate and join it with
|
||||
// this region recursively.
|
||||
Box::new(
|
||||
this.join(multiexp_inner(
|
||||
pool,
|
||||
bases,
|
||||
density_map,
|
||||
exponents,
|
||||
skip,
|
||||
c,
|
||||
false,
|
||||
))
|
||||
.map(move |(this, mut higher): (_, G)| {
|
||||
for _ in 0..c {
|
||||
higher = higher.double();
|
||||
}
|
||||
|
||||
higher.add_assign(&this);
|
||||
|
||||
higher
|
||||
}),
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
/// Perform multi-exponentiation. The caller is responsible for ensuring the
|
||||
/// query size is the same as the number of exponents.
|
||||
pub fn multiexp<Q, D, G, S>(
|
||||
pool: &Worker,
|
||||
bases: S,
|
||||
density_map: D,
|
||||
exponents: Arc<Vec<G::Scalar>>,
|
||||
) -> Box<dyn Future<Item = G, Error = SynthesisError>>
|
||||
where
|
||||
for<'a> &'a Q: QueryDensity,
|
||||
D: Send + Sync + 'static + Clone + AsRef<Q>,
|
||||
G: PrimeCurve,
|
||||
S: SourceBuilder<<G as PrimeCurve>::Affine>,
|
||||
{
|
||||
let c = if exponents.len() < 32 {
|
||||
3u32
|
||||
} else {
|
||||
(f64::from(exponents.len() as u32)).ln().ceil() as u32
|
||||
};
|
||||
|
||||
if let Some(query_size) = density_map.as_ref().get_query_size() {
|
||||
// If the density map has a known query size, it should not be
|
||||
// inconsistent with the number of exponents.
|
||||
|
||||
assert!(query_size == exponents.len());
|
||||
}
|
||||
|
||||
multiexp_inner(pool, bases, density_map, exponents, 0, c, true)
|
||||
}
|
||||
|
||||
#[cfg(feature = "pairing")]
|
||||
#[test]
|
||||
fn test_with_bls12() {
|
||||
fn naive_multiexp<G: PrimeCurve>(
|
||||
bases: Arc<Vec<<G as PrimeCurve>::Affine>>,
|
||||
exponents: Arc<Vec<G::Scalar>>,
|
||||
) -> G {
|
||||
assert_eq!(bases.len(), exponents.len());
|
||||
|
||||
let mut acc = G::identity();
|
||||
|
||||
for (base, exp) in bases.iter().zip(exponents.iter()) {
|
||||
AddAssign::<&G>::add_assign(&mut acc, &(*base * *exp));
|
||||
}
|
||||
|
||||
acc
|
||||
}
|
||||
|
||||
use bls12_381::{Bls12, Scalar};
|
||||
use group::{Curve, Group};
|
||||
use pairing::Engine;
|
||||
use rand;
|
||||
|
||||
const SAMPLES: usize = 1 << 14;
|
||||
|
||||
let rng = &mut rand::thread_rng();
|
||||
let v = Arc::new(
|
||||
(0..SAMPLES)
|
||||
.map(|_| Scalar::random(rng))
|
||||
.collect::<Vec<_>>(),
|
||||
);
|
||||
let g = Arc::new(
|
||||
(0..SAMPLES)
|
||||
.map(|_| <Bls12 as Engine>::G1::random(rng).to_affine())
|
||||
.collect::<Vec<_>>(),
|
||||
);
|
||||
|
||||
let naive: <Bls12 as Engine>::G1 = naive_multiexp(g.clone(), v.clone());
|
||||
|
||||
let pool = Worker::new();
|
||||
|
||||
let fast = multiexp(&pool, (g, 0), FullDensity, v).wait().unwrap();
|
||||
|
||||
assert_eq!(naive, fast);
|
||||
}
|
|
@ -1,226 +0,0 @@
|
|||
// For randomness (during paramgen and proof generation)
|
||||
use rand::thread_rng;
|
||||
|
||||
// For benchmarking
|
||||
use std::time::{Duration, Instant};
|
||||
|
||||
// Bring in some tools for using finite fiels
|
||||
use ff::{Field, PrimeField};
|
||||
|
||||
// We're going to use the BLS12-381 pairing-friendly elliptic curve.
|
||||
use bls12_381::{Bls12, Scalar};
|
||||
|
||||
// We'll use these interfaces to construct our circuit.
|
||||
use bellman::{Circuit, ConstraintSystem, SynthesisError};
|
||||
|
||||
// We're going to use the Groth16 proving system.
|
||||
use bellman::groth16::{
|
||||
create_random_proof, generate_random_parameters, prepare_verifying_key, verify_proof, Proof,
|
||||
};
|
||||
|
||||
const MIMC_ROUNDS: usize = 322;
|
||||
|
||||
/// This is an implementation of MiMC, specifically a
|
||||
/// variant named `LongsightF322p3` for BLS12-381.
|
||||
/// See http://eprint.iacr.org/2016/492 for more
|
||||
/// information about this construction.
|
||||
///
|
||||
/// ```
|
||||
/// function LongsightF322p3(xL ⦂ Fp, xR ⦂ Fp) {
|
||||
/// for i from 0 up to 321 {
|
||||
/// xL, xR := xR + (xL + Ci)^3, xL
|
||||
/// }
|
||||
/// return xL
|
||||
/// }
|
||||
/// ```
|
||||
fn mimc<Scalar: PrimeField>(mut xl: Scalar, mut xr: Scalar, constants: &[Scalar]) -> Scalar {
|
||||
assert_eq!(constants.len(), MIMC_ROUNDS);
|
||||
|
||||
for i in 0..MIMC_ROUNDS {
|
||||
let mut tmp1 = xl;
|
||||
tmp1.add_assign(&constants[i]);
|
||||
let mut tmp2 = tmp1.square();
|
||||
tmp2.mul_assign(&tmp1);
|
||||
tmp2.add_assign(&xr);
|
||||
xr = xl;
|
||||
xl = tmp2;
|
||||
}
|
||||
|
||||
xl
|
||||
}
|
||||
|
||||
/// This is our demo circuit for proving knowledge of the
|
||||
/// preimage of a MiMC hash invocation.
|
||||
struct MiMCDemo<'a, Scalar: PrimeField> {
|
||||
xl: Option<Scalar>,
|
||||
xr: Option<Scalar>,
|
||||
constants: &'a [Scalar],
|
||||
}
|
||||
|
||||
/// Our demo circuit implements this `Circuit` trait which
|
||||
/// is used during paramgen and proving in order to
|
||||
/// synthesize the constraint system.
|
||||
impl<'a, Scalar: PrimeField> Circuit<Scalar> for MiMCDemo<'a, Scalar> {
|
||||
fn synthesize<CS: ConstraintSystem<Scalar>>(self, cs: &mut CS) -> Result<(), SynthesisError> {
|
||||
assert_eq!(self.constants.len(), MIMC_ROUNDS);
|
||||
|
||||
// Allocate the first component of the preimage.
|
||||
let mut xl_value = self.xl;
|
||||
let mut xl = cs.alloc(
|
||||
|| "preimage xl",
|
||||
|| xl_value.ok_or(SynthesisError::AssignmentMissing),
|
||||
)?;
|
||||
|
||||
// Allocate the second component of the preimage.
|
||||
let mut xr_value = self.xr;
|
||||
let mut xr = cs.alloc(
|
||||
|| "preimage xr",
|
||||
|| xr_value.ok_or(SynthesisError::AssignmentMissing),
|
||||
)?;
|
||||
|
||||
for i in 0..MIMC_ROUNDS {
|
||||
// xL, xR := xR + (xL + Ci)^3, xL
|
||||
let cs = &mut cs.namespace(|| format!("round {}", i));
|
||||
|
||||
// tmp = (xL + Ci)^2
|
||||
let tmp_value = xl_value.map(|mut e| {
|
||||
e.add_assign(&self.constants[i]);
|
||||
e.square()
|
||||
});
|
||||
let tmp = cs.alloc(
|
||||
|| "tmp",
|
||||
|| tmp_value.ok_or(SynthesisError::AssignmentMissing),
|
||||
)?;
|
||||
|
||||
cs.enforce(
|
||||
|| "tmp = (xL + Ci)^2",
|
||||
|lc| lc + xl + (self.constants[i], CS::one()),
|
||||
|lc| lc + xl + (self.constants[i], CS::one()),
|
||||
|lc| lc + tmp,
|
||||
);
|
||||
|
||||
// new_xL = xR + (xL + Ci)^3
|
||||
// new_xL = xR + tmp * (xL + Ci)
|
||||
// new_xL - xR = tmp * (xL + Ci)
|
||||
let new_xl_value = xl_value.map(|mut e| {
|
||||
e.add_assign(&self.constants[i]);
|
||||
e.mul_assign(&tmp_value.unwrap());
|
||||
e.add_assign(&xr_value.unwrap());
|
||||
e
|
||||
});
|
||||
|
||||
let new_xl = if i == (MIMC_ROUNDS - 1) {
|
||||
// This is the last round, xL is our image and so
|
||||
// we allocate a public input.
|
||||
cs.alloc_input(
|
||||
|| "image",
|
||||
|| new_xl_value.ok_or(SynthesisError::AssignmentMissing),
|
||||
)?
|
||||
} else {
|
||||
cs.alloc(
|
||||
|| "new_xl",
|
||||
|| new_xl_value.ok_or(SynthesisError::AssignmentMissing),
|
||||
)?
|
||||
};
|
||||
|
||||
cs.enforce(
|
||||
|| "new_xL = xR + (xL + Ci)^3",
|
||||
|lc| lc + tmp,
|
||||
|lc| lc + xl + (self.constants[i], CS::one()),
|
||||
|lc| lc + new_xl - xr,
|
||||
);
|
||||
|
||||
// xR = xL
|
||||
xr = xl;
|
||||
xr_value = xl_value;
|
||||
|
||||
// xL = new_xL
|
||||
xl = new_xl;
|
||||
xl_value = new_xl_value;
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_mimc() {
|
||||
// This may not be cryptographically safe, use
|
||||
// `OsRng` (for example) in production software.
|
||||
let rng = &mut thread_rng();
|
||||
|
||||
// Generate the MiMC round constants
|
||||
let constants = (0..MIMC_ROUNDS)
|
||||
.map(|_| Scalar::random(rng))
|
||||
.collect::<Vec<_>>();
|
||||
|
||||
println!("Creating parameters...");
|
||||
|
||||
// Create parameters for our circuit
|
||||
let params = {
|
||||
let c = MiMCDemo {
|
||||
xl: None,
|
||||
xr: None,
|
||||
constants: &constants,
|
||||
};
|
||||
|
||||
generate_random_parameters::<Bls12, _, _>(c, rng).unwrap()
|
||||
};
|
||||
|
||||
// Prepare the verification key (for proof verification)
|
||||
let pvk = prepare_verifying_key(¶ms.vk);
|
||||
|
||||
println!("Creating proofs...");
|
||||
|
||||
// Let's benchmark stuff!
|
||||
const SAMPLES: u32 = 50;
|
||||
let mut total_proving = Duration::new(0, 0);
|
||||
let mut total_verifying = Duration::new(0, 0);
|
||||
|
||||
// Just a place to put the proof data, so we can
|
||||
// benchmark deserialization.
|
||||
let mut proof_vec = vec![];
|
||||
|
||||
for _ in 0..SAMPLES {
|
||||
// Generate a random preimage and compute the image
|
||||
let xl = Scalar::random(rng);
|
||||
let xr = Scalar::random(rng);
|
||||
let image = mimc(xl, xr, &constants);
|
||||
|
||||
proof_vec.truncate(0);
|
||||
|
||||
let start = Instant::now();
|
||||
{
|
||||
// Create an instance of our circuit (with the
|
||||
// witness)
|
||||
let c = MiMCDemo {
|
||||
xl: Some(xl),
|
||||
xr: Some(xr),
|
||||
constants: &constants,
|
||||
};
|
||||
|
||||
// Create a groth16 proof with our parameters.
|
||||
let proof = create_random_proof(c, ¶ms, rng).unwrap();
|
||||
|
||||
proof.write(&mut proof_vec).unwrap();
|
||||
}
|
||||
|
||||
total_proving += start.elapsed();
|
||||
|
||||
let start = Instant::now();
|
||||
let proof = Proof::read(&proof_vec[..]).unwrap();
|
||||
// Check the proof
|
||||
assert!(verify_proof(&pvk, &proof, &[image]).is_ok());
|
||||
total_verifying += start.elapsed();
|
||||
}
|
||||
let proving_avg = total_proving / SAMPLES;
|
||||
let proving_avg =
|
||||
proving_avg.subsec_nanos() as f64 / 1_000_000_000f64 + (proving_avg.as_secs() as f64);
|
||||
|
||||
let verifying_avg = total_verifying / SAMPLES;
|
||||
let verifying_avg =
|
||||
verifying_avg.subsec_nanos() as f64 / 1_000_000_000f64 + (verifying_avg.as_secs() as f64);
|
||||
|
||||
println!("Average proving time: {:?} seconds", proving_avg);
|
||||
println!("Average verifying time: {:?} seconds", verifying_avg);
|
||||
}
|
|
@ -1,105 +0,0 @@
|
|||
name: CI checks
|
||||
|
||||
on: [push, pull_request]
|
||||
|
||||
jobs:
|
||||
lint:
|
||||
name: Lint
|
||||
runs-on: ubuntu-latest
|
||||
|
||||
steps:
|
||||
- uses: actions/checkout@v1
|
||||
- uses: actions-rs/toolchain@v1
|
||||
with:
|
||||
toolchain: 1.36.0
|
||||
override: true
|
||||
|
||||
# Ensure all code has been formatted with rustfmt
|
||||
- run: rustup component add rustfmt
|
||||
- name: Check formatting
|
||||
uses: actions-rs/cargo@v1
|
||||
with:
|
||||
command: fmt
|
||||
args: -- --check --color always
|
||||
|
||||
test:
|
||||
name: Test on ${{ matrix.os }}
|
||||
runs-on: ${{ matrix.os }}
|
||||
strategy:
|
||||
matrix:
|
||||
os: [ubuntu-latest, windows-latest, macOS-latest]
|
||||
|
||||
steps:
|
||||
- uses: actions/checkout@v1
|
||||
- uses: actions-rs/toolchain@v1
|
||||
with:
|
||||
toolchain: 1.36.0
|
||||
override: true
|
||||
- name: cargo fetch
|
||||
uses: actions-rs/cargo@v1
|
||||
with:
|
||||
command: fetch
|
||||
- name: Build tests
|
||||
uses: actions-rs/cargo@v1
|
||||
with:
|
||||
command: build
|
||||
args: --verbose --release --tests --features endo
|
||||
- name: Run tests
|
||||
uses: actions-rs/cargo@v1
|
||||
with:
|
||||
command: test
|
||||
args: --verbose --release --features endo
|
||||
- name: Build tests (no endomorphism)
|
||||
uses: actions-rs/cargo@v1
|
||||
with:
|
||||
command: build
|
||||
args: --verbose --release --tests
|
||||
- name: Run tests (no endomorphism)
|
||||
uses: actions-rs/cargo@v1
|
||||
with:
|
||||
command: test
|
||||
args: --verbose --release
|
||||
|
||||
no-std:
|
||||
name: Check no-std compatibility
|
||||
runs-on: ubuntu-latest
|
||||
|
||||
steps:
|
||||
- uses: actions/checkout@v1
|
||||
- uses: actions-rs/toolchain@v1
|
||||
with:
|
||||
toolchain: 1.36.0
|
||||
override: true
|
||||
- run: rustup target add thumbv6m-none-eabi
|
||||
- name: cargo fetch
|
||||
uses: actions-rs/cargo@v1
|
||||
with:
|
||||
command: fetch
|
||||
- name: Build
|
||||
uses: actions-rs/cargo@v1
|
||||
with:
|
||||
command: build
|
||||
args: --verbose --target thumbv6m-none-eabi --no-default-features --features groups,pairings
|
||||
|
||||
doc-links:
|
||||
name: Nightly lint
|
||||
runs-on: ubuntu-latest
|
||||
|
||||
steps:
|
||||
- uses: actions/checkout@v1
|
||||
- uses: actions-rs/toolchain@v1
|
||||
with:
|
||||
toolchain: nightly
|
||||
override: true
|
||||
- name: cargo fetch
|
||||
uses: actions-rs/cargo@v1
|
||||
with:
|
||||
command: fetch
|
||||
|
||||
# Ensure intra-documentation links all resolve correctly
|
||||
# Requires #![deny(intra_doc_link_resolution_failure)] in crate.
|
||||
- name: Check intra-doc links
|
||||
uses: actions-rs/cargo@v1
|
||||
with:
|
||||
command: doc
|
||||
args: --document-private-items
|
|
@ -1,3 +0,0 @@
|
|||
/target
|
||||
**/*.rs.bk
|
||||
Cargo.lock
|
|
@ -1,14 +0,0 @@
|
|||
Copyrights in the "bls12_381" library are retained by their contributors. No
|
||||
copyright assignment is required to contribute to the "bls12_381" library.
|
||||
|
||||
The "bls12_381" library is licensed under either of
|
||||
|
||||
* Apache License, Version 2.0, (see ./LICENSE-APACHE or http://www.apache.org/licenses/LICENSE-2.0)
|
||||
* MIT license (see ./LICENSE-MIT or http://opensource.org/licenses/MIT)
|
||||
|
||||
at your option.
|
||||
|
||||
Unless you explicitly state otherwise, any contribution intentionally
|
||||
submitted for inclusion in the work by you, as defined in the Apache-2.0
|
||||
license, shall be dual licensed as above, without any additional terms or
|
||||
conditions.
|
|
@ -1,62 +0,0 @@
|
|||
[package]
|
||||
authors = [
|
||||
"Sean Bowe <ewillbefull@gmail.com>",
|
||||
"Jack Grigg <thestr4d@gmail.com>",
|
||||
]
|
||||
description = "Implementation of the BLS12-381 pairing-friendly elliptic curve construction"
|
||||
documentation = "https://docs.rs/bls12_381/"
|
||||
homepage = "https://github.com/zkcrypto/bls12_381"
|
||||
license = "MIT/Apache-2.0"
|
||||
name = "bls12_381"
|
||||
repository = "https://github.com/zkcrypto/bls12_381"
|
||||
version = "0.2.0"
|
||||
edition = "2018"
|
||||
|
||||
[package.metadata.docs.rs]
|
||||
rustdoc-args = [ "--html-in-header", "katex-header.html" ]
|
||||
|
||||
[dev-dependencies]
|
||||
criterion = "0.3"
|
||||
|
||||
[[bench]]
|
||||
name = "groups"
|
||||
harness = false
|
||||
required-features = ["groups"]
|
||||
|
||||
[dependencies.byteorder]
|
||||
version = "1"
|
||||
default-features = false
|
||||
|
||||
[dependencies.ff]
|
||||
path = "../ff"
|
||||
version = "0.7"
|
||||
default-features = false
|
||||
|
||||
[dependencies.group]
|
||||
path = "../group"
|
||||
version = "0.7"
|
||||
default-features = false
|
||||
optional = true
|
||||
|
||||
[dependencies.pairing]
|
||||
path = "../pairing"
|
||||
version = "0.17"
|
||||
optional = true
|
||||
|
||||
[dependencies.rand_core]
|
||||
version = "0.5"
|
||||
default-features = false
|
||||
|
||||
[dependencies.subtle]
|
||||
version = "2.2.1"
|
||||
default-features = false
|
||||
|
||||
[features]
|
||||
default = ["groups", "pairings", "alloc"]
|
||||
groups = ["group"]
|
||||
pairings = ["groups", "pairing"]
|
||||
alloc = []
|
||||
nightly = ["subtle/nightly"]
|
||||
|
||||
# GLV patents US7110538B2 and US7995752B2 expire in September 2020.
|
||||
endo = []
|
|
@ -1,201 +0,0 @@
|
|||
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.
|
|
@ -1,23 +0,0 @@
|
|||
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.
|
|
@ -1,64 +0,0 @@
|
|||
# bls12_381 [![Crates.io](https://img.shields.io/crates/v/bls12_381.svg)](https://crates.io/crates/bls12_381) #
|
||||
|
||||
This crate provides an implementation of the BLS12-381 pairing-friendly elliptic curve construction.
|
||||
|
||||
* **This implementation has not been reviewed or audited. Use at your own risk.**
|
||||
* This implementation targets Rust `1.36` or later.
|
||||
* This implementation does not require the Rust standard library.
|
||||
* All operations are constant time unless explicitly noted.
|
||||
|
||||
## Features
|
||||
|
||||
* `groups` (on by default): Enables APIs for performing group arithmetic with G1, G2, and GT.
|
||||
* `pairings` (on by default): Enables some APIs for performing pairings.
|
||||
* `alloc` (on by default): Enables APIs that require an allocator; these include pairing optimizations.
|
||||
* `nightly`: Enables `subtle/nightly` which tries to prevent compiler optimizations that could jeopardize constant time operations. Requires the nightly Rust compiler.
|
||||
* `endo`: Enables optimizations that leverage curve endomorphisms, which may run foul of patents US7110538B2 and US7995752B2 set to expire in September 2020.
|
||||
|
||||
## [Documentation](https://docs.rs/bls12_381)
|
||||
|
||||
## Curve Description
|
||||
|
||||
BLS12-381 is a pairing-friendly elliptic curve construction from the [BLS family](https://eprint.iacr.org/2002/088), with embedding degree 12. It is built over a 381-bit prime field `GF(p)` with...
|
||||
|
||||
* z = `-0xd201000000010000`
|
||||
* p = (z - 1)<sup>2</sup>(z<sup>4</sup> - z<sup>2</sup> + 1) / 3 + z
|
||||
* = `0x1a0111ea397fe69a4b1ba7b6434bacd764774b84f38512bf6730d2a0f6b0f6241eabfffeb153ffffb9feffffffffaaab`
|
||||
* q = z<sup>4</sup> - z<sup>2</sup> + 1
|
||||
* = `0x73eda753299d7d483339d80809a1d80553bda402fffe5bfeffffffff00000001`
|
||||
|
||||
... yielding two **source groups** G<sub>1</sub> and G<sub>2</sub>, each of 255-bit prime order `q`, such that an efficiently computable non-degenerate bilinear pairing function `e` exists into a third **target group** G<sub>T</sub>. Specifically, G<sub>1</sub> is the `q`-order subgroup of E(F<sub>p</sub>) : y<sup>2</sup> = x<sup>3</sup> + 4 and G<sub>2</sub> is the `q`-order subgroup of E'(F<sub>p<sup>2</sup></sub>) : y<sup>2</sup> = x<sup>3</sup> + 4(u + 1) where the extension field F<sub>p<sup>2</sup></sub> is defined as F<sub>p</sub>(u) / (u<sup>2</sup> + 1).
|
||||
|
||||
BLS12-381 is chosen so that `z` has small Hamming weight (to improve pairing performance) and also so that `GF(q)` has a large 2<sup>32</sup> primitive root of unity for performing radix-2 fast Fourier transforms for efficient multi-point evaluation and interpolation. It is also chosen so that it exists in a particularly efficient and rigid subfamily of BLS12 curves.
|
||||
|
||||
### Curve Security
|
||||
|
||||
Pairing-friendly elliptic curve constructions are (necessarily) less secure than conventional elliptic curves due to their small "embedding degree". Given a small enough embedding degree, the pairing function itself would allow for a break in DLP hardness if it projected into a weak target group, as weaknesses in this target group are immediately translated into weaknesses in the source group.
|
||||
|
||||
In order to achieve reasonable security without an unreasonably expensive pairing function, a careful choice of embedding degree, base field characteristic and prime subgroup order must be made. BLS12-381 uses an embedding degree of 12 to ensure fast pairing performance but a choice of a 381-bit base field characteristic to yield a 255-bit subgroup order (for protection against [Pollard's rho algorithm](https://en.wikipedia.org/wiki/Pollard%27s_rho_algorithm)) while reaching close to a 128-bit security level.
|
||||
|
||||
There are [known optimizations](https://ellipticnews.wordpress.com/2016/05/02/kim-barbulescu-variant-of-the-number-field-sieve-to-compute-discrete-logarithms-in-finite-fields/) of the [Number Field Sieve algorithm](https://en.wikipedia.org/wiki/General_number_field_sieve) which could be used to weaken DLP security in the target group by taking advantage of its structure, as it is a multiplicative subgroup of a low-degree extension field. However, these attacks require an (as of yet unknown) efficient algorithm for scanning a large space of polynomials. Even if the attack were practical it would only reduce security to roughly 117 to 120 bits. (This contrasts with 254-bit BN curves which usually have less than 100 bits of security in the same situation.)
|
||||
|
||||
### Alternative Curves
|
||||
|
||||
Applications may wish to exchange pairing performance and/or G<sub>2</sub> performance by using BLS24 or KSS16 curves which conservatively target 128-bit security. In applications that need cycles of elliptic curves for e.g. arbitrary proof composition, MNT6/MNT4 curve cycles are known that target the 128-bit security level. In applications that only need fixed-depth proof composition, curves of this form have been constructed as part of Zexe.
|
||||
|
||||
## Acknowledgements
|
||||
|
||||
Please see `Cargo.toml` for a list of primary authors of this codebase.
|
||||
|
||||
## License
|
||||
|
||||
Licensed under either of
|
||||
|
||||
* Apache License, Version 2.0, ([LICENSE-APACHE](LICENSE-APACHE) or http://www.apache.org/licenses/LICENSE-2.0)
|
||||
* MIT license ([LICENSE-MIT](LICENSE-MIT) or http://opensource.org/licenses/MIT)
|
||||
|
||||
at your option.
|
||||
|
||||
### Contribution
|
||||
|
||||
Unless you explicitly state otherwise, any contribution intentionally
|
||||
submitted for inclusion in the work by you, as defined in the Apache-2.0
|
||||
license, shall be dual licensed as above, without any additional terms or
|
||||
conditions.
|
|
@ -1,44 +0,0 @@
|
|||
# 0.2.0
|
||||
|
||||
This release adds implementations of the `ff`, `group`, and `pairing` traits (with the
|
||||
latter two being gated by the `groups` and `pairings` feature flags respectively).
|
||||
Additional trait implementations (for standard traits) have been added where the `ff`,
|
||||
`group`, and `pairing` trait bounds require them.
|
||||
|
||||
## Added
|
||||
* `bls12_381::Bls12`, a `pairing::Engine` for BLS12-381 pairing operations. It implements
|
||||
the following traits:
|
||||
* `pairing::{Engine, MultiMillerLoop}`
|
||||
* New trait implementations for `bls12_381::G1Projective`:
|
||||
* `group::{Curve, Group, GroupEncoding, WnafGroup}`
|
||||
* `group::prime::{PrimeCurve, PrimeGroup}`
|
||||
* New trait implementations for `bls12_381::G1Affine`:
|
||||
* `group::{GroupEncoding, UncompressedEncoding}`
|
||||
* `group::prime::PrimeCurveAffine`
|
||||
* `pairing::PairingCurveAffine`
|
||||
* New trait implementations for `bls12_381::G2Projective`:
|
||||
* `group::{Curve, Group, GroupEncoding, WnafGroup}`
|
||||
* `group::prime::{PrimeCurve, PrimeGroup}`
|
||||
* New trait implementations for `bls12_381::G2Affine`:
|
||||
* `group::{GroupEncoding, UncompressedEncoding}`
|
||||
* `group::prime::PrimeCurveAffine`
|
||||
* `pairing::PairingCurveAffine`
|
||||
* New trait implementations for `bls12_381::Gt`:
|
||||
* `group::Group`
|
||||
* New trait implementations for `bls12_381::MillerLoopResult`:
|
||||
* `pairing::MillerLoopResult`
|
||||
* New trait implementations for `bls12_381::Scalar`:
|
||||
* `ff::{Field, PrimeField}`
|
||||
|
||||
# 0.1.1
|
||||
|
||||
Added `clear_cofactor` methods to `G1Projective` and `G2Projective`. If the crate feature `endo`
|
||||
is enabled the G2 cofactor clearing will use the curve endomorphism technique described by
|
||||
[Budroni-Pintore](https://ia.cr/2017/419). If the crate feature `endo` is _not_ enabled then
|
||||
the code will simulate the effects of the Budroni-Pintore cofactor clearing in order to keep
|
||||
the API consistent. In September 2020, when patents US7110538B2 and US7995752B2 expire, the
|
||||
endo feature will be made default. However, for now it must be explicitly enabled.
|
||||
|
||||
# 0.1.0
|
||||
|
||||
Initial release.
|
|
@ -1,170 +0,0 @@
|
|||
#[macro_use]
|
||||
extern crate criterion;
|
||||
|
||||
extern crate bls12_381;
|
||||
use bls12_381::*;
|
||||
|
||||
use criterion::{black_box, Criterion};
|
||||
|
||||
fn criterion_benchmark(c: &mut Criterion) {
|
||||
// Pairings
|
||||
{
|
||||
let g = G1Affine::generator();
|
||||
let h = G2Affine::generator();
|
||||
c.bench_function("full pairing", move |b| {
|
||||
b.iter(|| pairing(black_box(&g), black_box(&h)))
|
||||
});
|
||||
c.bench_function("G2 preparation for pairing", move |b| {
|
||||
b.iter(|| G2Prepared::from(h))
|
||||
});
|
||||
let prep = G2Prepared::from(h);
|
||||
c.bench_function("miller loop for pairing", move |b| {
|
||||
b.iter(|| multi_miller_loop(&[(&g, &prep)]))
|
||||
});
|
||||
let prep = G2Prepared::from(h);
|
||||
let r = multi_miller_loop(&[(&g, &prep)]);
|
||||
c.bench_function("final exponentiation for pairing", move |b| {
|
||||
b.iter(|| r.final_exponentiation())
|
||||
});
|
||||
}
|
||||
// G1Affine
|
||||
{
|
||||
let name = "G1Affine";
|
||||
let a = G1Affine::generator();
|
||||
let s = Scalar::from_raw([1, 2, 3, 4]);
|
||||
let compressed = [0u8; 48];
|
||||
let uncompressed = [0u8; 96];
|
||||
c.bench_function(&format!("{} check on curve", name), move |b| {
|
||||
b.iter(|| black_box(a).is_on_curve())
|
||||
});
|
||||
c.bench_function(&format!("{} check equality", name), move |b| {
|
||||
b.iter(|| black_box(a) == black_box(a))
|
||||
});
|
||||
c.bench_function(&format!("{} scalar multiplication", name), move |b| {
|
||||
b.iter(|| black_box(a) * black_box(s))
|
||||
});
|
||||
c.bench_function(&format!("{} subgroup check", name), move |b| {
|
||||
b.iter(|| black_box(a).is_torsion_free())
|
||||
});
|
||||
c.bench_function(
|
||||
&format!("{} deserialize compressed point", name),
|
||||
move |b| b.iter(|| G1Affine::from_compressed(black_box(&compressed))),
|
||||
);
|
||||
c.bench_function(
|
||||
&format!("{} deserialize uncompressed point", name),
|
||||
move |b| b.iter(|| G1Affine::from_uncompressed(black_box(&uncompressed))),
|
||||
);
|
||||
}
|
||||
|
||||
// G1Projective
|
||||
{
|
||||
let name = "G1Projective";
|
||||
let a = G1Projective::generator();
|
||||
let a_affine = G1Affine::generator();
|
||||
let s = Scalar::from_raw([1, 2, 3, 4]);
|
||||
|
||||
const N: usize = 10000;
|
||||
let v = vec![G1Projective::generator(); N];
|
||||
let mut q = vec![G1Affine::identity(); N];
|
||||
|
||||
c.bench_function(&format!("{} check on curve", name), move |b| {
|
||||
b.iter(|| black_box(a).is_on_curve())
|
||||
});
|
||||
c.bench_function(&format!("{} check equality", name), move |b| {
|
||||
b.iter(|| black_box(a) == black_box(a))
|
||||
});
|
||||
c.bench_function(&format!("{} to affine", name), move |b| {
|
||||
b.iter(|| G1Affine::from(black_box(a)))
|
||||
});
|
||||
c.bench_function(&format!("{} doubling", name), move |b| {
|
||||
b.iter(|| black_box(a).double())
|
||||
});
|
||||
c.bench_function(&format!("{} addition", name), move |b| {
|
||||
b.iter(|| black_box(a).add(&a))
|
||||
});
|
||||
c.bench_function(&format!("{} mixed addition", name), move |b| {
|
||||
b.iter(|| black_box(a).add_mixed(&a_affine))
|
||||
});
|
||||
c.bench_function(&format!("{} scalar multiplication", name), move |b| {
|
||||
b.iter(|| black_box(a) * black_box(s))
|
||||
});
|
||||
c.bench_function(&format!("{} batch to affine n={}", name, N), move |b| {
|
||||
b.iter(|| {
|
||||
G1Projective::batch_normalize(black_box(&v), black_box(&mut q));
|
||||
black_box(&q)[0]
|
||||
})
|
||||
});
|
||||
}
|
||||
|
||||
// G2Affine
|
||||
{
|
||||
let name = "G2Affine";
|
||||
let a = G2Affine::generator();
|
||||
let s = Scalar::from_raw([1, 2, 3, 4]);
|
||||
let compressed = [0u8; 96];
|
||||
let uncompressed = [0u8; 192];
|
||||
c.bench_function(&format!("{} check on curve", name), move |b| {
|
||||
b.iter(|| black_box(a).is_on_curve())
|
||||
});
|
||||
c.bench_function(&format!("{} check equality", name), move |b| {
|
||||
b.iter(|| black_box(a) == black_box(a))
|
||||
});
|
||||
c.bench_function(&format!("{} scalar multiplication", name), move |b| {
|
||||
b.iter(|| black_box(a) * black_box(s))
|
||||
});
|
||||
c.bench_function(&format!("{} subgroup check", name), move |b| {
|
||||
b.iter(|| black_box(a).is_torsion_free())
|
||||
});
|
||||
c.bench_function(
|
||||
&format!("{} deserialize compressed point", name),
|
||||
move |b| b.iter(|| G2Affine::from_compressed(black_box(&compressed))),
|
||||
);
|
||||
c.bench_function(
|
||||
&format!("{} deserialize uncompressed point", name),
|
||||
move |b| b.iter(|| G2Affine::from_uncompressed(black_box(&uncompressed))),
|
||||
);
|
||||
}
|
||||
|
||||
// G2Projective
|
||||
{
|
||||
let name = "G2Projective";
|
||||
let a = G2Projective::generator();
|
||||
let a_affine = G2Affine::generator();
|
||||
let s = Scalar::from_raw([1, 2, 3, 4]);
|
||||
|
||||
const N: usize = 10000;
|
||||
let v = vec![G2Projective::generator(); N];
|
||||
let mut q = vec![G2Affine::identity(); N];
|
||||
|
||||
c.bench_function(&format!("{} check on curve", name), move |b| {
|
||||
b.iter(|| black_box(a).is_on_curve())
|
||||
});
|
||||
c.bench_function(&format!("{} check equality", name), move |b| {
|
||||
b.iter(|| black_box(a) == black_box(a))
|
||||
});
|
||||
c.bench_function(&format!("{} to affine", name), move |b| {
|
||||
b.iter(|| G2Affine::from(black_box(a)))
|
||||
});
|
||||
c.bench_function(&format!("{} doubling", name), move |b| {
|
||||
b.iter(|| black_box(a).double())
|
||||
});
|
||||
c.bench_function(&format!("{} addition", name), move |b| {
|
||||
b.iter(|| black_box(a).add(&a))
|
||||
});
|
||||
c.bench_function(&format!("{} mixed addition", name), move |b| {
|
||||
b.iter(|| black_box(a).add_mixed(&a_affine))
|
||||
});
|
||||
c.bench_function(&format!("{} scalar multiplication", name), move |b| {
|
||||
b.iter(|| black_box(a) * black_box(s))
|
||||
});
|
||||
c.bench_function(&format!("{} batch to affine n={}", name, N), move |b| {
|
||||
b.iter(|| {
|
||||
G2Projective::batch_normalize(black_box(&v), black_box(&mut q));
|
||||
black_box(&q)[0]
|
||||
})
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
criterion_group!(benches, criterion_benchmark);
|
||||
criterion_main!(benches);
|
|
@ -1,15 +0,0 @@
|
|||
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/katex@0.10.0/dist/katex.min.css" integrity="sha384-9eLZqc9ds8eNjO3TmqPeYcDj8n+Qfa4nuSiGYa6DjLNcv9BtN69ZIulL9+8CqC9Y" crossorigin="anonymous">
|
||||
<script src="https://cdn.jsdelivr.net/npm/katex@0.10.0/dist/katex.min.js" integrity="sha384-K3vbOmF2BtaVai+Qk37uypf7VrgBubhQreNQe9aGsz9lB63dIFiQVlJbr92dw2Lx" crossorigin="anonymous"></script>
|
||||
<script src="https://cdn.jsdelivr.net/npm/katex@0.10.0/dist/contrib/auto-render.min.js" integrity="sha384-kmZOZB5ObwgQnS/DuDg6TScgOiWWBiVt0plIRkZCmE6rDZGrEOQeHM5PcHi+nyqe" crossorigin="anonymous"></script>
|
||||
<script>
|
||||
document.addEventListener("DOMContentLoaded", function() {
|
||||
renderMathInElement(document.body, {
|
||||
delimiters: [
|
||||
{left: "$$", right: "$$", display: true},
|
||||
{left: "\\(", right: "\\)", display: false},
|
||||
{left: "$", right: "$", display: false},
|
||||
{left: "\\[", right: "\\]", display: true}
|
||||
]
|
||||
});
|
||||
});
|
||||
</script>
|
|
@ -1 +0,0 @@
|
|||
1.36.0
|
|
@ -1,916 +0,0 @@
|
|||
//! This module provides an implementation of the BLS12-381 base field `GF(p)`
|
||||
//! where `p = 0x1a0111ea397fe69a4b1ba7b6434bacd764774b84f38512bf6730d2a0f6b0f6241eabfffeb153ffffb9feffffffffaaab`
|
||||
|
||||
use core::convert::TryFrom;
|
||||
use core::fmt;
|
||||
use core::ops::{Add, AddAssign, Mul, MulAssign, Neg, Sub, SubAssign};
|
||||
use rand_core::RngCore;
|
||||
use subtle::{Choice, ConditionallySelectable, ConstantTimeEq, CtOption};
|
||||
|
||||
use crate::util::{adc, mac, sbb};
|
||||
|
||||
// The internal representation of this type is six 64-bit unsigned
|
||||
// integers in little-endian order. `Fp` values are always in
|
||||
// Montgomery form; i.e., Scalar(a) = aR mod p, with R = 2^384.
|
||||
#[derive(Copy, Clone)]
|
||||
pub struct Fp([u64; 6]);
|
||||
|
||||
impl fmt::Debug for Fp {
|
||||
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
||||
let tmp = self.to_bytes();
|
||||
write!(f, "0x")?;
|
||||
for &b in tmp.iter() {
|
||||
write!(f, "{:02x}", b)?;
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
impl Default for Fp {
|
||||
fn default() -> Self {
|
||||
Fp::zero()
|
||||
}
|
||||
}
|
||||
|
||||
impl ConstantTimeEq for Fp {
|
||||
fn ct_eq(&self, other: &Self) -> Choice {
|
||||
self.0[0].ct_eq(&other.0[0])
|
||||
& self.0[1].ct_eq(&other.0[1])
|
||||
& self.0[2].ct_eq(&other.0[2])
|
||||
& self.0[3].ct_eq(&other.0[3])
|
||||
& self.0[4].ct_eq(&other.0[4])
|
||||
& self.0[5].ct_eq(&other.0[5])
|
||||
}
|
||||
}
|
||||
|
||||
impl Eq for Fp {}
|
||||
impl PartialEq for Fp {
|
||||
#[inline]
|
||||
fn eq(&self, other: &Self) -> bool {
|
||||
bool::from(self.ct_eq(other))
|
||||
}
|
||||
}
|
||||
|
||||
impl ConditionallySelectable for Fp {
|
||||
fn conditional_select(a: &Self, b: &Self, choice: Choice) -> Self {
|
||||
Fp([
|
||||
u64::conditional_select(&a.0[0], &b.0[0], choice),
|
||||
u64::conditional_select(&a.0[1], &b.0[1], choice),
|
||||
u64::conditional_select(&a.0[2], &b.0[2], choice),
|
||||
u64::conditional_select(&a.0[3], &b.0[3], choice),
|
||||
u64::conditional_select(&a.0[4], &b.0[4], choice),
|
||||
u64::conditional_select(&a.0[5], &b.0[5], choice),
|
||||
])
|
||||
}
|
||||
}
|
||||
|
||||
/// p = 4002409555221667393417789825735904156556882819939007885332058136124031650490837864442687629129015664037894272559787
|
||||
const MODULUS: [u64; 6] = [
|
||||
0xb9fe_ffff_ffff_aaab,
|
||||
0x1eab_fffe_b153_ffff,
|
||||
0x6730_d2a0_f6b0_f624,
|
||||
0x6477_4b84_f385_12bf,
|
||||
0x4b1b_a7b6_434b_acd7,
|
||||
0x1a01_11ea_397f_e69a,
|
||||
];
|
||||
|
||||
/// INV = -(p^{-1} mod 2^64) mod 2^64
|
||||
const INV: u64 = 0x89f3_fffc_fffc_fffd;
|
||||
|
||||
/// R = 2^384 mod p
|
||||
const R: Fp = Fp([
|
||||
0x7609_0000_0002_fffd,
|
||||
0xebf4_000b_c40c_0002,
|
||||
0x5f48_9857_53c7_58ba,
|
||||
0x77ce_5853_7052_5745,
|
||||
0x5c07_1a97_a256_ec6d,
|
||||
0x15f6_5ec3_fa80_e493,
|
||||
]);
|
||||
|
||||
/// R2 = 2^(384*2) mod p
|
||||
const R2: Fp = Fp([
|
||||
0xf4df_1f34_1c34_1746,
|
||||
0x0a76_e6a6_09d1_04f1,
|
||||
0x8de5_476c_4c95_b6d5,
|
||||
0x67eb_88a9_939d_83c0,
|
||||
0x9a79_3e85_b519_952d,
|
||||
0x1198_8fe5_92ca_e3aa,
|
||||
]);
|
||||
|
||||
/// R3 = 2^(384*3) mod p
|
||||
const R3: Fp = Fp([
|
||||
0xed48_ac6b_d94c_a1e0,
|
||||
0x315f_831e_03a7_adf8,
|
||||
0x9a53_352a_615e_29dd,
|
||||
0x34c0_4e5e_921e_1761,
|
||||
0x2512_d435_6572_4728,
|
||||
0x0aa6_3460_9175_5d4d,
|
||||
]);
|
||||
|
||||
impl<'a> Neg for &'a Fp {
|
||||
type Output = Fp;
|
||||
|
||||
#[inline]
|
||||
fn neg(self) -> Fp {
|
||||
self.neg()
|
||||
}
|
||||
}
|
||||
|
||||
impl Neg for Fp {
|
||||
type Output = Fp;
|
||||
|
||||
#[inline]
|
||||
fn neg(self) -> Fp {
|
||||
-&self
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a, 'b> Sub<&'b Fp> for &'a Fp {
|
||||
type Output = Fp;
|
||||
|
||||
#[inline]
|
||||
fn sub(self, rhs: &'b Fp) -> Fp {
|
||||
self.sub(rhs)
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a, 'b> Add<&'b Fp> for &'a Fp {
|
||||
type Output = Fp;
|
||||
|
||||
#[inline]
|
||||
fn add(self, rhs: &'b Fp) -> Fp {
|
||||
self.add(rhs)
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a, 'b> Mul<&'b Fp> for &'a Fp {
|
||||
type Output = Fp;
|
||||
|
||||
#[inline]
|
||||
fn mul(self, rhs: &'b Fp) -> Fp {
|
||||
self.mul(rhs)
|
||||
}
|
||||
}
|
||||
|
||||
impl_binops_additive!(Fp, Fp);
|
||||
impl_binops_multiplicative!(Fp, Fp);
|
||||
|
||||
impl Fp {
|
||||
/// Returns zero, the additive identity.
|
||||
#[inline]
|
||||
pub const fn zero() -> Fp {
|
||||
Fp([0, 0, 0, 0, 0, 0])
|
||||
}
|
||||
|
||||
/// Returns one, the multiplicative identity.
|
||||
#[inline]
|
||||
pub const fn one() -> Fp {
|
||||
R
|
||||
}
|
||||
|
||||
pub fn is_zero(&self) -> Choice {
|
||||
self.ct_eq(&Fp::zero())
|
||||
}
|
||||
|
||||
/// Attempts to convert a big-endian byte representation of
|
||||
/// a scalar into an `Fp`, failing if the input is not canonical.
|
||||
pub fn from_bytes(bytes: &[u8; 48]) -> CtOption<Fp> {
|
||||
let mut tmp = Fp([0, 0, 0, 0, 0, 0]);
|
||||
|
||||
tmp.0[5] = u64::from_be_bytes(<[u8; 8]>::try_from(&bytes[0..8]).unwrap());
|
||||
tmp.0[4] = u64::from_be_bytes(<[u8; 8]>::try_from(&bytes[8..16]).unwrap());
|
||||
tmp.0[3] = u64::from_be_bytes(<[u8; 8]>::try_from(&bytes[16..24]).unwrap());
|
||||
tmp.0[2] = u64::from_be_bytes(<[u8; 8]>::try_from(&bytes[24..32]).unwrap());
|
||||
tmp.0[1] = u64::from_be_bytes(<[u8; 8]>::try_from(&bytes[32..40]).unwrap());
|
||||
tmp.0[0] = u64::from_be_bytes(<[u8; 8]>::try_from(&bytes[40..48]).unwrap());
|
||||
|
||||
// Try to subtract the modulus
|
||||
let (_, borrow) = sbb(tmp.0[0], MODULUS[0], 0);
|
||||
let (_, borrow) = sbb(tmp.0[1], MODULUS[1], borrow);
|
||||
let (_, borrow) = sbb(tmp.0[2], MODULUS[2], borrow);
|
||||
let (_, borrow) = sbb(tmp.0[3], MODULUS[3], borrow);
|
||||
let (_, borrow) = sbb(tmp.0[4], MODULUS[4], borrow);
|
||||
let (_, borrow) = sbb(tmp.0[5], MODULUS[5], borrow);
|
||||
|
||||
// If the element is smaller than MODULUS then the
|
||||
// subtraction will underflow, producing a borrow value
|
||||
// of 0xffff...ffff. Otherwise, it'll be zero.
|
||||
let is_some = (borrow as u8) & 1;
|
||||
|
||||
// Convert to Montgomery form by computing
|
||||
// (a.R^0 * R^2) / R = a.R
|
||||
tmp *= &R2;
|
||||
|
||||
CtOption::new(tmp, Choice::from(is_some))
|
||||
}
|
||||
|
||||
/// Converts an element of `Fp` into a byte representation in
|
||||
/// big-endian byte order.
|
||||
pub fn to_bytes(&self) -> [u8; 48] {
|
||||
// Turn into canonical form by computing
|
||||
// (a.R) / R = a
|
||||
let tmp = Fp::montgomery_reduce(
|
||||
self.0[0], self.0[1], self.0[2], self.0[3], self.0[4], self.0[5], 0, 0, 0, 0, 0, 0,
|
||||
);
|
||||
|
||||
let mut res = [0; 48];
|
||||
res[0..8].copy_from_slice(&tmp.0[5].to_be_bytes());
|
||||
res[8..16].copy_from_slice(&tmp.0[4].to_be_bytes());
|
||||
res[16..24].copy_from_slice(&tmp.0[3].to_be_bytes());
|
||||
res[24..32].copy_from_slice(&tmp.0[2].to_be_bytes());
|
||||
res[32..40].copy_from_slice(&tmp.0[1].to_be_bytes());
|
||||
res[40..48].copy_from_slice(&tmp.0[0].to_be_bytes());
|
||||
|
||||
res
|
||||
}
|
||||
|
||||
pub(crate) fn random<R: RngCore + ?Sized>(rng: &mut R) -> Fp {
|
||||
let mut bytes = [0u8; 96];
|
||||
rng.fill_bytes(&mut bytes);
|
||||
|
||||
// Parse the random bytes as a big-endian number, to match Fp encoding order.
|
||||
Fp::from_u768([
|
||||
u64::from_be_bytes(<[u8; 8]>::try_from(&bytes[0..8]).unwrap()),
|
||||
u64::from_be_bytes(<[u8; 8]>::try_from(&bytes[8..16]).unwrap()),
|
||||
u64::from_be_bytes(<[u8; 8]>::try_from(&bytes[16..24]).unwrap()),
|
||||
u64::from_be_bytes(<[u8; 8]>::try_from(&bytes[24..32]).unwrap()),
|
||||
u64::from_be_bytes(<[u8; 8]>::try_from(&bytes[32..40]).unwrap()),
|
||||
u64::from_be_bytes(<[u8; 8]>::try_from(&bytes[40..48]).unwrap()),
|
||||
u64::from_be_bytes(<[u8; 8]>::try_from(&bytes[48..56]).unwrap()),
|
||||
u64::from_be_bytes(<[u8; 8]>::try_from(&bytes[56..64]).unwrap()),
|
||||
u64::from_be_bytes(<[u8; 8]>::try_from(&bytes[64..72]).unwrap()),
|
||||
u64::from_be_bytes(<[u8; 8]>::try_from(&bytes[72..80]).unwrap()),
|
||||
u64::from_be_bytes(<[u8; 8]>::try_from(&bytes[80..88]).unwrap()),
|
||||
u64::from_be_bytes(<[u8; 8]>::try_from(&bytes[88..96]).unwrap()),
|
||||
])
|
||||
}
|
||||
|
||||
/// Reduces a big-endian 64-bit limb representation of a 768-bit number.
|
||||
fn from_u768(limbs: [u64; 12]) -> Fp {
|
||||
// We reduce an arbitrary 768-bit number by decomposing it into two 384-bit digits
|
||||
// with the higher bits multiplied by 2^384. Thus, we perform two reductions
|
||||
//
|
||||
// 1. the lower bits are multiplied by R^2, as normal
|
||||
// 2. the upper bits are multiplied by R^2 * 2^384 = R^3
|
||||
//
|
||||
// and computing their sum in the field. It remains to see that arbitrary 384-bit
|
||||
// numbers can be placed into Montgomery form safely using the reduction. The
|
||||
// reduction works so long as the product is less than R=2^384 multiplied by
|
||||
// the modulus. This holds because for any `c` smaller than the modulus, we have
|
||||
// that (2^384 - 1)*c is an acceptable product for the reduction. Therefore, the
|
||||
// reduction always works so long as `c` is in the field; in this case it is either the
|
||||
// constant `R2` or `R3`.
|
||||
let d1 = Fp([limbs[11], limbs[10], limbs[9], limbs[8], limbs[7], limbs[6]]);
|
||||
let d0 = Fp([limbs[5], limbs[4], limbs[3], limbs[2], limbs[1], limbs[0]]);
|
||||
// Convert to Montgomery form
|
||||
d0 * R2 + d1 * R3
|
||||
}
|
||||
|
||||
/// Returns whether or not this element is strictly lexicographically
|
||||
/// larger than its negation.
|
||||
pub fn lexicographically_largest(&self) -> Choice {
|
||||
// This can be determined by checking to see if the element is
|
||||
// larger than (p - 1) // 2. If we subtract by ((p - 1) // 2) + 1
|
||||
// and there is no underflow, then the element must be larger than
|
||||
// (p - 1) // 2.
|
||||
|
||||
// First, because self is in Montgomery form we need to reduce it
|
||||
let tmp = Fp::montgomery_reduce(
|
||||
self.0[0], self.0[1], self.0[2], self.0[3], self.0[4], self.0[5], 0, 0, 0, 0, 0, 0,
|
||||
);
|
||||
|
||||
let (_, borrow) = sbb(tmp.0[0], 0xdcff_7fff_ffff_d556, 0);
|
||||
let (_, borrow) = sbb(tmp.0[1], 0x0f55_ffff_58a9_ffff, borrow);
|
||||
let (_, borrow) = sbb(tmp.0[2], 0xb398_6950_7b58_7b12, borrow);
|
||||
let (_, borrow) = sbb(tmp.0[3], 0xb23b_a5c2_79c2_895f, borrow);
|
||||
let (_, borrow) = sbb(tmp.0[4], 0x258d_d3db_21a5_d66b, borrow);
|
||||
let (_, borrow) = sbb(tmp.0[5], 0x0d00_88f5_1cbf_f34d, borrow);
|
||||
|
||||
// If the element was smaller, the subtraction will underflow
|
||||
// producing a borrow value of 0xffff...ffff, otherwise it will
|
||||
// be zero. We create a Choice representing true if there was
|
||||
// overflow (and so this element is not lexicographically larger
|
||||
// than its negation) and then negate it.
|
||||
|
||||
!Choice::from((borrow as u8) & 1)
|
||||
}
|
||||
|
||||
/// Constructs an element of `Fp` without checking that it is
|
||||
/// canonical.
|
||||
pub const fn from_raw_unchecked(v: [u64; 6]) -> Fp {
|
||||
Fp(v)
|
||||
}
|
||||
|
||||
/// Although this is labeled "vartime", it is only
|
||||
/// variable time with respect to the exponent. It
|
||||
/// is also not exposed in the public API.
|
||||
pub fn pow_vartime(&self, by: &[u64; 6]) -> Self {
|
||||
let mut res = Self::one();
|
||||
for e in by.iter().rev() {
|
||||
for i in (0..64).rev() {
|
||||
res = res.square();
|
||||
|
||||
if ((*e >> i) & 1) == 1 {
|
||||
res *= self;
|
||||
}
|
||||
}
|
||||
}
|
||||
res
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn sqrt(&self) -> CtOption<Self> {
|
||||
// We use Shank's method, as p = 3 (mod 4). This means
|
||||
// we only need to exponentiate by (p+1)/4. This only
|
||||
// works for elements that are actually quadratic residue,
|
||||
// so we check that we got the correct result at the end.
|
||||
|
||||
let sqrt = self.pow_vartime(&[
|
||||
0xee7f_bfff_ffff_eaab,
|
||||
0x07aa_ffff_ac54_ffff,
|
||||
0xd9cc_34a8_3dac_3d89,
|
||||
0xd91d_d2e1_3ce1_44af,
|
||||
0x92c6_e9ed_90d2_eb35,
|
||||
0x0680_447a_8e5f_f9a6,
|
||||
]);
|
||||
|
||||
CtOption::new(sqrt, sqrt.square().ct_eq(self))
|
||||
}
|
||||
|
||||
#[inline]
|
||||
/// Computes the multiplicative inverse of this field
|
||||
/// element, returning None in the case that this element
|
||||
/// is zero.
|
||||
pub fn invert(&self) -> CtOption<Self> {
|
||||
// Exponentiate by p - 2
|
||||
let t = self.pow_vartime(&[
|
||||
0xb9fe_ffff_ffff_aaa9,
|
||||
0x1eab_fffe_b153_ffff,
|
||||
0x6730_d2a0_f6b0_f624,
|
||||
0x6477_4b84_f385_12bf,
|
||||
0x4b1b_a7b6_434b_acd7,
|
||||
0x1a01_11ea_397f_e69a,
|
||||
]);
|
||||
|
||||
CtOption::new(t, !self.is_zero())
|
||||
}
|
||||
|
||||
#[inline]
|
||||
const fn subtract_p(&self) -> Fp {
|
||||
let (r0, borrow) = sbb(self.0[0], MODULUS[0], 0);
|
||||
let (r1, borrow) = sbb(self.0[1], MODULUS[1], borrow);
|
||||
let (r2, borrow) = sbb(self.0[2], MODULUS[2], borrow);
|
||||
let (r3, borrow) = sbb(self.0[3], MODULUS[3], borrow);
|
||||
let (r4, borrow) = sbb(self.0[4], MODULUS[4], borrow);
|
||||
let (r5, borrow) = sbb(self.0[5], MODULUS[5], borrow);
|
||||
|
||||
// If underflow occurred on the final limb, borrow = 0xfff...fff, otherwise
|
||||
// borrow = 0x000...000. Thus, we use it as a mask!
|
||||
let r0 = (self.0[0] & borrow) | (r0 & !borrow);
|
||||
let r1 = (self.0[1] & borrow) | (r1 & !borrow);
|
||||
let r2 = (self.0[2] & borrow) | (r2 & !borrow);
|
||||
let r3 = (self.0[3] & borrow) | (r3 & !borrow);
|
||||
let r4 = (self.0[4] & borrow) | (r4 & !borrow);
|
||||
let r5 = (self.0[5] & borrow) | (r5 & !borrow);
|
||||
|
||||
Fp([r0, r1, r2, r3, r4, r5])
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub const fn add(&self, rhs: &Fp) -> Fp {
|
||||
let (d0, carry) = adc(self.0[0], rhs.0[0], 0);
|
||||
let (d1, carry) = adc(self.0[1], rhs.0[1], carry);
|
||||
let (d2, carry) = adc(self.0[2], rhs.0[2], carry);
|
||||
let (d3, carry) = adc(self.0[3], rhs.0[3], carry);
|
||||
let (d4, carry) = adc(self.0[4], rhs.0[4], carry);
|
||||
let (d5, _) = adc(self.0[5], rhs.0[5], carry);
|
||||
|
||||
// Attempt to subtract the modulus, to ensure the value
|
||||
// is smaller than the modulus.
|
||||
(&Fp([d0, d1, d2, d3, d4, d5])).subtract_p()
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub const fn neg(&self) -> Fp {
|
||||
let (d0, borrow) = sbb(MODULUS[0], self.0[0], 0);
|
||||
let (d1, borrow) = sbb(MODULUS[1], self.0[1], borrow);
|
||||
let (d2, borrow) = sbb(MODULUS[2], self.0[2], borrow);
|
||||
let (d3, borrow) = sbb(MODULUS[3], self.0[3], borrow);
|
||||
let (d4, borrow) = sbb(MODULUS[4], self.0[4], borrow);
|
||||
let (d5, _) = sbb(MODULUS[5], self.0[5], borrow);
|
||||
|
||||
// Let's use a mask if `self` was zero, which would mean
|
||||
// the result of the subtraction is p.
|
||||
let mask = (((self.0[0] | self.0[1] | self.0[2] | self.0[3] | self.0[4] | self.0[5]) == 0)
|
||||
as u64)
|
||||
.wrapping_sub(1);
|
||||
|
||||
Fp([
|
||||
d0 & mask,
|
||||
d1 & mask,
|
||||
d2 & mask,
|
||||
d3 & mask,
|
||||
d4 & mask,
|
||||
d5 & mask,
|
||||
])
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub const fn sub(&self, rhs: &Fp) -> Fp {
|
||||
(&rhs.neg()).add(self)
|
||||
}
|
||||
|
||||
#[inline(always)]
|
||||
const fn montgomery_reduce(
|
||||
t0: u64,
|
||||
t1: u64,
|
||||
t2: u64,
|
||||
t3: u64,
|
||||
t4: u64,
|
||||
t5: u64,
|
||||
t6: u64,
|
||||
t7: u64,
|
||||
t8: u64,
|
||||
t9: u64,
|
||||
t10: u64,
|
||||
t11: u64,
|
||||
) -> Self {
|
||||
// The Montgomery reduction here is based on Algorithm 14.32 in
|
||||
// Handbook of Applied Cryptography
|
||||
// <http://cacr.uwaterloo.ca/hac/about/chap14.pdf>.
|
||||
|
||||
let k = t0.wrapping_mul(INV);
|
||||
let (_, carry) = mac(t0, k, MODULUS[0], 0);
|
||||
let (r1, carry) = mac(t1, k, MODULUS[1], carry);
|
||||
let (r2, carry) = mac(t2, k, MODULUS[2], carry);
|
||||
let (r3, carry) = mac(t3, k, MODULUS[3], carry);
|
||||
let (r4, carry) = mac(t4, k, MODULUS[4], carry);
|
||||
let (r5, carry) = mac(t5, k, MODULUS[5], carry);
|
||||
let (r6, r7) = adc(t6, 0, carry);
|
||||
|
||||
let k = r1.wrapping_mul(INV);
|
||||
let (_, carry) = mac(r1, k, MODULUS[0], 0);
|
||||
let (r2, carry) = mac(r2, k, MODULUS[1], carry);
|
||||
let (r3, carry) = mac(r3, k, MODULUS[2], carry);
|
||||
let (r4, carry) = mac(r4, k, MODULUS[3], carry);
|
||||
let (r5, carry) = mac(r5, k, MODULUS[4], carry);
|
||||
let (r6, carry) = mac(r6, k, MODULUS[5], carry);
|
||||
let (r7, r8) = adc(t7, r7, carry);
|
||||
|
||||
let k = r2.wrapping_mul(INV);
|
||||
let (_, carry) = mac(r2, k, MODULUS[0], 0);
|
||||
let (r3, carry) = mac(r3, k, MODULUS[1], carry);
|
||||
let (r4, carry) = mac(r4, k, MODULUS[2], carry);
|
||||
let (r5, carry) = mac(r5, k, MODULUS[3], carry);
|
||||
let (r6, carry) = mac(r6, k, MODULUS[4], carry);
|
||||
let (r7, carry) = mac(r7, k, MODULUS[5], carry);
|
||||
let (r8, r9) = adc(t8, r8, carry);
|
||||
|
||||
let k = r3.wrapping_mul(INV);
|
||||
let (_, carry) = mac(r3, k, MODULUS[0], 0);
|
||||
let (r4, carry) = mac(r4, k, MODULUS[1], carry);
|
||||
let (r5, carry) = mac(r5, k, MODULUS[2], carry);
|
||||
let (r6, carry) = mac(r6, k, MODULUS[3], carry);
|
||||
let (r7, carry) = mac(r7, k, MODULUS[4], carry);
|
||||
let (r8, carry) = mac(r8, k, MODULUS[5], carry);
|
||||
let (r9, r10) = adc(t9, r9, carry);
|
||||
|
||||
let k = r4.wrapping_mul(INV);
|
||||
let (_, carry) = mac(r4, k, MODULUS[0], 0);
|
||||
let (r5, carry) = mac(r5, k, MODULUS[1], carry);
|
||||
let (r6, carry) = mac(r6, k, MODULUS[2], carry);
|
||||
let (r7, carry) = mac(r7, k, MODULUS[3], carry);
|
||||
let (r8, carry) = mac(r8, k, MODULUS[4], carry);
|
||||
let (r9, carry) = mac(r9, k, MODULUS[5], carry);
|
||||
let (r10, r11) = adc(t10, r10, carry);
|
||||
|
||||
let k = r5.wrapping_mul(INV);
|
||||
let (_, carry) = mac(r5, k, MODULUS[0], 0);
|
||||
let (r6, carry) = mac(r6, k, MODULUS[1], carry);
|
||||
let (r7, carry) = mac(r7, k, MODULUS[2], carry);
|
||||
let (r8, carry) = mac(r8, k, MODULUS[3], carry);
|
||||
let (r9, carry) = mac(r9, k, MODULUS[4], carry);
|
||||
let (r10, carry) = mac(r10, k, MODULUS[5], carry);
|
||||
let (r11, _) = adc(t11, r11, carry);
|
||||
|
||||
// Attempt to subtract the modulus, to ensure the value
|
||||
// is smaller than the modulus.
|
||||
(&Fp([r6, r7, r8, r9, r10, r11])).subtract_p()
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub const fn mul(&self, rhs: &Fp) -> Fp {
|
||||
let (t0, carry) = mac(0, self.0[0], rhs.0[0], 0);
|
||||
let (t1, carry) = mac(0, self.0[0], rhs.0[1], carry);
|
||||
let (t2, carry) = mac(0, self.0[0], rhs.0[2], carry);
|
||||
let (t3, carry) = mac(0, self.0[0], rhs.0[3], carry);
|
||||
let (t4, carry) = mac(0, self.0[0], rhs.0[4], carry);
|
||||
let (t5, t6) = mac(0, self.0[0], rhs.0[5], carry);
|
||||
|
||||
let (t1, carry) = mac(t1, self.0[1], rhs.0[0], 0);
|
||||
let (t2, carry) = mac(t2, self.0[1], rhs.0[1], carry);
|
||||
let (t3, carry) = mac(t3, self.0[1], rhs.0[2], carry);
|
||||
let (t4, carry) = mac(t4, self.0[1], rhs.0[3], carry);
|
||||
let (t5, carry) = mac(t5, self.0[1], rhs.0[4], carry);
|
||||
let (t6, t7) = mac(t6, self.0[1], rhs.0[5], carry);
|
||||
|
||||
let (t2, carry) = mac(t2, self.0[2], rhs.0[0], 0);
|
||||
let (t3, carry) = mac(t3, self.0[2], rhs.0[1], carry);
|
||||
let (t4, carry) = mac(t4, self.0[2], rhs.0[2], carry);
|
||||
let (t5, carry) = mac(t5, self.0[2], rhs.0[3], carry);
|
||||
let (t6, carry) = mac(t6, self.0[2], rhs.0[4], carry);
|
||||
let (t7, t8) = mac(t7, self.0[2], rhs.0[5], carry);
|
||||
|
||||
let (t3, carry) = mac(t3, self.0[3], rhs.0[0], 0);
|
||||
let (t4, carry) = mac(t4, self.0[3], rhs.0[1], carry);
|
||||
let (t5, carry) = mac(t5, self.0[3], rhs.0[2], carry);
|
||||
let (t6, carry) = mac(t6, self.0[3], rhs.0[3], carry);
|
||||
let (t7, carry) = mac(t7, self.0[3], rhs.0[4], carry);
|
||||
let (t8, t9) = mac(t8, self.0[3], rhs.0[5], carry);
|
||||
|
||||
let (t4, carry) = mac(t4, self.0[4], rhs.0[0], 0);
|
||||
let (t5, carry) = mac(t5, self.0[4], rhs.0[1], carry);
|
||||
let (t6, carry) = mac(t6, self.0[4], rhs.0[2], carry);
|
||||
let (t7, carry) = mac(t7, self.0[4], rhs.0[3], carry);
|
||||
let (t8, carry) = mac(t8, self.0[4], rhs.0[4], carry);
|
||||
let (t9, t10) = mac(t9, self.0[4], rhs.0[5], carry);
|
||||
|
||||
let (t5, carry) = mac(t5, self.0[5], rhs.0[0], 0);
|
||||
let (t6, carry) = mac(t6, self.0[5], rhs.0[1], carry);
|
||||
let (t7, carry) = mac(t7, self.0[5], rhs.0[2], carry);
|
||||
let (t8, carry) = mac(t8, self.0[5], rhs.0[3], carry);
|
||||
let (t9, carry) = mac(t9, self.0[5], rhs.0[4], carry);
|
||||
let (t10, t11) = mac(t10, self.0[5], rhs.0[5], carry);
|
||||
|
||||
Self::montgomery_reduce(t0, t1, t2, t3, t4, t5, t6, t7, t8, t9, t10, t11)
|
||||
}
|
||||
|
||||
/// Squares this element.
|
||||
#[inline]
|
||||
pub const fn square(&self) -> Self {
|
||||
let (t1, carry) = mac(0, self.0[0], self.0[1], 0);
|
||||
let (t2, carry) = mac(0, self.0[0], self.0[2], carry);
|
||||
let (t3, carry) = mac(0, self.0[0], self.0[3], carry);
|
||||
let (t4, carry) = mac(0, self.0[0], self.0[4], carry);
|
||||
let (t5, t6) = mac(0, self.0[0], self.0[5], carry);
|
||||
|
||||
let (t3, carry) = mac(t3, self.0[1], self.0[2], 0);
|
||||
let (t4, carry) = mac(t4, self.0[1], self.0[3], carry);
|
||||
let (t5, carry) = mac(t5, self.0[1], self.0[4], carry);
|
||||
let (t6, t7) = mac(t6, self.0[1], self.0[5], carry);
|
||||
|
||||
let (t5, carry) = mac(t5, self.0[2], self.0[3], 0);
|
||||
let (t6, carry) = mac(t6, self.0[2], self.0[4], carry);
|
||||
let (t7, t8) = mac(t7, self.0[2], self.0[5], carry);
|
||||
|
||||
let (t7, carry) = mac(t7, self.0[3], self.0[4], 0);
|
||||
let (t8, t9) = mac(t8, self.0[3], self.0[5], carry);
|
||||
|
||||
let (t9, t10) = mac(t9, self.0[4], self.0[5], 0);
|
||||
|
||||
let t11 = t10 >> 63;
|
||||
let t10 = (t10 << 1) | (t9 >> 63);
|
||||
let t9 = (t9 << 1) | (t8 >> 63);
|
||||
let t8 = (t8 << 1) | (t7 >> 63);
|
||||
let t7 = (t7 << 1) | (t6 >> 63);
|
||||
let t6 = (t6 << 1) | (t5 >> 63);
|
||||
let t5 = (t5 << 1) | (t4 >> 63);
|
||||
let t4 = (t4 << 1) | (t3 >> 63);
|
||||
let t3 = (t3 << 1) | (t2 >> 63);
|
||||
let t2 = (t2 << 1) | (t1 >> 63);
|
||||
let t1 = t1 << 1;
|
||||
|
||||
let (t0, carry) = mac(0, self.0[0], self.0[0], 0);
|
||||
let (t1, carry) = adc(t1, 0, carry);
|
||||
let (t2, carry) = mac(t2, self.0[1], self.0[1], carry);
|
||||
let (t3, carry) = adc(t3, 0, carry);
|
||||
let (t4, carry) = mac(t4, self.0[2], self.0[2], carry);
|
||||
let (t5, carry) = adc(t5, 0, carry);
|
||||
let (t6, carry) = mac(t6, self.0[3], self.0[3], carry);
|
||||
let (t7, carry) = adc(t7, 0, carry);
|
||||
let (t8, carry) = mac(t8, self.0[4], self.0[4], carry);
|
||||
let (t9, carry) = adc(t9, 0, carry);
|
||||
let (t10, carry) = mac(t10, self.0[5], self.0[5], carry);
|
||||
let (t11, _) = adc(t11, 0, carry);
|
||||
|
||||
Self::montgomery_reduce(t0, t1, t2, t3, t4, t5, t6, t7, t8, t9, t10, t11)
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_conditional_selection() {
|
||||
let a = Fp([1, 2, 3, 4, 5, 6]);
|
||||
let b = Fp([7, 8, 9, 10, 11, 12]);
|
||||
|
||||
assert_eq!(
|
||||
ConditionallySelectable::conditional_select(&a, &b, Choice::from(0u8)),
|
||||
a
|
||||
);
|
||||
assert_eq!(
|
||||
ConditionallySelectable::conditional_select(&a, &b, Choice::from(1u8)),
|
||||
b
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_equality() {
|
||||
fn is_equal(a: &Fp, b: &Fp) -> bool {
|
||||
let eq = a == b;
|
||||
let ct_eq = a.ct_eq(&b);
|
||||
|
||||
assert_eq!(eq, bool::from(ct_eq));
|
||||
|
||||
eq
|
||||
}
|
||||
|
||||
assert!(is_equal(&Fp([1, 2, 3, 4, 5, 6]), &Fp([1, 2, 3, 4, 5, 6])));
|
||||
|
||||
assert!(!is_equal(&Fp([7, 2, 3, 4, 5, 6]), &Fp([1, 2, 3, 4, 5, 6])));
|
||||
assert!(!is_equal(&Fp([1, 7, 3, 4, 5, 6]), &Fp([1, 2, 3, 4, 5, 6])));
|
||||
assert!(!is_equal(&Fp([1, 2, 7, 4, 5, 6]), &Fp([1, 2, 3, 4, 5, 6])));
|
||||
assert!(!is_equal(&Fp([1, 2, 3, 7, 5, 6]), &Fp([1, 2, 3, 4, 5, 6])));
|
||||
assert!(!is_equal(&Fp([1, 2, 3, 4, 7, 6]), &Fp([1, 2, 3, 4, 5, 6])));
|
||||
assert!(!is_equal(&Fp([1, 2, 3, 4, 5, 7]), &Fp([1, 2, 3, 4, 5, 6])));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_squaring() {
|
||||
let a = Fp([
|
||||
0xd215_d276_8e83_191b,
|
||||
0x5085_d80f_8fb2_8261,
|
||||
0xce9a_032d_df39_3a56,
|
||||
0x3e9c_4fff_2ca0_c4bb,
|
||||
0x6436_b6f7_f4d9_5dfb,
|
||||
0x1060_6628_ad4a_4d90,
|
||||
]);
|
||||
let b = Fp([
|
||||
0x33d9_c42a_3cb3_e235,
|
||||
0xdad1_1a09_4c4c_d455,
|
||||
0xa2f1_44bd_729a_aeba,
|
||||
0xd415_0932_be9f_feac,
|
||||
0xe27b_c7c4_7d44_ee50,
|
||||
0x14b6_a78d_3ec7_a560,
|
||||
]);
|
||||
|
||||
assert_eq!(a.square(), b);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_multiplication() {
|
||||
let a = Fp([
|
||||
0x0397_a383_2017_0cd4,
|
||||
0x734c_1b2c_9e76_1d30,
|
||||
0x5ed2_55ad_9a48_beb5,
|
||||
0x095a_3c6b_22a7_fcfc,
|
||||
0x2294_ce75_d4e2_6a27,
|
||||
0x1333_8bd8_7001_1ebb,
|
||||
]);
|
||||
let b = Fp([
|
||||
0xb9c3_c7c5_b119_6af7,
|
||||
0x2580_e208_6ce3_35c1,
|
||||
0xf49a_ed3d_8a57_ef42,
|
||||
0x41f2_81e4_9846_e878,
|
||||
0xe076_2346_c384_52ce,
|
||||
0x0652_e893_26e5_7dc0,
|
||||
]);
|
||||
let c = Fp([
|
||||
0xf96e_f3d7_11ab_5355,
|
||||
0xe8d4_59ea_00f1_48dd,
|
||||
0x53f7_354a_5f00_fa78,
|
||||
0x9e34_a4f3_125c_5f83,
|
||||
0x3fbe_0c47_ca74_c19e,
|
||||
0x01b0_6a8b_bd4a_dfe4,
|
||||
]);
|
||||
|
||||
assert_eq!(a * b, c);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_addition() {
|
||||
let a = Fp([
|
||||
0x5360_bb59_7867_8032,
|
||||
0x7dd2_75ae_799e_128e,
|
||||
0x5c5b_5071_ce4f_4dcf,
|
||||
0xcdb2_1f93_078d_bb3e,
|
||||
0xc323_65c5_e73f_474a,
|
||||
0x115a_2a54_89ba_be5b,
|
||||
]);
|
||||
let b = Fp([
|
||||
0x9fd2_8773_3d23_dda0,
|
||||
0xb16b_f2af_738b_3554,
|
||||
0x3e57_a75b_d3cc_6d1d,
|
||||
0x900b_c0bd_627f_d6d6,
|
||||
0xd319_a080_efb2_45fe,
|
||||
0x15fd_caa4_e4bb_2091,
|
||||
]);
|
||||
let c = Fp([
|
||||
0x3934_42cc_b58b_b327,
|
||||
0x1092_685f_3bd5_47e3,
|
||||
0x3382_252c_ab6a_c4c9,
|
||||
0xf946_94cb_7688_7f55,
|
||||
0x4b21_5e90_93a5_e071,
|
||||
0x0d56_e30f_34f5_f853,
|
||||
]);
|
||||
|
||||
assert_eq!(a + b, c);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_subtraction() {
|
||||
let a = Fp([
|
||||
0x5360_bb59_7867_8032,
|
||||
0x7dd2_75ae_799e_128e,
|
||||
0x5c5b_5071_ce4f_4dcf,
|
||||
0xcdb2_1f93_078d_bb3e,
|
||||
0xc323_65c5_e73f_474a,
|
||||
0x115a_2a54_89ba_be5b,
|
||||
]);
|
||||
let b = Fp([
|
||||
0x9fd2_8773_3d23_dda0,
|
||||
0xb16b_f2af_738b_3554,
|
||||
0x3e57_a75b_d3cc_6d1d,
|
||||
0x900b_c0bd_627f_d6d6,
|
||||
0xd319_a080_efb2_45fe,
|
||||
0x15fd_caa4_e4bb_2091,
|
||||
]);
|
||||
let c = Fp([
|
||||
0x6d8d_33e6_3b43_4d3d,
|
||||
0xeb12_82fd_b766_dd39,
|
||||
0x8534_7bb6_f133_d6d5,
|
||||
0xa21d_aa5a_9892_f727,
|
||||
0x3b25_6cfb_3ad8_ae23,
|
||||
0x155d_7199_de7f_8464,
|
||||
]);
|
||||
|
||||
assert_eq!(a - b, c);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_negation() {
|
||||
let a = Fp([
|
||||
0x5360_bb59_7867_8032,
|
||||
0x7dd2_75ae_799e_128e,
|
||||
0x5c5b_5071_ce4f_4dcf,
|
||||
0xcdb2_1f93_078d_bb3e,
|
||||
0xc323_65c5_e73f_474a,
|
||||
0x115a_2a54_89ba_be5b,
|
||||
]);
|
||||
let b = Fp([
|
||||
0x669e_44a6_8798_2a79,
|
||||
0xa0d9_8a50_37b5_ed71,
|
||||
0x0ad5_822f_2861_a854,
|
||||
0x96c5_2bf1_ebf7_5781,
|
||||
0x87f8_41f0_5c0c_658c,
|
||||
0x08a6_e795_afc5_283e,
|
||||
]);
|
||||
|
||||
assert_eq!(-a, b);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_debug() {
|
||||
assert_eq!(
|
||||
format!(
|
||||
"{:?}",
|
||||
Fp([
|
||||
0x5360_bb59_7867_8032,
|
||||
0x7dd2_75ae_799e_128e,
|
||||
0x5c5b_5071_ce4f_4dcf,
|
||||
0xcdb2_1f93_078d_bb3e,
|
||||
0xc323_65c5_e73f_474a,
|
||||
0x115a_2a54_89ba_be5b,
|
||||
])
|
||||
),
|
||||
"0x104bf052ad3bc99bcb176c24a06a6c3aad4eaf2308fc4d282e106c84a757d061052630515305e59bdddf8111bfdeb704"
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_from_bytes() {
|
||||
let mut a = Fp([
|
||||
0xdc90_6d9b_e3f9_5dc8,
|
||||
0x8755_caf7_4596_91a1,
|
||||
0xcff1_a7f4_e958_3ab3,
|
||||
0x9b43_821f_849e_2284,
|
||||
0xf575_54f3_a297_4f3f,
|
||||
0x085d_bea8_4ed4_7f79,
|
||||
]);
|
||||
|
||||
for _ in 0..100 {
|
||||
a = a.square();
|
||||
let tmp = a.to_bytes();
|
||||
let b = Fp::from_bytes(&tmp).unwrap();
|
||||
|
||||
assert_eq!(a, b);
|
||||
}
|
||||
|
||||
assert_eq!(
|
||||
-Fp::one(),
|
||||
Fp::from_bytes(&[
|
||||
26, 1, 17, 234, 57, 127, 230, 154, 75, 27, 167, 182, 67, 75, 172, 215, 100, 119, 75,
|
||||
132, 243, 133, 18, 191, 103, 48, 210, 160, 246, 176, 246, 36, 30, 171, 255, 254, 177,
|
||||
83, 255, 255, 185, 254, 255, 255, 255, 255, 170, 170
|
||||
])
|
||||
.unwrap()
|
||||
);
|
||||
|
||||
assert!(bool::from(
|
||||
Fp::from_bytes(&[
|
||||
27, 1, 17, 234, 57, 127, 230, 154, 75, 27, 167, 182, 67, 75, 172, 215, 100, 119, 75,
|
||||
132, 243, 133, 18, 191, 103, 48, 210, 160, 246, 176, 246, 36, 30, 171, 255, 254, 177,
|
||||
83, 255, 255, 185, 254, 255, 255, 255, 255, 170, 170
|
||||
])
|
||||
.is_none()
|
||||
));
|
||||
|
||||
assert!(bool::from(Fp::from_bytes(&[0xff; 48]).is_none()));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_sqrt() {
|
||||
// a = 4
|
||||
let a = Fp::from_raw_unchecked([
|
||||
0xaa27_0000_000c_fff3,
|
||||
0x53cc_0032_fc34_000a,
|
||||
0x478f_e97a_6b0a_807f,
|
||||
0xb1d3_7ebe_e6ba_24d7,
|
||||
0x8ec9_733b_bf78_ab2f,
|
||||
0x09d6_4551_3d83_de7e,
|
||||
]);
|
||||
|
||||
assert_eq!(
|
||||
// sqrt(4) = -2
|
||||
-a.sqrt().unwrap(),
|
||||
// 2
|
||||
Fp::from_raw_unchecked([
|
||||
0x3213_0000_0006_554f,
|
||||
0xb93c_0018_d6c4_0005,
|
||||
0x5760_5e0d_b0dd_bb51,
|
||||
0x8b25_6521_ed1f_9bcb,
|
||||
0x6cf2_8d79_0162_2c03,
|
||||
0x11eb_ab9d_bb81_e28c,
|
||||
])
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_inversion() {
|
||||
let a = Fp([
|
||||
0x43b4_3a50_78ac_2076,
|
||||
0x1ce0_7630_46f8_962b,
|
||||
0x724a_5276_486d_735c,
|
||||
0x6f05_c2a6_282d_48fd,
|
||||
0x2095_bd5b_b4ca_9331,
|
||||
0x03b3_5b38_94b0_f7da,
|
||||
]);
|
||||
let b = Fp([
|
||||
0x69ec_d704_0952_148f,
|
||||
0x985c_cc20_2219_0f55,
|
||||
0xe19b_ba36_a9ad_2f41,
|
||||
0x19bb_16c9_5219_dbd8,
|
||||
0x14dc_acfd_fb47_8693,
|
||||
0x115f_f58a_fff9_a8e1,
|
||||
]);
|
||||
|
||||
assert_eq!(a.invert().unwrap(), b);
|
||||
assert!(bool::from(Fp::zero().invert().is_none()));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_lexicographic_largest() {
|
||||
assert!(!bool::from(Fp::zero().lexicographically_largest()));
|
||||
assert!(!bool::from(Fp::one().lexicographically_largest()));
|
||||
assert!(!bool::from(
|
||||
Fp::from_raw_unchecked([
|
||||
0xa1fa_ffff_fffe_5557,
|
||||
0x995b_fff9_76a3_fffe,
|
||||
0x03f4_1d24_d174_ceb4,
|
||||
0xf654_7998_c199_5dbd,
|
||||
0x778a_468f_507a_6034,
|
||||
0x0205_5993_1f7f_8103
|
||||
])
|
||||
.lexicographically_largest()
|
||||
));
|
||||
assert!(bool::from(
|
||||
Fp::from_raw_unchecked([
|
||||
0x1804_0000_0001_5554,
|
||||
0x8550_0005_3ab0_0001,
|
||||
0x633c_b57c_253c_276f,
|
||||
0x6e22_d1ec_31eb_b502,
|
||||
0xd391_6126_f2d1_4ca2,
|
||||
0x17fb_b857_1a00_6596,
|
||||
])
|
||||
.lexicographically_largest()
|
||||
));
|
||||
assert!(bool::from(
|
||||
Fp::from_raw_unchecked([
|
||||
0x43f5_ffff_fffc_aaae,
|
||||
0x32b7_fff2_ed47_fffd,
|
||||
0x07e8_3a49_a2e9_9d69,
|
||||
0xeca8_f331_8332_bb7a,
|
||||
0xef14_8d1e_a0f4_c069,
|
||||
0x040a_b326_3eff_0206,
|
||||
])
|
||||
.lexicographically_largest()
|
||||
));
|
||||
}
|
|
@ -1,643 +0,0 @@
|
|||
use crate::fp::*;
|
||||
use crate::fp2::*;
|
||||
use crate::fp6::*;
|
||||
|
||||
use core::fmt;
|
||||
use core::ops::{Add, AddAssign, Mul, MulAssign, Neg, Sub, SubAssign};
|
||||
use rand_core::RngCore;
|
||||
use subtle::{Choice, ConditionallySelectable, ConstantTimeEq, CtOption};
|
||||
|
||||
/// This represents an element $c_0 + c_1 w$ of $\mathbb{F}_{p^12} = \mathbb{F}_{p^6} / w^2 - v$.
|
||||
pub struct Fp12 {
|
||||
pub c0: Fp6,
|
||||
pub c1: Fp6,
|
||||
}
|
||||
|
||||
impl From<Fp> for Fp12 {
|
||||
fn from(f: Fp) -> Fp12 {
|
||||
Fp12 {
|
||||
c0: Fp6::from(f),
|
||||
c1: Fp6::zero(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl From<Fp2> for Fp12 {
|
||||
fn from(f: Fp2) -> Fp12 {
|
||||
Fp12 {
|
||||
c0: Fp6::from(f),
|
||||
c1: Fp6::zero(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl From<Fp6> for Fp12 {
|
||||
fn from(f: Fp6) -> Fp12 {
|
||||
Fp12 {
|
||||
c0: f,
|
||||
c1: Fp6::zero(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl PartialEq for Fp12 {
|
||||
fn eq(&self, other: &Fp12) -> bool {
|
||||
self.ct_eq(other).into()
|
||||
}
|
||||
}
|
||||
|
||||
impl Copy for Fp12 {}
|
||||
impl Clone for Fp12 {
|
||||
#[inline]
|
||||
fn clone(&self) -> Self {
|
||||
*self
|
||||
}
|
||||
}
|
||||
|
||||
impl Default for Fp12 {
|
||||
fn default() -> Self {
|
||||
Fp12::zero()
|
||||
}
|
||||
}
|
||||
|
||||
impl fmt::Debug for Fp12 {
|
||||
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
||||
write!(f, "{:?} + ({:?})*w", self.c0, self.c1)
|
||||
}
|
||||
}
|
||||
|
||||
impl ConditionallySelectable for Fp12 {
|
||||
#[inline(always)]
|
||||
fn conditional_select(a: &Self, b: &Self, choice: Choice) -> Self {
|
||||
Fp12 {
|
||||
c0: Fp6::conditional_select(&a.c0, &b.c0, choice),
|
||||
c1: Fp6::conditional_select(&a.c1, &b.c1, choice),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl ConstantTimeEq for Fp12 {
|
||||
#[inline(always)]
|
||||
fn ct_eq(&self, other: &Self) -> Choice {
|
||||
self.c0.ct_eq(&other.c0) & self.c1.ct_eq(&other.c1)
|
||||
}
|
||||
}
|
||||
|
||||
impl Fp12 {
|
||||
#[inline]
|
||||
pub fn zero() -> Self {
|
||||
Fp12 {
|
||||
c0: Fp6::zero(),
|
||||
c1: Fp6::zero(),
|
||||
}
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn one() -> Self {
|
||||
Fp12 {
|
||||
c0: Fp6::one(),
|
||||
c1: Fp6::zero(),
|
||||
}
|
||||
}
|
||||
|
||||
pub(crate) fn random<R: RngCore + ?Sized>(rng: &mut R) -> Self {
|
||||
Fp12 {
|
||||
c0: Fp6::random(rng),
|
||||
c1: Fp6::random(rng),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn mul_by_014(&self, c0: &Fp2, c1: &Fp2, c4: &Fp2) -> Fp12 {
|
||||
let aa = self.c0.mul_by_01(c0, c1);
|
||||
let bb = self.c1.mul_by_1(c4);
|
||||
let o = c1 + c4;
|
||||
let c1 = self.c1 + self.c0;
|
||||
let c1 = c1.mul_by_01(c0, &o);
|
||||
let c1 = c1 - aa - bb;
|
||||
let c0 = bb;
|
||||
let c0 = c0.mul_by_nonresidue();
|
||||
let c0 = c0 + aa;
|
||||
|
||||
Fp12 { c0, c1 }
|
||||
}
|
||||
|
||||
#[inline(always)]
|
||||
pub fn is_zero(&self) -> Choice {
|
||||
self.c0.is_zero() & self.c1.is_zero()
|
||||
}
|
||||
|
||||
#[inline(always)]
|
||||
pub fn conjugate(&self) -> Self {
|
||||
Fp12 {
|
||||
c0: self.c0,
|
||||
c1: -self.c1,
|
||||
}
|
||||
}
|
||||
|
||||
/// Raises this element to p.
|
||||
#[inline(always)]
|
||||
pub fn frobenius_map(&self) -> Self {
|
||||
let c0 = self.c0.frobenius_map();
|
||||
let c1 = self.c1.frobenius_map();
|
||||
|
||||
// c1 = c1 * (u + 1)^((p - 1) / 6)
|
||||
let c1 = c1
|
||||
* Fp6::from(Fp2 {
|
||||
c0: Fp::from_raw_unchecked([
|
||||
0x0708_9552_b319_d465,
|
||||
0xc669_5f92_b50a_8313,
|
||||
0x97e8_3ccc_d117_228f,
|
||||
0xa35b_aeca_b2dc_29ee,
|
||||
0x1ce3_93ea_5daa_ce4d,
|
||||
0x08f2_220f_b0fb_66eb,
|
||||
]),
|
||||
c1: Fp::from_raw_unchecked([
|
||||
0xb2f6_6aad_4ce5_d646,
|
||||
0x5842_a06b_fc49_7cec,
|
||||
0xcf48_95d4_2599_d394,
|
||||
0xc11b_9cba_40a8_e8d0,
|
||||
0x2e38_13cb_e5a0_de89,
|
||||
0x110e_efda_8884_7faf,
|
||||
]),
|
||||
});
|
||||
|
||||
Fp12 { c0, c1 }
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn square(&self) -> Self {
|
||||
let ab = self.c0 * self.c1;
|
||||
let c0c1 = self.c0 + self.c1;
|
||||
let c0 = self.c1.mul_by_nonresidue();
|
||||
let c0 = c0 + self.c0;
|
||||
let c0 = c0 * c0c1;
|
||||
let c0 = c0 - ab;
|
||||
let c1 = ab + ab;
|
||||
let c0 = c0 - ab.mul_by_nonresidue();
|
||||
|
||||
Fp12 { c0, c1 }
|
||||
}
|
||||
|
||||
pub fn invert(&self) -> CtOption<Self> {
|
||||
(self.c0.square() - self.c1.square().mul_by_nonresidue())
|
||||
.invert()
|
||||
.map(|t| Fp12 {
|
||||
c0: self.c0 * t,
|
||||
c1: self.c1 * -t,
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a, 'b> Mul<&'b Fp12> for &'a Fp12 {
|
||||
type Output = Fp12;
|
||||
|
||||
#[inline]
|
||||
fn mul(self, other: &'b Fp12) -> Self::Output {
|
||||
let aa = self.c0 * other.c0;
|
||||
let bb = self.c1 * other.c1;
|
||||
let o = other.c0 + other.c1;
|
||||
let c1 = self.c1 + self.c0;
|
||||
let c1 = c1 * o;
|
||||
let c1 = c1 - aa;
|
||||
let c1 = c1 - bb;
|
||||
let c0 = bb.mul_by_nonresidue();
|
||||
let c0 = c0 + aa;
|
||||
|
||||
Fp12 { c0, c1 }
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a, 'b> Add<&'b Fp12> for &'a Fp12 {
|
||||
type Output = Fp12;
|
||||
|
||||
#[inline]
|
||||
fn add(self, rhs: &'b Fp12) -> Self::Output {
|
||||
Fp12 {
|
||||
c0: self.c0 + rhs.c0,
|
||||
c1: self.c1 + rhs.c1,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> Neg for &'a Fp12 {
|
||||
type Output = Fp12;
|
||||
|
||||
#[inline]
|
||||
fn neg(self) -> Self::Output {
|
||||
Fp12 {
|
||||
c0: -self.c0,
|
||||
c1: -self.c1,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Neg for Fp12 {
|
||||
type Output = Fp12;
|
||||
|
||||
#[inline]
|
||||
fn neg(self) -> Self::Output {
|
||||
-&self
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a, 'b> Sub<&'b Fp12> for &'a Fp12 {
|
||||
type Output = Fp12;
|
||||
|
||||
#[inline]
|
||||
fn sub(self, rhs: &'b Fp12) -> Self::Output {
|
||||
Fp12 {
|
||||
c0: self.c0 - rhs.c0,
|
||||
c1: self.c1 - rhs.c1,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl_binops_additive!(Fp12, Fp12);
|
||||
impl_binops_multiplicative!(Fp12, Fp12);
|
||||
|
||||
#[test]
|
||||
fn test_arithmetic() {
|
||||
use crate::fp::*;
|
||||
use crate::fp2::*;
|
||||
|
||||
let a = Fp12 {
|
||||
c0: Fp6 {
|
||||
c0: Fp2 {
|
||||
c0: Fp::from_raw_unchecked([
|
||||
0x47f9_cb98_b1b8_2d58,
|
||||
0x5fe9_11eb_a3aa_1d9d,
|
||||
0x96bf_1b5f_4dd8_1db3,
|
||||
0x8100_d27c_c925_9f5b,
|
||||
0xafa2_0b96_7464_0eab,
|
||||
0x09bb_cea7_d8d9_497d,
|
||||
]),
|
||||
c1: Fp::from_raw_unchecked([
|
||||
0x0303_cb98_b166_2daa,
|
||||
0xd931_10aa_0a62_1d5a,
|
||||
0xbfa9_820c_5be4_a468,
|
||||
0x0ba3_643e_cb05_a348,
|
||||
0xdc35_34bb_1f1c_25a6,
|
||||
0x06c3_05bb_19c0_e1c1,
|
||||
]),
|
||||
},
|
||||
c1: Fp2 {
|
||||
c0: Fp::from_raw_unchecked([
|
||||
0x46f9_cb98_b162_d858,
|
||||
0x0be9_109c_f7aa_1d57,
|
||||
0xc791_bc55_fece_41d2,
|
||||
0xf84c_5770_4e38_5ec2,
|
||||
0xcb49_c1d9_c010_e60f,
|
||||
0x0acd_b8e1_58bf_e3c8,
|
||||
]),
|
||||
c1: Fp::from_raw_unchecked([
|
||||
0x8aef_cb98_b15f_8306,
|
||||
0x3ea1_108f_e4f2_1d54,
|
||||
0xcf79_f69f_a1b7_df3b,
|
||||
0xe4f5_4aa1_d16b_1a3c,
|
||||
0xba5e_4ef8_6105_a679,
|
||||
0x0ed8_6c07_97be_e5cf,
|
||||
]),
|
||||
},
|
||||
c2: Fp2 {
|
||||
c0: Fp::from_raw_unchecked([
|
||||
0xcee5_cb98_b15c_2db4,
|
||||
0x7159_1082_d23a_1d51,
|
||||
0xd762_30e9_44a1_7ca4,
|
||||
0xd19e_3dd3_549d_d5b6,
|
||||
0xa972_dc17_01fa_66e3,
|
||||
0x12e3_1f2d_d6bd_e7d6,
|
||||
]),
|
||||
c1: Fp::from_raw_unchecked([
|
||||
0xad2a_cb98_b173_2d9d,
|
||||
0x2cfd_10dd_0696_1d64,
|
||||
0x0739_6b86_c6ef_24e8,
|
||||
0xbd76_e2fd_b1bf_c820,
|
||||
0x6afe_a7f6_de94_d0d5,
|
||||
0x1099_4b0c_5744_c040,
|
||||
]),
|
||||
},
|
||||
},
|
||||
c1: Fp6 {
|
||||
c0: Fp2 {
|
||||
c0: Fp::from_raw_unchecked([
|
||||
0x47f9_cb98_b1b8_2d58,
|
||||
0x5fe9_11eb_a3aa_1d9d,
|
||||
0x96bf_1b5f_4dd8_1db3,
|
||||
0x8100_d27c_c925_9f5b,
|
||||
0xafa2_0b96_7464_0eab,
|
||||
0x09bb_cea7_d8d9_497d,
|
||||
]),
|
||||
c1: Fp::from_raw_unchecked([
|
||||
0x0303_cb98_b166_2daa,
|
||||
0xd931_10aa_0a62_1d5a,
|
||||
0xbfa9_820c_5be4_a468,
|
||||
0x0ba3_643e_cb05_a348,
|
||||
0xdc35_34bb_1f1c_25a6,
|
||||
0x06c3_05bb_19c0_e1c1,
|
||||
]),
|
||||
},
|
||||
c1: Fp2 {
|
||||
c0: Fp::from_raw_unchecked([
|
||||
0x46f9_cb98_b162_d858,
|
||||
0x0be9_109c_f7aa_1d57,
|
||||
0xc791_bc55_fece_41d2,
|
||||
0xf84c_5770_4e38_5ec2,
|
||||
0xcb49_c1d9_c010_e60f,
|
||||
0x0acd_b8e1_58bf_e3c8,
|
||||
]),
|
||||
c1: Fp::from_raw_unchecked([
|
||||
0x8aef_cb98_b15f_8306,
|
||||
0x3ea1_108f_e4f2_1d54,
|
||||
0xcf79_f69f_a1b7_df3b,
|
||||
0xe4f5_4aa1_d16b_1a3c,
|
||||
0xba5e_4ef8_6105_a679,
|
||||
0x0ed8_6c07_97be_e5cf,
|
||||
]),
|
||||
},
|
||||
c2: Fp2 {
|
||||
c0: Fp::from_raw_unchecked([
|
||||
0xcee5_cb98_b15c_2db4,
|
||||
0x7159_1082_d23a_1d51,
|
||||
0xd762_30e9_44a1_7ca4,
|
||||
0xd19e_3dd3_549d_d5b6,
|
||||
0xa972_dc17_01fa_66e3,
|
||||
0x12e3_1f2d_d6bd_e7d6,
|
||||
]),
|
||||
c1: Fp::from_raw_unchecked([
|
||||
0xad2a_cb98_b173_2d9d,
|
||||
0x2cfd_10dd_0696_1d64,
|
||||
0x0739_6b86_c6ef_24e8,
|
||||
0xbd76_e2fd_b1bf_c820,
|
||||
0x6afe_a7f6_de94_d0d5,
|
||||
0x1099_4b0c_5744_c040,
|
||||
]),
|
||||
},
|
||||
},
|
||||
};
|
||||
|
||||
let b = Fp12 {
|
||||
c0: Fp6 {
|
||||
c0: Fp2 {
|
||||
c0: Fp::from_raw_unchecked([
|
||||
0x47f9_cb98_b1b8_2d58,
|
||||
0x5fe9_11eb_a3aa_1d9d,
|
||||
0x96bf_1b5f_4dd8_1db3,
|
||||
0x8100_d272_c925_9f5b,
|
||||
0xafa2_0b96_7464_0eab,
|
||||
0x09bb_cea7_d8d9_497d,
|
||||
]),
|
||||
c1: Fp::from_raw_unchecked([
|
||||
0x0303_cb98_b166_2daa,
|
||||
0xd931_10aa_0a62_1d5a,
|
||||
0xbfa9_820c_5be4_a468,
|
||||
0x0ba3_643e_cb05_a348,
|
||||
0xdc35_34bb_1f1c_25a6,
|
||||
0x06c3_05bb_19c0_e1c1,
|
||||
]),
|
||||
},
|
||||
c1: Fp2 {
|
||||
c0: Fp::from_raw_unchecked([
|
||||
0x46f9_cb98_b162_d858,
|
||||
0x0be9_109c_f7aa_1d57,
|
||||
0xc791_bc55_fece_41d2,
|
||||
0xf84c_5770_4e38_5ec2,
|
||||
0xcb49_c1d9_c010_e60f,
|
||||
0x0acd_b8e1_58bf_e348,
|
||||
]),
|
||||
c1: Fp::from_raw_unchecked([
|
||||
0x8aef_cb98_b15f_8306,
|
||||
0x3ea1_108f_e4f2_1d54,
|
||||
0xcf79_f69f_a1b7_df3b,
|
||||
0xe4f5_4aa1_d16b_1a3c,
|
||||
0xba5e_4ef8_6105_a679,
|
||||
0x0ed8_6c07_97be_e5cf,
|
||||
]),
|
||||
},
|
||||
c2: Fp2 {
|
||||
c0: Fp::from_raw_unchecked([
|
||||
0xcee5_cb98_b15c_2db4,
|
||||
0x7159_1082_d23a_1d51,
|
||||
0xd762_30e9_44a1_7ca4,
|
||||
0xd19e_3dd3_549d_d5b6,
|
||||
0xa972_dc17_01fa_66e3,
|
||||
0x12e3_1f2d_d6bd_e7d6,
|
||||
]),
|
||||
c1: Fp::from_raw_unchecked([
|
||||
0xad2a_cb98_b173_2d9d,
|
||||
0x2cfd_10dd_0696_1d64,
|
||||
0x0739_6b86_c6ef_24e8,
|
||||
0xbd76_e2fd_b1bf_c820,
|
||||
0x6afe_a7f6_de94_d0d5,
|
||||
0x1099_4b0c_5744_c040,
|
||||
]),
|
||||
},
|
||||
},
|
||||
c1: Fp6 {
|
||||
c0: Fp2 {
|
||||
c0: Fp::from_raw_unchecked([
|
||||
0x47f9_cb98_b1b8_2d58,
|
||||
0x5fe9_11eb_a3aa_1d9d,
|
||||
0x96bf_1b5f_4dd2_1db3,
|
||||
0x8100_d27c_c925_9f5b,
|
||||
0xafa2_0b96_7464_0eab,
|
||||
0x09bb_cea7_d8d9_497d,
|
||||
]),
|
||||
c1: Fp::from_raw_unchecked([
|
||||
0x0303_cb98_b166_2daa,
|
||||
0xd931_10aa_0a62_1d5a,
|
||||
0xbfa9_820c_5be4_a468,
|
||||
0x0ba3_643e_cb05_a348,
|
||||
0xdc35_34bb_1f1c_25a6,
|
||||
0x06c3_05bb_19c0_e1c1,
|
||||
]),
|
||||
},
|
||||
c1: Fp2 {
|
||||
c0: Fp::from_raw_unchecked([
|
||||
0x46f9_cb98_b162_d858,
|
||||
0x0be9_109c_f7aa_1d57,
|
||||
0xc791_bc55_fece_41d2,
|
||||
0xf84c_5770_4e38_5ec2,
|
||||
0xcb49_c1d9_c010_e60f,
|
||||
0x0acd_b8e1_58bf_e3c8,
|
||||
]),
|
||||
c1: Fp::from_raw_unchecked([
|
||||
0x8aef_cb98_b15f_8306,
|
||||
0x3ea1_108f_e4f2_1d54,
|
||||
0xcf79_f69f_a117_df3b,
|
||||
0xe4f5_4aa1_d16b_1a3c,
|
||||
0xba5e_4ef8_6105_a679,
|
||||
0x0ed8_6c07_97be_e5cf,
|
||||
]),
|
||||
},
|
||||
c2: Fp2 {
|
||||
c0: Fp::from_raw_unchecked([
|
||||
0xcee5_cb98_b15c_2db4,
|
||||
0x7159_1082_d23a_1d51,
|
||||
0xd762_30e9_44a1_7ca4,
|
||||
0xd19e_3dd3_549d_d5b6,
|
||||
0xa972_dc17_01fa_66e3,
|
||||
0x12e3_1f2d_d6bd_e7d6,
|
||||
]),
|
||||
c1: Fp::from_raw_unchecked([
|
||||
0xad2a_cb98_b173_2d9d,
|
||||
0x2cfd_10dd_0696_1d64,
|
||||
0x0739_6b86_c6ef_24e8,
|
||||
0xbd76_e2fd_b1bf_c820,
|
||||
0x6afe_a7f6_de94_d0d5,
|
||||
0x1099_4b0c_5744_c040,
|
||||
]),
|
||||
},
|
||||
},
|
||||
};
|
||||
|
||||
let c = Fp12 {
|
||||
c0: Fp6 {
|
||||
c0: Fp2 {
|
||||
c0: Fp::from_raw_unchecked([
|
||||
0x47f9_cb98_71b8_2d58,
|
||||
0x5fe9_11eb_a3aa_1d9d,
|
||||
0x96bf_1b5f_4dd8_1db3,
|
||||
0x8100_d27c_c925_9f5b,
|
||||
0xafa2_0b96_7464_0eab,
|
||||
0x09bb_cea7_d8d9_497d,
|
||||
]),
|
||||
c1: Fp::from_raw_unchecked([
|
||||
0x0303_cb98_b166_2daa,
|
||||
0xd931_10aa_0a62_1d5a,
|
||||
0xbfa9_820c_5be4_a468,
|
||||
0x0ba3_643e_cb05_a348,
|
||||
0xdc35_34bb_1f1c_25a6,
|
||||
0x06c3_05bb_19c0_e1c1,
|
||||
]),
|
||||
},
|
||||
c1: Fp2 {
|
||||
c0: Fp::from_raw_unchecked([
|
||||
0x46f9_cb98_b162_d858,
|
||||
0x0be9_109c_f7aa_1d57,
|
||||
0x7791_bc55_fece_41d2,
|
||||
0xf84c_5770_4e38_5ec2,
|
||||
0xcb49_c1d9_c010_e60f,
|
||||
0x0acd_b8e1_58bf_e3c8,
|
||||
]),
|
||||
c1: Fp::from_raw_unchecked([
|
||||
0x8aef_cb98_b15f_8306,
|
||||
0x3ea1_108f_e4f2_1d54,
|
||||
0xcf79_f69f_a1b7_df3b,
|
||||
0xe4f5_4aa1_d16b_133c,
|
||||
0xba5e_4ef8_6105_a679,
|
||||
0x0ed8_6c07_97be_e5cf,
|
||||
]),
|
||||
},
|
||||
c2: Fp2 {
|
||||
c0: Fp::from_raw_unchecked([
|
||||
0xcee5_cb98_b15c_2db4,
|
||||
0x7159_1082_d23a_1d51,
|
||||
0xd762_40e9_44a1_7ca4,
|
||||
0xd19e_3dd3_549d_d5b6,
|
||||
0xa972_dc17_01fa_66e3,
|
||||
0x12e3_1f2d_d6bd_e7d6,
|
||||
]),
|
||||
c1: Fp::from_raw_unchecked([
|
||||
0xad2a_cb98_b173_2d9d,
|
||||
0x2cfd_10dd_0696_1d64,
|
||||
0x0739_6b86_c6ef_24e8,
|
||||
0xbd76_e2fd_b1bf_c820,
|
||||
0x6afe_a7f6_de94_d0d5,
|
||||
0x1099_4b0c_1744_c040,
|
||||
]),
|
||||
},
|
||||
},
|
||||
c1: Fp6 {
|
||||
c0: Fp2 {
|
||||
c0: Fp::from_raw_unchecked([
|
||||
0x47f9_cb98_b1b8_2d58,
|
||||
0x5fe9_11eb_a3aa_1d9d,
|
||||
0x96bf_1b5f_4dd8_1db3,
|
||||
0x8100_d27c_c925_9f5b,
|
||||
0xafa2_0b96_7464_0eab,
|
||||
0x09bb_cea7_d8d9_497d,
|
||||
]),
|
||||
c1: Fp::from_raw_unchecked([
|
||||
0x0303_cb98_b166_2daa,
|
||||
0xd931_10aa_0a62_1d5a,
|
||||
0xbfa9_820c_5be4_a468,
|
||||
0x0ba3_643e_cb05_a348,
|
||||
0xdc35_34bb_1f1c_25a6,
|
||||
0x06c3_05bb_19c0_e1c1,
|
||||
]),
|
||||
},
|
||||
c1: Fp2 {
|
||||
c0: Fp::from_raw_unchecked([
|
||||
0x46f9_cb98_b162_d858,
|
||||
0x0be9_109c_f7aa_1d57,
|
||||
0xc791_bc55_fece_41d2,
|
||||
0xf84c_5770_4e38_5ec2,
|
||||
0xcb49_c1d3_c010_e60f,
|
||||
0x0acd_b8e1_58bf_e3c8,
|
||||
]),
|
||||
c1: Fp::from_raw_unchecked([
|
||||
0x8aef_cb98_b15f_8306,
|
||||
0x3ea1_108f_e4f2_1d54,
|
||||
0xcf79_f69f_a1b7_df3b,
|
||||
0xe4f5_4aa1_d16b_1a3c,
|
||||
0xba5e_4ef8_6105_a679,
|
||||
0x0ed8_6c07_97be_e5cf,
|
||||
]),
|
||||
},
|
||||
c2: Fp2 {
|
||||
c0: Fp::from_raw_unchecked([
|
||||
0xcee5_cb98_b15c_2db4,
|
||||
0x7159_1082_d23a_1d51,
|
||||
0xd762_30e9_44a1_7ca4,
|
||||
0xd19e_3dd3_549d_d5b6,
|
||||
0xa972_dc17_01fa_66e3,
|
||||
0x12e3_1f2d_d6bd_e7d6,
|
||||
]),
|
||||
c1: Fp::from_raw_unchecked([
|
||||
0xad2a_cb98_b173_2d9d,
|
||||
0x2cfd_10dd_0696_1d64,
|
||||
0x0739_6b86_c6ef_24e8,
|
||||
0xbd76_e2fd_b1bf_c820,
|
||||
0x6afe_a7f6_de94_d0d5,
|
||||
0x1099_4b0c_5744_1040,
|
||||
]),
|
||||
},
|
||||
},
|
||||
};
|
||||
|
||||
// because a and b and c are similar to each other and
|
||||
// I was lazy, this is just some arbitrary way to make
|
||||
// them a little more different
|
||||
let a = a.square().invert().unwrap().square() + c;
|
||||
let b = b.square().invert().unwrap().square() + a;
|
||||
let c = c.square().invert().unwrap().square() + b;
|
||||
|
||||
assert_eq!(a.square(), a * a);
|
||||
assert_eq!(b.square(), b * b);
|
||||
assert_eq!(c.square(), c * c);
|
||||
|
||||
assert_eq!((a + b) * c.square(), (c * c * a) + (c * c * b));
|
||||
|
||||
assert_eq!(
|
||||
a.invert().unwrap() * b.invert().unwrap(),
|
||||
(a * b).invert().unwrap()
|
||||
);
|
||||
assert_eq!(a.invert().unwrap() * a, Fp12::one());
|
||||
|
||||
assert!(a != a.frobenius_map());
|
||||
assert_eq!(
|
||||
a,
|
||||
a.frobenius_map()
|
||||
.frobenius_map()
|
||||
.frobenius_map()
|
||||
.frobenius_map()
|
||||
.frobenius_map()
|
||||
.frobenius_map()
|
||||
.frobenius_map()
|
||||
.frobenius_map()
|
||||
.frobenius_map()
|
||||
.frobenius_map()
|
||||
.frobenius_map()
|
||||
.frobenius_map()
|
||||
);
|
||||
}
|
|
@ -1,875 +0,0 @@
|
|||
//! This module implements arithmetic over the quadratic extension field Fp2.
|
||||
|
||||
use core::fmt;
|
||||
use core::ops::{Add, AddAssign, Mul, MulAssign, Neg, Sub, SubAssign};
|
||||
use rand_core::RngCore;
|
||||
use subtle::{Choice, ConditionallySelectable, ConstantTimeEq, CtOption};
|
||||
|
||||
use crate::fp::Fp;
|
||||
|
||||
#[derive(Copy, Clone)]
|
||||
pub struct Fp2 {
|
||||
pub c0: Fp,
|
||||
pub c1: Fp,
|
||||
}
|
||||
|
||||
impl fmt::Debug for Fp2 {
|
||||
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
||||
write!(f, "{:?} + {:?}*u", self.c0, self.c1)
|
||||
}
|
||||
}
|
||||
|
||||
impl Default for Fp2 {
|
||||
fn default() -> Self {
|
||||
Fp2::zero()
|
||||
}
|
||||
}
|
||||
|
||||
impl From<Fp> for Fp2 {
|
||||
fn from(f: Fp) -> Fp2 {
|
||||
Fp2 {
|
||||
c0: f,
|
||||
c1: Fp::zero(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl ConstantTimeEq for Fp2 {
|
||||
fn ct_eq(&self, other: &Self) -> Choice {
|
||||
self.c0.ct_eq(&other.c0) & self.c1.ct_eq(&other.c1)
|
||||
}
|
||||
}
|
||||
|
||||
impl Eq for Fp2 {}
|
||||
impl PartialEq for Fp2 {
|
||||
#[inline]
|
||||
fn eq(&self, other: &Self) -> bool {
|
||||
bool::from(self.ct_eq(other))
|
||||
}
|
||||
}
|
||||
|
||||
impl ConditionallySelectable for Fp2 {
|
||||
fn conditional_select(a: &Self, b: &Self, choice: Choice) -> Self {
|
||||
Fp2 {
|
||||
c0: Fp::conditional_select(&a.c0, &b.c0, choice),
|
||||
c1: Fp::conditional_select(&a.c1, &b.c1, choice),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> Neg for &'a Fp2 {
|
||||
type Output = Fp2;
|
||||
|
||||
#[inline]
|
||||
fn neg(self) -> Fp2 {
|
||||
self.neg()
|
||||
}
|
||||
}
|
||||
|
||||
impl Neg for Fp2 {
|
||||
type Output = Fp2;
|
||||
|
||||
#[inline]
|
||||
fn neg(self) -> Fp2 {
|
||||
-&self
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a, 'b> Sub<&'b Fp2> for &'a Fp2 {
|
||||
type Output = Fp2;
|
||||
|
||||
#[inline]
|
||||
fn sub(self, rhs: &'b Fp2) -> Fp2 {
|
||||
self.sub(rhs)
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a, 'b> Add<&'b Fp2> for &'a Fp2 {
|
||||
type Output = Fp2;
|
||||
|
||||
#[inline]
|
||||
fn add(self, rhs: &'b Fp2) -> Fp2 {
|
||||
self.add(rhs)
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a, 'b> Mul<&'b Fp2> for &'a Fp2 {
|
||||
type Output = Fp2;
|
||||
|
||||
#[inline]
|
||||
fn mul(self, rhs: &'b Fp2) -> Fp2 {
|
||||
self.mul(rhs)
|
||||
}
|
||||
}
|
||||
|
||||
impl_binops_additive!(Fp2, Fp2);
|
||||
impl_binops_multiplicative!(Fp2, Fp2);
|
||||
|
||||
impl Fp2 {
|
||||
#[inline]
|
||||
pub const fn zero() -> Fp2 {
|
||||
Fp2 {
|
||||
c0: Fp::zero(),
|
||||
c1: Fp::zero(),
|
||||
}
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub const fn one() -> Fp2 {
|
||||
Fp2 {
|
||||
c0: Fp::one(),
|
||||
c1: Fp::zero(),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn is_zero(&self) -> Choice {
|
||||
self.c0.is_zero() & self.c1.is_zero()
|
||||
}
|
||||
|
||||
pub(crate) fn random<R: RngCore + ?Sized>(rng: &mut R) -> Fp2 {
|
||||
Fp2 {
|
||||
c0: Fp::random(rng),
|
||||
c1: Fp::random(rng),
|
||||
}
|
||||
}
|
||||
|
||||
/// Raises this element to p.
|
||||
#[inline(always)]
|
||||
pub fn frobenius_map(&self) -> Self {
|
||||
// This is always just a conjugation. If you're curious why, here's
|
||||
// an article about it: https://alicebob.cryptoland.net/the-frobenius-endomorphism-with-finite-fields/
|
||||
self.conjugate()
|
||||
}
|
||||
|
||||
#[inline(always)]
|
||||
pub fn conjugate(&self) -> Self {
|
||||
Fp2 {
|
||||
c0: self.c0,
|
||||
c1: -self.c1,
|
||||
}
|
||||
}
|
||||
|
||||
#[inline(always)]
|
||||
pub fn mul_by_nonresidue(&self) -> Fp2 {
|
||||
// Multiply a + bu by u + 1, getting
|
||||
// au + a + bu^2 + bu
|
||||
// and because u^2 = -1, we get
|
||||
// (a - b) + (a + b)u
|
||||
|
||||
Fp2 {
|
||||
c0: self.c0 - self.c1,
|
||||
c1: self.c0 + self.c1,
|
||||
}
|
||||
}
|
||||
|
||||
/// Returns whether or not this element is strictly lexicographically
|
||||
/// larger than its negation.
|
||||
#[inline]
|
||||
pub fn lexicographically_largest(&self) -> Choice {
|
||||
// If this element's c1 coefficient is lexicographically largest
|
||||
// then it is lexicographically largest. Otherwise, in the event
|
||||
// the c1 coefficient is zero and the c0 coefficient is
|
||||
// lexicographically largest, then this element is lexicographically
|
||||
// largest.
|
||||
|
||||
self.c1.lexicographically_largest()
|
||||
| (self.c1.is_zero() & self.c0.lexicographically_largest())
|
||||
}
|
||||
|
||||
pub const fn square(&self) -> Fp2 {
|
||||
// Complex squaring:
|
||||
//
|
||||
// v0 = c0 * c1
|
||||
// c0' = (c0 + c1) * (c0 + \beta*c1) - v0 - \beta * v0
|
||||
// c1' = 2 * v0
|
||||
//
|
||||
// In BLS12-381's F_{p^2}, our \beta is -1 so we
|
||||
// can modify this formula:
|
||||
//
|
||||
// c0' = (c0 + c1) * (c0 - c1)
|
||||
// c1' = 2 * c0 * c1
|
||||
|
||||
let a = (&self.c0).add(&self.c1);
|
||||
let b = (&self.c0).sub(&self.c1);
|
||||
let c = (&self.c0).add(&self.c0);
|
||||
|
||||
Fp2 {
|
||||
c0: (&a).mul(&b),
|
||||
c1: (&c).mul(&self.c1),
|
||||
}
|
||||
}
|
||||
|
||||
pub const fn mul(&self, rhs: &Fp2) -> Fp2 {
|
||||
// Karatsuba multiplication:
|
||||
//
|
||||
// v0 = a0 * b0
|
||||
// v1 = a1 * b1
|
||||
// c0 = v0 + \beta * v1
|
||||
// c1 = (a0 + a1) * (b0 + b1) - v0 - v1
|
||||
//
|
||||
// In BLS12-381's F_{p^2}, our \beta is -1 so we
|
||||
// can modify this formula. (Also, since we always
|
||||
// subtract v1, we can compute v1 = -a1 * b1.)
|
||||
//
|
||||
// v0 = a0 * b0
|
||||
// v1 = (-a1) * b1
|
||||
// c0 = v0 + v1
|
||||
// c1 = (a0 + a1) * (b0 + b1) - v0 + v1
|
||||
|
||||
let v0 = (&self.c0).mul(&rhs.c0);
|
||||
let v1 = (&(&self.c1).neg()).mul(&rhs.c1);
|
||||
let c0 = (&v0).add(&v1);
|
||||
let c1 = (&(&self.c0).add(&self.c1)).mul(&(&rhs.c0).add(&rhs.c1));
|
||||
let c1 = (&c1).sub(&v0);
|
||||
let c1 = (&c1).add(&v1);
|
||||
|
||||
Fp2 { c0, c1 }
|
||||
}
|
||||
|
||||
pub const fn add(&self, rhs: &Fp2) -> Fp2 {
|
||||
Fp2 {
|
||||
c0: (&self.c0).add(&rhs.c0),
|
||||
c1: (&self.c1).add(&rhs.c1),
|
||||
}
|
||||
}
|
||||
|
||||
pub const fn sub(&self, rhs: &Fp2) -> Fp2 {
|
||||
Fp2 {
|
||||
c0: (&self.c0).sub(&rhs.c0),
|
||||
c1: (&self.c1).sub(&rhs.c1),
|
||||
}
|
||||
}
|
||||
|
||||
pub const fn neg(&self) -> Fp2 {
|
||||
Fp2 {
|
||||
c0: (&self.c0).neg(),
|
||||
c1: (&self.c1).neg(),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn sqrt(&self) -> CtOption<Self> {
|
||||
// Algorithm 9, https://eprint.iacr.org/2012/685.pdf
|
||||
// with constant time modifications.
|
||||
|
||||
CtOption::new(Fp2::zero(), self.is_zero()).or_else(|| {
|
||||
// a1 = self^((p - 3) / 4)
|
||||
let a1 = self.pow_vartime(&[
|
||||
0xee7f_bfff_ffff_eaaa,
|
||||
0x07aa_ffff_ac54_ffff,
|
||||
0xd9cc_34a8_3dac_3d89,
|
||||
0xd91d_d2e1_3ce1_44af,
|
||||
0x92c6_e9ed_90d2_eb35,
|
||||
0x0680_447a_8e5f_f9a6,
|
||||
]);
|
||||
|
||||
// alpha = a1^2 * self = self^((p - 3) / 2 + 1) = self^((p - 1) / 2)
|
||||
let alpha = a1.square() * self;
|
||||
|
||||
// x0 = self^((p + 1) / 4)
|
||||
let x0 = a1 * self;
|
||||
|
||||
// In the event that alpha = -1, the element is order p - 1 and so
|
||||
// we're just trying to get the square of an element of the subfield
|
||||
// Fp. This is given by x0 * u, since u = sqrt(-1). Since the element
|
||||
// x0 = a + bu has b = 0, the solution is therefore au.
|
||||
CtOption::new(
|
||||
Fp2 {
|
||||
c0: -x0.c1,
|
||||
c1: x0.c0,
|
||||
},
|
||||
alpha.ct_eq(&(&Fp2::one()).neg()),
|
||||
)
|
||||
// Otherwise, the correct solution is (1 + alpha)^((q - 1) // 2) * x0
|
||||
.or_else(|| {
|
||||
CtOption::new(
|
||||
(alpha + Fp2::one()).pow_vartime(&[
|
||||
0xdcff_7fff_ffff_d555,
|
||||
0x0f55_ffff_58a9_ffff,
|
||||
0xb398_6950_7b58_7b12,
|
||||
0xb23b_a5c2_79c2_895f,
|
||||
0x258d_d3db_21a5_d66b,
|
||||
0x0d00_88f5_1cbf_f34d,
|
||||
]) * x0,
|
||||
Choice::from(1),
|
||||
)
|
||||
})
|
||||
// Only return the result if it's really the square root (and so
|
||||
// self is actually quadratic nonresidue)
|
||||
.and_then(|sqrt| CtOption::new(sqrt, sqrt.square().ct_eq(self)))
|
||||
})
|
||||
}
|
||||
|
||||
/// Computes the multiplicative inverse of this field
|
||||
/// element, returning None in the case that this element
|
||||
/// is zero.
|
||||
pub fn invert(&self) -> CtOption<Self> {
|
||||
// We wish to find the multiplicative inverse of a nonzero
|
||||
// element a + bu in Fp2. We leverage an identity
|
||||
//
|
||||
// (a + bu)(a - bu) = a^2 + b^2
|
||||
//
|
||||
// which holds because u^2 = -1. This can be rewritten as
|
||||
//
|
||||
// (a + bu)(a - bu)/(a^2 + b^2) = 1
|
||||
//
|
||||
// because a^2 + b^2 = 0 has no nonzero solutions for (a, b).
|
||||
// This gives that (a - bu)/(a^2 + b^2) is the inverse
|
||||
// of (a + bu). Importantly, this can be computing using
|
||||
// only a single inversion in Fp.
|
||||
|
||||
(self.c0.square() + self.c1.square()).invert().map(|t| Fp2 {
|
||||
c0: self.c0 * t,
|
||||
c1: self.c1 * -t,
|
||||
})
|
||||
}
|
||||
|
||||
/// Although this is labeled "vartime", it is only
|
||||
/// variable time with respect to the exponent. It
|
||||
/// is also not exposed in the public API.
|
||||
pub fn pow_vartime(&self, by: &[u64; 6]) -> Self {
|
||||
let mut res = Self::one();
|
||||
for e in by.iter().rev() {
|
||||
for i in (0..64).rev() {
|
||||
res = res.square();
|
||||
|
||||
if ((*e >> i) & 1) == 1 {
|
||||
res *= self;
|
||||
}
|
||||
}
|
||||
}
|
||||
res
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_conditional_selection() {
|
||||
let a = Fp2 {
|
||||
c0: Fp::from_raw_unchecked([1, 2, 3, 4, 5, 6]),
|
||||
c1: Fp::from_raw_unchecked([7, 8, 9, 10, 11, 12]),
|
||||
};
|
||||
let b = Fp2 {
|
||||
c0: Fp::from_raw_unchecked([13, 14, 15, 16, 17, 18]),
|
||||
c1: Fp::from_raw_unchecked([19, 20, 21, 22, 23, 24]),
|
||||
};
|
||||
|
||||
assert_eq!(
|
||||
ConditionallySelectable::conditional_select(&a, &b, Choice::from(0u8)),
|
||||
a
|
||||
);
|
||||
assert_eq!(
|
||||
ConditionallySelectable::conditional_select(&a, &b, Choice::from(1u8)),
|
||||
b
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_equality() {
|
||||
fn is_equal(a: &Fp2, b: &Fp2) -> bool {
|
||||
let eq = a == b;
|
||||
let ct_eq = a.ct_eq(&b);
|
||||
|
||||
assert_eq!(eq, bool::from(ct_eq));
|
||||
|
||||
eq
|
||||
}
|
||||
|
||||
assert!(is_equal(
|
||||
&Fp2 {
|
||||
c0: Fp::from_raw_unchecked([1, 2, 3, 4, 5, 6]),
|
||||
c1: Fp::from_raw_unchecked([7, 8, 9, 10, 11, 12]),
|
||||
},
|
||||
&Fp2 {
|
||||
c0: Fp::from_raw_unchecked([1, 2, 3, 4, 5, 6]),
|
||||
c1: Fp::from_raw_unchecked([7, 8, 9, 10, 11, 12]),
|
||||
}
|
||||
));
|
||||
|
||||
assert!(!is_equal(
|
||||
&Fp2 {
|
||||
c0: Fp::from_raw_unchecked([2, 2, 3, 4, 5, 6]),
|
||||
c1: Fp::from_raw_unchecked([7, 8, 9, 10, 11, 12]),
|
||||
},
|
||||
&Fp2 {
|
||||
c0: Fp::from_raw_unchecked([1, 2, 3, 4, 5, 6]),
|
||||
c1: Fp::from_raw_unchecked([7, 8, 9, 10, 11, 12]),
|
||||
}
|
||||
));
|
||||
|
||||
assert!(!is_equal(
|
||||
&Fp2 {
|
||||
c0: Fp::from_raw_unchecked([1, 2, 3, 4, 5, 6]),
|
||||
c1: Fp::from_raw_unchecked([2, 8, 9, 10, 11, 12]),
|
||||
},
|
||||
&Fp2 {
|
||||
c0: Fp::from_raw_unchecked([1, 2, 3, 4, 5, 6]),
|
||||
c1: Fp::from_raw_unchecked([7, 8, 9, 10, 11, 12]),
|
||||
}
|
||||
));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_squaring() {
|
||||
let a = Fp2 {
|
||||
c0: Fp::from_raw_unchecked([
|
||||
0xc9a2_1831_63ee_70d4,
|
||||
0xbc37_70a7_196b_5c91,
|
||||
0xa247_f8c1_304c_5f44,
|
||||
0xb01f_c2a3_726c_80b5,
|
||||
0xe1d2_93e5_bbd9_19c9,
|
||||
0x04b7_8e80_020e_f2ca,
|
||||
]),
|
||||
c1: Fp::from_raw_unchecked([
|
||||
0x952e_a446_0462_618f,
|
||||
0x238d_5edd_f025_c62f,
|
||||
0xf6c9_4b01_2ea9_2e72,
|
||||
0x03ce_24ea_c1c9_3808,
|
||||
0x0559_50f9_45da_483c,
|
||||
0x010a_768d_0df4_eabc,
|
||||
]),
|
||||
};
|
||||
let b = Fp2 {
|
||||
c0: Fp::from_raw_unchecked([
|
||||
0xa1e0_9175_a4d2_c1fe,
|
||||
0x8b33_acfc_204e_ff12,
|
||||
0xe244_15a1_1b45_6e42,
|
||||
0x61d9_96b1_b6ee_1936,
|
||||
0x1164_dbe8_667c_853c,
|
||||
0x0788_557a_cc7d_9c79,
|
||||
]),
|
||||
c1: Fp::from_raw_unchecked([
|
||||
0xda6a_87cc_6f48_fa36,
|
||||
0x0fc7_b488_277c_1903,
|
||||
0x9445_ac4a_dc44_8187,
|
||||
0x0261_6d5b_c909_9209,
|
||||
0xdbed_4677_2db5_8d48,
|
||||
0x11b9_4d50_76c7_b7b1,
|
||||
]),
|
||||
};
|
||||
|
||||
assert_eq!(a.square(), b);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_multiplication() {
|
||||
let a = Fp2 {
|
||||
c0: Fp::from_raw_unchecked([
|
||||
0xc9a2_1831_63ee_70d4,
|
||||
0xbc37_70a7_196b_5c91,
|
||||
0xa247_f8c1_304c_5f44,
|
||||
0xb01f_c2a3_726c_80b5,
|
||||
0xe1d2_93e5_bbd9_19c9,
|
||||
0x04b7_8e80_020e_f2ca,
|
||||
]),
|
||||
c1: Fp::from_raw_unchecked([
|
||||
0x952e_a446_0462_618f,
|
||||
0x238d_5edd_f025_c62f,
|
||||
0xf6c9_4b01_2ea9_2e72,
|
||||
0x03ce_24ea_c1c9_3808,
|
||||
0x0559_50f9_45da_483c,
|
||||
0x010a_768d_0df4_eabc,
|
||||
]),
|
||||
};
|
||||
let b = Fp2 {
|
||||
c0: Fp::from_raw_unchecked([
|
||||
0xa1e0_9175_a4d2_c1fe,
|
||||
0x8b33_acfc_204e_ff12,
|
||||
0xe244_15a1_1b45_6e42,
|
||||
0x61d9_96b1_b6ee_1936,
|
||||
0x1164_dbe8_667c_853c,
|
||||
0x0788_557a_cc7d_9c79,
|
||||
]),
|
||||
c1: Fp::from_raw_unchecked([
|
||||
0xda6a_87cc_6f48_fa36,
|
||||
0x0fc7_b488_277c_1903,
|
||||
0x9445_ac4a_dc44_8187,
|
||||
0x0261_6d5b_c909_9209,
|
||||
0xdbed_4677_2db5_8d48,
|
||||
0x11b9_4d50_76c7_b7b1,
|
||||
]),
|
||||
};
|
||||
let c = Fp2 {
|
||||
c0: Fp::from_raw_unchecked([
|
||||
0xf597_483e_27b4_e0f7,
|
||||
0x610f_badf_811d_ae5f,
|
||||
0x8432_af91_7714_327a,
|
||||
0x6a9a_9603_cf88_f09e,
|
||||
0xf05a_7bf8_bad0_eb01,
|
||||
0x0954_9131_c003_ffae,
|
||||
]),
|
||||
c1: Fp::from_raw_unchecked([
|
||||
0x963b_02d0_f93d_37cd,
|
||||
0xc95c_e1cd_b30a_73d4,
|
||||
0x3087_25fa_3126_f9b8,
|
||||
0x56da_3c16_7fab_0d50,
|
||||
0x6b50_86b5_f4b6_d6af,
|
||||
0x09c3_9f06_2f18_e9f2,
|
||||
]),
|
||||
};
|
||||
|
||||
assert_eq!(a * b, c);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_addition() {
|
||||
let a = Fp2 {
|
||||
c0: Fp::from_raw_unchecked([
|
||||
0xc9a2_1831_63ee_70d4,
|
||||
0xbc37_70a7_196b_5c91,
|
||||
0xa247_f8c1_304c_5f44,
|
||||
0xb01f_c2a3_726c_80b5,
|
||||
0xe1d2_93e5_bbd9_19c9,
|
||||
0x04b7_8e80_020e_f2ca,
|
||||
]),
|
||||
c1: Fp::from_raw_unchecked([
|
||||
0x952e_a446_0462_618f,
|
||||
0x238d_5edd_f025_c62f,
|
||||
0xf6c9_4b01_2ea9_2e72,
|
||||
0x03ce_24ea_c1c9_3808,
|
||||
0x0559_50f9_45da_483c,
|
||||
0x010a_768d_0df4_eabc,
|
||||
]),
|
||||
};
|
||||
let b = Fp2 {
|
||||
c0: Fp::from_raw_unchecked([
|
||||
0xa1e0_9175_a4d2_c1fe,
|
||||
0x8b33_acfc_204e_ff12,
|
||||
0xe244_15a1_1b45_6e42,
|
||||
0x61d9_96b1_b6ee_1936,
|
||||
0x1164_dbe8_667c_853c,
|
||||
0x0788_557a_cc7d_9c79,
|
||||
]),
|
||||
c1: Fp::from_raw_unchecked([
|
||||
0xda6a_87cc_6f48_fa36,
|
||||
0x0fc7_b488_277c_1903,
|
||||
0x9445_ac4a_dc44_8187,
|
||||
0x0261_6d5b_c909_9209,
|
||||
0xdbed_4677_2db5_8d48,
|
||||
0x11b9_4d50_76c7_b7b1,
|
||||
]),
|
||||
};
|
||||
let c = Fp2 {
|
||||
c0: Fp::from_raw_unchecked([
|
||||
0x6b82_a9a7_08c1_32d2,
|
||||
0x476b_1da3_39ba_5ba4,
|
||||
0x848c_0e62_4b91_cd87,
|
||||
0x11f9_5955_295a_99ec,
|
||||
0xf337_6fce_2255_9f06,
|
||||
0x0c3f_e3fa_ce8c_8f43,
|
||||
]),
|
||||
c1: Fp::from_raw_unchecked([
|
||||
0x6f99_2c12_73ab_5bc5,
|
||||
0x3355_1366_17a1_df33,
|
||||
0x8b0e_f74c_0aed_aff9,
|
||||
0x062f_9246_8ad2_ca12,
|
||||
0xe146_9770_738f_d584,
|
||||
0x12c3_c3dd_84bc_a26d,
|
||||
]),
|
||||
};
|
||||
|
||||
assert_eq!(a + b, c);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_subtraction() {
|
||||
let a = Fp2 {
|
||||
c0: Fp::from_raw_unchecked([
|
||||
0xc9a2_1831_63ee_70d4,
|
||||
0xbc37_70a7_196b_5c91,
|
||||
0xa247_f8c1_304c_5f44,
|
||||
0xb01f_c2a3_726c_80b5,
|
||||
0xe1d2_93e5_bbd9_19c9,
|
||||
0x04b7_8e80_020e_f2ca,
|
||||
]),
|
||||
c1: Fp::from_raw_unchecked([
|
||||
0x952e_a446_0462_618f,
|
||||
0x238d_5edd_f025_c62f,
|
||||
0xf6c9_4b01_2ea9_2e72,
|
||||
0x03ce_24ea_c1c9_3808,
|
||||
0x0559_50f9_45da_483c,
|
||||
0x010a_768d_0df4_eabc,
|
||||
]),
|
||||
};
|
||||
let b = Fp2 {
|
||||
c0: Fp::from_raw_unchecked([
|
||||
0xa1e0_9175_a4d2_c1fe,
|
||||
0x8b33_acfc_204e_ff12,
|
||||
0xe244_15a1_1b45_6e42,
|
||||
0x61d9_96b1_b6ee_1936,
|
||||
0x1164_dbe8_667c_853c,
|
||||
0x0788_557a_cc7d_9c79,
|
||||
]),
|
||||
c1: Fp::from_raw_unchecked([
|
||||
0xda6a_87cc_6f48_fa36,
|
||||
0x0fc7_b488_277c_1903,
|
||||
0x9445_ac4a_dc44_8187,
|
||||
0x0261_6d5b_c909_9209,
|
||||
0xdbed_4677_2db5_8d48,
|
||||
0x11b9_4d50_76c7_b7b1,
|
||||
]),
|
||||
};
|
||||
let c = Fp2 {
|
||||
c0: Fp::from_raw_unchecked([
|
||||
0xe1c0_86bb_bf1b_5981,
|
||||
0x4faf_c3a9_aa70_5d7e,
|
||||
0x2734_b5c1_0bb7_e726,
|
||||
0xb2bd_7776_af03_7a3e,
|
||||
0x1b89_5fb3_98a8_4164,
|
||||
0x1730_4aef_6f11_3cec,
|
||||
]),
|
||||
c1: Fp::from_raw_unchecked([
|
||||
0x74c3_1c79_9519_1204,
|
||||
0x3271_aa54_79fd_ad2b,
|
||||
0xc9b4_7157_4915_a30f,
|
||||
0x65e4_0313_ec44_b8be,
|
||||
0x7487_b238_5b70_67cb,
|
||||
0x0952_3b26_d0ad_19a4,
|
||||
]),
|
||||
};
|
||||
|
||||
assert_eq!(a - b, c);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_negation() {
|
||||
let a = Fp2 {
|
||||
c0: Fp::from_raw_unchecked([
|
||||
0xc9a2_1831_63ee_70d4,
|
||||
0xbc37_70a7_196b_5c91,
|
||||
0xa247_f8c1_304c_5f44,
|
||||
0xb01f_c2a3_726c_80b5,
|
||||
0xe1d2_93e5_bbd9_19c9,
|
||||
0x04b7_8e80_020e_f2ca,
|
||||
]),
|
||||
c1: Fp::from_raw_unchecked([
|
||||
0x952e_a446_0462_618f,
|
||||
0x238d_5edd_f025_c62f,
|
||||
0xf6c9_4b01_2ea9_2e72,
|
||||
0x03ce_24ea_c1c9_3808,
|
||||
0x0559_50f9_45da_483c,
|
||||
0x010a_768d_0df4_eabc,
|
||||
]),
|
||||
};
|
||||
let b = Fp2 {
|
||||
c0: Fp::from_raw_unchecked([
|
||||
0xf05c_e7ce_9c11_39d7,
|
||||
0x6274_8f57_97e8_a36d,
|
||||
0xc4e8_d9df_c664_96df,
|
||||
0xb457_88e1_8118_9209,
|
||||
0x6949_13d0_8772_930d,
|
||||
0x1549_836a_3770_f3cf,
|
||||
]),
|
||||
c1: Fp::from_raw_unchecked([
|
||||
0x24d0_5bb9_fb9d_491c,
|
||||
0xfb1e_a120_c12e_39d0,
|
||||
0x7067_879f_c807_c7b1,
|
||||
0x60a9_269a_31bb_dab6,
|
||||
0x45c2_56bc_fd71_649b,
|
||||
0x18f6_9b5d_2b8a_fbde,
|
||||
]),
|
||||
};
|
||||
|
||||
assert_eq!(-a, b);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_sqrt() {
|
||||
// a = 1488924004771393321054797166853618474668089414631333405711627789629391903630694737978065425271543178763948256226639*u + 784063022264861764559335808165825052288770346101304131934508881646553551234697082295473567906267937225174620141295
|
||||
let a = Fp2 {
|
||||
c0: Fp::from_raw_unchecked([
|
||||
0x2bee_d146_27d7_f9e9,
|
||||
0xb661_4e06_660e_5dce,
|
||||
0x06c4_cc7c_2f91_d42c,
|
||||
0x996d_7847_4b7a_63cc,
|
||||
0xebae_bc4c_820d_574e,
|
||||
0x1886_5e12_d93f_d845,
|
||||
]),
|
||||
c1: Fp::from_raw_unchecked([
|
||||
0x7d82_8664_baf4_f566,
|
||||
0xd17e_6639_96ec_7339,
|
||||
0x679e_ad55_cb40_78d0,
|
||||
0xfe3b_2260_e001_ec28,
|
||||
0x3059_93d0_43d9_1b68,
|
||||
0x0626_f03c_0489_b72d,
|
||||
]),
|
||||
};
|
||||
|
||||
assert_eq!(a.sqrt().unwrap().square(), a);
|
||||
|
||||
// b = 5, which is a generator of the p - 1 order
|
||||
// multiplicative subgroup
|
||||
let b = Fp2 {
|
||||
c0: Fp::from_raw_unchecked([
|
||||
0x6631_0000_0010_5545,
|
||||
0x2114_0040_0eec_000d,
|
||||
0x3fa7_af30_c820_e316,
|
||||
0xc52a_8b8d_6387_695d,
|
||||
0x9fb4_e61d_1e83_eac5,
|
||||
0x005c_b922_afe8_4dc7,
|
||||
]),
|
||||
c1: Fp::zero(),
|
||||
};
|
||||
|
||||
assert_eq!(b.sqrt().unwrap().square(), b);
|
||||
|
||||
// c = 25, which is a generator of the (p - 1) / 2 order
|
||||
// multiplicative subgroup
|
||||
let c = Fp2 {
|
||||
c0: Fp::from_raw_unchecked([
|
||||
0x44f6_0000_0051_ffae,
|
||||
0x86b8_0141_9948_0043,
|
||||
0xd715_9952_f1f3_794a,
|
||||
0x755d_6e3d_fe1f_fc12,
|
||||
0xd36c_d6db_5547_e905,
|
||||
0x02f8_c8ec_bf18_67bb,
|
||||
]),
|
||||
c1: Fp::zero(),
|
||||
};
|
||||
|
||||
assert_eq!(c.sqrt().unwrap().square(), c);
|
||||
|
||||
// 2155129644831861015726826462986972654175647013268275306775721078997042729172900466542651176384766902407257452753362*u + 2796889544896299244102912275102369318775038861758288697415827248356648685135290329705805931514906495247464901062529
|
||||
// is nonsquare.
|
||||
assert!(bool::from(
|
||||
Fp2 {
|
||||
c0: Fp::from_raw_unchecked([
|
||||
0xc5fa_1bc8_fd00_d7f6,
|
||||
0x3830_ca45_4606_003b,
|
||||
0x2b28_7f11_04b1_02da,
|
||||
0xa7fb_30f2_8230_f23e,
|
||||
0x339c_db9e_e953_dbf0,
|
||||
0x0d78_ec51_d989_fc57,
|
||||
]),
|
||||
c1: Fp::from_raw_unchecked([
|
||||
0x27ec_4898_cf87_f613,
|
||||
0x9de1_394e_1abb_05a5,
|
||||
0x0947_f85d_c170_fc14,
|
||||
0x586f_bc69_6b61_14b7,
|
||||
0x2b34_75a4_077d_7169,
|
||||
0x13e1_c895_cc4b_6c22,
|
||||
])
|
||||
}
|
||||
.sqrt()
|
||||
.is_none()
|
||||
));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_inversion() {
|
||||
let a = Fp2 {
|
||||
c0: Fp::from_raw_unchecked([
|
||||
0x1128_ecad_6754_9455,
|
||||
0x9e7a_1cff_3a4e_a1a8,
|
||||
0xeb20_8d51_e08b_cf27,
|
||||
0xe98a_d408_11f5_fc2b,
|
||||
0x736c_3a59_232d_511d,
|
||||
0x10ac_d42d_29cf_cbb6,
|
||||
]),
|
||||
c1: Fp::from_raw_unchecked([
|
||||
0xd328_e37c_c2f5_8d41,
|
||||
0x948d_f085_8a60_5869,
|
||||
0x6032_f9d5_6f93_a573,
|
||||
0x2be4_83ef_3fff_dc87,
|
||||
0x30ef_61f8_8f48_3c2a,
|
||||
0x1333_f55a_3572_5be0,
|
||||
]),
|
||||
};
|
||||
|
||||
let b = Fp2 {
|
||||
c0: Fp::from_raw_unchecked([
|
||||
0x0581_a133_3d4f_48a6,
|
||||
0x5824_2f6e_f074_8500,
|
||||
0x0292_c955_349e_6da5,
|
||||
0xba37_721d_dd95_fcd0,
|
||||
0x70d1_6790_3aa5_dfc5,
|
||||
0x1189_5e11_8b58_a9d5,
|
||||
]),
|
||||
c1: Fp::from_raw_unchecked([
|
||||
0x0eda_09d2_d7a8_5d17,
|
||||
0x8808_e137_a7d1_a2cf,
|
||||
0x43ae_2625_c1ff_21db,
|
||||
0xf85a_c9fd_f7a7_4c64,
|
||||
0x8fcc_dda5_b8da_9738,
|
||||
0x08e8_4f0c_b32c_d17d,
|
||||
]),
|
||||
};
|
||||
|
||||
assert_eq!(a.invert().unwrap(), b);
|
||||
|
||||
assert!(bool::from(Fp2::zero().invert().is_none()));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_lexicographic_largest() {
|
||||
assert!(!bool::from(Fp2::zero().lexicographically_largest()));
|
||||
assert!(!bool::from(Fp2::one().lexicographically_largest()));
|
||||
assert!(bool::from(
|
||||
Fp2 {
|
||||
c0: Fp::from_raw_unchecked([
|
||||
0x1128_ecad_6754_9455,
|
||||
0x9e7a_1cff_3a4e_a1a8,
|
||||
0xeb20_8d51_e08b_cf27,
|
||||
0xe98a_d408_11f5_fc2b,
|
||||
0x736c_3a59_232d_511d,
|
||||
0x10ac_d42d_29cf_cbb6,
|
||||
]),
|
||||
c1: Fp::from_raw_unchecked([
|
||||
0xd328_e37c_c2f5_8d41,
|
||||
0x948d_f085_8a60_5869,
|
||||
0x6032_f9d5_6f93_a573,
|
||||
0x2be4_83ef_3fff_dc87,
|
||||
0x30ef_61f8_8f48_3c2a,
|
||||
0x1333_f55a_3572_5be0,
|
||||
]),
|
||||
}
|
||||
.lexicographically_largest()
|
||||
));
|
||||
assert!(!bool::from(
|
||||
Fp2 {
|
||||
c0: -Fp::from_raw_unchecked([
|
||||
0x1128_ecad_6754_9455,
|
||||
0x9e7a_1cff_3a4e_a1a8,
|
||||
0xeb20_8d51_e08b_cf27,
|
||||
0xe98a_d408_11f5_fc2b,
|
||||
0x736c_3a59_232d_511d,
|
||||
0x10ac_d42d_29cf_cbb6,
|
||||
]),
|
||||
c1: -Fp::from_raw_unchecked([
|
||||
0xd328_e37c_c2f5_8d41,
|
||||
0x948d_f085_8a60_5869,
|
||||
0x6032_f9d5_6f93_a573,
|
||||
0x2be4_83ef_3fff_dc87,
|
||||
0x30ef_61f8_8f48_3c2a,
|
||||
0x1333_f55a_3572_5be0,
|
||||
]),
|
||||
}
|
||||
.lexicographically_largest()
|
||||
));
|
||||
assert!(!bool::from(
|
||||
Fp2 {
|
||||
c0: Fp::from_raw_unchecked([
|
||||
0x1128_ecad_6754_9455,
|
||||
0x9e7a_1cff_3a4e_a1a8,
|
||||
0xeb20_8d51_e08b_cf27,
|
||||
0xe98a_d408_11f5_fc2b,
|
||||
0x736c_3a59_232d_511d,
|
||||
0x10ac_d42d_29cf_cbb6,
|
||||
]),
|
||||
c1: Fp::zero(),
|
||||
}
|
||||
.lexicographically_largest()
|
||||
));
|
||||
assert!(bool::from(
|
||||
Fp2 {
|
||||
c0: -Fp::from_raw_unchecked([
|
||||
0x1128_ecad_6754_9455,
|
||||
0x9e7a_1cff_3a4e_a1a8,
|
||||
0xeb20_8d51_e08b_cf27,
|
||||
0xe98a_d408_11f5_fc2b,
|
||||
0x736c_3a59_232d_511d,
|
||||
0x10ac_d42d_29cf_cbb6,
|
||||
]),
|
||||
c1: Fp::zero(),
|
||||
}
|
||||
.lexicographically_largest()
|
||||
));
|
||||
}
|
|
@ -1,513 +0,0 @@
|
|||
use crate::fp::*;
|
||||
use crate::fp2::*;
|
||||
|
||||
use core::fmt;
|
||||
use core::ops::{Add, AddAssign, Mul, MulAssign, Neg, Sub, SubAssign};
|
||||
use rand_core::RngCore;
|
||||
use subtle::{Choice, ConditionallySelectable, ConstantTimeEq, CtOption};
|
||||
|
||||
/// This represents an element $c_0 + c_1 v + c_2 v^2$ of $\mathbb{F}_{p^6} = \mathbb{F}_{p^2} / v^3 - u - 1$.
|
||||
pub struct Fp6 {
|
||||
pub c0: Fp2,
|
||||
pub c1: Fp2,
|
||||
pub c2: Fp2,
|
||||
}
|
||||
|
||||
impl From<Fp> for Fp6 {
|
||||
fn from(f: Fp) -> Fp6 {
|
||||
Fp6 {
|
||||
c0: Fp2::from(f),
|
||||
c1: Fp2::zero(),
|
||||
c2: Fp2::zero(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl From<Fp2> for Fp6 {
|
||||
fn from(f: Fp2) -> Fp6 {
|
||||
Fp6 {
|
||||
c0: f,
|
||||
c1: Fp2::zero(),
|
||||
c2: Fp2::zero(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl PartialEq for Fp6 {
|
||||
fn eq(&self, other: &Fp6) -> bool {
|
||||
self.ct_eq(other).into()
|
||||
}
|
||||
}
|
||||
|
||||
impl Copy for Fp6 {}
|
||||
impl Clone for Fp6 {
|
||||
#[inline]
|
||||
fn clone(&self) -> Self {
|
||||
*self
|
||||
}
|
||||
}
|
||||
|
||||
impl Default for Fp6 {
|
||||
fn default() -> Self {
|
||||
Fp6::zero()
|
||||
}
|
||||
}
|
||||
|
||||
impl fmt::Debug for Fp6 {
|
||||
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
||||
write!(f, "{:?} + ({:?})*v + ({:?})*v^2", self.c0, self.c1, self.c2)
|
||||
}
|
||||
}
|
||||
|
||||
impl ConditionallySelectable for Fp6 {
|
||||
#[inline(always)]
|
||||
fn conditional_select(a: &Self, b: &Self, choice: Choice) -> Self {
|
||||
Fp6 {
|
||||
c0: Fp2::conditional_select(&a.c0, &b.c0, choice),
|
||||
c1: Fp2::conditional_select(&a.c1, &b.c1, choice),
|
||||
c2: Fp2::conditional_select(&a.c2, &b.c2, choice),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl ConstantTimeEq for Fp6 {
|
||||
#[inline(always)]
|
||||
fn ct_eq(&self, other: &Self) -> Choice {
|
||||
self.c0.ct_eq(&other.c0) & self.c1.ct_eq(&other.c1) & self.c2.ct_eq(&other.c2)
|
||||
}
|
||||
}
|
||||
|
||||
impl Fp6 {
|
||||
#[inline]
|
||||
pub fn zero() -> Self {
|
||||
Fp6 {
|
||||
c0: Fp2::zero(),
|
||||
c1: Fp2::zero(),
|
||||
c2: Fp2::zero(),
|
||||
}
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn one() -> Self {
|
||||
Fp6 {
|
||||
c0: Fp2::one(),
|
||||
c1: Fp2::zero(),
|
||||
c2: Fp2::zero(),
|
||||
}
|
||||
}
|
||||
|
||||
pub(crate) fn random<R: RngCore + ?Sized>(rng: &mut R) -> Self {
|
||||
Fp6 {
|
||||
c0: Fp2::random(rng),
|
||||
c1: Fp2::random(rng),
|
||||
c2: Fp2::random(rng),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn mul_by_1(&self, c1: &Fp2) -> Fp6 {
|
||||
let b_b = self.c1 * c1;
|
||||
|
||||
let t1 = (self.c1 + self.c2) * c1 - b_b;
|
||||
let t1 = t1.mul_by_nonresidue();
|
||||
|
||||
let t2 = (self.c0 + self.c1) * c1 - b_b;
|
||||
|
||||
Fp6 {
|
||||
c0: t1,
|
||||
c1: t2,
|
||||
c2: b_b,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn mul_by_01(&self, c0: &Fp2, c1: &Fp2) -> Fp6 {
|
||||
let a_a = self.c0 * c0;
|
||||
let b_b = self.c1 * c1;
|
||||
|
||||
let t1 = (self.c1 + self.c2) * c1 - b_b;
|
||||
let t1 = t1.mul_by_nonresidue() + a_a;
|
||||
|
||||
let t2 = (c0 + c1) * (self.c0 + self.c1) - a_a - b_b;
|
||||
|
||||
let t3 = (self.c0 + self.c2) * c0 - a_a + b_b;
|
||||
|
||||
Fp6 {
|
||||
c0: t1,
|
||||
c1: t2,
|
||||
c2: t3,
|
||||
}
|
||||
}
|
||||
|
||||
/// Multiply by quadratic nonresidue v.
|
||||
pub fn mul_by_nonresidue(&self) -> Self {
|
||||
// Given a + bv + cv^2, this produces
|
||||
// av + bv^2 + cv^3
|
||||
// but because v^3 = u + 1, we have
|
||||
// c(u + 1) + av + v^2
|
||||
|
||||
Fp6 {
|
||||
c0: self.c2.mul_by_nonresidue(),
|
||||
c1: self.c0,
|
||||
c2: self.c1,
|
||||
}
|
||||
}
|
||||
|
||||
/// Raises this element to p.
|
||||
#[inline(always)]
|
||||
pub fn frobenius_map(&self) -> Self {
|
||||
let c0 = self.c0.frobenius_map();
|
||||
let c1 = self.c1.frobenius_map();
|
||||
let c2 = self.c2.frobenius_map();
|
||||
|
||||
// c1 = c1 * (u + 1)^((p - 1) / 3)
|
||||
let c1 = c1
|
||||
* Fp2 {
|
||||
c0: Fp::zero(),
|
||||
c1: Fp::from_raw_unchecked([
|
||||
0xcd03_c9e4_8671_f071,
|
||||
0x5dab_2246_1fcd_a5d2,
|
||||
0x5870_42af_d385_1b95,
|
||||
0x8eb6_0ebe_01ba_cb9e,
|
||||
0x03f9_7d6e_83d0_50d2,
|
||||
0x18f0_2065_5463_8741,
|
||||
]),
|
||||
};
|
||||
|
||||
// c2 = c2 * (u + 1)^((2p - 2) / 3)
|
||||
let c2 = c2
|
||||
* Fp2 {
|
||||
c0: Fp::from_raw_unchecked([
|
||||
0x890d_c9e4_8675_45c3,
|
||||
0x2af3_2253_3285_a5d5,
|
||||
0x5088_0866_309b_7e2c,
|
||||
0xa20d_1b8c_7e88_1024,
|
||||
0x14e4_f04f_e2db_9068,
|
||||
0x14e5_6d3f_1564_853a,
|
||||
]),
|
||||
c1: Fp::zero(),
|
||||
};
|
||||
|
||||
Fp6 { c0, c1, c2 }
|
||||
}
|
||||
|
||||
#[inline(always)]
|
||||
pub fn is_zero(&self) -> Choice {
|
||||
self.c0.is_zero() & self.c1.is_zero() & self.c2.is_zero()
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn square(&self) -> Self {
|
||||
let s0 = self.c0.square();
|
||||
let ab = self.c0 * self.c1;
|
||||
let s1 = ab + ab;
|
||||
let s2 = (self.c0 - self.c1 + self.c2).square();
|
||||
let bc = self.c1 * self.c2;
|
||||
let s3 = bc + bc;
|
||||
let s4 = self.c2.square();
|
||||
|
||||
Fp6 {
|
||||
c0: s3.mul_by_nonresidue() + s0,
|
||||
c1: s4.mul_by_nonresidue() + s1,
|
||||
c2: s1 + s2 + s3 - s0 - s4,
|
||||
}
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn invert(&self) -> CtOption<Self> {
|
||||
let c0 = (self.c1 * self.c2).mul_by_nonresidue();
|
||||
let c0 = self.c0.square() - c0;
|
||||
|
||||
let c1 = self.c2.square().mul_by_nonresidue();
|
||||
let c1 = c1 - (self.c0 * self.c1);
|
||||
|
||||
let c2 = self.c1.square();
|
||||
let c2 = c2 - (self.c0 * self.c2);
|
||||
|
||||
let tmp = ((self.c1 * c2) + (self.c2 * c1)).mul_by_nonresidue();
|
||||
let tmp = tmp + (self.c0 * c0);
|
||||
|
||||
tmp.invert().map(|t| Fp6 {
|
||||
c0: t * c0,
|
||||
c1: t * c1,
|
||||
c2: t * c2,
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a, 'b> Mul<&'b Fp6> for &'a Fp6 {
|
||||
type Output = Fp6;
|
||||
|
||||
#[inline]
|
||||
fn mul(self, other: &'b Fp6) -> Self::Output {
|
||||
let aa = self.c0 * other.c0;
|
||||
let bb = self.c1 * other.c1;
|
||||
let cc = self.c2 * other.c2;
|
||||
|
||||
let t1 = other.c1 + other.c2;
|
||||
let tmp = self.c1 + self.c2;
|
||||
let t1 = t1 * tmp;
|
||||
let t1 = t1 - bb;
|
||||
let t1 = t1 - cc;
|
||||
let t1 = t1.mul_by_nonresidue();
|
||||
let t1 = t1 + aa;
|
||||
|
||||
let t3 = other.c0 + other.c2;
|
||||
let tmp = self.c0 + self.c2;
|
||||
let t3 = t3 * tmp;
|
||||
let t3 = t3 - aa;
|
||||
let t3 = t3 + bb;
|
||||
let t3 = t3 - cc;
|
||||
|
||||
let t2 = other.c0 + other.c1;
|
||||
let tmp = self.c0 + self.c1;
|
||||
let t2 = t2 * tmp;
|
||||
let t2 = t2 - aa;
|
||||
let t2 = t2 - bb;
|
||||
let cc = cc.mul_by_nonresidue();
|
||||
let t2 = t2 + cc;
|
||||
|
||||
Fp6 {
|
||||
c0: t1,
|
||||
c1: t2,
|
||||
c2: t3,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a, 'b> Add<&'b Fp6> for &'a Fp6 {
|
||||
type Output = Fp6;
|
||||
|
||||
#[inline]
|
||||
fn add(self, rhs: &'b Fp6) -> Self::Output {
|
||||
Fp6 {
|
||||
c0: self.c0 + rhs.c0,
|
||||
c1: self.c1 + rhs.c1,
|
||||
c2: self.c2 + rhs.c2,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> Neg for &'a Fp6 {
|
||||
type Output = Fp6;
|
||||
|
||||
#[inline]
|
||||
fn neg(self) -> Self::Output {
|
||||
Fp6 {
|
||||
c0: -self.c0,
|
||||
c1: -self.c1,
|
||||
c2: -self.c2,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Neg for Fp6 {
|
||||
type Output = Fp6;
|
||||
|
||||
#[inline]
|
||||
fn neg(self) -> Self::Output {
|
||||
-&self
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a, 'b> Sub<&'b Fp6> for &'a Fp6 {
|
||||
type Output = Fp6;
|
||||
|
||||
#[inline]
|
||||
fn sub(self, rhs: &'b Fp6) -> Self::Output {
|
||||
Fp6 {
|
||||
c0: self.c0 - rhs.c0,
|
||||
c1: self.c1 - rhs.c1,
|
||||
c2: self.c2 - rhs.c2,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl_binops_additive!(Fp6, Fp6);
|
||||
impl_binops_multiplicative!(Fp6, Fp6);
|
||||
|
||||
#[test]
|
||||
fn test_arithmetic() {
|
||||
use crate::fp::*;
|
||||
|
||||
let a = Fp6 {
|
||||
c0: Fp2 {
|
||||
c0: Fp::from_raw_unchecked([
|
||||
0x47f9_cb98_b1b8_2d58,
|
||||
0x5fe9_11eb_a3aa_1d9d,
|
||||
0x96bf_1b5f_4dd8_1db3,
|
||||
0x8100_d27c_c925_9f5b,
|
||||
0xafa2_0b96_7464_0eab,
|
||||
0x09bb_cea7_d8d9_497d,
|
||||
]),
|
||||
c1: Fp::from_raw_unchecked([
|
||||
0x0303_cb98_b166_2daa,
|
||||
0xd931_10aa_0a62_1d5a,
|
||||
0xbfa9_820c_5be4_a468,
|
||||
0x0ba3_643e_cb05_a348,
|
||||
0xdc35_34bb_1f1c_25a6,
|
||||
0x06c3_05bb_19c0_e1c1,
|
||||
]),
|
||||
},
|
||||
c1: Fp2 {
|
||||
c0: Fp::from_raw_unchecked([
|
||||
0x46f9_cb98_b162_d858,
|
||||
0x0be9_109c_f7aa_1d57,
|
||||
0xc791_bc55_fece_41d2,
|
||||
0xf84c_5770_4e38_5ec2,
|
||||
0xcb49_c1d9_c010_e60f,
|
||||
0x0acd_b8e1_58bf_e3c8,
|
||||
]),
|
||||
c1: Fp::from_raw_unchecked([
|
||||
0x8aef_cb98_b15f_8306,
|
||||
0x3ea1_108f_e4f2_1d54,
|
||||
0xcf79_f69f_a1b7_df3b,
|
||||
0xe4f5_4aa1_d16b_1a3c,
|
||||
0xba5e_4ef8_6105_a679,
|
||||
0x0ed8_6c07_97be_e5cf,
|
||||
]),
|
||||
},
|
||||
c2: Fp2 {
|
||||
c0: Fp::from_raw_unchecked([
|
||||
0xcee5_cb98_b15c_2db4,
|
||||
0x7159_1082_d23a_1d51,
|
||||
0xd762_30e9_44a1_7ca4,
|
||||
0xd19e_3dd3_549d_d5b6,
|
||||
0xa972_dc17_01fa_66e3,
|
||||
0x12e3_1f2d_d6bd_e7d6,
|
||||
]),
|
||||
c1: Fp::from_raw_unchecked([
|
||||
0xad2a_cb98_b173_2d9d,
|
||||
0x2cfd_10dd_0696_1d64,
|
||||
0x0739_6b86_c6ef_24e8,
|
||||
0xbd76_e2fd_b1bf_c820,
|
||||
0x6afe_a7f6_de94_d0d5,
|
||||
0x1099_4b0c_5744_c040,
|
||||
]),
|
||||
},
|
||||
};
|
||||
|
||||
let b = Fp6 {
|
||||
c0: Fp2 {
|
||||
c0: Fp::from_raw_unchecked([
|
||||
0xf120_cb98_b16f_d84b,
|
||||
0x5fb5_10cf_f3de_1d61,
|
||||
0x0f21_a5d0_69d8_c251,
|
||||
0xaa1f_d62f_34f2_839a,
|
||||
0x5a13_3515_7f89_913f,
|
||||
0x14a3_fe32_9643_c247,
|
||||
]),
|
||||
c1: Fp::from_raw_unchecked([
|
||||
0x3516_cb98_b16c_82f9,
|
||||
0x926d_10c2_e126_1d5f,
|
||||
0x1709_e01a_0cc2_5fba,
|
||||
0x96c8_c960_b825_3f14,
|
||||
0x4927_c234_207e_51a9,
|
||||
0x18ae_b158_d542_c44e,
|
||||
]),
|
||||
},
|
||||
c1: Fp2 {
|
||||
c0: Fp::from_raw_unchecked([
|
||||
0xbf0d_cb98_b169_82fc,
|
||||
0xa679_10b7_1d1a_1d5c,
|
||||
0xb7c1_47c2_b8fb_06ff,
|
||||
0x1efa_710d_47d2_e7ce,
|
||||
0xed20_a79c_7e27_653c,
|
||||
0x02b8_5294_dac1_dfba,
|
||||
]),
|
||||
c1: Fp::from_raw_unchecked([
|
||||
0x9d52_cb98_b180_82e5,
|
||||
0x621d_1111_5176_1d6f,
|
||||
0xe798_8260_3b48_af43,
|
||||
0x0ad3_1637_a4f4_da37,
|
||||
0xaeac_737c_5ac1_cf2e,
|
||||
0x006e_7e73_5b48_b824,
|
||||
]),
|
||||
},
|
||||
c2: Fp2 {
|
||||
c0: Fp::from_raw_unchecked([
|
||||
0xe148_cb98_b17d_2d93,
|
||||
0x94d5_1104_3ebe_1d6c,
|
||||
0xef80_bca9_de32_4cac,
|
||||
0xf77c_0969_2827_95b1,
|
||||
0x9dc1_009a_fbb6_8f97,
|
||||
0x0479_3199_9a47_ba2b,
|
||||
]),
|
||||
c1: Fp::from_raw_unchecked([
|
||||
0x253e_cb98_b179_d841,
|
||||
0xc78d_10f7_2c06_1d6a,
|
||||
0xf768_f6f3_811b_ea15,
|
||||
0xe424_fc9a_ab5a_512b,
|
||||
0x8cd5_8db9_9cab_5001,
|
||||
0x0883_e4bf_d946_bc32,
|
||||
]),
|
||||
},
|
||||
};
|
||||
|
||||
let c = Fp6 {
|
||||
c0: Fp2 {
|
||||
c0: Fp::from_raw_unchecked([
|
||||
0x6934_cb98_b176_82ef,
|
||||
0xfa45_10ea_194e_1d67,
|
||||
0xff51_313d_2405_877e,
|
||||
0xd0cd_efcc_2e8d_0ca5,
|
||||
0x7bea_1ad8_3da0_106b,
|
||||
0x0c8e_97e6_1845_be39,
|
||||
]),
|
||||
c1: Fp::from_raw_unchecked([
|
||||
0x4779_cb98_b18d_82d8,
|
||||
0xb5e9_1144_4daa_1d7a,
|
||||
0x2f28_6bda_a653_2fc2,
|
||||
0xbca6_94f6_8bae_ff0f,
|
||||
0x3d75_e6b8_1a3a_7a5d,
|
||||
0x0a44_c3c4_98cc_96a3,
|
||||
]),
|
||||
},
|
||||
c1: Fp2 {
|
||||
c0: Fp::from_raw_unchecked([
|
||||
0x8b6f_cb98_b18a_2d86,
|
||||
0xe8a1_1137_3af2_1d77,
|
||||
0x3710_a624_493c_cd2b,
|
||||
0xa94f_8828_0ee1_ba89,
|
||||
0x2c8a_73d6_bb2f_3ac7,
|
||||
0x0e4f_76ea_d7cb_98aa,
|
||||
]),
|
||||
c1: Fp::from_raw_unchecked([
|
||||
0xcf65_cb98_b186_d834,
|
||||
0x1b59_112a_283a_1d74,
|
||||
0x3ef8_e06d_ec26_6a95,
|
||||
0x95f8_7b59_9214_7603,
|
||||
0x1b9f_00f5_5c23_fb31,
|
||||
0x125a_2a11_16ca_9ab1,
|
||||
]),
|
||||
},
|
||||
c2: Fp2 {
|
||||
c0: Fp::from_raw_unchecked([
|
||||
0x135b_cb98_b183_82e2,
|
||||
0x4e11_111d_1582_1d72,
|
||||
0x46e1_1ab7_8f10_07fe,
|
||||
0x82a1_6e8b_1547_317d,
|
||||
0x0ab3_8e13_fd18_bb9b,
|
||||
0x1664_dd37_55c9_9cb8,
|
||||
]),
|
||||
c1: Fp::from_raw_unchecked([
|
||||
0xce65_cb98_b131_8334,
|
||||
0xc759_0fdb_7c3a_1d2e,
|
||||
0x6fcb_8164_9d1c_8eb3,
|
||||
0x0d44_004d_1727_356a,
|
||||
0x3746_b738_a7d0_d296,
|
||||
0x136c_144a_96b1_34fc,
|
||||
]),
|
||||
},
|
||||
};
|
||||
|
||||
assert_eq!(a.square(), a * a);
|
||||
assert_eq!(b.square(), b * b);
|
||||
assert_eq!(c.square(), c * c);
|
||||
|
||||
assert_eq!((a + b) * c.square(), (c * c * a) + (c * c * b));
|
||||
|
||||
assert_eq!(
|
||||
a.invert().unwrap() * b.invert().unwrap(),
|
||||
(a * b).invert().unwrap()
|
||||
);
|
||||
assert_eq!(a.invert().unwrap() * a, Fp6::one());
|
||||
}
|
1678
bls12_381/src/g1.rs
1678
bls12_381/src/g1.rs
File diff suppressed because it is too large
Load Diff
2142
bls12_381/src/g2.rs
2142
bls12_381/src/g2.rs
File diff suppressed because it is too large
Load Diff
|
@ -1,81 +0,0 @@
|
|||
//! # `bls12_381`
|
||||
//!
|
||||
//! This crate provides an implementation of the BLS12-381 pairing-friendly elliptic
|
||||
//! curve construction.
|
||||
//!
|
||||
//! * **This implementation has not been reviewed or audited. Use at your own risk.**
|
||||
//! * This implementation targets Rust `1.36` or later.
|
||||
//! * This implementation does not require the Rust standard library.
|
||||
//! * All operations are constant time unless explicitly noted.
|
||||
|
||||
#![no_std]
|
||||
#![cfg_attr(docsrs, feature(doc_cfg))]
|
||||
// Catch documentation errors caused by code changes.
|
||||
#![deny(intra_doc_link_resolution_failure)]
|
||||
#![deny(missing_debug_implementations)]
|
||||
#![deny(missing_docs)]
|
||||
#![deny(unsafe_code)]
|
||||
#![allow(clippy::too_many_arguments)]
|
||||
#![allow(clippy::many_single_char_names)]
|
||||
// This lint is described at
|
||||
// https://rust-lang.github.io/rust-clippy/master/index.html#suspicious_arithmetic_impl
|
||||
// In our library, some of the arithmetic involving extension fields will necessarily
|
||||
// involve various binary operators, and so this lint is triggered unnecessarily.
|
||||
#![allow(clippy::suspicious_arithmetic_impl)]
|
||||
|
||||
#[cfg(feature = "alloc")]
|
||||
extern crate alloc;
|
||||
|
||||
#[cfg(test)]
|
||||
#[macro_use]
|
||||
extern crate std;
|
||||
|
||||
#[cfg(test)]
|
||||
#[cfg(feature = "groups")]
|
||||
mod tests;
|
||||
|
||||
#[macro_use]
|
||||
mod util;
|
||||
|
||||
/// Notes about how the BLS12-381 elliptic curve is designed, specified
|
||||
/// and implemented by this library.
|
||||
pub mod notes {
|
||||
pub mod design;
|
||||
pub mod serialization;
|
||||
}
|
||||
|
||||
mod scalar;
|
||||
|
||||
pub use scalar::Scalar;
|
||||
|
||||
#[cfg(feature = "groups")]
|
||||
mod fp;
|
||||
#[cfg(feature = "groups")]
|
||||
mod fp2;
|
||||
#[cfg(feature = "groups")]
|
||||
mod g1;
|
||||
#[cfg(feature = "groups")]
|
||||
mod g2;
|
||||
|
||||
#[cfg(feature = "groups")]
|
||||
pub use g1::{G1Affine, G1Projective};
|
||||
#[cfg(feature = "groups")]
|
||||
pub use g2::{G2Affine, G2Projective};
|
||||
|
||||
#[cfg(feature = "groups")]
|
||||
mod fp12;
|
||||
#[cfg(feature = "groups")]
|
||||
mod fp6;
|
||||
|
||||
// The BLS parameter x for BLS12-381 is -0xd201000000010000
|
||||
const BLS_X: u64 = 0xd201_0000_0001_0000;
|
||||
const BLS_X_IS_NEGATIVE: bool = true;
|
||||
|
||||
#[cfg(feature = "pairings")]
|
||||
mod pairings;
|
||||
|
||||
#[cfg(feature = "pairings")]
|
||||
pub use pairings::{pairing, Bls12, Gt, MillerLoopResult};
|
||||
|
||||
#[cfg(all(feature = "pairings", feature = "alloc"))]
|
||||
pub use pairings::{multi_miller_loop, G2Prepared};
|
|
@ -1,62 +0,0 @@
|
|||
//! # Design of BLS12-381
|
||||
//! ## Fixed Generators
|
||||
//!
|
||||
//! Although any generator produced by hashing to $\mathbb{G}_1$ or $\mathbb{G}_2$ is
|
||||
//! safe to use in a cryptographic protocol, we specify some simple, fixed generators.
|
||||
//!
|
||||
//! In order to derive these generators, we select the lexicographically smallest
|
||||
//! valid $x$-coordinate and the lexicographically smallest corresponding $y$-coordinate,
|
||||
//! and then scale the resulting point by the cofactor, such that the result is not the
|
||||
//! identity. This results in the following fixed generators:
|
||||
//!
|
||||
//! 1. $\mathbb{G}_1$
|
||||
//! * $x = 3685416753713387016781088315183077757961620795782546409894578378688607592378376318836054947676345821548104185464507$
|
||||
//! * $y = 1339506544944476473020471379941921221584933875938349620426543736416511423956333506472724655353366534992391756441569$
|
||||
//! 2. $\mathbb{G}_2$
|
||||
//! * $x = 352701069587466618187139116011060144890029952792775240219908644239793785735715026873347600343865175952761926303160 + 3059144344244213709971259814753781636986470325476647558659373206291635324768958432433509563104347017837885763365758 u$
|
||||
//! * $y = 1985150602287291935568054521177171638300868978215655730859378665066344726373823718423869104263333984641494340347905 + 927553665492332455747201965776037880757740193453592970025027978793976877002675564980949289727957565575433344219582 u$
|
||||
//!
|
||||
//! This can be derived using the following sage script:
|
||||
//!
|
||||
//! ```text
|
||||
//! param = -0xd201000000010000
|
||||
//! def r(x):
|
||||
//! return (x**4) - (x**2) + 1
|
||||
//! def q(x):
|
||||
//! return (((x - 1) ** 2) * ((x**4) - (x**2) + 1) // 3) + x
|
||||
//! def g1_h(x):
|
||||
//! return ((x-1)**2) // 3
|
||||
//! def g2_h(x):
|
||||
//! return ((x**8) - (4 * (x**7)) + (5 * (x**6)) - (4 * (x**4)) + (6 * (x**3)) - (4 * (x**2)) - (4*x) + 13) // 9
|
||||
//! q = q(param)
|
||||
//! r = r(param)
|
||||
//! Fq = GF(q)
|
||||
//! ec = EllipticCurve(Fq, [0, 4])
|
||||
//! def psqrt(v):
|
||||
//! assert(not v.is_zero())
|
||||
//! a = sqrt(v)
|
||||
//! b = -a
|
||||
//! if a < b:
|
||||
//! return a
|
||||
//! else:
|
||||
//! return b
|
||||
//! for x in range(0,100):
|
||||
//! rhs = Fq(x)^3 + 4
|
||||
//! if rhs.is_square():
|
||||
//! y = psqrt(rhs)
|
||||
//! p = ec(x, y) * g1_h(param)
|
||||
//! if (not p.is_zero()) and (p * r).is_zero():
|
||||
//! print("g1 generator: {}".format(p))
|
||||
//! break
|
||||
//! Fq2.<i> = GF(q^2, modulus=[1, 0, 1])
|
||||
//! ec2 = EllipticCurve(Fq2, [0, (4 * (1 + i))])
|
||||
//! assert(ec2.order() == (r * g2_h(param)))
|
||||
//! for x in range(0,100):
|
||||
//! rhs = (Fq2(x))^3 + (4 * (1 + i))
|
||||
//! if rhs.is_square():
|
||||
//! y = psqrt(rhs)
|
||||
//! p = ec2(Fq2(x), y) * g2_h(param)
|
||||
//! if not p.is_zero() and (p * r).is_zero():
|
||||
//! print("g2 generator: {}".format(p))
|
||||
//! break
|
||||
//! ```
|
|
@ -1,29 +0,0 @@
|
|||
//! # BLS12-381 serialization
|
||||
//!
|
||||
//! * $\mathbb{F}\_p$ elements are encoded in big-endian form. They occupy 48
|
||||
//! bytes in this form.
|
||||
//! * $\mathbb{F}\_{p^2}$ elements are encoded in big-endian form, meaning that
|
||||
//! the $\mathbb{F}\_{p^2}$ element $c\_0 + c\_1 \cdot u$ is represented by the
|
||||
//! $\mathbb{F}\_p$ element $c\_1$ followed by the $\mathbb{F}\_p$ element $c\_0$.
|
||||
//! This means $\mathbb{F}_{p^2}$ elements occupy 96 bytes in this form.
|
||||
//! * The group $\mathbb{G}\_1$ uses $\mathbb{F}\_p$ elements for coordinates. The
|
||||
//! group $\mathbb{G}\_2$ uses $\mathbb{F}_{p^2}$ elements for coordinates.
|
||||
//! * $\mathbb{G}\_1$ and $\mathbb{G}\_2$ elements can be encoded in uncompressed
|
||||
//! form (the x-coordinate followed by the y-coordinate) or in compressed form
|
||||
//! (just the x-coordinate). $\mathbb{G}\_1$ elements occupy 96 bytes in
|
||||
//! uncompressed form, and 48 bytes in compressed form. $\mathbb{G}\_2$
|
||||
//! elements occupy 192 bytes in uncompressed form, and 96 bytes in compressed
|
||||
//! form.
|
||||
//!
|
||||
//! The most-significant three bits of a $\mathbb{G}\_1$ or $\mathbb{G}\_2$
|
||||
//! encoding should be masked away before the coordinate(s) are interpreted.
|
||||
//! These bits are used to unambiguously represent the underlying element:
|
||||
//! * The most significant bit, when set, indicates that the point is in
|
||||
//! compressed form. Otherwise, the point is in uncompressed form.
|
||||
//! * The second-most significant bit indicates that the point is at infinity.
|
||||
//! If this bit is set, the remaining bits of the group element's encoding
|
||||
//! should be set to zero.
|
||||
//! * The third-most significant bit is set if (and only if) this point is in
|
||||
//! compressed form _and_ it is not the point at infinity _and_ its
|
||||
//! y-coordinate is the lexicographically largest of the two associated with
|
||||
//! the encoded x-coordinate.
|
|
@ -1,886 +0,0 @@
|
|||
use crate::fp::Fp;
|
||||
use crate::fp12::Fp12;
|
||||
use crate::fp2::Fp2;
|
||||
use crate::fp6::Fp6;
|
||||
use crate::{G1Affine, G1Projective, G2Affine, G2Projective, Scalar, BLS_X, BLS_X_IS_NEGATIVE};
|
||||
|
||||
use core::borrow::Borrow;
|
||||
use core::fmt;
|
||||
use core::iter::Sum;
|
||||
use core::ops::{Add, AddAssign, Mul, MulAssign, Neg, Sub, SubAssign};
|
||||
use group::Group;
|
||||
use pairing::{Engine, MultiMillerLoop, PairingCurveAffine};
|
||||
use rand_core::RngCore;
|
||||
use subtle::{Choice, ConditionallySelectable, ConstantTimeEq};
|
||||
|
||||
#[cfg(feature = "alloc")]
|
||||
use alloc::vec::Vec;
|
||||
|
||||
/// Represents results of a Miller loop, one of the most expensive portions
|
||||
/// of the pairing function. `MillerLoopResult`s cannot be compared with each
|
||||
/// other until `.final_exponentiation()` is called, which is also expensive.
|
||||
#[cfg_attr(docsrs, doc(cfg(feature = "pairings")))]
|
||||
#[derive(Copy, Clone, Debug)]
|
||||
pub struct MillerLoopResult(pub(crate) Fp12);
|
||||
|
||||
impl ConditionallySelectable for MillerLoopResult {
|
||||
fn conditional_select(a: &Self, b: &Self, choice: Choice) -> Self {
|
||||
MillerLoopResult(Fp12::conditional_select(&a.0, &b.0, choice))
|
||||
}
|
||||
}
|
||||
|
||||
impl MillerLoopResult {
|
||||
/// This performs a "final exponentiation" routine to convert the result
|
||||
/// of a Miller loop into an element of `Gt` with help of efficient squaring
|
||||
/// operation in the so-called `cyclotomic subgroup` of `Fq6` so that
|
||||
/// it can be compared with other elements of `Gt`.
|
||||
pub fn final_exponentiation(&self) -> Gt {
|
||||
#[must_use]
|
||||
fn fp4_square(a: Fp2, b: Fp2) -> (Fp2, Fp2) {
|
||||
let t0 = a.square();
|
||||
let t1 = b.square();
|
||||
let mut t2 = t1.mul_by_nonresidue();
|
||||
let c0 = t2 + t0;
|
||||
t2 = a + b;
|
||||
t2 = t2.square();
|
||||
t2 -= t0;
|
||||
let c1 = t2 - t1;
|
||||
|
||||
(c0, c1)
|
||||
}
|
||||
// Adaptation of Algorithm 5.5.4, Guide to Pairing-Based Cryptography
|
||||
// Faster Squaring in the Cyclotomic Subgroup of Sixth Degree Extensions
|
||||
// https://eprint.iacr.org/2009/565.pdf
|
||||
#[must_use]
|
||||
fn cyclotomic_square(f: Fp12) -> Fp12 {
|
||||
let mut z0 = f.c0.c0;
|
||||
let mut z4 = f.c0.c1;
|
||||
let mut z3 = f.c0.c2;
|
||||
let mut z2 = f.c1.c0;
|
||||
let mut z1 = f.c1.c1;
|
||||
let mut z5 = f.c1.c2;
|
||||
|
||||
let (t0, t1) = fp4_square(z0, z1);
|
||||
|
||||
// For A
|
||||
z0 = t0 - z0;
|
||||
z0 = z0 + z0 + t0;
|
||||
|
||||
z1 = t1 + z1;
|
||||
z1 = z1 + z1 + t1;
|
||||
|
||||
let (mut t0, t1) = fp4_square(z2, z3);
|
||||
let (t2, t3) = fp4_square(z4, z5);
|
||||
|
||||
// For C
|
||||
z4 = t0 - z4;
|
||||
z4 = z4 + z4 + t0;
|
||||
|
||||
z5 = t1 + z5;
|
||||
z5 = z5 + z5 + t1;
|
||||
|
||||
// For B
|
||||
t0 = t3.mul_by_nonresidue();
|
||||
z2 = t0 + z2;
|
||||
z2 = z2 + z2 + t0;
|
||||
|
||||
z3 = t2 - z3;
|
||||
z3 = z3 + z3 + t2;
|
||||
|
||||
Fp12 {
|
||||
c0: Fp6 {
|
||||
c0: z0,
|
||||
c1: z4,
|
||||
c2: z3,
|
||||
},
|
||||
c1: Fp6 {
|
||||
c0: z2,
|
||||
c1: z1,
|
||||
c2: z5,
|
||||
},
|
||||
}
|
||||
}
|
||||
#[must_use]
|
||||
fn cycolotomic_exp(f: Fp12) -> Fp12 {
|
||||
let x = BLS_X;
|
||||
let mut tmp = Fp12::one();
|
||||
let mut found_one = false;
|
||||
for i in (0..64).rev().map(|b| ((x >> b) & 1) == 1) {
|
||||
if found_one {
|
||||
tmp = cyclotomic_square(tmp)
|
||||
} else {
|
||||
found_one = i;
|
||||
}
|
||||
|
||||
if i {
|
||||
tmp *= f;
|
||||
}
|
||||
}
|
||||
|
||||
tmp.conjugate()
|
||||
}
|
||||
|
||||
let mut f = self.0;
|
||||
let mut t0 = f
|
||||
.frobenius_map()
|
||||
.frobenius_map()
|
||||
.frobenius_map()
|
||||
.frobenius_map()
|
||||
.frobenius_map()
|
||||
.frobenius_map();
|
||||
Gt(f.invert()
|
||||
.map(|mut t1| {
|
||||
let mut t2 = t0 * t1;
|
||||
t1 = t2;
|
||||
t2 = t2.frobenius_map().frobenius_map();
|
||||
t2 *= t1;
|
||||
t1 = cyclotomic_square(t2).conjugate();
|
||||
let mut t3 = cycolotomic_exp(t2);
|
||||
let mut t4 = cyclotomic_square(t3);
|
||||
let mut t5 = t1 * t3;
|
||||
t1 = cycolotomic_exp(t5);
|
||||
t0 = cycolotomic_exp(t1);
|
||||
let mut t6 = cycolotomic_exp(t0);
|
||||
t6 *= t4;
|
||||
t4 = cycolotomic_exp(t6);
|
||||
t5 = t5.conjugate();
|
||||
t4 *= t5 * t2;
|
||||
t5 = t2.conjugate();
|
||||
t1 *= t2;
|
||||
t1 = t1.frobenius_map().frobenius_map().frobenius_map();
|
||||
t6 *= t5;
|
||||
t6 = t6.frobenius_map();
|
||||
t3 *= t0;
|
||||
t3 = t3.frobenius_map().frobenius_map();
|
||||
t3 *= t1;
|
||||
t3 *= t6;
|
||||
f = t3 * t4;
|
||||
|
||||
f
|
||||
})
|
||||
// We unwrap() because `MillerLoopResult` can only be constructed
|
||||
// by a function within this crate, and we uphold the invariant
|
||||
// that the enclosed value is nonzero.
|
||||
.unwrap())
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a, 'b> Add<&'b MillerLoopResult> for &'a MillerLoopResult {
|
||||
type Output = MillerLoopResult;
|
||||
|
||||
#[inline]
|
||||
fn add(self, rhs: &'b MillerLoopResult) -> MillerLoopResult {
|
||||
MillerLoopResult(self.0 * rhs.0)
|
||||
}
|
||||
}
|
||||
|
||||
impl_add_binop_specify_output!(MillerLoopResult, MillerLoopResult, MillerLoopResult);
|
||||
|
||||
/// This is an element of $\mathbb{G}_T$, the target group of the pairing function. As with
|
||||
/// $\mathbb{G}_1$ and $\mathbb{G}_2$ this group has order $q$.
|
||||
///
|
||||
/// Typically, $\mathbb{G}_T$ is written multiplicatively but we will write it additively to
|
||||
/// keep code and abstractions consistent.
|
||||
#[cfg_attr(docsrs, doc(cfg(feature = "pairings")))]
|
||||
#[derive(Copy, Clone, Debug, Default)]
|
||||
pub struct Gt(pub(crate) Fp12);
|
||||
|
||||
impl fmt::Display for Gt {
|
||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||
write!(f, "{:?}", self)
|
||||
}
|
||||
}
|
||||
|
||||
impl ConstantTimeEq for Gt {
|
||||
fn ct_eq(&self, other: &Self) -> Choice {
|
||||
self.0.ct_eq(&other.0)
|
||||
}
|
||||
}
|
||||
|
||||
impl ConditionallySelectable for Gt {
|
||||
fn conditional_select(a: &Self, b: &Self, choice: Choice) -> Self {
|
||||
Gt(Fp12::conditional_select(&a.0, &b.0, choice))
|
||||
}
|
||||
}
|
||||
|
||||
impl Eq for Gt {}
|
||||
impl PartialEq for Gt {
|
||||
#[inline]
|
||||
fn eq(&self, other: &Self) -> bool {
|
||||
bool::from(self.ct_eq(other))
|
||||
}
|
||||
}
|
||||
|
||||
impl Gt {
|
||||
/// Returns the group identity, which is $1$.
|
||||
pub fn identity() -> Gt {
|
||||
Gt(Fp12::one())
|
||||
}
|
||||
|
||||
/// Doubles this group element.
|
||||
pub fn double(&self) -> Gt {
|
||||
Gt(self.0.square())
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> Neg for &'a Gt {
|
||||
type Output = Gt;
|
||||
|
||||
#[inline]
|
||||
fn neg(self) -> Gt {
|
||||
// The element is unitary, so we just conjugate.
|
||||
Gt(self.0.conjugate())
|
||||
}
|
||||
}
|
||||
|
||||
impl Neg for Gt {
|
||||
type Output = Gt;
|
||||
|
||||
#[inline]
|
||||
fn neg(self) -> Gt {
|
||||
-&self
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a, 'b> Add<&'b Gt> for &'a Gt {
|
||||
type Output = Gt;
|
||||
|
||||
#[inline]
|
||||
fn add(self, rhs: &'b Gt) -> Gt {
|
||||
Gt(self.0 * rhs.0)
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a, 'b> Sub<&'b Gt> for &'a Gt {
|
||||
type Output = Gt;
|
||||
|
||||
#[inline]
|
||||
fn sub(self, rhs: &'b Gt) -> Gt {
|
||||
self + (-rhs)
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a, 'b> Mul<&'b Scalar> for &'a Gt {
|
||||
type Output = Gt;
|
||||
|
||||
fn mul(self, other: &'b Scalar) -> Self::Output {
|
||||
let mut acc = Gt::identity();
|
||||
|
||||
// This is a simple double-and-add implementation of group element
|
||||
// multiplication, moving from most significant to least
|
||||
// significant bit of the scalar.
|
||||
//
|
||||
// We skip the leading bit because it's always unset for Fq
|
||||
// elements.
|
||||
for bit in other
|
||||
.to_bytes()
|
||||
.iter()
|
||||
.rev()
|
||||
.flat_map(|byte| (0..8).rev().map(move |i| Choice::from((byte >> i) & 1u8)))
|
||||
.skip(1)
|
||||
{
|
||||
acc = acc.double();
|
||||
acc = Gt::conditional_select(&acc, &(acc + self), bit);
|
||||
}
|
||||
|
||||
acc
|
||||
}
|
||||
}
|
||||
|
||||
impl_binops_additive!(Gt, Gt);
|
||||
impl_binops_multiplicative!(Gt, Scalar);
|
||||
|
||||
impl<T> Sum<T> for Gt
|
||||
where
|
||||
T: Borrow<Gt>,
|
||||
{
|
||||
fn sum<I>(iter: I) -> Self
|
||||
where
|
||||
I: Iterator<Item = T>,
|
||||
{
|
||||
iter.fold(Self::identity(), |acc, item| acc + item.borrow())
|
||||
}
|
||||
}
|
||||
|
||||
impl Group for Gt {
|
||||
type Scalar = Scalar;
|
||||
|
||||
fn random<R: RngCore + ?Sized>(rng: &mut R) -> Self {
|
||||
loop {
|
||||
let inner = Fp12::random(rng);
|
||||
|
||||
// Not all elements of Fp12 are elements of the prime-order multiplicative
|
||||
// subgroup. We run the random element through final_exponentiation to obtain
|
||||
// a valid element, which requires that it is non-zero.
|
||||
if !bool::from(inner.is_zero()) {
|
||||
return MillerLoopResult(inner).final_exponentiation();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn identity() -> Self {
|
||||
Self::identity()
|
||||
}
|
||||
|
||||
fn generator() -> Self {
|
||||
// pairing(&G1Affine::generator(), &G2Affine::generator())
|
||||
Gt(Fp12 {
|
||||
c0: Fp6 {
|
||||
c0: Fp2 {
|
||||
c0: Fp::from_raw_unchecked([
|
||||
0x1972_e433_a01f_85c5,
|
||||
0x97d3_2b76_fd77_2538,
|
||||
0xc8ce_546f_c96b_cdf9,
|
||||
0xcef6_3e73_66d4_0614,
|
||||
0xa611_3427_8184_3780,
|
||||
0x13f3_448a_3fc6_d825,
|
||||
]),
|
||||
c1: Fp::from_raw_unchecked([
|
||||
0xd263_31b0_2e9d_6995,
|
||||
0x9d68_a482_f779_7e7d,
|
||||
0x9c9b_2924_8d39_ea92,
|
||||
0xf480_1ca2_e131_07aa,
|
||||
0xa16c_0732_bdbc_b066,
|
||||
0x083c_a4af_ba36_0478,
|
||||
]),
|
||||
},
|
||||
c1: Fp2 {
|
||||
c0: Fp::from_raw_unchecked([
|
||||
0x59e2_61db_0916_b641,
|
||||
0x2716_b6f4_b23e_960d,
|
||||
0xc8e5_5b10_a0bd_9c45,
|
||||
0x0bdb_0bd9_9c4d_eda8,
|
||||
0x8cf8_9ebf_57fd_aac5,
|
||||
0x12d6_b792_9e77_7a5e,
|
||||
]),
|
||||
c1: Fp::from_raw_unchecked([
|
||||
0x5fc8_5188_b0e1_5f35,
|
||||
0x34a0_6e3a_8f09_6365,
|
||||
0xdb31_26a6_e02a_d62c,
|
||||
0xfc6f_5aa9_7d9a_990b,
|
||||
0xa12f_55f5_eb89_c210,
|
||||
0x1723_703a_926f_8889,
|
||||
]),
|
||||
},
|
||||
c2: Fp2 {
|
||||
c0: Fp::from_raw_unchecked([
|
||||
0x9358_8f29_7182_8778,
|
||||
0x43f6_5b86_11ab_7585,
|
||||
0x3183_aaf5_ec27_9fdf,
|
||||
0xfa73_d7e1_8ac9_9df6,
|
||||
0x64e1_76a6_a64c_99b0,
|
||||
0x179f_a78c_5838_8f1f,
|
||||
]),
|
||||
c1: Fp::from_raw_unchecked([
|
||||
0x672a_0a11_ca2a_ef12,
|
||||
0x0d11_b9b5_2aa3_f16b,
|
||||
0xa444_12d0_699d_056e,
|
||||
0xc01d_0177_221a_5ba5,
|
||||
0x66e0_cede_6c73_5529,
|
||||
0x05f5_a71e_9fdd_c339,
|
||||
]),
|
||||
},
|
||||
},
|
||||
c1: Fp6 {
|
||||
c0: Fp2 {
|
||||
c0: Fp::from_raw_unchecked([
|
||||
0xd30a_88a1_b062_c679,
|
||||
0x5ac5_6a5d_35fc_8304,
|
||||
0xd0c8_34a6_a81f_290d,
|
||||
0xcd54_30c2_da37_07c7,
|
||||
0xf0c2_7ff7_8050_0af0,
|
||||
0x0924_5da6_e2d7_2eae,
|
||||
]),
|
||||
c1: Fp::from_raw_unchecked([
|
||||
0x9f2e_0676_791b_5156,
|
||||
0xe2d1_c823_4918_fe13,
|
||||
0x4c9e_459f_3c56_1bf4,
|
||||
0xa3e8_5e53_b9d3_e3c1,
|
||||
0x820a_121e_21a7_0020,
|
||||
0x15af_6183_41c5_9acc,
|
||||
]),
|
||||
},
|
||||
c1: Fp2 {
|
||||
c0: Fp::from_raw_unchecked([
|
||||
0x7c95_658c_2499_3ab1,
|
||||
0x73eb_3872_1ca8_86b9,
|
||||
0x5256_d749_4774_34bc,
|
||||
0x8ba4_1902_ea50_4a8b,
|
||||
0x04a3_d3f8_0c86_ce6d,
|
||||
0x18a6_4a87_fb68_6eaa,
|
||||
]),
|
||||
c1: Fp::from_raw_unchecked([
|
||||
0xbb83_e71b_b920_cf26,
|
||||
0x2a52_77ac_92a7_3945,
|
||||
0xfc0e_e59f_94f0_46a0,
|
||||
0x7158_cdf3_7860_58f7,
|
||||
0x7cc1_061b_82f9_45f6,
|
||||
0x03f8_47aa_9fdb_e567,
|
||||
]),
|
||||
},
|
||||
c2: Fp2 {
|
||||
c0: Fp::from_raw_unchecked([
|
||||
0x8078_dba5_6134_e657,
|
||||
0x1cd7_ec9a_4399_8a6e,
|
||||
0xb1aa_599a_1a99_3766,
|
||||
0xc9a0_f62f_0842_ee44,
|
||||
0x8e15_9be3_b605_dffa,
|
||||
0x0c86_ba0d_4af1_3fc2,
|
||||
]),
|
||||
c1: Fp::from_raw_unchecked([
|
||||
0xe80f_f2a0_6a52_ffb1,
|
||||
0x7694_ca48_721a_906c,
|
||||
0x7583_183e_03b0_8514,
|
||||
0xf567_afdd_40ce_e4e2,
|
||||
0x9a6d_96d2_e526_a5fc,
|
||||
0x197e_9f49_861f_2242,
|
||||
]),
|
||||
},
|
||||
},
|
||||
})
|
||||
}
|
||||
|
||||
fn is_identity(&self) -> Choice {
|
||||
self.ct_eq(&Self::identity())
|
||||
}
|
||||
|
||||
#[must_use]
|
||||
fn double(&self) -> Self {
|
||||
self.double()
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(feature = "alloc")]
|
||||
#[cfg_attr(docsrs, doc(cfg(all(feature = "pairings", feature = "alloc"))))]
|
||||
#[derive(Clone, Debug)]
|
||||
/// This structure contains cached computations pertaining to a $\mathbb{G}_2$
|
||||
/// element as part of the pairing function (specifically, the Miller loop) and
|
||||
/// so should be computed whenever a $\mathbb{G}_2$ element is being used in
|
||||
/// multiple pairings or is otherwise known in advance. This should be used in
|
||||
/// conjunction with the [`multi_miller_loop`](crate::multi_miller_loop)
|
||||
/// function provided by this crate.
|
||||
///
|
||||
/// Requires the `alloc` and `pairing` crate features to be enabled.
|
||||
pub struct G2Prepared {
|
||||
infinity: Choice,
|
||||
coeffs: Vec<(Fp2, Fp2, Fp2)>,
|
||||
}
|
||||
|
||||
#[cfg(feature = "alloc")]
|
||||
impl From<G2Affine> for G2Prepared {
|
||||
fn from(q: G2Affine) -> G2Prepared {
|
||||
struct Adder {
|
||||
cur: G2Projective,
|
||||
base: G2Affine,
|
||||
coeffs: Vec<(Fp2, Fp2, Fp2)>,
|
||||
}
|
||||
|
||||
impl MillerLoopDriver for Adder {
|
||||
type Output = ();
|
||||
|
||||
fn doubling_step(&mut self, _: Self::Output) -> Self::Output {
|
||||
let coeffs = doubling_step(&mut self.cur);
|
||||
self.coeffs.push(coeffs);
|
||||
}
|
||||
fn addition_step(&mut self, _: Self::Output) -> Self::Output {
|
||||
let coeffs = addition_step(&mut self.cur, &self.base);
|
||||
self.coeffs.push(coeffs);
|
||||
}
|
||||
fn square_output(_: Self::Output) -> Self::Output {}
|
||||
fn conjugate(_: Self::Output) -> Self::Output {}
|
||||
fn one() -> Self::Output {}
|
||||
}
|
||||
|
||||
let is_identity = q.is_identity();
|
||||
let q = G2Affine::conditional_select(&q, &G2Affine::generator(), is_identity);
|
||||
|
||||
let mut adder = Adder {
|
||||
cur: G2Projective::from(q),
|
||||
base: q,
|
||||
coeffs: Vec::with_capacity(68),
|
||||
};
|
||||
|
||||
miller_loop(&mut adder);
|
||||
|
||||
assert_eq!(adder.coeffs.len(), 68);
|
||||
|
||||
G2Prepared {
|
||||
infinity: is_identity,
|
||||
coeffs: adder.coeffs,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(feature = "alloc")]
|
||||
#[cfg_attr(docsrs, doc(cfg(all(feature = "pairings", feature = "alloc"))))]
|
||||
/// Computes $$\sum_{i=1}^n \textbf{ML}(a_i, b_i)$$ given a series of terms
|
||||
/// $$(a_1, b_1), (a_2, b_2), ..., (a_n, b_n).$$
|
||||
///
|
||||
/// Requires the `alloc` and `pairing` crate features to be enabled.
|
||||
pub fn multi_miller_loop(terms: &[(&G1Affine, &G2Prepared)]) -> MillerLoopResult {
|
||||
struct Adder<'a, 'b, 'c> {
|
||||
terms: &'c [(&'a G1Affine, &'b G2Prepared)],
|
||||
index: usize,
|
||||
}
|
||||
|
||||
impl<'a, 'b, 'c> MillerLoopDriver for Adder<'a, 'b, 'c> {
|
||||
type Output = Fp12;
|
||||
|
||||
fn doubling_step(&mut self, mut f: Self::Output) -> Self::Output {
|
||||
let index = self.index;
|
||||
for term in self.terms {
|
||||
let either_identity = term.0.is_identity() | term.1.infinity;
|
||||
|
||||
let new_f = ell(f, &term.1.coeffs[index], term.0);
|
||||
f = Fp12::conditional_select(&new_f, &f, either_identity);
|
||||
}
|
||||
self.index += 1;
|
||||
|
||||
f
|
||||
}
|
||||
fn addition_step(&mut self, mut f: Self::Output) -> Self::Output {
|
||||
let index = self.index;
|
||||
for term in self.terms {
|
||||
let either_identity = term.0.is_identity() | term.1.infinity;
|
||||
|
||||
let new_f = ell(f, &term.1.coeffs[index], term.0);
|
||||
f = Fp12::conditional_select(&new_f, &f, either_identity);
|
||||
}
|
||||
self.index += 1;
|
||||
|
||||
f
|
||||
}
|
||||
fn square_output(f: Self::Output) -> Self::Output {
|
||||
f.square()
|
||||
}
|
||||
fn conjugate(f: Self::Output) -> Self::Output {
|
||||
f.conjugate()
|
||||
}
|
||||
fn one() -> Self::Output {
|
||||
Fp12::one()
|
||||
}
|
||||
}
|
||||
|
||||
let mut adder = Adder { terms, index: 0 };
|
||||
|
||||
let tmp = miller_loop(&mut adder);
|
||||
|
||||
MillerLoopResult(tmp)
|
||||
}
|
||||
|
||||
/// Invoke the pairing function without the use of precomputation and other optimizations.
|
||||
#[cfg_attr(docsrs, doc(cfg(feature = "pairings")))]
|
||||
pub fn pairing(p: &G1Affine, q: &G2Affine) -> Gt {
|
||||
struct Adder {
|
||||
cur: G2Projective,
|
||||
base: G2Affine,
|
||||
p: G1Affine,
|
||||
}
|
||||
|
||||
impl MillerLoopDriver for Adder {
|
||||
type Output = Fp12;
|
||||
|
||||
fn doubling_step(&mut self, f: Self::Output) -> Self::Output {
|
||||
let coeffs = doubling_step(&mut self.cur);
|
||||
ell(f, &coeffs, &self.p)
|
||||
}
|
||||
fn addition_step(&mut self, f: Self::Output) -> Self::Output {
|
||||
let coeffs = addition_step(&mut self.cur, &self.base);
|
||||
ell(f, &coeffs, &self.p)
|
||||
}
|
||||
fn square_output(f: Self::Output) -> Self::Output {
|
||||
f.square()
|
||||
}
|
||||
fn conjugate(f: Self::Output) -> Self::Output {
|
||||
f.conjugate()
|
||||
}
|
||||
fn one() -> Self::Output {
|
||||
Fp12::one()
|
||||
}
|
||||
}
|
||||
|
||||
let either_identity = p.is_identity() | q.is_identity();
|
||||
let p = G1Affine::conditional_select(&p, &G1Affine::generator(), either_identity);
|
||||
let q = G2Affine::conditional_select(&q, &G2Affine::generator(), either_identity);
|
||||
|
||||
let mut adder = Adder {
|
||||
cur: G2Projective::from(q),
|
||||
base: q,
|
||||
p,
|
||||
};
|
||||
|
||||
let tmp = miller_loop(&mut adder);
|
||||
let tmp = MillerLoopResult(Fp12::conditional_select(
|
||||
&tmp,
|
||||
&Fp12::one(),
|
||||
either_identity,
|
||||
));
|
||||
tmp.final_exponentiation()
|
||||
}
|
||||
|
||||
trait MillerLoopDriver {
|
||||
type Output;
|
||||
|
||||
fn doubling_step(&mut self, f: Self::Output) -> Self::Output;
|
||||
fn addition_step(&mut self, f: Self::Output) -> Self::Output;
|
||||
fn square_output(f: Self::Output) -> Self::Output;
|
||||
fn conjugate(f: Self::Output) -> Self::Output;
|
||||
fn one() -> Self::Output;
|
||||
}
|
||||
|
||||
/// This is a "generic" implementation of the Miller loop to avoid duplicating code
|
||||
/// structure elsewhere; instead, we'll write concrete instantiations of
|
||||
/// `MillerLoopDriver` for whatever purposes we need (such as caching modes).
|
||||
fn miller_loop<D: MillerLoopDriver>(driver: &mut D) -> D::Output {
|
||||
let mut f = D::one();
|
||||
|
||||
let mut found_one = false;
|
||||
for i in (0..64).rev().map(|b| (((BLS_X >> 1) >> b) & 1) == 1) {
|
||||
if !found_one {
|
||||
found_one = i;
|
||||
continue;
|
||||
}
|
||||
|
||||
f = driver.doubling_step(f);
|
||||
|
||||
if i {
|
||||
f = driver.addition_step(f);
|
||||
}
|
||||
|
||||
f = D::square_output(f);
|
||||
}
|
||||
|
||||
f = driver.doubling_step(f);
|
||||
|
||||
if BLS_X_IS_NEGATIVE {
|
||||
f = D::conjugate(f);
|
||||
}
|
||||
|
||||
f
|
||||
}
|
||||
|
||||
fn ell(f: Fp12, coeffs: &(Fp2, Fp2, Fp2), p: &G1Affine) -> Fp12 {
|
||||
let mut c0 = coeffs.0;
|
||||
let mut c1 = coeffs.1;
|
||||
|
||||
c0.c0 *= p.y;
|
||||
c0.c1 *= p.y;
|
||||
|
||||
c1.c0 *= p.x;
|
||||
c1.c1 *= p.x;
|
||||
|
||||
f.mul_by_014(&coeffs.2, &c1, &c0)
|
||||
}
|
||||
|
||||
fn doubling_step(r: &mut G2Projective) -> (Fp2, Fp2, Fp2) {
|
||||
// Adaptation of Algorithm 26, https://eprint.iacr.org/2010/354.pdf
|
||||
let tmp0 = r.x.square();
|
||||
let tmp1 = r.y.square();
|
||||
let tmp2 = tmp1.square();
|
||||
let tmp3 = (tmp1 + r.x).square() - tmp0 - tmp2;
|
||||
let tmp3 = tmp3 + tmp3;
|
||||
let tmp4 = tmp0 + tmp0 + tmp0;
|
||||
let tmp6 = r.x + tmp4;
|
||||
let tmp5 = tmp4.square();
|
||||
let zsquared = r.z.square();
|
||||
r.x = tmp5 - tmp3 - tmp3;
|
||||
r.z = (r.z + r.y).square() - tmp1 - zsquared;
|
||||
r.y = (tmp3 - r.x) * tmp4;
|
||||
let tmp2 = tmp2 + tmp2;
|
||||
let tmp2 = tmp2 + tmp2;
|
||||
let tmp2 = tmp2 + tmp2;
|
||||
r.y -= tmp2;
|
||||
let tmp3 = tmp4 * zsquared;
|
||||
let tmp3 = tmp3 + tmp3;
|
||||
let tmp3 = -tmp3;
|
||||
let tmp6 = tmp6.square() - tmp0 - tmp5;
|
||||
let tmp1 = tmp1 + tmp1;
|
||||
let tmp1 = tmp1 + tmp1;
|
||||
let tmp6 = tmp6 - tmp1;
|
||||
let tmp0 = r.z * zsquared;
|
||||
let tmp0 = tmp0 + tmp0;
|
||||
|
||||
(tmp0, tmp3, tmp6)
|
||||
}
|
||||
|
||||
fn addition_step(r: &mut G2Projective, q: &G2Affine) -> (Fp2, Fp2, Fp2) {
|
||||
// Adaptation of Algorithm 27, https://eprint.iacr.org/2010/354.pdf
|
||||
let zsquared = r.z.square();
|
||||
let ysquared = q.y.square();
|
||||
let t0 = zsquared * q.x;
|
||||
let t1 = ((q.y + r.z).square() - ysquared - zsquared) * zsquared;
|
||||
let t2 = t0 - r.x;
|
||||
let t3 = t2.square();
|
||||
let t4 = t3 + t3;
|
||||
let t4 = t4 + t4;
|
||||
let t5 = t4 * t2;
|
||||
let t6 = t1 - r.y - r.y;
|
||||
let t9 = t6 * q.x;
|
||||
let t7 = t4 * r.x;
|
||||
r.x = t6.square() - t5 - t7 - t7;
|
||||
r.z = (r.z + t2).square() - zsquared - t3;
|
||||
let t10 = q.y + r.z;
|
||||
let t8 = (t7 - r.x) * t6;
|
||||
let t0 = r.y * t5;
|
||||
let t0 = t0 + t0;
|
||||
r.y = t8 - t0;
|
||||
let t10 = t10.square() - ysquared;
|
||||
let ztsquared = r.z.square();
|
||||
let t10 = t10 - ztsquared;
|
||||
let t9 = t9 + t9 - t10;
|
||||
let t10 = r.z + r.z;
|
||||
let t6 = -t6;
|
||||
let t1 = t6 + t6;
|
||||
|
||||
(t10, t1, t9)
|
||||
}
|
||||
|
||||
impl PairingCurveAffine for G1Affine {
|
||||
type Pair = G2Affine;
|
||||
type PairingResult = Gt;
|
||||
|
||||
fn pairing_with(&self, other: &Self::Pair) -> Self::PairingResult {
|
||||
pairing(self, other)
|
||||
}
|
||||
}
|
||||
|
||||
impl PairingCurveAffine for G2Affine {
|
||||
type Pair = G1Affine;
|
||||
type PairingResult = Gt;
|
||||
|
||||
fn pairing_with(&self, other: &Self::Pair) -> Self::PairingResult {
|
||||
pairing(other, self)
|
||||
}
|
||||
}
|
||||
|
||||
/// A [`pairing::Engine`] for BLS12-381 pairing operations.
|
||||
#[cfg_attr(docsrs, doc(cfg(feature = "pairings")))]
|
||||
#[derive(Clone, Debug)]
|
||||
pub struct Bls12;
|
||||
|
||||
impl Engine for Bls12 {
|
||||
type Fr = Scalar;
|
||||
type G1 = G1Projective;
|
||||
type G1Affine = G1Affine;
|
||||
type G2 = G2Projective;
|
||||
type G2Affine = G2Affine;
|
||||
type Gt = Gt;
|
||||
|
||||
fn pairing(p: &Self::G1Affine, q: &Self::G2Affine) -> Self::Gt {
|
||||
pairing(p, q)
|
||||
}
|
||||
}
|
||||
|
||||
impl pairing::MillerLoopResult for MillerLoopResult {
|
||||
type Gt = Gt;
|
||||
|
||||
fn final_exponentiation(&self) -> Self::Gt {
|
||||
self.final_exponentiation()
|
||||
}
|
||||
}
|
||||
|
||||
impl MultiMillerLoop for Bls12 {
|
||||
type G2Prepared = G2Prepared;
|
||||
type Result = MillerLoopResult;
|
||||
|
||||
fn multi_miller_loop(terms: &[(&Self::G1Affine, &Self::G2Prepared)]) -> Self::Result {
|
||||
multi_miller_loop(terms)
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_gt_generator() {
|
||||
assert_eq!(
|
||||
Gt::generator(),
|
||||
pairing(&G1Affine::generator(), &G2Affine::generator())
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_bilinearity() {
|
||||
use crate::Scalar;
|
||||
|
||||
let a = Scalar::from_raw([1, 2, 3, 4]).invert().unwrap().square();
|
||||
let b = Scalar::from_raw([5, 6, 7, 8]).invert().unwrap().square();
|
||||
let c = a * b;
|
||||
|
||||
let g = G1Affine::from(G1Affine::generator() * a);
|
||||
let h = G2Affine::from(G2Affine::generator() * b);
|
||||
let p = pairing(&g, &h);
|
||||
|
||||
assert!(p != Gt::identity());
|
||||
|
||||
let expected = G1Affine::from(G1Affine::generator() * c);
|
||||
|
||||
assert_eq!(p, pairing(&expected, &G2Affine::generator()));
|
||||
assert_eq!(
|
||||
p,
|
||||
pairing(&G1Affine::generator(), &G2Affine::generator()) * c
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_unitary() {
|
||||
let g = G1Affine::generator();
|
||||
let h = G2Affine::generator();
|
||||
let p = -pairing(&g, &h);
|
||||
let q = pairing(&g, &-h);
|
||||
let r = pairing(&-g, &h);
|
||||
|
||||
assert_eq!(p, q);
|
||||
assert_eq!(q, r);
|
||||
}
|
||||
|
||||
#[cfg(feature = "alloc")]
|
||||
#[test]
|
||||
fn test_multi_miller_loop() {
|
||||
let a1 = G1Affine::generator();
|
||||
let b1 = G2Affine::generator();
|
||||
|
||||
let a2 = G1Affine::from(
|
||||
G1Affine::generator() * Scalar::from_raw([1, 2, 3, 4]).invert().unwrap().square(),
|
||||
);
|
||||
let b2 = G2Affine::from(
|
||||
G2Affine::generator() * Scalar::from_raw([4, 2, 2, 4]).invert().unwrap().square(),
|
||||
);
|
||||
|
||||
let a3 = G1Affine::identity();
|
||||
let b3 = G2Affine::from(
|
||||
G2Affine::generator() * Scalar::from_raw([9, 2, 2, 4]).invert().unwrap().square(),
|
||||
);
|
||||
|
||||
let a4 = G1Affine::from(
|
||||
G1Affine::generator() * Scalar::from_raw([5, 5, 5, 5]).invert().unwrap().square(),
|
||||
);
|
||||
let b4 = G2Affine::identity();
|
||||
|
||||
let a5 = G1Affine::from(
|
||||
G1Affine::generator() * Scalar::from_raw([323, 32, 3, 1]).invert().unwrap().square(),
|
||||
);
|
||||
let b5 = G2Affine::from(
|
||||
G2Affine::generator() * Scalar::from_raw([4, 2, 2, 9099]).invert().unwrap().square(),
|
||||
);
|
||||
|
||||
let b1_prepared = G2Prepared::from(b1);
|
||||
let b2_prepared = G2Prepared::from(b2);
|
||||
let b3_prepared = G2Prepared::from(b3);
|
||||
let b4_prepared = G2Prepared::from(b4);
|
||||
let b5_prepared = G2Prepared::from(b5);
|
||||
|
||||
let expected = pairing(&a1, &b1)
|
||||
+ pairing(&a2, &b2)
|
||||
+ pairing(&a3, &b3)
|
||||
+ pairing(&a4, &b4)
|
||||
+ pairing(&a5, &b5);
|
||||
|
||||
let test = multi_miller_loop(&[
|
||||
(&a1, &b1_prepared),
|
||||
(&a2, &b2_prepared),
|
||||
(&a3, &b3_prepared),
|
||||
(&a4, &b4_prepared),
|
||||
(&a5, &b5_prepared),
|
||||
])
|
||||
.final_exponentiation();
|
||||
|
||||
assert_eq!(expected, test);
|
||||
}
|
File diff suppressed because it is too large
Load Diff
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
|
@ -1,230 +0,0 @@
|
|||
use super::*;
|
||||
|
||||
macro_rules! test_vectors {
|
||||
($projective:ident, $affine:ident, $serialize:ident, $deserialize:ident, $expected:ident) => {
|
||||
let mut e = $projective::identity();
|
||||
|
||||
let mut v = vec![];
|
||||
{
|
||||
let mut expected = $expected;
|
||||
for _ in 0..1000 {
|
||||
let e_affine = $affine::from(e);
|
||||
let encoded = e_affine.$serialize();
|
||||
v.extend_from_slice(&encoded[..]);
|
||||
|
||||
let mut decoded = encoded;
|
||||
let len_of_encoding = decoded.len();
|
||||
(&mut decoded[..]).copy_from_slice(&expected[0..len_of_encoding]);
|
||||
expected = &expected[len_of_encoding..];
|
||||
let decoded = $affine::$deserialize(&decoded).unwrap();
|
||||
assert_eq!(e_affine, decoded);
|
||||
|
||||
e = &e + &$projective::generator();
|
||||
}
|
||||
}
|
||||
|
||||
assert_eq!(&v[..], $expected);
|
||||
};
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn g1_uncompressed_valid_test_vectors() {
|
||||
let bytes: &'static [u8] = include_bytes!("g1_uncompressed_valid_test_vectors.dat");
|
||||
test_vectors!(
|
||||
G1Projective,
|
||||
G1Affine,
|
||||
to_uncompressed,
|
||||
from_uncompressed,
|
||||
bytes
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn g1_compressed_valid_test_vectors() {
|
||||
let bytes: &'static [u8] = include_bytes!("g1_compressed_valid_test_vectors.dat");
|
||||
test_vectors!(
|
||||
G1Projective,
|
||||
G1Affine,
|
||||
to_compressed,
|
||||
from_compressed,
|
||||
bytes
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn g2_uncompressed_valid_test_vectors() {
|
||||
let bytes: &'static [u8] = include_bytes!("g2_uncompressed_valid_test_vectors.dat");
|
||||
test_vectors!(
|
||||
G2Projective,
|
||||
G2Affine,
|
||||
to_uncompressed,
|
||||
from_uncompressed,
|
||||
bytes
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn g2_compressed_valid_test_vectors() {
|
||||
let bytes: &'static [u8] = include_bytes!("g2_compressed_valid_test_vectors.dat");
|
||||
test_vectors!(
|
||||
G2Projective,
|
||||
G2Affine,
|
||||
to_compressed,
|
||||
from_compressed,
|
||||
bytes
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_pairing_result_against_relic() {
|
||||
/*
|
||||
Sent to me from Diego Aranha (author of RELIC library):
|
||||
1250EBD871FC0A92 A7B2D83168D0D727 272D441BEFA15C50 3DD8E90CE98DB3E7 B6D194F60839C508 A84305AACA1789B6
|
||||
089A1C5B46E5110B 86750EC6A5323488 68A84045483C92B7 AF5AF689452EAFAB F1A8943E50439F1D 59882A98EAA0170F
|
||||
1368BB445C7C2D20 9703F239689CE34C 0378A68E72A6B3B2 16DA0E22A5031B54 DDFF57309396B38C 881C4C849EC23E87
|
||||
193502B86EDB8857 C273FA075A505129 37E0794E1E65A761 7C90D8BD66065B1F FFE51D7A579973B1 315021EC3C19934F
|
||||
01B2F522473D1713 91125BA84DC4007C FBF2F8DA752F7C74 185203FCCA589AC7 19C34DFFBBAAD843 1DAD1C1FB597AAA5
|
||||
018107154F25A764 BD3C79937A45B845 46DA634B8F6BE14A 8061E55CCEBA478B 23F7DACAA35C8CA7 8BEAE9624045B4B6
|
||||
19F26337D205FB46 9CD6BD15C3D5A04D C88784FBB3D0B2DB DEA54D43B2B73F2C BB12D58386A8703E 0F948226E47EE89D
|
||||
06FBA23EB7C5AF0D 9F80940CA771B6FF D5857BAAF222EB95 A7D2809D61BFE02E 1BFD1B68FF02F0B8 102AE1C2D5D5AB1A
|
||||
11B8B424CD48BF38 FCEF68083B0B0EC5 C81A93B330EE1A67 7D0D15FF7B984E89 78EF48881E32FAC9 1B93B47333E2BA57
|
||||
03350F55A7AEFCD3 C31B4FCB6CE5771C C6A0E9786AB59733 20C806AD36082910 7BA810C5A09FFDD9 BE2291A0C25A99A2
|
||||
04C581234D086A99 02249B64728FFD21 A189E87935A95405 1C7CDBA7B3872629 A4FAFC05066245CB 9108F0242D0FE3EF
|
||||
0F41E58663BF08CF 068672CBD01A7EC7 3BACA4D72CA93544 DEFF686BFD6DF543 D48EAA24AFE47E1E FDE449383B676631
|
||||
*/
|
||||
|
||||
let a = G1Affine::generator();
|
||||
let b = G2Affine::generator();
|
||||
|
||||
use super::fp::Fp;
|
||||
use super::fp12::Fp12;
|
||||
use super::fp2::Fp2;
|
||||
use super::fp6::Fp6;
|
||||
|
||||
let res = pairing(&a, &b);
|
||||
|
||||
let prep = G2Prepared::from(b);
|
||||
|
||||
assert_eq!(
|
||||
res,
|
||||
multi_miller_loop(&[(&a, &prep)]).final_exponentiation()
|
||||
);
|
||||
|
||||
assert_eq!(
|
||||
res.0,
|
||||
Fp12 {
|
||||
c0: Fp6 {
|
||||
c0: Fp2 {
|
||||
c0: Fp::from_raw_unchecked([
|
||||
0x1972_e433_a01f_85c5,
|
||||
0x97d3_2b76_fd77_2538,
|
||||
0xc8ce_546f_c96b_cdf9,
|
||||
0xcef6_3e73_66d4_0614,
|
||||
0xa611_3427_8184_3780,
|
||||
0x13f3_448a_3fc6_d825,
|
||||
]),
|
||||
c1: Fp::from_raw_unchecked([
|
||||
0xd263_31b0_2e9d_6995,
|
||||
0x9d68_a482_f779_7e7d,
|
||||
0x9c9b_2924_8d39_ea92,
|
||||
0xf480_1ca2_e131_07aa,
|
||||
0xa16c_0732_bdbc_b066,
|
||||
0x083c_a4af_ba36_0478,
|
||||
])
|
||||
},
|
||||
c1: Fp2 {
|
||||
c0: Fp::from_raw_unchecked([
|
||||
0x59e2_61db_0916_b641,
|
||||
0x2716_b6f4_b23e_960d,
|
||||
0xc8e5_5b10_a0bd_9c45,
|
||||
0x0bdb_0bd9_9c4d_eda8,
|
||||
0x8cf8_9ebf_57fd_aac5,
|
||||
0x12d6_b792_9e77_7a5e,
|
||||
]),
|
||||
c1: Fp::from_raw_unchecked([
|
||||
0x5fc8_5188_b0e1_5f35,
|
||||
0x34a0_6e3a_8f09_6365,
|
||||
0xdb31_26a6_e02a_d62c,
|
||||
0xfc6f_5aa9_7d9a_990b,
|
||||
0xa12f_55f5_eb89_c210,
|
||||
0x1723_703a_926f_8889,
|
||||
])
|
||||
},
|
||||
c2: Fp2 {
|
||||
c0: Fp::from_raw_unchecked([
|
||||
0x9358_8f29_7182_8778,
|
||||
0x43f6_5b86_11ab_7585,
|
||||
0x3183_aaf5_ec27_9fdf,
|
||||
0xfa73_d7e1_8ac9_9df6,
|
||||
0x64e1_76a6_a64c_99b0,
|
||||
0x179f_a78c_5838_8f1f,
|
||||
]),
|
||||
c1: Fp::from_raw_unchecked([
|
||||
0x672a_0a11_ca2a_ef12,
|
||||
0x0d11_b9b5_2aa3_f16b,
|
||||
0xa444_12d0_699d_056e,
|
||||
0xc01d_0177_221a_5ba5,
|
||||
0x66e0_cede_6c73_5529,
|
||||
0x05f5_a71e_9fdd_c339,
|
||||
])
|
||||
}
|
||||
},
|
||||
c1: Fp6 {
|
||||
c0: Fp2 {
|
||||
c0: Fp::from_raw_unchecked([
|
||||
0xd30a_88a1_b062_c679,
|
||||
0x5ac5_6a5d_35fc_8304,
|
||||
0xd0c8_34a6_a81f_290d,
|
||||
0xcd54_30c2_da37_07c7,
|
||||
0xf0c2_7ff7_8050_0af0,
|
||||
0x0924_5da6_e2d7_2eae,
|
||||
]),
|
||||
c1: Fp::from_raw_unchecked([
|
||||
0x9f2e_0676_791b_5156,
|
||||
0xe2d1_c823_4918_fe13,
|
||||
0x4c9e_459f_3c56_1bf4,
|
||||
0xa3e8_5e53_b9d3_e3c1,
|
||||
0x820a_121e_21a7_0020,
|
||||
0x15af_6183_41c5_9acc,
|
||||
])
|
||||
},
|
||||
c1: Fp2 {
|
||||
c0: Fp::from_raw_unchecked([
|
||||
0x7c95_658c_2499_3ab1,
|
||||
0x73eb_3872_1ca8_86b9,
|
||||
0x5256_d749_4774_34bc,
|
||||
0x8ba4_1902_ea50_4a8b,
|
||||
0x04a3_d3f8_0c86_ce6d,
|
||||
0x18a6_4a87_fb68_6eaa,
|
||||
]),
|
||||
c1: Fp::from_raw_unchecked([
|
||||
0xbb83_e71b_b920_cf26,
|
||||
0x2a52_77ac_92a7_3945,
|
||||
0xfc0e_e59f_94f0_46a0,
|
||||
0x7158_cdf3_7860_58f7,
|
||||
0x7cc1_061b_82f9_45f6,
|
||||
0x03f8_47aa_9fdb_e567,
|
||||
])
|
||||
},
|
||||
c2: Fp2 {
|
||||
c0: Fp::from_raw_unchecked([
|
||||
0x8078_dba5_6134_e657,
|
||||
0x1cd7_ec9a_4399_8a6e,
|
||||
0xb1aa_599a_1a99_3766,
|
||||
0xc9a0_f62f_0842_ee44,
|
||||
0x8e15_9be3_b605_dffa,
|
||||
0x0c86_ba0d_4af1_3fc2,
|
||||
]),
|
||||
c1: Fp::from_raw_unchecked([
|
||||
0xe80f_f2a0_6a52_ffb1,
|
||||
0x7694_ca48_721a_906c,
|
||||
0x7583_183e_03b0_8514,
|
||||
0xf567_afdd_40ce_e4e2,
|
||||
0x9a6d_96d2_e526_a5fc,
|
||||
0x197e_9f49_861f_2242,
|
||||
])
|
||||
}
|
||||
}
|
||||
}
|
||||
);
|
||||
}
|
|
@ -1,174 +0,0 @@
|
|||
/// Compute a + b + carry, returning the result and the new carry over.
|
||||
#[inline(always)]
|
||||
pub const fn adc(a: u64, b: u64, carry: u64) -> (u64, u64) {
|
||||
let ret = (a as u128) + (b as u128) + (carry as u128);
|
||||
(ret as u64, (ret >> 64) as u64)
|
||||
}
|
||||
|
||||
/// Compute a - (b + borrow), returning the result and the new borrow.
|
||||
#[inline(always)]
|
||||
pub const fn sbb(a: u64, b: u64, borrow: u64) -> (u64, u64) {
|
||||
let ret = (a as u128).wrapping_sub((b as u128) + ((borrow >> 63) as u128));
|
||||
(ret as u64, (ret >> 64) as u64)
|
||||
}
|
||||
|
||||
/// Compute a + (b * c) + carry, returning the result and the new carry over.
|
||||
#[inline(always)]
|
||||
pub const fn mac(a: u64, b: u64, c: u64, carry: u64) -> (u64, u64) {
|
||||
let ret = (a as u128) + ((b as u128) * (c as u128)) + (carry as u128);
|
||||
(ret as u64, (ret >> 64) as u64)
|
||||
}
|
||||
|
||||
macro_rules! impl_add_binop_specify_output {
|
||||
($lhs:ident, $rhs:ident, $output:ident) => {
|
||||
impl<'b> Add<&'b $rhs> for $lhs {
|
||||
type Output = $output;
|
||||
|
||||
#[inline]
|
||||
fn add(self, rhs: &'b $rhs) -> $output {
|
||||
&self + rhs
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> Add<$rhs> for &'a $lhs {
|
||||
type Output = $output;
|
||||
|
||||
#[inline]
|
||||
fn add(self, rhs: $rhs) -> $output {
|
||||
self + &rhs
|
||||
}
|
||||
}
|
||||
|
||||
impl Add<$rhs> for $lhs {
|
||||
type Output = $output;
|
||||
|
||||
#[inline]
|
||||
fn add(self, rhs: $rhs) -> $output {
|
||||
&self + &rhs
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
macro_rules! impl_sub_binop_specify_output {
|
||||
($lhs:ident, $rhs:ident, $output:ident) => {
|
||||
impl<'b> Sub<&'b $rhs> for $lhs {
|
||||
type Output = $output;
|
||||
|
||||
#[inline]
|
||||
fn sub(self, rhs: &'b $rhs) -> $output {
|
||||
&self - rhs
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> Sub<$rhs> for &'a $lhs {
|
||||
type Output = $output;
|
||||
|
||||
#[inline]
|
||||
fn sub(self, rhs: $rhs) -> $output {
|
||||
self - &rhs
|
||||
}
|
||||
}
|
||||
|
||||
impl Sub<$rhs> for $lhs {
|
||||
type Output = $output;
|
||||
|
||||
#[inline]
|
||||
fn sub(self, rhs: $rhs) -> $output {
|
||||
&self - &rhs
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
macro_rules! impl_binops_additive_specify_output {
|
||||
($lhs:ident, $rhs:ident, $output:ident) => {
|
||||
impl_add_binop_specify_output!($lhs, $rhs, $output);
|
||||
impl_sub_binop_specify_output!($lhs, $rhs, $output);
|
||||
};
|
||||
}
|
||||
|
||||
macro_rules! impl_binops_multiplicative_mixed {
|
||||
($lhs:ident, $rhs:ident, $output:ident) => {
|
||||
impl<'b> Mul<&'b $rhs> for $lhs {
|
||||
type Output = $output;
|
||||
|
||||
#[inline]
|
||||
fn mul(self, rhs: &'b $rhs) -> $output {
|
||||
&self * rhs
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> Mul<$rhs> for &'a $lhs {
|
||||
type Output = $output;
|
||||
|
||||
#[inline]
|
||||
fn mul(self, rhs: $rhs) -> $output {
|
||||
self * &rhs
|
||||
}
|
||||
}
|
||||
|
||||
impl Mul<$rhs> for $lhs {
|
||||
type Output = $output;
|
||||
|
||||
#[inline]
|
||||
fn mul(self, rhs: $rhs) -> $output {
|
||||
&self * &rhs
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
macro_rules! impl_binops_additive {
|
||||
($lhs:ident, $rhs:ident) => {
|
||||
impl_binops_additive_specify_output!($lhs, $rhs, $lhs);
|
||||
|
||||
impl SubAssign<$rhs> for $lhs {
|
||||
#[inline]
|
||||
fn sub_assign(&mut self, rhs: $rhs) {
|
||||
*self = &*self - &rhs;
|
||||
}
|
||||
}
|
||||
|
||||
impl AddAssign<$rhs> for $lhs {
|
||||
#[inline]
|
||||
fn add_assign(&mut self, rhs: $rhs) {
|
||||
*self = &*self + &rhs;
|
||||
}
|
||||
}
|
||||
|
||||
impl<'b> SubAssign<&'b $rhs> for $lhs {
|
||||
#[inline]
|
||||
fn sub_assign(&mut self, rhs: &'b $rhs) {
|
||||
*self = &*self - rhs;
|
||||
}
|
||||
}
|
||||
|
||||
impl<'b> AddAssign<&'b $rhs> for $lhs {
|
||||
#[inline]
|
||||
fn add_assign(&mut self, rhs: &'b $rhs) {
|
||||
*self = &*self + rhs;
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
macro_rules! impl_binops_multiplicative {
|
||||
($lhs:ident, $rhs:ident) => {
|
||||
impl_binops_multiplicative_mixed!($lhs, $rhs, $lhs);
|
||||
|
||||
impl MulAssign<$rhs> for $lhs {
|
||||
#[inline]
|
||||
fn mul_assign(&mut self, rhs: $rhs) {
|
||||
*self = &*self * &rhs;
|
||||
}
|
||||
}
|
||||
|
||||
impl<'b> MulAssign<&'b $rhs> for $lhs {
|
||||
#[inline]
|
||||
fn mul_assign(&mut self, rhs: &'b $rhs) {
|
||||
*self = &*self * rhs;
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
|
@ -1,3 +0,0 @@
|
|||
target/
|
||||
**/*.rs.bk
|
||||
Cargo.lock
|
|
@ -1,28 +0,0 @@
|
|||
[package]
|
||||
name = "ff"
|
||||
version = "0.7.0"
|
||||
authors = [
|
||||
"Sean Bowe <ewillbefull@gmail.com>",
|
||||
"Jack Grigg <thestr4d@gmail.com>",
|
||||
]
|
||||
description = "Library for building and interfacing with finite fields"
|
||||
readme = "README.md"
|
||||
documentation = "https://docs.rs/ff/"
|
||||
homepage = "https://github.com/ebfull/ff"
|
||||
license = "MIT/Apache-2.0"
|
||||
repository = "https://github.com/ebfull/ff"
|
||||
edition = "2018"
|
||||
|
||||
[dependencies]
|
||||
byteorder = { version = "1", default-features = false }
|
||||
ff_derive = { version = "0.7", path = "ff_derive", optional = true }
|
||||
rand_core = { version = "0.5", default-features = false }
|
||||
subtle = { version = "2.2.1", default-features = false, features = ["i128"] }
|
||||
|
||||
[features]
|
||||
default = ["std"]
|
||||
derive = ["ff_derive"]
|
||||
std = []
|
||||
|
||||
[badges]
|
||||
maintenance = { status = "actively-developed" }
|
|
@ -1,202 +0,0 @@
|
|||
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.
|
||||
|
|
@ -1,21 +0,0 @@
|
|||
The MIT License (MIT)
|
||||
|
||||
Copyright (c) 2017 Sean Bowe
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights
|
||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the Software is
|
||||
furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in
|
||||
all copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
THE SOFTWARE.
|
67
ff/README.md
67
ff/README.md
|
@ -1,67 +0,0 @@
|
|||
# ff
|
||||
|
||||
`ff` is a finite field library written in pure Rust, with no `unsafe{}` code.
|
||||
|
||||
## Disclaimers
|
||||
|
||||
* This library does not provide constant-time guarantees.
|
||||
|
||||
## Usage
|
||||
|
||||
Add the `ff` crate to your `Cargo.toml`:
|
||||
|
||||
```toml
|
||||
[dependencies]
|
||||
ff = "0.7"
|
||||
```
|
||||
|
||||
The `ff` crate contains `Field`, `PrimeField`, `PrimeFieldRepr` and `SqrtField` traits.
|
||||
See the **[documentation](https://docs.rs/ff/)** for more.
|
||||
|
||||
### #![derive(PrimeField)]
|
||||
|
||||
If you need an implementation of a prime field, this library also provides a procedural
|
||||
macro that will expand into an efficient implementation of a prime field when supplied
|
||||
with the modulus. `PrimeFieldGenerator` must be an element of Fp of p-1 order, that is
|
||||
also quadratic nonresidue.
|
||||
|
||||
First, enable the `derive` crate feature:
|
||||
|
||||
```toml
|
||||
[dependencies]
|
||||
ff = { version = "0.7", features = ["derive"] }
|
||||
```
|
||||
|
||||
And then use the macro like so:
|
||||
|
||||
```rust
|
||||
extern crate rand;
|
||||
#[macro_use]
|
||||
extern crate ff;
|
||||
|
||||
#[derive(PrimeField)]
|
||||
#[PrimeFieldModulus = "52435875175126190479447740508185965837690552500527637822603658699938581184513"]
|
||||
#[PrimeFieldGenerator = "7"]
|
||||
struct Fp(FpRepr);
|
||||
```
|
||||
|
||||
And that's it! `Fp` now implements `Field` and `PrimeField`. `Fp` will also implement
|
||||
`SqrtField` if supported. The library implements `FpRepr` itself and derives
|
||||
`PrimeFieldRepr` for it.
|
||||
|
||||
## 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.
|
|
@ -1,28 +0,0 @@
|
|||
[package]
|
||||
name = "ff_derive"
|
||||
version = "0.7.0"
|
||||
authors = [
|
||||
"Sean Bowe <ewillbefull@gmail.com>",
|
||||
"Jack Grigg <thestr4d@gmail.com>",
|
||||
]
|
||||
description = "Procedural macro library used to build custom prime field implementations"
|
||||
documentation = "https://docs.rs/ff/"
|
||||
homepage = "https://github.com/ebfull/ff"
|
||||
license = "MIT/Apache-2.0"
|
||||
repository = "https://github.com/ebfull/ff"
|
||||
edition = "2018"
|
||||
|
||||
[lib]
|
||||
proc-macro = true
|
||||
|
||||
[dependencies]
|
||||
addchain = "0.2"
|
||||
num-bigint = "0.3"
|
||||
num-traits = "0.2"
|
||||
num-integer = "0.1"
|
||||
proc-macro2 = "1"
|
||||
quote = "1"
|
||||
syn = "1"
|
||||
|
||||
[badges]
|
||||
maintenance = { status = "passively-maintained" }
|
File diff suppressed because it is too large
Load Diff
|
@ -1,56 +0,0 @@
|
|||
//! Fixed-exponent variable-base exponentiation using addition chains.
|
||||
|
||||
use addchain::{build_addition_chain, Step};
|
||||
use num_bigint::BigUint;
|
||||
use quote::quote;
|
||||
use syn::Ident;
|
||||
|
||||
/// Returns t{n} as an ident.
|
||||
fn get_temp(n: usize) -> Ident {
|
||||
Ident::new(&format!("t{}", n), proc_macro2::Span::call_site())
|
||||
}
|
||||
|
||||
pub(crate) fn generate(
|
||||
base: &proc_macro2::TokenStream,
|
||||
exponent: BigUint,
|
||||
) -> proc_macro2::TokenStream {
|
||||
let steps = build_addition_chain(exponent);
|
||||
|
||||
let mut gen = proc_macro2::TokenStream::new();
|
||||
|
||||
// First entry in chain is one, i.e. the base.
|
||||
let start = get_temp(0);
|
||||
gen.extend(quote! {
|
||||
let #start = #base;
|
||||
});
|
||||
|
||||
let mut tmps = vec![start];
|
||||
for (i, step) in steps.into_iter().enumerate() {
|
||||
let out = get_temp(i + 1);
|
||||
|
||||
gen.extend(match step {
|
||||
Step::Double { index } => {
|
||||
let val = &tmps[index];
|
||||
quote! {
|
||||
let #out = #val.square();
|
||||
}
|
||||
}
|
||||
Step::Add { left, right } => {
|
||||
let left = &tmps[left];
|
||||
let right = &tmps[right];
|
||||
quote! {
|
||||
let #out = #left * #right;
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
tmps.push(out.clone());
|
||||
}
|
||||
|
||||
let end = tmps.last().expect("have last");
|
||||
gen.extend(quote! {
|
||||
#end
|
||||
});
|
||||
|
||||
gen
|
||||
}
|
343
ff/src/lib.rs
343
ff/src/lib.rs
|
@ -1,343 +0,0 @@
|
|||
//! This crate provides traits for working with finite fields.
|
||||
|
||||
// Catch documentation errors caused by code changes.
|
||||
#![no_std]
|
||||
#![deny(intra_doc_link_resolution_failure)]
|
||||
#![allow(unused_imports)]
|
||||
|
||||
#[cfg(feature = "std")]
|
||||
#[macro_use]
|
||||
extern crate std;
|
||||
|
||||
#[cfg(feature = "derive")]
|
||||
pub use ff_derive::*;
|
||||
|
||||
use byteorder::ByteOrder;
|
||||
use core::convert::TryFrom;
|
||||
use core::fmt;
|
||||
use core::marker::PhantomData;
|
||||
use core::ops::{Add, AddAssign, BitAnd, Mul, MulAssign, Neg, Shr, Sub, SubAssign};
|
||||
use rand_core::RngCore;
|
||||
#[cfg(feature = "std")]
|
||||
use std::io::{self, Read, Write};
|
||||
use subtle::{ConditionallySelectable, CtOption};
|
||||
|
||||
/// This trait represents an element of a field.
|
||||
pub trait Field:
|
||||
Sized
|
||||
+ Eq
|
||||
+ Copy
|
||||
+ Clone
|
||||
+ Default
|
||||
+ Send
|
||||
+ Sync
|
||||
+ fmt::Debug
|
||||
+ fmt::Display
|
||||
+ 'static
|
||||
+ ConditionallySelectable
|
||||
+ Add<Output = Self>
|
||||
+ Sub<Output = Self>
|
||||
+ Mul<Output = Self>
|
||||
+ Neg<Output = Self>
|
||||
+ for<'a> Add<&'a Self, Output = Self>
|
||||
+ for<'a> Mul<&'a Self, Output = Self>
|
||||
+ for<'a> Sub<&'a Self, Output = Self>
|
||||
+ MulAssign
|
||||
+ AddAssign
|
||||
+ SubAssign
|
||||
+ for<'a> MulAssign<&'a Self>
|
||||
+ for<'a> AddAssign<&'a Self>
|
||||
+ for<'a> SubAssign<&'a Self>
|
||||
{
|
||||
/// Returns an element chosen uniformly at random using a user-provided RNG.
|
||||
fn random<R: RngCore + ?Sized>(rng: &mut R) -> Self;
|
||||
|
||||
/// 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.
|
||||
#[must_use]
|
||||
fn square(&self) -> Self;
|
||||
|
||||
/// Doubles this element.
|
||||
#[must_use]
|
||||
fn double(&self) -> Self;
|
||||
|
||||
/// Computes the multiplicative inverse of this element,
|
||||
/// failing if the element is zero.
|
||||
fn invert(&self) -> CtOption<Self>;
|
||||
|
||||
/// Returns the square root of the field element, if it is
|
||||
/// quadratic residue.
|
||||
fn sqrt(&self) -> CtOption<Self>;
|
||||
|
||||
/// Exponentiates `self` by `exp`, where `exp` is a little-endian order
|
||||
/// integer exponent.
|
||||
///
|
||||
/// **This operation is variable time with respect to the exponent.** If the
|
||||
/// exponent is fixed, this operation is effectively constant time.
|
||||
fn pow_vartime<S: AsRef<[u64]>>(&self, exp: S) -> Self {
|
||||
let mut res = Self::one();
|
||||
for e in exp.as_ref().iter().rev() {
|
||||
for i in (0..64).rev() {
|
||||
res = res.square();
|
||||
|
||||
if ((*e >> i) & 1) == 1 {
|
||||
res.mul_assign(self);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
res
|
||||
}
|
||||
}
|
||||
|
||||
/// Helper trait for converting the binary representation of a prime field element into a
|
||||
/// specific endianness. This is useful when you need to act on the bit representation
|
||||
/// of an element generically, as the native binary representation of a prime field is
|
||||
/// field-dependent.
|
||||
pub trait Endianness: ByteOrder {
|
||||
/// Converts the provided representation between native and little-endian.
|
||||
fn toggle_little_endian<T: AsMut<[u8]>>(t: &mut T);
|
||||
}
|
||||
|
||||
impl Endianness for byteorder::BigEndian {
|
||||
fn toggle_little_endian<T: AsMut<[u8]>>(t: &mut T) {
|
||||
t.as_mut().reverse();
|
||||
}
|
||||
}
|
||||
|
||||
impl Endianness for byteorder::LittleEndian {
|
||||
fn toggle_little_endian<T: AsMut<[u8]>>(_: &mut T) {
|
||||
// No-op
|
||||
}
|
||||
}
|
||||
|
||||
/// This represents an element of a prime field.
|
||||
pub trait PrimeField: Field + From<u64> {
|
||||
/// The prime field can be converted back and forth into this binary
|
||||
/// representation.
|
||||
type Repr: Default + AsRef<[u8]> + AsMut<[u8]> + From<Self> + for<'r> From<&'r Self>;
|
||||
|
||||
/// This indicates the endianness of [`PrimeField::Repr`].
|
||||
type ReprEndianness: Endianness;
|
||||
|
||||
/// 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(10);
|
||||
|
||||
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(u64::from(c)));
|
||||
}
|
||||
None => {
|
||||
return None;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Some(res)
|
||||
}
|
||||
|
||||
/// Attempts to convert a byte representation of a field element into an element of
|
||||
/// this prime field, failing if the input is not canonical (is not smaller than the
|
||||
/// field's modulus).
|
||||
///
|
||||
/// The byte representation is interpreted with the endianness defined by
|
||||
/// [`PrimeField::ReprEndianness`].
|
||||
fn from_repr(_: Self::Repr) -> Option<Self>;
|
||||
|
||||
/// Converts an element of the prime field into the standard byte representation for
|
||||
/// this field.
|
||||
///
|
||||
/// The endianness of the byte representation is defined by
|
||||
/// [`PrimeField::ReprEndianness`].
|
||||
fn to_repr(&self) -> Self::Repr;
|
||||
|
||||
/// Returns true iff this element is odd.
|
||||
fn is_odd(&self) -> bool;
|
||||
|
||||
/// Returns true iff this element is even.
|
||||
#[inline(always)]
|
||||
fn is_even(&self) -> bool {
|
||||
!self.is_odd()
|
||||
}
|
||||
|
||||
/// 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;
|
||||
}
|
||||
|
||||
/// Takes a little-endian representation of some value, and returns its bits in big-endian
|
||||
/// order.
|
||||
#[derive(Debug)]
|
||||
pub struct BitIterator<T, E: AsRef<[T]>> {
|
||||
t: E,
|
||||
n: usize,
|
||||
_limb: PhantomData<T>,
|
||||
}
|
||||
|
||||
impl<E: AsRef<[u64]>> BitIterator<u64, E> {
|
||||
pub fn new(t: E) -> Self {
|
||||
let n = t.as_ref().len() * 64;
|
||||
|
||||
BitIterator {
|
||||
t,
|
||||
n,
|
||||
_limb: PhantomData::default(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<E: AsRef<[u64]>> Iterator for BitIterator<u64, 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)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<E: AsRef<[u8]>> BitIterator<u8, E> {
|
||||
pub fn new(t: E) -> Self {
|
||||
let n = t.as_ref().len() * 8;
|
||||
|
||||
BitIterator {
|
||||
t,
|
||||
n,
|
||||
_limb: PhantomData::default(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<E: AsRef<[u8]>> Iterator for BitIterator<u8, E> {
|
||||
type Item = bool;
|
||||
|
||||
fn next(&mut self) -> Option<bool> {
|
||||
if self.n == 0 {
|
||||
None
|
||||
} else {
|
||||
self.n -= 1;
|
||||
let part = self.n / 8;
|
||||
let bit = self.n - (8 * part);
|
||||
|
||||
Some(self.t.as_ref()[part] & (1 << bit) > 0)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_bit_iterator() {
|
||||
let mut a = BitIterator::<u64, _>::new([0xa953_d79b_83f6_ab59, 0x6dea_2059_e200_bd39]);
|
||||
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::<u64, _>::new([
|
||||
0x429d_5f3a_c3a3_b759,
|
||||
0xb10f_4c66_768b_1c92,
|
||||
0x9236_8b6d_16ec_d3b4,
|
||||
0xa57e_a85a_e877_5219,
|
||||
]);
|
||||
|
||||
for e in expected.chars() {
|
||||
assert!(a.next().unwrap() == (e == '1'));
|
||||
}
|
||||
|
||||
assert!(a.next().is_none());
|
||||
}
|
||||
|
||||
pub use self::arith_impl::*;
|
||||
|
||||
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
|
||||
}
|
||||
}
|
|
@ -1,3 +0,0 @@
|
|||
/target
|
||||
**/*.rs.bk
|
||||
Cargo.lock
|
|
@ -1,14 +0,0 @@
|
|||
Copyrights in the "group" library are retained by their contributors. No
|
||||
copyright assignment is required to contribute to the "group" library.
|
||||
|
||||
The "group" 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.
|
|
@ -1,25 +0,0 @@
|
|||
[package]
|
||||
name = "group"
|
||||
version = "0.7.0"
|
||||
authors = [
|
||||
"Sean Bowe <ewillbefull@gmail.com>",
|
||||
"Jack Grigg <jack@z.cash>",
|
||||
]
|
||||
readme = "README.md"
|
||||
license = "MIT/Apache-2.0"
|
||||
|
||||
description = "Elliptic curve group traits and utilities"
|
||||
documentation = "https://docs.rs/group/"
|
||||
homepage = "https://github.com/ebfull/group"
|
||||
repository = "https://github.com/ebfull/group"
|
||||
edition = "2018"
|
||||
|
||||
[dependencies]
|
||||
byteorder = { version = "1", default-features = false }
|
||||
ff = { version = "0.7", path = "../ff" }
|
||||
rand = "0.7"
|
||||
rand_xorshift = "0.2"
|
||||
subtle = { version = "2.2.1", default-features = false }
|
||||
|
||||
[badges]
|
||||
maintenance = { status = "actively-developed" }
|
|
@ -1,201 +0,0 @@
|
|||
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.
|
|
@ -1,23 +0,0 @@
|
|||
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.
|
|
@ -1,20 +0,0 @@
|
|||
# group [![Crates.io](https://img.shields.io/crates/v/group.svg)](https://crates.io/crates/group) #
|
||||
|
||||
`group` is a crate for working with groups over elliptic curves.
|
||||
|
||||
## 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.
|
|
@ -1,119 +0,0 @@
|
|||
use core::fmt;
|
||||
use core::ops::{Mul, Neg};
|
||||
use ff::{BitIterator, Endianness, PrimeField};
|
||||
use subtle::{Choice, CtOption};
|
||||
|
||||
use crate::{prime::PrimeGroup, Curve, Group, GroupEncoding, GroupOps, GroupOpsOwned};
|
||||
|
||||
/// This trait represents an element of a cryptographic group with a large prime-order
|
||||
/// subgroup and a comparatively-small cofactor.
|
||||
pub trait CofactorGroup:
|
||||
Group
|
||||
+ GroupEncoding
|
||||
+ GroupOps<<Self as CofactorGroup>::Subgroup>
|
||||
+ GroupOpsOwned<<Self as CofactorGroup>::Subgroup>
|
||||
{
|
||||
/// The large prime-order subgroup in which cryptographic operations are performed.
|
||||
/// If `Self` implements `PrimeGroup`, then `Self::Subgroup` may be `Self`.
|
||||
type Subgroup: PrimeGroup<Scalar = Self::Scalar> + Into<Self>;
|
||||
|
||||
/// Maps `self` to the prime-order subgroup by multiplying this element by some
|
||||
/// `k`-multiple of the cofactor.
|
||||
///
|
||||
/// The value `k` does not vary between inputs for a given implementation, but may
|
||||
/// vary between different implementations of `CofactorGroup` because some groups have
|
||||
/// more efficient methods of clearing the cofactor when `k` is allowed to be
|
||||
/// different than `1`.
|
||||
///
|
||||
/// If `Self` implements [`PrimeGroup`], this returns `self`.
|
||||
fn clear_cofactor(&self) -> Self::Subgroup;
|
||||
|
||||
/// Returns `self` if it is contained in the prime-order subgroup.
|
||||
///
|
||||
/// If `Self` implements [`PrimeGroup`], this returns `Some(self)`.
|
||||
fn into_subgroup(self) -> CtOption<Self::Subgroup>;
|
||||
|
||||
/// Determines if this element is of small order.
|
||||
///
|
||||
/// Returns:
|
||||
/// - `true` if `self` is in the torsion subgroup.
|
||||
/// - `false` if `self` is not in the torsion subgroup.
|
||||
fn is_small_order(&self) -> Choice {
|
||||
self.clear_cofactor().is_identity()
|
||||
}
|
||||
|
||||
/// Determines if this element is "torsion free", i.e., is contained in the
|
||||
/// prime-order subgroup.
|
||||
///
|
||||
/// Returns:
|
||||
/// - `true` if `self` has zero torsion component and is in the prime-order subgroup.
|
||||
/// - `false` if `self` has non-zero torsion component and is not in the prime-order
|
||||
/// subgroup.
|
||||
fn is_torsion_free(&self) -> Choice {
|
||||
// Obtain the scalar field characteristic in little endian.
|
||||
let mut char = Self::Scalar::char();
|
||||
<Self::Scalar as PrimeField>::ReprEndianness::toggle_little_endian(&mut char);
|
||||
|
||||
// Multiply self by the characteristic to eliminate any prime-order subgroup
|
||||
// component.
|
||||
let bits = BitIterator::<u8, _>::new(char);
|
||||
let mut res = Self::identity();
|
||||
for i in bits {
|
||||
res = res.double();
|
||||
if i {
|
||||
res.add_assign(self)
|
||||
}
|
||||
}
|
||||
|
||||
// If the result is the identity, there was zero torsion component!
|
||||
res.is_identity()
|
||||
}
|
||||
}
|
||||
|
||||
/// Efficient representation of an elliptic curve point guaranteed to be
|
||||
/// in the correct prime order subgroup.
|
||||
pub trait CofactorCurve:
|
||||
Curve<AffineRepr = <Self as CofactorCurve>::Affine> + CofactorGroup
|
||||
{
|
||||
type Affine: CofactorCurveAffine<Curve = Self, Scalar = Self::Scalar>
|
||||
+ Mul<Self::Scalar, Output = Self>
|
||||
+ for<'r> Mul<Self::Scalar, Output = Self>;
|
||||
}
|
||||
|
||||
/// Affine representation of an elliptic curve point guaranteed to be
|
||||
/// in the correct prime order subgroup.
|
||||
pub trait CofactorCurveAffine:
|
||||
GroupEncoding
|
||||
+ Copy
|
||||
+ Clone
|
||||
+ Sized
|
||||
+ Send
|
||||
+ Sync
|
||||
+ fmt::Debug
|
||||
+ fmt::Display
|
||||
+ PartialEq
|
||||
+ Eq
|
||||
+ 'static
|
||||
+ Neg<Output = Self>
|
||||
+ Mul<<Self as CofactorCurveAffine>::Scalar, Output = <Self as CofactorCurveAffine>::Curve>
|
||||
+ for<'r> Mul<
|
||||
<Self as CofactorCurveAffine>::Scalar,
|
||||
Output = <Self as CofactorCurveAffine>::Curve,
|
||||
>
|
||||
{
|
||||
type Scalar: PrimeField;
|
||||
type Curve: CofactorCurve<Affine = Self, Scalar = Self::Scalar>;
|
||||
|
||||
/// Returns the additive identity.
|
||||
fn identity() -> Self;
|
||||
|
||||
/// Returns a fixed generator of unknown exponent.
|
||||
fn generator() -> Self;
|
||||
|
||||
/// Determines if this point represents the point at infinity; the
|
||||
/// additive identity.
|
||||
fn is_identity(&self) -> Choice;
|
||||
|
||||
/// Converts this element to its curve representation.
|
||||
fn to_curve(&self) -> Self::Curve;
|
||||
}
|
164
group/src/lib.rs
164
group/src/lib.rs
|
@ -1,164 +0,0 @@
|
|||
// Catch documentation errors caused by code changes.
|
||||
#![deny(intra_doc_link_resolution_failure)]
|
||||
|
||||
use ff::PrimeField;
|
||||
use rand::RngCore;
|
||||
use std::fmt;
|
||||
use std::iter::Sum;
|
||||
use std::ops::{Add, AddAssign, Mul, MulAssign, Neg, Sub, SubAssign};
|
||||
use subtle::{Choice, CtOption};
|
||||
|
||||
pub mod cofactor;
|
||||
pub mod prime;
|
||||
pub mod tests;
|
||||
|
||||
mod wnaf;
|
||||
pub use self::wnaf::{Wnaf, WnafGroup};
|
||||
|
||||
/// A helper trait for types with a group operation.
|
||||
pub trait GroupOps<Rhs = Self, Output = Self>:
|
||||
Add<Rhs, Output = Output> + Sub<Rhs, Output = Output> + AddAssign<Rhs> + SubAssign<Rhs>
|
||||
{
|
||||
}
|
||||
|
||||
impl<T, Rhs, Output> GroupOps<Rhs, Output> for T where
|
||||
T: Add<Rhs, Output = Output> + Sub<Rhs, Output = Output> + AddAssign<Rhs> + SubAssign<Rhs>
|
||||
{
|
||||
}
|
||||
|
||||
/// A helper trait for references with a group operation.
|
||||
pub trait GroupOpsOwned<Rhs = Self, Output = Self>: for<'r> GroupOps<&'r Rhs, Output> {}
|
||||
impl<T, Rhs, Output> GroupOpsOwned<Rhs, Output> for T where T: for<'r> GroupOps<&'r Rhs, Output> {}
|
||||
|
||||
/// A helper trait for types implementing group scalar multiplication.
|
||||
pub trait ScalarMul<Rhs, Output = Self>: Mul<Rhs, Output = Output> + MulAssign<Rhs> {}
|
||||
|
||||
impl<T, Rhs, Output> ScalarMul<Rhs, Output> for T where T: Mul<Rhs, Output = Output> + MulAssign<Rhs>
|
||||
{}
|
||||
|
||||
/// A helper trait for references implementing group scalar multiplication.
|
||||
pub trait ScalarMulOwned<Rhs, Output = Self>: for<'r> ScalarMul<&'r Rhs, Output> {}
|
||||
impl<T, Rhs, Output> ScalarMulOwned<Rhs, Output> for T where T: for<'r> ScalarMul<&'r Rhs, Output> {}
|
||||
|
||||
/// This trait represents an element of a cryptographic group.
|
||||
pub trait Group:
|
||||
Clone
|
||||
+ Copy
|
||||
+ fmt::Debug
|
||||
+ fmt::Display
|
||||
+ Eq
|
||||
+ Sized
|
||||
+ Send
|
||||
+ Sync
|
||||
+ 'static
|
||||
+ Sum
|
||||
+ for<'a> Sum<&'a Self>
|
||||
+ Neg<Output = Self>
|
||||
+ GroupOps
|
||||
+ GroupOpsOwned
|
||||
+ ScalarMul<<Self as Group>::Scalar>
|
||||
+ ScalarMulOwned<<Self as Group>::Scalar>
|
||||
{
|
||||
/// Scalars modulo the order of this group's scalar field.
|
||||
type Scalar: PrimeField;
|
||||
|
||||
/// Returns an element chosen uniformly at random from the non-identity elements of
|
||||
/// this group.
|
||||
///
|
||||
/// This function is non-deterministic, and samples from the user-provided RNG.
|
||||
fn random<R: RngCore + ?Sized>(rng: &mut R) -> Self;
|
||||
|
||||
/// Returns the additive identity, also known as the "neutral element".
|
||||
fn identity() -> Self;
|
||||
|
||||
/// Returns a fixed generator of the prime-order subgroup.
|
||||
fn generator() -> Self;
|
||||
|
||||
/// Determines if this point is the identity.
|
||||
fn is_identity(&self) -> Choice;
|
||||
|
||||
/// Doubles this element.
|
||||
#[must_use]
|
||||
fn double(&self) -> Self;
|
||||
}
|
||||
|
||||
/// Efficient representation of an elliptic curve point guaranteed.
|
||||
pub trait Curve:
|
||||
Group + GroupOps<<Self as Curve>::AffineRepr> + GroupOpsOwned<<Self as Curve>::AffineRepr>
|
||||
{
|
||||
/// The affine representation for this elliptic curve.
|
||||
type AffineRepr;
|
||||
|
||||
/// Converts a batch of projective elements into affine elements. This function will
|
||||
/// panic if `p.len() != q.len()`.
|
||||
fn batch_normalize(p: &[Self], q: &mut [Self::AffineRepr]) {
|
||||
assert_eq!(p.len(), q.len());
|
||||
|
||||
for (p, q) in p.iter().zip(q.iter_mut()) {
|
||||
*q = p.to_affine();
|
||||
}
|
||||
}
|
||||
|
||||
/// Converts this element into its affine representation.
|
||||
fn to_affine(&self) -> Self::AffineRepr;
|
||||
}
|
||||
|
||||
pub trait GroupEncoding: Sized {
|
||||
/// The encoding of group elements.
|
||||
///
|
||||
/// The `Default` implementation is not required to return a valid point encoding. The
|
||||
/// bound is present to enable encodings to be constructed generically:
|
||||
/// ```
|
||||
/// # use group::GroupEncoding;
|
||||
/// # use subtle::CtOption;
|
||||
/// # struct G;
|
||||
/// # impl GroupEncoding for G {
|
||||
/// # type Repr = [u8; 0];
|
||||
/// # fn from_bytes(bytes: &Self::Repr) -> CtOption<Self> { unimplemented!() }
|
||||
/// # fn from_bytes_unchecked(bytes: &Self::Repr) -> CtOption<Self> { unimplemented!() }
|
||||
/// # fn to_bytes(&self) -> Self::Repr { unimplemented!() }
|
||||
/// # }
|
||||
/// # let buf = &[0u8; 0][..];
|
||||
/// let mut encoding = <G as GroupEncoding>::Repr::default();
|
||||
/// encoding.as_mut().copy_from_slice(buf);
|
||||
/// ```
|
||||
///
|
||||
/// It is recommended that the default should be the all-zeroes encoding.
|
||||
type Repr: Default + AsRef<[u8]> + AsMut<[u8]>;
|
||||
|
||||
/// Attempts to deserialize a group element from its encoding.
|
||||
fn from_bytes(bytes: &Self::Repr) -> CtOption<Self>;
|
||||
|
||||
/// Attempts to deserialize a group element, not checking if the element is valid.
|
||||
///
|
||||
/// **This is dangerous to call unless you trust the bytes you are reading; otherwise,
|
||||
/// API invariants may be broken.** Please consider using
|
||||
/// [`GroupEncoding::from_bytes`] instead.
|
||||
fn from_bytes_unchecked(bytes: &Self::Repr) -> CtOption<Self>;
|
||||
|
||||
/// Converts this element into its byte encoding. This may or may not support
|
||||
/// encoding the identity.
|
||||
// TODO: Figure out how to handle identity encoding generically.
|
||||
fn to_bytes(&self) -> Self::Repr;
|
||||
}
|
||||
|
||||
/// Affine representation of a point on an elliptic curve that has a defined uncompressed
|
||||
/// encoding.
|
||||
pub trait UncompressedEncoding: Sized {
|
||||
type Uncompressed: Default + AsRef<[u8]> + AsMut<[u8]>;
|
||||
|
||||
/// Attempts to deserialize an element from its uncompressed encoding.
|
||||
fn from_uncompressed(bytes: &Self::Uncompressed) -> CtOption<Self>;
|
||||
|
||||
/// Attempts to deserialize an uncompressed element, not checking if the element is in
|
||||
/// the correct subgroup.
|
||||
///
|
||||
/// **This is dangerous to call unless you trust the bytes you are reading; otherwise,
|
||||
/// API invariants may be broken.** Please consider using
|
||||
/// [`UncompressedEncoding::from_uncompressed`] instead.
|
||||
fn from_uncompressed_unchecked(bytes: &Self::Uncompressed) -> CtOption<Self>;
|
||||
|
||||
/// Converts this element into its uncompressed encoding, so long as it's not
|
||||
/// the point at infinity.
|
||||
fn to_uncompressed(&self) -> Self::Uncompressed;
|
||||
}
|
|
@ -1,52 +0,0 @@
|
|||
use core::fmt;
|
||||
use core::ops::{Mul, Neg};
|
||||
use ff::PrimeField;
|
||||
use subtle::Choice;
|
||||
|
||||
use crate::{Curve, Group, GroupEncoding};
|
||||
|
||||
/// This trait represents an element of a prime-order cryptographic group.
|
||||
pub trait PrimeGroup: Group + GroupEncoding {}
|
||||
|
||||
/// Efficient representation of an elliptic curve point guaranteed to be
|
||||
/// in the correct prime order subgroup.
|
||||
pub trait PrimeCurve: Curve<AffineRepr = <Self as PrimeCurve>::Affine> + PrimeGroup {
|
||||
type Affine: PrimeCurveAffine<Curve = Self, Scalar = Self::Scalar>
|
||||
+ Mul<Self::Scalar, Output = Self>
|
||||
+ for<'r> Mul<Self::Scalar, Output = Self>;
|
||||
}
|
||||
|
||||
/// Affine representation of an elliptic curve point guaranteed to be
|
||||
/// in the correct prime order subgroup.
|
||||
pub trait PrimeCurveAffine:
|
||||
GroupEncoding
|
||||
+ Copy
|
||||
+ Clone
|
||||
+ Sized
|
||||
+ Send
|
||||
+ Sync
|
||||
+ fmt::Debug
|
||||
+ fmt::Display
|
||||
+ PartialEq
|
||||
+ Eq
|
||||
+ 'static
|
||||
+ Neg<Output = Self>
|
||||
+ Mul<<Self as PrimeCurveAffine>::Scalar, Output = <Self as PrimeCurveAffine>::Curve>
|
||||
+ for<'r> Mul<<Self as PrimeCurveAffine>::Scalar, Output = <Self as PrimeCurveAffine>::Curve>
|
||||
{
|
||||
type Scalar: PrimeField;
|
||||
type Curve: PrimeCurve<Affine = Self, Scalar = Self::Scalar>;
|
||||
|
||||
/// Returns the additive identity.
|
||||
fn identity() -> Self;
|
||||
|
||||
/// Returns a fixed generator of unknown exponent.
|
||||
fn generator() -> Self;
|
||||
|
||||
/// Determines if this point represents the point at infinity; the
|
||||
/// additive identity.
|
||||
fn is_identity(&self) -> Choice;
|
||||
|
||||
/// Converts this element to its curve representation.
|
||||
fn to_curve(&self) -> Self::Curve;
|
||||
}
|
|
@ -1,447 +0,0 @@
|
|||
use ff::{Field, PrimeField};
|
||||
use rand::SeedableRng;
|
||||
use rand_xorshift::XorShiftRng;
|
||||
use std::ops::{Mul, Neg};
|
||||
|
||||
use crate::{
|
||||
prime::{PrimeCurve, PrimeCurveAffine},
|
||||
wnaf::WnafGroup,
|
||||
GroupEncoding, UncompressedEncoding,
|
||||
};
|
||||
|
||||
pub fn curve_tests<G: PrimeCurve>() {
|
||||
let mut rng = XorShiftRng::from_seed([
|
||||
0x59, 0x62, 0xbe, 0x5d, 0x76, 0x3d, 0x31, 0x8d, 0x17, 0xdb, 0x37, 0x32, 0x54, 0x06, 0xbc,
|
||||
0xe5,
|
||||
]);
|
||||
|
||||
// Negation edge case with identity.
|
||||
{
|
||||
let z = G::identity().neg();
|
||||
assert!(bool::from(z.is_identity()));
|
||||
}
|
||||
|
||||
// Doubling edge case with identity.
|
||||
{
|
||||
let z = G::identity().double();
|
||||
assert!(bool::from(z.is_identity()));
|
||||
}
|
||||
|
||||
// Addition edge cases with identity
|
||||
{
|
||||
let mut r = G::random(&mut rng);
|
||||
let rcopy = r;
|
||||
r.add_assign(&G::identity());
|
||||
assert_eq!(r, rcopy);
|
||||
r.add_assign(&G::Affine::identity());
|
||||
assert_eq!(r, rcopy);
|
||||
|
||||
let mut z = G::identity();
|
||||
z.add_assign(&G::identity());
|
||||
assert!(bool::from(z.is_identity()));
|
||||
z.add_assign(&G::Affine::identity());
|
||||
assert!(bool::from(z.is_identity()));
|
||||
|
||||
let mut z2 = z;
|
||||
z2.add_assign(&r);
|
||||
|
||||
z.add_assign(&r.to_affine());
|
||||
|
||||
assert_eq!(z, z2);
|
||||
assert_eq!(z, r);
|
||||
}
|
||||
|
||||
// Transformations
|
||||
{
|
||||
let a = G::random(&mut rng);
|
||||
let b = a.to_affine().to_curve();
|
||||
let c = a.to_affine().to_curve().to_affine().to_curve();
|
||||
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_compressed_encoding_tests::<G>();
|
||||
}
|
||||
|
||||
pub fn random_wnaf_tests<G: WnafGroup>() {
|
||||
use crate::wnaf::*;
|
||||
|
||||
let mut rng = XorShiftRng::from_seed([
|
||||
0x59, 0x62, 0xbe, 0x5d, 0x76, 0x3d, 0x31, 0x8d, 0x17, 0xdb, 0x37, 0x32, 0x54, 0x06, 0xbc,
|
||||
0xe5,
|
||||
]);
|
||||
|
||||
{
|
||||
let mut table = vec![];
|
||||
let mut wnaf = vec![];
|
||||
|
||||
for w in 2..14 {
|
||||
for _ in 0..100 {
|
||||
let g = G::random(&mut rng);
|
||||
let s = G::Scalar::random(&mut rng);
|
||||
let mut g1 = g;
|
||||
g1.mul_assign(s);
|
||||
|
||||
wnaf_table(&mut table, g, w);
|
||||
wnaf_form(&mut wnaf, s.to_repr(), 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::random(&mut rng);
|
||||
let s = G::Scalar::random(&mut rng);
|
||||
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(G::random(&mut rng), 1)
|
||||
.scalar(&G::Scalar::random(&mut rng));
|
||||
}
|
||||
wnaf.base(g, 1).scalar(&s)
|
||||
};
|
||||
let g7 = {
|
||||
let mut wnaf = Wnaf::new();
|
||||
{
|
||||
// Populate the vectors.
|
||||
wnaf.base(G::random(&mut rng), 1)
|
||||
.scalar(&G::Scalar::random(&mut rng));
|
||||
}
|
||||
wnaf.scalar(&s).base(g)
|
||||
};
|
||||
let g8 = {
|
||||
let mut wnaf = Wnaf::new();
|
||||
{
|
||||
// Populate the vectors.
|
||||
wnaf.base(G::random(&mut rng), 1)
|
||||
.scalar(&G::Scalar::random(&mut rng));
|
||||
}
|
||||
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(G::random(&mut rng), 1)
|
||||
.scalar(&G::Scalar::random(&mut rng));
|
||||
}
|
||||
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: PrimeCurve>() {
|
||||
let mut rng = XorShiftRng::from_seed([
|
||||
0x59, 0x62, 0xbe, 0x5d, 0x76, 0x3d, 0x31, 0x8d, 0x17, 0xdb, 0x37, 0x32, 0x54, 0x06, 0xbc,
|
||||
0xe5,
|
||||
]);
|
||||
|
||||
for _ in 0..1000 {
|
||||
let r = G::random(&mut rng);
|
||||
|
||||
let s = G::Scalar::random(&mut rng);
|
||||
let sneg = s.neg();
|
||||
|
||||
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!(bool::from(t3.is_identity()));
|
||||
|
||||
let mut t4 = t1;
|
||||
t4.add_assign(&t2.to_affine());
|
||||
assert!(bool::from(t4.is_identity()));
|
||||
|
||||
assert_eq!(t1.neg(), t2);
|
||||
}
|
||||
}
|
||||
|
||||
fn random_doubling_tests<G: PrimeCurve>() {
|
||||
let mut rng = XorShiftRng::from_seed([
|
||||
0x59, 0x62, 0xbe, 0x5d, 0x76, 0x3d, 0x31, 0x8d, 0x17, 0xdb, 0x37, 0x32, 0x54, 0x06, 0xbc,
|
||||
0xe5,
|
||||
]);
|
||||
|
||||
for _ in 0..1000 {
|
||||
let mut a = G::random(&mut rng);
|
||||
let mut b = G::random(&mut rng);
|
||||
|
||||
// 2(a + b)
|
||||
let tmp1 = (a + b).double();
|
||||
|
||||
// 2a + 2b
|
||||
a = a.double();
|
||||
b = b.double();
|
||||
|
||||
let mut tmp2 = a;
|
||||
tmp2.add_assign(&b);
|
||||
|
||||
let mut tmp3 = a;
|
||||
tmp3.add_assign(&b.to_affine());
|
||||
|
||||
assert_eq!(tmp1, tmp2);
|
||||
assert_eq!(tmp1, tmp3);
|
||||
}
|
||||
}
|
||||
|
||||
fn random_multiplication_tests<G: PrimeCurve>() {
|
||||
let mut rng = XorShiftRng::from_seed([
|
||||
0x59, 0x62, 0xbe, 0x5d, 0x76, 0x3d, 0x31, 0x8d, 0x17, 0xdb, 0x37, 0x32, 0x54, 0x06, 0xbc,
|
||||
0xe5,
|
||||
]);
|
||||
|
||||
for _ in 0..1000 {
|
||||
let mut a = G::random(&mut rng);
|
||||
let mut b = G::random(&mut rng);
|
||||
let a_affine = a.to_affine();
|
||||
let b_affine = b.to_affine();
|
||||
|
||||
let s = G::Scalar::random(&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 = Mul::<G::Scalar>::mul(a_affine, s);
|
||||
tmp3.add_assign(Mul::<G::Scalar>::mul(b_affine, s));
|
||||
|
||||
assert_eq!(tmp1, tmp2);
|
||||
assert_eq!(tmp1, tmp3);
|
||||
}
|
||||
}
|
||||
|
||||
fn random_addition_tests<G: PrimeCurve>() {
|
||||
let mut rng = XorShiftRng::from_seed([
|
||||
0x59, 0x62, 0xbe, 0x5d, 0x76, 0x3d, 0x31, 0x8d, 0x17, 0xdb, 0x37, 0x32, 0x54, 0x06, 0xbc,
|
||||
0xe5,
|
||||
]);
|
||||
|
||||
for _ in 0..1000 {
|
||||
let a = G::random(&mut rng);
|
||||
let b = G::random(&mut rng);
|
||||
let c = G::random(&mut rng);
|
||||
let a_affine = a.to_affine();
|
||||
let b_affine = b.to_affine();
|
||||
let c_affine = c.to_affine();
|
||||
|
||||
// a + a should equal the doubling
|
||||
{
|
||||
let mut aplusa = a;
|
||||
aplusa.add_assign(&a);
|
||||
|
||||
let mut aplusamixed = a;
|
||||
aplusamixed.add_assign(&a.to_affine());
|
||||
|
||||
let adouble = a.double();
|
||||
|
||||
assert_eq!(aplusa, adouble);
|
||||
assert_eq!(aplusa, aplusamixed);
|
||||
}
|
||||
|
||||
let mut tmp = vec![G::identity(); 6];
|
||||
|
||||
// (a + b) + c
|
||||
tmp[0] = a;
|
||||
tmp[0].add_assign(&b);
|
||||
tmp[0].add_assign(&c);
|
||||
|
||||
// a + (b + c)
|
||||
tmp[1] = b;
|
||||
tmp[1].add_assign(&c);
|
||||
tmp[1].add_assign(&a);
|
||||
|
||||
// (a + c) + b
|
||||
tmp[2] = a;
|
||||
tmp[2].add_assign(&c);
|
||||
tmp[2].add_assign(&b);
|
||||
|
||||
// Mixed addition
|
||||
|
||||
// (a + b) + c
|
||||
tmp[3] = a_affine.to_curve();
|
||||
tmp[3].add_assign(&b_affine);
|
||||
tmp[3].add_assign(&c_affine);
|
||||
|
||||
// a + (b + c)
|
||||
tmp[4] = b_affine.to_curve();
|
||||
tmp[4].add_assign(&c_affine);
|
||||
tmp[4].add_assign(&a_affine);
|
||||
|
||||
// (a + c) + b
|
||||
tmp[5] = a_affine.to_curve();
|
||||
tmp[5].add_assign(&c_affine);
|
||||
tmp[5].add_assign(&b_affine);
|
||||
|
||||
// Comparisons
|
||||
for i in 0..6 {
|
||||
for j in 0..6 {
|
||||
assert_eq!(tmp[i], tmp[j]);
|
||||
assert_eq!(tmp[i].to_affine(), tmp[j].to_affine());
|
||||
}
|
||||
|
||||
assert!(tmp[i] != a);
|
||||
assert!(tmp[i] != b);
|
||||
assert!(tmp[i] != c);
|
||||
|
||||
assert!(a != tmp[i]);
|
||||
assert!(b != tmp[i]);
|
||||
assert!(c != tmp[i]);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn random_transformation_tests<G: PrimeCurve>() {
|
||||
let mut rng = XorShiftRng::from_seed([
|
||||
0x59, 0x62, 0xbe, 0x5d, 0x76, 0x3d, 0x31, 0x8d, 0x17, 0xdb, 0x37, 0x32, 0x54, 0x06, 0xbc,
|
||||
0xe5,
|
||||
]);
|
||||
|
||||
for _ in 0..1000 {
|
||||
let g = G::random(&mut rng);
|
||||
let g_affine = g.to_affine();
|
||||
let g_projective = g_affine.to_curve();
|
||||
assert_eq!(g, g_projective);
|
||||
}
|
||||
|
||||
// Batch normalization
|
||||
for _ in 0..10 {
|
||||
let mut v = (0..1000).map(|_| G::random(&mut rng)).collect::<Vec<_>>();
|
||||
|
||||
use rand::distributions::{Distribution, Uniform};
|
||||
let between = Uniform::new(0, 1000);
|
||||
// Sprinkle in some normalized points
|
||||
for _ in 0..5 {
|
||||
v[between.sample(&mut rng)] = G::identity();
|
||||
}
|
||||
for _ in 0..5 {
|
||||
let s = between.sample(&mut rng);
|
||||
v[s] = v[s].to_affine().to_curve();
|
||||
}
|
||||
|
||||
let expected_v = v.iter().map(|v| v.to_affine()).collect::<Vec<_>>();
|
||||
|
||||
let mut normalized = vec![G::Affine::identity(); v.len()];
|
||||
G::batch_normalize(&v, &mut normalized);
|
||||
|
||||
assert_eq!(normalized, expected_v);
|
||||
}
|
||||
}
|
||||
|
||||
fn random_compressed_encoding_tests<G: PrimeCurve>() {
|
||||
let mut rng = XorShiftRng::from_seed([
|
||||
0x59, 0x62, 0xbe, 0x5d, 0x76, 0x3d, 0x31, 0x8d, 0x17, 0xdb, 0x37, 0x32, 0x54, 0x06, 0xbc,
|
||||
0xe5,
|
||||
]);
|
||||
|
||||
assert_eq!(
|
||||
G::Affine::from_bytes(&G::Affine::identity().to_bytes()).unwrap(),
|
||||
G::Affine::identity()
|
||||
);
|
||||
|
||||
for _ in 0..1000 {
|
||||
let mut r = G::random(&mut rng).to_affine();
|
||||
|
||||
let compressed = r.to_bytes();
|
||||
let de_compressed = G::Affine::from_bytes(&compressed).unwrap();
|
||||
assert_eq!(de_compressed, r);
|
||||
|
||||
r = r.neg();
|
||||
|
||||
let compressed = r.to_bytes();
|
||||
let de_compressed = G::Affine::from_bytes(&compressed).unwrap();
|
||||
assert_eq!(de_compressed, r);
|
||||
}
|
||||
}
|
||||
|
||||
pub fn random_uncompressed_encoding_tests<G: PrimeCurve>()
|
||||
where
|
||||
<G as PrimeCurve>::Affine: UncompressedEncoding,
|
||||
{
|
||||
let mut rng = XorShiftRng::from_seed([
|
||||
0x59, 0x62, 0xbe, 0x5d, 0x76, 0x3d, 0x31, 0x8d, 0x17, 0xdb, 0x37, 0x32, 0x54, 0x06, 0xbc,
|
||||
0xe5,
|
||||
]);
|
||||
|
||||
assert_eq!(
|
||||
G::Affine::from_uncompressed(&G::Affine::identity().to_uncompressed()).unwrap(),
|
||||
G::Affine::identity()
|
||||
);
|
||||
|
||||
for _ in 0..1000 {
|
||||
let r = G::random(&mut rng).to_affine();
|
||||
|
||||
let uncompressed = r.to_uncompressed();
|
||||
let de_uncompressed = G::Affine::from_uncompressed(&uncompressed).unwrap();
|
||||
assert_eq!(de_uncompressed, r);
|
||||
}
|
||||
}
|
|
@ -1,214 +0,0 @@
|
|||
use byteorder::{ByteOrder, LittleEndian};
|
||||
use ff::PrimeField;
|
||||
use std::iter;
|
||||
|
||||
use super::Group;
|
||||
|
||||
/// Extension trait on a [`Group`] that provides helpers used by [`Wnaf`].
|
||||
pub trait WnafGroup: Group {
|
||||
/// 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;
|
||||
}
|
||||
|
||||
/// Replaces the contents of `table` with a w-NAF window table for the given window size.
|
||||
pub(crate) fn wnaf_table<G: Group>(table: &mut Vec<G>, mut base: G, window: usize) {
|
||||
table.truncate(0);
|
||||
table.reserve(1 << (window - 1));
|
||||
|
||||
let dbl = base.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 little-endian
|
||||
/// scalar.
|
||||
pub(crate) fn wnaf_form<S: AsRef<[u8]>>(wnaf: &mut Vec<i64>, c: S, window: usize) {
|
||||
// Required by the NAF definition
|
||||
debug_assert!(window >= 2);
|
||||
// Required so that the NAF digits fit in i64
|
||||
debug_assert!(window <= 64);
|
||||
|
||||
wnaf.truncate(0);
|
||||
|
||||
let bit_len = c.as_ref().len() * 8;
|
||||
let u64_len = (bit_len + 1) / 64;
|
||||
|
||||
let mut c_u64 = vec![0u64; u64_len + 1];
|
||||
LittleEndian::read_u64_into(c.as_ref(), &mut c_u64[0..u64_len]);
|
||||
|
||||
let width = 1u64 << window;
|
||||
let window_mask = width - 1;
|
||||
|
||||
let mut pos = 0;
|
||||
let mut carry = 0;
|
||||
while pos < bit_len {
|
||||
// Construct a buffer of bits of the scalar, starting at bit `pos`
|
||||
let u64_idx = pos / 64;
|
||||
let bit_idx = pos % 64;
|
||||
let bit_buf = if bit_idx + window < 64 {
|
||||
// This window's bits are contained in a single u64
|
||||
c_u64[u64_idx] >> bit_idx
|
||||
} else {
|
||||
// Combine the current u64's bits with the bits from the next u64
|
||||
(c_u64[u64_idx] >> bit_idx) | (c_u64[u64_idx + 1] << (64 - bit_idx))
|
||||
};
|
||||
|
||||
// Add the carry into the current window
|
||||
let window_val = carry + (bit_buf & window_mask);
|
||||
|
||||
if window_val & 1 == 0 {
|
||||
// If the window value is even, preserve the carry and emit 0.
|
||||
// Why is the carry preserved?
|
||||
// If carry == 0 and window_val & 1 == 0, then the next carry should be 0
|
||||
// If carry == 1 and window_val & 1 == 0, then bit_buf & 1 == 1 so the next carry should be 1
|
||||
wnaf.push(0);
|
||||
pos += 1;
|
||||
} else {
|
||||
wnaf.push(if window_val < width / 2 {
|
||||
carry = 0;
|
||||
window_val as i64
|
||||
} else {
|
||||
carry = 1;
|
||||
(window_val as i64).wrapping_sub(width as i64)
|
||||
});
|
||||
wnaf.extend(iter::repeat(0).take(window - 1));
|
||||
pos += window;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// 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: Group>(table: &[G], wnaf: &[i64]) -> G {
|
||||
let mut result = G::identity();
|
||||
|
||||
let mut found_one = false;
|
||||
|
||||
for n in wnaf.iter().rev() {
|
||||
if found_one {
|
||||
result = result.double();
|
||||
}
|
||||
|
||||
if *n != 0 {
|
||||
found_one = true;
|
||||
|
||||
if *n > 0 {
|
||||
result += &table[(n / 2) as usize];
|
||||
} else {
|
||||
result -= &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: Group> Wnaf<(), Vec<G>, Vec<i64>> {
|
||||
/// Construct a new wNAF context without allocating.
|
||||
pub fn new() -> Self {
|
||||
Wnaf {
|
||||
base: vec![],
|
||||
scalar: vec![],
|
||||
window_size: (),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<G: WnafGroup> Wnaf<(), Vec<G>, Vec<i64>> {
|
||||
/// 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 Group>::Scalar) -> Wnaf<usize, &mut Vec<G>, &[i64]> {
|
||||
// We hard-code a window size of 4.
|
||||
let window_size = 4;
|
||||
|
||||
// Compute the wNAF form of the scalar.
|
||||
wnaf_form(&mut self.scalar, scalar.to_repr(), 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: Group> 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: Group> 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: Group>(&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: Group>(&mut self, scalar: &<G as Group>::Scalar) -> G
|
||||
where
|
||||
B: AsRef<[G]>,
|
||||
{
|
||||
wnaf_form(self.scalar.as_mut(), scalar.to_repr(), self.window_size);
|
||||
wnaf_exp(self.base.as_ref(), self.scalar.as_mut())
|
||||
}
|
||||
}
|
|
@ -1,95 +0,0 @@
|
|||
name: CI checks
|
||||
|
||||
on: [push, pull_request]
|
||||
|
||||
jobs:
|
||||
lint:
|
||||
name: Lint
|
||||
runs-on: ubuntu-latest
|
||||
|
||||
steps:
|
||||
- uses: actions/checkout@v1
|
||||
- uses: actions-rs/toolchain@v1
|
||||
with:
|
||||
toolchain: 1.36.0
|
||||
override: true
|
||||
|
||||
# Ensure all code has been formatted with rustfmt
|
||||
- run: rustup component add rustfmt
|
||||
- name: Check formatting
|
||||
uses: actions-rs/cargo@v1
|
||||
with:
|
||||
command: fmt
|
||||
args: -- --check --color always
|
||||
|
||||
test:
|
||||
name: Test on ${{ matrix.os }}
|
||||
runs-on: ${{ matrix.os }}
|
||||
strategy:
|
||||
matrix:
|
||||
os: [ubuntu-latest, windows-latest, macOS-latest]
|
||||
|
||||
steps:
|
||||
- uses: actions/checkout@v1
|
||||
- uses: actions-rs/toolchain@v1
|
||||
with:
|
||||
toolchain: 1.36.0
|
||||
override: true
|
||||
- name: cargo fetch
|
||||
uses: actions-rs/cargo@v1
|
||||
with:
|
||||
command: fetch
|
||||
- name: Build tests
|
||||
uses: actions-rs/cargo@v1
|
||||
with:
|
||||
command: build
|
||||
args: --verbose --release --tests
|
||||
- name: Run tests
|
||||
uses: actions-rs/cargo@v1
|
||||
with:
|
||||
command: test
|
||||
args: --verbose --release
|
||||
|
||||
no-std:
|
||||
name: Check no-std compatibility
|
||||
runs-on: ubuntu-latest
|
||||
|
||||
steps:
|
||||
- uses: actions/checkout@v1
|
||||
- uses: actions-rs/toolchain@v1
|
||||
with:
|
||||
toolchain: 1.36.0
|
||||
override: true
|
||||
- run: rustup target add thumbv6m-none-eabi
|
||||
- name: cargo fetch
|
||||
uses: actions-rs/cargo@v1
|
||||
with:
|
||||
command: fetch
|
||||
- name: Build
|
||||
uses: actions-rs/cargo@v1
|
||||
with:
|
||||
command: build
|
||||
args: --verbose --target thumbv6m-none-eabi --no-default-features
|
||||
|
||||
doc-links:
|
||||
name: Nightly lint
|
||||
runs-on: ubuntu-latest
|
||||
|
||||
steps:
|
||||
- uses: actions/checkout@v1
|
||||
- uses: actions-rs/toolchain@v1
|
||||
with:
|
||||
toolchain: nightly
|
||||
override: true
|
||||
- name: cargo fetch
|
||||
uses: actions-rs/cargo@v1
|
||||
with:
|
||||
command: fetch
|
||||
|
||||
# Ensure intra-documentation links all resolve correctly
|
||||
# Requires #![deny(intra_doc_link_resolution_failure)] in crate.
|
||||
- name: Check intra-doc links
|
||||
uses: actions-rs/cargo@v1
|
||||
with:
|
||||
command: doc
|
||||
args: --document-private-items
|
|
@ -1,3 +0,0 @@
|
|||
/target
|
||||
**/*.rs.bk
|
||||
Cargo.lock
|
|
@ -1,14 +0,0 @@
|
|||
Copyrights in the "jubjub" library are retained by their contributors. No
|
||||
copyright assignment is required to contribute to the "jubjub" library.
|
||||
|
||||
The "jubjub" 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.
|
|
@ -1,63 +0,0 @@
|
|||
[package]
|
||||
authors = [
|
||||
"Sean Bowe <ewillbefull@gmail.com>",
|
||||
"Eirik Ogilvie-Wigley <eowigley@gmail.com>",
|
||||
"Jack Grigg <thestr4d@gmail.com>",
|
||||
]
|
||||
description = "Implementation of the Jubjub elliptic curve group"
|
||||
documentation = "https://docs.rs/jubjub/"
|
||||
homepage = "https://github.com/zkcrypto/jubjub"
|
||||
license = "MIT/Apache-2.0"
|
||||
name = "jubjub"
|
||||
repository = "https://github.com/zkcrypto/jubjub"
|
||||
version = "0.4.0"
|
||||
edition = "2018"
|
||||
|
||||
[dependencies.bls12_381]
|
||||
path = "../bls12_381"
|
||||
version = "0.2"
|
||||
default-features = false
|
||||
|
||||
[dependencies.byteorder]
|
||||
version = "1"
|
||||
default-features = false
|
||||
|
||||
[dependencies.ff]
|
||||
path = "../ff"
|
||||
version = "0.7"
|
||||
default-features = false
|
||||
|
||||
[dependencies.group]
|
||||
path = "../group"
|
||||
version = "0.7"
|
||||
default-features = false
|
||||
|
||||
[dependencies.rand_core]
|
||||
version = "0.5"
|
||||
default-features = false
|
||||
|
||||
[dependencies.subtle]
|
||||
version = "^2.2.1"
|
||||
default-features = false
|
||||
|
||||
[dev-dependencies]
|
||||
criterion = "0.3"
|
||||
|
||||
[dev-dependencies.rand_xorshift]
|
||||
version = "0.2"
|
||||
default-features = false
|
||||
|
||||
[features]
|
||||
default = []
|
||||
|
||||
[[bench]]
|
||||
name = "fq_bench"
|
||||
harness = false
|
||||
|
||||
[[bench]]
|
||||
name = "fr_bench"
|
||||
harness = false
|
||||
|
||||
[[bench]]
|
||||
name = "point_bench"
|
||||
harness = false
|
|
@ -1,201 +0,0 @@
|
|||
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.
|
|
@ -1,23 +0,0 @@
|
|||
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.
|
|
@ -1,53 +0,0 @@
|
|||
# jubjub [![Crates.io](https://img.shields.io/crates/v/jubjub.svg)](https://crates.io/crates/jubjub) #
|
||||
|
||||
<img
|
||||
width="15%"
|
||||
align="right"
|
||||
src="https://raw.githubusercontent.com/zcash/zips/master/protocol/jubjub.png"/>
|
||||
|
||||
This is a pure Rust implementation of the Jubjub elliptic curve group and its associated fields.
|
||||
|
||||
* **This implementation has not been reviewed or audited. Use at your own risk.**
|
||||
* This implementation targets Rust `1.36` or later.
|
||||
* All operations are constant time unless explicitly noted.
|
||||
* This implementation does not require the Rust standard library.
|
||||
|
||||
## [Documentation](https://docs.rs/jubjub)
|
||||
|
||||
## Curve Description
|
||||
|
||||
Jubjub is the [twisted Edwards curve](https://en.wikipedia.org/wiki/Twisted_Edwards_curve) `-u^2 + v^2 = 1 + d.u^2.v^2` of rational points over `GF(q)` with a subgroup of prime order `r` and cofactor `8`.
|
||||
|
||||
```
|
||||
q = 0x73eda753299d7d483339d80809a1d80553bda402fffe5bfeffffffff00000001
|
||||
r = 0x0e7db4ea6533afa906673b0101343b00a6682093ccc81082d0970e5ed6f72cb7
|
||||
d = -(10240/10241)
|
||||
```
|
||||
|
||||
The choice of `GF(q)` is made to be the scalar field of the BLS12-381 elliptic curve construction.
|
||||
|
||||
Jubjub is birationally equivalent to a [Montgomery curve](https://en.wikipedia.org/wiki/Montgomery_curve) `y^2 = x^3 + Ax^2 + x` over the same field with `A = 40962`. This value of `A` is the smallest integer such that `(A - 2) / 4` is a small integer, `A^2 - 4` is nonsquare in `GF(q)`, and the Montgomery curve and its quadratic twist have small cofactors `8` and `4`, respectively. This is identical to the relationship between Curve25519 and ed25519.
|
||||
|
||||
Please see [./doc/evidence/](./doc/evidence/) for supporting evidence that Jubjub meets the [SafeCurves](https://safecurves.cr.yp.to/index.html) criteria. The tool in [./doc/derive/](./doc/derive/) will derive the curve parameters via the above criteria to demonstrate rigidity.
|
||||
|
||||
## Acknowledgements
|
||||
|
||||
Jubjub was designed by Sean Bowe. Daira Hopwood is responsible for its name and specification. The security evidence in [./doc/evidence/](./doc/evidence/) is the product of Daira Hopwood and based on SafeCurves by Daniel J. Bernstein and Tanja Lange. Peter Newell and Daira Hopwood are responsible for the Jubjub bird image.
|
||||
|
||||
Please see `Cargo.toml` for a list of primary authors of this codebase.
|
||||
|
||||
## License
|
||||
|
||||
Licensed under either of
|
||||
|
||||
* Apache License, Version 2.0, ([LICENSE-APACHE](LICENSE-APACHE) or http://www.apache.org/licenses/LICENSE-2.0)
|
||||
* MIT license ([LICENSE-MIT](LICENSE-MIT) or http://opensource.org/licenses/MIT)
|
||||
|
||||
at your option.
|
||||
|
||||
### Contribution
|
||||
|
||||
Unless you explicitly state otherwise, any contribution intentionally
|
||||
submitted for inclusion in the work by you, as defined in the Apache-2.0
|
||||
license, shall be dual licensed as above, without any additional terms or
|
||||
conditions.
|
|
@ -1,53 +0,0 @@
|
|||
# 0.4.0
|
||||
|
||||
This release adds implementations of the `ff` and `group` traits. Additional trait
|
||||
implementations (for standard traits) have been added where the `ff` and `group` trait
|
||||
bounds require them.
|
||||
|
||||
## Added
|
||||
* `jubjub::SubgroupPoint`, which represents an element of Jubjub's prime-order subgroup.
|
||||
It implements the following traits:
|
||||
* `group::{Group, GroupEncoding}`
|
||||
* `group::prime::PrimeGroup`
|
||||
* New trait implementations for `jubjub::ExtendedPoint`:
|
||||
* `group::{Curve, Group, GroupEncoding, WnafGroup}`
|
||||
* `group::cofactor::{CofactorCurve, CofactorGroup}`
|
||||
* New trait implementations for `jubjub::AffinePoint`:
|
||||
* `group::GroupEncoding`
|
||||
* `group::cofactor::CofactorCurveAffine`
|
||||
* New trait implementations for `jubjub::Fr`:
|
||||
* `ff::{Field, PrimeField}`
|
||||
* `jubjub::AffinePoint::is_identity`
|
||||
* `jubjub::AffinePoint::to_extended`
|
||||
* `jubjub::Scalar`, as an alias for `jubjub::Fr`.
|
||||
|
||||
## Changed
|
||||
* We've migrated to `bls12_381 0.2`.
|
||||
* `rand_core` is now a regular dependency.
|
||||
* We depend on the `byteorder` crate again, as it is part of the `ff::PrimeField` trait.
|
||||
* The benchmarks are now implemented using `criterion`.
|
||||
|
||||
# 0.3.0
|
||||
|
||||
This release now depends on the `bls12_381` crate, which exposes the `Fq` field type that we re-export.
|
||||
|
||||
* The `Fq` and `Fr` field types now have better constant function support for various operations and constructors.
|
||||
* We no longer depend on the `byteorder` crate.
|
||||
* We've bumped our `rand_core` dev-dependency up to 0.5.
|
||||
* We've removed the `std` and `nightly` features.
|
||||
* We've bumped our dependency of `subtle` up to `^2.2.1`.
|
||||
|
||||
# 0.2.0
|
||||
|
||||
This release switches to `subtle 2.1` to bring in the `CtOption` type, and also makes a few useful API changes.
|
||||
|
||||
* Implemented `Mul<Fr>` for `AffineNielsPoint` and `ExtendedNielsPoint`
|
||||
* Changed `AffinePoint::to_niels()` to be a `const` function so that constant curve points can be constructed without statics.
|
||||
* Implemented `multiply_bits` for `AffineNielsPoint`, `ExtendedNielsPoint`
|
||||
* Removed `CtOption` and replaced it with `CtOption` from `subtle` crate.
|
||||
* Modified receivers of some methods to reduce stack usage
|
||||
* Changed various `into_bytes` methods into `to_bytes`
|
||||
|
||||
# 0.1.0
|
||||
|
||||
Initial release.
|
|
@ -1,58 +0,0 @@
|
|||
use criterion::{criterion_group, criterion_main, Criterion};
|
||||
use jubjub::*;
|
||||
|
||||
fn bench_add_assign(c: &mut Criterion) {
|
||||
let mut n = Fq::one();
|
||||
let neg_one = -Fq::one();
|
||||
c.bench_function("Fq add_assign", |b| {
|
||||
b.iter(move || {
|
||||
n += &neg_one;
|
||||
})
|
||||
});
|
||||
}
|
||||
|
||||
fn bench_sub_assign(c: &mut Criterion) {
|
||||
let mut n = Fq::one();
|
||||
let neg_one = -Fq::one();
|
||||
c.bench_function("Fq sub_assign", |b| {
|
||||
b.iter(move || {
|
||||
n -= &neg_one;
|
||||
})
|
||||
});
|
||||
}
|
||||
|
||||
fn bench_mul_assign(c: &mut Criterion) {
|
||||
let mut n = Fq::one();
|
||||
let neg_one = -Fq::one();
|
||||
c.bench_function("Fq mul_assign", |b| {
|
||||
b.iter(move || {
|
||||
n *= &neg_one;
|
||||
})
|
||||
});
|
||||
}
|
||||
|
||||
fn bench_square(c: &mut Criterion) {
|
||||
let n = Fq::one();
|
||||
c.bench_function("Fq square", |b| b.iter(move || n.square()));
|
||||
}
|
||||
|
||||
fn bench_invert(c: &mut Criterion) {
|
||||
let n = Fq::one();
|
||||
c.bench_function("Fq invert", |b| b.iter(move || n.invert()));
|
||||
}
|
||||
|
||||
fn bench_sqrt(c: &mut Criterion) {
|
||||
let n = Fq::one().double().double();
|
||||
c.bench_function("Fq sqrt", |b| b.iter(move || n.sqrt()));
|
||||
}
|
||||
|
||||
criterion_group!(
|
||||
benches,
|
||||
bench_add_assign,
|
||||
bench_sub_assign,
|
||||
bench_mul_assign,
|
||||
bench_square,
|
||||
bench_invert,
|
||||
bench_sqrt,
|
||||
);
|
||||
criterion_main!(benches);
|
|
@ -1,58 +0,0 @@
|
|||
use criterion::{criterion_group, criterion_main, Criterion};
|
||||
use jubjub::*;
|
||||
|
||||
fn bench_add_assign(c: &mut Criterion) {
|
||||
let mut n = Fr::one();
|
||||
let neg_one = -Fr::one();
|
||||
c.bench_function("Fr add_assign", |b| {
|
||||
b.iter(move || {
|
||||
n += &neg_one;
|
||||
})
|
||||
});
|
||||
}
|
||||
|
||||
fn bench_sub_assign(c: &mut Criterion) {
|
||||
let mut n = Fr::one();
|
||||
let neg_one = -Fr::one();
|
||||
c.bench_function("Fr sub_assign", |b| {
|
||||
b.iter(move || {
|
||||
n -= &neg_one;
|
||||
})
|
||||
});
|
||||
}
|
||||
|
||||
fn bench_mul_assign(c: &mut Criterion) {
|
||||
let mut n = Fr::one();
|
||||
let neg_one = -Fr::one();
|
||||
c.bench_function("Fr mul_assign", |b| {
|
||||
b.iter(move || {
|
||||
n *= &neg_one;
|
||||
})
|
||||
});
|
||||
}
|
||||
|
||||
fn bench_square(c: &mut Criterion) {
|
||||
let n = Fr::one();
|
||||
c.bench_function("Fr square", |b| b.iter(move || n.square()));
|
||||
}
|
||||
|
||||
fn bench_invert(c: &mut Criterion) {
|
||||
let n = Fr::one();
|
||||
c.bench_function("Fr invert", |b| b.iter(move || n.invert()));
|
||||
}
|
||||
|
||||
fn bench_sqrt(c: &mut Criterion) {
|
||||
let n = Fr::one().double().double();
|
||||
c.bench_function("Fr sqrt", |b| b.iter(move || n.sqrt()));
|
||||
}
|
||||
|
||||
criterion_group!(
|
||||
benches,
|
||||
bench_add_assign,
|
||||
bench_sub_assign,
|
||||
bench_mul_assign,
|
||||
bench_square,
|
||||
bench_invert,
|
||||
bench_sqrt,
|
||||
);
|
||||
criterion_main!(benches);
|
|
@ -1,73 +0,0 @@
|
|||
use criterion::{criterion_group, criterion_main, Criterion};
|
||||
use jubjub::*;
|
||||
|
||||
// Non-Niels
|
||||
|
||||
fn bench_point_doubling(c: &mut Criterion) {
|
||||
let a = ExtendedPoint::identity();
|
||||
c.bench_function("Jubjub point doubling", |bencher| {
|
||||
bencher.iter(move || a.double())
|
||||
});
|
||||
}
|
||||
|
||||
fn bench_point_addition(c: &mut Criterion) {
|
||||
let a = ExtendedPoint::identity();
|
||||
let b = -ExtendedPoint::identity();
|
||||
c.bench_function("Jubjub point addition", |bencher| {
|
||||
bencher.iter(move || a + b)
|
||||
});
|
||||
}
|
||||
|
||||
fn bench_point_subtraction(c: &mut Criterion) {
|
||||
let a = ExtendedPoint::identity();
|
||||
let b = -ExtendedPoint::identity();
|
||||
c.bench_function("Jubjub point subtraction", |bencher| {
|
||||
bencher.iter(move || a + b)
|
||||
});
|
||||
}
|
||||
|
||||
// Niels
|
||||
|
||||
fn bench_cached_point_addition(c: &mut Criterion) {
|
||||
let a = ExtendedPoint::identity();
|
||||
let b = ExtendedPoint::identity().to_niels();
|
||||
c.bench_function("Jubjub cached point addition", |bencher| {
|
||||
bencher.iter(move || a + b)
|
||||
});
|
||||
}
|
||||
|
||||
fn bench_cached_point_subtraction(c: &mut Criterion) {
|
||||
let a = ExtendedPoint::identity();
|
||||
let b = ExtendedPoint::identity().to_niels();
|
||||
c.bench_function("Jubjub cached point subtraction", |bencher| {
|
||||
bencher.iter(move || a + b)
|
||||
});
|
||||
}
|
||||
|
||||
fn bench_cached_affine_point_addition(c: &mut Criterion) {
|
||||
let a = ExtendedPoint::identity();
|
||||
let b = AffinePoint::identity().to_niels();
|
||||
c.bench_function("Jubjub cached affine point addition", |bencher| {
|
||||
bencher.iter(move || a + b)
|
||||
});
|
||||
}
|
||||
|
||||
fn bench_cached_affine_point_subtraction(c: &mut Criterion) {
|
||||
let a = ExtendedPoint::identity();
|
||||
let b = AffinePoint::identity().to_niels();
|
||||
c.bench_function("Jubjub cached affine point subtraction", |bencher| {
|
||||
bencher.iter(move || a + b)
|
||||
});
|
||||
}
|
||||
|
||||
criterion_group!(
|
||||
benches,
|
||||
bench_point_doubling,
|
||||
bench_point_addition,
|
||||
bench_point_subtraction,
|
||||
bench_cached_point_addition,
|
||||
bench_cached_point_subtraction,
|
||||
bench_cached_affine_point_addition,
|
||||
bench_cached_affine_point_subtraction,
|
||||
);
|
||||
criterion_main!(benches);
|
|
@ -1 +0,0 @@
|
|||
*.sage.py
|
|
@ -1,32 +0,0 @@
|
|||
q = 0x73eda753299d7d483339d80809a1d80553bda402fffe5bfeffffffff00000001
|
||||
Fq = GF(q)
|
||||
|
||||
# We wish to find a Montgomery curve with B = 1 and A the smallest such
|
||||
# that (A - 2) / 4 is a small integer.
|
||||
def get_A(n):
|
||||
return (n * 4) + 2
|
||||
|
||||
# A = 2 is invalid (singular curve), so we start at i = 1 (A = 6)
|
||||
i = 1
|
||||
|
||||
while True:
|
||||
A = Fq(get_A(i))
|
||||
i = i + 1
|
||||
|
||||
# We also want that A^2 - 4 is nonsquare.
|
||||
if ((A^2) - 4).is_square():
|
||||
continue
|
||||
|
||||
ec = EllipticCurve(Fq, [0, A, 0, 1, 0])
|
||||
o = ec.order()
|
||||
|
||||
if (o % 8 == 0):
|
||||
o = o // 8
|
||||
if is_prime(o):
|
||||
twist = ec.quadratic_twist()
|
||||
otwist = twist.order()
|
||||
if (otwist % 4 == 0):
|
||||
otwist = otwist // 4
|
||||
if is_prime(otwist):
|
||||
print "A = %s" % A
|
||||
exit(0)
|
|
@ -1,102 +0,0 @@
|
|||
# Byte-compiled / optimized / DLL files
|
||||
__pycache__/
|
||||
*.py[cod]
|
||||
*$py.class
|
||||
|
||||
# C extensions
|
||||
*.so
|
||||
|
||||
# Distribution / packaging
|
||||
.Python
|
||||
env/
|
||||
build/
|
||||
develop-eggs/
|
||||
dist/
|
||||
downloads/
|
||||
eggs/
|
||||
.eggs/
|
||||
lib/
|
||||
lib64/
|
||||
parts/
|
||||
sdist/
|
||||
var/
|
||||
wheels/
|
||||
*.egg-info/
|
||||
.installed.cfg
|
||||
*.egg
|
||||
|
||||
# PyInstaller
|
||||
# Usually these files are written by a python script from a template
|
||||
# before PyInstaller builds the exe, so as to inject date/other infos into it.
|
||||
*.manifest
|
||||
*.spec
|
||||
|
||||
# Installer logs
|
||||
pip-log.txt
|
||||
pip-delete-this-directory.txt
|
||||
|
||||
# Unit test / coverage reports
|
||||
htmlcov/
|
||||
.tox/
|
||||
.coverage
|
||||
.coverage.*
|
||||
.cache
|
||||
nosetests.xml
|
||||
coverage.xml
|
||||
*.cover
|
||||
.hypothesis/
|
||||
|
||||
# Translations
|
||||
*.mo
|
||||
*.pot
|
||||
|
||||
# Django stuff:
|
||||
*.log
|
||||
local_settings.py
|
||||
|
||||
# Flask stuff:
|
||||
instance/
|
||||
.webassets-cache
|
||||
|
||||
# Scrapy stuff:
|
||||
.scrapy
|
||||
|
||||
# Sphinx documentation
|
||||
docs/_build/
|
||||
|
||||
# PyBuilder
|
||||
target/
|
||||
|
||||
# Jupyter Notebook
|
||||
.ipynb_checkpoints
|
||||
|
||||
# pyenv
|
||||
.python-version
|
||||
|
||||
# celery beat schedule file
|
||||
celerybeat-schedule
|
||||
|
||||
# SageMath parsed files
|
||||
*.sage.py
|
||||
|
||||
# dotenv
|
||||
.env
|
||||
|
||||
# virtualenv
|
||||
.venv
|
||||
venv/
|
||||
ENV/
|
||||
|
||||
# Spyder project settings
|
||||
.spyderproject
|
||||
.spyproject
|
||||
|
||||
# Rope project settings
|
||||
.ropeproject
|
||||
|
||||
# mkdocs documentation
|
||||
/site
|
||||
|
||||
# mypy
|
||||
.mypy_cache/
|
||||
|
|
@ -1,19 +0,0 @@
|
|||
Copyright (c) 2017 The Zcash developers
|
||||
|
||||
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.
|
|
@ -1,28 +0,0 @@
|
|||
Jubjub supporting evidence
|
||||
--------------------------
|
||||
|
||||
This repository contains supporting evidence that the twisted Edwards curve
|
||||
-x^2 + y^2 = 1 - (10240/10241).x^2.y^2 of rational points over
|
||||
GF(52435875175126190479447740508185965837690552500527637822603658699938581184513),
|
||||
[also called "Jubjub"](https://z.cash/technology/jubjub.html),
|
||||
satisfies the [SafeCurves criteria](https://safecurves.cr.yp.to/index.html).
|
||||
|
||||
The script ``verify.sage`` is based on
|
||||
[this script from the SafeCurves site](https://safecurves.cr.yp.to/verify.html),
|
||||
modified
|
||||
|
||||
* to support twisted Edwards curves;
|
||||
* to generate a file 'primes' containing the primes needed for primality proofs,
|
||||
if it is not already present;
|
||||
* to change the directory in which Pocklington proof files are generated
|
||||
(``proof/`` rather than ``../../../proof``), and to create that directory
|
||||
if it does not exist.
|
||||
|
||||
Prerequisites:
|
||||
|
||||
* apt-get install sagemath
|
||||
* pip install sortedcontainers
|
||||
|
||||
Run ``sage verify.sage .``, or ``./run.sh`` to also print out the results.
|
||||
|
||||
Note that the "rigidity" criterion cannot be checked automatically.
|
|
@ -1 +0,0 @@
|
|||
-1
|
|
@ -1 +0,0 @@
|
|||
19257038036680949359750312669786877991949435402254120286184196891950884077233
|
|
@ -1 +0,0 @@
|
|||
6554484396890773809930967563523245729705921265872317281365359162392183254199
|
|
@ -1 +0,0 @@
|
|||
52435875175126190479447740508185965837690552500527637822603658699938581184513
|
|
@ -1 +0,0 @@
|
|||
fully rigid
|
|
@ -1,4 +0,0 @@
|
|||
#!/bin/sh
|
||||
sage verify.sage .
|
||||
grep -Rn '.' verify-* |grep '^verify-.*:1:' |sed 's/:1:/ = /'
|
||||
|
|
@ -1 +0,0 @@
|
|||
tedwards
|
|
@ -1,444 +0,0 @@
|
|||
import os
|
||||
import sys
|
||||
from errno import ENOENT, EEXIST
|
||||
from sortedcontainers import SortedSet
|
||||
|
||||
|
||||
def readfile(fn):
|
||||
fd = open(fn,'r')
|
||||
r = fd.read()
|
||||
fd.close()
|
||||
return r
|
||||
|
||||
def writefile(fn,s):
|
||||
fd = open(fn,'w')
|
||||
fd.write(s)
|
||||
fd.close()
|
||||
|
||||
def expand2(n):
|
||||
s = ""
|
||||
|
||||
while n != 0:
|
||||
j = 16
|
||||
while 2**j < abs(n): j += 1
|
||||
if 2**j - abs(n) > abs(n) - 2**(j-1): j -= 1
|
||||
|
||||
if abs(abs(n) - 2**j) > 2**(j - 10):
|
||||
if n > 0:
|
||||
if s != "": s += " + "
|
||||
s += str(n)
|
||||
else:
|
||||
s += " - " + str(-n)
|
||||
n = 0
|
||||
elif n > 0:
|
||||
if s != "": s += " + "
|
||||
s += "2^" + str(j)
|
||||
n -= 2**j
|
||||
else:
|
||||
s += " - 2^" + str(j)
|
||||
n += 2**j
|
||||
|
||||
return s
|
||||
|
||||
def requirement(fn,istrue):
|
||||
writefile(fn,str(istrue) + '\n')
|
||||
return istrue
|
||||
|
||||
def verify():
|
||||
try:
|
||||
os.mkdir('proof')
|
||||
except OSError as e:
|
||||
if e.errno != EEXIST: raise
|
||||
|
||||
try:
|
||||
s = set(map(Integer, readfile('primes').split()))
|
||||
except IOError, e:
|
||||
if e.errno != ENOENT: raise
|
||||
s = set()
|
||||
|
||||
needtofactor = SortedSet()
|
||||
V = SortedSet() # distinct verified primes
|
||||
verify_primes(V, s, needtofactor)
|
||||
verify_pass(V, needtofactor)
|
||||
|
||||
old = V
|
||||
needtofactor.update(V)
|
||||
while len(needtofactor) > len(old):
|
||||
k = len(needtofactor) - len(old)
|
||||
sys.stdout.write('Factoring %d integer%s' % (k, '' if k == 1 else 's'))
|
||||
sys.stdout.flush()
|
||||
for x in needtofactor:
|
||||
if x not in old:
|
||||
for (y, z) in factor(x):
|
||||
s.add(y)
|
||||
sys.stdout.write('.')
|
||||
sys.stdout.flush()
|
||||
|
||||
print('')
|
||||
|
||||
old = needtofactor.copy()
|
||||
verify_primes(V, s, needtofactor)
|
||||
|
||||
writefile('primes', '\n'.join(map(str, s)) + '\n')
|
||||
writefile('verify-primes', '<html><body>\n' +
|
||||
''.join(('2\n' if v == 2 else
|
||||
'<a href=proof/%s.html>%s</a>\n' % (v,v)) for v in V) +
|
||||
'</body></html>\n')
|
||||
|
||||
verify_pass(V, needtofactor)
|
||||
|
||||
|
||||
def verify_primes(V, s, needtofactor):
|
||||
for n in sorted(s):
|
||||
if not n.is_prime() or n in V: continue
|
||||
needtofactor.add(n-1)
|
||||
if n == 2:
|
||||
V.add(n)
|
||||
continue
|
||||
for trybase in primes(2,10000):
|
||||
base = Integers(n)(trybase)
|
||||
if base^(n-1) != 1: continue
|
||||
proof = 'Primality proof for n = %s:\n' % n
|
||||
proof += '<p>Take b = %s.\n' % base
|
||||
proof += '<p>b^(n-1) mod n = 1.\n'
|
||||
f = factor(1)
|
||||
for v in reversed(V):
|
||||
if f.prod()^2 <= n:
|
||||
if n % v == 1:
|
||||
u = base^((n-1)/v)-1
|
||||
if u.is_unit():
|
||||
if v == 2:
|
||||
proof += '<p>2 is prime.\n'
|
||||
else:
|
||||
proof += '<p><a href=%s.html>%s is prime.</a>\n' % (v,v)
|
||||
proof += '<br>b^((n-1)/%s)-1 mod n = %s, which is a unit, inverse %s.\n' % (v,u,1/u)
|
||||
f *= factor(v)^(n-1).valuation(v)
|
||||
if f.prod()^2 <= n: continue
|
||||
if n % f.prod() != 1: continue
|
||||
proof += '<p>(%s) divides n-1.\n' % f
|
||||
proof += '<p>(%s)^2 > n.\n' % f
|
||||
proof += "<p>n is prime by Pocklington's theorem.\n"
|
||||
proof += '\n'
|
||||
writefile('proof/%s.html' % n,proof)
|
||||
V.add(n)
|
||||
break
|
||||
|
||||
|
||||
def verify_pass(V, needtofactor):
|
||||
p = Integer(readfile('p'))
|
||||
k = GF(p)
|
||||
kz.<z> = k[]
|
||||
l = Integer(readfile('l'))
|
||||
x0 = Integer(readfile('x0'))
|
||||
y0 = Integer(readfile('y0'))
|
||||
x1 = Integer(readfile('x1'))
|
||||
y1 = Integer(readfile('y1'))
|
||||
shape = readfile('shape').strip()
|
||||
rigid = readfile('rigid').strip()
|
||||
|
||||
safefield = True
|
||||
safeeq = True
|
||||
safebase = True
|
||||
saferho = True
|
||||
safetransfer = True
|
||||
safedisc = True
|
||||
saferigid = True
|
||||
safeladder = True
|
||||
safetwist = True
|
||||
safecomplete = True
|
||||
safeind = True
|
||||
|
||||
pstatus = 'Unverified'
|
||||
if not p.is_prime(): pstatus = 'False'
|
||||
needtofactor.add(p)
|
||||
if p in V: pstatus = 'True'
|
||||
if pstatus != 'True': safefield = False
|
||||
writefile('verify-pisprime',pstatus + '\n')
|
||||
|
||||
pstatus = 'Unverified'
|
||||
if not l.is_prime(): pstatus = 'False'
|
||||
needtofactor.add(l)
|
||||
if l in V: pstatus = 'True'
|
||||
if pstatus != 'True': safebase = False
|
||||
writefile('verify-lisprime',pstatus + '\n')
|
||||
|
||||
writefile('expand2-p','= %s\n' % expand2(p))
|
||||
writefile('expand2-l','<br>= %s\n' % expand2(l))
|
||||
|
||||
writefile('hex-p',hex(p) + '\n')
|
||||
writefile('hex-l',hex(l) + '\n')
|
||||
writefile('hex-x0',hex(x0) + '\n')
|
||||
writefile('hex-x1',hex(x1) + '\n')
|
||||
writefile('hex-y0',hex(y0) + '\n')
|
||||
writefile('hex-y1',hex(y1) + '\n')
|
||||
|
||||
gcdlpis1 = gcd(l,p) == 1
|
||||
safetransfer &= requirement('verify-gcdlp1',gcdlpis1)
|
||||
|
||||
writefile('verify-movsafe','Unverified\n')
|
||||
writefile('verify-embeddingdegree','Unverified\n')
|
||||
if gcdlpis1 and l.is_prime():
|
||||
u = Integers(l)(p)
|
||||
d = l-1
|
||||
needtofactor.add(d)
|
||||
for v in V:
|
||||
while d % v == 0: d /= v
|
||||
if d == 1:
|
||||
d = l-1
|
||||
for v in V:
|
||||
while d % v == 0:
|
||||
if u^(d/v) != 1: break
|
||||
d /= v
|
||||
safetransfer &= requirement('verify-movsafe',(l-1)/d <= 100)
|
||||
writefile('verify-embeddingdegree','<font size=1>%s</font><br>= (l-1)/%s\n' % (d,(l-1)/d))
|
||||
|
||||
t = p+1-l*round((p+1)/l)
|
||||
if l^2 > 16*p:
|
||||
writefile('verify-trace','%s\n' % t)
|
||||
f = factor(1)
|
||||
d = (p+1-t)/l
|
||||
needtofactor.add(d)
|
||||
for v in V:
|
||||
while d % v == 0:
|
||||
d //= v
|
||||
f *= factor(v)
|
||||
writefile('verify-cofactor','%s\n' % f)
|
||||
else:
|
||||
writefile('verify-trace','Unverified\n')
|
||||
writefile('verify-cofactor','Unverified\n')
|
||||
|
||||
D = t^2-4*p
|
||||
needtofactor.add(D)
|
||||
for v in V:
|
||||
while D % v^2 == 0: D /= v^2
|
||||
if prod([v for v in V if D % v == 0]) != -D:
|
||||
writefile('verify-disc','Unverified\n')
|
||||
writefile('verify-discisbig','Unverified\n')
|
||||
safedisc = False
|
||||
else:
|
||||
f = -prod([factor(v) for v in V if D % v == 0])
|
||||
if D % 4 != 1:
|
||||
D *= 4
|
||||
f = factor(4) * f
|
||||
Dbits = (log(-D)/log(2)).numerical_approx()
|
||||
writefile('verify-disc','<font size=1>%s</font><br>= <font size=1>%s</font><br>≈ -2^%.1f\n' % (D,f,Dbits))
|
||||
safedisc &= requirement('verify-discisbig',D < -2^100)
|
||||
|
||||
pi4 = 0.78539816339744830961566084581987572105
|
||||
rho = log(pi4*l)/log(4)
|
||||
writefile('verify-rho','%.1f\n' % rho)
|
||||
saferho &= requirement('verify-rhoabove100',rho.numerical_approx() >= 100)
|
||||
|
||||
twistl = 'Unverified'
|
||||
d = p+1+t
|
||||
needtofactor.add(d)
|
||||
for v in V:
|
||||
while d % v == 0: d /= v
|
||||
if d == 1:
|
||||
d = p+1+t
|
||||
for v in V:
|
||||
if d % v == 0:
|
||||
if twistl == 'Unverified' or v > twistl: twistl = v
|
||||
|
||||
writefile('verify-twistl','%s\n' % twistl)
|
||||
writefile('verify-twistembeddingdegree','Unverified\n')
|
||||
writefile('verify-twistmovsafe','Unverified\n')
|
||||
if twistl == 'Unverified':
|
||||
writefile('hex-twistl','Unverified\n')
|
||||
writefile('expand2-twistl','Unverified\n')
|
||||
writefile('verify-twistcofactor','Unverified\n')
|
||||
writefile('verify-gcdtwistlp1','Unverified\n')
|
||||
writefile('verify-twistrho','Unverified\n')
|
||||
safetwist = False
|
||||
else:
|
||||
writefile('hex-twistl',hex(twistl) + '\n')
|
||||
writefile('expand2-twistl','<br>= %s\n' % expand2(twistl))
|
||||
f = factor(1)
|
||||
d = (p+1+t)/twistl
|
||||
needtofactor.add(d)
|
||||
for v in V:
|
||||
while d % v == 0:
|
||||
d //= v
|
||||
f *= factor(v)
|
||||
writefile('verify-twistcofactor','%s\n' % f)
|
||||
gcdtwistlpis1 = gcd(twistl,p) == 1
|
||||
safetwist &= requirement('verify-gcdtwistlp1',gcdtwistlpis1)
|
||||
|
||||
movsafe = 'Unverified'
|
||||
embeddingdegree = 'Unverified'
|
||||
if gcdtwistlpis1 and twistl.is_prime():
|
||||
u = Integers(twistl)(p)
|
||||
d = twistl-1
|
||||
needtofactor.add(d)
|
||||
for v in V:
|
||||
while d % v == 0: d /= v
|
||||
if d == 1:
|
||||
d = twistl-1
|
||||
for v in V:
|
||||
while d % v == 0:
|
||||
if u^(d/v) != 1: break
|
||||
d /= v
|
||||
safetwist &= requirement('verify-twistmovsafe',(twistl-1)/d <= 100)
|
||||
writefile('verify-twistembeddingdegree',"<font size=1>%s</font><br>= (l'-1)/%s\n" % (d,(twistl-1)/d))
|
||||
|
||||
rho = log(pi4*twistl)/log(4)
|
||||
writefile('verify-twistrho','%.1f\n' % rho)
|
||||
safetwist &= requirement('verify-twistrhoabove100',rho.numerical_approx() >= 100)
|
||||
|
||||
precomp = 0
|
||||
joint = l
|
||||
needtofactor.add(p+1-t)
|
||||
needtofactor.add(p+1+t)
|
||||
for v in V:
|
||||
d1 = p+1-t
|
||||
d2 = p+1+t
|
||||
while d1 % v == 0 or d2 % v == 0:
|
||||
if d1 % v == 0: d1 //= v
|
||||
if d2 % v == 0: d2 //= v
|
||||
# best case for attack: cyclic; each power is usable
|
||||
# also assume that kangaroo is as efficient as rho
|
||||
if v + sqrt(pi4*joint/v) < sqrt(pi4*joint):
|
||||
precomp += v
|
||||
joint /= v
|
||||
|
||||
rho = log(precomp + sqrt(pi4 * joint))/log(2)
|
||||
writefile('verify-jointrho','%.1f\n' % rho)
|
||||
safetwist &= requirement('verify-jointrhoabove100',rho.numerical_approx() >= 100)
|
||||
|
||||
|
||||
x0 = k(x0)
|
||||
y0 = k(y0)
|
||||
x1 = k(x1)
|
||||
y1 = k(y1)
|
||||
|
||||
if shape in ('edwards', 'tedwards'):
|
||||
d = Integer(readfile('d'))
|
||||
a = 1
|
||||
if shape == 'tedwards':
|
||||
a = Integer(readfile('a'))
|
||||
|
||||
writefile('verify-shape','Twisted Edwards\n')
|
||||
writefile('verify-equation','%sx^2+y^2 = 1%+dx^2y^2\n' % (a, d))
|
||||
if a == 1:
|
||||
writefile('verify-shape','Edwards\n')
|
||||
writefile('verify-equation','x^2+y^2 = 1%+dx^2y^2\n' % d)
|
||||
|
||||
a = k(a)
|
||||
d = k(d)
|
||||
elliptic = a*d*(a-d)
|
||||
level0 = a*x0^2+y0^2-1-d*x0^2*y0^2
|
||||
level1 = a*x1^2+y1^2-1-d*x1^2*y1^2
|
||||
|
||||
if shape == 'montgomery':
|
||||
writefile('verify-shape','Montgomery\n')
|
||||
A = Integer(readfile('A'))
|
||||
B = Integer(readfile('B'))
|
||||
equation = '%sy^2 = x^3<wbr>%+dx^2+x' % (B,A)
|
||||
if B == 1:
|
||||
equation = 'y^2 = x^3<wbr>%+dx^2+x' % A
|
||||
writefile('verify-equation',equation + '\n')
|
||||
|
||||
A = k(A)
|
||||
B = k(B)
|
||||
elliptic = B*(A^2-4)
|
||||
level0 = B*y0^2-x0^3-A*x0^2-x0
|
||||
level1 = B*y1^2-x1^3-A*x1^2-x1
|
||||
|
||||
if shape == 'shortw':
|
||||
writefile('verify-shape','short Weierstrass\n')
|
||||
a = Integer(readfile('a'))
|
||||
b = Integer(readfile('b'))
|
||||
writefile('verify-equation','y^2 = x^3<wbr>%+dx<wbr>%+d\n' % (a,b))
|
||||
|
||||
a = k(a)
|
||||
b = k(b)
|
||||
elliptic = 4*a^3+27*b^2
|
||||
level0 = y0^2-x0^3-a*x0-b
|
||||
level1 = y1^2-x1^3-a*x1-b
|
||||
|
||||
writefile('verify-elliptic',str(elliptic) + '\n')
|
||||
safeeq &= requirement('verify-iselliptic',elliptic != 0)
|
||||
safebase &= requirement('verify-isoncurve0',level0 == 0)
|
||||
safebase &= requirement('verify-isoncurve1',level1 == 0)
|
||||
|
||||
if shape in ('edwards', 'tedwards'):
|
||||
A = 2*(a+d)/(a-d)
|
||||
B = 4/(a-d)
|
||||
x0,y0 = (1+y0)/(1-y0),((1+y0)/(1-y0))/x0
|
||||
x1,y1 = (1+y1)/(1-y1),((1+y1)/(1-y1))/x1
|
||||
shape = 'montgomery'
|
||||
|
||||
if shape == 'montgomery':
|
||||
a = (3-A^2)/(3*B^2)
|
||||
b = (2*A^3-9*A)/(27*B^3)
|
||||
x0,y0 = (x0+A/3)/B,y0/B
|
||||
x1,y1 = (x1+A/3)/B,y1/B
|
||||
shape = 'shortw'
|
||||
|
||||
try:
|
||||
E = EllipticCurve([a,b])
|
||||
numorder2 = 0
|
||||
numorder4 = 0
|
||||
for P in E(0).division_points(4):
|
||||
if P != 0 and 2*P == 0:
|
||||
numorder2 += 1
|
||||
if 2*P != 0 and 4*P == 0:
|
||||
numorder4 += 1
|
||||
writefile('verify-numorder2',str(numorder2) + '\n')
|
||||
writefile('verify-numorder4',str(numorder4) + '\n')
|
||||
completesingle = False
|
||||
completemulti = False
|
||||
if numorder4 == 2 and numorder2 == 1:
|
||||
# complete edwards form, and montgomery with unique point of order 2
|
||||
completesingle = True
|
||||
completemulti = True
|
||||
# should extend this to allow complete twisted hessian
|
||||
safecomplete &= requirement('verify-completesingle',completesingle)
|
||||
safecomplete &= requirement('verify-completemulti',completemulti)
|
||||
safecomplete &= requirement('verify-ltimesbase1is0',l * E([x1,y1]) == 0)
|
||||
writefile('verify-ltimesbase1',str(l * E([x1,y1])) + '\n')
|
||||
writefile('verify-cofactorbase01',str(((p+1-t)//l) * E([x0,y0]) == E([x1,y1])) + '\n')
|
||||
except:
|
||||
writefile('verify-numorder2','Unverified\n')
|
||||
writefile('verify-numorder4','Unverified\n')
|
||||
writefile('verify-ltimesbase1','Unverified\n')
|
||||
writefile('verify-cofactorbase01','Unverified\n')
|
||||
safecomplete = False
|
||||
|
||||
montladder = False
|
||||
for r,e in (z^3+a*z+b).roots():
|
||||
if (3*r^2+a).is_square():
|
||||
montladder = True
|
||||
safeladder &= requirement('verify-montladder',montladder)
|
||||
|
||||
indistinguishability = False
|
||||
elligator2 = False
|
||||
if (p+1-t) % 2 == 0:
|
||||
if b != 0:
|
||||
indistinguishability = True
|
||||
elligator2 = True
|
||||
safeind &= requirement('verify-indistinguishability',indistinguishability)
|
||||
writefile('verify-ind-notes','Elligator 2: %s.\n' % ['No','Yes'][elligator2])
|
||||
|
||||
saferigid &= (rigid == 'fully rigid' or rigid == 'somewhat rigid')
|
||||
|
||||
safecurve = True
|
||||
safecurve &= requirement('verify-safefield',safefield)
|
||||
safecurve &= requirement('verify-safeeq',safeeq)
|
||||
safecurve &= requirement('verify-safebase',safebase)
|
||||
safecurve &= requirement('verify-saferho',saferho)
|
||||
safecurve &= requirement('verify-safetransfer',safetransfer)
|
||||
safecurve &= requirement('verify-safedisc',safedisc)
|
||||
safecurve &= requirement('verify-saferigid',saferigid)
|
||||
safecurve &= requirement('verify-safeladder',safeladder)
|
||||
safecurve &= requirement('verify-safetwist',safetwist)
|
||||
safecurve &= requirement('verify-safecomplete',safecomplete)
|
||||
safecurve &= requirement('verify-safeind',safeind)
|
||||
requirement('verify-safecurve',safecurve)
|
||||
|
||||
originaldir = os.open('.',os.O_RDONLY)
|
||||
for i in range(1,len(sys.argv)):
|
||||
os.fchdir(originaldir)
|
||||
os.chdir(sys.argv[i])
|
||||
verify()
|
||||
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue