Remove subtree crates

Now that we have finished the large refactor, we can make subsequent
refactoring changes within the individual crates, and propagate the
changes into the Zcash crates via normal dependency updates.
This commit is contained in:
Jack Grigg 2020-08-27 23:25:47 +01:00
parent 3f06d92eec
commit 3a4ea5da18
121 changed files with 0 additions and 28725 deletions

View File

@ -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]

2
bellman/.gitignore vendored
View File

@ -1,2 +0,0 @@
target
Cargo.lock

View File

@ -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.

View File

@ -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" }

View File

@ -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.

View File

@ -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.

View File

@ -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.

View File

@ -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);
}

View File

@ -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),
}
}
}

View File

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

View File

@ -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]);
}
}
}

View File

@ -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
}
}

View File

@ -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));
}
}

View File

@ -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)),
&current_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());
}
}
}
}

View File

@ -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);
}
}
}
}
}
}

View File

@ -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());
}

View File

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

View File

@ -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(),
),
})
}

View File

@ -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>(&params.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),
},
&params,
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());
}
}
}

View File

@ -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(),
})
}

View File

@ -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)
}
}

View File

@ -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(&params.a[..]) {
assert_eq!(u, a);
}
for (v, b) in v_i
.iter()
.filter(|&&e| e != Fr::zero())
.zip(&params.b_g1[..])
{
assert_eq!(v, b);
}
for (v, b) in v_i
.iter()
.filter(|&&e| e != Fr::zero())
.zip(&params.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(&params.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, &params, 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(&params.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());
}

View File

@ -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)
}
}

View File

@ -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(&params.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, &params, &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()
}
}

View File

@ -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::*;

View File

@ -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);
}

View File

@ -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(&params.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, &params, 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);
}

View File

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

View File

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

View File

@ -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.

View File

@ -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 = []

View File

@ -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.

View File

@ -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.

View File

@ -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.

View File

@ -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.

View File

@ -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);

View File

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

View File

@ -1 +0,0 @@
1.36.0

View File

@ -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()
));
}

View File

@ -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()
);
}

View File

@ -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()
));
}

View File

@ -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());
}

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View File

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

View File

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

View File

@ -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.

View File

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

View File

@ -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,
])
}
}
}
);
}

View File

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

3
ff/.gitignore vendored
View File

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

View File

@ -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" }

View File

@ -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.

View File

@ -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.

View File

@ -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.

View File

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

View File

@ -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
}

View File

@ -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
}
}

3
group/.gitignore vendored
View File

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

View File

@ -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.

View File

@ -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" }

View File

@ -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.

View File

@ -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.

View File

@ -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.

View File

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

View File

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

View File

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

View File

@ -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);
}
}

View File

@ -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())
}
}

View File

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

3
jubjub/.gitignore vendored
View File

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

View File

@ -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.

View File

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

View File

@ -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.

View File

@ -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.

View File

@ -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.

View File

@ -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.

View File

@ -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);

View File

@ -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);

View File

@ -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);

View File

@ -1 +0,0 @@
*.sage.py

View File

@ -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)

View File

@ -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/

View File

@ -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.

View File

@ -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.

View File

@ -1 +0,0 @@
-1

View File

@ -1 +0,0 @@
19257038036680949359750312669786877991949435402254120286184196891950884077233

View File

@ -1 +0,0 @@
6554484396890773809930967563523245729705921265872317281365359162392183254199

View File

@ -1 +0,0 @@
52435875175126190479447740508185965837690552500527637822603658699938581184513

View File

@ -1 +0,0 @@
fully rigid

View File

@ -1,4 +0,0 @@
#!/bin/sh
sage verify.sage .
grep -Rn '.' verify-* |grep '^verify-.*:1:' |sed 's/:1:/ = /'

View File

@ -1 +0,0 @@
tedwards

View File

@ -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>&#x2248; -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